第五章:容器化技术 Docker

最后更新: 2024-01-01 作者: DevOps Team
页面目录

第五章:容器化技术 Docker

Docker 是现代云原生应用的基础,本章将深入讲解 Docker 的核心概念、Dockerfile 编写、容器编排以及安全和优化技巧。


5.1 Docker 核心概念

5.1.1 容器 vs 虚拟机

┌─────────────────────────────────┐     ┌─────────────────────────────────┐
│          虚拟机架构              │     │          容器架构                │
│                                 │     │                                 │
│  ┌───────┐ ┌───────┐ ┌───────┐ │     │   ┌───────┐ ┌───────┐ ┌───────┐ │
│  │ App 1 │ │ App 2 │ │ App 3 │ │     │   │ App 1 │ │ App 2 │ │ App 3 │ │
│  └───────┘ └───────┘ └───────┘ │     │   └───────┘ └───────┘ └───────┘ │
│  ┌───────┐ ┌───────┐ ┌───────┐ │     │   ┌───────┐ ┌───────┐ ┌───────┐ │
│  │ Bin/  │ │ Bin/  │ │ Bin/  │ │     │   │ Lib 1 │ │ Lib 2 │ │ Lib 3 │ │
│  │  Lib  │ │  Lib  │ │  Lib  │ │     │   └───────┘ └───────┘ └───────┘ │
│  └───────┘ └───────┘ └───────┘ │     │            ┌─────────┐           │
│  ┌───────────────────────────┐│     │            │Docker   │           │
│  │      Guest OS             ││     │            │Engine   │           │
│  └───────────────────────────┘│     │            └────┬────┘           │
│  ┌───────────────────────────┐│     │    ┌────────────┼────────────┐  │
│  │      Hypervisor          ││     │    │      Operating System     │  │
│  └───────────────────────────┘│     │    └────────────┬────────────┘  │
│  ┌───────────────────────────┐│     │           ┌──────┴──────┐       │
│  │      Hardware            ││     │           │   Hardware   │       │
│  └───────────────────────────┘│     │           └─────────────┘       │
│                                 │     │                                 │
│  启动慢(分钟级) 资源占用大      │     │  启动快(秒级)  资源利用率高       │
└─────────────────────────────────┘     └─────────────────────────────────┘

5.1.2 Docker 架构

┌─────────────────────────────────────────────────────────────────┐
│                          Docker Host                            │
│                                                                 │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────────────┐   │
│  │   Client    │───→│   Daemon    │───→│  Container Runtime  │   │
│  │  (docker    │    │  (dockerd)  │    │    (containerd)     │   │
│  │   CLI)      │    │             │    │                     │   │
│  └─────────────┘    └─────────────┘    └─────────────────────┘   │
│         │                                    │                   │
│         │                                    ↓                   │
│         │                           ┌─────────────────┐         │
│         │                           │   shim (runc)   │         │
│         │                           └────────┬────────┘         │
│         │                                    │                   │
│  ┌──────┴────────────────────────────────────┴──────────────┐  │
│  │                        Containers                          │  │
│  │   ┌─────────┐   ┌─────────┐   ┌─────────┐                 │  │
│  │   │Container│   │Container│   │Container│                 │  │
│  │   │    1    │   │    2    │   │    3    │                 │  │
│  │   └─────────┘   └─────────┘   └─────────┘                 │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                 │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                      Images & Volumes                     │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

5.1.3 Docker 核心组件

组件 说明
Docker Daemon 后台守护进程,负责管理容器、镜像、网络和卷
Docker Client 命令行工具,与 Daemon 通信
Registry 镜像仓库,存储和分发镜像 (Docker Hub)
Image 容器模板,只读文件系统
Container 镜像的运行实例
Volume 持久化数据存储
Network 容器网络配置

5.2 Dockerfile 编写

5.2.1 Dockerfile 基础指令

# 基础镜像
FROM python:3.11-slim

# 维护者信息 (已废弃,推荐使用 LABEL)
MAINTAINER Author Name <author@example.com>

# LABEL 方式
LABEL maintainer="author@example.com"
LABEL version="1.0"
LABEL description="Application description"

# 设置工作目录
WORKDIR /app

# 复制文件
COPY package*.json ./
COPY ./src /app/src
COPY --chown=user:group files /dest

# 添加远程文件或解压包
ADD https://example.com/file.tar.gz /tmp/
ADD archive.tar.gz /app/

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

# 环境变量
ENV NODE_ENV=production
ENV APP_PORT=8080

