HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • 概览

    • K8s 实战学习实验室
    • 服务访问清单
    • K8s-Lab 学习总纲、仓库评估与专家路线图
  • 课程正文

    • 环境验证与第一课:认识你的真实集群
    • 第二课:kubectl apply 之后,到底发生了什么
    • 第三课:调度器如何选节点,为什么 Pod 会 Pending
    • 第四课:Kubernetes 网络、协议分层、VXLAN/IPIP/WireGuard 原理与排障
    • 第五课:NetworkPolicy、零信任网络与流量边界
    • 第六课:身份、认证、授权、准入与 ServiceAccount / RBAC 原理
    • 第七课:ConfigMap 与 Secret 注入模型、更新机制与安全边界
    • 第八课:存储持久化、PV / PVC / StorageClass 与 NFS 原理
    • 第九课:StatefulSet、Headless Service、稳定身份与存储原理
    • 第十课:探针、滚动更新、优雅终止与 PDB 原理
    • 第十一课:requests / limits、QoS、OOM 与驱逐原理
    • 第十二课:HPA、自动扩缩容、指标链路与副本伸缩原理
    • 第十三课:Service、EndpointSlice、kube-proxy、CoreDNS 与服务发现原理
    • 第十四课:Ingress-nginx、反向代理、Host / Path、NodePort 与北南向流量原理
    • 第十五课:HTTPS、TLS、SNI、证书信任与 Ingress 终止原理
    • 第十六课:cert-manager、Ingress 自动签发、证书生命周期与 ACME 工作流原理
    • 第十七课:ACME、Let's Encrypt、HTTP-01 / DNS-01、Orders / Challenges 与生产限制原理
    • 第十八课:大模型全生态,从数据到训练到部署到治理原理
    • 第十九课:大模型数据集、清洗、标注、切分、版本管理与质量治理原理
    • 第二十课:大模型训练、SFT、LoRA、Checkpoint、Adapter 与模型产物原理
    • 第二十一课:大模型推理、量化、KV Cache、vLLM、吞吐/延迟与部署发布链路原理
  • 实验操作记录

    • 本次仓库审查操作记录与命令原理
    • 本轮操作记录:环境验证、集群基线盘点与故障样本采集
    • 本轮操作记录:kubectl apply 主链路实验
    • 本轮操作记录:调度实验与 Pending 排查
    • 本轮操作记录:Kubernetes 网络原理、协议对比与调试实验
    • 本轮操作记录:NetworkPolicy 与零信任网络实验
    • 本轮操作记录:身份、认证、授权、准入实验
    • 本轮操作记录:ConfigMap 与 Secret 注入、更新与安全边界实验
    • 本轮操作记录:存储持久化、PV / PVC / StorageClass 与 NFS 实验
    • 本轮操作记录:StatefulSet、Headless Service 与稳定身份实验
    • 本轮操作记录:探针、滚动更新、优雅终止与 PDB 实验
    • 本轮操作记录:资源模型、QoS、OOM 与 CPU 节流实验
    • 本轮操作记录:HPA 自动扩缩容实验
    • 本轮操作记录:Service、EndpointSlice、CoreDNS 与服务发现排障实验
    • 本轮操作记录:Ingress-nginx、NodePort 与北南向流量实验
    • 本轮操作记录:HTTPS、TLS、自签证书与 Ingress 实验
    • 本轮操作记录:cert-manager 安装、CA 签发与 Ingress 自动证书实验
    • 本轮操作记录:ACME staging、HTTP-01 失败样本与排障实验
    • 本轮操作记录:大模型全生态与基础原理科普文撰写
    • 本轮操作记录:大模型数据集样本与治理文档编写
    • 本轮操作记录:大模型训练与模型产物概念文撰写
    • 本轮操作记录:大模型推理与服务发布概念文撰写

本轮操作记录:Ingress-nginx、NodePort 与北南向流量实验

本轮目标

这一轮我要把下面这些事做成事实:

  1. 这套集群当前入口层到底是不是 ingress-nginx
  2. Kubernetes Gateway API 在这套集群里到底有没有安装
  3. IngressClass 怎样决定某个 Ingress 会不会生效
  4. 外部流量怎样从 NodePort 进入 ingress controller
  5. 为什么同样是打 30080,不同 Host 会得到 200 / 404 / 503
  6. 为什么 503 常常表示“后端没活着”,不是“入口没命中”

Step 1:先核对入口底座,不做想当然假设

实际命令

