第 08 章:Docker 基础镜像
第 08 章:Docker 基础镜像
Alpine Linux 作为 Docker 基础镜像的最佳实践。
8.1 为什么选择 Alpine 作为基础镜像
镜像大小对比(2026 年数据):
alpine:3.20 ████ 5.7 MB
debian:bookworm-slim ████████████████████ 74 MB
ubuntu:24.04 ███████████████████████ 78 MB
centos:stream9 ██████████████████████████████ 150 MB
| 指标 | Alpine | Debian Slim | Ubuntu |
|---|---|---|---|
| 基础镜像大小 | ~5 MB | ~74 MB | ~78 MB |
| 包数量 | ~14,000 | ~59,000 | ~60,000 |
| 拉取时间 (100Mbps) | <1s | ~6s | ~6s |
| 安全漏洞数 | 少 | 中 | 多 |
| 启动速度 | 极快 | 快 | 快 |
| musl 兼容性 | 完美 | N/A | N/A |
8.2 基础镜像使用
拉取与标签
# 推荐使用明确的版本标签
docker pull alpine:3.20
docker pull alpine:3.20.3
# 不推荐在生产使用 latest
docker pull alpine:latest
# edge 版本(滚动更新)
docker pull alpine:edge
# 镜像大小验证
docker images alpine
# REPOSITORY TAG SIZE
# alpine 3.20 7.73MB
基本 Dockerfile
# 最小化 Web 应用
FROM alpine:3.20
# 元数据
LABEL maintainer="[email protected]"
LABEL version="1.0"
LABEL description="Minimal web application"
# 安装依赖(使用 --no-cache 避免缓存索引)
RUN apk add --no-cache \
nginx \
curl \
tini \
&& mkdir -p /run/nginx /var/www/html
# 复制应用
COPY index.html /var/www/html/
COPY nginx.conf /etc/nginx/nginx.conf
# 非 root 用户
RUN adduser -D -s /sbin/nologin appuser \
&& chown -R appuser:appuser /var/www/html
EXPOSE 80
# 使用 tini 作为 PID 1(处理信号)
ENTRYPOINT ["tini", "--"]
CMD ["nginx", "-g", "daemon off;"]
8.3 多阶段构建
Go 应用多阶段构建
# ---- 构建阶段 ----
FROM golang:1.22-alpine AS builder
WORKDIR /build
# 安装构建依赖
RUN apk add --no-cache git ca-certificates
# 下载依赖
COPY go.mod go.sum ./
RUN go mod download
# 编译静态链接二进制
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-s -w" -o /app/server ./cmd/server
# ---- 运行阶段 ----
FROM alpine:3.20
# 时区和 CA 证书
RUN apk add --no-cache ca-certificates tzdata \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& apk del tzdata
# 非 root 用户
RUN adduser -D -s /sbin/nologin appuser
WORKDIR /app
COPY --from=builder /app/server .
COPY --chown=appuser:appuser config/ /app/config/
USER appuser
EXPOSE 8080
ENTRYPOINT ["./server"]
Node.js 应用多阶段构建
# ---- 依赖安装阶段 ----
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
# ---- 构建阶段 ----
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- 生产运行阶段 ----
FROM alpine:3.20 AS production
RUN apk add --no-cache \
nodejs \
npm \
tini \
ca-certificates
RUN adduser -D -s /sbin/nologin appuser
WORKDIR /app
COPY --from=deps --chown=appuser:appuser /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appuser /app/dist ./dist
COPY --chown=appuser:appuser package.json ./
USER appuser
EXPOSE 3000
ENTRYPOINT ["tini", "--"]
CMD ["node", "dist/index.js"]
Python 应用多阶段构建
# ---- 构建阶段 ----
FROM python:3.12-alpine AS builder
WORKDIR /app
# 安装编译依赖
RUN apk add --no-cache \
gcc \
musl-dev \
libffi-dev
# 创建虚拟环境
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# 安装 Python 依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# ---- 运行阶段 ----
FROM alpine:3.20
RUN apk add --no-cache \
python3 \
libffi \
tini \
&& adduser -D -s /sbin/nologin appuser
WORKDIR /app
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY --chown=appuser:appuser . .
USER appuser
EXPOSE 8000
ENTRYPOINT ["tini", "--"]
CMD ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0"]
Java 应用多阶段构建
# ---- 构建阶段 ----
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY . .
RUN ./gradlew bootJar --no-daemon
# ---- 运行阶段 ----
FROM alpine:3.20
RUN apk add --no-cache \
openjdk21-jre \
tini \
&& adduser -D -s /sbin/nologin javauser
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
USER javauser
EXPOSE 8080
ENTRYPOINT ["tini", "--"]
CMD ["java", "-jar", "app.jar"]
8.4 镜像优化技巧
减少层数
# ❌ 不好的写法(多层)
RUN apk add nginx
RUN apk add curl
RUN apk add vim
RUN rm -rf /var/cache/apk/*
# ✅ 好的写法(单层 + --no-cache)
RUN apk add --no-cache nginx curl vim
利用缓存
# ✅ 先复制依赖文件,再复制源码
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# 这样只要 package.json 不变,npm ci 层就会被缓存
使用 .dockerignore
# .dockerignore
.git
.gitignore
node_modules
*.md
.env
.env.*
Dockerfile
docker-compose*.yml
.dockerignore
tests/
coverage/
.nyc_output/
dist/
使用 BuildKit
# 启用 BuildKit
export DOCKER_BUILDKIT=1
# 使用缓存挂载
# syntax=docker/dockerfile:1
FROM alpine:3.20
RUN --mount=type=cache,target=/var/cache/apk \
apk add nginx python3
# 多平台构建
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
最终镜像大小对比
# 检查镜像层
docker history myapp:latest
# 使用 dive 工具分析镜像
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
wagoodman/dive myapp:latest
# 镜像大小检查脚本
cat > /usr/local/bin/image-size << 'SCRIPT'
#!/bin/sh
for img in "$@"; do
size=$(docker image inspect "$img" --format='{{.Size}}' | numfmt --to=iec)
echo "$img: $size"
done
SCRIPT
chmod +x /usr/local/bin/image-size
8.5 常见语言的 Alpine 镜像
| 语言 | 基础镜像 | 大小 |
|---|---|---|
| Go | golang:1.22-alpine | ~250 MB (构建) |
| Node.js | node:20-alpine | ~130 MB |
| Python | python:3.12-alpine | ~50 MB |
| Java | eclipse-temurin:21-jre-alpine | ~150 MB |
| Ruby | ruby:3.3-alpine | ~50 MB |
| PHP | php:8.3-fpm-alpine | ~85 MB |
| Rust | rust:1.77-alpine | ~300 MB (构建) |
8.6 处理 musl 兼容性
# 问题:某些二进制文件依赖 glibc
# 解决方案 1:安装 gcompat
RUN apk add --no-cache gcompat
# 解决方案 2:使用 Debian slim 运行时
FROM debian:bookworm-slim AS runtime
COPY --from=builder /app/binary /usr/local/bin/
# 解决方案 3:静态链接编译
# Go
RUN CGO_ENABLED=0 go build -o /app/server
# Rust
RUN cargo build --release --target x86_64-unknown-linux-musl
# C/C++
RUN gcc -static -o /app/server server.c
8.7 安全最佳实践
FROM alpine:3.20 AS production
# 安全加固
RUN apk add --no-cache ca-certificates \
&& update-ca-certificates \
&& addgroup -S appgroup \
&& adduser -S -G appgroup -s /sbin/nologin appuser
# 只读文件系统
RUN mkdir -p /app /tmp \
&& chown -R appuser:appgroup /app /tmp
WORKDIR /app
COPY --chown=appuser:appgroup . .
# 使用非 root 用户
USER appuser
# 只暴露必要端口
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1
ENTRYPOINT ["./app"]
8.8 CI/CD 集成
GitLab CI 示例
# .gitlab-ci.yml
stages:
- build
- test
- deploy
variables:
DOCKER_IMAGE: registry.example.com/myapp
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $DOCKER_IMAGE:$CI_COMMIT_SHA .
- docker push $DOCKER_IMAGE:$CI_COMMIT_SHA
test:
stage: test
image: alpine:3.20
script:
- apk add --no-cache python3
- python3 tests/run_tests.py
GitHub Actions 示例
# .github/workflows/docker.yml
name: Docker Build
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: myapp:latest
cache-from: type=gha
cache-to: type=gha,mode=max
8.9 注意事项
⚠️ 常见陷阱
apk add必须使用--no-cache避免缓存增大镜像- 安装后不要单独删除缓存,
--no-cache已自动处理- 注意 musl 与 glibc 的兼容性问题
- 生产镜像不要包含构建工具和调试工具
💡 优化清单
- 使用多阶段构建分离构建和运行
- 使用
tini或dumb-init作为 PID 1- 使用非 root 用户运行应用
- 合并 RUN 指令减少层数
- 利用
.dockerignore排除无关文件
扩展阅读
上一章:第 07 章:桌面环境 下一章:第 09 章:安全加固