docker-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Docker Patterns

Docker 模式

Docker and Docker Compose best practices for containerized development.
面向容器化开发的Docker和Docker Compose最佳实践。

When to Activate

适用场景

  • Setting up Docker Compose for local development
  • Designing multi-container architectures
  • Troubleshooting container networking or volume issues
  • Reviewing Dockerfiles for security and size
  • Migrating from local dev to containerized workflow
  • 为本地开发搭建Docker Compose环境
  • 设计多容器架构
  • 排查容器网络或卷相关问题
  • 审查Dockerfile的安全性与镜像大小
  • 从本地开发迁移至容器化工作流

Docker Compose for Local Development

用于本地开发的Docker Compose

Standard Web App Stack

标准Web应用栈

yaml
undefined
yaml
undefined

docker-compose.yml

docker-compose.yml

services: app: build: context: . target: dev # Use dev stage of multi-stage Dockerfile ports: - "3000:3000" volumes: - .:/app # Bind mount for hot reload - /app/node_modules # Anonymous volume -- preserves container deps environment: - DATABASE_URL=postgres://postgres:postgres@db:5432/app_dev - REDIS_URL=redis://redis:6379/0 - NODE_ENV=development depends_on: db: condition: service_healthy redis: condition: service_started command: npm run dev
db: image: postgres:16-alpine ports: - "5432:5432" environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: app_dev volumes: - pgdata:/var/lib/postgresql/data - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 3s retries: 5
redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redisdata:/data
mailpit: # Local email testing image: axllent/mailpit ports: - "8025:8025" # Web UI - "1025:1025" # SMTP
volumes: pgdata: redisdata:
undefined
services: app: build: context: . target: dev # Use dev stage of multi-stage Dockerfile ports: - "3000:3000" volumes: - .:/app # Bind mount for hot reload - /app/node_modules # Anonymous volume -- preserves container deps environment: - DATABASE_URL=postgres://postgres:postgres@db:5432/app_dev - REDIS_URL=redis://redis:6379/0 - NODE_ENV=development depends_on: db: condition: service_healthy redis: condition: service_started command: npm run dev
db: image: postgres:16-alpine ports: - "5432:5432" environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: app_dev volumes: - pgdata:/var/lib/postgresql/data - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 3s retries: 5
redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redisdata:/data
mailpit: # Local email testing image: axllent/mailpit ports: - "8025:8025" # Web UI - "1025:1025" # SMTP
volumes: pgdata: redisdata:
undefined

Development vs Production Dockerfile

开发与生产环境Dockerfile对比

dockerfile
undefined
dockerfile
undefined

Stage: dependencies

Stage: dependencies

FROM node:22-alpine AS deps WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci
FROM node:22-alpine AS deps WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci

Stage: dev (hot reload, debug tools)

Stage: dev (hot reload, debug tools)

FROM node:22-alpine AS dev WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . EXPOSE 3000 CMD ["npm", "run", "dev"]
FROM node:22-alpine AS dev WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . EXPOSE 3000 CMD ["npm", "run", "dev"]

Stage: build

Stage: build

FROM node:22-alpine AS build WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . RUN npm run build && npm prune --production
FROM node:22-alpine AS build WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . RUN npm run build && npm prune --production

Stage: production (minimal image)

Stage: production (minimal image)

FROM node:22-alpine AS production WORKDIR /app RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001 USER appuser COPY --from=build --chown=appuser:appgroup /app/dist ./dist COPY --from=build --chown=appuser:appgroup /app/node_modules ./node_modules COPY --from=build --chown=appuser:appgroup /app/package.json ./ ENV NODE_ENV=production EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:3000/health || exit 1 CMD ["node", "dist/server.js"]
undefined
FROM node:22-alpine AS production WORKDIR /app RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001 USER appuser COPY --from=build --chown=appuser:appgroup /app/dist ./dist COPY --from=build --chown=appuser:appgroup /app/node_modules ./node_modules COPY --from=build --chown=appuser:appgroup /app/package.json ./ ENV NODE_ENV=production EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:3000/health || exit 1 CMD ["node", "dist/server.js"]
undefined

Override Files

覆盖文件

yaml
undefined
yaml
undefined

docker-compose.override.yml (auto-loaded, dev-only settings)

