服务拓扑怎么画:一张图讲清三层边界

一张图为什么会失真

大部分架构图的问题不是画得丑,而是混层:把公网入口、进程、数据库、定时任务全塞进同一张平面图,读者分不清哪条线是网络跳转、哪条是函数调用。

解决办法是把拓扑拆成三层,每层只回答一个问题:

第一层 · 流量层
请求从哪进来、经过哪些网络跳转。只画公网可达的边界。
第二层 · 进程层
哪几个进程在跑、谁调谁。只画同一台机器或同一编排单元内的进程。
第三层 · 数据层
数据落在哪、谁写谁读。只画存储与读写方向。
💡
判断标准

如果一张图里既有 https:// 又有 SELECT,那它一定混层了。拆开画。

三层合起来长这样

下面这张是典型的单机双服务部署,把三层叠在一张图里,但用节点形状区分职责。

服务拓扑(三层叠合) Mermaid
ℹ️
形状约定

圆角矩形 = 网络实体,方括号 = 进程,圆柱 = 存储,虚线 = 非同步/旁路。约定统一了,图就有信息量。

落地配置

三层边界最终都要落到配置文件上,改完再用一条命令验证:

# nginx:手工管理证书与上游
server {
    listen 443 ssl http2;
    server_name kb.example.com;
    ssl_certificate     /etc/ssl/kb.crt;
    ssl_certificate_key /etc/ssl/kb.key;
    location /api/ { proxy_pass http://127.0.0.1:8000; }
    location /     { root /srv/kb/current; }
}
# docker compose:把三层写成三个 service
services:
  edge:
    image: caddy:2-alpine
    ports: ["443:443"]
    volumes: ["./Caddyfile:/etc/caddy/Caddyfile"]
  app:
    build: .
    expose: ["8000"]
  job:
    build: ./job
    depends_on: [app]
# 验证三层是否真的通了:静态、API、证书各打一发
curl -sI https://kb.example.com/ | head -1
curl -s https://kb.example.com/api/health
echo | openssl s_client -connect kb.example.com:443 2>/dev/null \
  | openssl x509 -noout -dates
💡
Caddy 的等价写法

如果不想手工管证书,同样的需求换成 Caddy 只要三行:域名块 + handle /api/* 反代 + handle 静态。 证书签发和续期 Caddy 自己包了,代价是少一层精细控制。

⚠️
常见坑

Caddy 的 handle互斥的,handle_path 会剥掉前缀。用 handle /api/* 转发时上游收到的是带前缀的路径,FastAPI 的路由要相应加上 /api 前缀,否则全 404。

一张手绘草图胜过十段描述

和同事对齐拓扑时,先画后写。下面这种草图式布局最快:

服务拓扑手绘草图:Caddy 边缘、FastAPI 进程、SQLite 存储、定时任务四个节点及其连线

收尾

架构图的价值不在于画得全,而在于让读者一眼看出「哪条线断了会炸」。

画完三层之后,回头在每个节点旁标一个数字:这台服务挂掉,影响多少用户。标注完,优先级自然就出来了。

目录

图表