> Docker镜像管理:构建、推送与优化

为什么要关心镜像大小

我曾经构建了一个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.0478MB需要完整Linux环境
debian:bookworm-slim74MB比ubuntu稍小
alpine:3.197MB追求极致小体积
node:181GB不推荐,太大
node:18-alpine175MBNode.js推荐
python:3.11-slim155MBPython推荐
golang:1.21-alpine258MBGo编译阶段
scratch0MB静态编译的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 Web1.2GB85MB93%
Python API980MB155MB84%
Go微服务800MB12MB98%
Nginx静态站187MB25MB87%

总结

镜像管理的核心原则:选小基础镜像、用多阶段构建、减少层数、善用缓存。把这些做好,你的Docker镜像体积能减少80%以上,CI/CD速度也会大幅提升。

Docker基础操作可以回顾Docker入门教程。用Docker Compose管理多容器应用请看Docker Compose实战

Docker 镜像管理 Dockerfile
cd ..