几乎每份架构文档里都有这样一张图:一个写着“Ingress”的方框,一支箭头直接指向一排 Pod。平时看着没问题,可一旦有人在排查 502 时问起 TLS 在哪里终止、连接由哪个代理持有,或者为什么 NetworkPolicy 会拦下“经过 Ingress”的流量,这张图就答不上来了。一张好的 Kubernetes Ingress 架构图,应该在别人提问之前就把答案画出来。本文逐跳梳理请求路径,把一个最小的 Ingress 清单对应到方框和箭头上,列出评审中最常见的错误,并演示如何在 Archboard 中画出同样的图。
请求路径,逐跳拆解
跟着 https://shop.example.com/api/orders 从客户端一路走到容器:
- 客户端 → DNS。 客户端解析
shop.example.com。记录(A/AAAA、CNAME 或云厂商的别名记录)指向的是云负载均衡器,而不是某个节点。 - DNS → 云负载均衡器。 客户端连接到负载均衡器。在大多数集群中,它之所以存在,是因为 Ingress 控制器通过
type: LoadBalancer类型的 Service 对外暴露,由云集成负责创建,并把流量转发到节点端口或直接转发到控制器 Pod。 - 负载均衡器 → Ingress 控制器。 控制器是一个普通的工作负载(Deployment 或 DaemonSet),运行 NGINX、Envoy、HAProxy 或 Traefik 等反向代理。真正接受连接、代理请求的是它。
- Ingress 规则(host/path)。 控制器监听属于自己类别的 Ingress 对象,并把它们转换成代理配置。Ingress 对象本身不经手任何数据包,它只是配置。
- Service(ClusterIP)。 每条规则指向一个 Service 名称和端口。Service 提供稳定的名称,并通过 label 选择 Pod。
- EndpointSlices。 控制平面把每个 Service 背后 Pod 的 IP 连同各自的 ready 条件一起记录在 EndpointSlice 对象中。包括 ingress-nginx 在内的许多控制器会读取 EndpointSlices,直接把流量发往 Pod IP,而不经过 ClusterIP。但 Service 仍然必须存在:控制器正是通过它找到这些端点的。
- Pod。 请求最终到达某个就绪 Pod 的容器端口。
关于 ingress-nginx 的说明:社区版 ingress-nginx 控制器已于 2026 年 3 月退役,不再获得新版本、bug 修复或安全补丁。它仍是最为人熟知的例子;新集群请选择仍在维护的控制器或 Gateway API。
TLS 终止:选定边界并画出来
HTTPS 通常在以下三个位置之一终止:
| TLS 终止位置 | 证书由谁持有 | 图上要画什么 |
|---|---|---|
| 云负载均衡器 | 云厂商的证书服务 | 边界止于 LB;把 LB → 控制器标注为 HTTP 或重新加密的 HTTPS |
| Ingress 控制器 | spec.tls 中引用的 kubernetes.io/tls 类型 Secret |
Secret 连到 Ingress;边界止于控制器 |
| Pod(透传) | 应用本身 | 控制器按 SNI 原样转发 TLS;边界止于 Pod。这是控制器的特有功能,不属于 Ingress 规范 |
Secret 必须与引用它的 Ingress 位于同一个 namespace。如果在负载均衡器终止后又重新加密到控制器,要画两段 TLS,而不是一段。
基于主机与基于路径的路由
- 基于主机的路由把不同的主机名发往不同的后端(如
shop.example.com与admin.example.com)。每个主机画一组连线。 - 基于路径的路由按 URL 路径拆分同一个主机。使用
pathType: Prefix时,按/分隔的路径元素进行匹配,因此/api能匹配/api/orders,但不能匹配/apiv2。Exact只匹配该路径本身;ImplementationSpecific的行为由控制器决定。 - 多条路径同时匹配时,最长的路径优先。实际中的 Ingress 大多同时使用主机和路径规则。
NetworkPolicy 作用在哪里
NetworkPolicy 在 L3/L4 过滤 Pod 流量,而且只有在集群的网络插件支持时才会生效。它不理解主机和路径,也不挂在 Ingress 对象上。在图上,它管控的是“控制器 Pod → 应用 Pod”这一跳。如果应用所在 namespace 配置了默认拒绝入站的策略,就需要再加一条允许来自控制器 namespace 流量的策略,例如在 kubernetes.io/metadata.name: ingress-nginx 上使用 namespaceSelector。“负载均衡器 → 控制器”这一跳则由云防火墙规则以及控制器 namespace 中的策略管控。
多个 Ingress 类
集群里经常同时运行多个控制器:一个对外、一个对内,或者迁移期间新旧并存。每个控制器认领一个 IngressClass,每个 Ingress 通过 spec.ingressClassName 选择其中之一。可以用注解 ingressclass.kubernetes.io/is-default-class: "true" 把某个类设为默认。图上要给每个控制器画出来自各自负载均衡器的独立路径,并在每个 Ingress 上标注它的类;否则读者会以为所有流量都经过同一个代理。
Gateway API 简述
Gateway API(gateway.networking.k8s.io)是更新的、面向角色的替代方案:GatewayClass 描述具体实现,Gateway 定义监听器和 TLS(通常由平台团队负责),HTTPRoute 承载路由规则(通常由应用团队负责)。其核心 HTTP 路由自 2023 年的 v1.0 起已 GA。Ingress API 依然是 GA 并受支持,但已被冻结,不再增加新功能。图的结构几乎不变:Gateway 取代控制器监听器的位置,HTTPRoute 取代 Ingress 规则的位置。
最小 Ingress 与 Kubernetes Ingress 架构图的对应关系
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
namespace: shop
spec:
ingressClassName: nginx
tls:
- hosts:
- shop.example.com
secretName: shop-example-com-tls
rules:
- host: shop.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: orders-api
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: storefront
port:
number: 80
| 字段 | 图上的方框或箭头 |
|---|---|
metadata.namespace: shop |
包住 Ingress、Secret、Service 和 Deployment 的 Namespace 框 |
spec.ingressClassName: nginx |
由哪个控制器接管:从该控制器指向 Ingress 的虚线“监听”连线 |
spec.tls[].hosts + secretName |
连到 Ingress 的 Secret 节点;止于控制器的 TLS 边界 |
rules[].host |
DNS 记录的标签,以及来自负载均衡器的入口连线 |
paths[0]: /api → orders-api:8080 |
从控制器到 orders-api Service 的连线,标注 /api |
paths[1]: / → storefront:80 |
从控制器到 storefront Service 的连线,标注 / |
backend.service.port.number |
每条“控制器 → Service”连线上的端口标签 |
/api/orders 同时匹配两条路径;/api 更长,所以请求会发往 orders-api。
常见错误
- 把 Ingress 画成代理。 “LB → Ingress → Pod”读起来就像 Ingress 是个 Pod。真正的代理是控制器,Ingress 只是它读取的配置。把控制器放在数据路径上,再用虚线把 Ingress 挂在它旁边。
- 省略 Service。 Ingress 的后端引用的是 Service,而不是 Pod。即使流量直接发往 Pod IP,label 不一致时出问题的也是 Service 的选择器,所以要把它留在图上。
- 没有 TLS 边界。 图上看不出 HTTPS 在哪里终止,就没人能判断 VPC 内部的流量是否为明文。
- 悬空的 NetworkPolicy。 一条没有连到所管控那一跳的策略,什么也说明不了。
在 Archboard 中绘制
Archboard 的 Kubernetes 节点包为包括 IngressClass、Gateway 和 Route 在内的上述对象提供了带端口的一等节点,Network 节点包则涵盖 DNS 和负载均衡器。完成版就是下面这张图,在 Archboard 中打开 会把它复制到你的工作区;/examples/kubernetes-ingress-flow 是一个更小的 Ingress → Service → Pods 草图。要自己动手绘制,请按以下步骤:
- 新建一张图,放一个客户端(Core 包的 Box 或 Icon,或 C4 包的 Person),再从 Network 包中添加一个 DNS 节点和一个负载均衡器节点。
- 添加一个名为
ingress-nginx的 Namespace 框,在里面放控制器的 Deployment 和 Pod。来自负载均衡器的连线接到这个 Pod,因为它才是代理。 - 再添加一个名为
shop的 Namespace 框,在里面放 Ingress、一个 Secret(shop-example-com-tls)以及两个 Service 节点orders-api和storefront,每个 Service 各连一个 Deployment 和一个 Pod。 - 沿数据路径连接端口:客户端 → DNS → 负载均衡器 → 控制器 Pod → 各个 Service(连线标注
/api和/)→ Pod。把 Service → Pod 的连线标注为“EndpointSlices”,让读者看清流量是如何到达 Pod 的。 - 用一条虚线“监听”连线把控制器连到 Ingress,再把 Secret 连到 Ingress。
- 在
shop框中添加一个 NetworkPolicy 节点,并把它连到它所放行的“控制器 → 应用”这一跳。 - 用 Core 包的 Frame 或 Box 圈出加密区段来标出 TLS 边界,并注明在哪里终止。
- 把 Ingress 的 YAML 粘贴到画布上的 Markdown 块中,放在它所描述的方框旁边。
图表内容
- Kubernetes cluster
- ingress-nginx
- shop
- Clients
- DNS · shop.example.com
- Cloud load balancer
- ingress-nginx-controller
- shop-example-com-tls
- orders-api
- storefront
- allow-from-ingress
- DNS lookup
- HTTPS :443
- TCP :443
- /api
- /
- EndpointSlices
- allows ingress-nginx → :8080
- watches
- TLS certificate
Free 方案每张图最多 15 个图表节点,另加 150 个便签和文本。上面的完整版本用了 18 个,因此请去掉包括控制器在内的三个 Deployment 节点,把 Service 直接连到 Pod,流量路径依然完整。可以导出为 SVG 或 PNG(Free 方案带水印),也可以导出为结构化 JSON,和清单文件放在一起管理。如需查看整个集群的全景,请参阅 /examples/kubernetes-microservices 和 面向 Kubernetes 的 Archboard。官方参考是 Kubernetes Ingress 文档。
常见问题
Ingress 应该画在应用的 namespace 里吗? 是的。Ingress 是属于 namespace 的对象,只能引用同一 namespace 中的 Service 和 TLS Secret。控制器通常运行在单独的 namespace 中。
流量会经过 Service 的 ClusterIP 吗? 取决于控制器。许多控制器根据 EndpointSlices 直接代理到 Pod IP。无论如何都要画出 Service,并在连线上加以标注。
新集群该用 Ingress 还是 Gateway API? 如果你的控制器支持,选 Gateway API:Ingress API 已被冻结。画图的方法是一样的。