PlantUML 的 !include 是基础预处理。但企业级架构图常常需要从外部数据源读 service 列表、读端口号、读 AWS 资源 ID 生成图。这篇是 Jinja/JSON/YAML 动态生成实战。
为什么需要动态生成
硬编码 .puml 的痛:
1 2 3 4 5 6 7 8 9 10 11 12
@startuml component "user-service" as us component "order-service" as os component "payment-service" as ps component "inventory-service" as is component "notification-service" as ns
us --> os : HTTP 8080 os --> ps : HTTP 8081 os --> is : HTTP 8082 us --> ns : HTTP 8083 @enduml
问题是:新增一个 service 要手动改两处——component 列表 + 连线。新增 service 多到 30+ 个时,改图是噩梦。
withopen("architecture.puml", "w") as f: f.write("@startuml\n\n") for svc in config["services"]: f.write(f'component "{svc["name"]}" as {svc["alias"]}\n') f.write("\n") for svc in config["services"]: for dep in svc["deps"]: f.write(f'{svc["alias"]} --> {dep} : HTTP\n') f.write("\n@enduml\n")
# gen_k8s_diagram.py import yaml, subprocess from kubernetes import client, config
config.load_kube_config() v1 = client.CoreV1Api()
puml_lines = ["@startuml", "left to right direction", ""]
services = {} for svc in v1.list_service_for_all_namespaces().items: name = svc.metadata.name namespace = svc.metadata.namespace label = f"{namespace}/{name}".replace("-", "_") services[name] = {"label": label, "namespace": namespace}
puml_lines.extend([ f'component "{svc.metadata.name}" as {services[svc.metadata.name]["label"]}' for svc in v1.list_service_for_all_namespaces().items ])
puml_lines.append("") endpoints = v1.list_endpoints_for_all_namespaces() for ep in endpoints.items: for subset in (ep.subsets or []): ifnot subset.ports: continue for port in subset.ports: target_name = ep.metadata.name target_label = services[target_name]["label"] for ref in (subset.addresses or []): if ref.target_ref and ref.target_ref.kind == "Pod": pod_name = ref.target_ref.name puml_lines.append(f'{target_label} --> [{pod_name}] : {port.port}')
puml_lines.append("@enduml")
withopen("k8s-topology.puml", "w") as f: f.write("\n".join(puml_lines))