跳过正文

Kubernetes Ingress 配置实践

·1214 字·6 分钟·
目录

为什么需要 Ingress
#

没有 Ingress 时的困境:

  • 每个服务要对外暴露就得用 Service type: LoadBalancer,会消耗大量云负载均衡器资源,成本高
  • 无法基于 Host / Path 进行路由,所有流量都是 L4 级别
  • TLS 终止需要在每个服务单独处理
  • 无法统一做限速、认证、监控

Ingress 做了什么:

外部请求
   ↓
LoadBalancer(只需要一个)
   ↓
Ingress Controller(nginx/traefik 等)
   ↓  基于 Host、Path 路由
Service A / Service B / Service C

Ingress 工作在 L7(HTTP/HTTPS)层,可以做:基于域名路由、基于路径路由、TLS 终止、重写 URL、限速、认证、灰度发布等。

Ingress 与 Service 的区别:

特性Service(LoadBalancer)Ingress
工作层L4(TCP/UDP)L7(HTTP/HTTPS)
路由能力Host / Path
TLS 终止需自行处理统一处理
云LB 数量每个 Service 一个共享一个
成本

Ingress Controller 选型
#

Controller维护方适用场景优势劣势
ingress-nginxK8s 社区通用场景功能最全、社区大、文档多配置相对复杂
TraefikTraefik Labs微服务、动态配置自动发现、Dashboard 好看学习曲线
AWS ALBAWSEKS + AWS 原生与 AWS 深度集成只能在 AWS 用
ContourVMware/CNCF需要 HTTP/2、gRPCEnvoy 作为数据面,性能好社区较小
HAProxyHAProxy Tech高性能四/七层极致性能配置麻烦

生产选型建议:

  • 自建 K8s / 非云厂商托管 → ingress-nginx(最成熟,问题多容易搜到答案)
  • EKS 且深度使用 AWS 服务 → AWS Load Balancer Controller (ALB)
  • 需要动态证书管理和漂亮 Dashboard → Traefik

安装 ingress-nginx
#

# 使用 Helm 安装(推荐)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

# 生产配置
cat > ingress-nginx-values.yaml << 'EOF'
controller:
  replicaCount: 2        # 至少 2 副本,高可用

  # 资源限制
  resources:
    requests:
      cpu: 100m
      memory: 90Mi
    limits:
      cpu: 500m
      memory: 512Mi

  # Pod 反亲和,避免两个副本调度到同一节点
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          podAffinityTerm:
            labelSelector:
              matchExpressions:
                - key: app.kubernetes.io/component
                  operator: In
                  values:
                    - controller
            topologyKey: kubernetes.io/hostname

  # 全局默认配置
  config:
    use-gzip: "true"
    gzip-level: "5"
    proxy-body-size: "100m"
    proxy-read-timeout: "60"
    proxy-connect-timeout: "10"
    keep-alive: "75"
    worker-processes: "auto"

  # 开启 metrics(用于 Prometheus 采集)
  metrics:
    enabled: true
    serviceMonitor:
      enabled: true   # 如果使用 kube-prometheus-stack

  # HPA 配置
  autoscaling:
    enabled: true
    minReplicas: 2
    maxReplicas: 10
    targetCPUUtilizationPercentage: 70

  service:
    annotations:
      # AWS EKS:使用 NLB
      service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
      service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
EOF

helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
  -n ingress-nginx \
  --create-namespace \
  -f ingress-nginx-values.yaml \
  --wait \
  --timeout 5m

Ingress 资源配置
#

基础路由
#

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  namespace: production
  annotations:
    kubernetes.io/ingress.class: "nginx"   # 指定使用哪个 Controller
spec:
  ingressClassName: nginx    # K8s 1.18+ 推荐方式
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-service
                port:
                  number: 80

Path 类型:Prefix vs Exact vs ImplementationSpecific
#

