简介
在构建 Docker 镜像时,常遇到以下问题:
每一次执行 RUN 命令会让镜像新增一层 layer,导致镜像变大,虽然通过
&&
连接多个命令能缓解此问题,但如果命令之间用到 Docker 指令例如COPY
、WORKDIR
等,依然会导致多个layer。有些工具在构建过程中会用到,但是最终的镜像是不需要的(例如本案里中编译 GreatSQL 需要安装 cmake, gcc 等组件),这要求在构建完后还要额外清理这些工具,清理的过程又可能导致新的layer。
不分构建过程甚至部分源码不想被看到。
为了解决上述问题,从 Docker 17.05 版本开始,在构建镜像时增加了新特性:多阶段构建(multi-stage builds),将构建过程分为多个阶段,每个阶段都可以指定一个基础镜像,这样在一个 Dockerfile 就能将多个镜像的特性同时用到。
这种方法在不牺牲构建过程的复杂度和灵活性的前提下,可以显著减小最终镜像的大小,提高安全性,并且易于维护。
相关文档地址:https://docs.docker.com/develop/develop-images/multistage-build/。
分阶段构建是一种强大的 Docker 构建策略,它可以帮助你创建更加精简、安全和易于维护的镜像。通过将构建过程分解为多个独立的阶段,你可以在不影响最终镜像质量的前提下,充分利用 Docker 的构建缓存机制,加速构建流程。
举例
先用一个非常简单的案例来说明如何进行多阶段构建。
下面是一份精简的 Dockerfile:
# 第一阶段
FROM oraclelinux:8-slim as builder
# 在第一阶段进行构建
RUN touch tmp/GreatSQL-8.0.32-26-Linux-glibc2.28-x86_64.tar.xz && echo $RANDOM > tmp/GreatSQL-8.0.32-26-Linux-glibc2.28-x86_64.tar.xz
# 第二阶段
FROM oraclelinux:8-slim
# 从第一阶段构建的结果中将文件拷贝过来
COPY --from=builder tmp/GreatSQL-8.0.32-26-Linux-glibc2.28-x86_64.tar.xz tmp
CMD ["sh"]复制
结合备注内容,上述案例应该还是非常好理解的,就不赘述了。
GreatSQL-Build 镜像优化
上述这些问题,在 GreatSQL-Build 镜像中都存在。该镜像的尺寸高达 1.18GB,构建过程也非常慢,是时候对其进行优化了。
优化后的 Dockerfile 主要内容如下:
FROM oraclelinux:8-slim as builder
WORKDIR
LABEL maintainer="greatsql.cn" \
email="greatsql@greatdb.com" \
forum="https://greatsql.cn/forum.php" \
gitee="https://gitee.com/GreatSQL/GreatSQL-Docker"
ARG TARGETARCH \
OPT_DIR=/opt \
MYSQL_UID=3306 \
MYSQL_USER=mysql \
GREATSQL_BUILD_DOWNLOAD_URL="https://gitee.com/GreatSQL/GreatSQL-Docker/raw/greatsql-8.0.32-26/GreatSQL-Build" \
GREATSQL_ENV="greatsql-setenv.sh" \
ENTRYPOINT="docker-entrypoint.sh" \
DEPS="autoconf automake binutils bison cmake cyrus-sasl-devel cyrus-sasl-scram gcc-c++ \
gcc-toolset-11 gcc-toolset-11-annobin-plugin-gcc jemalloc jemalloc-devel krb5-devel libaio-devel \
libcurl-devel libtirpc-devel libudev-devel m4 make ncurses-devel numactl-devel openldap-devel \
openssl openssl-devel pam-devel readline-devel zlib-devel xz util-linux findutils"
RUN echo '[main]' > etc/dnf/dnf.conf && \
microdnf install -y oracle-epel-release-el8 && \
microdnf install -y ${DEPS} && \
microdnf update -y && \
microdnf clean all && \
source opt/rh/gcc-toolset-11/enable && \
echo 'source opt/rh/gcc-toolset-11/enable' >> root/.bash_profile; \
/usr/sbin/groupadd -g ${MYSQL_UID} ${MYSQL_USER} && \
/usr/sbin/useradd -u ${MYSQL_UID} -g ${MYSQL_UID} -s sbin/nologin ${MYSQL_USER} && \
curl -o ${OPT_DIR}/${GREATSQL_ENV} ${GREATSQL_BUILD_DOWNLOAD_URL}/${GREATSQL_ENV} && \
curl -o ${ENTRYPOINT} ${GREATSQL_BUILD_DOWNLOAD_URL}/${ENTRYPOINT} && \
chmod +x *sh ${OPT_DIR}/*sh && \
sh docker-entrypoint.sh
FROM oraclelinux:8-slim as greatsql_build
LABEL maintainer="greatsql.cn" \
email="greatsql@greatdb.com" \
forum="https://greatsql.cn/forum.php" \
gitee="https://gitee.com/GreatSQL/GreatSQL-Docker"
ARG TARGETARCH \
OPT_DIR=/opt
# copy GreatSQL tarball to /opt
COPY --from=builder ${OPT_DIR}/GreatSQL*.tar.xz ${OPT_DIR}
CMD ["sh"]复制
这份文件也已经同步到gitee上:https://gitee.com/GreatSQL/GreatSQL-Docker/blob/master/GreatSQL-Build/Dockerfile。
还是同样的思路,在第一阶段完成环境初始化及 GreatSQL 源码编译,第二阶段只需把编译后的二进制包拷贝过来就行。
经过优化后,新的 GreatSQL-Build 镜像尺寸仅为 200MB,只有原来的 1/6,效果显著。
$ docker images
...
# 阿里云ACR上的旧镜像,还没更新
registry.cn-beijing.aliyuncs.com/greatsql/greatsql_build latest a8a4dcaa1ce1 9 days ago 1.18GB
# 腾讯云TCR上的新镜像,已更新
ccr.ccs.tencentyun.com/greatsql/greatsql_build latest 1b53cc0906cc 15 hours ago 200MB复制
以上。
Enjoy GreatSQL :)
文章推荐:
想看更多技术好文,点个“在看”吧
题图由阿里通义万相生成