Ingress 实战:从 Service 到七层路由
概述#
在前面的章节中,我们已经学习了 Service——它是 Kubernetes 集群内部的"内线电话",负责把 Pod 的端口暴露给集群内的其他服务。但当你的应用需要对外提供服务时,Service 就显得不够用了:
- NodePort 需要在每个节点上开放端口,端口范围受限(30000-32767),且需要对外暴露节点 IP
- LoadBalancer 虽然能正常工作,但每个 Service 都要创建一个云负载均衡器,成本高且管理复杂
Ingress 就是来解决这个问题的:它是 Kubernetes 的七层(HTTP/HTTPS)统一入口,一个 Ingress 就能把集群内多个服务暴露到同一个域名或 IP 下,按 Host 和 Path 做路由转发。
类比一下:
- Service 是"内线电话"——集群内部服务之间打电话,直接拨分机号(ClusterIP:Port)
- Ingress 是"前台总机"——外部用户打公司总机号码,前台根据你要找的人(域名+路径)把电话转接到对应的分机
2026 现状:Ingress API 本身已进入维护模式,社区主推的继任者是 Gateway API(v1.0 已 GA,表达能力更强、角色划分更清晰、原生支持更多协议)。存量集群 Ingress 仍在大量使用,作为入门与生产的基础依然值得掌握;新项目可评估直接上 Gateway API。
Ingress vs Service:四层还是七层?#
要理解 Ingress 和 Service 的区别,核心是搞清楚网络层级:
| 对比项 | Service | Ingress |
|---|---|---|
| 网络层级 | L4(传输层:TCP/UDP) | L7(应用层:HTTP/HTTPS) |
| 路由依据 | IP + Port | Host(域名) + Path(路径) + Header |
| 暴露范围 | 集群内部(ClusterIP)或节点级(NodePort) | 集群外部(通过 Ingress Controller) |
| TLS 终止 | 不支持(需要应用自己处理) | 支持(在 Ingress Controller 层终止 TLS) |
| 典型使用场景 | 微服务间内部调用 | 对外提供 HTTP/HTTPS 服务 |
Service 的工作方式#
Service 工作在四层,它只关心"IP + 端口",不解析 HTTP 协议内容:
外部请求 → LoadBalancer/NodePort → Service (iptables/IPVS) → PodService 能做到的路由非常有限:
- 按 label selector 选择后端 Pod
- 支持简单的会话保持(基于客户端 IP 的哈希)
- 支持 TCP/UDP 流量(不关心上层协议)
Ingress 的工作方式#
Ingress 工作在七层,它能解析 HTTP 请求的内容:
外部请求 → Ingress Controller (LoadBalancer) → Ingress 规则匹配 → Service → PodIngress 能做到的路由非常灵活:
- 按 Host 路由:
api.example.com→ api-service,web.example.com→ web-service - 按 Path 路由:
/api/*→ api-service,/admin/*→ admin-service - 按 Header 路由(部分 Controller 支持):
X-Version: v2→ canary-service - TLS 终止:在 Ingress Controller 层解密 HTTPS,后端 Pod 只处理 HTTP
- 流量控制:限流、超时、重试(取决于 Controller 实现)
什么时候用 Service,什么时候用 Ingress?#
用 Service:
- 集群内部服务间调用(如 backend 访问 database)
- 非 HTTP 协议(如 gRPC、MySQL、Redis)
- 需要四层负载均衡的场景
用 Ingress:
- 对外提供 HTTP/HTTPS 服务
- 需要基于域名或路径的路由
- 需要集中管理 TLS 证书
- 需要七层流量控制(限流、认证、重写)
Ingress Controller:规则的"执行者"#
重要概念:Ingress 只是一个声明式资源配置,它定义了"流量应该怎么路由",但它自己不做任何转发。真正处理流量的是 Ingress Controller。
类比:Ingress 是"路由表",Ingress Controller 是"前台总机接线员"——路由表告诉接线员怎么转接,接线员真正执行转接动作。
架构关系#
外部流量
↓
LoadBalancer / 主机 IP
↓
Ingress Controller(作为 Pod 运行在集群内)
↓ 读取 Ingress 资源的定义
根据规则转发
↓
Service(ClusterIP)
↓
Pod主流 Ingress Controller 对比#
| Controller | 特点 | 适用场景 |
|---|---|---|
| NGINX Ingress Controller | 最成熟、文档最全、社区最大 | 绝大多数场景的首选 |
| Traefik | 自动服务发现、美观的 Dashboard | 动态环境、需要可视化 |
| HAProxy Ingress | 性能优秀、配置灵活 | 高并发、需要精细化控制 |
| Istio Gateway | 服务网格集成、L7 策略强大 | 已使用 Istio 的环境 |
| AWS ALB Ingress | 直接使用 AWS ALB | AWS EKS 环境、需要 AWS 原生集成 |
安装 NGINX Ingress Controller#
生产环境推荐使用 NGINX Ingress Controller(以下简称 NIC),安装方式:
# 添加 Helm 仓库
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
# 安装(创建 namespace ingress-nginx)
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace
# 验证安装
kubectl get pods -n ingress-nginx
# NAME READY STATUS
# ingress-nginx-controller-xxxx 1/1 Running
# ingress-nginx-admission-webhook-xxxx 1/1 Running安装后会自动创建一个 LoadBalancer 类型的 Service(如果是云环境),或者一个 NodePort 类型的 Service(如果是裸金属):
kubectl get svc -n ingress-nginx
# NAME TYPE EXTERNAL-IP
# ingress-nginx-controller LoadBalancer 203.0.113.10这个 EXTERNAL-IP 就是你的 Ingress 的入口地址,把域名(如 app.example.com)解析到这个 IP 即可。
Ingress Class#
从 Kubernetes 1.18 开始,Ingress 支持 ingressClassName 字段,用于指定使用哪个 Ingress Controller(当集群内有多个 Controller 时):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
spec:
ingressClassName: nginx # 指定使用 NGINX Ingress Controller
rules:
- host: app.example.com
http:
# ... 路由规则如果集群内只有一种 Controller,可以不写这个字段,Controller 会处理所有 Ingress。
路由规则:Host 匹配 + Path 匹配#
Ingress 的路由规则定义在 spec.rules 中,按 Host 和 Path 两级匹配。
基础示例:单域名多路径路由#
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
namespace: production
spec:
ingressClassName: nginx
rules:
- host: app.example.com # 匹配的域名
http:
paths:
- path: /api # 匹配路径(前缀匹配)
pathType: Prefix
backend:
service:
name: api-service # 转发到这个 Service
port:
number: 80
- path: / # 默认路径
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80关键点:
pathType: Prefix表示前缀匹配:/api/users会匹配/apipathType: Exact表示精确匹配:只匹配/api,不匹配/api/users- 匹配顺序:NGINX 按规则在 YAML 中的顺序匹配,把最具体的路径放前面(
/api在前,/在后)
Host 匹配:多域名路由#
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-host-ingress
spec:
ingressClassName: nginx
rules:
- host: api.example.com # 第一个域名
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- host: web.example.com # 第二个域名
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80Path 重写#
有时候后端服务期望的路径和 Ingress 暴露的路径不一致(如 Ingress 用 /api/ 路由,但后端服务根路径就是 /):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rewrite-example
annotations:
nginx.ingress.kubernetes.io/rewrite-target: / # 把匹配到的 path 替换为 /
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80效果:外部请求 app.example.com/api/users → Ingress Controller 把路径重写为 /users → 转发给 api-service
TLS 证书配置#
Ingress 支持在 Controller 层终止 TLS(解密 HTTPS 请求,转发 HTTP 给后端 Pod),这样后端应用不需要自己处理证书。
方式一:手动创建 TLS Secret#
第一步:准备证书文件(tls.crt 和 tls.key)
# 假设你已经有证书文件
ls my-cert/
# tls.crt (证书文件,包含公钥)
# tls.key (私钥文件)第二步:创建 tls 类型的 Secret
kubectl create secret tls app-tls-secret \
--cert=tls.crt \
--key=tls.key \
--namespace production或者写 YAML:
apiVersion: v1
kind: Secret
metadata:
name: app-tls-secret
namespace: production
type: kubernetes.io/tls # 关键:类型是 tls
data:
tls.crt: <base64 编码的证书>
tls.key: <base64 编码的私钥>注意:生产环境不要直接把证书写进 Git。推荐用 cert-manager 自动申请和续期 Let's Encrypt 证书(进阶内容,本章不展开)。
第三步:在 Ingress 中引用这个 Secret
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example
spec:
ingressClassName: nginx
tls: # TLS 配置块
- hosts:
- app.example.com # 这个域名的 HTTPS 请求
secretName: app-tls-secret # 使用这个 Secret 中的证书
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80方式二:使用 cert-manager 自动管理证书(推荐生产环境)#
cert-manager 是 Kubernetes 的证书管理控制器,能自动:
- 从 Let's Encrypt 申请免费证书
- 在证书到期前自动续期
- 把证书保存为 Kubernetes Secret
# 1. 创建 ClusterIssuer(Let's Encrypt 生产环境)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx # 用 NGINX Ingress 做验证
---
# 2. 在 Ingress 中声明使用这个 Issuer
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: auto-tls-example
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod # 关键注解
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls-auto # cert-manager 会自动创建这个 Secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80实战:一个 Ingress 路由三个 Service#
这是最常用的实战场景:你有多个服务,但只想暴露一个公网入口,通过域名和路径区分流量。
场景定义#
| 域名 | 路径 | 转发目标 |
|---|---|---|
api.example.com | / | api-svc:8080 |
web.example.com | / | web-svc:3000 |
api.example.com | /admin | admin-svc:9090 |
第一步:准备后端 Service(假设你已经有对应的 Deployment)#
# api-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: api-svc
namespace: production
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080
---
# web-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: web-svc
namespace: production
spec:
selector:
app: web
ports:
- port: 80
targetPort: 3000
---
# admin-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: admin-svc
namespace: production
spec:
selector:
app: admin
ports:
- port: 80
targetPort: 9090第二步:创建 Ingress 规则#
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: unified-entry-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/rewrite-target: / # 路径重写(如需要)
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
- web.example.com
secretName: unified-tls-secret # 两个域名的证书都放这里
rules:
# 规则一:api.example.com → api-svc
- host: api.example.com
http:
paths:
- path: /admin
pathType: Prefix
backend:
service:
name: admin-svc
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
# 规则二:web.example.com → web-svc
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80关键点:
/admin路径要放在/前面,否则/admin/xxx会被/规则匹配到api-svc- 如果
admin-svc期望的路径就是/admin,需要加rewrite-target: /注解把/admin前缀去掉再转发
第三步:验证#
# 查看 Ingress 状态
kubectl get ingress -n production
# NAME CLASS HOSTS ADDRESS PORTS AGE
# unified-entry-ingress nginx api.example.com,web.example.com 203.0.113.10 80,443 5m
# 测试 HTTP(会自动跳转到 HTTPS,如果配置了 TLS)
curl -L http://api.example.com/health
# 测试 HTTPS
curl https://api.example.com/api/users
# 测试路径路由
curl https://web.example.com/
curl https://api.example.com/admin/dashboard小结#
本章我们学习了 Kubernetes 的七层流量入口 Ingress:
Ingress vs Service:Service 是四层内部暴露(像内线电话),Ingress 是七层外部统一入口(像前台总机),支持基于域名和路径的路由。
Ingress Controller 是执行者:Ingress 只是路由规则的声明,真正处理流量的是 Controller(最常用的是 NGINX Ingress Controller)。安装 Controller 后,它会自动读取集群内所有 Ingress 资源并生成对应的 NGINX 配置。
路由规则:通过
host字段匹配域名,通过path+pathType匹配路径。pathType: Prefix是最常用的配置。TLS 证书:在 Ingress 的
tls字段中引用一个tls类型的 Secret,Controller 会在流量进入时终止 TLS,再把解密后的 HTTP 请求转发给后端 Service。生产环境推荐用 cert-manager 自动管理证书。多服务统一入口:一个 Ingress 资源可以通过多个
rules把不同域名和路径路由到不同 Service,这是 Ingress 最核心的使用场景。
下一步:Ingress 解决的是"流量怎么进入集群"的问题。下一章我们将深入学习 Kubernetes 的网络模型——Pod 之间是怎么通信的、CNI 插件怎么选、DNS 是怎么解析的,以及如何使用 NetworkPolicy 做网络隔离。
实战要点:
- 新手常犯的错误:创建了 Ingress 但忘了安装 Ingress Controller → 规则不生效
- 路径匹配顺序很重要:
/api要写在/前面,否则会被默认规则覆盖 - TLS 证书的 Common Name(CN)必须和 Ingress 规则中的
host匹配,否则浏览器会报证书不匹配错误 - 生产环境务必配置 readinessProbe(就绪探针),避免 Ingress Controller 把流量转发到还未启动完成的 Pod