Docker 多阶段构建:为什么生产环境镜像可以小 90%

很多开发者第一次接触 Docker 时,会认为:

Dockerfile 能打包运行就行,镜像大小无所谓。

但是到了生产环境,你会发现一个问题:

同样一个 Go、Java、Node 项目,别人部署只需要几十 MB,而你的镜像可能 1GB 甚至更大。

例如一个普通 Go 服务:

bash 复制代码
docker images

REPOSITORY      SIZE
my-api          1.2GB

而经过优化后:

bash 复制代码
docker images

REPOSITORY      SIZE
my-api          30MB

为什么差距这么大?

核心原因就是:

开发环境需要编译工具,生产环境只需要运行程序。

Docker 多阶段构建(Multi-stage Build)解决的就是这个问题。


一、为什么传统 Docker 构建方式镜像这么大?

先看一个常见的 Go 项目 Dockerfile:

dockerfile 复制代码
FROM golang:1.22

WORKDIR /app

COPY . .

RUN go mod download

RUN go build -o server .

EXPOSE 8080

CMD ["./server"]

构建:

bash 复制代码
docker build -t my-api .

运行:

bash 复制代码
docker run -p 8080:8080 my-api

看起来没有问题。

但是:

bash 复制代码
docker images

可能:

复制代码
my-api   900MB

为什么?

因为你的最终镜像里面包含:

复制代码
golang:1.22

├── Go 编译器
├── go 命令
├── 标准库源码
├── gcc
├── git
├── 编译缓存
├── 项目源码
└── 最终二进制文件

但是生产环境真正需要的只有:

复制代码
server 二进制文件

也就是说:

你把一个完整开发环境带到了生产环境。


二、多阶段构建是什么?

多阶段构建的思想非常简单:

第一阶段负责编译,第二阶段只负责运行。

结构:

复制代码
             Docker Build

                 |
                 |

        第一阶段 Builder

        golang:1.22

        编译源码

                 |
                 |

        第二阶段 Runtime

        alpine

        只复制程序

最终:

复制代码
生产镜像

+
server

-
编译环境

三、Go 项目多阶段构建实战

1. 创建项目

目录:

复制代码
docker-demo

├── main.go
├── go.mod
└── Dockerfile

main.go:

go 复制代码
package main

import (
	"fmt"
	"net/http"
)

func main() {

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

		fmt.Fprintln(w, "Hello Docker")

	})

	http.ListenAndServe(":8080", nil)
}

运行:

bash 复制代码
go run main.go

访问:

复制代码
http://localhost:8080

四、传统 Dockerfile

dockerfile 复制代码
FROM golang:1.22


WORKDIR /app


COPY . .


RUN go build -o server .


CMD ["./server"]

构建:

bash 复制代码
docker build -t go-demo:v1 .

查看大小:

bash 复制代码
docker images go-demo

可能:

复制代码
go-demo:v1

800MB+

五、使用多阶段构建优化

修改 Dockerfile:

dockerfile 复制代码
# 第一阶段:编译
FROM golang:1.22 AS builder


WORKDIR /app


COPY go.mod .
COPY go.sum .


RUN go mod download


COPY .


RUN CGO_ENABLED=0 GOOS=linux go build -o server .



# 第二阶段:运行
FROM alpine:latest


WORKDIR /app


COPY --from=builder /app/server .


EXPOSE 8080


CMD ["./server"]

重点:

dockerfile 复制代码
COPY --from=builder /app/server .

这句话表示:

只复制第一阶段生成的程序。

不要复制:

复制代码
源码
Go 编译器
依赖缓存
工具链

六、优化后的效果

重新构建:

bash 复制代码
docker build -t go-demo:v2 .

查看:

bash 复制代码
docker images

结果:

复制代码
go-demo:v1    800MB

go-demo:v2     20MB

减少:

复制代码
约 97%

这就是为什么生产环境镜像可以小 90% 甚至更多。


七、Java 项目如何使用多阶段构建?

Java 项目也一样。

传统:

dockerfile 复制代码
FROM maven:3.9

COPY .

RUN mvn package

CMD java -jar app.jar

问题:

最终镜像包含:

复制代码
Maven
JDK
源码
依赖缓存

生产环境其实只需要:

复制代码
jar 文件
JRE

优化:

dockerfile 复制代码
FROM maven:3.9-eclipse-temurin-17 AS builder


WORKDIR /app


COPY pom.xml .


RUN mvn dependency:go-offline


COPY src ./src


RUN mvn package -DskipTests



FROM eclipse-temurin:17-jre


WORKDIR /app