类型说明匹配示例
Prefix前缀匹配(基于 / 分割的路径段)/api 匹配 /api/api/users/api/v1/
Exact精确匹配/api 只匹配 /api,不匹配 /api/
ImplementationSpecific行为取决于 IngressClassnginx 中等同于 Prefix
# 常见场景:前端走根路径,API 走 /api 前缀
spec:
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix      # /api、/api/users 都匹配
            backend:
              service:
                name: api-service
                port:
                  number: 8080
          - path: /
            pathType: Prefix      # 兜底路由,放在最后
            backend:
              service:
                name: frontend-service
                port:
                  number: 80

多域名路由
#

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-domain-ingress
  namespace: production
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com
        - api.example.com
      secretName: example-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-service
                port:
                  number: 80
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 8080

URL Rewrite
#

# 将 /app/api/users 重写为 /api/users(去掉 /app 前缀)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2   # $2 对应 (.*) 捕获组
spec:
  ingressClassName: nginx
  rules:
    - host: example.com
      http:
        paths:
          - path: /app(/|$)(.*)      # 正则:/app 后面的内容赋值给 $2
            pathType: ImplementationSpecific
            backend:
              service:
                name: app-service
                port:
                  number: 80

TLS 配置
#

方法一:cert-manager 自动签发(推荐)
#

# 安装 cert-manager
helm upgrade --install cert-manager jetstack/cert-manager \
  -n cert-manager \
  --create-namespace \
  --set installCRDs=true \
  --version v1.13.0 \
  --wait
# 创建 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
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
      - http01:
          ingress:
            class: nginx
# Ingress 中引用,cert-manager 自动创建证书 Secret
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-ingress
  namespace: production
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"   # 关键 annotation
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com
      secretName: app-example-tls    # cert-manager 会自动创建这个 Secret
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app-service
                port:
                  number: 80
# 查看证书状态
kubectl get certificate -n production
kubectl describe certificate app-example-tls -n production

# 证书签发过程中查看 Challenge
kubectl get challenges -n production

方法二:手动管理证书 Secret
#

# 从证书文件创建 Secret
kubectl create secret tls my-tls-secret \
  --cert=path/to/tls.crt \
  --key=path/to/tls.key \
  -n production

# 查看证书到期时间
kubectl get secret my-tls-secret -n production -o jsonpath='{.data.tls\.crt}' \
  | base64 -d | openssl x509 -noout -dates

常用 Annotations
#

限速与超时
#

metadata:
  annotations:
    # 限制每秒请求数(基于客户端 IP)
    nginx.ingress.kubernetes.io/limit-rps: "10"
    # 限制每分钟连接数
    nginx.ingress.kubernetes.io/limit-connections: "5"

    # 超时设置(秒)
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "60"

    # 请求体大小限制
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"

Proxy Buffer 配置
#

metadata:
  annotations:
    # 启用 proxy buffer(大响应时避免 upstream 等待 client 读取)
    nginx.ingress.kubernetes.io/proxy-buffering: "on"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
    nginx.ingress.kubernetes.io/proxy-buffer-size: "8k"

CORS 配置
#

metadata:
  annotations:
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.example.com"
    nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
    nginx.ingress.kubernetes.io/cors-allow-headers: "Authorization, Content-Type, X-Requested-With"
    nginx.ingress.kubernetes.io/cors-max-age: "600"

Basic Auth 认证
#

# 创建 htpasswd 文件
htpasswd -c auth admin
kubectl create secret generic basic-auth \
  --from-file=auth \
  -n production
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"

HTTP 强制跳转 HTTPS
#

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

WebSocket 支持
#

metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"   # 长连接不超时
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/configuration-snippet: |
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";

灰度发布(Canary)
#

ingress-nginx 内置 Canary 支持,通过 annotations 实现按比例/按 Header/按 Cookie 分流。

按权重分流(最常用)
#

# 稳定版 Ingress(原有)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress-stable
  namespace: production
spec:
  ingressClassName: nginx
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app-service-v1
                port:
                  number: 80
---
# 金丝雀 Ingress(新版本,承载 10% 流量)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress-canary
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"   # 10% 流量到新版本
spec:
  ingressClassName: nginx
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app-service-v2    # 新版本 Service
                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"
    # 请求头带 X-Canary: true 的流量路由到新版本

