上一章讲了 Service。Service 只能在四层做负载均衡,无法基于域名和路径做路由。Ingress 解决了这个问题。
6.1 为什么需要 Ingress
假设一个项目有 10 个微服务,每个服务都创建一个 NodePort 或 LoadBalancer:
- 外部用户要记 10 个端口或 10 个公网 IP
- 每个服务都要单独申请 TLS 证书
- 路径转发、重写、限流等七层能力没法在 Service 层实现
Ingress 的思路是:用一个入口(通常是 80/443 端口)承载多个后端 Service,根据域名和路径做七层路由。
用户
│
192.168.0.201:80
│
Ingress Controller
(Nginx / Traefik)
│
┌────────────┼────────────┐
▼ ▼ ▼
/api/* /web/* /shop/*
│ │ │
api-svc web-svc shop-svc
外部用户只看到一个入口,内部流量被自动分发。Ingress 的工作链路:
6.2 Ingress 与 Ingress Controller
这是两个不同的概念,必须区分清楚。
6.2.1 Ingress 资源
Ingress 是一个 K8s 资源对象,定义路由规则:哪个域名、哪条路径,转发到哪个 Service。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
spec:
rules:
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
6.2.2 Ingress Controller
Ingress 资源只是规则,真正执行转发的是 Ingress Controller。它本质上是跑在 Pod 里的反向代理,监听 Ingress 规则变化,动态更新自己的配置。
常用控制器:
| 控制器 | 特点 |
|---|---|
| nginx-ingress | 稳定、生态成熟、企业用得最多 |
| Traefik | 云原生、自动服务发现、配置简洁 |
| HAProxy | 性能高、功能丰富 |
| Istio Gateway | 服务网格场景 |
| Kong | API 网关能力 |
没有安装 Ingress Controller 时,Ingress 资源不会生效。kubectl get ingress 能看到规则,但流量不会被转发。
6.3 基于 host 和 path 的路由
Ingress 最常用的两种路由方式:按域名和按路径。
6.3.1 按域名路由
不同域名进入不同 Service:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-host-ingress
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
6.3.2 按路径路由
同一个域名,不同路径进入不同 Service:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-path-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- path: /shop
pathType: Prefix
backend:
service:
name: shop-svc
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
pathType: Prefix 表示前缀匹配。path: /api 会匹配 /api、/api/users 等。
注解
nginx.ingress.kubernetes.io/rewrite-target: /会把路径去掉前缀后转发。比如/api/users转发到后端时变成/users。
6.4 安装 nginx-ingress-controller
在 3 节点集群中,可以用官方 Helm 或 YAML 安装。这里以 YAML 方式为例:
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.6/deploy/static/provider/baremetal/deploy.yaml
安装后查看 Pod 是否运行:
$ kubectl get pod -n ingress-nginx
NAME READY STATUS
ingress-nginx-controller-XXXXX 1/1 Running
Controller 会创建一个 Service,默认类型是 NodePort。在我们的集群中,可以通过任意节点访问:
$ kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
ingress-nginx-controller NodePort 10.96.45.6 <none> 80:30080/TCP,443:30443/TCP
外部访问入口:
curl http://192.168.0.201:30080
curl http://192.168.0.202:30080
curl http://192.168.0.203:30080
6.5 TLS 证书终结
Ingress 可以在入口处终结 HTTPS,后端仍然用 HTTP 通信。证书需要以 Secret 形式挂载。
6.5.1 手动创建 TLS Secret
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key -out tls.crt -subj "/CN=app.example.com"
$ kubectl create secret tls app-tls --cert=tls.crt --key=tls.key
6.5.2 使用 cert-manager 自动签发
生产环境手动维护证书太麻烦。cert-manager 可以自动向 Let's Encrypt 或私有 CA 申请证书。
安装 cert-manager:
$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.0/cert-manager.yaml
创建 ClusterIssuer:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
在 Ingress 中引用 ClusterIssuer:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
cert-manager 会自动创建证书,并把证书写进 app-tls 这个 Secret 中。
6.6 实战:多路径 Ingress
假设项目有三个服务:
web-svc:前端页面,路径/api-svc:后端接口,路径/apishop-svc:商城服务,路径/shop
先创建三个 Service:
# services.yaml
apiVersion: v1
kind: Service
metadata:
name: web-svc
spec:
selector:
app: web
ports:
- port: 80
---
apiVersion: v1
kind: Service
metadata:
name: api-svc
spec:
selector:
app: api
ports:
- port: 80
---
apiVersion: v1
kind: Service
metadata:
name: shop-svc
spec:
selector:
app: shop
ports:
- port: 80
再创建 Ingress:
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- path: /shop
pathType: Prefix
backend:
service:
name: shop-svc
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
创建后查看:
$ kubectl apply -f services.yaml -f ingress.yaml
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
app-ingress nginx app.example.com 192.168.0.201 80 2m
测试访问:
# 在本机配置 /etc/hosts 指向任意节点
# 192.168.0.201 app.example.com
$ curl http://app.example.com:30080/
$ curl http://app.example.com:30080/api/users
$ curl http://app.example.com:30080/shop/products
6.7 验证 Ingress 规则是否生效
进入 Controller Pod 查看生成的 Nginx 配置:
$ kubectl exec -n ingress-nginx -it ingress-nginx-controller-XXXXX -- cat /etc/nginx/nginx.conf | grep app.example.com
或者查看 Controller 日志:
$ kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx
如果 Ingress 不生效,常见问题:
| 现象 | 原因 |
|---|---|
| 返回 404 | Service 没后端,或路径写错 |
| 返回 502 | 后端 Pod 没 Ready 或端口不对 |
| 域名不通 | /etc/hosts 没配,或 DNS 没解析到 Ingress 入口 |
| TLS 不生效 | Secret 不存在或证书和域名不匹配 |
6.8 本章小结
- Ingress 提供七层路由,让多个 Service 共享一个入口
- Ingress 只是规则,必须配合 Ingress Controller 执行
- 常见路由方式:按域名、按路径
- TLS 证书可以手动挂载,也可以用 cert-manager 自动签发
- 生产环境推荐 nginx-ingress 或 Traefik,配合 cert-manager 管理 HTTPS
6.9 课后练习
- 安装 nginx-ingress-controller,确认 Pod 运行,并找到 NodePort 端口。
- 创建三个 Nginx Deployment 和 Service,用 Ingress 按路径
/api、/shop、/转发。 - 在本机配置
app.example.com指向192.168.0.201,用 curl 测试不同路径。 - 尝试用 cert-manager 或自签名证书为 Ingress 配置 HTTPS,并验证浏览器访问。