首页 / 资讯中心 / 文章详情

Nginx Ingress Controller 实战:从原理到排查的完整指南

Nginx Ingress Controller 实战:从原理到排查的完整指南 ★ FEATURED ARTICLE
Kubernetes 的系列文章写到第 19 篇前面把集群搭建、Pod、Deployment、Service 这些核心对象都过了一遍。但有一个问题几乎每篇留言区都会出现Service 不是已经能把流量带进 Pod 了吗为什么还要装一个叫 Ingress Controller 的东西答案其实不复杂Service 解决的是“集群内部怎么找到 Pod”的问题而 Ingress 解决的是“集群外部怎么把流量送进来、送到哪条路径上”的问题。第 18 篇结束后不少读者已经在自建集群里跑起了应用接下来自然要面对入口流量管理。这篇就把 Kubernetes 里最常用的 Nginx Ingress Controller 完整讲一遍从原理区分、版本匹配、安装方式、Demo 验证到上线后最常见的坑我把实际踩过的坑和排查顺序一并写出来。这篇适合两类人一类是刚把集群跑起来、准备用域名对外提供服务的新手另一类是已经部署过 Ingress 但总遇到 404、证书不生效、Pod 起不来之类问题、想系统梳理一遍的人。文章里的命令和 YAML 都是我实际跑过的照着抄基本能成但每一个关键步骤我会解释“为什么要这么做”方便你自己改。1. 为什么 Service 都配好了入口流量还得单独装一个组件1.1 Ingress 和 Ingress Controller 是两个容易混淆的角色很多初学者把 Ingress 当成一个“能转发流量的组件”这是第一个容易混淆的地方。实际上 Ingress 只是一份资源定义它描述“我期望到达域名 A 的请求被送到服务 X到达域名 B 且路径以 /api 开头的请求被送到服务 Y”。这份描述写完之后没有任何进程会去执行它必须有一个独立的控制器一直盯着这类资源看到有新的 Ingress 就解析规则、生成配置、加载配置并真正转发流量。Nginx Ingress Controller 就是这样一个控制器。它本身跑在集群里以 Deployment 或 DaemonSet 方式运行里面包含一个定制过的 Nginx 进程和一个和 Kubernetes API Server 通信的控制循环。通俗地讲Ingress 资源是“需求文档”Controller 是“执行部门”。需求文档写得再漂亮没有执行部门流量还是进不来。这个关系弄明白之后很多后续问题就迎刃而解了。比如你在集群里 apply 了一个 Ingress 资源发现完全没生效第一反应不应该是怀疑 YAML 写错了而是先问一句这个集群里到底有没有在运行并监听 Ingress 的控制器没装 Controller只创建一个 Ingress 对象就跟在空房间里贴了张快递柜使用说明一样什么都不会发生。1.2 三种暴露方式对比为什么 Ingress 通常是更合理的选择在第 18 篇里我聊过 Service 的几种类型NodePort 和 LoadBalancer 都是“把 Service 直接暴露出去”的思路。这两种方式在小规模场景下够用但当你服务多了之后问题会非常明显。每个通过 NodePort 暴露的服务都要占用一个端口端口数量有限不说客户端还得记住“哪个端口对应哪个服务”极其反人类。LoadBalancer 在云环境里意味着每个服务都创建一个云负载均衡器费用高、创建慢而且它不是七层路由做不到按域名、按路径转发。NodePort 和 LoadBalancer 都解决不了“同一域名下不同路径分给不同服务”这种需求。Ingress 站在所有 Service 前面对外只需要暴露一个入口内部按域名和路径把请求分发给不同的 Service。你可以理解成 Service 是各家商铺的门牌号Ingress 是商场的外大门访客只需要进商场的正门再根据导览牌找到对应商铺。这里想提一个常见争论是不是有了 Ingress 就不需要 NodePort 或 LoadBalancer 了并不是。Ingress Controller 本身也暴露在集群中大多数云环境里还是通过 LoadBalancer 类型的 Service 把 Controller 的端口暴露出去。所以更准确的说法是Ingress 把“每个服务都暴露端口”收敛成了“只暴露 Controller 一个入口”后面的服务通过集群内部网络转发整个架构的暴露面大大缩小。2. 动手之前先确认版本与运行模式能省掉一半的安装血泪2.1 Controller 版本和 K8s 版本如何对应直接决定你装完之后会不会报一堆 API 错误Ingress 资源从extensions/v1beta1走到networking.k8s.io/v1beta1再到现在的networking.k8s.io/v1经历了好几次版本变动。旧版本的 Controller 直接拿旧 API 去连接新集群轻则警告重则完全无法识别 Ingress 资源。另一个方向是很新的 Controller 如果去连特别老的集群也可能因为集群缺少某些 CRD 或者 webhook 机制而不工作。所以安装前第一件事是查官方兼容性矩阵。在 Nginx Ingress Controller 的官方发布说明里会明确标注每个 Controller 版本支持的 Kubernetes 版本范围。我自己的习惯是先确定集群版本再去兼容性矩阵里找 Controller 版本尽量选“同时支持当前集群版本”的最新稳定版而不是直接装最新版。这里给一个通用的自查思路kubectl version看集群版本helm show chart或官方发布页看 Controller 版本。如果版本匹配没问题再来谈安装。这一步看起来很简单但我在帮别人排查问题时发现至少有三成“装完 Controller 后 Ingress 不生效”的案例根源都是版本不匹配换了匹配版本后一切正常。2.2 这次实验用的环境和运行模式说明我这次实验用的是一个小型测试集群架构是标准的一主双节点Kubernetes 版本和 Controller 稳定版本做了匹配验证。测试环境资源有限所以 Controller 我选择了 Deployment 模式部署副本数设置为 2保证有一个副本能正常调度起来。运行模式这个点很容易被忽略。Controller 有两种常见落地方式一种是 Deployment 配合 LoadBalancer 或 NodePort 类型的 Service 对外暴露另一种是 DaemonSet 配合 hostNetwork让 Controller 直接共享节点网络用节点的 IP 加特定端口对外提供服务。云环境里第一种更常用因为没有额外网络配置云厂商的负载均衡器会直接挂到 Controller 的 Service 后面裸机环境或者已经搭建好 LoadBalancer 的网络环境里第二种更直接。我的建议是云上环境无脑选 Deployment LoadBalancer Service自建机房或裸机集群如果不想额外部署一套负载均衡软件可以选 DaemonSet hostNetwork但要注意端口冲突问题。各个节点的 80/443 端口如果有其他进程占用Controller 会起不来。3. 三种安装方式我都跑了一遍推荐这么用3.1 用 Helm 安装适合生产环境的默认选择Helm 是我最推荐的方式原因有三个升级回滚方便、Values 参数覆盖能力很强、官方 chart 维护得很及时。很多生产环境里团队对 Nginx Controller 做的自定义最终都会落在一套 Helm Values 里比手工维护一份上千行的 YAML 要省心得多。先添加官方仓库并更新helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update然后是安装命令。我这次统一把 Controller 放在独立的ingress-nginx命名空间里和业务资源隔离helm install ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx \ --create-namespace \ --set controller.replicaCount2安装完成后观察 Pod 状态kubectl get pods -n ingress-nginx -w大多数情况下 Pod 会很快进入 Running。这里有一个新手容易踩的坑如果你用的集群节点 CPU 或内存非常小Controller 默认的资源请求也可能让 Pod 一直处于 Pending。这时候看kubectl describe pod会发现调度失败的提示临时解决办法是在 helm 安装时把资源请求调低--set controller.resources.requests.cpu100m \ --set controller.resources.requests.memory100Mi生产环境不要无脑调低这只是测试集群的应急手段。资源限制的意义是保证 Controller 在业务流量上涨时不会被无聊业务挤死尤其是生产环境建议给足配额。3.2 用裸 YAML 安装适合离线环境和想完全掌控的场景Helm 虽然方便但有些场景工具箱里没有 Helm 这个工具或者网络环境根本拉不了远程 chart。这时候可以用官方维护的裸 YAML 清单直接安装kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml执行之后它会创建 namespace、RBAC、Deployment、Service 等一堆资源。这种方式的好处是透明你能看清每个资源对象坏处是后续升级得重新 apply 一次新版清单每次 apply 完整份清单时如果在新旧版本之间有资源删除可能需要手动处理。我如果用裸 YAML 方式通常会先下载到本地再把镜像地址和副本数改成自己集群需要的值最后kubectl apply -f本地文件这样不会把整个 YAML 文件的网络地址暴露在服务器上。还有一种偏门但很有用的做法用裸 YAML 清单安装但把Deployment改成DaemonSet并把参数改成 hostNetwork 模式。这个方法我在裸机多节点场景试过效果不错可以保证每个节点都有一个入口层减少跨节点转发。改法也不复杂把清单里的kind: Deployment改成kind: DaemonSet删掉replicas字段再在 pod 模板里加hostNetwork: true和对应的dnsPolicy: ClusterFirstWithHostNet。3.3 安装后两分钟内要做的检查清单我总结了一份安装完成后的检查清单每次装完都按顺序过一遍能避免大部分“假装成功”的情况kubectl get pods -n ingress-nginx确认所有 Controller 副本都是 Running/Ready。kubectl get svc -n ingress-nginx确认 Service 类型和外部 IP 分配符合预期。如果是 NodePort看暴露的端口号如果是 LoadBalancer看 EXTERNAL-IP 有没有分配出来。kubectl logs -n ingress-nginx -l app.kubernetes.io/nameingress-nginx --tail50确认日志里没有报错尤其是和 API Server 连接、证书加载相关的错误。确认 IngressClass 是否创建新版本 Controller 默认会创建一个名为nginx的 IngressClass 对象。可以用kubectl get ingressclass查看。如果这个对象不存在你后续创建 Ingress 时会面临“路由规则没被任何控制器处理”的尴尬局面。这套检查在五分钟内就能完成但它决定了你后续在业务上只遇到“业务代码问题”而不是“基础组件没起来”的问题。4. 用一个最小 Demo 打通 Ingress 全链路部署、路由、验证4.1 先部署一个后端服务让流量有地方可去为了验证 Ingress我先创建一个最简单的后端服务。这里用 Nginx 官方镜像作为业务 Pod主要目的是让请求能返回一个可以被识别的页面方便判断流量是否真的走了 Controller。apiVersion: apps/v1 kind: Deployment metadata: name: demo-web namespace: default spec: replicas: 2 selector: matchLabels: app: demo-web template: metadata: labels: app: demo-web spec: containers: - name: demo-web image: nginx:1.27 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: demo-web-svc namespace: default spec: selector: app: demo-web ports: - name: http port: 80 targetPort: 80创建后验证一下 Service 本身能不能访问。进入集群任意一个节点或直接用一个临时 Pod curl 一下 Service 的 ClusterIP确认后端是通的。这一步很重要很多 Ingress 不生效是因为后端 Service 本身就出问题了如果跳过这一步直接测 Ingress排查链路会变得很混乱。4.2 创建 Ingress 资源并理解 pathType 的作用接下来的核心是 Ingress 资源apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: demo-web-ingress namespace: default spec: ingressClassName: nginx rules: - host: demo.app.local http: paths: - path: / pathType: Prefix backend: service: name: demo-web-svc port: number: 80这里两个要素需要解释一下。ingressClassName: nginx是告诉集群这条 Ingress 规则应该由名为nginx的 IngressClass 对应的控制器来处理。如果集群里同时装了多个 Ingress Controller比如 Nginx 和另一个这个字段就决定了谁有资格接管这条规则不写或者写错都会导致无人认领。pathType: Prefix意味着/匹配所有路径。如果你希望只对/api开头的请求生效就把 path 改成/apipathType 改成前缀匹配即可。实际业务里我建议尽量细化路径避免一个/*把静态资源和接口请求全打到同一个后端后期想拆拆不动。4.3 用 Host 头和 curl 验证流量是否真的经过 Controller创建完 Ingress 之后拿到 Controller 的对外地址。如果是 LoadBalancer 类型看外部 IP如果是 NodePort看节点 IP 和端口。然后不用急着改 DNS直接通过 curl 的 Host 头访问模拟域名解析curl -H Host: demo.app.local http://controller-ip/如果返回的是你后端 Nginx 默认页面说明整条链路是通的。多试两次还会发现请求被两个副本轮流处理这就说明负载均衡也在正常工作。这个技巧在生产排查里也特别常用。有时候 DNS 还没生效或者出现问题明明协议没问题但人已经慌了。用 Host 头绕开 DNS 直接测试是快速隔离“域名解析问题”和“Ingress 路由问题”的标准手段。还有一个值得验证的角度看看 Controller 的访问日志里有没有对应的请求记录。kubectl logs -n ingress-nginx -l app.kubernetes.io/nameingress-nginx -f你会看到类似194.123.45.67 - - [25/Jan/2025:10:00:00 0000] GET / HTTP/1.1 200这样格式的日志。看到请求进入 Controller基本可以宣告整个 Nginx Ingress Controller 的安装和核心配置完成了。我甚至建议把这个“看日志验证”变成习惯任何一次流量异常排查都先开到 Controller 日志再判断问题在哪一层。5. 上岗后常见问题的排查顺序从 404 到证书再到资源5.1 老是碰到 404先别怀疑后端服务沿着 Controller 侧找原因404 是装完 Ingress 后最常见的现象但“404”出现的位置不同原因也完全不一样。最需要注意的一点是如果请求没有匹配到任何 Ingress 规则Controller 会返回它自己的默认后端 404这时候问题根本不在后端服务。我一般按这个顺序查kubectl get ingress看 Ingress 是否创建成功状态里有没有记录到 Controller 的地址。看 Ingress 的ingressClassName字段确认写的是nginx而不是别的值。看 Controller 是否生成对应 host 的配置。在进入 Controller Pod 后执行nginx -s reload没什么意义应该看它的配置目录里有没有生成对应 host 的 server 块或者直接看 Controller 日志有没有提到这条 host。最后才去看后端 Service 的 Endpoint 是否正常。之前有位朋友排查了整整一天后来发现 Ingress 的pathType写成了Exact而实际访问路径是/app和/完全匹配不上自然一直 404。这类问题只要在创建 Ingress 时多想一下匹配规则就不会折腾这么久。5.2 证书配置了但 HTTPS 不生效很多是因为 Secret 放错了命名空间给 Ingress 配置 HTTPS 的标准做法是创建 TLS Secret然后在 Ingress 里引用。很多人的第一反应是“证书没问题”但实际往往死在命名空间这个细节上。kubectl create secret tls demo-tls \ --certserver.crt \ --keyserver.key \ --namespace defaultIngress 引用 TLS 的 Secret 时必须和 Ingress 在同一个命名空间。跨命名空间引用 Secret 是不允许的如果你把证书 Secret 创建在ingress-nginx命名空间而在default命名空间创建 IngressController 会报出找不到对应 Secret 的错误HTTPS 自然不生效。另外要养成检查证书有效期的习惯。证书过期导致的 HTTPS 故障表现和配置错误几乎一样浏览器会报证书无效但服务器日志里不一定有明显报错。kubectl exec进 Controller Pod 里用openssl s_client -connect检查一下证书过期时间三秒钟就能排除掉这个因素。如果你的证书不是一次生效还会遇到“改了证书但客户端仍然拿到旧证书”的情况。这是因为 Nginx Ingress Controller 用到的证书 Secret 保存后它需要一点时间去重新加载配置。等十秒左右再测如果还是旧证书就得看 Controller 日志里有没有加载失败的提示了。5.3 Controller 自身卡在 Pending 或 Evicted资源、端口和 HostNetwork 都要检查Controller 的 Pod 如果一直 Pending看kubectl describe pod是首选操作。常见原因有三种一是资源不够。Controller 默认的 requests 配置在一些小内存节点上会导致调度不成功这个我的处理方式在上面 Helm 安装时已经说过了。二是端口冲突。特别是使用 hostNetwork 模式时节点上的 80/443 端口如果被其他进程或者老的 Controller 占用新 Pod 会一直起不来。遇到这种问题netstat -tlnp | grep -E :(80|443)看一下端口占用一清二楚。三是被节点驱逐。Controller 内存占用在流量高峰期会显著上涨如果节点内存本来就紧张Controller 的 Pod 可能触发 OOMKilled 或 Evicted。这时候除了看节点资源还要关注 Controller 自身的参数调整比如内存缓存大小、worker 进程数别一上来就增加副本数扩容解决不了内存泄漏式的消耗。5.4 排查链路总结任何时候先看日志和事件再动手用一句话总结我的排查习惯Ingress 流量不通时先到 Controller 侧看日志先到事件里看报错而不是反复改 Ingress 资源或者重启 Controller。具体链路是这样客户端访问不正常先用 Host 头直连 Controller 地址确认请求有没有真的到达 Controller如果到达了看 Controller 返回什么状态码如果是 404/502再去 Controller 的配置里确认 host 和 path 是否匹配、后端 Service 的 Endpoint 是否存在如果直接连不到 Controller就要回头检查 Service 和 Controller Pod 本身了。这套链路走完95% 的问题都能定位。剩下的 5%说实话我遇到的基本都是集群本身的问题比如节点的 CNI 网络组件异常这已经超出了 Ingress 排查的范畴。在多年的实际操作中我发现Nginx Ingress Controller 本身安装并不复杂绝大部分时间都花在版本匹配和细节排查上。所以我在这次整理里特意把检查清单和排查顺序放在前面就是想让你在面对问题时有路可走而不是凭感觉重装一遍。最后再提一个小技巧等你把这套流程跑通之后可以把安装命令和常用运维命令存成一个小脚本或者 checklist 文档下次再部署直接照着过基本不会再踩同样的坑。
阅读完成 · 觉得有帮助?
咨询建站