Docker Compose一键启动微服务集群:别再手动敲命令了

上周线上部署一个微服务项目,折腾了整整一下午。

三个服务:应用服务、数据库、Redis。正常操作应该是:先起数据库,等它ready,再起Redis,最后起应用服务。但我敲命令的时候手滑了一下,顺序搞反了,结果应用服务一直报连接失败。

查日志、重启、再查日志……搞了两个小时才发现启动顺序的问题。

那一刻我真的想抽自己——为什么不早点用Docker Compose?

今天聊聊怎么用Docker Compose一键启动微服务集群,省得像我一样折腾。


为什么要用Docker Compose?

先说说不用Docker Compose的痛苦。

假设你要启动一个简单的微服务集群:Nginx + 后端API + MySQL + Redis。手动部署的流程大概是这样的:

# 启动MySQL
docker run -d --name mysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v mysql-data:/var/lib/mysql \
  mysql:8.0

# 等MySQL启动……sleep 10秒(这招真的不靠谱)

# 启动Redis
docker run -d --name redis redis:7.0

# 启动后端服务
docker run -d --name api \
  --link mysql \
  --link redis \
  -e DB_HOST=mysql \
  -e REDIS_HOST=redis \
  my-api:latest

# 启动Nginx
docker run -d -p 80:80 \
  --link api \
  -v /path/to/nginx.conf:/etc/nginx/nginx.conf \
  nginx:latest

问题在哪?

  1. 启动顺序难控制——你不知道MySQL什么时候真的ready了,sleep是赌博
  2. 命令太长——敲错一个字母就重来
  3. 网络配置麻烦——--link已经被废弃了,现在得自己搞network
  4. 没有统一入口——重启、停止、查看日志得分开操作每个容器

Docker Compose解决的正是这些问题。


一个完整的docker-compose.yml

这是我那个项目的配置文件,拿出来给你参考:

version: '3.8'

services:
  # MySQL数据库
  mysql:
    image: mysql:8.0
    container_name: project-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: appdb
      TZ: Asia/Shanghai
    volumes:
      - mysql-data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p123456"]
      interval: 5s
      timeout: 10s
      retries: 5
    networks:
      - app-network

  # Redis缓存
  redis:
    image: redis:7.0-alpine
    container_name: project-redis
    restart: always
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 3
    networks:
      - app-network

  # 后端API服务
  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    container_name: project-api
    restart: always
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    environment:
      DB_HOST: mysql
      DB_PORT: 3306
      DB_USER: root
      DB_PASSWORD: 123456
      REDIS_HOST: redis
      REDIS_PORT: 6379
    ports:
      - "8000:8000"
    networks:
      - app-network

  # Nginx反向代理
  nginx:
    image: nginx:latest
    container_name: project-nginx
    restart: always
    depends_on:
      - api
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/logs:/var/log/nginx
    ports:
      - "80:80"
    networks:
      - app-network

# 数据卷
volumes:
  mysql-data:
    driver: local
  redis-data:
    driver: local

# 自定义网络
networks:
  app-network:
    driver: bridge

一行命令启动所有服务:

docker-compose up -d

搞定。就这四个字,代替了我之前敲的那几十行命令。


关键配置详解

让我挑几个最重要的配置说说。

1. 健康检查——避免连接失败

这是最关键的配置,也是我之前踩坑的地方。

healthcheck:
  test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p123456"]
  interval: 5s
  timeout: 10s
  retries: 5

test 定义了健康检查命令。对于MySQL,用mysqladmin ping;对于Redis,用redis-cli ping;对于HTTP服务,用curlwget

interval 是检查间隔,每5秒检查一次。

timeout 是单次检查超时时间,10秒没响应就当失败。

retries 是重试次数,连续失败5次才判定为不健康。

配合 depends_oncondition: service_healthy,就能确保依赖服务真正ready后才启动。

2. 服务依赖——控制启动顺序

depends_on:
  mysql:
    condition: service_healthy
  redis:
    condition: service_healthy

这个配置的意思是:api 服务必须等待 mysqlredis 都健康检查通过后才能启动。

注意:不要只用短格式的 depends_on: [mysql, redis],那只保证容器启动,不保证服务ready。我吃过这个亏,应用启动时数据库还在初始化,结果连接失败。

