Ingress - HTTP 路由

Ingress - HTTP 路由

为什么需要 Ingress

Service 只能提供 L4 负载均衡,无法实现:

  • 域名路由:根据域名转发
  • 路径路由:根据 URL 路径转发
  • SSL/TLS 终止:HTTPS 证书管理
  • 会话保持:Session Affinity
  • 重定向和重写:URL 重定向

Ingress 提供 L7(HTTP/HTTPS)负载均衡和路由。

Ingress 架构

Internet
    ↓
[Ingress Controller]
    ↓
[Ingress Rules]
    ↓
┌──────────┬──────────┬──────────┐
│ Service1 │ Service2 │ Service3 │
└──────────┴──────────┴──────────┘

组件

  • Ingress Controller:实现 Ingress 规则的控制器
  • Ingress:定义路由规则的 API 对象

安装 Ingress Controller

Nginx Ingress Controller

# 使用 Helm 安装
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install nginx-ingress ingress-nginx/ingress-nginx

# 或使用 kubectl
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/cloud/deploy.yaml

Minikube 启用

minikube addons enable ingress

# 验证
kubectl get pods -n ingress-nginx

基础示例

单个服务

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
kubectl apply -f ingress.yaml

# 查看
kubectl get ingress

# 输出
NAME             CLASS   HOSTS         ADDRESS        PORTS   AGE
simple-ingress   nginx   example.com   192.168.1.100  80      1m

访问:

curl http://example.com  # 需要配置 DNS 或 /etc/hosts

多路径路由

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: path-based-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.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
      - path: /
        pathType: Prefix
        backend:
          service:
            name: default-service
            port:
              number: 8080

路由规则:

  • myapp.com/api/* → api-service
  • myapp.com/web/* → web-service
  • myapp.com/* → default-service

多域名路由

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: host-based-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080
  - host: web.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
  - host: admin.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: admin-service
            port:
              number: 3000

HTTPS/TLS 配置

创建 TLS Secret

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

# 创建 Secret
kubectl create secret tls example-tls \
  --cert=tls.crt \
  --key=tls.key

配置 HTTPS

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-ingress
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    - www.example.com
    secretName: example-tls
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

现在可以通过 HTTPS 访问:

curl https://example.com

HTTP 重定向到 HTTPS

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: redirect-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    secretName: example-tls
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

Ingress 注解

常用注解

metadata:
  annotations:
    # 重写路径
    nginx.ingress.kubernetes.io/rewrite-target: /
    
    # 请求体大小限制
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    
    # 超时设置
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    
    # 会话保持
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "route"
    
    # CORS
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-origin: "*"
    
    # 限流
    nginx.ingress.kubernetes.io/limit-rps: "10"
    
    # 白名单
    nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/24,192.168.1.0/24"

路径重写示例

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: example.com
    http:
      paths:
      - path: /api(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

请求映射:

  • example.com/api/usersapi-service/users
  • example.com/api/v1/productsapi-service/v1/products

完整实战示例

部署多个服务

# Frontend Service
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  selector:
    app: frontend
  ports:
  - port: 80
    targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: nginx:alpine
        ports:
        - containerPort: 3000
---
# Backend API Service
apiVersion: v1
kind: Service
metadata:
  name: backend-api
spec:
  selector:
    app: backend-api
  ports:
  - port: 8080
    targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend-api
  template:
    metadata:
      labels:
        app: backend-api
    spec:
      containers:
      - name: api
        image: myapi:v1
        ports:
        - containerPort: 8080
---
# Admin Service
apiVersion: v1
kind: Service
metadata:
  name: admin
spec:
  selector:
    app: admin
  ports:
  - port: 3000
    targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: admin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: admin
  template:
    metadata:
      labels:
        app: admin
    spec:
      containers:
      - name: admin
        image: admin-panel:v1
        ports:
        - containerPort: 3000

配置 Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - myapp.com
    - api.myapp.com
    - admin.myapp.com
    secretName: myapp-tls
  rules:
  # 主站
  - host: myapp.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend
            port:
              number: 80
  # API
  - host: api.myapp.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: backend-api
            port:
              number: 8080
  # 管理后台
  - host: admin.myapp.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: admin
            port:
              number: 3000

高级功能

金丝雀发布

# 主版本
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-main
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-v1
            port:
              number: 80
---
# 金丝雀版本(10% 流量)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-v2
            port:
              number: 80

基于 Header 的路由

metadata:
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
    nginx.ingress.kubernetes.io/canary-by-header-value: "true"

基本认证

# 创建密码文件
htpasswd -c auth admin
kubectl create secret generic basic-auth --from-file=auth
metadata:
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"

速率限制

metadata:
  annotations:
    nginx.ingress.kubernetes.io/limit-rps: "10"
    nginx.ingress.kubernetes.io/limit-connections: "5"

Cert-Manager 自动证书

安装 Cert-Manager

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml

配置 Let's Encrypt

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

使用自动证书

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    secretName: example-tls  # Cert-Manager 会自动创建
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

常用命令

# 查看 Ingress
kubectl get ingress
kubectl describe ingress <name>

# 查看 Ingress Controller 日志
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx

# 测试 Ingress
kubectl get ingress <name> -o jsonpath='{.status.loadBalancer.ingress[0].ip}'

# 删除 Ingress
kubectl delete ingress <name>

# 查看 Ingress Class
kubectl get ingressclass

最佳实践

1. 使用 Ingress Class

spec:
  ingressClassName: nginx  # 明确指定

2. 合理设置超时

metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"

3. 启用 HTTPS

metadata:
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"

4. 设置资源限制

metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"

5. 监控和日志

# 查看访问日志
kubectl logs -n ingress-nginx <pod-name>

# 监控指标
kubectl port-forward -n ingress-nginx <pod-name> 10254:10254
curl http://localhost:10254/metrics

小结

Ingress 是 Kubernetes 中实现 HTTP/HTTPS 路由的核心资源:

核心功能

  • 域名路由:基于域名转发
  • 路径路由:基于 URL 路径转发
  • TLS 终止:HTTPS 证书管理
  • 高级路由:重写、重定向、金丝雀

组件

  • Ingress Controller:实现者(Nginx、Traefik、HAProxy)
  • Ingress:路由规则定义

最佳实践

  • 使用 HTTPS 和自动证书
  • 合理配置超时和限流
  • 监控 Ingress Controller
  • 使用金丝雀发布

下一章我们将学习资源管理,优化集群资源使用。