为什么要关心镜像大小
我曾经构建了一个Node.js应用的Docker镜像,足足1.2GB。每次部署都要传很久,CI/CD流水线慢得令人发指。后来优化到了85MB,部署时间从5分钟缩短到30秒。
镜像管理是Docker进阶的必经之路。
镜像基础操作
查看和管理镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| # 列出本地镜像
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myapp latest a1b2c3d4e5f6 2 hours ago 85MB
node 18-alpine 1234abcd5678 3 days ago 175MB
nginx latest abcdef123456 1 week ago 187MB
ubuntu 22.04 fedcba654321 2 weeks ago 77.8MB
# 查看镜像详细信息
$ docker inspect myapp:latest | head -20
# 查看镜像的构建历史(每一层)
$ docker history myapp:latest
IMAGE CREATED CREATED BY SIZE
a1b2c3d4e5f6 2 hours ago CMD ["node" "app.js"] 0B
<missing> 2 hours ago COPY . /app 1.5MB
<missing> 2 hours ago RUN npm install --production 12MB
<missing> 2 hours ago WORKDIR /app 0B
<missing> 3 days ago /bin/sh -c #(nop) CMD ["node"] 0B
# 删除镜像
$ docker rmi myapp:v1
$ docker rmi a1b2c3d4e5f6 # 用ID删除
# 批量删除无用镜像
$ docker image prune -a
WARNING! This will remove all images without at least one container associated to them.
Total reclaimed space: 2.3GB
|
镜像标签管理
1
2
3
4
5
6
7
8
| # 给镜像打标签
$ docker tag myapp:latest myapp:v1.0
$ docker tag myapp:latest registry.example.com/myapp:v1.0
$ docker images | grep myapp
myapp latest a1b2c3d4e5f6 2 hours ago 85MB
myapp v1.0 a1b2c3d4e5f6 2 hours ago 85MB
registry.example.com/myapp v1.0 a1b2c3d4e5f6 2 hours ago 85MB
|
Dockerfile最佳实践
基础镜像选择
| 基础镜像 | 大小 | 适用场景 |
|---|
ubuntu:22.04 | 78MB | 需要完整Linux环境 |
debian:bookworm-slim | 74MB | 比ubuntu稍小 |
alpine:3.19 | 7MB | 追求极致小体积 |
node:18 | 1GB | 不推荐,太大 |
node:18-alpine | 175MB | Node.js推荐 |
python:3.11-slim | 155MB | Python推荐 |
golang:1.21-alpine | 258MB | Go编译阶段 |
scratch | 0MB | 静态编译的Go程序 |
反面教材 vs 优化版本
反面教材(1.2GB):
1
2
3
4
5
6
| FROM node:18
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["node", "app.js"]
|
优化版本(85MB):
1
2
3
4
5
6
7
8
9
10
11
12
| FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app .
EXPOSE 3000
USER node
CMD ["node", "app.js"]
|
优化技巧
1. 善用.dockerignore
1
2
3
4
5
6
7
8
9
| # .dockerignore
node_modules
.git
.gitignore
*.md
.env
.DS_Store
tests/
docs/
|
2. 合并RUN命令减少层数
1
2
3
4
5
6
7
8
9
10
| # 差:3层
RUN apt update
RUN apt install -y curl wget
RUN apt clean
# 好:1层
RUN apt update && \
apt install -y curl wget && \
apt clean && \
rm -rf /var/lib/apt/lists/*
|
3. 利用构建缓存
1
2
3
4
5
6
| # 把不常变化的放前面
COPY package*.json ./
RUN npm install
# 把经常变化的放后面
COPY . .
|
多阶段构建
多阶段构建是减小镜像体积的大杀器,特别适合编译型语言。
Go应用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 阶段1:编译
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o main .
# 阶段2:运行(只有几MB!)
FROM scratch
COPY --from=builder /app/main /main
EXPOSE 8080
ENTRYPOINT ["/main"]
|
1
2
3
| $ docker build -t mygoapp .
$ docker images | grep mygoapp
mygoapp latest abc123 12MB # 只有12MB!
|
Python应用示例
1
2
3
4
5
6
7
8
9
10
11
12
| FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
EXPOSE 5000
CMD ["python", "app.py"]
|
推送镜像到Registry
Docker Hub
1
2
3
4
5
6
7
8
9
10
11
12
| # 登录
$ docker login
Username: myusername
Password:
Login Succeeded
# 标记并推送
$ docker tag myapp:latest myusername/myapp:v1.0
$ docker push myusername/myapp:v1.0
The push refers to repository [docker.io/myusername/myapp]
a1b2c3d4: Pushed
v1.0: digest: sha256:xxxx size: 1234
|
私有Registry
1
2
3
4
5
6
7
8
9
| # 用Docker跑一个私有Registry
$ docker run -d -p 5000:5000 --name registry registry:2
# 推送到私有Registry
$ docker tag myapp:latest localhost:5000/myapp:v1.0
$ docker push localhost:5000/myapp:v1.0
# 从私有Registry拉取
$ docker pull localhost:5000/myapp:v1.0
|
镜像安全扫描
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 使用Docker Scout扫描漏洞
$ docker scout cves myapp:latest
✓ Image stored for indexing
✓ Indexed 125 packages
Target: myapp:latest
digest: sha256:xxxx
Vulnerabilities:
0C 2H 5M 12L
critical high medium low
# 使用Trivy(更强大的开源工具)
$ docker run --rm aquasec/trivy image myapp:latest
|
镜像大小对比
通过优化,我的几个项目的镜像大小变化:
| 应用 | 优化前 | 优化后 | 减少比例 |
|---|
| Node.js Web | 1.2GB | 85MB | 93% |
| Python API | 980MB | 155MB | 84% |
| Go微服务 | 800MB | 12MB | 98% |
| Nginx静态站 | 187MB | 25MB | 87% |
总结
镜像管理的核心原则:选小基础镜像、用多阶段构建、减少层数、善用缓存。把这些做好,你的Docker镜像体积能减少80%以上,CI/CD速度也会大幅提升。
Docker基础操作可以回顾Docker入门教程。用Docker Compose管理多容器应用请看Docker Compose实战。