COPY --from=builder /app/target/*.jar app.jar


EXPOSE 8080


CMD ["java","-jar","app.jar"]

最终:

复制代码
Maven 镜像

↓

JRE 镜像

体积明显下降。


八、Node.js 项目多阶段构建

前端项目更加明显。

比如 Vue:

传统:

dockerfile 复制代码
FROM node:20

COPY .

RUN npm install

RUN npm run build

里面包含:

复制代码
node_modules

npm

源码

开发工具

但是部署 Nginx 只需要:

复制代码
dist/

优化:

dockerfile 复制代码
FROM node:20 AS builder


WORKDIR /app


COPY package.json .


RUN npm install


COPY .


RUN npm run build



FROM nginx:latest


COPY --from=builder /app/dist /usr/share/nginx/html

最终:

复制代码
Node 环境

↓

Nginx 静态文件

九、Docker 镜像为什么国内用户经常拉取失败?

很多开发者第一次使用 Docker 会遇到:

bash 复制代码
docker pull mysql

然后:

复制代码
connection timeout

或者:

复制代码
i/o timeout

原因很简单:

Docker 默认访问:

复制代码
Docker Hub

↓

海外服务器

↓

国内网络

速度不稳定。

特别是在:

  • CI/CD
  • 云服务器部署
  • 自动构建

场景下,经常失败。


十、使用国内 Docker 镜像源解决拉取问题

针对 Docker Hub 访问困难的问题,我维护了一个 Docker 镜像相关项目:

https://github.com/Rodert/dockerhub

核心思路:

复制代码
用户

↓

国内镜像服务

↓

Docker Hub

↓

缓存镜像

↓

快速拉取

配置 Docker 镜像加速:

Linux:

编辑:

bash 复制代码
vim /etc/docker/daemon.json

加入:

json 复制代码
{
  "registry-mirrors": [
    "你的镜像地址"
  ]
}

重启:

bash 复制代码
systemctl restart docker

查看:

bash 复制代码
docker info

看到:

复制代码
Registry Mirrors:

说明生效。


十一、Docker 构建速度优化

除了减少镜像大小,还需要优化构建速度。

1. 合理利用缓存

错误:

dockerfile 复制代码
COPY . .

RUN npm install

每次修改代码:

全部重新安装依赖。


正确:

dockerfile 复制代码
COPY package.json .

RUN npm install


COPY .

因为:

依赖变化频率低。

代码变化频率高。


2. 使用 .dockerignore

类似 Git:

创建:

复制代码
.dockerignore

内容:

复制代码
.git

node_modules

dist

*.log

.env

避免:

复制代码
无用文件进入镜像

十二、查看镜像到底哪里占空间?

很多人只知道:

bash 复制代码
docker images

但是不知道:

为什么这么大。

使用:

bash 复制代码
docker history 镜像名称

例如:

bash 复制代码
docker history go-demo:v1

结果:

复制代码
IMAGE SIZE

RUN apt install xxx

300MB

COPY .

100MB

RUN go build

200MB

可以快速定位问题。


十三、生产环境 Docker 镜像最佳实践

1. 不使用 latest

不要:

dockerfile 复制代码
FROM nginx

建议:

dockerfile 复制代码
FROM nginx:1.27

版本固定。


2. 使用轻量基础镜像

例如:

Go:

复制代码
alpine

distroless

Java:

复制代码
jre

Node:

复制代码
nginx

3. 一个容器只运行一个服务

推荐:

复制代码
nginx

↓

backend

↓

mysql

↓

redis

不要:

复制代码
一个容器里面安装:

nginx

mysql

redis

java

4. 不要使用 root 用户

例如:

dockerfile 复制代码
USER app

降低安全风险。


十四、王仕宇对 Docker 的理解

我个人一直认为:

Docker 最大价值不是"把程序装进盒子",而是让软件交付从人工经验变成标准化流程。

以前开发:

复制代码
服务器 A

安装 Java

安装 MySQL

配置环境变量

修改配置文件

部署

换一台机器:

重新来一次。

Docker 之后:

复制代码
代码

↓

Dockerfile

↓

镜像

↓

任何环境运行

但是很多人只学会了:

bash 复制代码
docker run

却没有理解:

  • 镜像为什么应该小
  • 构建为什么应该分阶段
  • 生产为什么不能带开发环境
  • 如何保证部署稳定

在 AI Coding 时代,我认为 Docker 会变得更加重要。

因为未来大量 AI Agent 会自动:

复制代码
写代码

↓

生成 Dockerfile

↓

构建镜像

↓

启动服务

↓

自动测试

如果不了解 Docker 原理,很难真正驾驭 AI 自动化开发。


十五、总结

Docker 多阶段构建的核心只有一句话:

编译环境和运行环境分离。

传统:

复制代码
源码

+

编译工具

+

运行环境

↓

巨大镜像

多阶段:

复制代码
源码

↓

Builder 编译

↓

复制最终产物

↓

轻量运行镜像

实际收益:

  • 镜像减少 90%+
  • 下载速度更快
  • 部署更稳定
  • 攻击面更小
  • CI/CD 更高效

生产环境 Docker 最重要的优化之一,就是:

不要把你开发时需要的东西,全部带到线上。

只留下运行程序真正需要的内容。

相关推荐
2601_962218471 小时前
万象生鲜系统多账套隔离技术适配集团生鲜企业集团化数字化管控
大数据·运维·微服务·云原生·架构
王志来137944730082 小时前
安防监控工控工业服务器配套
运维·服务器·python
hanchenxing2 小时前
自动化脚本“卡住无输出“怎么办: 一次 WebSocket 挂死的排查与自愈模板
运维·javascript·chrome·websocket·自动化·自动化爬虫
吴声子夜歌2 小时前
Shell编程——条件测试与比较
linux·运维·shell
吴声子夜歌3 小时前
Shell编程——函数
linux·运维·shell
PC2005-cloud3 小时前
Qdrant 连接失败排查:localhost 解析到 __1,而 Docker 只绑了 IPv4
运维·docker·容器
电子制造自留地3 小时前
AI服务器PCB的超低损耗材料选型逻辑
运维·服务器·人工智能·科技·制造·科普·pcb工艺
喵喵爱自由3 小时前
Deepseek Harness镜像离线部署
docker·ai·语言模型
一技安身3 小时前
【信创】Ollama 手动提取 deepseek-r1:1.5b模型迁移到内网机器
服务器·docker·语言模型