在实际维护容器集群的时候,几乎所有人都绕不开一个问题:集群里的服务怎么让外部访问。常见做法很多,但真正上线稳定、又方便管理的方案,绕不开 Ingress。这篇就从一个实际场景说起,把 Ingress 配合域名访问服务的配置、原理和踩坑经验一次讲透。内容适合刚接触 Kubernetes 的开发、运维,也适合在云托管集群上做应用暴露的同学对照参考。
先说一个我实际遇到过的例子。某天一个前端同事跑过来问,说服务已经部署到集群里了,但是接口地址是一串 NodePort 端口,业务方不满意,要求用https://api.example.com这种标准域名访问。我看了下现状:集群里有十几个服务,每个都用随机端口暴露,安全组规则堆了一堆。这种局面,靠 NodePort 继续硬扛确实不行,换 Ingress 是顺理成章的事情。
1. 为什么是 Ingress:服务暴露方式的对比选型
1.1 从 NodePort 到 Ingress 的演进逻辑
Kubernetes 里把服务暴露到集群外,最原始的方式是给 Service 指定type: NodePort。这种方案会在每台节点上开一个高危端口,然后通过节点IP + 端口的方式访问。问题很明显:
- 端口范围有限,默认是 30000-32767,服务一多就不好管理
- 端口是随机的,业务方记不住,域名解析也没法做
- 只能做四层转发,对 HTTP 层面的路径路由、请求头改写、TLS 终止这些能力一概没有
- 每个服务单独开端口,安全组规则繁琐,暴露面也大
后来有了 LoadBalancer 类型的 Service,云厂商会帮你创建一个负载均衡器,给一个公网 IP。但是每个服务一个 LB,成本高不说,同样的七层能力依然缺失。真正解决域名访问、路径转发、证书管理这些问题的是 Ingress。
1.2 Ingress 的核心价值
Ingress 不是 Service 的替代品,它是位于 Service 之上的一个入口层。它做的是:根据域名和 URL 路径,把外部请求转发到集群内对应的 Service,再由 Service 转发到 Pod。
用一个收银台的类比来理解:每个 Service 是一个收银员,只管自己柜台的业务。Ingress 是商场大厅的总导览台,顾客进来说“我要去三楼餐饮区”,导览台告诉他去哪个柜台。没有导览台,顾客就得挨个柜台问。
Ingress 解决了 NodePort 和 LoadBalancer 解决不了的三个核心问题:
- 域名路由:同一个 IP 可以对应多个域名,不同域名转发给不同服务
- 路径路由:同一个域名下,
/api走 A 服务,/web走 B 服务 - 集中管理 TLS 证书和通用流量策略
1.3 各方案对比速查
| 暴露方式 | 协议支持 | 域名支持 | 路径路由 | TLS 管理 | 适用场景 |
|---|---|---|---|---|---|
| NodePort | 四层 | 不支持 | 不支持 | 不支持 | 临时调试、内网小规模 |
| LoadBalancer | 四层 | 不支持 | 不支持 | 不支持 | 单一服务暴露、需要固定公网 IP |
| Ingress | 七层 | 支持 | 支持 | 支持 | 多服务统一入口、域名访问、生产环境 |
提示:Ingress 依赖 Ingress Controller 才能真正工作,下文会详细解释两者关系。
2. Ingress 和 Ingress Controller:先搞清谁干活
2.1 两个概念不能混
很多新手一开始会把 Ingress 和 Ingress Controller 混为一谈。简单说:
- Ingress 是 Kubernetes 里的一个 API 资源对象,用 YAML 定义你想要的转发规则。它本身不干活。
- Ingress Controller 是真正干活的东西,它是一个 Pod,持续监听 Ingress 资源的变化,然后把规则转换成底层反向代理的配置,比如 Nginx 的配置或者 Traefik 的配置。
我用一句话总结:Ingress 是需求单,Controller 是执行者。没有 Controller,你创建一百个 Ingress 资源也不会产生任何实际路由效果。
2.2 Controller 的工作原理
以最常见的 ingress-nginx 为例,它的工作原理是这样的:
- 启动后通过 Kubernetes API 监听 Ingress、Service、Endpoint 等资源的变化
- 当某个 Ingress 资源被创建或修改时,Controller 会生成一段 Nginx 配置
- 配置中把域名、路径、转发目标对应到具体的 Service 和端口
- 更新配置文件并触发 Nginx reload
- 外部流量到达 Controller 暴露出来的入口(通常是 LoadBalancer 类型 Service 或宿主机端口),由 Nginx 根据配置转发到后端 Pod
整个过程对用户来说是自动的。你只需要维护好 Ingress 资源,Controller 负责把规则变成实际可用的转发链路。
2.3 Controller 选型
生产环境常用的 Controller 有两类:
- ingress-nginx:基于 Nginx 反向代理,成熟稳定,功能全,性能好,社区资料多。适合绝大多数业务场景。
- Traefik:自带 Web UI,配置热更新体验好,在动态服务发现、云原生场景下用得比较多。适合微服务架构中使用服务发现频繁变化的场景。
还有基于 Envoy 的 Controller,性能更强但配置复杂度高,普通场景不常用。
我建议:没有特殊理由,直接上 ingress-nginx。不是因为别的 Controller 不好,而是因为 ingress-nginx 的资料、踩坑案例最丰富,出了问题好排查。
注意:云托管的集群一般自带了一个预装的 Controller,通常不让用户自行安装 ingress-nginx。具体以你使用的集群服务商的文档为准。但无论哪种,Ingress 资源本身的语法大同小异,核心概念不冲突。
3. 从 Service 到域名访问:Ingress 配置实操
3.1 前提条件
在创建 Ingress 之前,你需要确保集群里已经有:
- 一个 Deployment(承载业务)
- 一个 Service(用于 Pod 间负载均衡)
- 一个 Ingress Controller(负责执行规则)
Deployment 和 Service 的 YAML 这里不展开,常规写法大家都熟。假设你有一个名为myapp的 Service,监听端口80,Selector 匹配的 Pod 标签是app=myapp。接下来要做的,就是写一个 Ingress,把app.example.com这个域名转发到这个 Service。
3.2 创建第一个 Ingress 资源
下面这个 YAML 是一个最基础的 Ingress 配置:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: myapp-ingress namespace: default spec: ingressClassName: nginx rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: myapp port: number: 80字段逐个说一遍:
ingressClassName:指定用哪个 Controller。如果集群有默认 Controller,这一项也可以不写。rules.host:你要绑定的域名。注意这里只是把域名和路由规则关联起来,DNS 的指向要另行配置,后面会讲。rules.http.paths:路径匹配规则列表。/表示该域名下的所有路径都转发到myapp。backend.service.name:后端 Service 名称。backend.service.port.number:后端 Service 端口。
创建之后,可以用kubectl get ingress查看状态。正常情况下,ADDRESS列会显示 Controller 对应的入口地址。如果没有显示地址,或者显示-,说明 Controller 可能没就绪或者没被正确关联。
3.3 路径匹配规则:Prefix 与 Exact
上面的 YAML 里,pathType: Prefix是一个很容易忽略但很关键的字段。它决定了路径匹配方式:
Prefix:前缀匹配。/api会匹配/api、/api/v1、/api/v1/users等所有以/api开头的路径。Exact:精确匹配。只有请求路径完全等于配置的值才会命中。ImplementationSpecific:由 Controller 自定义实现,一般用得少。
实际使用中,我推荐:
- 根路径
/使用Prefix,用来做兜底 - 具体的接口前缀如
/api使用Prefix - 对某些特殊的精确路径,如
/healthz,使用Exact避免误匹配
3.4 一个域名对应多个服务
真实场景中,一个域名下通常不止一个服务。比如https://app.example.com/api走后端接口服务,https://app.example.com/web走前端静态资源服务。这是 Ingress 路径路由最常见的用法。
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app-routing namespace: default spec: ingressClassName: nginx rules: - host: app.example.com http: paths: - path: /api pathType: Prefix backend: service: name: api-service port: number: 8080 - path: /web pathType: Prefix backend: service: name: web-service port: number: 80这样配置后,访问https://app.example.com/api会转发到api-service,访问https://app.example.com/web会转发到web-service。
注意:如果你的后端服务内部包含路径信息,比如 API 服务在代码里直接监听根路径
/,但你是通过/api前缀转发过去的,请求到达 Pod 时路径仍然带着/api前缀。这时候就需要用到路径重写功能,常用注解是nginx.ingress.kubernetes.io/rewrite-target,后面的常见问题部分会详细讲。
3.5 多个域名对应不同服务
如果业务有多个域名,比如a.example.com和b.example.com分别对应两个独立的系统,可以在同一个 Ingress 资源里写多个rules:
spec: ingressClassName: nginx rules: - host: a.example.com http: paths: - path: / pathType: Prefix backend: service: name: service-a port: number: 80 - host: b.example.com http: paths: - path: / pathType: Prefix backend: service: name: service-b port: number: 80这样做的好处是,多个域名的 TLS 证书和访问策略可以集中在一个 Ingress 资源里管理。当然,如果域名之间隔离要求高,也可以各自创建独立的 Ingress 资源,效果一样,管理上更清晰。
3.6 DNS 解析:域名怎么指向 Ingress 入口
Ingress 创建完成后,还需要把域名解析到 Ingress Controller 的入口地址。这个入口地址取决于 Controller 部署方式:
- 如果是云托管的 Kubernetes 集群,Controller 通常封装为 LoadBalancer 类型的 Service,会分配一个公网 IP 或者域名。你需要去 DNS 服务商处添加一条解析记录,把域名指向这个入口。
- 如果是自建集群,Controller 会通过 NodePort 或者宿主机的端口暴露,那 DNS 就需要指向相应节点的 IP。
这一步常被忽略。很多时候 Ingress 配置没问题,但访问域名就是不通,追查到最后发现是 DNS 没加记录。先用kubectl get svc -n ingress-nginx看 Controller 的入口地址,再去做解析。
3.7 验证访问
配置完成后,验证方式很简单。在本地执行:
curl -H "Host: app.example.com" http://<INGRESS_ADDRESS>/如果返回了后端服务的响应内容,说明 Ingress 转发链路是通的。此时再去检查 DNS 解析记录是否正确,确认无误后,用真实域名访问就可以了。
4. HTTPS 访问与常用注解配置
4.1 TLS 证书配置
生产环境的域名访问基本都是 HTTPS。Ingress 资源本身支持 TLS 配置,证书以 Secret 方式存放在集群里。
第一步,创建证书 Secret。前提是你已经拿到了域名对应的证书文件,比如tls.crt和tls.key:
kubectl create secret tls example-com-tls \ --cert=tls.crt \ --key=tls.key \ -n default第二步,在 Ingress 中引用这个 Secret:
spec: ingressClassName: nginx tls: - hosts: - app.example.com secretName: example-com-tls rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: myapp port: number: 80配置完成后,https://app.example.com可以正常访问,证书也能被正确识别。
如果是在云托管的集群上,通常可以不用自己维护证书,直接用托管证书方案,Controller 会自动绑定证书到入口层。方式不同,但思路都是把证书置入集群并关联到 Ingress 上。
注意:Secret 必须和 Ingress 在同一个命名空间。这是 Kubernetes 资源引用的基本规则,跨命名空间引用是不允许的。
4.2 HTTP 强制跳转 HTTPS
默认情况下,除非你配置了默认证书或全局强制跳转,否则 HTTP 和 HTTPS 都可能可以访问。对于对安全性要求严格的业务,需要把 HTTP 所有请求都跳到 HTTPS。
ingress-nginx 提供了注解实现这个功能:
metadata: annotations: nginx.ingress.kubernetes.io/force-ssl-redirect: "true"加上这个注解后,访问http://app.example.com会自动 301 跳转到https://app.example.com。这个功能比在业务代码里写跳转逻辑靠谱得多,属于入口层治理的典型动作。
4.3 路径重写:解决后端路径差异
前面提到,通过/api前缀转发后,请求路径会保留/api,而后端服务实际监听的是/。如果不做处理,后端逻辑就会收到一个带前缀的路径,很可能导致路由匹配失败。
ingress-nginx 的重写注解可以解决这个问题:
metadata: annotations: nginx.ingress.kubernetes.io/rewrite-target: /更精细的写法是用捕获组把动态部分保留下来:
nginx.ingress.kubernetes.io/rewrite-target: /$2配合路径定义里的重写规则,可以把/api/v1/xxx重写为/v1/xxx再转发给后端。这个功能挺常用,值得花点时间试一遍。
4.4 其他常用注解
用 ingress-nginx 时,下面几个注解在生产环境中出现频率也很高:
| 注解 | 作用 |
|---|---|
nginx.ingress.kubernetes.io/proxy-body-size | 限制请求体大小,默认 1m,上传类接口要调大 |
nginx.ingress.kubernetes.io/proxy-connect-timeout | 后端连接超时时间 |
nginx.ingress.kubernetes.io/limit-rps | 限制单个 IP 每秒请求数,简单限流 |
nginx.ingress.kubernetes.io/cors-allow-origin | 跨域放行配置 |
nginx.ingress.kubernetes.io/upstream-vhost | 转发时指定上游请求 Host |
这些注解都是 Controller 厂商特定的。也就是说,你换一个 Controller,注解语法可能完全不同。所以使用前先确认你用的是哪个 Controller,再查对应的注解文档。
提示:注解不是 Kubernetes 官方的 Ingress 语法,不加也不影响 Ingress 资源本身的有效性。但很多生产需求,如限流、超时、重写,都得靠注解实现。
4.5 默认证书与通配符域名
如果服务较多,每个 Ingress 都写一份 TLS 配置会比较繁琐。可以在 Ingress Controller 层面配置一个默认证书,这样所有没有明确配置证书的 Ingress 都会自动使用默认证书。这种方式适合内部系统、非关键业务统一加密的场景,省事,但安全性要求高的生产环境不建议这样做。
5. 常见问题与排查实录
5.1 访问返回 404 Not Found
这是 Ingress 配置中最常见的问题。排查思路分几步:
- 先确认 Ingress 资源有没有生效:
kubectl get ingress,看看资源是否存在,状态是否正常。 - 再访问后端 Service 本身,排除业务服务问题:
kubectl exec -it <任意Pod> -- curl http://<Service名称>:<端口>/healthz - 如果后端没问题,再检查 Ingress 里
backend.service.name和port.number是否写对。这里最容易踩坑的是端口写错,Service 端口和 Pod 内监听端口混淆。
我踩过的最典型的坑是:Service 端口是 80,但实际 Pod 监听的是 8080,Ingress 里port.number写成 8080,结果请求到达 Service 后被拒绝。记住,Ingress 转发的目标是 Service 的port,不是 Pod 的targetPort。
5.2 域名解析到入口 IP 但不通
域名已经解析到 Controller 的入口 IP,但访问时一直超时或拒绝连接。排查方向:
- 看 Controller 的 Service 类型:如果是 LoadBalancer,确认云平台安全组是否放行了 80/443 端口
- 如果是 NodePort 暴露,检查节点防火墙是否放行对应端口
- 在集群外
telnet <入口IP> 80测试连通性
这类问题大多不是 Ingress 配置的问题,而是网络层可达性的问题。安全组、防火墙、云平台白名单,都要逐一排除。
5.3 已配置 TLS 但浏览器不认证书
浏览器报证书错误,原因一般是:
- 页面访问的是
http,但后端强制跳转到了https,新证书链不完整 - Secret 内容有问题,需要用
kubectl get secret <名称> -o yaml检查,确认tls.crt和tls.key键名正确 - 证书的域名与访问的域名不一致。证书只有覆盖
app.example.com,但实际访问的是www.example.com,那 cert 就会报不匹配
5.4 WebSocket 或长连接突然断开
某些业务用到 WebSocket 或 SSE 长连接,配置 Ingress 后经常出现断连问题。这跟 Nginx 默认的代理超时机制有关。需要加注解调整:
metadata: annotations: nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"把读取和发送超时调到足够大,长连接就能保持稳定。
5.5 日志排查技巧
ingress-nginx 的所有转发日志都输出到 Controller 的 Pod 日志里。排查问题时,直接看日志是最快的路径:
kubectl logs -f -n ingress-nginx deployment/ingress-nginx-controller日志会显示请求的域名、路径、后端转发地址、响应码等信息,特别是请求被转到哪个 Service、实际返回了多少,一目了然。比在业务 Pod 里盲猜高效得多。
5.6 常见问题速查表
| 现象 | 可能原因 | 排查思路 |
|---|---|---|
| 404 Not Found | 后端路径不匹配或 Service 配置错误 | 检查 Ingress backend 配置和后端服务可达性 |
| 502 Bad Gateway | 后端 Pod 异常或监听端口不对 | 检查 Pod 状态和 Service targetPort |
| 301 无限跳转 | HTTP 与 HTTPS 来回跳 | 检查是否有两处强制跳转配置冲突 |
| 连接超时 | 安全组未放行端口或 Controller 未就绪 | 检查入口 Service 和节点防火墙 |
| 证书错误 | Secret 内容不正确或域名不匹配 | 检查 Secret 内容和证书域名 |
5.7 生产环境使用的几个小建议
Ingress 配置虽然不算复杂,但在生产环境里长期运行,有几个事情值得提前做好。
Controller 别只部署一个副本。ingress-nginx 默认支持多副本,直接调整replicas就好。一个副本挂掉,全部域名访问就中断了。生产环境至少两个副本,并且让 Pod 调度到不同节点。
所有 Ingress 资源统一加ingressClassName字段。如果集群里存在多个 Controller,或者未来有迁移计划,显式指定可以避免流到错误的执行者手里。
域名尽量集中管理。不要每个业务团队各建各的 Ingress,最后连域名归属都搞不清楚。建议用一个独立命名空间管理所有 Ingress 资源,或者在命名规范上做明确约定。
最后说说我在实际使用中的体会。Ingress 配置本身并不复杂,真正花时间的地方是在路由规则设计、路径重写策略以及证书生命周期管理这些细节上。我第一次配置 Ingress 时,把 Service 端口写错,排查了半天。后来养成了一个习惯:先在 Ingress 之外验证后端 Service 本身是通的,再去检查 Ingress 配置,定位速度快了不止一倍。
如果你目前还在用 NodePort 硬撑,可以尽快把入口层改造提到日程上。多花半天时间,把 Ingress 体系搭起来,后续所有服务的暴露问题就都统一了。域名访问是业务常态需求,而 Ingress 就是 Kubernetes 对这个需求给出的标准答案。