KUBECONFIG=~/.kube/config-k8s-lab kubectl -n ingress-nginx get all -o wide

KUBECONFIG=~/.kube/config-k8s-lab kubectl get ingress -A

KUBECONFIG=~/.kube/config-k8s-lab kubectl get gatewayclass,gateway,httproute -A

KUBECONFIG=~/.kube/config-k8s-lab kubectl api-resources | grep -E 'Ingress|Gateway|HTTPRoute|GatewayClass'

sed -n '1,140p' SERVICE-ACCESS.md

为什么先做这一步

因为仓库里的名字和真实集群能力不一定完全等价。

例如 SERVICE-ACCESS.md 里写了:

  • Gateway API

但这不代表 Kubernetes 集群里真的装了 Gateway API CRD。

专家的第一反应应该是:

  • 先验证资源类型是否存在
  • 再验证对应 controller 是否存在

我看到的结果

ingress-nginx

  • controller Deployment 存在
  • Pod 正常运行
  • Service ingress-nginx-controller 类型是 NodePort
  • 80:30080/TCP
  • 443:30443/TCP

集群里的 Ingress

已有一个:

  • dev/demo-ingress

Gateway API

实际执行 kubectl get gatewayclass,gateway,httproute -A 返回:

error: the server doesn't have a resource type "gatewayclass"

而 api-resources 里只有:

  • IngressClass
  • Ingress

没有:

  • GatewayClass
  • Gateway
  • HTTPRoute

原理解释

这一点非常重要。

它说明:

  1. 这套集群的真实北南向入口能力是 Ingress
  2. 当前并没有安装 Kubernetes Gateway API CRD
  3. 文档里某个“Gateway API”名字,可能是产品入口、系统名、业务名,不等于 K8s API 一定存在

这类“先验名字”和“真实能力”分离的意识,是专家必须具备的。


Step 2:核对 IngressClass、controller 参数和现有样本

实际命令

KUBECONFIG=~/.kube/config-k8s-lab kubectl get ingressclass -A -o yaml

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-nginx get deploy ingress-nginx-controller -o yaml | sed -n '1,240p'

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n dev get ingress demo-ingress -o yaml

我看到的关键结果

IngressClass

只有一个:

  • name: nginx
  • spec.controller: k8s.io/ingress-nginx

controller 启动参数

我看到关键参数:

  • --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
  • --controller-class=k8s.io/ingress-nginx
  • --ingress-class=nginx

现有样本

dev/demo-ingress 里明确写了:

  • ingressClassName: nginx

原理解释

这一步讲透了两个核心点。

第一:IngressClass 是“认领关系”的桥

controller 不是看见所有 Ingress 都处理。

它会先判断:

  • ingressClassName 是不是自己认的
  • 对应 IngressClass 的 spec.controller 是不是自己

第二:--publish-service 会影响 Ingress status.address

这就是为什么很多 Ingress 的 ADDRESS 字段会显示成:

  • controller Service 的 ClusterIP
  • 或者 LB 地址

不是凭空冒出来的。


Step 3:进入 controller 容器,确认它确实是“控制器 + 反向代理”

实际命令

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-nginx exec deploy/ingress-nginx-controller -- \
  sh -c 'sed -n "1,260p" /etc/nginx/nginx.conf'

为什么这一步很重要

因为我要把这件事从抽象概念变成你亲眼可见的事实:

Ingress controller 会监听 Kubernetes 对象,然后生成真正的 Nginx 代理配置。

我看到的结果

可以看到全局配置例如:

  • resolver 10.96.0.10
  • access_log /var/log/nginx/access.log
  • server { server_name "_" ... }

原理解释

这说明:

  • controller 不是一个只会调用 API 的“逻辑程序”
  • 它内部真的跑着 Nginx 数据平面
  • 并且会把 Kubernetes 资源翻译成 Nginx 配置

后面我还会进一步验证具体 server block。


Step 4:创建本课实验对象

实际命令

KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/14-ingress

创建了什么

ingress-lab 命名空间

用来隔离本课对象。

portal Deployment

作用:

  • 模拟根路径 / 后端

api Deployment

作用:

  • 模拟 /api 后端

portal-svc / api-svc

作用:

  • 为两个正常后端提供 Service

broken-svc

作用:

  • selector 指向不存在的 app=ghost
  • 故意制造“Service 存在但没有 Endpoint”的样本

app-ingress

规则:

  • Host = apps.k8s-lab.local
  • /api -> api-svc
  • /broken -> broken-svc
  • / -> portal-svc

