Skip to content

Latest commit

 

History

History
253 lines (204 loc) · 9.39 KB

03.md

File metadata and controls

253 lines (204 loc) · 9.39 KB

一些最小依赖优化大小经验

然后接下来的部分就是和 overlay 层无关的了,主要是一些经验和冷门知识。

多阶段构建

构建基本分为两个大阶段,构建 + 打包,后者是打包成最小的运行依赖,前者则会附带很多编译工具,这些都是没必要附带除去的

golang

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 的特性都不支持。

nodejs

在 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

善用 .dockerignore 减少不必要的文件到镜像里

docker-slim

slimtoolkit/slim ,这个工具我是存疑的,把我们的部分镜像处理成 2M ,还是不够智能。

dockle

goodwithtech/dockle 是一个用于检查 Docker 镜像安全性和最佳实践的工具,例如前面缓存没清理它能扫描到。

链接