3. 自定义网络——容器间通信

networks:
  app-network:
    driver: bridge

services:
  mysql:
    networks:
      - app-network

  api:
    networks:
      - app-network

所有服务加入同一个自定义网络后,它们之间可以用服务名互相访问。比如 api 服务里配置 DB_HOST=mysql,就能直接连到MySQL容器。

千万别用 --link,那个已经被废弃了。

4. 数据卷——数据持久化

volumes:
  - mysql-data:/var/lib/mysql  # 命名卷
  - ./init.sql:/docker-entrypoint-initdb.d/init.sql  # 挂载文件

volumes:
  mysql-data:
    driver: local

命名卷(mysql-data)由Docker管理,好处是:

  • 路径不依赖宿主机,可移植性好
  • 删除容器后数据不会丢

绑定挂载(./init.sql:...)直接映射宿主机文件,适合配置文件和初始化脚本。


实战操作技巧

启动和停止

# 启动所有服务(后台运行)
docker-compose up -d

# 启动单个服务
docker-compose up -d mysql

# 停止所有服务
docker-compose stop

# 停止并删除容器
docker-compose down

# 停止并删除容器+数据卷(慎用!数据会丢)
docker-compose down -v

查看日志

# 查看所有服务日志
docker-compose logs

# 实时追踪日志(tail -f效果)
docker-compose logs -f

# 只看某个服务的日志
docker-compose logs -f api

# 查看最近100行
docker-compose logs --tail=100

进入容器调试

# 进入api容器
docker-compose exec api bash

# 进入MySQL容器执行SQL
docker-compose exec mysql mysql -uroot -p123456

重建服务

# 修改了Dockerfile或依赖,需要重建
docker-compose up -d --build api

# 强制重建(不使用缓存)
docker-compose up -d --build --force-recreate

踩坑记录:血泪教训

坑1:健康检查命令写错了

# ❌ 错误:健康检查命令需要在容器内能执行
healthcheck:
  test: ["CMD", "mysql", "-h", "localhost", "-e", "SELECT 1"]

# ✅ 正确:用mysqladmin ping
healthcheck:
  test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p123456"]

如果你用的镜像没有 mysqladmin,就得换命令。有些镜像甚至没有 curl,健康检查得想办法绕过。

坑2:时区问题

容器默认用UTC时间,日志时间会跟实际差8小时。

# 加上时区环境变量
environment:
  TZ: Asia/Shanghai

或者挂载宿主机的时区文件:

volumes:
  - /etc/localtime:/etc/localtime:ro

坑3:权限问题

挂载目录时要注意权限,Nginx的日志目录如果属主不对,会写不进去。

# 启动前先创建目录并设置权限
mkdir -p ./nginx/logs
chown -R 101:101 ./nginx/logs  # Nginx容器里的用户ID是101

或者用匿名卷让Docker自动处理权限:

volumes:
  - nginx-logs:/var/log/nginx

坑4:服务名冲突

如果你有多个Docker Compose项目,服务名可能冲突。比如都有 mysql 容器,启动第二个项目时会报错。

解决方法是给容器加个唯一前缀:

services:
  mysql:
    container_name: project1-mysql  # 加上项目名前缀

或者在不同的目录里运行Docker Compose,它会自动加目录名前缀。


写在最后

用了Docker Compose之后,我的部署流程变成了这样:

git pull
docker-compose up -d --build
docker-compose ps  # 检查服务状态

三行命令。

之前要搞半天的活,现在几秒钟搞定。而且配置文件(docker-compose.yml)本身就是文档,团队成员一看就知道项目是怎么部署的。

当然,Docker Compose也有局限。如果你搞的是几十个服务的大规模集群,该用Kubernetes还得用K8s。但对于中小规模的微服务项目,尤其是开发测试环境,Docker Compose真的是性价比最高的方案。

别再手动敲 docker run 了,真的不酷。把你的部署流程写成 docker-compose.yml,一键启动,省下来的时间去干点有意义的事——比如陪家人,或者多写几行代码。

有问题欢迎评论区交流。如果这篇文章帮到了你,点个赞呗~


Docker官方文档:https://docs.docker.com/compose/
Docker Compose示例:https://github.com/docker/awesome-compose

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