目录

Docker 最佳实践

快速入门

安装与版本

当前使用版本为 Docker 29.4

macOS

Docker Desktop 是 macOS 推荐的方式:

# 使用 Homebrew 安装
brew install --cask docker

# 或下载 Docker Desktop for Mac
# 访问 https://docs.docker.com/desktop/install/mac-install/

# 启动 Docker Desktop
open -a Docker

# 验证安装
docker --version

# 运行 hello world
docker run hello-world

Windows

下载并安装 Docker Desktop for Windows

# 运行安装程序 Docker Desktop Installer.exe
# 启动 Docker Desktop
docker --version

# 运行 hello world
docker run hello-world

Linux (Ubuntu)

# 更新 apt 包索引
sudo apt update

# 安装必要的前置依赖
sudo apt install -y ca-certificates curl gnupg lsb-release

# 添加 Docker GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# 添加 Docker 仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装 Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 验证安装
sudo docker --version

# 运行 hello world(无需 sudo)
sudo usermod -aG docker $USER
docker run hello-world

Dockerfile 基础

基本结构

# 基础镜像
FROM python:3.12-slim

# 维护者
LABEL maintainer="[email protected]"

# 工作目录
WORKDIR /app

# 复制文件
COPY requirements.txt .

# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 暴露端口
EXPOSE 8000

# 启动命令
CMD ["python", "app.py"]

常见指令

指令 说明
FROM 指定基础镜像
RUN 执行命令
COPY 复制文件
ADD 复制和解压(支持远程 URL)
WORKDIR 设置工作目录
EXPOSE 声明端口
ENV 环境变量
CMD 容器启动命令
ENTRYPOINT 入口点

多阶段构建

减少镜像大小,只保留运行时需要的内容:

Node.js 示例

# 构建阶段
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build

# 运行阶段
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]

Go 示例

# 构建阶段
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .

# 运行阶段
FROM alpine:3.19
WORKDIR /app
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]

.dockerignore

排除不需要的文件:

# 版本控制
.git
.gitignore

# 依赖
node_modules
__pycache__
*.pyc

# 文档
README.md
docs

# IDE
.vscode
.idea

# 测试
*.test
coverage

# 环境文件
.env
.env.*

# 构建产物
dist
build
*.log

镜像优化

使用特定版本标签

# 避免使用 latest(可能导致不确定行为)
FROM node:20-alpine      # 指定版本
FROM python:3.12-slim    # 使用 slim 变体减小体积

合并指令

# 低效
RUN pip install flask
RUN pip install requests
RUN pip install redis

# 高效
RUN pip install flask requests redis

顺序优化

# 将不常变化的层放前面
COPY package*.json ./
RUN npm ci

# 经常变化的放后面
COPY . .

使用 heredoc

# 更易读的 RUN 指令
RUN <<EOF
apt-get update
apt-get install -y curl
apt-get clean
EOF

网络

暴露端口

# 暴露单个端口
EXPOSE 8000

# 暴露多个端口
EXPOSE 8080 8443

运行容器

# 映射端口
docker run -p 8080:8000 myapp

# 映射所有暴露的端口
docker run -P myapp

数据管理

数据卷

# 创建数据卷
docker volume create mydata

# 运行容器并挂载数据卷
docker run -v mydata:/app/data myapp

# 或使用匿名卷
docker run -v /app/data myapp

绑定挂载

# 绑定本地目录
docker run -v $(pwd):/app myapp

# 只读挂载
docker run -v $(pwd):/app:ro myapp

tmpfs 挂载(内存)

# 将数据存储在内存中
docker run --tmpfs /app/tmp myapp

环境变量

设置环境变量

# 构建时设置
ENV NODE_ENV=production
ENV PORT=8080

运行时不覆盖

ENV APP_VERSION=1.0.0
# 运行时可以用 -e 覆盖

使用 ARG 和 ENV

# ARG 只在构建时生效
ARG APP_ENV=production

# ENV 在构建和运行时都生效
ENV APP_ENV=${APP_ENV}

安全最佳实践

避免 root 用户运行

# 创建非 root 用户
RUN groupadd -r appgroup && useradd -r -g appgroup appuser

USER appuser

# 或者使用 numeric UID
USER 1000:1000

扫描漏洞

# 使用 Trivy 扫描
brew install trivy
trivy image myapp:latest

# 使用 Docker Scout
docker scout cves myapp:latest

健康检查

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8000/health || exit 1

安全基础镜像

# 使用官方认证镜像
FROM python:3.12-slim-bookworm

# 或使用 distroless
FROM gcr.io/distroless/nodejs20-debian12

Docker Compose

基本配置

# docker-compose.yml


services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - NODE_ENV=production
    volumes:
      - ./data:/app/data
    depends_on:
      - db
      - redis
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db_data:/var/lib/postgresql/data
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    restart: unless-stopped

volumes:
  db_data:

调试

进入容器

# 交互式 shell
docker run -it myapp /bin/sh

# 在运行中的容器执行
docker exec -it mycontainer /bin/bash

查看日志

# 查看日志
docker logs mycontainer

# 实时跟踪
docker logs -f mycontainer

# 最近日志
docker logs --tail 100 mycontainer

检查容器

# 查看运行状态
docker ps

# 查看所有容器(包括停止)
docker ps -a

# 检查容器详细信息
docker inspect mycontainer

# 查看资源使用
docker stats

CI/CD 集成

GitHub Actions

# .github/workflows/docker.yml
name: Docker

on:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: user/myapp:latest,user/myapp:${{ github.sha }}

相关资源