Repository Reading Site
第十四课:Ingress-nginx、反向代理、Host / Path、NodePort 与北南向流量原理
上一课我们已经把集群内部服务发现讲清楚了: 但那条链主要还是: 现在你必须再打通另一条链: 这就是北南向流量,也就是 north-south 流量。 如果这条线不通,真实生产里你会频繁卡在这些问题上: 1. Service 明明正常,为什么浏览器还是打不开 2. 为什么访问同一个 NodePort,不同 `Host` 头结果完全不一样 3. 为什么没有 `H
第十四课:Ingress-nginx、反向代理、Host / Path、NodePort 与北南向流量原理
为什么这一课必须接在 Service 之后
上一课我们已经把集群内部服务发现讲清楚了:
- Service 给一组动态 Pod 提供稳定入口
- EndpointSlice 维护后端成员表
- kube-proxy 负责把 ClusterIP 转发到 Pod
- CoreDNS 负责把服务名解析出来
但那条链主要还是:
集群内部东西南北互通,也就是 east-west 流量。
现在你必须再打通另一条链:
集群外的请求,到底怎样进入集群,命中哪条规则,再被反向代理到后端 Service。
这就是北南向流量,也就是 north-south 流量。
如果这条线不通,真实生产里你会频繁卡在这些问题上:
- Service 明明正常,为什么浏览器还是打不开
- 为什么访问同一个 NodePort,不同
Host头结果完全不一样 - 为什么没有
Host头时常见的是404 - 为什么 Ingress 明明有规则,但访问出来是
503 - 为什么 Ingress 对象创建了却根本不生效
- Ingress、NodePort、LoadBalancer、Gateway API 到底是什么关系
这一课的目标不是“会写一个 Ingress YAML”,而是把:
- 四层入口
- 七层反向代理
- Host / Path 匹配
- Ingress controller 模式
- NodePort 数据平面
这整条链真正看懂。
先建立总图:外部请求进集群通常有哪几层
你以后脑子里要有一个非常清晰的分层模型。
第一层:客户端
可能是:
- 浏览器
curl- 手机 App
- 第三方系统
它发出来的是 HTTP 或 HTTPS 请求。
第二层:节点入口
最基础的暴露方式通常是:
NodePort- 云厂商
LoadBalancer - 主机网络监听
在这套集群里,ingress-nginx-controller 是通过:
NodePort 30080/30443
对外暴露的。
第三层:Ingress Controller
它本质上是一个反向代理程序加一个 Kubernetes 控制器。
在这套集群里,这个角色是:
ingress-nginx
第四层:后端 Service
Ingress 不会直接找 Pod。
它先把流量路由到:
Service
然后再由 Service 对应的后端成员表去找到实际 Pod。
第五层:业务 Pod
真正处理请求的是:
- 业务容器
所以你以后要把北南向流量的链路理解成:
Client
-> NodeIP:NodePort
-> ingress-nginx controller
-> Ingress 规则匹配
-> backend Service
-> backend Pod
这条链里任何一层出问题,最终用户看到的可能都只是:
- 404
- 503
- 502
- 连接超时
真正的工程能力,就是把这个表象拆回分层。
先分清几个最容易混淆的对象
Service
作用是:
给后端 Pod 集合一个稳定入口。
它偏四层语义:
- IP
- port
- TCP / UDP
NodePort
它不是独立资源类型,而是 Service 的一种暴露方式。
作用是:
在每个节点上开一个固定端口,把请求送进对应 Service。
Ingress
它不是数据平面程序,只是声明式路由规则。
它描述的是:
- 哪个
Host - 哪个
Path - 转给哪个后端 Service
它偏七层 HTTP/HTTPS 语义。
Ingress Controller
这才是真正把 Ingress 规则落地的人。
它会:
- watch Ingress、Service、EndpointSlice
- 生成代理配置
- 让真实流量按规则走
所以:
Ingress 对象本身不会转发任何流量,Controller 才会。
Gateway API
它可以理解成 Ingress 之后的下一代入口 API 家族。
它更强、更细粒度、更模块化。
但注意:
不是仓库里写了“Gateway API”四个字,集群里就真的有 Kubernetes Gateway API CRD。
这一课我专门做了验证:这套集群当前并没有安装 GatewayClass / Gateway / HTTPRoute 这些 CRD。
也就是说:
- 当前这套 K8s 入口能力,真实可用的是
Ingress - 不是 Kubernetes Gateway API
这一步非常值得你记住,因为专家不能靠名字猜能力,必须靠事实验证能力。
这套集群里的真实入口底座
这一课我先核对了这套真实集群,而不是直接上实验。
1. ingress-nginx-controller 是通过 NodePort 暴露的
我看到:
- Service 名称:
ingress-nginx-controller - 类型:
NodePort - ClusterIP:
10.109.198.120 - HTTP:
80 -> 30080 - HTTPS:
443 -> 30443
而且这个 Service 的:
externalTrafficPolicy: Cluster
这一点非常关键。
它意味着:
任意节点都可以接受这个 NodePort 流量,再由 kube-proxy 转发到真正的 ingress controller Pod。
2. ingress controller 真实跑在哪
我看到 controller Pod 是:
ingress-nginx-controller-c9f8d656d-jj7d4- Pod IP:
10.244.119.205 - 节点:
us590068728056
这意味着:
- controller 并不在每个节点都跑
- 但 NodePort 仍然能在其他节点上生效
这就是 kube-proxy 跨节点转发在起作用。
3. IngressClass 只有一个 nginx
我看到:
IngressClass/nginxspec.controller = k8s.io/ingress-nginx
而 controller 启动参数里也明确有:
--controller-class=k8s.io/ingress-nginx--ingress-class=nginx
这两边拼起来的意义是:
这个 controller 只会认领 class 为
nginx、且 controller 字段匹配的 Ingress。
4. 这套集群没有安装 Kubernetes Gateway API CRD
我实际执行:
kubectl get gatewayclass,gateway,httproute -A
得到的是:
error: the server doesn't have a resource type "gatewayclass"
这说明:
GatewayClassGatewayHTTPRoute
这些资源类型压根不存在。
所以以后你看到“Gateway API”这个词,第一反应应该是:
- 这是产品名、系统名,还是 Kubernetes CRD 真存在?
不要混。
Host 到底为什么这么重要
这是入口流量最容易“会用但没想透”的地方。
HTTP/1.1 以后,请求头里通常会带:
Host: xxx
Ingress controller 正是依赖这个字段判断:
- 这个请求该进哪个虚拟主机
也就是所谓:
- virtual host
- vhost
- 基于域名的七层路由
所以同样打到:
10.10.0.1:30080
如果 Host 不同,命中的规则就可能完全不同。
如果 Host 根本不匹配任何 Ingress 规则,常见结果就是:
404 Not Found
这个 404 不是业务应用返回的,而是 ingress controller 的默认 server 返回的。
再往深一点:HTTPS 时还会涉及 SNI
如果是 HTTPS,除了 HTTP 层的 Host 头,还会有 TLS 握手阶段的:
- SNI
也就是 Server Name Indication。
它决定:
- TLS 握手时该拿哪张证书
所以你以后要分清:
Host偏 HTTP 层路由SNI偏 TLS 层证书选择
这次实验重点做 HTTP,但概念上你必须把这两层分开。
404、503、502 在 Ingress 语境里通常分别意味着什么
这部分是生产排障的高频核心。
404 Not Found
常见表示:
- 请求已经到达 ingress controller
- 但没有命中任何有效路由规则
典型原因:
Host不匹配Path不匹配- Ingress 根本没被 controller 认领
503 Service Temporarily Unavailable
常见表示:
- 路由规则命中了
- 但后端没有可用 upstream
典型原因:
- Service 没有 active endpoints
- Pod 还没 Ready
- 后端全挂了
502 Bad Gateway
常见表示:
- ingress controller 尝试和上游通信
- 但上游响应异常、握手异常或链路异常
典型原因:
- 上游端口不通
- 协议对不上
- 后端连接被重置
注意:
这不是绝对法则,但作为第一层排障判断非常有用。
这一课里我真实做出了:
404503
而 502 我在本机公网路径上也碰到过一个看起来像外部环境干扰的样本,所以操作记录里我会把“如何把环境干扰和 Kubernetes 行为分离”也写进去。
本次实验的设计
这一课我没有直接拿现成业务做实验,而是专门做了一个入口流量训练场。
正常后端 1:portal
作用:
- 作为
/根路径后端
正常后端 2:api
作用:
- 作为
/api路径后端
故障后端:broken-svc
作用:
- Service 存在
- 但 selector 指向不存在的 Pod
- 用来演示“规则命中了,但后端没活着”的
503
正常 Ingress:app-ingress
规则:
Host = apps.k8s-lab.local/api->api-svc/broken->broken-svc/->portal-svc
错误 Class 的 Ingress:ignored-ingress
规则:
Host = ignored.apps.k8s-lab.localingressClassName = ghost
它的作用不是转发成功,而是演示:
Ingress 对象创建出来了,但没有 controller 认它,所以不会真正落地。
真实实验里发生了什么
下面这几组现象,你要能完整复述。
现象 1:app-ingress 被认领了,ignored-ingress 没有
我看到:
-
app-ingressClass: nginxAddress: 10.109.198.120Events: Sync
-
ignored-ingressClass: ghostAddress:空Events:空
而 controller 日志里更明确:
Found valid IngressClass ingress-lab/app-ingressIgnoring ingress ... ignored-ingress ... no object matching key "ghost"
这说明:
- Ingress controller 会先校验 IngressClass
- class 不匹配时,对象存在也没用
这就是为什么我前面一直强调:
Ingress 是声明,controller 才是执行者。
现象 2:controller 真的把规则写进了 Nginx 配置
我进入 ingress-nginx-controller 容器,直接看 /etc/nginx/nginx.conf,看到了:
## start server apps.k8s-lab.localserver_name "apps.k8s-lab.local"location "/broken/"location = "/broken"location "/api/"location = "/api"location "/"
而且还看到:
service_name "broken-svc"service_name "api-svc"service_name "portal-svc"
但没有看到:
ignored.apps.k8s-lab.local
这说明:
- controller 确实把 Kubernetes Ingress 规则翻译成了 Nginx 配置
- 它会为不同路径生成不同 location
- 没被认领的 Ingress 根本不会出现在代理配置里
现象 3:从集群内打 ingress Service,根路径和 /api/ 都成功
我在 ingress-client 里执行:
- 带
Host: apps.k8s-lab.local请求/ - 返回
portal
执行:
- 带
Host: apps.k8s-lab.local请求/api/ - 返回
api
这证明:
- 七层路由规则已经生效
- 同一个 ingress controller,可以按
Host + Path把请求送到不同后端 Service
现象 4:/broken 返回 503
同样带正确 Host 请求 /broken,我看到:
503 Service Temporarily Unavailable
而 describe ingress 里也能看到:
broken-svc:80 ()
括号里没有任何 endpoint。
controller 日志也持续告警:
Service "ingress-lab/broken-svc" does not have any active Endpoint
这说明:
- 路由命中了
- 但上游不可用
这正是 Ingress 语境里最经典的 503 含义。
现象 5:没有 Host 或 Host 指向未生效 Ingress 时,返回 404
我在 control-plane 节点本机执行:
curl http://127.0.0.1:30080/- 返回
404
再执行:
curl -H 'Host: ignored.apps.k8s-lab.local' http://127.0.0.1:30080/- 仍然
404
这说明:
- 请求已经到达 ingress-nginx
- 但没有命中有效 server block
你要特别注意这里的分层:
- 不是 Service 不通
- 不是 Pod 不通
- 是七层路由没命中
现象 6:/api 先 302,再 /api/ 返回 200
我在节点上直接请求:
/api
得到:
302 FoundLocation: /api/
再请求:
/api/
得到:
200 OK- 返回
api后端内容
这个现象很适合训练你做边界判断。
这里的 302 不是 ingress controller 在改路由策略,而是:
- backend 用的是
busybox httpd /api是目录- 后端自己做了目录标准化重定向
这件事非常重要,因为真实生产里你一定会遇到:
- 入口规则没错
- 但最终行为其实是后端应用自己的逻辑
所以不能把所有入口现象都归因给 Ingress。
NodePort 到底怎样把流量送进 ingress controller
这一段是本课最硬核的部分。
我在 control-plane 节点 iptables-save 里看到:
KUBE-NODEPORTS ... --dport 30080 -> KUBE-EXT-CG5I4G2RS3ZVWGLKKUBE-EXT-CG5I4G2RS3ZVWGLK -> KUBE-SVC-CG5I4G2RS3ZVWGLKKUBE-SVC-CG5I4G2RS3ZVWGLK -> 10.244.119.205:80
同时还看到:
10.109.198.120:80 -> KUBE-SVC-CG5I4G2RS3ZVWGLK
也就是说:
- 打节点
30080 - 先命中 NodePort 规则
- 再进入 ingress controller 这个 Service 的链
- 最终 DNAT 到 controller Pod
10.244.119.205:80
为什么这个证据特别重要
因为它把下面两件事打通了:
- NodePort 是怎么把外部流量送进集群的
- ingress-nginx Service 和 controller Pod 是怎样衔接的
很多人会把 “Ingress” 当作一个抽象能力,但真正落地时,它依然离不开:
- Service
- kube-proxy
- 内核转发表
这就是 Kubernetes 分层组合的本质。
为什么打 control-plane 的 30080 也能进到 worker 上的 controller Pod
这一点你必须能讲清楚。
controller Pod 明明跑在:
us590068728056
而我却在 control-plane 节点上访问:
10.10.0.1:30080
照样能拿到 portal 和 api 的响应。
根因有两个。
第一:NodePort 是每个节点都监听的“逻辑端口”
只要这个节点上有 kube-proxy 规则,它就能接受该 NodePort 流量。
第二:这套 Service 的 externalTrafficPolicy = Cluster
这表示:
接到流量的节点,不要求自己本地必须有后端,它可以把流量继续转发到别的节点上的后端。
所以 control-plane 节点即使自己没有 ingress controller Pod,也能把请求送到:
10.244.119.205:80
再由 ingress controller 继续代理到业务后端。
这就是 NodePort 全节点可达的根本原因之一。
为什么 127.0.0.1:30080 在这套集群里也能访问
这不是所有人都会想到的问题。
我能在 control-plane 节点上直接访问:
http://127.0.0.1:30080
原因不是“NodePort 天然就支持 localhost”,而是 kube-proxy 日志里明确写了:
Setting route_localnet=1 to allow node-ports on localhost
这意味着:
- kube-proxy 在这套 iptables 模式配置里专门允许了 localhost 命中 NodePort
这类细节你记住后,很多“为什么我本机打 localhost 也能通”的问题就不再神秘了。
这次还顺手验证了一个很重要的方法论:先排除外部环境干扰
我在自己这台工作机上直接访问公网节点 30080 时,一度得到过:
502 Bad GatewayEmpty reply from server
而同样的请求,一旦 SSH 到节点本机再执行 curl,结果就稳定符合预期:
/->200/api/->200/broken->503- 无 Host ->
404
这件事非常值得你记住。
因为它说明:
外部客户端看到的异常,不一定就是 Kubernetes 本身的问题。
中间还可能夹着:
- 本机代理
NO_PROXY配置- 本地安全软件
- 云厂商网络
- NAT / 防火墙
所以当现象诡异时,最好的办法是:
- 先在集群内验证
- 再在节点本机验证
- 最后才看公网链路
这样你就能把“集群内逻辑是否正确”和“外部网络环境是否干扰”分开。
作为专家,你应该怎样排 Ingress 问题
建议你按下面这个顺序来。
第一步:确认集群有没有入口控制器
kubectl -n ingress-nginx get all
kubectl get ingressclass
回答:
- 有没有 controller
- 有哪些 IngressClass
- 入口 Service 是怎么暴露的
第二步:看 Ingress 自己有没有被认领
kubectl describe ingress -n <ns> <name>
重点看:
Ingress ClassAddressEvents
如果 class 不对,controller 根本不会处理它。
第三步:看 controller 日志
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller
重点找:
Ignoring ingressdoes not have any active EndpointBackend successfully reloaded
这一步在本课里非常有价值,因为它直接暴露了:
ignored-ingress被忽略broken-svc没 active endpoint
第四步:看后端 Service 和 EndpointSlice
kubectl get svc,endpoints,endpointslices -n <ns>
这一步能快速把问题切开:
- 路由没命中
- 路由命中但后端不存在
- 路由命中且后端存在,但应用自己返回错误
第五步:必要时进 controller 容器看配置
kubectl -n ingress-nginx exec deploy/ingress-nginx-controller -- sed -n '1,260p' /etc/nginx/nginx.conf
这一步能回答:
- 规则到底有没有被渲染进去
- server_name 是什么
- location 怎么排序
第六步:必要时下沉到节点看 NodePort 规则
iptables-save
重点找:
KUBE-NODEPORTSKUBE-EXT-*KUBE-SVC-*KUBE-SEP-*
这一步能帮你回答:
- 节点到底有没有把 30080 收进来
- 是不是成功转到 ingress controller Pod
这节课你必须真正记住的判断句
- Ingress 不是数据平面程序,它只是路由规则;真正执行的是 Ingress Controller。
- NodePort 负责把流量引入集群,Ingress Controller 负责七层 HTTP/HTTPS 路由。
Host和Path是 Ingress 最核心的匹配维度,没有命中通常就是404。503往往意味着规则命中了,但后端 upstream 不可用。- IngressClass 决定某个 Ingress 会不会被某个 controller 认领。
- Ingress 最终仍然要依赖 Service、EndpointSlice 和 kube-proxy。
- 入口问题必须按层排,不能把所有 4xx / 5xx 都归到 Ingress 头上。
- 本机公网访问异常时,先用“集群内 + 节点本机”验证,避免被外部网络环境误导。
你现在具备了什么能力
学完这一课,你已经不只是“会写 Ingress YAML”了。
你开始具备的是:
- 能分清四层入口和七层路由
- 能解释 NodePort、Service、Ingress Controller 的组合关系
- 能读
404、503背后的不同故障层级 - 能判断 Ingress 有没有被正确认领
- 能下沉到 Nginx 配置和
iptables规则验证实际流量路径
这已经是平台工程和生产排障里非常核心的一组能力。