Docker 工程化实践(六):Docker Compose 管理多容器应用

Docker 工程化实践系列 · 第 6 篇

引言:一个后端项目从来不止一个服务

前面五篇文章,我们构建和优化的都是一个独立的 Fastify API 容器。但在真实的后端项目中,你的架构大概率是这样的:

复制代码
                Nginx(反向代理)
                  │
              Fastify API
              /          \
       PostgreSQL        Redis

即使只是一个中等复杂度的项目,也至少涉及 API 服务、数据库和缓存三个组件。如果手动管理这些容器,你需要执行:

bash 复制代码
docker run -d --name postgres -e POSTGRES_PASSWORD=... -v postgres-data:/var/lib/postgresql/data postgres:17
docker run -d --name redis redis:8
docker run -d --name api -p 3000:3000 -e DATABASE_URL=... -e REDIS_HOST=... --link postgres --link redis fastify-api
docker run -d --name nginx -p 80:80 nginx

每次启动都要记住一长串参数,服务之间的网络连通需要手动配置,环境变量散落在各处,新人入职想跑起项目得先背诵这些命令。Docker Compose 正是为解决这个问题而生的------用一个声明式 YAML 文件描述整个应用栈,然后一条命令启动所有服务。

一、Docker Compose 是什么

Docker Compose 是 Docker 官方提供的多容器编排工具。核心用法很简单:在项目根目录下创建一个 docker-compose.yml 文件,描述项目需要哪些服务、每个服务用什么镜像、端口怎么映射、数据怎么持久化、服务之间怎么通信。之后用 docker compose up 一键启动所有服务,用 docker compose down 一键停止并清理。

一个最简单的 Compose 文件长这样:

yaml 复制代码
services:
  api:
    build: .
    ports:
      - "3000:3000"
  postgres:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: password
    volumes:
      - postgres-data:/var/lib/postgresql/data
  redis:
    image: redis:8

volumes:
  postgres-data:

这个文件定义了三个服务------api、postgres、redis------以及一个数据卷用于持久化数据库文件。不需要记命令参数,不需要手动创建网络,所有配置都在文件中,可以直接提交到 Git 仓库和团队共享。

二、Compose 的四个核心概念

在讲核心概念前,先看一张核心该您总览表

1. Service(服务)

Service 是 Compose 中最基本的概念,每个 service 对应一个容器。比如上面的例子中定义了 api、postgres、redis 三个 service。对于自定义镜像的服务,用 build 指定 Dockerfile 的位置;对于使用官方镜像的服务,直接用 image 指定镜像名称和版本。

2. Network(网络)

Compose 默认会创建一个独立的网络,所有服务自动加入这个网络。在这个网络内部,容器之间可以直接通过服务名互相通信 ,而不需要使用 IP 地址或 --link

这意味着你的 Fastify API 可以这样配置数据库连接:

复制代码
DATABASE_URL=postgres://postgres:password@postgres:5432/app
REDIS_HOST=redis

注意主机名是 postgresredis------就是 Compose 文件中定义的服务名------而不是 localhost。这是很多初学者第一次部署踩坑的地方:在容器内部,localhost 指向容器自己,而数据库运行在另一个容器中,必须用服务名来访问。Compose 内置的 DNS 会自动将服务名解析为对应容器的内部 IP。

3. Volume(数据卷)

默认情况下,容器删除后容器内的所有数据都会丢失。对于数据库容器而言,这意味着删除 Postgres 容器 → 所有数据消失------这在生产环境中是完全不可接受的。

Volume 用于将容器内的目录映射到宿主机上持久化的存储空间:

yaml 复制代码
postgres:
  image: postgres:17
  volumes:
    - postgres-data:/var/lib/postgresql/data

即使容器被删除重建,只要 Volume 还在,数据就不会丢失。

4. Environment(环境变量)

Compose 支持多种方式配置环境变量。可以直接在文件中用 environment 声明,也可以通过 env_file 引用外部 .env 文件:

yaml 复制代码
api:
  environment:
    NODE_ENV: production
    DATABASE_URL: postgres://postgres:password@postgres:5432/app
  # 或者
  env_file:
    - .env

推荐把非敏感配置写在 Compose 文件中,敏感信息(如数据库密码)通过外部 .env 文件注入,并确保 .env.gitignore 中。

三、一个完整的 Fastify + PostgreSQL + Redis 项目

下面是一个接近真实项目的 Compose 配置:

yaml 复制代码
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    env_file:
      - .env
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started

  postgres:
    image: postgres:17
    env_file:
      - .env
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-appuser} -d ${POSTGRES_DB:-appdb}"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:8
    volumes:
      - redis-data:/data

volumes:
  postgres-data:
  redis-data:

对应的 .env 文件:

复制代码
POSTGRES_USER=appuser
POSTGRES_PASSWORD=apppassword
POSTGRES_DB=appdb
DATABASE_URL=postgres://appuser:apppassword@postgres:5432/appdb
REDIS_HOST=redis
REDIS_PORT=6379

