如何把 Docker Compose 文件转成架构图

compose.yaml 已经列出了你的服务、网络和卷;本文介绍如何把它画成图、找出它隐藏的调用,并让图保持最新。

2026/10/22

如何把 Docker Compose 文件转成架构图

Compose 文件往往是你手头对一个系统最诚实的描述。在笔记本上、在 CI 里,有时甚至在生产环境中,真正启动起来的就是它。但它也是一堵 YAML 墙,新同事仍然得问:哪个服务在和哪个服务通信?

本文教你把 Compose 文件变成一张值得信赖的架构图:渲染出 Compose 实际运行的文件,把每个键映射到图元素,补上文件无法表达的调用,并让图与文件保持同步。最后,我们用 Archboard 的免费工具在几秒内生成第一版草图。

示例:一个小型 shop 应用栈

下面是我们要画的 compose.yaml。它很短,但常见的坑一个不少:一个接入两个网络的服务、一个只存在于配置文件里的依赖,以及一个只在开发时使用的工具。

name: shop

services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - api
    networks: [frontend]

  api:
    build: ./api
    environment:
      DATABASE_URL: postgres://shop:${POSTGRES_PASSWORD}@db:5432/shop
      REDIS_URL: redis://cache:6379/0
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    networks: [frontend, backend]

  worker:
    build: ./api
    command: ["node", "worker.js"]
    environment:
      DATABASE_URL: postgres://shop:${POSTGRES_PASSWORD}@db:5432/shop
      REDIS_URL: redis://cache:6379/0
    depends_on:
      - db
    deploy:
      replicas: 2
    networks: [backend]

  db:
    image: postgres:16
    environment:
      POSTGRES_USER: shop
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U shop"]
      interval: 5s
    networks: [backend]

  cache:
    image: redis:7
    networks: [backend]

  adminer:
    image: adminer
    ports:
      - "8081:8080"
    profiles: [debug]
    networks: [backend]

volumes:
  db-data:

networks:
  frontend:
  backend:

第 1 步:渲染 Compose 实际运行的文件

团队很少原样运行单个文件。override 文件会添加端口,.env 文件会填入变量,extends 会从另一个服务拉取字段。动手画之前,先让 Compose 给出最终结果:

docker compose config > rendered.yaml

docker compose config 会合并 override 文件、填入变量、解析 extends,然后输出 Compose 将要运行的模型。请依据这份输出来画,而不是依据你碰巧打开的那个文件。

提醒一句:输出中包含变量的真实值,包括 .env 里的任何机密信息。请像对待 .env 文件本身一样对待 rendered.yaml,不要提交,也不要分享。

第 2 步:把 Compose 键映射到图元素

图的大部分内容可以直接从文件中得到:

Compose 键 图元素
services.<name> 每个服务一张卡片,以其键名命名
image / build 写在卡片上:镜像标签或构建上下文
ports (8080:80) 写在卡片上,以及从外部进入的连线上::8080 → 80
deploy.replicas / scale 卡片上的副本数,而不是额外的方框
networks 每个网络一个框;每个服务放在其第一个网络的框中
具名 volumes 一个卷节点,连接到挂载它的服务
secrets、configs 连接到使用它们的服务的节点,只写名称
depends_on 启动顺序,写在卡片上或画成虚线连线
profiles 可选服务:省略,或标记为可选

有两条规则能让图保持诚实。像 api 这样加入两个网络的服务,是这两个网络之间的桥梁,所以把它放在第一个网络的框的边缘,让它的连线跨进另一个网络。另外,副本是一个数字,而不是一摞方框:worker ×2 在你扩容到五个时依然正确。

第 3 步:补上文件里看不到的调用

这一步工具替你做不了。depends_on 关心的是启动顺序:Compose 会先启动 db 再启动 api,并且在设置了 condition: service_healthy 时等待健康检查通过。至于运行时谁调用谁,它什么也没说。

真正的调用藏在三个地方:

  • 连接字符串。 DATABASE_URL 和 REDIS_URL 指明了主机(db、cache)和端口。每一个都是一条连线。
  • 挂载进容器的配置文件。 nginx.conf 中写着 proxy_pass http://api:3000;。这就是从 web 到 api 的连线,而它位于 Compose 文件之外。
  • 代码。 一个服务可以调用另一个服务,而 Compose 里没有任何一行体现这一点。worker 使用了 REDIS_URL,却没有针对 cache 的 depends_on。如果 Redis 启动得晚,worker 会在启动时失败,而架构图是这个缺口最先暴露出来的地方。

简单搜索一下就能找到其中大部分:

grep -rnE "://([^/@[:space:]]*@)?(api|web|db|cache)[:/]|proxy_pass" --include="*.conf" --include="*.yaml" --include="*.yml" --include="*.env" .