# 暴露端口
EXPOSE 8080 8443

# 设置启动命令
CMD ["python", "app.py"]
ENTRYPOINT ["./entrypoint.sh"]

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

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

# 多阶段构建支持
FROM golang:1.21 AS builder
WORKDIR /build
COPY . .
RUN go build -o myapp

FROM alpine:latest
COPY --from=builder /build/myapp /usr/local/bin/
CMD ["myapp"]

5.2.2 多阶段构建

# 多阶段构建示例 - Go 应用
# ========================================
# Stage 1: Builder
# ========================================
FROM golang:1.21-alpine AS builder

# 安装构建依赖
RUN apk add --no-cache git ca-certificates

WORKDIR /build

# 复制依赖清单并下载
COPY go.mod go.sum ./
RUN go mod download

# 复制源代码并构建
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
    -ldflags="-w -s" \
    -o myapp

# ========================================
# Stage 2: Runtime
# ========================================
FROM gcr.io/distroless/static-debian11 AS runtime

# 从 builder 阶段复制二进制文件
COPY --from=builder /build/myapp /myapp

# 从 builder 阶段复制非特权用户
COPY --from=builder /etc/passwd /etc/passwd

# 设置非 root 用户运行
USER nonroot:nonroot

WORKDIR /

# 暴露端口
EXPOSE 8080

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD ["wget", "-q", "--spider", "http://localhost:8080/health"]

ENTRYPOINT ["/myapp"]

5.2.3 Node.js 应用 Dockerfile

# Node.js 应用多阶段构建
# ========================================
# Stage 1: Dependencies
# ========================================
FROM node:18-alpine AS deps

WORKDIR /app

# 先复制 package 文件以利用 Docker 缓存
COPY package*.json ./
RUN npm ci --only=production

# ========================================
# Stage 2: Builder
# ========================================
FROM node:18-alpine AS builder

WORKDIR /app

# 复制依赖和源代码
COPY package*.json ./
COPY --from=deps /app/node_modules ./
COPY . .

# 构建应用
RUN npm run build

# ========================================
# Stage 3: Runner
# ========================================
FROM node:18-alpine AS runner

WORKDIR /app

# 创建非 root 用户
RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 appuser

# 复制依赖和构建产物
COPY --from=deps --chown=appuser:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:nodejs /app/dist ./dist
COPY --from=builder --chown=appuser:nodejs /app/package*.json ./

# 设置环境变量
ENV NODE_ENV=production
ENV PORT=8080

USER appuser

EXPOSE 8080

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
    CMD "node -e \"require('http').get('http://localhost:${PORT}/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))\""

CMD ["node", "dist/main.js"]

5.2.4 Java 应用 Dockerfile

# Spring Boot 应用多阶段构建
# ========================================
# Stage 1: Builder
# ========================================
FROM maven:3.9-eclipse-temurin-21 AS builder

WORKDIR /build

# 复制 pom.xml 并下载依赖
COPY pom.xml .
RUN mvn dependency:go-offline -B

# 复制源代码并构建
COPY src ./src
RUN mvn package -DskipTests -B

# ========================================
# Stage 2: Runtime
# ========================================
FROM eclipse-temurin:21-jre-alpine AS runtime

# 安装 curl 用于健康检查
RUN apk add --no-cache curl

WORKDIR /app

