概述
Kubernetes Service 提供四层负载均衡,但在生产环境中,绝大多数 Web 应用需要七层路由能力:基于域名的虚拟主机、基于路径的路由、TLS 终止、灰度发布。Ingress 就是 K8s 对七层路由的抽象,而 Ingress Controller 则是这一抽象的具体实现。
选择 Ingress Controller 不是一个小决策——它处于所有外部流量的入口位置,一旦选错或配置不当,影响的是整个集群的服务可用性。本文对比主流 Ingress Controller 的优劣,并给出生产环境配置实践。
本文基于 Kubernetes v1.30。参考 Kubernetes Ingress 文档
Ingress 原理
数据流路径
客户端 → 负载均衡器(云LB/MetalLB) → Ingress Controller Pod → Service → Pod
↑
Ingress 资源
(路由规则)
Ingress Controller 本质上是一个运行在集群中的 Pod(通常是 Deployment 或 DaemonSet),它:
- 监听 K8s API 中的 Ingress 资源变化
- 将 Ingress 规则翻译成自身配置(如 nginx.conf)
- 热加载配置,处理外部请求并路由到对应 Service
Ingress 资源结构
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: api-v1-service
port:
number: 8080
- path: /v2
pathType: Prefix
backend:
service:
name: api-v2-service
port:
number: 8080
pathType 三种模式
| pathType | 匹配规则 | 示例 |
|---|---|---|
Exact | 精确匹配 | /health 只匹配 /health |
Prefix | 前缀匹配 | /api 匹配 /api、/api/v1、/api/v2/users |
ImplementationSpecific | 由 Ingress Controller 决定 | Nginx 用正则,Traefik 用自己的规则 |
注意:
Prefix匹配/api时也会匹配/apixxx,因为它是前缀匹配而非路径分段匹配。如果需要按路径分段匹配,使用 Nginx 的rewrite-target注解或正则路径。
主流 Ingress Controller 对比
四大主流方案
| 特性 | Nginx Ingress | Traefik | HAProxy Ingress | Envoy Gateway |
|---|---|---|---|---|
| 底层引擎 | Nginx/OpenResty | 自研 Go | HAProxy | Envoy |
| 配置方式 | 注解 + ConfigMap | CRD + 注解 | 注解 + ConfigMap | CRD + xDS |
| 动态配置 | 需要 reload(社区版) | 热加载 | 热加载 | 热加载 |
| 性能 | 高 | 中高 | 极高 | 极高 |
| 社区活跃度 | 最高 | 高 | 中 | 高(CNCF) |
| 学习曲线 | 低(熟悉 Nginx) | 低 | 中 | 高 |
| 生态丰富度 | 高 | 中 | 中 | 高(Service Mesh) |
| 适用规模 | 中小到中大型 | 中小型 | 大型 | 大型/Service Mesh |
各方案详解
Nginx Ingress Controller
最广泛使用的 Ingress Controller,分为社区版(kubernetes/ingress-nginx)和 F5 版(nginxinc/kubernetes-ingress)。
优势:社区最大,文档丰富,踩坑资料多;基于 Nginx,运维人员熟悉;注解丰富,支持复杂路由逻辑。
劣势:社区版配置变更需要 reload(有毫秒级中断);注解太多导致配置碎片化,难以管理;对高级流量管理(金丝雀、A/B 测试)支持不够优雅。
Traefik
Go 语言编写的现代反向代理,自动服务发现。
优势:配置热加载无中断;原生支持 Let’s Encrypt 自动证书;Dashboard 直观;CRD(IngressRoute)功能强大。
劣势:性能不如 Nginx/HAProxy;高级路由依赖 CRD,与标准 Ingress 兼容性一般;社区版功能有限,企业版收费。
HAProxy Ingress
基于工业级 LB HAProxy。
优势:性能极高,连接复用优秀;统计报表强大;适合高并发场景。
劣势:社区较小,文档不如 Nginx 丰富;注解风格与 Nginx 不同,迁移成本高。
Envoy Gateway
基于 Envoy 的下一代网关,CNCF 项目。
优势:极高性能和可观测性;原生支持 HTTP/2、gRPC;与 Service Mesh(Istio)无缝衔接;xDS 动态配置无中断。
劣势:学习曲线陡峭;生态仍在发展中;配置复杂度高。
选型建议
| 场景 | 推荐 | 原因 |
|---|---|---|
| 通用 Web 应用,团队熟悉 Nginx | Nginx Ingress | 生态成熟,资料丰富 |
| 快速原型/中小规模,需自动证书 | Traefik | 配置简单,Let’s Encrypt 集成 |
| 超高并发,性能优先 | HAProxy Ingress | 性能最优 |
| Service Mesh / gRPC 密集 | Envoy Gateway | 原生支持,与 Istio 集成 |
| 多团队共享集群 | Nginx Ingress | 多 IngressClass 隔离 |
Nginx Ingress Controller 部署
安装
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.replicaCount=2 \
--set controller.service.type=LoadBalancer \
--set controller.config.proxy-body-size=50m \
--set controller.config.proxy-read-timeout=3600 \
--set controller.config.proxy-send-timeout=3600
生产级 Helm 配置
# values.yaml
controller:
replicaCount: 2
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 1000m
memory: 512Mi
nodeSelector:
node-role: ingress
tolerations:
- key: "ingress-only"
operator: "Equal"
value: "true"
effect: "NoSchedule"
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
topologyKey: kubernetes.io/hostname
config:
proxy-body-size: "50m"
proxy-read-timeout: "3600"
proxy-send-timeout: "3600"
proxy-connect-timeout: "10"
keep-alive: "120"
keep-alive-requests: "10000"
worker-processes: "auto"
max-worker-connections: "65535"
enable-brotli: "true"
brotli-types: "text/xml text/plain text/css application/javascript application/json image/svg+xml"
use-gzip: "true"
gzip-types: "text/plain text/css application/javascript application/json"
ssl-protocols: "TLSv1.2 TLSv1.3"
ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"
ssl-session-cache: "shared:SSL:10m"
ssl-session-timeout: "10m"
log-format-escape-json: "true"
log-format-upstream: '{"time":"$time_iso8601","remote_addr":"$remote_addr","x_forwarded_for":"$proxy_add_x_forwarded_for","request":"$request","status":$status,"body_bytes_sent":$body_bytes_sent,"request_time":$request_time,"upstream_response_time":"$upstream_response_time","upstream_addr":"$upstream_addr","upstream_status":"$upstream_status","http_referer":"$http_referer","http_user_agent":"$http_user_agent","request_id":"$req_id"}'
podSecurityContext:
runAsNonRoot: true
runAsUser: 101
fsGroup: 101
containerSecurityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
healthCheckPath: /healthz
metrics:
enabled: true
serviceMonitor:
enabled: true
admissionWebhooks:
enabled: true
failurePolicy: Fail
defaultBackend:
enabled: true
replicaCount: 2
TLS 终止
证书管理方案
| 方案 | 适用场景 | 特点 |
|---|---|---|
| 手动创建 TLS Secret | 内部服务/自签证书 | 简单,需手动续期 |
| cert-manager + Let’s Encrypt | 公网域名 | 自动签发和续期 |
| cert-manager + 自建 CA | 内部服务 | 统一管理内部证书 |
| 云厂商证书管理 | 云上 K8s | 与云 LB 集成 |
cert-manager 自动证书
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set installCRDs=true
# Let's Encrypt 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-key
solvers:
- http01:
ingress:
class: nginx
# Ingress 自动签发证书
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- api.example.com
secretName: api-tls # cert-manager 会自动创建
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
TLS 版本与加密套件加固
# ConfigMap TLS 加固
data:
ssl-protocols: "TLSv1.2 TLSv1.3" # 禁用 TLS 1.0/1.1
ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
ssl-prefer-server-ciphers: "true"
ssl-session-cache: "shared:SSL:10m"
ssl-session-timeout: "10m"
ssl-session-tickets: "false" # 禁用 session tickets
hsts: "true" # 启用 HSTS
hsts-max-age: "31536000"
hsts-include-subdomains: "true"
hsts-preload: "true"
HTTP 强制跳转 HTTPS
# 全局跳转(ConfigMap)
data:
ssl-redirect: "true"
# 单个 Ingress 注解
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
路径路由
基于路径的多服务路由
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: api.example.com
http:
paths:
# /api/v1/* -> api-v1-service/*
- path: /api/v1(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-v1-service
port:
number: 8080
# /api/v2/* -> api-v2-service/*
- path: /api/v2(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-v2-service
port:
number: 8080
# /static/* -> static-service/*(不重写路径)
- path: /static/
pathType: Prefix
backend:
service:
name: static-service
port:
number: 80
# / -> frontend-service
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
WebSocket 支持
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/upstream-hash-by: "$remote_addr" # 会话保持
Nginx Ingress Controller 默认支持 WebSocket,只需确保超时时间足够长。
gRPC 路由
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: grpc-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
spec:
tls:
- hosts:
- grpc.example.com
secretName: grpc-tls
rules:
- host: grpc.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: grpc-service
port:
number: 9090
灰度发布
Canary 注解方式
Nginx Ingress Controller 通过注解支持金丝雀发布:
# 主 Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-main
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-stable
port:
number: 8080
# Canary Ingress —— 按权重灰度
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10" # 10%流量到新版本
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-canary
port:
number: 8080
灰度策略对比
| 策略 | 注解 | 适用场景 |
|---|---|---|
| 按权重 | canary-weight: "10" | 10%流量到新版本 |
| 按 Header | canary-by-header: "x-canary" | 指定 Header 值路由到新版本 |
| 按 Cookie | canary-by-cookie: "canary" | 指定 Cookie 路由到新版本 |
# 按 Header 灰度
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"
# 组合使用:10%流量 + 特定Header全量
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
nginx.ingress.kubernetes.io/canary-by-header: "x-canary"
nginx.ingress.kubernetes.io/canary-by-header-value: "always"
注意:Canary Ingress 不会单独处理请求,它只是在主 Ingress 的基础上添加路由规则。两个 Ingress 必须使用相同的 host 和 path。
性能调优
Nginx Worker 参数调优
# ConfigMap 调优
data:
worker-processes: "auto" # 自动匹配CPU核数
worker-cpu-affinity: "auto" # CPU 亲和性
max-worker-connections: "65535" # 单 worker 最大连接数
max-worker-open-files: "65535" # 单 worker 最大文件描述符
worker-shutdown-timeout: "240s" # 优雅关闭等待时间
worker-processes: "4" # 手动指定(auto不准时)
连接与超时调优
data:
keep-alive: "120" # 客户端 keep-alive 秒数
keep-alive-requests: "10000" # keep-alive 最大请求数
upstream-keepalive-connections: "320" # 到后端的 keepalive 连接数
upstream-keepalive-timeout: "60" # 到后端的 keepalive 超时
upstream-keepalive-requests: "10000" # 到后端的 keepalive 请求数
proxy-connect-timeout: "10" # 连接后端超时
proxy-read-timeout: "60" # 读后端超时
proxy-send-timeout: "60" # 发后端超时
proxy-next-upstream: "error timeout" # 失败后重试下一个后端
proxy-next-upstream-tries: "3" # 最多重试3次
压缩优化
data:
use-gzip: "true"
gzip-types: "text/plain text/css application/javascript application/json application/xml image/svg+xml"
gzip-min-length: "1024" # 大于1KB才压缩
gzip-comp-level: "5" # 压缩级别1-9
enable-brotli: "true" # Brotli 压缩(比gzip好)
brotli-types: "text/plain text/css application/javascript application/json application/xml image/svg+xml"
brotli-min-length: "1024"
缓冲区调优
data:
proxy-buffer-size: "16k" # 代理缓冲区大小
proxy-buffers: "4 16k" # 代理缓冲区数量和大小
proxy-busy-buffers-size: "32k" # 繁忙缓冲区大小
client-header-buffer-size: "4k" # 客户端请求头缓冲区
large-client-header-buffers: "4 16k" # 大请求头缓冲区
负载均衡策略
# 默认:轮询(round-robin)
# 注解方式切换
annotations:
nginx.ingress.kubernetes.io/load-balance: "least_conn" # 最少连接
# nginx.ingress.kubernetes.io/load-balance: "ip_hash" # IP哈希(会话保持)
# nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri" # URI哈希
| 策略 | 说明 | 适用场景 |
|---|---|---|
round_robin(默认) | 轮询 | 通用 |
least_conn | 最少连接 | 长连接、请求耗时差异大 |
ip_hash | 客户端 IP 哈希 | 需要会话保持 |
consistent_hash | 一致性哈希 | 缓存场景 |
可观测性
日志配置
# JSON 格式日志(推荐)
data:
log-format-escape-json: "true"
log-format-upstream: >-
{"time":"$time_iso8601","remote_addr":"$remote_addr","x_forwarded_for":"$proxy_add_x_forwarded_for","request":"$request","method":"$request_method","uri":"$request_uri","status":$status,"body_bytes_sent":$body_bytes_sent,"request_time":$request_time,"upstream_response_time":"$upstream_response_time","upstream_addr":"$upstream_addr","upstream_status":"$upstream_status","http_referer":"$http_referer","http_user_agent":"$http_user_agent","request_id":"$req_id","host":"$host","server_name":"$server_name"}
Prometheus 指标
# 启用 metrics
controller:
metrics:
enabled: true
serviceMonitor:
enabled: true
additionalLabels:
release: prometheus
关键指标:
| 指标 | 含义 | 告警阈值 |
|---|---|---|
nginx_ingress_controller_requests | 请求总数/QPS | 根据容量规划 |
nginx_ingress_controller_response_duration_seconds | 响应延迟 P99 | > 1s |
nginx_ingress_controller_nginx_process_connections | 当前连接数 | > max-worker-connections 的 80% |
nginx_ingress_controller_upstream_latency_seconds | 后端延迟 | > 500ms |
nginx_ingress_controller_errors | 错误数 | 持续增长 |
Grafana Dashboard
推荐使用官方 Dashboard ID:9614(Nginx Ingress Controller)和 14314(Nginx 详细监控)。
生产实践
多 IngressClass 隔离
# 内网 IngressClass
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx-internal
annotations:
ingressclass.kubernetes.io/is-default-class: "false"
spec:
controller: k8s.io/ingress-nginx
parameters:
apiGroup: k8s.example.com
kind: IngressParameters
name: internal-params
# 外网 IngressClass
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx-external
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: k8s.io/ingress-nginx
节点亲和性部署
# 将 Ingress Controller 调度到专用节点
apiVersion: apps/v1
kind: Deployment
metadata:
name: ingress-nginx-controller
spec:
template:
spec:
nodeSelector:
node-role.kubernetes.io/ingress: "true"
tolerations:
- key: "ingress-only"
operator: "Exists"
effect: "NoSchedule"
# 反亲和性确保副本分布在不同节点
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
topologyKey: kubernetes.io/hostname
速率限制
annotations:
nginx.ingress.kubernetes.io/limit-connections: "100" # 最大并发连接
nginx.ingress.kubernetes.io/limit-rps: "50" # 每秒请求数
nginx.ingress.kubernetes.io/limit-burst: "100" # 突发请求数
nginx.ingress.kubernetes.io/limit-retry-after-time: "60" # 429响应Retry-After
基础认证
# 创建 htpasswd Secret
kubectl create secret generic basic-auth \
--from-file=auth \
-n production
# Ingress 注解
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
CORS 配置
annotations:
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com,https://app.example.com"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"
nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
nginx.ingress.kubernetes.io/cors-max-age: "86400"
常见问题排查
502 Bad Gateway
# 原因:后端 Service 没有就绪或 Pod 不可达
# 排查步骤:
kubectl get endpoints <service-name> -n <namespace> # 检查 Endpoints
kubectl get pods -l app=<app-name> -n <namespace> # 检查 Pod 状态
kubectl logs <ingress-pod> -n ingress-nginx | grep <domain> # 检查 Ingress 日志
504 Gateway Timeout
# 原因:后端响应超时
# 解决:调整超时配置
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
配置不生效
# 检查 Ingress 是否被正确识别
kubectl get ingress -A
kubectl describe ingress <name> -n <namespace>
# 检查 IngressClass 是否匹配
kubectl get ingressclass
# 检查 Nginx 配置是否生成
kubectl exec -it <ingress-pod> -n ingress-nginx -- cat /etc/nginx/nginx.conf | grep <domain>
# 强制 reload(不推荐,仅排障)
kubectl exec -it <ingress-pod> -n ingress-nginx -- /nginx-ingress-controller --publish-service 2>&1 | head
SSL 证书不生效
# 检查证书 Secret 是否存在
kubectl get secret <tls-secret-name> -n <namespace>
kubectl get certificate -n <namespace> # cert-manager
# 检查证书内容
kubectl get secret <tls-secret-name> -n <namespace> -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout
总结
Ingress Controller 是 K8s 集群的流量入口,选型和配置直接影响服务可用性。核心要点:
- 选型看场景:Nginx Ingress 是最安全的选择,生态成熟、资料丰富。只有在有明确需求(极高性能、Service Mesh 集成)时才考虑其他方案。
- TLS 必须加固:禁用 TLS 1.0/1.1,启用 HSTS,使用 cert-manager 自动管理证书。
- 灰度发布用 Canary 注解:Nginx Ingress 的 canary 注解能满足大部分灰度需求,复杂的流量管理考虑 Istio/Envoy。
- 性能调优抓关键参数:
max-worker-connections、upstream-keepalive-connections、keep-alive是三个最关键的性能参数。 - 可观测性不能少:JSON 日志 + Prometheus 指标是标配,没有可观测性的网关等于盲飞。
- 多副本 + 反亲和性:Ingress Controller 至少 2 副本,分布在不同节点,避免单点故障。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Kubernetes Ingress 文档 — Kubernetes 官方,参考了Kubernetes Ingress 文档相关内容