第 6 章:K8s Ingress:从七层路由到 TLS 终结

3 阅读4分钟

上一章讲了 Service。Service 只能在四层做负载均衡,无法基于域名和路径做路由。Ingress 解决了这个问题。

6.1 为什么需要 Ingress

假设一个项目有 10 个微服务,每个服务都创建一个 NodePort 或 LoadBalancer:

  • 外部用户要记 10 个端口或 10 个公网 IP
  • 每个服务都要单独申请 TLS 证书
  • 路径转发、重写、限流等七层能力没法在 Service 层实现

Ingress 的思路是:用一个入口(通常是 80/443 端口)承载多个后端 Service,根据域名和路径做七层路由。

Ingress 路由

                    用户
                     │
            192.168.0.201:80
                     │
              Ingress Controller
              (Nginx / Traefik)
                     │
        ┌────────────┼────────────┐
        ▼            ▼            ▼
    /api/*        /web/*         /shop/*
        │            │            │
    api-svc      web-svc      shop-svc

外部用户只看到一个入口,内部流量被自动分发。Ingress 的工作链路:

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服务网格场景
KongAPI 网关能力

没有安装 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:后端接口,路径 /api
  • shop-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 不生效,常见问题:

现象原因
返回 404Service 没后端,或路径写错
返回 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 课后练习

  1. 安装 nginx-ingress-controller,确认 Pod 运行,并找到 NodePort 端口。
  2. 创建三个 Nginx Deployment 和 Service,用 Ingress 按路径 /api/shop/ 转发。
  3. 在本机配置 app.example.com 指向 192.168.0.201,用 curl 测试不同路径。
  4. 尝试用 cert-manager 或自签名证书为 Ingress 配置 HTTPS,并验证浏览器访问。