按 Cookie 分流#

metadata:
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-cookie: "canary_user"
    # Cookie 中 canary_user=always 则路由到新版本
    # canary_user=never 则永远走稳定版

灰度发布流程:

# 1. 创建 canary Ingress,先 5% 流量
kubectl apply -f ingress-canary.yaml

# 2. 观察监控,逐步提高比例
kubectl annotate ingress app-ingress-canary \
  nginx.ingress.kubernetes.io/canary-weight=30 \
  --overwrite -n production

# 3. 稳定后切全量
kubectl annotate ingress app-ingress-canary \
  nginx.ingress.kubernetes.io/canary-weight=100 \
  --overwrite -n production

# 4. 删除旧 Service,删除 canary Ingress,更新 stable Ingress 指向新版本
kubectl delete ingress app-ingress-canary -n production

排查常见问题
#

Ingress 不生效
#

# 1. 确认 Ingress 资源存在且 ADDRESS 已分配
kubectl get ingress -n production
# ADDRESS 列为空说明 Controller 没有处理到,检查 ingressClassName

# 2. 检查 ingressClassName 是否匹配
kubectl get ingressclass
kubectl get ingress <name> -n production -o jsonpath='{.spec.ingressClassName}'

# 3. 查看 ingress-nginx controller 日志
kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller --tail=50

# 4. 确认 Service 和 Endpoints 正常
kubectl get endpoints <svc-name> -n production

502 Bad Gateway
#

# 通常是后端 Pod 无法访问
# 1. 检查 Endpoints
kubectl get endpoints <backend-service> -n production

# 2. 测试从 Controller 到 Pod 的连通性
kubectl exec -n ingress-nginx <nginx-pod> -- curl http://<pod-ip>:<port>

# 3. 查看 nginx 错误日志
kubectl logs -n ingress-nginx <nginx-pod> | grep "upstream"

# 4. 检查 Service port 配置是否匹配 Pod 的 containerPort
kubectl describe svc <svc-name> -n production
kubectl describe pod <pod-name> -n production | grep -A 5 "Ports:"

SSL 证书问题
#

# 查看 cert-manager 日志
kubectl logs -n cert-manager -l app=cert-manager --tail=50

# 查看 Certificate 资源状态
kubectl describe certificate <cert-name> -n production

# 查看 CertificateRequest
kubectl get certificaterequest -n production
kubectl describe certificaterequest <name> -n production

# 查看 ACME Challenge(http01 验证)
kubectl get challenges -n production
# Challenge 需要通过 http://domain/.well-known/acme-challenge/xxx 可访问

# 手动测试 ACME 验证路径是否可达
curl http://app.example.com/.well-known/acme-challenge/test

查看 nginx 实际配置
#

# 进入 Controller Pod 查看生成的 nginx.conf
kubectl exec -n ingress-nginx <controller-pod> -- cat /etc/nginx/nginx.conf | grep -A 20 "server_name app.example.com"

# 或使用 nginx -T 查看完整配置(包含所有 include)
kubectl exec -n ingress-nginx <controller-pod> -- nginx -T 2>/dev/null | head -200
Wenzhuo Huang
作者
Wenzhuo Huang
搞运维的工程师,写代码的运维人。专注 Kubernetes、AWS、GitOps 与基础设施可靠性。这个博客既是我的技术笔记本,也是我踩过的坑的受害者档案。

相关文章

Helm 使用指南:从入门到生产实践

·1312 字·7 分钟
Helm 从入门到生产实践:Chart 结构、values 覆盖、模板语法、–atomic/–wait 等生产参数,以及常用 Chart 安装示例。

Kubernetes 安全加固实践

·1561 字·8 分钟
K8s 安全加固从 Pod 到集群:SecurityContext 配置、网络策略隔离、Secret 安全管理、镜像漏洞扫描、RBAC 最小权限原则的落地实践。

Kubernetes 故障排查 SOP

·1491 字·7 分钟
从现象到根因的 K8s 故障排查全流程:Pod 异常状态、Node NotReady、Service 不通、存储挂载失败等场景的系统化排查方法。