ignored-ingress

规则:

  • Host = ignored.apps.k8s-lab.local
  • ingressClassName = ghost

目的:

  • 演示“对象创建成功,但 controller 不认,所以不会生效”

ingress-client

作用:

  • 作为集群内 HTTP 客户端

原理解释

这组实验的设计故意包含:

  • 成功样本
  • 路由命中但上游失败样本
  • 对象存在但 controller 忽略样本

因为只有把这三类现象并排放,你才能真正理解:

  • 404
  • 503
  • “Ingress 看起来存在但其实没生效”

到底分别对应哪一层。


Step 5:观察对象就绪后的真实状态

实际命令

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-lab rollout status deployment/portal --timeout=180s

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-lab rollout status deployment/api --timeout=180s

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-lab wait --for=condition=Ready pod/ingress-client --timeout=180s

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-lab get pod,svc,ingress,endpoints,endpointslices -o wide

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-lab describe ingress app-ingress

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-lab describe ingress ignored-ingress

我看到的关键结果

Pod

  • portal-8fdcd8777-9mbxd -> 10.244.147.102
  • api-6897b4fd7-hhx6d -> 10.244.147.103
  • ingress-client -> 10.244.147.104

三个 Pod 都在:

  • wk-1

Service

  • portal-svc -> ClusterIP 10.110.217.109
  • api-svc -> ClusterIP 10.109.19.60
  • broken-svc -> ClusterIP 10.106.225.55

Endpoints

  • portal-svc -> 10.244.147.102:8080
  • api-svc -> 10.244.147.103:8080
  • broken-svc -> <none>

Ingress

  • app-ingress
    • CLASS: nginx
    • ADDRESS: 10.109.198.120
  • ignored-ingress
    • CLASS: ghost
    • ADDRESS: 空

原理解释

这一组事实已经足够说明:

  1. app-ingress 被 controller 认领并回写了地址
  2. ignored-ingress 没被任何 controller 接手
  3. broken-svc 作为 Ingress 后端时,天然会成为后面 503 的候选样本

Step 6:验证 controller 是否真的渲染了这条 Ingress

实际命令

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-nginx exec deploy/ingress-nginx-controller -- \
  sh -c 'grep -n "apps.k8s-lab.local\|ignored.apps.k8s-lab.local\|broken-svc\|api-svc\|portal-svc" /etc/nginx/nginx.conf'

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-nginx exec deploy/ingress-nginx-controller -- \
  sh -c 'sed -n "548,1010p" /etc/nginx/nginx.conf'

我看到的结果

配置里明确出现:

  • ## start server apps.k8s-lab.local
  • server_name "apps.k8s-lab.local"
  • service_name "broken-svc"
  • service_name "api-svc"
  • service_name "portal-svc"

并且具体 location 包含:

  • location "/broken/"
  • location = "/broken"
  • location "/api/"
  • location = "/api"
  • location "/"

但是没有看到:

  • ignored.apps.k8s-lab.local

原理解释

这一步是本课最能打通“控制器模式”的证据之一。

它证明:

  1. app-ingress 已经被 controller 翻译成 Nginx server block
  2. 不同路径真的被翻译成不同 location
  3. ignored-ingress 因 class 不匹配,连配置文件都进不去

以后如果你怀疑 Ingress 规则没生效,进 controller 看生成配置是非常强的一招。


Step 7:先从集群内访问 ingress controller Service

实际命令

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-lab exec ingress-client -- \
  sh -c "wget -qO- --header='Host: apps.k8s-lab.local' http://ingress-nginx-controller.ingress-nginx.svc.cluster.local/"

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-lab exec ingress-client -- \
  sh -c "wget -S -O- --header='Host: apps.k8s-lab.local' http://ingress-nginx-controller.ingress-nginx.svc.cluster.local/api/ 2>&1"

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-lab exec ingress-client -- \
  sh -c "wget -S -O- --header='Host: apps.k8s-lab.local' http://ingress-nginx-controller.ingress-nginx.svc.cluster.local/broken 2>&1"

为什么先从集群内打

因为我要先排除公网链路、运营商、代理软件等外部干扰。

只要在集群内打 ingress controller Service 正常,我们就能先确认:

  • Ingress 规则本身
  • controller 本身
  • 后端 Service

这一整段逻辑是成立的。

我看到的结果

/

返回:

app=portal
pod=portal-8fdcd8777-9mbxd
pod_ip=10.244.147.102
node=wk-1
route=/

/api/

返回:

