引言

Docker已经成为现代软件开发和部署的标准工具。通过容器化,我们可以创建一致的开发环境、简化部署流程、提高资源利用率。本文将从Docker的基础概念开始,逐步深入到生产环境的最佳实践,帮助你全面掌握Docker技术。

Docker核心概念

1. 镜像(Image)

Docker镜像是创建容器的基础,是一个只读的模板:

1
2
3
4
5
6
7
8
9
# 拉取官方镜像
docker pull nginx:latest
docker pull node:18-alpine

# 查看本地镜像
docker images

# 删除镜像
docker rmi nginx:latest

2. 容器(Container)

容器是镜像的运行实例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 运行容器
docker run -d --name my-nginx -p 8080:80 nginx

# 查看运行中的容器
docker ps

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

# 停止容器
docker stop my-nginx

# 启动容器
docker start my-nginx

# 删除容器
docker rm my-nginx

3. 仓库(Repository)

Docker仓库是存储和分发镜像的地方:

1
2
3
4
5
6
7
8
# 登录Docker Hub
docker login

# 推送镜像
docker push username/my-app:1.0

# 拉取私有镜像
docker pull private-registry/my-app:latest

Dockerfile最佳实践

基础镜像选择

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# ✅ 使用官方基础镜像
FROM node:18-alpine

# ✅ 使用多阶段构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

优化Dockerfile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# ❌ 不好的实践
FROM node:18

# 多个RUN指令创建多个层
RUN apt-get update
RUN apt-get install -y python
RUN npm install

# 使用COPY . . 导致缓存失效
COPY . .
RUN npm install

# ✅ 好的实践
FROM node:18-alpine

# 合并RUN指令,减少层数
RUN apk add --no-cache python3 make g++ && \
    npm install -g nodemon && \
    npm cache clean --force

# 优化COPY顺序,先复制依赖文件
COPY package*.json ./
RUN npm ci --only=production

# 最后复制源代码
COPY . .

# 非root用户运行
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs

EXPOSE 3000
CMD ["npm", "start"]

多阶段构建示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app

# 安装依赖
COPY package*.json ./
RUN npm ci

# 构建应用
COPY . .
RUN npm run build

# 生产阶段
FROM nginx:alpine AS production

# 复制构建产物
COPY --from=builder /app/dist /usr/share/nginx/html

# 复制nginx配置
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Docker Compose

基础配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DB_HOST=database
      - REDIS_HOST=redis
    depends_on:
      - database
      - redis
    volumes:
      - ./logs:/app/logs
    restart: unless-stopped

  database:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

开发环境配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# docker-compose.dev.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
      - "9229:9229"  # Debug端口
    environment:
      - NODE_ENV=development
      - CHOKIDAR_USEPOLLING=true
    volumes:
      - .:/app
      - /app/node_modules
    command: npm run dev

生产环境配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# docker-compose.prod.yml
version: '3.8'

services:
  app:
    image: myapp:latest
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
    environment:
      - NODE_ENV=production
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - app

常用Docker命令

镜像管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 构建镜像
docker build -t myapp:1.0 .
docker build -t myapp:latest -f Dockerfile.prod .

# 查看镜像历史
docker history myapp:1.0

# 导出镜像
docker save -o myapp.tar myapp:1.0

# 导入镜像
docker load -i myapp.tar

# 标记镜像
docker tag myapp:1.0 username/myapp:1.0

容器管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 运行容器
docker run -d \
  --name myapp \
  -p 3000:3000 \
  -e NODE_ENV=production \
  -v /app/logs:/app/logs \
  --restart unless-stopped \
  myapp:1.0

# 进入容器
docker exec -it myapp sh

# 查看容器日志
docker logs myapp
docker logs -f myapp  # 实时日志

# 查看容器资源使用
docker stats
docker stats myapp

# 复制文件
docker cp myapp:/app/logs ./logs
docker cp ./config myapp:/app/config

网络管理

1
2
3
4
5
6
7
8
9
# 创建网络
docker network create myapp-network

# 连接容器到网络
docker network connect myapp-network myapp

# 查看网络
docker network ls
docker network inspect myapp-network

数据持久化

数据卷

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 创建数据卷
docker volume create app-data

# 挂载数据卷
docker run -v app-data:/app/data myapp

# 查看数据卷
docker volume ls
docker volume inspect app-data

# 备份数据卷
docker run --rm -v app-data:/data -v $(pwd):/backup \
  alpine tar czf /backup/backup.tar.gz -C /data .

# 恢复数据卷
docker run --rm -v app-data:/data -v $(pwd):/backup \
  alpine tar xzf /backup/backup.tar.gz -C /data

绑定挂载

1
2
3
4
5
6
7
8
# 挂载本地目录
docker run -v $(pwd)/logs:/app/logs myapp

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

