上一篇【第16篇】Service的负载均衡原理——kube-proxy到底干了什么
下一篇【第18篇】Ingress Controller选型和实战——Nginx Ingress完全指南


摘要

Service不错——ClusterIP让你内部Pod互相找到,NodePort对外开了个后门,LoadBalancer更是直接挂上了云厂商的LB。但你有没有发现一个尴尬的问题:一个公网LB一个月几百块,你有3个服务就得买3个LB,10个服务就得买10个——这谁顶得住?而且Service只管四层(TCP/UDP),HTTP Header里那些花活(域名路由、路径匹配、Cookie粘滞、Header改写)它一概不关心。

这就是Ingress出场的原因——它是K8s原生的七层HTTP路由入口。一个Ingress Controller对应一个LB,通过域名和路径把流量分发给不同的后端Service。本文先把Ingress和Service的关系理清楚,再逐字段拆解Ingress YAML,最后演示TLS证书管理。


一、Ingress是什么——“HTTP反向代理入口”

1.1 Service解决不了的三个问题

【Service的局限——为什么要Ingress】

  问题1:每个公网服务要一个LB = 穷死
  
  用 LoadBalancer Service:               用 Ingress:
  ┌──────┐ ┌──────┐ ┌──────┐            ┌─────────────────────────┐
  │ LB-1 │ │ LB-2 │ │ LB-3 │            │     一个 LB (省钱!)     │
  │$100/月│ │$100/月│ │$100/月│            │                         │
  └──┬───┘ └──┬───┘ └──┬───┘            │  api.example.com → API   │
     │        │        │                 │  web.example.com → Front │
     ▼        ▼        ▼                 │  admin.example.com→Admin │
  ┌──────┐┌──────┐┌──────┐              └─────────────────────────┘
  │API   ││Front ││Admin │
  └──────┘└──────┘└──────┘
  10个服务 = 10个LB = ¥1000/月 😱         10个服务 = 1个LB = ¥100/月 😎

  问题2:域名路由 Service不行

  问题3:URL路径路由 Service不行
  /api/* → API Service, /web/* → Frontend Service

要点:Service工作在OSI第四层(传输层),只管IP和端口。Ingress工作在第七层(应用层),能看HTTP的Host头、URL路径、Cookie等。打个比方:Service是邮局的"按地址送信",Ingress是公司的"前台接待员"——看你是来面试的、送快递的还是谈合作的,把你引到不同的办公室

1.2 Ingress ≠ Ingress Controller

这是很多人学Ingress时踩的第一个坑——Ingress只是"规则",Ingress Controller才是真正转发流量的"发动机"

【Ingress 和 Ingress Controller 的关系】

  你写的 Ingress 资源:                    实际干活的是 Ingress Controller:
  ┌─────────────────────────┐              ┌─────────────────────────┐
  │ apiVersion: networking  │              │  Nginx Ingress Controller│
  │   .k8s.io/v1            │              │  (一个运行的Pod)          │
  │ kind: Ingress           │   ──监听──▶  │                         │
  │                         │              │  监听Ingress资源变化     │
  │ spec:                   │              │  → 生成 nginx.conf      │
  │   rules:                │              │  → reload nginx         │
  │   - host: api.ex.com    │              │  → 按规则转发HTTP流量    │
  │     paths:              │              │                         │
  │     - path: /users      │              │  类比:                  │
  │       backend: ...      │              │  Ingress = nginx.conf   │
  └─────────────────────────┘              │  Ingress Controller     │
                                           │           = nginx 进程  │
                                           └─────────────────────────┘

  Ingress 只是配置文件——自己不干活!
  跟Service类似:Service也是"配置",真正转发的是kube-proxy

要点:K8s默认不安装任何Ingress Controller——你装了Nginx Ingress、Traefik、Contour等任一款Controller后,Ingress资源才能生效。这个设计很像Java的接口和实现:Ingress是接口标准,各家Controller是具体实现。


二、Ingress YAML——完整拆解

一个完整的Ingress资源长这样:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  namespace: default
  annotations:                    # 各种功能开关(不同Controller不同)
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx         # ★ 指定用哪个 Ingress Controller
  tls:                            # ★ TLS配置
  - hosts:
    - api.example.com
    - web.example.com
    secretName: example-tls       # TLS证书存在这个Secret里
  rules:                          # ★ 路由规则——核心!
  - host: api.example.com         # 域名匹配
    http:
      paths:
      - path: /users              # 路径前缀匹配
        pathType: Prefix          # Prefix(前缀)或 Exact(精确)
        backend:
          service:
            name: users-service   # 转发到哪个Service
            port:
              number: 8080
      - path: /orders
        pathType: Prefix
        backend:
          service:
            name: orders-service
            port:
              number: 8080
  - host: web.example.com         # 另一个域名
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80
  defaultBackend:                 # ★ 默认后端——匹配不到任何规则时用
    resource:
      apiGroup: k8s.example.com
      kind: StorageBucket
      name: static-assets

2.1 ingressClassName——“谁来执行这些规则?”

spec:
  ingressClassName: nginx   # 告诉K8s:"这个Ingress交给Nginx Ingress Controller处理"

如果你集群里装了多个Ingress Controller(比如同时有Nginx和Traefik),这个字段确保Ingress只被指定的Controller处理。K8s 1.18+版本强烈推荐设置这个字段。

2.2 host和path——“根据什么路由?”

【Ingress路由决策流程】

  HTTP请求: GET http://api.example.com/users/123

       │
       ▼
  ┌─────────────────────────────────────────────┐
  │  Ingress Controller                          │
  │                                             │
  │  Step 1: 匹配 host                          │
  │  "Host头是 api.example.com 吗?"             │
  │  遍历所有rules,找到 host 匹配的             │
  │       │                                     │
  │       ▼                                     │
  │  Step 2: 匹配 path                          │
  │  "URL路径是 /users/123"                     │
  │  pathType是Prefix:/users能匹配 /users/123  │
  │       │                                     │
  │       ▼                                     │
  │  Step 3: 转发到 backend                     │
  │  转发到 users-service:8080                  │
  └─────────────────────────────────────────────┘

2.3 pathType——前缀还是精确?

pathType 含义 示例 /users能匹配的请求
Prefix 路径前缀匹配 path: /users /users/users//users/123/users/123/orders
Exact 精确匹配 path: /users /users
ImplementationSpecific 由Controller决定 取决于具体Controller的实现
# Prefix vs Exact 的区别
paths:
- path: /api
  pathType: Prefix     # /api、/api/、/api/users 都能匹配
  backend: ...
- path: /health
  pathType: Exact      # 只有 /health 能匹配,/health/check 不行
  backend: ...

2.4 defaultBackend——“如果都没匹配到…”

# 所有规则都没匹配上时的兜底处理
spec:
  defaultBackend:
    service:
      name: 404-handler   # 返回404页面
      port:
        number: 80
  rules:
  - host: api.example.com
    # ...

要点defaultBackend不是必选项,但我建议都配上——至少指向一个返回404的服务。否则没匹配到的请求可能会被路由到随机后端,或者返回Ingress Controller自己的默认页面,用户体验极差。


三、基于域名的路由——“一个入口,多个服务”

【域名路由示意】

  user1: api.example.com/users    ─┐
  user2: api.example.com/orders  ──┤
                                    │
                                    ▼
                          ┌─────────────────┐
                          │  Ingress Controller│
                          │  (Nginx/Traefik)  │
                          │  :80/:443         │
                          └──┬──────┬──────┬──┘
                             │      │      │
                   host匹配   │      │      │
              ┌──────────────┘      │      └──────────────┐
              ▼                     ▼                     ▼
        api.example.com      web.example.com       admin.example.com
              │                     │                     │
     ┌────────┴────────┐     ┌──────┴──────┐      ┌──────┴──────┐
     │ /users  │ /orders│    │     /       │      │     /       │
     ▼         ▼        │    ▼             │      ▼             │
  ┌──────┐ ┌────────┐   │ ┌──────────┐    │  ┌──────────┐    │
  │users │ │orders  │   │ │frontend  │    │  │admin     │    │
  │svc   │ │svc     │   │ │svc       │    │  │panel svc │    │
  └──────┘ └────────┘   │ └──────────┘    │  └──────────┘    │
                        └────────────────┘  └────────────────┘
# 域名+路径双层路由
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-domain-ingress
spec:
  ingressClassName: nginx
  rules:
  # 第一个域名:api.example.com
  - host: api.example.com
    http:
      paths:
      - path: /users
        pathType: Prefix
        backend:
          service:
            name: users-service
            port:
              number: 8080
      - path: /orders
        pathType: Prefix
        backend:
          service:
            name: orders-service
            port:
              number: 8080
      - path: /products
        pathType: Prefix
        backend:
          service:
            name: products-service
            port:
              number: 8080
  
  # 第二个域名:web.example.com
  - host: web.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80
  
  # 第三个域名:admin.example.com
  - host: admin.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: admin-service
            port:
              number: 8080

四、TLS配置——让Ingress终结HTTPS

Ingress Controller是TLS终结的最佳位置——客户端到Ingress是HTTPS,Ingress到后端Pod是HTTP。这样后端Pod不用管证书的事,省心又安全。

【TLS 终结——Ingress处理HTTPS,后端Pod只管HTTP】

  客户端                           Ingress Controller                  后端Pod
  ──────                          ──────────────────                  ──────
  https://api.example.com/users   ┌────────────────┐                  ┌────┐
  ──────────────────────────────► │ 解密TLS         │  http://10.244  │    │
  加密传输                         │ 检查Host/Path   │  ─────────────► │    │
                                  │ 路由到后端       │  明文传输        │    │
                                  │ (K8s内部网络安全) │                 └────┘
                                  └────────────────┘

4.1 手动配置TLS证书

# Step 1: 用OpenSSL生成自签名证书(仅限测试!)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout tls.key -out tls.crt \
  -subj "/CN=api.example.com/O=MyOrg"

# Step 2: 创建TLS Secret
kubectl create secret tls example-tls \
  --cert=tls.crt \
  --key=tls.key \
  -n default

# Step 3: 在Ingress中引用
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress-tls
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.example.com
    - web.example.com       # 一个Secret可以服务多个域名
    secretName: example-tls # 刚才创建的Secret
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

4.2 用cert-manager自动管理证书——“再也不用记续期时间”

cert-manager是K8s生态中管理TLS证书的事实标准。它自动从Let’s Encrypt(免费)等CA申请证书、自动续期。

# 安装cert-manager(Helm方式)
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set installCRDs=true
# 创建Issuer(证书颁发者配置)
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-prod
  namespace: default
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    - http01:
        ingress:
          class: nginx
---
# Ingress——只需加一个Annotation,cert-manager自动帮你拿证书
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress-tls-auto
  annotations:
    cert-manager.io/issuer: "letsencrypt-prod"  # ← 就这一行!
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.example.com
    secretName: api-tls-secret  # cert-manager自动创建这个Secret
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

要点:cert-manager注册免费TLS证书需要域名有真实DNS解析(Let’s Encrypt会验证域名所有权)。本地测试用自签名证书就够了,但生产环境强烈建议用cert-manager + Let’s Encrypt——免费、自动续期、不用半夜爬起来更新过期证书。


五、rewrite-target——URL路径改写

这是Ingress最常见的玩法——外部路径和内部路径不一样:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite-ingress
  annotations:
    # 关键:重写目标路径
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      # 外部: /api/users  →  内部: /users
      # 正则 (.*) 捕获组,$2 代表第二个捕获组
      - path: /api(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: api-service
            port:
              number: 8080
【URL 重写示意】

  外部请求                                    内部转发
  ─────────                                  ────────
  /api/users               ──rewrite──►      /users
  /api/users/123            ──rewrite──►      /users/123
  /api/orders               ──rewrite──►      /orders
  /health                   ──不匹配──►       /health(走其他规则或defaultBackend)

  注解说明:
  nginx.ingress.kubernetes.io/rewrite-target: /$2
  path: /api(/|$)(.*)
  正则解释:
    /api      — 匹配 "/api"
    (/|$)     — $1: 匹配 "/" 或 结尾
    (.*)      — $2: 匹配剩余所有字符
  rewrite到: /$2 = 去掉 /api 前缀

六、同一个Ingress管理多个服务——完整示例

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: main-ingress
  annotations:
    cert-manager.io/issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - "*.example.com"
    secretName: wildcard-tls
  defaultBackend:
    service:
      name: default-backend
      port:
        number: 80
  rules:
  # 用户API
  - host: api.example.com
    http:
      paths:
      - path: /v1/users
        pathType: Prefix
        backend:
          service:
            name: users-v1-service
            port:
              number: 8080
      - path: /v2/users
        pathType: Prefix
        backend:
          service:
            name: users-v2-service  # v2灰度版
            port:
              number: 8080
  
  # PC Web前端
  - host: www.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-frontend-service
            port:
              number: 80
  
  # 移动端API(不同的域名)
  - host: m.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: mobile-api-service
            port:
              number: 8080
  
  # 管理后台
  - host: admin.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: admin-service
            port:
              number: 8080
# 部署并验证
kubectl apply -f main-ingress.yaml
kubectl get ingress
# NAME           CLASS   HOSTS                              ADDRESS      PORTS
# main-ingress   nginx   api.example.com,www.example.com   10.0.1.100   80, 443
#                        m.example.com,admin.example.com

# 测试路由
curl -H "Host: api.example.com" http://10.0.1.100/v1/users
curl -H "Host: www.example.com" http://10.0.1.100/
curl -H "Host: admin.example.com" http://10.0.1.100/

本篇小结

Ingress是K8s七层路由的标准入口——它让你用一个LB管理N个服务,通过域名和路径精准分发HTTP流量:

  1. Ingress是规则,Controller是引擎:Ingress资源只是"配置文件",真正干活的是Nginx Ingress/Traefik等Controller
  2. 域名+路径双层路由hostpath组合决定流量去向,Prefix/Exact两种匹配模式各有用处
  3. TLS终结:Ingress处理HTTPS→HTTP转换,cert-manager免费自动管理证书,告别手动续期
  4. pathType和rewrite:控制路径匹配粒度和改写规则,是Ingress日常使用率最高的功能

下一篇,咱们深入Ingress Controller的世界——Nginx Ingress怎么工作、常用Annotations大全、金丝雀发布怎么配、Traefik有什么不一样的玩法。


上一篇【第16篇】Service的负载均衡原理——kube-proxy到底干了什么
下一篇【第18篇】Ingress Controller选型和实战——Nginx Ingress完全指南


Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