注意几个细节:数据库密码等敏感信息没有直接写在 docker-compose.yml 中,而是通过 env_file 引用外部 .env 文件------和第五篇"不要把密钥写进镜像"的原则保持一致;.env 文件需要加入 .gitignore,避免提交到 Git 仓库。

几个值得注意的细节。depends_on 控制的是服务之间的启动依赖顺序 ------普通 depends_on 只保证"先启动 Postgres,再启动 API",但不保证 Postgres 已经准备好接受连接(数据库进程启动了不等于初始化完成了)。加上 condition: service_healthy 配合 healthcheck,才能让 Compose 等待 Postgres 达到 healthy 状态后再启动 API,避免 API 启动时连不上数据库。Postgres 服务通过 pg_isready 命令检测自身是否准备好接受连接,这是 Compose 中推荐的等待数据库就绪的方式。

四、常用命令

bash 复制代码
# 启动所有服务
docker compose up

# 后台运行
docker compose up -d

# 后台运行并在代码变更后重新构建 API 镜像
docker compose up -d --build

# 查看所有服务运行状态
docker compose ps

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

# 只看 API 服务的日志,并持续跟踪
docker compose logs -f api

# 进入 API 容器
docker compose exec api sh

# 停止所有服务并删除容器和网络(保留数据卷)
docker compose down

# 停止并同时删除数据卷
docker compose down -v

其中 docker compose downdocker compose down -v 的区别非常关键。docker compose down 停止并删除所有容器和 Compose 自动创建的网络,但保留数据卷 ------数据库数据不会丢失,下次 docker compose up 时数据还在。docker compose down -v 则会在删除容器和网络的同时删除所有声明的 Volume ,这意味着 Postgres 中的数据库数据、Redis 中的数据都会被清空。在生产环境或任何需要保留数据的场景中,使用 -v 之前一定要确认数据已经备份。如果不确定,先用不带 -v 的命令。

看完这个图片你就会对上面的代码更理解了

五、Compose 与手动 docker run 的对比

维度 docker run Docker Compose
管理对象 单个容器 多个服务
配置方式 命令行参数 YAML 文件
网络配置 手动创建和连接 自动创建,服务名直连
配置复用 靠脚本或文档 YAML 文件直接共享
团队协作 新人需背诵命令 clone 后一行 docker compose up
版本控制 无法追溯 随代码一起进入 Git

本质区别在于:docker run 是命令式的------你告诉 Docker"做什么",参数散落在命令行中;Compose 是声明式的------你告诉 Docker"我想要什么状态",然后 Docker 去达成它。声明式配置天然更适合团队协作和版本管理。

六、总结

Docker Compose 让多容器应用的管理从一个手动的、易出错的过程,变成了一个可版本控制、可一键启动的配置文件。核心概念只有四个------Service 定义容器、Network 处理通信、Volume 持久化数据、Environment 注入配置------但它们组合起来可以描述任意复杂的应用栈。

完成本篇文章后,你应该能够做到:在项目根目录下放一份 docker-compose.yml,新成员 clone 代码后只需执行 docker compose up 就能跑起完整的开发环境------API、数据库、缓存全部就绪。

至此,Docker 工程化实践系列的"Docker 核心能力"部分已经完成。我们从理解 Docker 的基本原理出发,逐步掌握了镜像分层、Build Cache、多阶段构建、生产镜像优化,最后用 Compose 把多个容器编排成一个完整应用。这些知识覆盖了从本地开发到生产部署的完整链路------接下来可以进一步探索 CI/CD 自动构建部署、Kubernetes 容器编排等更高级的工程化实践。

上一篇:Docker 工程化实践(五):Docker 镜像优化最佳实践

相关推荐
luyun0202022 小时前
论坛里的小工具,吾爱出品
运维·服务器·windows
江湖有缘2 小时前
Docker实战 | 使用Docker部署EasyNode服务器面板工具
服务器·docker·容器
心机之蛙qee2 小时前
Docker Compose 多容器编排
java·docker·容器
AI大佬的小弟2 小时前
私有化 Dify 应用开发(1):Dify 入门|Docker 一键部署全流程
docker·大模型落地·ai 应用开发·dify 教程·私有化 ai 部署·docker 实战·开源 ai 平台
Miao121312 小时前
工程交付指标入门:如何重新掌控软件交付
大数据·运维·devops
涛声依旧393163 小时前
运维项目:InnoAI SQL 助手
运维·数据库·sql
养海绵宝宝的小蜗3 小时前
LVS(Linux Virtual Server)项目知识点总结
linux·运维·lvs
ShiXZ2134 小时前
Docker 安装 Elasticsearch 7.17.6 完整教程
elasticsearch·docker·容器
joopthd4 小时前
打破进口垄断:TDLAS/UV-DOAS/FTIR 全光谱检测国产器件全产业链布局(江苏旭海光电技术梳理)
运维·人工智能·uv·激光气体检测