HTTP/1.1 200 OK
...
app=api
pod=api-6897b4fd7-hhx6d
pod_ip=10.244.147.103
node=wk-1
route=/api

/broken

返回:

HTTP/1.1 503 Service Temporarily Unavailable

原理解释

这一步说明:

  1. 带正确 Host 时,ingress-nginx 能正确命中 server block
  2. / 和 /api/ 已经按路径分别路由到两个后端
  3. /broken 命中规则后,由于上游 broken-svc 没 Endpoint,所以返回 503

这里非常重要的一点是:

503 不是路由没命中,而是路由命中了,但 upstream 不可用。


Step 8:从 control-plane 节点本机验证 NodePort

实际命令

ssh root@107.148.176.193 \
  "curl -sS -D - http://127.0.0.1:30080/ -H 'Host: apps.k8s-lab.local'"

ssh root@107.148.176.193 \
  "curl -sS -D - http://127.0.0.1:30080/api -H 'Host: apps.k8s-lab.local'"

ssh root@107.148.176.193 \
  "curl -sS -D - http://127.0.0.1:30080/api/ -H 'Host: apps.k8s-lab.local'"

ssh root@107.148.176.193 \
  "curl -sS -D - http://127.0.0.1:30080/broken -H 'Host: apps.k8s-lab.local'"

ssh root@107.148.176.193 \
  "curl -sS -D - http://127.0.0.1:30080/"

ssh root@107.148.176.193 \
  "curl -sS -D - http://127.0.0.1:30080/ -H 'Host: ignored.apps.k8s-lab.local'"

ssh root@107.148.176.193 \
  "curl -sS -D - http://10.10.0.1:30080/ -H 'Host: apps.k8s-lab.local'"

我看到的关键结果

正确 Host + /

返回 200 OK,正文是:

app=portal
...

正确 Host + /api

返回:

HTTP/1.1 302 Found
Location: /api/

正确 Host + /api/

返回 200 OK,正文是:

app=api
...

正确 Host + /broken

返回:

HTTP/1.1 503 Service Temporarily Unavailable

没有 Host

返回:

HTTP/1.1 404 Not Found

Host 指向 ignored-ingress

仍然:

HTTP/1.1 404 Not Found

打 10.10.0.1:30080

也能返回正常 portal 内容。

原理解释

这一组现象把入口排障最重要的几个状态全部做实了。

200

说明:

  • 命中了有效 Host / Path
  • upstream 存在

302

这次不是 ingress controller 自己的路由变化,而是 backend busybox httpd 对目录 /api 做了标准化跳转到 /api/

503

说明:

  • 命中了 app-ingress
  • 命中了 /broken
  • 但上游 broken-svc 没活跃 Endpoint

404

说明:

  • 请求到了 ingress-nginx
  • 但没命中任何有效虚拟主机

ignored.apps.k8s-lab.local 也返回 404,正好反证:

  • ignored-ingress 没被 controller 接手

Step 9:核对 controller 日志,把“认领 / 忽略 / 无后端 / 请求结果”串起来

实际命令

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller --tail=60

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller --tail=60 | grep 'ingress-lab/'

我看到的关键结果

日志里明确出现:

  • Found valid IngressClass ... app-ingress
  • Ignoring ingress ... ignored-ingress ... no object matching key "ghost"
  • Service "ingress-lab/broken-svc" does not have any active Endpoint.

同时访问日志还记录了:

  • GET /broken -> 503 -> upstream name ingress-lab-broken-svc-80
  • GET /api -> 302 -> upstream 10.244.147.103:8080
  • GET / -> 200 -> upstream 10.244.147.102:8080

原理解释

这一层证据价值非常高,因为它把:

  • 控制器认领逻辑
  • 配置重载
  • 后端可用性
  • 最终请求结果

全部放在一条观测线上。

以后你排 Ingress 问题时,controller 日志是必查项,不是可选项。


Step 10:下沉到节点,看 NodePort 与 Service 规则

实际命令

ssh root@107.148.176.193 \
  'sudo iptables-save | grep -n -C 3 -E "30080|ingress-nginx-controller:http"'

ssh root@107.148.176.193 \
  'sudo iptables-save | grep -n -C 2 10.109.198.120'

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n ingress-nginx get svc ingress-nginx-controller -o yaml | sed -n '1,220p'

我看到的关键结果

Service 定义

  • type: NodePort
  • clusterIP: 10.109.198.120
  • nodePort: 30080
  • externalTrafficPolicy: Cluster

iptables