# 创建非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# 复制 jar 文件
COPY --from=builder --chown=appuser:appgroup /build/target/*.jar app.jar

USER appuser

EXPOSE 8080

# JVM 优化
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"

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

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

5.3 Docker Compose 编排

5.3.1 docker-compose.yml 基础

# docker-compose.yml
version: "3.9"

services:
  # 应用服务
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        - NODE_ENV=production
    image: myapp:latest
    container_name: myapp
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    volumes:
      - ./data:/app/data
      - app-logs:/app/logs
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - backend
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: "1.0"
          memory: 1G
        reservations:
          cpus: "0.5"
          memory: 512M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s

  # 数据库服务
  db:
    image: postgres:15-alpine
    container_name: postgres
    restart: unless-stopped
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=mydb
    volumes:
      - postgres-data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Redis 服务
  redis:
    image: redis:7-alpine
    container_name: redis
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data
    ports:
      - "6379:6379"
    networks:
      - backend
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Nginx 反向代理
  nginx:
    image: nginx:1.25-alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - app
    networks:
      - backend

networks:
  backend:
    driver: bridge

volumes:
  postgres-data:
    driver: local
  redis-data:
    driver: local
  app-logs:
    driver: local

5.3.2 开发和生产环境配置

# docker-compose.yml
version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - NODE_ENV=${NODE_ENV:-development}
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - "8080:8080"
# docker-compose.prod.yml
version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.prod
    environment:
      - NODE_ENV=production
    volumes:
      - app-logs:/app/logs
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    restart: unless-stopped
# 使用不同环境启动
# 开发环境
docker-compose up -d

# 生产环境
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# 扩展服务
docker-compose up -d --scale app=5

5.3.3 本地开发环境

# docker-compose.dev.yml
version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
      - DEBUG=*
    command: npm run dev
    ports:
      - "8080:8080"
      - "9229:9229"  # Node.js 调试端口
    depends_on:
      - db
      - redis

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=devdb
    volumes:
      - postgres-dev:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  redis:
    image: redis:7-alpine
    volumes:
      - redis-dev:/data
    ports:
      - "6379:6379"

  mailhog:
    image: mailhog/mailhog:latest
    ports:
      - "1025:1025"  # SMTP 服务器
      - "8025:8025"  # Web UI

volumes:
  postgres-dev:
  redis-dev:

5.4 Docker 网络

5.4.1 网络驱动

# 网络配置示例
services:
  app:
    networks:
      - frontend
      - backend
      - monitoring
  
  db:
    networks:
      - backend
  
  redis:
    networks:
      - backend
  
  prometheus:
    networks:
      - monitoring

networks:
  frontend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16
  
  backend:
    driver: bridge
    internal: true  # 禁止外部访问
  
  monitoring:
    driver: bridge
    attachable: true

5.4.2 容器间通信

# 创建自定义网络
docker network create my-network

# 启动容器并加入网络
docker run -d --name app --network my-network myapp:latest

# 连接已运行容器到网络
docker network connect my-network db

# 查看网络信息
docker network inspect my-network

# 断开容器与网络
docker network disconnect my-network db

5.5 Docker 镜像优化

5.5.1 优化策略

优化策略 说明 效果
使用 Alpine 基础镜像 更小的镜像体积 减少 90%+
多阶段构建 分离构建和运行环境 减少 50%+
合并 RUN 指令 减少镜像层数 减少层数
.dockerignore 排除不需要的文件 减少构建上下文
并行下载 利用 BuildKit 加快构建速度
顺序优化 合理安排指令顺序 利用缓存

5.5.2 .dockerignore 示例

# .dockerignore

# 版本控制
.git
.gitignore
.env.local

# 文档
*.md
docs/

# IDE
.idea/
.vscode/
*.swp
*.swo

# 测试和覆盖率
coverage/
.nyc_output/
test/
tests/
__tests__/
*.test.js
*.spec.js

# 构建产物
dist/
build/
.next/
.nuxt/
out/

# 依赖
node_modules/
vendor/
bower_components/

# 日志
logs/
*.log
npm-debug.log*

# 其他
.DS_Store
Thumbs.db
*.tmp
*.temp

5.6 Docker 安全

5.6.1 安全最佳实践

# 安全 Dockerfile 示例
# 使用特定版本标签,不使用 latest
FROM node:18.17.0-alpine3.18

# 使用非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# 只复制必要文件
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci --only=production && npm cache clean --force

COPY --chown=appuser:appgroup . .

# 设置只读文件系统 (可选)
# ReadonlyRootFilesystem: true

USER appuser

# 不使用 root 运行服务
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1

CMD ["node", "server.js"]

5.6.2 安全扫描

# 使用 Trivy 扫描镜像漏洞
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
    aquasec/trivy:latest image myapp:latest

# 使用 Docker Scout
docker scout cves myapp:latest

# 使用 Snyk
docker run --rm -it \
    -e SNYK_TOKEN=$SNYK_TOKEN \
    snyk/snyk:docker test myapp:latest

# Docker CIS 检查
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
    aquasec/docker-bench:latest

5.7 本章小结

主题 关键点
Dockerfile 多阶段构建、层缓存优化、非 root 用户
Docker Compose 服务编排、环境配置、依赖管理
网络 bridge、host、overlay 网络模式
安全 漏洞扫描、最小权限、安全配置

📌 下一章预告

下一章我们将学习 Kubernetes 容器编排,包括:

  • Kubernetes 架构与核心概念
  • Pod、Deployment、Service 详解
  • Helm 图表管理
  • 生产环境部署策略

💡 提示:始终使用最小化基础镜像,定期扫描漏洞,并遵循"最小权限"原则配置容器。