# 临时文件系统
docker run --tmpfs /app/temp myapp

性能优化

镜像大小优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# ✅ 使用alpine版本
FROM node:18-alpine

# ✅ 使用.npmrc减少依赖大小
RUN echo 'package-lock=false' > .npmrc && \
    echo 'audit=false' >> .npmrc

# ✅ 多阶段构建减少最终镜像
FROM node:18-alpine AS builder
# 构建步骤...

FROM node:18-alpine AS runtime
# 只复制必要文件
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules

构建缓存优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# ✅ 优化COPY顺序
COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# ✅ 使用BuildKit缓存挂载
# docker build --mount=type=cache,target=/root/.npm -t myapp .
RUN --mount=type=cache,target=/root/.npm \
    npm ci

运行时优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# docker-compose.yml
version: '3.8'

services:
  app:
    image: myapp:1.0
    # 资源限制
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M
    # 健康检查
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

安全最佳实践

镜像安全

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# ✅ 使用非root用户
FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs

# ✅ 使用多阶段构建
FROM node:18-alpine AS builder
# 构建步骤...

FROM node:18-alpine AS runtime
# 生产阶段...

# ✅ 最小化安装
RUN apk add --no-cache dumb-init

运行时安全

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# ✅ 只开放必要端口
docker run -p 3000:3000 myapp

# ✅ 只读文件系统
docker run --read-only --tmpfs /tmp myapp

# ✅ 限制特权
docker run --user 1001:1001 --cap-drop ALL myapp

# ✅ 安全扫描
docker scan myapp:1.0

# ✅ 使用AppArmor/SELinux
docker run --security-opt apparmor:myapp-profile myapp

网络安全

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# docker-compose.yml
version: '3.8'

services:
  app:
    image: myapp:1.0
    networks:
      - frontend
      - backend
    # 不暴露端口到主机

  database:
    image: postgres:15
    networks:
      - backend
    # 只有app可以访问数据库

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # 内部网络,不能访问外网

CI/CD集成

GitHub Actions示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# .github/workflows/docker.yml
name: Build and Push Docker Image

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3

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

    - name: Login to Docker Hub
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}

    - name: Build and push
      uses: docker/build-push-action@v4
      with:
        context: .
        push: true
        tags: |
          username/myapp:latest
          username/myapp:${{ github.sha }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

GitLab CI示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# .gitlab-ci.yml
stages:
  - build
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"

build:
  stage: build
  image: docker:20.10
  services:
    - docker:20.10-dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  only:
    - main

deploy:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache openssh-client
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
  script:
    - ssh deploy@server "cd /app && docker-compose pull && docker-compose up -d"
  only:
    - main

监控和日志

日志管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# docker-compose.yml
version: '3.8'

services:
  app:
    image: myapp:1.0
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    labels:
      - "logging=promtail"

  loki:
    image: grafana/loki:2.8
    ports:
      - "3100:3100"
    volumes:
      - ./loki-config.yml:/etc/loki/local-config.yaml

  promtail:
    image: grafana/promtail:2.8
    volumes:
      - /var/log:/var/log
      - ./promtail-config.yml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml

监控集成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# monitoring.yml
version: '3.8'

services:
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3001:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-storage:/var/lib/grafana

  node-exporter:
    image: prom/node-exporter:latest
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro

volumes:
  grafana-storage:

故障排查

常见问题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 查看容器详细信息
docker inspect myapp

# 查看容器内进程
docker exec myapp ps aux

# 查看端口占用
docker port myapp

# 进入调试模式
docker run -it --entrypoint sh myapp

# 查看资源使用
docker stats --no-stream myapp

# 清理未使用的资源
docker system prune -a
docker volume prune
docker network prune

性能分析

1
2
3
4
5
6
7
8
9
# 使用cAdvisor
docker run -d \
  --name=cadvisor \
  -p 8080:8080 \
  -v /:/rootfs:ro \
  -v /var/run:/var/run:rw \
  -v /sys:/sys:ro \
  -v /var/lib/docker/:/var/lib/docker:ro \
  gcr.io/cadvisor/cadvisor:latest

总结

Docker容器化技术的核心要点:

基础概念:

  1. 理解镜像、容器、仓库的关系
  2. 掌握Dockerfile编写技巧
  3. 学会使用Docker Compose编排多容器应用

最佳实践:

  1. 使用多阶段构建优化镜像大小
  2. 实施数据持久化策略
  3. 遵循安全最佳实践
  4. 建立监控和日志系统

生产部署:

  1. 集成CI/CD流水线
  2. 实施健康检查
  3. 配置资源限制
  4. 建立备份和恢复策略

掌握Docker将大大简化你的开发和部署流程,提高应用的可移植性和可扩展性。


相关资源: