一个包含十几份清单的 namespace,部署起来很容易,讲清楚却很难。YAML 告诉 API 服务器它需要的一切,却不会告诉新同事哪个 Service 位于哪个 Deployment 前面,也不会说明 checkout API 为什么要调用 payments。本文给出一套可重复的方法,手工把 Kubernetes YAML 转成架构图:盘点对象、从特定字段读出关系、决定省略什么、完成布局,并让图保持最新。最后,我们会在 Archboard 中用 Kubernetes 节点包重建这张图。
这里没有魔法:你读取字段,画出连线。这正是重点。图应该表达意图,而意图需要由你来补充。
第 1 步:盘点对象
从渲染后的清单开始,而不是模板。如果你使用 Helm 或 Kustomize,先渲染,才能看到真正被应用的内容:
helm template shop ./charts/shop -f values-prod.yaml > rendered.yaml
kubectl kustomize overlays/prod > rendered.yaml
如果工作负载已经在运行,就直接询问集群,并明确列出资源类型:
kubectl get deploy,svc,ing,cm,secret,pvc,hpa,networkpolicy -n shop
不要依赖 kubectl get all。all 类别包括 Pod、Service、Deployment、ReplicaSet、StatefulSet、DaemonSet、Job、CronJob 和 HPA,但不包括 Ingress、ConfigMap、Secret、PVC、NetworkPolicy 和 ServiceAccount。其中好几个恰恰承载着你要画的连线。
对承载关系的对象,取出完整的 spec:
kubectl get ingress -n shop -o yaml
kubectl get svc -n shop -o custom-columns=NAME:.metadata.name,SELECTOR:.spec.selector
kubectl get pods -n shop --show-labels
把盘点结果写成一份扁平清单:类型、名称、namespace。这份清单就是你画图时的检查表。
示例:把一个服务的 Kubernetes YAML 转成图
下面是 shop namespace 中一个服务的小型但典型的配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout-api
namespace: shop
spec:
selector:
matchLabels:
app: checkout-api
template:
metadata:
labels:
app: checkout-api
tier: backend
spec:
containers:
- name: api
image: registry.example.com/shop/checkout-api:1.14.2
ports:
- containerPort: 8080
resources:
requests:
cpu: 250m
envFrom:
- configMapRef:
name: checkout-config
- secretRef:
name: checkout-db-credentials
---
apiVersion: v1
kind: Service
metadata:
name: checkout-api
namespace: shop
spec:
selector:
app: checkout-api
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
namespace: shop
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /api/checkout
pathType: Prefix
backend:
service:
name: checkout-api
port:
number: 80
---
apiVersion: v1
kind: ConfigMap
metadata:
name: checkout-config
namespace: shop
data:
PAYMENTS_URL: http://payments.shop.svc.cluster.local
LOG_LEVEL: info
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: checkout-api
namespace: shop
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: checkout-api
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
五个对象,一个被引用但未展示的 Secret,以及一个藏在配置值里的依赖。
第 2 步:在字段中找出关系
Kubernetes 对象很少通过 ID 互相引用。它们通过名称引用,更常见的是通过 label。label 和选择器就是粘合剂;开始之前,值得重读一遍 Kubernetes 关于 labels 和选择器的文档。
按流量顺序遍历清单:
- Ingress → Service。 每个 path 中的
backend.service.name和port.number直接指明 Service。host 和 path 成为连线的标签。 - Service → Deployment。 Service 从不直接点名 Deployment。它的
spec.selector与 Deployment 的spec.template.metadata.labels中的 label 匹配。这里app: checkout-api匹配,所以画出这条连线。如果不确定,kubectl get endpointslices -n shop会显示 Service 实际选中的 Pod 地址;没有 endpoint 说明选择器什么都没匹配到,或者匹配到的 Pod 还没有 IP。 - Deployment → ConfigMap / Secret / PVC。 查看
envFrom、env[].valueFrom和volumes[](configMap、secret、persistentVolumeClaim.claimName)。 - HPA → Deployment。
spec.scaleTargetRef指明目标。 - NetworkPolicy。
spec.podSelector选出它保护的 Pod;ingress[].from说明谁可以与它们通信,egress[].to说明它们可以连接到哪里。 - 隐藏的依赖。
PAYMENTS_URL指向payments.shop.svc.cluster.local。没有任何字段把 checkout 和 payments 连起来,但这个调用真实存在。这正是工具会漏掉、而人最需要的那条连线。
从 YAML 字段到图元素
| YAML 字段 | 图元素 |
|---|---|
metadata.namespace |
包住对象的 Namespace 框 |
kind: Deployment |
Deployment 节点,副本数作为标签 |
Ingress rules[].host + paths[].path |
Ingress 节点,出向连线上标注 host/path |
Ingress backend.service.name |
连线 Ingress → Service |
Service selector ↔ 模板 labels |
连线 Service → Deployment |
Service port → targetPort |
连线标签,例如 80 → 8080 |
envFrom / volumes[].configMap |
连线 ConfigMap → Deployment |
secretRef / volumes[].secret |
连线 Secret → Deployment |
persistentVolumeClaim.claimName |
连线 PVC → Deployment |
HPA scaleTargetRef |
Deployment 旁的 HPA 节点,标签 3–10 |
NetworkPolicy podSelector、ingress[].from |
所保护工作负载旁的 NetworkPolicy 节点 |
| 配置值中的 Service DNS 名称 | 指向被依赖 Service 的连线 |
第 3 步:决定省略什么
什么都画的图,等于什么都没说。以下规则很实用:
- 副本数是标签,不是方框。 只画一个 Deployment 节点,并在上面写
3–10 replicas (HPA)。三个一模一样的 Pod 方框只会增加噪音,而且 HPA 一扩缩就不再准确。 - 隐藏 ReplicaSet。 它们只是滚动发布的实现细节。
- 只在必要时画 Pod,例如独立的 Pod,或者在解释如何调度到 Node 时。
- 折叠样板内容。 ServiceAccount、默认 RBAC 和镜像拉取用的 Secret,通常放在一条注释里,而不是画在画布上。
- 保留能解释决策的内容。 如果 checkout 无法直接访问数据库是因为某个 NetworkPolicy,那它就应该留在图上。
第 4 步:完成布局
把南北向流量从左到右排列:客户端 → Ingress → Service → Deployment → 下游 Service 和数据存储。配置(ConfigMap、Secret)放在它所服务的工作负载下方,扩缩(HPA)放在上方。把每个 namespace 画成一个框,这样跨 namespace 的调用就会以“穿出框”的连线形式凸显出来。连线上要标注协议和端口,而不只是箭头。
自动可视化工具与手绘架构图
生态中有些工具会从 API 服务器读取集群的实时状态,并把它渲染成图。它们对“此刻正在运行什么”非常准确,排障时很有用。代价是:它们会显示每一个 ReplicaSet、Pod 和 endpoint,看不到藏在环境变量里的依赖,也无法表达意图,比如某个组件为什么存在、接下来计划做什么。手绘的图不那么完整,但对新人上手、设计评审和故障处理时的背景说明更有用。很多团队两者并用。
第 5 步:保持最新
只有和清单一起变化的图才值得信任:
- 把图文件放在清单旁边,在同一个 pull request 中评审。
- 在 PR 模板里加一行:“是否修改了 Ingress、Service 选择器、HPA 或 NetworkPolicy?请更新架构图。”
- 在定期评审时重新运行第 1 步的盘点命令,并与图进行对比。
在 Archboard 中重建示例
在 Archboard 中,你可以根据刚刚整理好的清单来画图,决定哪些内容应该出现在图上,并使用与 Kubernetes kind 对应的节点:
- 使用 Google 或 GitHub 登录,并新建一张图。
- 从 Kubernetes 节点包中添加一个 Namespace。它以框的形式呈现,命名为
shop。 - 在框外左侧,添加一个 Core 的 Box 或 Text 表示客户端。
- 在框内从左到右放置 Ingress、Service 和 Deployment。连接它们的端口,并给连线加上
shop.example.com/api/checkout和80 → 8080的标签。 - 在 Deployment 下方添加 ConfigMap 和 Secret 并连接。在上方添加一个标签为
3–10, CPU 70%的 HPA。 - 为
payments添加第二个 Service,并从 Deployment 连过去,让隐藏的依赖变得可见。 - 在画布上放一个 Markdown 块,用围栏代码块写入关键的 YAML 片段,或写一段简短的“为什么”说明。
- 把图导出为 JSON,并提交到清单旁边;再导出 SVG 或 PNG 用于 README。
图表内容
- shop
- Clients
- checkout-api
- checkout-config
- checkout-db-credentials
- payments
- HTTPS
- shop.example.com/api/checkout
- 80 → 8080
- PAYMENTS_URL
- CPU 70%
- envFrom
这个示例在 Free 方案每张图 15 个图表节点的限制之内;便签和文本单独计算,最多 150 个。参考布局可以打开 /examples/kubernetes-ingress-flow 和 /examples/kubernetes-microservices;使用此示例 可以把其中任意一个复制到你的工作区,就像上面这张图的 在 Archboard 中打开 一样。更多模式见 /use-cases/kubernetes。
常见问题
Archboard 能导入我的 Kubernetes YAML 吗?
可以,作为起点。把渲染后的清单或 kubectl get -o yaml 的输出粘贴到免费的Kubernetes YAML 转图表工具:它会把命名空间画成框,并按 YAML 中的定义连接 Ingress、Service、工作负载、ConfigMap、Secret 和卷。查看无需账号;使用免费账号即可在 Archboard 中将结果作为新图表打开,或在编辑器中使用 从 YAML 导入… 将其添加到现有图表中。它不会与你的 YAML 保持同步,因此本文的步骤仍可用于检查结果并保持图表最新。
每个 Pod 都要画出来吗? 通常不需要。一个带副本数标签的 Deployment 节点更清晰,而且在 HPA 扩缩时依然准确。
Helm chart 怎么办?
先用 helm template 渲染,然后画渲染结果,而不是模板。
如何找出没有出现在任何字段里的依赖?
在 ConfigMap 和 env 值中搜索 svc.cluster.local 等主机名,然后与各服务的负责人确认。