docker多阶镜像(multi-stage)构建

本文介绍Docker多阶镜像的使用方法及其优势。通过示例对比了传统两步法构建与多阶镜像的区别,展示了如何在一个Dockerfile中高效地完成编译和部署流程,减少了磁盘空间占用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

博客作为学习笔记记录,若有理解,表述错误,欢迎指出。

docker 多阶镜像(multi-stage)是为了解决多个容器镜像之间文件拷贝等问题,多阶镜像有几个常用的场景:

1. debug: 用于调试dockerfile的特定阶段

2. 用于测试和debug阶段

 

Q: 为什么要用多阶镜像

在以前,如果需要从一个容器里把编译好的文件放到另外一个容器执行,需要分开两步操作。

举个栗子:

1、 编译工程的docker,对应dockerfile:

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
COPY app.go .
RUN go get -d -v golang.org/x/net/html \
  && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app

2、运行程序的docker

FROM
alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"] 

3、 将编译的程序拷到运行的docker里运行

#!/bin/sh

# 构建app可执行文件的docker镜像,运行临时docker容器
# 并把app可执行文件拷贝到host上,
# 删除临时docker容器
echo Building alexellis2/href-counter:build

docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \  
    -t alexellis2/href-counter:build . -f Dockerfile.build

docker container create --name extract alexellis2/href-counter:build 
docker container cp extract:/go/src/github.com/alexellis/href-counter/app ./app  
docker container rm -f extract

# 构建app执行环境的docker镜像,运行执行环境容器
# 并把app可执行文件拷贝到运行环境docker容器中,
# 删除host上app
echo Building alexellis2/href-counter:latest

docker build --no-cache -t alexellis2/href-counter:latest .
rm ./app

这样做也能实现app到运行环境执行,但会有什么问题呢:

问题1:分开操作,麻烦

问题2:需编多个镜像,然后把app拷出来,占用disk空间

  A:  使用多阶镜像构建方式

     使用多阶镜像后,前面那个操作,可以简化成这样:

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html 
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"] 

分析以上dockerfile:

1、 每个阶段stage都可以用不同base image

2、只会保留最后一个stage的镜像,前面的都会摒弃

3、可以在后面的stage使用前面stage的东西,eg:

COPY --from=0 /go/src/github.com/alexellis/href-counter/app .

4、 每个阶段以正数索引,从0开始,也可以通过FROM xxx as <stage-name>方式给每个阶段命名,eg:

FROM golang:1.7.3 as builder
…
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .

5、在多阶中,可以分阶段build,eg:

$ docker build --target builder -t alexellis2/href-counter:latest .

     用途:

     A、 debug

     B、testing

     C、只编某个阶段的镜像

6、 COPY --from=xxx,源image没有限制,eg:

COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf

  from 镜像来源:

    a. 本地image name

    b. 本地image tag

    c. docker registry上的image,tag ID

 

REF:

https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage

