1. 从 Service 到外部访问:Ingress-Nginx 到底解决了什么问题
集群里 Pod 跑起来了,Service 也能在集群内部互相访问,但一到「让外面的人打开浏览器访问」这一步,很多人就卡住了。NodePort 要记端口号,LoadBalancer 在自建环境里又没有云厂商给你分配公网 IP,Service 数量一多,端口和 IP 就彻底乱套。Ingress-Nginx 就是来解决这最后一公里的:它本质是一个跑在集群里的 Nginx,通过监听 Ingress 资源的变化,动态生成 Nginx 配置并热加载,把外部请求按域名和路径转发到不同的 Service 上。
一句话概括它的能力:七层路由转发 + 负载均衡 + TLS 终止 + 静态资源托管 + 限流。适合谁?适合已经用 kubeadm 或二进制方式搭好集群、正在做自建环境对外暴露服务的同学。它和 Service 的分工是:Service 负责集群内部的四层寻址,Ingress 负责集群边缘的七层入口。你不需要给每个服务都开一个 LoadBalancer,只需要一个 Ingress-Nginx 的入口 IP,剩下的靠 Host 和 Path 规则分流。
这一篇是 k8s 集群搭建系列的第四篇,前三篇我们把控制面、工作节点、网络插件、Dashboard 都搞定了。现在集群是就绪状态,接下来要做的就是把 Dashboard 以及后续的业务服务,通过 Ingress-Nginx 暴露成一个带域名的 HTTPS 地址。整个过程分四步:装 MetalLB 拿到入口 IP、装 Ingress-Nginx 控制器、写 Ingress 资源定义路由、配 TLS 证书并用 curl 和 kubectl describe 验证。每一步我都会给出可直接复制的 YAML 和命令,你跟着敲就行。
先说清楚一个容易混淆的点:Ingress 是 k8s 的一种资源类型(规则声明),Ingress-Nginx 是具体实现这个规则的控制器(干活的人)。你写的是 Ingress YAML,真正让它生效的是 Ingress-Nginx Controller。市面上还有 Traefik、HAProxy Ingress 等实现,本文只讲 Ingress-Nginx,因为它是目前社区最主流、文档最全、踩坑资料最多的一个。
2. 前置准备:MetalLB 分配入口 IP 与 Ingress-Nginx 控制器部署
在自建内网环境里,Ingress-Nginx 的 Service 类型是 LoadBalancer,但裸机集群没有云厂商的 LB 实现,这个 Service 会一直卡在<pending>状态拿不到 external-ip。MetalLB 就是填补这个空缺的:它用 L2 或 BGP 模式,从一个你规划的 IP 地址池里给 LoadBalancer 类型的 Service 分配地址。如果你的服务器本身已经绑定了固定公网 IP,这一步可以跳过;但实验环境是内网,所以必须装。
装 MetalLB 之前有个前置动作:把 kube-proxy 的 IPVS 模式设为 strict ARP。执行kubectl edit configmap -n kube-system kube-proxy,找到mode和ipvs部分,改成下面这样:
apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: "ipvs" ipvs: strictARP: true改完保存退出,然后重启 kube-proxy 让配置生效:kubectl rollout restart daemonset kube-proxy -n kube-system。这一步不做的话,MetalLB 的 L2 模式可能无法正常响应 ARP 请求,表现为 IP 分配了但 ping 不通。
接着部署 MetalLB 本体。官方安装清单比较大,我把它精简成关键部分,你直接 apply 即可。核心是命名空间、CRD、controller Deployment、speaker DaemonSet 和 webhook。注意镜像地址要换成你自己的私有仓库 tag:
kubectl apply -f metallb-native.yaml kubectl get pods -n metallb-system看到 controller 和每个节点上的 speaker 都是 Running,就说明装好了。然后配置地址池,写一个metallb-layer2-config.yaml:
apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: default-pool namespace: metallb-system spec: addresses: - 10.150.1.90-10.150.1.91 --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: default-l2-advertisement namespace: metallb-system spec: ipAddressPools: - default-pooladdresses换成你内网里一段没人用的空闲 IP,我这里是 10.150.1.90 到 91 两个地址。执行kubectl apply -f metallb-layer2-config.yaml应用。L2 模式不需要路由器支持 BGP,配置最简单,适合实验环境。
接下来装 Ingress-Nginx。可以用 helm,也可以直接 apply 官方 yaml。我用后者,方便控制镜像来源。官方清单里包含 Namespace、ServiceAccount、RBAC、ConfigMap、两个 Service(controller 和 admission)、Deployment、两个 Job(生成和 patch webhook 证书)、IngressClass 和 ValidatingWebhookConfiguration。关键点在于 controller 的 Service 类型是 LoadBalancer,MetalLB 会从这里分配 external-ip:
kubectl apply -f ingress-nginx.yaml kubectl get svc -n ingress-nginx等一会儿,你会看到ingress-nginx-controller的 EXTERNAL-IP 变成了 10.150.1.90,这就是集群的统一入口。同时确认 IngressClass 存在:kubectl get ingressclass,应该有一个名为 nginx 的类,controller 字段是k8s.io/ingress-nginx。这个类名后面写 Ingress 资源时要用到。
3. 可复制的 Ingress 资源与 TLS 证书配置
现在入口有了,开始写路由规则。我拿第三篇里部署的 Kubernetes Dashboard 做例子,把它通过k8s.test.com/dashboard暴露出来。Dashboard 后端本身是 HTTPS 的,所以 Ingress 这里要加一个backend-protocol: HTTPS的注解,否则会 502。
先创建 TLS 证书。自签一张就行,生产环境换成正式证书:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout dashboard.key -out dashboard.crt \ -subj "/CN=k8s.test.com/O=dashboard" kubectl create secret tls dashboard-tls-secret \ --key dashboard.key --cert dashboard.crt \ -n kubernetes-dashboard这里 Secret 必须建在 Dashboard 所在的kubernetes-dashboard命名空间,因为 Ingress 资源也在那个命名空间,TLS 引用是同命名空间的。创建完用kubectl get secret -n kubernetes-dashboard dashboard-tls-secret确认类型是kubernetes.io/tls。
然后写 Ingress 资源ingress-dashboard-config.yaml:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: kubernetes-dashboard-ingress namespace: kubernetes-dashboard annotations: nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" nginx.ingress.kubernetes.io/rewrite-target: /$2 spec: ingressClassName: nginx tls: - hosts: - k8s.test.com secretName: dashboard-tls-secret rules: - host: k8s.test.com http: paths: - path: /dashboard(/|$)(.*) pathType: ImplementationSpecific backend: service: name: kubernetes-dashboard-kong-proxy port: number: 443几个关键字段解释一下。ingressClassName: nginx指定用哪个控制器,必须和 IngressClass 名字一致。rewrite-target: /$2配合正则路径/dashboard(/|$)(.*),把/dashboard/xxx重写成/xxx再转发给后端,因为 Dashboard 后端不认识/dashboard这个前缀。backend-protocol: HTTPS告诉 Nginx 用 HTTPS 去连后端。pathType: ImplementationSpecific是因为用了正则,必须用这个类型。
如果你要配多个域名或多个路径,就在rules下面继续加。比如再加一个api.test.com指向另一个 Service:
- host: api.test.com http: paths: - path: / pathType: Prefix backend: service: name: my-api-service port: number: 8080Prefix类型做前缀匹配,适合普通 HTTP 服务。多个 Ingress 资源可以共存,控制器会把它们合并成一份 Nginx 配置。写完执行kubectl apply -f ingress-dashboard-config.yaml。
4. 验证路由生效:curl 与 kubectl describe 实操
配置提交后,先看 Ingress 资源本身的状态:
kubectl describe ingress kubernetes-dashboard-ingress -n kubernetes-dashboard输出里重点看三块:Rules段确认 Host 和 Path 解析正确,TLS段确认 secretName 是 dashboard-tls-secret,Events段如果没有报错就说明控制器接受了这份配置。如果 Events 里有invalid ingress class或admission webhook denied,说明类名写错或 webhook 校验没过。
接着验证控制器有没有真正生成 Nginx 配置。看 controller 的日志:
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=50正常会看到类似Adding ingress kubernetes-dashboard/kubernetes-dashboard-ingress和Reloading nginx configuration的日志。如果日志里出现error obtaining ingress class或unexpected error,就要回头检查 IngressClass。
然后从集群外部(你的办公电脑)验证。先在 hosts 文件里加一行,把域名指到入口 IP:
# Linux/macOS: /etc/hosts # Windows: C:\Windows\System32\drivers\etc\hosts 10.150.1.90 k8s.test.com用 curl 带-k跳过自签证书校验,验证 HTTPS 路由:
curl -k -I https://k8s.test.com/dashboard期望看到HTTP/2 200或者HTTP/1.1 302(Dashboard 可能重定向到登录页)。如果返回 404,说明 rewrite 规则没匹配上;返回 502,说明后端协议或端口不对;返回 503,说明后端 Service 没有可用 Endpoint。
再验证一下 HTTP 到 HTTPS 的跳转(如果你配了强制跳转注解的话),以及不带路径的根访问:
curl -k -I https://k8s.test.com/还可以直接绕过域名,用入口 IP 加 Host 头测试,排除 DNS 干扰:
curl -k -I https://10.150.1.90/dashboard -H "Host: k8s.test.com"这个命令能直接验证 Ingress 的 Host 匹配逻辑,如果它通了但域名访问不通,问题就在 DNS 或 hosts 配置上。最后浏览器打开https://k8s.test.com/dashboard,能看到 Dashboard 登录页就大功告成了。
5. 常见报错排查:从 401 到 local proxy failed 的对照表
Ingress-Nginx 的报错大多集中在几个固定位置,我把踩过的坑整理成对照表,你按现象查。
| 现象 | 可能原因 | 排查动作 |
|---|---|---|
| Service 一直 pending,无 external-ip | MetalLB 未装或地址池未配 | kubectl get pods -n metallb-system,检查 IPAddressPool |
| 访问返回 404 | Path 或 rewrite 规则不匹配 | kubectl describe ingress看 Rules,核对正则 |
| 访问返回 502 | 后端协议/端口错误 | 检查backend-protocol注解和 Service port |
| 访问返回 503 | 后端无可用 Endpoint | kubectl get endpoints -n <ns> <svc> |
报错local proxy failed | 后端 Pod 未就绪或探针失败 | 看后端 Pod 的 readinessProbe |
报错reading choices | 证书 Secret 格式错误 | 确认 Secret 类型是kubernetes.io/tls |
报错401 Unauthorized | Dashboard token 过期或 RBAC 不足 | 重新生成 token,检查 ServiceAccount |
报错OAuth相关 | 后端要求 OAuth 认证 | 检查后端服务自身的认证配置 |
| webhook 拒绝创建 Ingress | 类名错误或 webhook 证书失效 | kubectl get ingressclass,重启 admission Job |
重点说几个高频的。local proxy failed通常不是 Ingress 本身的问题,而是后端 Pod 没通过就绪探针,Endpoints 列表是空的,Nginx 转发过去没人接。用kubectl get endpoints -n kubernetes-dashboard kubernetes-dashboard-kong-proxy一看便知,如果 ENDPOINTS 是<none>,就去查后端 Pod 的 readinessProbe。
reading choices这个报错一般出现在 TLS 配置上,多半是 Secret 里的 crt 或 key 内容不对,或者 Secret 类型不是kubernetes.io/tls。用kubectl get secret dashboard-tls-secret -n kubernetes-dashboard -o yaml看 data 里有没有 tls.crt 和 tls.key 两个键。
401 Unauthorized在 Dashboard 场景很常见,那是 Dashboard 自己的登录认证,不是 Ingress 的问题。你需要用kubectl create token生成一个 token 去登录。如果确认是 Ingress 层面的 401,检查有没有配auth-type或auth-secret注解。
还有一个隐蔽的坑:如果你用了rewrite-target但路径正则写错,请求会被重写成空路径,后端收到/返回 404。调试时把rewrite-target临时去掉,看原始路径能不能通,能通就说明是 rewrite 的问题。
6. 把 AI 编码助手接进你的集群工作流
集群搭好、Ingress 通了之后,日常写 YAML、调 Ingress 注解、排查报错这些活儿,其实可以交给 AI 编码助手来加速。我现在的习惯是:遇到不熟悉的注解组合,先让助手生成一版草稿,再自己核对官方文档。这里推荐用 TaoToken 作为统一的模型接入层,它把多家模型的 API 收敛成一个 OpenAI 兼容的入口,配置一次就能在 Claude Code、Cline、Codex 这些工具里切换模型。
接入方式很简单,以 Claude Code 为例,核心是三件套:Base URL、API Key、Model ID。Base URL 填https://taotoken.net/api,API Key 在控制台的 API Keys 页面生成,Model ID 按你需要的模型填。如果你用的是 Cline 或 Codex,配置逻辑一样,只是配置文件位置不同——Codex 改auth.json,Cline 在 MCP 设置里填 Base URL 和 Key。
具体操作路径:先去 API Keys 页面 生成一个 Key,然后参考接入文档 把 Base URL 和 Key 填进你的工具。想先试试模型效果,可以直接在模型对话 里问几个 Ingress 注解的问题,验证通了再配到本地工具。如果你长期做编码和 Agent 任务,Coding Plan 会更划算。
配好之后,你可以直接让助手帮你生成 Ingress YAML、解释kubectl describe的输出、或者根据报错日志给出排查方向。比如把local proxy failed的日志贴进去,它能快速定位到是 Endpoint 为空还是探针失败。但记住一点:助手给的是草稿,最终 apply 到集群前,注解和路径正则一定要自己核对一遍,尤其是rewrite-target这种容易写错的地方。集群操作不可逆,多一层人工确认不亏。