强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Alpine Linux 完全指南 / 第 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
指标AlpineDebian SlimUbuntu
基础镜像大小~5 MB~74 MB~78 MB
包数量~14,000~59,000~60,000
拉取时间 (100Mbps)<1s~6s~6s
安全漏洞数
启动速度极快
musl 兼容性完美N/AN/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="dev@example.com"
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 镜像

语言基础镜像大小
Gogolang:1.22-alpine~250 MB (构建)
Node.jsnode:20-alpine~130 MB
Pythonpython:3.12-alpine~50 MB
Javaeclipse-temurin:21-jre-alpine~150 MB
Rubyruby:3.3-alpine~50 MB
PHPphp:8.3-fpm-alpine~85 MB
Rustrust: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 的兼容性问题
  • 生产镜像不要包含构建工具和调试工具

💡 优化清单

  • 使用多阶段构建分离构建和运行
  • 使用 tinidumb-init 作为 PID 1
  • 使用非 root 用户运行应用
  • 合并 RUN 指令减少层数
  • 利用 .dockerignore 排除无关文件

扩展阅读


上一章第 07 章:桌面环境 下一章第 09 章:安全加固