Docker多阶段构建实战:让Nginx镜像从400MB瘦身到200MB,体积砍半!
一、实验背景在上一节实验中我们基于 CentOS 7 构建了 YUM 版 Nginx 镜像但镜像体积高达 400MB。这是因为 CentOS 7 基础镜像本身就包含大量系统工具和库而容器运行 Nginx 实际上并不需要完整的操作系统环境。多阶段构建Multi-Stage Build是 Docker 17.05 引入的特性允许在同一个 Dockerfile 中定义多个 FROM 阶段。每个阶段可以有不同的基础镜像最终只保留最后一个阶段的产物从而大幅减小镜像体积。本实验将通过多阶段构建将 Nginx 镜像从 400MB 优化到 50MB 以内。二、实验环境准备确保已完成上一节实验本地存在nginx:yum-v1镜像用于对比[rootDocker ~]# docker images i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA 192.168.244.21:5000/test-nginx:1.27.2-alpine 74175cf34632 79.1MB 23.7MB busybox:latest 40680ace50cf 8.82MB 4.5MB U centos:7 e6dba3b9654d 434MB 212MB hello-world:latest 5e2309035332 21.8kB 9.49kB my-busybox:v1 f308fc7938a6 8.83MB 4.5MB mysql:8.0 bb3c7d314bcc 1.62GB 799MB nginx:1.27.2-alpine 74175cf34632 79.1MB 23.7MB nginx:yum-v1 36ed6ee1ba71 527MB 245MB U registry:latest 325b4b29b041 83.4MB 20.4MB U ubuntu:latest da6fc2be5478 158MB 45.3MB [rootDocker ~]#若无该镜像可参考上篇文章Docker镜像构建实战:从零编写DockerfileYUM安装搞定Nginx镜像-CSDN博客文章浏览阅读200次点赞6次收藏5次。本实验将使用 CentOS 7 作为基础镜像通过编写 Dockerfile 安装 Nginx 服务并构建一个可复用的 YUM 版 Nginx 镜像。https://blog.csdn.net/2502_90206768/article/details/166600108?spm1001.2014.3001.5501三、实验步骤3.1 创建项目目录[rootDocker ~]# mkdir -p /data/nginx-multistage [rootDocker ~]# cd /data/nginx-multistage/ [rootDocker nginx-multistage]#3.2 编写多阶段 Dockerfile创建Dockerfile文件内容如下# 第一阶段编译阶段 FROM alpine:3.18 AS builder LABEL stagebuilder LABEL maintaineradminexample.com # 1. 安装编译所需的依赖添加了 musl-dev并显式包含 gcc RUN apk add --no-cache \ gcc \ musl-dev \ make \ pcre-dev \ zlib-dev \ openssl-dev \ wget \ coreutils # 下载 Nginx 源码 WORKDIR /usr/local/src RUN wget http://nginx.org/download/nginx-1.24.0.tar.gz \ tar -zxvf nginx-1.24.0.tar.gz # 编译 Nginx WORKDIR /usr/local/src/nginx-1.24.0 # 2. 在 configure 时显式指定 CCgcc强制使用 gcc 编译器 RUN CCgcc ./configure \ --prefix/usr/local/nginx \ --with-http_ssl_module \ --with-http_stub_status_module \ --without-http_gzip_module \ make -j$(nproc) \ make install # 创建测试首页 RUN echo Multi-Stage Build Nginx /usr/local/nginx/html/index.html # 第二阶段运行阶段 FROM alpine:3.18 AS runner LABEL stagerunner LABEL maintaineradminexample.com LABEL descriptionOptimized Nginx image with multi-stage build # 安装 Nginx 运行所需的依赖 RUN apk add --no-cache pcre zlib openssl # 从第一阶段复制编译好的 Nginx COPY --frombuilder /usr/local/nginx /usr/local/nginx # 创建软链接 RUN ln -s /usr/local/nginx/sbin/nginx /usr/local/bin/nginx # 创建 nginx 用户 RUN addgroup -S nginx adduser -S -G nginx nginx WORKDIR /usr/local/nginx EXPOSE 80 443 CMD [nginx, -g, daemon off;]多阶段构建核心要点概念说明AS builder给第一阶段命名方便后续引用AS runner给第二阶段命名COPY --frombuilder从第一阶段复制编译产物到当前阶段最终镜像只包含最后一个阶段runner的内容builder 阶段的层不会出现在最终镜像中3.3 构建镜像[rootDocker nginx-multistage]# docker build -t nginx-ms . [] Building 74.4s (16/16) FINISHED docker:default [internal] load build definition from Dockerfile 0.0s transferring dockerfile: 1.60kB 0.0s [internal] load metadata for docker.io/library/alpine:3.18 0.8s [internal] load .dockerignore 0.0s transferring context: 2B 0.0s CACHED [builder 1/7] FROM docker.io/library/alpine:3.18sha256:de0eb0b3f2a47ba1eb89389859a9bd88b28e82f5826b6969ad604979713c2 0.0s resolve docker.io/library/alpine:3.18sha256:de0eb0b3f2a47ba1eb89389859a9bd88b28e82f5826b6969ad604979713c2d4f 0.0s [builder 2/7] RUN apk add --no-cache gcc musl-dev make pcre-dev zlib-dev openssl-dev wget co 7.4s [builder 3/7] WORKDIR /usr/local/src 0.0s [builder 4/7] RUN wget http://nginx.org/download/nginx-1.24.0.tar.gz tar -zxvf nginx-1.24.0.tar.gz 50.1s [builder 5/7] WORKDIR /usr/local/src/nginx-1.24.0 0.0s [builder 6/7] RUN CCgcc ./configure --prefix/usr/local/nginx --with-http_ssl_module --with-http_stub_status_m 15.0s [builder 7/7] RUN echo Multi-Stage Build Nginx /usr/local/nginx/html/index.html 0.2s CACHED [runner 2/6] RUN apk add --no-cache pcre zlib openssl 0.0s [runner 3/6] COPY --frombuilder /usr/local/nginx /usr/local/nginx 0.0s [runner 4/6] RUN ln -s /usr/local/nginx/sbin/nginx /usr/local/bin/nginx 0.3s [runner 5/6] RUN addgroup -S nginx adduser -S -G nginx nginx 0.2s [runner 6/6] WORKDIR /usr/local/nginx 0.0s exporting to image 0.3s exporting layers 0.2s exporting manifest sha256:fed9b288991da645cbaab74e5197a53964e58322ad574a19f39bf557739fad91 0.0s exporting config sha256:31baf771b19f113247739075a1beb36a5b8b7028a6fc2d05dde237e4d00e8ef1 0.0s exporting attestation manifest sha256:a157c56854d0699c3fae6a74911657a1ace90c22a495e88ec166b638efbb144d 0.0s exporting manifest list sha256:5cafba155457f198c5cd16e60b65a3e91f90b3f353093e4ffe80506425f8a1ef 0.0s naming to docker.io/library/nginx-ms:latest 0.0s unpacking to docker.io/library/nginx-ms:latest 0.1s [rootDocker nginx-multistage]#构建过程中Docker 会依次执行两个阶段builder 阶段下载源码、编译 Nginx这一层体积较大约 500MBrunner 阶段基于 Alpine 拉取最小运行时环境仅复制编译产物最终镜像非常精简构建完成后查看两个镜像的大小对比[rootDocker nginx-multistage]# docker images | grep nginx 192.168.244.21:5000/test-nginx:1.27.2-alpine 74175cf34632 79.1MB 23.7MB nginx-ms:latest 5cafba155457 19MB 5.81MB nginx:1.27.2-alpine 74175cf34632 79.1MB 23.7MB nginx:yum-v1 36ed6ee1ba71 527MB 245MB U [rootDocker nginx-multistage]#四、实验验证4.1 启动容器并访问[rootDocker nginx-multistage]# docker run -d --name nginx-ms -p 8081:80 nginx-ms:latest 975bab2c605a27f853b0166891d02f3ea2bed45b714c30cda9f2381c0598df1b [rootDocker nginx-multistage]# curl http://localhost:8081 Multi-Stage Build Nginx [rootDocker nginx-multistage]#4.2 进入容器检查环境[rootDocker nginx-multistage]# docker exec -it nginx-ms /bin/sh /usr/local/nginx # nginx -v nginx version: nginx/1.24.0 /usr/local/nginx # ls /usr/local/nginx/html/ 50x.html index.html /usr/local/nginx # cat /etc/os-release NAMEAlpine Linux IDalpine VERSION_ID3.18.12 PRETTY_NAMEAlpine Linux v3.18 HOME_URLhttps://alpinelinux.org/ BUG_REPORT_URLhttps://gitlab.alpinelinux.org/alpine/aports/-/issues /usr/local/nginx # ps aux | grep nginx 1 root 0:00 nginx: master process nginx -g daemon off; 7 nobody 0:00 nginx: worker process 19 root 0:00 grep nginx /usr/local/nginx #可以看到容器内运行的是极简的 Alpine Linux没有 gcc、make 等编译工具也没有 yum 包管理器。4.3 镜像层历史比对[rootDocker nginx-multistage]# docker history nginx:multistage-v1 IMAGE CREATED CREATED BY SIZE COMMENT 9d599813d93c About an hour ago CMD [nginx -g daemon off;] 0B buildkit.dockerfile.v0 missing About an hour ago EXPOSE [443/tcp 80/tcp] 0B buildkit.dockerfile.v0 missing About an hour ago WORKDIR /usr/local/nginx 0B buildkit.dockerfile.v0 missing About an hour ago RUN /bin/sh -c addgroup -S nginx adduser … 24.6kB buildkit.dockerfile.v0 missing About an hour ago RUN /bin/sh -c ln -sf /usr/local/nginx/sbin/… 0B buildkit.dockerfile.v0 missing About an hour ago COPY /usr/local/nginx /usr/local/nginx # bui… 6.06MB buildkit.dockerfile.v0 missing About an hour ago RUN /bin/sh -c apk add --no-cache pcre zlib … 1.2MB buildkit.dockerfile.v0 missing About an hour ago LABEL descriptionOptimized Nginx image with… 0B buildkit.dockerfile.v0 missing About an hour ago LABEL maintaineradminexample.com 0B buildkit.dockerfile.v0 missing About an hour ago LABEL stagerunner 0B buildkit.dockerfile.v0 missing 19 months ago CMD [/bin/sh] 0B buildkit.dockerfile.v0 missing 19 months ago ADD alpine-minirootfs-3.18.12-x86_64.tar.gz … 7.67MB buildkit.dockerfile.v0 [rootDocker nginx-multistage]# docker history nginx:yum-v1 IMAGE CREATED CREATED BY SIZE COMMENT 36ed6ee1ba71 2 days ago CMD [nginx -g daemon off;] 0B buildkit.dockerfile.v0 missing 2 days ago EXPOSE [443/tcp 80/tcp] 0B buildkit.dockerfile.v0 missing 2 days ago RUN /bin/sh -c rm -rf ./* curl -o Cen… 58.9MB buildkit.dockerfile.v0 missing 2 days ago WORKDIR /etc/yum.repos.d/ 0B buildkit.dockerfile.v0 missing 2 days ago LABEL descriptionNginx image built with YUM… 0B buildkit.dockerfile.v0 missing 2 days ago LABEL version1.0 0B buildkit.dockerfile.v0 missing 2 days ago LABEL maintaineradminexample.com 0B buildkit.dockerfile.v0 missing 5 years ago /bin/sh -c #(nop) CMD [/bin/bash] 0B missing 5 years ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B missing 5 years ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 223MB [rootDocker nginx-multistage]#多阶段构建的镜像层数更少且只包含运行阶段的内容builder 阶段的编译工具和源码不会出现在最终镜像中。4.4 镜像体积对比[rootDocker nginx-multistage]# docker images --format table {{.Repository}}\t{{.Tag}}\t{{.Size}} | grep nginx nginx-ms latest 19MB nginx multistage-v1 21.7MB nginx yum-v1 527MB 192.168.244.21:5000/test-nginx 1.27.2-alpine 79.1MB nginx 1.27.2-alpine 79.1MB [rootDocker nginx-multistage]#镜像大小说明nginx:yum-v1~420MB基于完整 CentOS 7包含编译工具和系统库nginx:multistage-v1~50MB基于 Alpine仅包含 Nginx 运行时和必要依赖五、实验总结本实验通过多阶段构建技术成功将 Nginx 镜像从 400MB 优化到 50MB 以内掌握了以下核心知识点多阶段构建原理同一个 Dockerfile 中可以定义多个 FROM 阶段每个阶段独立构建最终只保留最后一个阶段的产物。编译与运行分离builder 阶段负责下载源码和编译runner 阶段负责提供最小运行时环境两者互不干扰。Alpine Linux 的优势Alpine 是一个极简的 Linux 发行版基础镜像仅 5MB 左右非常适合用作容器运行环境。COPY --from 指令这是多阶段构建的核心允许从指定阶段复制文件到当前阶段。安全加固最终镜像中不包含编译工具gcc、make和源码减少了攻击面提升了安全性。延伸思考多阶段构建不仅适用于 Nginx对于 Go、Rust、Java 等需要编译的语言同样适用。例如 Go 语言可以在 builder 阶段编译出静态二进制文件在 runner 阶段直接复制该文件运行最终镜像甚至可以小到 10MB 以内。