然后与每个服务的负责人确认。把运行时调用画成实线连线,并标注协议和端口;把 depends_on 保留为注释或虚线连线,这样就没人会把启动顺序误当成流量。

第 4 步:决定省略什么

Compose 文件把系统本身和它周边的工具混在了一起。通常可以省略:

  • 仅用于开发的服务。 也就是放在 profiles 后面的服务,例如 adminer、邮件捕获工具或调试代理。
  • 一次性任务。 例如数据库迁移或数据填充脚本,除非这张图讲的就是部署。
  • 源代码和配置文件的绑定挂载。 配置文件只通过它带来的依赖来体现,比如这里从 nginx.conf 得出的 proxy_pass 连线。
  • 环境变量的值。 变量名可以帮助解释一条连线;值应该放在你的密钥管理工具里,而不是画在图上。

第 5 步:完成布局

把流量从左到右排列:先是浏览器,然后是发布的端口,再往后是端口背后的服务。把每个网络画成一个框,这样跨网络的调用一眼就能看出来。把数据存储放在一起,并把卷放在挂载它的服务下方。每条连线都标注协议和端口,例如 HTTP :3000 或 SQL :5432。

用免费工具生成第一版草图

你不必手工画这些卡片。把文件(或渲染后的输出;工具不会读取环境变量的值)粘贴到免费工具 Docker Compose 转图表 中即可。查看无需账号。每个服务会变成一张卡片,显示镜像、端口和副本数;网络会变成框;具名卷、secret 和 config 会连接到使用它们的服务;depends_on 则会变成服务之间的连接。

工具只画文件里写明的内容。它不显示运行时调用,不读取环境变量的值或 env_file 的内容,也不跟进 include;对于使用 extends 的服务,它只画该服务中直接写出的字段。这就是第 1 步和第 3 步依然重要的原因。

有了免费账号,你可以在 Archboard 中把结果作为新图表打开,然后补上第 3 步的运行时连线,并去掉第 4 步提到的噪音。经过这一轮整理,shop 应用栈是这样的:

一个 Compose 项目的 Docker 架构图:浏览器通过 8080 端口访问 web(nginx),web 把请求代理到 api,api 和有两个副本的 worker 调用 backend 网络中的 db(PostgreSQL)和 cache(Redis)。卷 db-data 由 db 挂载。

10 个组件 · 7 个连接

在 Archboard 中打开
图表内容
  • Browser
  • frontend
  • backend
  • web
  • api
  • cache
  • db
  • worker
  • db-data
  • HTTP :8080 → 80
  • proxy_pass :3000
  • SQL :5432
  • RESP :6379
  • jobs :6379
  • /var/lib/postgresql/data
本文的 shop 应用栈:各服务位于所属网络的框中,还有数据库的卷,以及从 nginx.conf 和连接 URL 中读出的运行时调用,这些仅凭 Compose 文件是看不出来的。

卡片来自 Docker 节点包,浏览器来自网络节点包,整张图也在 Free 套餐 15 个图表节点的限制之内。如果想看另一种 Docker 布局,包含容器、镜像仓库和负载均衡器,请参阅 Docker Runtime Mesh 示例。

保持最新

根据 Compose 文件画出的图,在有人新增一个服务的那天就会过时。下面三个习惯会有帮助:

  • 在 pull request 模板里加一行:“是否新增了服务、网络、发布端口或连接字符串?请更新架构图。”
  • 评审时重新运行 docker compose config,并把服务列表与图进行对比。
  • 在 README 中链接到实时的图,而不是粘贴截图,这样需要更新的只有一份。

当应用栈迁移到 Kubernetes 时,同样的方法也适用于清单;参见从 Kubernetes YAML 到架构图。

常见问题

depends_on 是否意味着一个服务调用另一个服务? 不是。它控制启动顺序,在设置了 condition: service_healthy 时还会等待健康检查。运行时调用来自连接字符串、配置文件和代码。

可以一次把多个 Compose 文件画成图吗? 先用 docker compose config 渲染,它会把 override 文件合并成一个模型。免费工具每次只读取一个文件。

工具会读取我的 .env 文件吗? 不会。变量不会被替换:用变量写的镜像标签或端口,例如 image: shop-api:${TAG},会按原样显示;environment 下的值和 env_file 中的值也不会被读取。

每个容器都要画一个方框吗? 每个服务画一张卡片,并在上面写明副本数。单个容器来来去去,团队思考的单位是服务。

修改 compose.yaml 后,图会自动更新吗? 不会。它只是一个起点。用上面的习惯让它保持最新,或者用工具重新画一遍,再把你的运行时连线搬过去。

免费开始 — 1 个席位,无需信用卡。

相关文章

← 返回博客

一分钟打开画布

使用 Google 或 GitHub 登录。单席位免费,团队加入后再升级。