K8s Lab 把当前仓库文档整理成一个可阅读的网页站点

Repository Reading Site

第十四课:Ingress-nginx、反向代理、Host / Path、NodePort 与北南向流量原理

上一课我们已经把集群内部服务发现讲清楚了: 但那条链主要还是: 现在你必须再打通另一条链: 这就是北南向流量,也就是 north-south 流量。 如果这条线不通,真实生产里你会频繁卡在这些问题上: 1. Service 明明正常,为什么浏览器还是打不开 2. 为什么访问同一个 NodePort,不同 `Host` 头结果完全不一样 3. 为什么没有 `H

Markdown14-第十四课-Ingress-nginx-反向代理-Host-Path-NodePort与北南向流量原理.md2026年4月10日 06:56

第十四课:Ingress-nginx、反向代理、Host / Path、NodePort 与北南向流量原理

为什么这一课必须接在 Service 之后

上一课我们已经把集群内部服务发现讲清楚了:

  • Service 给一组动态 Pod 提供稳定入口
  • EndpointSlice 维护后端成员表
  • kube-proxy 负责把 ClusterIP 转发到 Pod
  • CoreDNS 负责把服务名解析出来

但那条链主要还是:

集群内部东西南北互通,也就是 east-west 流量。

现在你必须再打通另一条链:

集群外的请求,到底怎样进入集群,命中哪条规则,再被反向代理到后端 Service。

这就是北南向流量,也就是 north-south 流量。

如果这条线不通,真实生产里你会频繁卡在这些问题上:

  1. Service 明明正常,为什么浏览器还是打不开
  2. 为什么访问同一个 NodePort,不同 Host 头结果完全不一样
  3. 为什么没有 Host 头时常见的是 404
  4. 为什么 Ingress 明明有规则,但访问出来是 503
  5. 为什么 Ingress 对象创建了却根本不生效
  6. 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/nginx
  • spec.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"

这说明:

  • GatewayClass
  • Gateway
  • HTTPRoute

这些资源类型压根不存在。

所以以后你看到“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,但概念上你必须把这两层分开。


404503502 在 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 尝试和上游通信
  • 但上游响应异常、握手异常或链路异常

典型原因:

  • 上游端口不通
  • 协议对不上
  • 后端连接被重置

注意:

这不是绝对法则,但作为第一层排障判断非常有用。

这一课里我真实做出了:

  • 404
  • 503

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.local
  • ingressClassName = ghost

它的作用不是转发成功,而是演示:

Ingress 对象创建出来了,但没有 controller 认它,所以不会真正落地。


真实实验里发生了什么

下面这几组现象,你要能完整复述。

现象 1:app-ingress 被认领了,ignored-ingress 没有

我看到:

  • app-ingress

    • Class: nginx
    • Address: 10.109.198.120
    • Events: Sync
  • ignored-ingress

    • Class: ghost
    • Address:
    • Events:

而 controller 日志里更明确:

  • Found valid IngressClass ingress-lab/app-ingress
  • Ignoring 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.local
  • server_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

这说明:

  1. controller 确实把 Kubernetes Ingress 规则翻译成了 Nginx 配置
  2. 它会为不同路径生成不同 location
  3. 没被认领的 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:没有 HostHost 指向未生效 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 Found
  • Location: /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-CG5I4G2RS3ZVWGLK
  • KUBE-EXT-CG5I4G2RS3ZVWGLK -> KUBE-SVC-CG5I4G2RS3ZVWGLK
  • KUBE-SVC-CG5I4G2RS3ZVWGLK -> 10.244.119.205:80

同时还看到:

  • 10.109.198.120:80 -> KUBE-SVC-CG5I4G2RS3ZVWGLK

也就是说:

  1. 打节点 30080
  2. 先命中 NodePort 规则
  3. 再进入 ingress controller 这个 Service 的链
  4. 最终 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

照样能拿到 portalapi 的响应。

根因有两个。

第一: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 Gateway
  • Empty reply from server

而同样的请求,一旦 SSH 到节点本机再执行 curl,结果就稳定符合预期:

  • / -> 200
  • /api/ -> 200
  • /broken -> 503
  • 无 Host -> 404

这件事非常值得你记住。

因为它说明:

外部客户端看到的异常,不一定就是 Kubernetes 本身的问题。

中间还可能夹着:

  • 本机代理
  • NO_PROXY 配置
  • 本地安全软件
  • 云厂商网络
  • NAT / 防火墙

所以当现象诡异时,最好的办法是:

  1. 先在集群内验证
  2. 再在节点本机验证
  3. 最后才看公网链路

这样你就能把“集群内逻辑是否正确”和“外部网络环境是否干扰”分开。


作为专家,你应该怎样排 Ingress 问题

建议你按下面这个顺序来。

第一步:确认集群有没有入口控制器

kubectl -n ingress-nginx get all
kubectl get ingressclass

回答:

  • 有没有 controller
  • 有哪些 IngressClass
  • 入口 Service 是怎么暴露的

第二步:看 Ingress 自己有没有被认领

kubectl describe ingress -n <ns> <name>

重点看:

  • Ingress Class
  • Address
  • Events

如果 class 不对,controller 根本不会处理它。

第三步:看 controller 日志

kubectl -n ingress-nginx logs deploy/ingress-nginx-controller

重点找:

  • Ignoring ingress
  • does not have any active Endpoint
  • Backend 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-NODEPORTS
  • KUBE-EXT-*
  • KUBE-SVC-*
  • KUBE-SEP-*

这一步能帮你回答:

  • 节点到底有没有把 30080 收进来
  • 是不是成功转到 ingress controller Pod

这节课你必须真正记住的判断句

  1. Ingress 不是数据平面程序,它只是路由规则;真正执行的是 Ingress Controller。
  2. NodePort 负责把流量引入集群,Ingress Controller 负责七层 HTTP/HTTPS 路由。
  3. HostPath 是 Ingress 最核心的匹配维度,没有命中通常就是 404
  4. 503 往往意味着规则命中了,但后端 upstream 不可用。
  5. IngressClass 决定某个 Ingress 会不会被某个 controller 认领。
  6. Ingress 最终仍然要依赖 Service、EndpointSlice 和 kube-proxy。
  7. 入口问题必须按层排,不能把所有 4xx / 5xx 都归到 Ingress 头上。
  8. 本机公网访问异常时,先用“集群内 + 节点本机”验证,避免被外部网络环境误导。

你现在具备了什么能力

学完这一课,你已经不只是“会写 Ingress YAML”了。

你开始具备的是:

  • 能分清四层入口和七层路由
  • 能解释 NodePort、Service、Ingress Controller 的组合关系
  • 能读 404503 背后的不同故障层级
  • 能判断 Ingress 有没有被正确认领
  • 能下沉到 Nginx 配置和 iptables 规则验证实际流量路径

这已经是平台工程和生产排障里非常核心的一组能力。