然后接下来的部分就是和 overlay 层无关的了,主要是一些经验和冷门知识。
构建基本分为两个大阶段,构建 + 打包,后者是打包成最小的运行依赖,前者则会附带很多编译工具,这些都是没必要附带除去的
FROM golang:1.22 AS build
WORKDIR /opt/app
COPY . .
RUN CGO_ENABLED=0 go build -o app_serve .
FROM photon:5.0
WORKDIR /opt/app
COPY --from=build /opt/app/app_serve .
...
daemon 类进程,都不推荐使用 musl libc 的 alpine,很多 glibc 的特性都不支持。
在 Node.js 中,分阶段构建的主要原因是为了优化构建过程和减少不必要的依赖。在开发过程中,我们会使用 npm install 命令安装所有的依赖,包括开发依赖(devDependencies)。这些开发依赖可能包含一些仅在开发过程中需要的工具、测试框架等,而在生产环境中并不需要。同时,在私有化里,一些客户现场会扫描主机文件,也就是会扫描到 /var/lib/docker/overlay2/<layer>
下的文件,减少不必要的依赖可以降低潜在的安全风险,使用 slim 镜像也是同理。
FROM node:18 AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM node:18-slim
WORKDIR /app
COPY --from=build /app/package*.json ./
RUN npm install --only=production
COPY --from=build /app/build ./build
CMD ["node", "build/server.js"]
其他也是类似的,这里不再举例。
参考:
这部分内容主要是讲解一些在使用包管理或者依赖相关工具的时候,会把下载的包缓存存放着,这些缓存可以清理掉。例如:
RUN set -eux; \
yum install -y libpng; \
yum clean all; \
rm -rf /var/cache/yum
其他类似,如下:
pip install --no-cache-dir xxx && find / -type d -name __pycache__ -prune -exec rm -rf {} \;
apk add --no-cache xxx
npm install --production && npm cache clean --force
apt install xxx && apt clean && rm -rf /var/cache/apt/* /var/lib/apt/lists/*
apt 这块还有额外注意的一点,例如安装一个 curl 的包:
$ apt install -y curl
...
The following additional packages will be installed:
ca-certificates krb5-locales libbrotli1 libcurl4 libgssapi-krb5-2 libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.5-0 libldap-common libnghttp2-14 libpsl5 librtmp1 libsasl2-2 libsasl2-modules libsasl2-modules-db
libssh2-1 libssl3 openssl publicsuffix
Suggested packages:
krb5-doc krb5-user libsasl2-modules-gssapi-mit | libsasl2-modules-gssapi-heimdal libsasl2-modules-ldap libsasl2-modules-otp libsasl2-modules-sql
The following NEW packages will be installed:
ca-certificates curl krb5-locales libbrotli1 libcurl4 libgssapi-krb5-2 libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.5-0 libldap-common libnghttp2-14 libpsl5 librtmp1 libsasl2-2 libsasl2-modules libsasl2-modules-db
libssh2-1 libssl3 openssl publicsuffix
0 upgraded, 22 newly installed, 0 to remove and 0 not upgraded.
apt 安装的时候会有三部分:
- Required packages
- Recommended packages
- Suggested packages
apt 提供选项 --no-install-recommends
不安装推荐的包,来减少容量。
$ apt install --no-install-recommends curl
...
The following additional packages will be installed:
libbrotli1 libcurl4 libgssapi-krb5-2 libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.5-0 libnghttp2-14 libpsl5 librtmp1 libsasl2-2 libsasl2-modules-db libssh2-1 libssl3
Suggested packages:
krb5-doc krb5-user
Recommended packages:
ca-certificates krb5-locales libldap-common publicsuffix libsasl2-modules
The following NEW packages will be installed:
curl libbrotli1 libcurl4 libgssapi-krb5-2 libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.5-0 libnghttp2-14 libpsl5 librtmp1 libsasl2-2 libsasl2-modules-db libssh2-1 libssl3
0 upgraded, 16 newly installed, 0 to remove and 0 not upgraded
另外,如果看过很多官方的 Dockerfile,发现在 apt install
后并没有清理缓存,那是因为 debian 那些基础镜像的 rootfs 里自带了 apt 的 Post-Invoke 之类的配置文件:
$ ls -l /etc/apt/apt.conf.d/
total 24
-rw-r--r-- 1 root root 399 May 25 2023 01autoremove
-rw-r--r-- 1 root root 182 Jan 8 2023 70debconf
-rw-r--r-- 1 root root 754 May 13 00:00 docker-autoremove-suggests
-rw-r--r-- 1 root root 1175 May 13 00:00 docker-clean
-rw-r--r-- 1 root root 481 May 13 00:00 docker-gzip-indexes
-rw-r--r-- 1 root root 269 May 13 00:00 docker-no-languages
参考:
同时,包管理安装一些工具或者编译工具,可以在使用完成后删掉,例如下面利用 apt-mark
使用完后把没 mark 的包删除掉:
ENV GOSU_VERSION 1.17
RUN set -eux; \
savedAptMark="$(apt-mark showmanual)"; \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates gnupg wget; \
rm -rf /var/lib/apt/lists/*; \
arch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
...
wget -O /usr/local/bin/gosu.asc "$url.asc"; \
wget -O /usr/local/bin/gosu "$url"; \
echo "$sha256 */usr/local/bin/gosu" | sha256sum -c -; \
export GNUPGHOME="$(mktemp -d)"; \
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
gpgconf --kill all; \
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
apt-mark auto '.*' > /dev/null; \
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
chmod +x /usr/local/bin/gosu; \
gosu --version; \
gosu nobody true
案例二,pm2 安装会用到 git:
RUN set -eux; \
savedAptMark="$(apt-mark showmanual)"; \
apt-get update; \
apt-get install -y --no-install-recommends git openssh-client ca-certificates; \
npm config set registry https://registry.npmmirror.com; \
npm install -g pm2; \
git config --global url."https://github.com".insteadOf ssh://[email protected]; \
pm2 install pm2-intercom; \
apt-mark auto '.*' > /dev/null; \
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
apt-get clean; \
npm cache clean --force; \
rm -rf /tmp/* /var/cache/apt/* /var/lib/apt/lists/* ~/.git
还有编译安装,一个 RUN
里安装编译依赖,下载源码,编译安装完成后,清理掉编译依赖:
RUN set -eux; \
\
apk add --no-cache --virtual .build-deps \
coreutils \
dpkg-dev dpkg \
gcc \
linux-headers \
make \
musl-dev \
openssl-dev \
wget \
; \
....
apk add --no-network --virtual .redis-rundeps $runDeps; \
apk del --no-network .build-deps; \
另外一些 rootfs 的包管理会有 db 文件存储,例如 centos7 的 yum :
$ ls -lh /var/lib/rpm
total 23M
-rw-r--r-- 1 root root 944K Nov 13 2020 Basenames
-rw-r--r-- 1 root root 8.0K Nov 13 2020 Conflictname
-rw-r--r-- 1 root root 284K Nov 13 2020 Dirnames
-rw-r--r-- 1 root root 8.0K Nov 13 2020 Group
-rw-r--r-- 1 root root 12K Nov 13 2020 Installtid
-rw-r--r-- 1 root root 16K Nov 13 2020 Name
-rw-r--r-- 1 root root 8.0K Nov 13 2020 Obsoletename
-rw-r--r-- 1 root root 19M Nov 13 2020 Packages
-rw-r--r-- 1 root root 1.8M Nov 13 2020 Providename
-rw-r--r-- 1 root root 84K Nov 13 2020 Requirename
-rw-r--r-- 1 root root 24K Nov 13 2020 Sha1header
-rw-r--r-- 1 root root 16K Nov 13 2020 Sigmd5
-rw-r--r-- 1 root root 8.0K Nov 13 2020 Triggername
-rw-r--r-- 1 root root 344K Nov 13 2020 __db.001
-rw-r--r-- 1 root root 96K Nov 13 2020 __db.002
-rw-r--r-- 1 root root 1.3M Nov 13 2020 __db.003
很多包管理工具存储了很多信息,而在使用 Dockerfile 的时候分层较多:
# 安装编译工具1
RUN set -eux; \
yum install xxx; \
...
yum clean all; \
rm -rf /var/cache/yum
# 安装编译工具1
RUN set -eux; \
yum install xxx2; \
...
yum clean all; \
rm -rf /var/cache/yum
RUN ...
rpm 变动则会改变 Packages
文件内容,后期这个 Packages
db 文件会很大,例如到 100MB,而由于 overlay 特性 n 个层都附带不一样的 Pakcages 文件,会很浪费空间。所以可以合并成一层 RUN,然后写好注释,每个依赖给哪个工具和二进制使用。
善用 .dockerignore
减少不必要的文件到镜像里
slimtoolkit/slim ,这个工具我是存疑的,把我们的部分镜像处理成 2M ,还是不够智能。
goodwithtech/dockle 是一个用于检查 Docker 镜像安全性和最佳实践的工具,例如前面缓存没清理它能扫描到。
- 目录
- 上一节:diff 的更多案例
- 下一节:一些镜像介绍