第五章:容器化技术 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 图表管理
- 生产环境部署策略
💡 提示:始终使用最小化基础镜像,定期扫描漏洞,并遵循"最小权限"原则配置容器。