Docker 容器化
概览
构建遵循安全、性能和可维护性最佳实践的生产就绪 Docker 容器。
何时使用
- 部署时容器化应用程序
- 为新服务创建 Dockerfile
- 优化现有容器镜像
- 设置开发环境
- 构建 CI/CD 容器管道
- 实施微服务
指令
1. 多阶段构建
# 多阶段构建 Node.js 应用程序
# 第一阶段:构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 第二阶段:生产
FROM node:18-alpine
WORKDIR /app
# 仅复制生产依赖项和构建文件
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package*.json ./
# 安全:以非根用户运行
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
USER nodejs
EXPOSE 3000
CMD ["node", "dist/index.js"]
2. 优化技术
层缓存
# ❌ 缓存不佳 - 源代码更改使依赖安装无效
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
# ✅ 好的缓存 - 依赖项分别缓存
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
最小化镜像大小
# ❌ 大镜像 (~800MB)
FROM ubuntu:latest
RUN apt-get update && apt-get install -y python3 python3-pip
# ✅ 最小镜像 (~50MB)
FROM python:3.11-alpine
3. 安全最佳实践
FROM node:18-alpine
# 更新包以获取安全补丁
RUN apk update && apk upgrade
# 不要以 root 用户运行
RUN addgroup -g 1001 appgroup && adduser -S -u 1001 -G appgroup appuser
USER appuser
# 使用特定版本,而不是 'latest'
WORKDIR /app
# 扫描漏洞
# 运行:docker scan your-image:tag
4. 环境配置
# 使用构建参数以增加灵活性
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
CMD node healthcheck.js || exit 1
# 元数据标签
LABEL maintainer="team@example.com" \
version="1.0.0" \
description="生产 API 服务"
5. Docker Compose 用于多容器
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
NODE_ENV: production
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- app-network
volumes:
- ./uploads:/app/uploads
restart: unless-stopped
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: password
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis-data:/data
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
postgres-data:
redis-data:
6. .dockerignore 文件
# .dockerignore
node_modules
npm-debug.log
dist
.git
.env
.env.local
*.md
!README.md
.DS_Store
coverage
.vscode
.idea
__pycache__
*.pyc
.pytest_cache
最佳实践
✅ 执行
- 使用官方基础镜像
- 实施多阶段构建
- 以非 root 用户运行
- 使用 .dockerignore
- 固定特定版本
- 包括健康检查
- 扫描漏洞
- 最小化层数
- 有效使用构建缓存
❌ 不要执行
- 在生产中使用 ‘latest’ 标签
- 以 root 用户运行
- 在镜像中包含机密
- 创建不必要的层
- 安装不必要的包
- 忽略安全更新
- 在容器中存储数据
按语言示例
Python (Django/Flask)
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
Java (Spring Boot)
FROM eclipse-temurin:17-jdk-alpine AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN ./mvnw package -DskipTests
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
RUN addgroup -S spring && adduser -S spring -G spring
USER spring
ENTRYPOINT ["java", "-jar", "app.jar"]
Go
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
CMD ["./main"]
有用命令
# 构建镜像
docker build -t myapp:1.0.0 .
# 禁用缓存构建
docker build --no-cache -t myapp:1.0.0 .
# 运行容器
docker run -d -p 3000:3000 --name myapp myapp:1.0.0
# 查看日志
docker logs -f myapp
# 在容器中执行命令
docker exec -it myapp sh
# 检查镜像层
docker history myapp:1.0.0
# 检查镜像大小
docker images myapp
# 清理
docker system prune -a
故障排除
容器立即退出:
docker logs container-name
docker inspect container-name
构建失败:
docker build --progress=plain -t myapp .
权限问题: 确保正确设置用户和卷权限。
大镜像大小:
- 使用 alpine 基础镜像
- 实施多阶段构建
- 移除不必要的文件
- 使用 .dockerignore