我看到了链路:

  • KUBE-NODEPORTS --dport 30080 -> KUBE-EXT-CG5I4G2RS3ZVWGLK
  • KUBE-EXT-CG5I4G2RS3ZVWGLK -> KUBE-SVC-CG5I4G2RS3ZVWGLK
  • KUBE-SVC-CG5I4G2RS3ZVWGLK -> 10.244.119.205:80

以及 ClusterIP 规则:

  • 10.109.198.120:80 -> KUBE-SVC-CG5I4G2RS3ZVWGLK

原理解释

这说明外部打到节点 30080 后:

  1. 先命中 NodePort 规则
  2. 进入 ingress controller 对应的 Service 链
  3. 最终 DNAT 到 ingress controller Pod 10.244.119.205:80

也就是说,Ingress 入口虽然表现成“七层反向代理”,但在它之前仍然有一段典型的 Kubernetes Service / kube-proxy 数据平面。


Step 11:解释为什么 127.0.0.1:30080 也能访问

实际命令

KUBECONFIG=~/.kube/config-k8s-lab \
kubectl -n kube-system logs ds/kube-proxy --tail=80 | grep -n 'route_localnet\|localhost'

我看到的结果

日志明确写了:

Setting route_localnet=1 to allow node-ports on localhost

原理解释

这说明当前这套 kube-proxy iptables 配置允许:

  • NodePort 命中 localhost

所以:

  • 127.0.0.1:30080

能够正常进入 NodePort 链路,并不是偶然现象。


Step 12:处理一个“工作机公网访问异常”的干扰样本

实际命令

env | grep -i proxy

curl -sS -D - http://107.148.176.193:30080/

curl --noproxy '*' -sS -D - http://107.148.176.193:30080/ -H 'Host: apps.k8s-lab.local'

我看到的结果

当前工作机里存在:

  • HTTP_PROXY=http://127.0.0.1:7890
  • HTTPS_PROXY=http://127.0.0.1:7890
  • NO_PROXY=localhost,127.0.0.1

而直接从这台工作机打公网 NodePort 时,一度出现了:

  • 502 Bad Gateway
  • curl: (52) Empty reply from server

原理解释

这一步很有实战意义。

因为它提醒你:

入口异常不一定都在 Kubernetes 内部,客户端本机网络环境也可能干扰结果。

所以我在本课里采用了更稳妥的方法:

  1. 先从集群内访问 controller Service
  2. 再从节点本机访问 NodePort
  3. 最后才看工作机公网访问

这就是把问题边界一层层缩小的标准工程方法。


Step 13:整理本轮最重要的结论

结论 1:这套集群当前用的是 Ingress,不是 Kubernetes Gateway API

这是事实验证得出的,不是名称猜测。

结论 2:Ingress 对象不会自己转发流量,controller 才会

ignored-ingress 已经把这一点证明得很清楚。

结论 3:404 和 503 代表的层次不同

  • 404 更偏“没命中有效路由”
  • 503 更偏“命中了路由,但后端不可用”

结论 4:NodePort 只是入口引流层,真正的 HTTP 路由发生在 ingress controller

这是四层与七层分工的典型体现。

结论 5:入口问题必须分层排查

不能把所有错误都笼统叫做:

  • “Ingress 不通”

本轮最值得记住的命令与用途

kubectl get ingressclass

看 controller 能认哪些 IngressClass。

kubectl describe ingress

看 Ingress 是否被认领、是否有地址、是否有同步事件。

kubectl logs deploy/ingress-nginx-controller

看 controller 是否忽略对象、是否发现后端没 Endpoint、是否重载配置。

kubectl exec ... /etc/nginx/nginx.conf

看 Ingress 规则是否真的被渲染进 Nginx。

curl -H 'Host: ...'

验证虚拟主机和路径路由。

iptables-save

看 NodePort 和 Service 在节点上的真实转发表。


这一课给你的专家能力提升点

如果你把这轮实验真正吃透,你已经具备了:

  • 把入口问题拆成四层引流和七层路由两段来分析的能力
  • 判断 Ingress 有没有被 controller 正确认领的能力
  • 区分 404 与 503 故障层级的能力
  • 下沉到 nginx.conf 和 iptables 做实证排障的能力

这已经是平台工程、架构设计和生产事故排查里非常核心的一块基本功。

Prev
本轮操作记录:Service、EndpointSlice、CoreDNS 与服务发现排障实验
Next
本轮操作记录:HTTPS、TLS、自签证书与 Ingress 实验