docker-compose.override.yml (auto-loaded, dev-only settings)

services: app: environment: - DEBUG=app:* - LOG_LEVEL=debug ports: - "9229:9229" # Node.js debugger
services: app: environment: - DEBUG=app:* - LOG_LEVEL=debug ports: - "9229:9229" # Node.js debugger

docker-compose.prod.yml (explicit for production)

docker-compose.prod.yml (explicit for production)

services: app: build: target: production restart: always deploy: resources: limits: cpus: "1.0" memory: 512M

```bash
services: app: build: target: production restart: always deploy: resources: limits: cpus: "1.0" memory: 512M

```bash

Development (auto-loads override)

Development (auto-loads override)

docker compose up
docker compose up

Production

Production

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
undefined
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
undefined

Networking

网络配置

Service Discovery

服务发现

Services in the same Compose network resolve by service name:
undefined
同一Compose网络中的服务可通过服务名称解析:
undefined

From "app" container:

From "app" container:

postgres://postgres:postgres@db:5432/app_dev # "db" resolves to the db container redis://redis:6379/0 # "redis" resolves to the redis container
undefined
postgres://postgres:postgres@db:5432/app_dev # "db" resolves to the db container redis://redis:6379/0 # "redis" resolves to the redis container
undefined

Custom Networks

自定义网络

yaml
services:
  frontend:
    networks:
      - frontend-net

  api:
    networks:
      - frontend-net
      - backend-net

  db:
    networks:
      - backend-net              # Only reachable from api, not frontend

networks:
  frontend-net:
  backend-net:
yaml
services:
  frontend:
    networks:
      - frontend-net

  api:
    networks:
      - frontend-net
      - backend-net

  db:
    networks:
      - backend-net              # Only reachable from api, not frontend

networks:
  frontend-net:
  backend-net:

Exposing Only What's Needed

仅暴露必要端口

yaml
services:
  db:
    ports:
      - "127.0.0.1:5432:5432"   # Only accessible from host, not network
    # Omit ports entirely in production -- accessible only within Docker network
yaml
services:
  db:
    ports:
      - "127.0.0.1:5432:5432"   # Only accessible from host, not network
    # Omit ports entirely in production -- accessible only within Docker network

Volume Strategies

卷策略

yaml
volumes:
  # Named volume: persists across container restarts, managed by Docker
  pgdata:

  # Bind mount: maps host directory into container (for development)
  # - ./src:/app/src

  # Anonymous volume: preserves container-generated content from bind mount override
  # - /app/node_modules
yaml
volumes:
  # Named volume: persists across container restarts, managed by Docker
  pgdata:

  # Bind mount: maps host directory into container (for development)
  # - ./src:/app/src

  # Anonymous volume: preserves container-generated content from bind mount override
  # - /app/node_modules

Common Patterns

常见模式

yaml
services:
  app:
    volumes:
      - .:/app                   # Source code (bind mount for hot reload)
      - /app/node_modules        # Protect container's node_modules from host
      - /app/.next               # Protect build cache

  db:
    volumes:
      - pgdata:/var/lib/postgresql/data          # Persistent data
      - ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql  # Init scripts
yaml
services:
  app:
    volumes:
      - .:/app                   # Source code (bind mount for hot reload)
      - /app/node_modules        # Protect container's node_modules from host
      - /app/.next               # Protect build cache

  db:
    volumes:
      - pgdata:/var/lib/postgresql/data          # Persistent data
      - ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql  # Init scripts

Container Security

容器安全

Dockerfile Hardening

Dockerfile 加固

dockerfile
undefined
dockerfile
undefined

1. Use specific tags (never :latest)

1. Use specific tags (never :latest)

FROM node:22.12-alpine3.20
FROM node:22.12-alpine3.20

2. Run as non-root

2. Run as non-root

RUN addgroup -g 1001 -S app && adduser -S app -u 1001 USER app
RUN addgroup -g 1001 -S app && adduser -S app -u 1001 USER app

3. Drop capabilities (in compose)

3. Drop capabilities (in compose)

4. Read-only root filesystem where possible

4. Read-only root filesystem where possible

5. No secrets in image layers

5. No secrets in image layers

undefined
undefined

Compose Security

Compose 安全配置

