1. 为什么需要从单容器走向多容器?

刚开始接触Docker时,大多数人都是从运行单个容器开始的。比如用docker run nginx启动一个Web服务器,或者用docker run mysql跑个数据库。这种单容器操作简单直接,就像在电脑上安装单个软件一样容易上手。但真实项目往往需要多个服务协同工作,比如一个典型的Web应用可能包含前端、后端、数据库、缓存等多个组件。

我刚开始用Docker部署项目时就踩过坑:手动启动了十几个容器,结果每次重启都要重新配置网络连接和端口映射,稍有不慎就会因为依赖关系导致服务启动失败。这时候就体现出Docker Compose的价值了——它让多容器管理变得像操作单个应用一样简单。

2. Docker单容器操作精要

2.1 基础命令实战

单容器操作是Docker的基石,这几个命令我每天都要用几十次:

# 构建镜像(注意最后那个点不能少)
docker build -t myapp .

# 运行容器(-d表示后台运行,-p做端口映射)
docker run -d -p 8080:80 myapp

# 查看运行中的容器
docker ps

# 查看容器日志(--tail=20表示只看最后20行)
docker logs --tail=20 container_id

最近帮团队新人排查问题时发现,很多人会忽略docker logs-f参数(实时追踪日志),这个在调试时特别有用。还有个实用技巧:用docker exec -it container_id sh进入容器内部排查问题,比反复重建镜像高效得多。

2.2 Dockerfile编写要点

好的Dockerfile就像精准的食谱,这是我总结的黄金法则:

  1. 基础镜像选择:优先选择官方镜像(如python:3.9-slim),体积小又安全
  2. 多阶段构建:用COPY --from减少最终镜像体积
  3. 层优化:把变动少的指令放前面,利用缓存加速构建

示例Dockerfile:

# 构建阶段
FROM node:16 as builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 运行阶段
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80

3. Docker Compose多容器编排

3.1 从单容器到多容器的跨越

第一次用Docker Compose部署WordPress+MySQL时,我被它的简洁震惊了。只需要一个docker-compose.yml文件:

version: '3.8'
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: example
  wordpress:
    image: wordpress
    ports:
      - "8000:80"
    depends_on:
      - db

运行docker-compose up -d就能启动整套服务,数据库和Web应用自动连接。相比手动操作,避免了以下坑:

  • 不用记住复杂的docker run参数
  • 自动创建容器间网络
  • 依赖服务自动按顺序启动

3.2 高级编排技巧

实际项目中我常用的进阶配置:

环境变量管理

services:
  app:
    env_file:
      - .env.prod
    environment:
      DEBUG: "false"

资源限制

services:
  redis:
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 500M

健康检查

services:
  web:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3

4. 实战:部署微服务应用

4.1 项目结构设计

以一个包含三个服务的电商系统为例:

ecommerce/
├── frontend/
│   ├── Dockerfile
│   └── ...
├── backend/
│   ├── Dockerfile
│   └── ...
├── database/
│   └── init.sql
└── docker-compose.yml

对应的compose文件:

version: '3.8'
services:
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    depends_on:
      backend:
        condition: service_healthy

  backend:
    build: ./backend
    environment:
      DB_URL: "postgres://db_user:db_pass@db:5432/ecom"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
    
  db:
    image: postgres:13
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: db_pass

volumes:
  db_data:

4.2 调试技巧

遇到容器启动失败时,我的排查步骤:

  1. docker-compose logs service_name 查看具体报错
  2. docker-compose config 验证配置文件语法
  3. 临时修改compose文件增加stdin_open: truetty: true进入调试模式
  4. 使用docker-compose up --force-recreate --build确保重建最新镜像

5. 生产环境注意事项

5.1 性能优化

在服务器资源有限的情况下,这些配置很关键:

services:
  redis:
    deploy:
      resources:
        reservations:
          cpus: '0.1'
          memory: 100M
        limits:
          cpus: '0.5'
          memory: 200M
    restart: unless-stopped

5.2 安全实践

从安全审计中学到的重要经验:

  1. 永远不要使用latest标签,明确指定版本号
  2. 定期扫描镜像漏洞:docker scan your_image
  3. 敏感数据使用secrets管理:
services:
  db:
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt

6. 常见问题解决方案

容器时区不对

# 在Dockerfile中
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

中文乱码问题

services:
  app:
    environment:
      LANG: C.UTF-8
    volumes:
      - /etc/localtime:/etc/localtime:ro

容器IP变动: 使用Docker Compose时直接用服务名访问(如db:3306),无需关心IP变化。这是我最喜欢的功能之一——再也不用维护复杂的hosts文件了。

最近在迁移旧项目时,把原本二十多个手动启动的容器改成了Compose编排,部署时间从半小时缩短到1分钟。这种效率提升让我更加确信:掌握Docker Compose是现代开发者必备的技能。

更多推荐