<think>嗯,用户之前问过NineData的Docker实现,现在又问Docker多阶构建是什么。看来他们可能是在部署或优化自己的Docker镜像,特别是在构建阶段遇到了问题,比如镜像大小或者依赖管理。 用户可能已经了解了一些Docker的基础知识,现在想深入优化构建过程。多阶构建是一个进阶话题,常用于减少镜像大小和分离构建环境与运行环境。用户可能遇到了构建后的镜像过大,或者构建过程中有太多不必要的依赖被带进生产环境的问题。 需要确认用户是否了解单阶段构建的局限性,比如构建工具和运行时依赖混合在一起,导致镜像臃肿。然后解释多阶构建如何通过多个阶段解决这个问题,第一阶段用于构建,第二阶段仅复制必要的文件,从而减小最终镜像的大小。 同时,用户可能关心实际应用中的例子,比如如何编写多阶段的Dockerfile,以及这样做的好处。需要提供清晰的示例,并对比单阶段和多阶段的区别,突出多阶段的优势,比如安全性、减少攻击面、提高部署效率等。 还要注意用户之前的问题涉及Java和Spring Boot,可能在回答中可以给出相关的例子,比如使用Maven构建,然后用Alpine镜像运行。此外,用户可能想知道最佳实践,比如如何选择基础镜像,如何处理依赖等,这些都需要在回答中涵盖。 最后,确保语言通俗易懂,避免使用过多术语,但又要准确。可能需要检查是否有其他相关知识需要补充,比如多阶构建的其他用途,比如分离测试和构建,但用户当前的问题集中在构建优化,所以重点放在减小镜像大小和分离环境上。</think>以下是 Docker **多阶构建**的详细说明: --- ### 一、什么是多阶构建Docker 多阶构建Multi-stage Build)是一种**优化镜像体积和安全性**的技术。通过将构建过程拆分为多个独立阶段(Stage),最终仅保留运行时所需的必要文件,丢弃构建环境的冗余依赖。 --- ### 二、为什么需要多阶构建? #### 传统单阶段构建的问题: 1. **镜像臃肿**:构建工具(如 Maven、GCC)和临时文件残留在最终镜像中。 2. **安全风险**:构建环境可能包含敏感信息(如密钥、调试工具)。 3. **依赖混杂**:开发依赖与运行时依赖混合,难以维护。 #### 多阶构建的优势: - **极简镜像**:最终镜像仅包含编译后的二进制文件/可执行文件。 - **环境隔离**:构建阶段与运行阶段完全分离。 - **安全性提升**:避免泄露构建过程的中间状态。 --- ### 三、工作原理 1. **定义多个 `FROM` 指令** 每个 `FROM` 指令代表一个独立的构建阶段,后续阶段可从前序阶段复制文件。 2. **命名阶段(可选)** 使用 `AS` 为阶段命名,方便后续引用。 3. **最终阶段为运行环境** 只有最后一个 `FROM` 阶段的输出会保留为最终镜像--- ### 四、使用示例 #### 场景:构建一个 Spring Boot Java 应用 **单阶段构建Dockerfile(问题示例)**: ```dockerfile FROM maven:3.8-jdk-11 # 包含完整的 Maven 和 JDK COPY src /app/src COPY pom.xml /app RUN mvn package # 构建生成 app.jar CMD ["java", "-jar", "/app/target/app.jar"] ``` - **缺点**:最终镜像包含 Maven、JDK 等无用内容,体积可能超过 500MB。 **多阶构建优化后的 Dockerfile**: ```dockerfile # 阶段 1:构建阶段(使用完整 JDK 和 Maven) FROM maven:3.8-jdk-11 AS builder COPY src /app/src COPY pom.xml /app RUN mvn package -DskipTests # 生成 app.jar # 阶段 2:运行阶段(仅保留 JRE 和编译结果) FROM openjdk:11-jre-slim # 轻量级运行环境 COPY --from=builder /app/target/app.jar /app.jar CMD ["java", "-jar", "/app.jar"] ``` - **效果**:最终镜像仅包含 JRE 和 `app.jar`,体积可缩小至约 150MB。 --- ### 五、关键技术点 1. **跨阶段文件复制** 使用 `COPY --from=<阶段名>` 从前序阶段复制文件: ```dockerfile COPY --from=builder /build/output /app ``` 2. **灵活选择基础镜像** - 构建阶段:使用功能完整的镜像(如 `gcc`, `python:3.9-sdk`)。 - 运行阶段:使用最小化镜像(如 `alpine`, `distroless`)。 3. **多阶段的高级用法** - **并行构建**:多个独立阶段可并行执行(需无依赖关系)。 - **条件复制**:选择性复制不同阶段的文件。 --- ### 六、适用场景 1. **编译型语言**(Go、Java、C++) 分离编译环境与运行环境。 2. **前端项目** 构建阶段安装 npm 依赖并打包,运行阶段使用 Nginx 托管静态文件。 3. **Python 项目** 构建阶段安装 `gcc` 编译依赖,运行阶段仅保留 `.whl` 包。 --- ### 七、最佳实践 1. **命名阶段**:使用 `AS` 提高可读性 ```dockerfile FROM golang:1.19 AS build FROM alpine:3.16 AS runtime ``` 2. **清理中间文件**:在构建阶段结束时删除临时文件 ```dockerfile RUN make build && make clean ``` 3. **使用 `.dockerignore`**:避免无关文件进入构建上下文。 --- ### 八、对比单阶段与多阶段 | | 单阶段构建 | 多阶构建 | |------------------|------------------|--------------------| | **镜像体积** | 大(含构建工具) | 小(仅运行时依赖) | | **安全性** | 较低(暴露构建过程)| 高(隔离构建环境) | | **构建速度** | 较快(无需分层) | 稍慢(多阶段步骤) | | **维护复杂度** | 低 | 中(需设计阶段逻辑)| --- 通过多阶构建,可以显著提升 Docker 镜像的**生产环境适用性**,是云原生应用优化的必备技能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值