yaml
services:
  app:
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp
      - /app/.cache
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE          # Only if binding to ports < 1024
yaml
services:
  app:
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp
      - /app/.cache
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE          # Only if binding to ports < 1024

Secret Management

密钥管理

yaml
undefined
yaml
undefined

GOOD: Use environment variables (injected at runtime)

GOOD: Use environment variables (injected at runtime)

services: app: env_file: - .env # Never commit .env to git environment: - API_KEY # Inherits from host environment
services: app: env_file: - .env # Never commit .env to git environment: - API_KEY # Inherits from host environment

GOOD: Docker secrets (Swarm mode)

GOOD: Docker secrets (Swarm mode)

secrets: db_password: file: ./secrets/db_password.txt
services: db: secrets: - db_password
secrets: db_password: file: ./secrets/db_password.txt
services: db: secrets: - db_password

BAD: Hardcoded in image

BAD: Hardcoded in image

ENV API_KEY=sk-proj-xxxxx # NEVER DO THIS

ENV API_KEY=sk-proj-xxxxx # NEVER DO THIS

undefined
undefined

.dockerignore

.dockerignore 文件

node_modules
.git
.env
.env.*
dist
coverage
*.log
.next
.cache
docker-compose*.yml
Dockerfile*
README.md
tests/
node_modules
.git
.env
.env.*
dist
coverage
*.log
.next
.cache
docker-compose*.yml
Dockerfile*
README.md
tests/

Debugging

调试方法

Common Commands

常用命令

bash
undefined
bash
undefined

View logs

View logs

docker compose logs -f app # Follow app logs docker compose logs --tail=50 db # Last 50 lines from db
docker compose logs -f app # Follow app logs docker compose logs --tail=50 db # Last 50 lines from db

Execute commands in running container

Execute commands in running container

docker compose exec app sh # Shell into app docker compose exec db psql -U postgres # Connect to postgres
docker compose exec app sh # Shell into app docker compose exec db psql -U postgres # Connect to postgres

Inspect

Inspect

docker compose ps # Running services docker compose top # Processes in each container docker stats # Resource usage
docker compose ps # Running services docker compose top # Processes in each container docker stats # Resource usage

Rebuild

Rebuild

docker compose up --build # Rebuild images docker compose build --no-cache app # Force full rebuild
docker compose up --build # Rebuild images docker compose build --no-cache app # Force full rebuild

Clean up

Clean up

docker compose down # Stop and remove containers docker compose down -v # Also remove volumes (DESTRUCTIVE) docker system prune # Remove unused images/containers
undefined
docker compose down # Stop and remove containers docker compose down -v # Also remove volumes (DESTRUCTIVE) docker system prune # Remove unused images/containers
undefined

Debugging Network Issues

排查网络问题

bash
undefined
bash
undefined

Check DNS resolution inside container

Check DNS resolution inside container

docker compose exec app nslookup db
docker compose exec app nslookup db

Check connectivity

Check connectivity

docker compose exec app wget -qO- http://api:3000/health
docker compose exec app wget -qO- http://api:3000/health

Inspect network

Inspect network

docker network ls docker network inspect <project>_default
undefined
docker network ls docker network inspect <project>_default
undefined

Anti-Patterns

反模式

undefined
undefined

BAD: Using docker compose in production without orchestration

BAD: Using docker compose in production without orchestration

Use Kubernetes, ECS, or Docker Swarm for production multi-container workloads

Use Kubernetes, ECS, or Docker Swarm for production multi-container workloads

BAD: Storing data in containers without volumes

BAD: Storing data in containers without volumes

Containers are ephemeral -- all data lost on restart without volumes

Containers are ephemeral -- all data lost on restart without volumes

BAD: Running as root

BAD: Running as root

Always create and use a non-root user

Always create and use a non-root user

BAD: Using :latest tag

BAD: Using :latest tag

Pin to specific versions for reproducible builds

Pin to specific versions for reproducible builds

BAD: One giant container with all services

BAD: One giant container with all services

Separate concerns: one process per container

Separate concerns: one process per container

BAD: Putting secrets in docker-compose.yml

BAD: Putting secrets in docker-compose.yml

Use .env files (gitignored) or Docker secrets

Use .env files (gitignored) or Docker secrets

undefined
undefined