Ingress 生产实战
概念引入
文章 09 里你学了一个最简单的 Ingress:按域名+路径把流量分到不同 Service。但生产环境远比这复杂:
基础 Ingress(#09) 生产 Ingress(本篇)
───────────────── ─────────────────
http://app.com → web https://app.com → web ← TLS 加密
http://app.com/api → api /api(/.*) → /$1 ← rewrite 重写
同一 Service 按 header 分流 ← 金丝雀灰度
每个 IP 每秒最多 10 请求 ← 限流保护
本文将同时展示传统 Ingress 注解方式 和 Gateway API 等效实现,帮你理解两者的联系。
#mermaid-svg-ybMOQy3XgiFdqjMZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ybMOQy3XgiFdqjMZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ybMOQy3XgiFdqjMZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ybMOQy3XgiFdqjMZ .error-icon{fill:#552222;}#mermaid-svg-ybMOQy3XgiFdqjMZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ybMOQy3XgiFdqjMZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ybMOQy3XgiFdqjMZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ybMOQy3XgiFdqjMZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ybMOQy3XgiFdqjMZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ybMOQy3XgiFdqjMZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ybMOQy3XgiFdqjMZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ybMOQy3XgiFdqjMZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ybMOQy3XgiFdqjMZ .marker.cross{stroke:#333333;}#mermaid-svg-ybMOQy3XgiFdqjMZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ybMOQy3XgiFdqjMZ p{margin:0;}#mermaid-svg-ybMOQy3XgiFdqjMZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ybMOQy3XgiFdqjMZ .cluster-label text{fill:#333;}#mermaid-svg-ybMOQy3XgiFdqjMZ .cluster-label span{color:#333;}#mermaid-svg-ybMOQy3XgiFdqjMZ .cluster-label span p{background-color:transparent;}#mermaid-svg-ybMOQy3XgiFdqjMZ .label text,#mermaid-svg-ybMOQy3XgiFdqjMZ span{fill:#333;color:#333;}#mermaid-svg-ybMOQy3XgiFdqjMZ .node rect,#mermaid-svg-ybMOQy3XgiFdqjMZ .node circle,#mermaid-svg-ybMOQy3XgiFdqjMZ .node ellipse,#mermaid-svg-ybMOQy3XgiFdqjMZ .node polygon,#mermaid-svg-ybMOQy3XgiFdqjMZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ybMOQy3XgiFdqjMZ .rough-node .label text,#mermaid-svg-ybMOQy3XgiFdqjMZ .node .label text,#mermaid-svg-ybMOQy3XgiFdqjMZ .image-shape .label,#mermaid-svg-ybMOQy3XgiFdqjMZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-ybMOQy3XgiFdqjMZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ybMOQy3XgiFdqjMZ .rough-node .label,#mermaid-svg-ybMOQy3XgiFdqjMZ .node .label,#mermaid-svg-ybMOQy3XgiFdqjMZ .image-shape .label,#mermaid-svg-ybMOQy3XgiFdqjMZ .icon-shape .label{text-align:center;}#mermaid-svg-ybMOQy3XgiFdqjMZ .node.clickable{cursor:pointer;}#mermaid-svg-ybMOQy3XgiFdqjMZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ybMOQy3XgiFdqjMZ .arrowheadPath{fill:#333333;}#mermaid-svg-ybMOQy3XgiFdqjMZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ybMOQy3XgiFdqjMZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ybMOQy3XgiFdqjMZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ybMOQy3XgiFdqjMZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ybMOQy3XgiFdqjMZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ybMOQy3XgiFdqjMZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ybMOQy3XgiFdqjMZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ybMOQy3XgiFdqjMZ .cluster text{fill:#333;}#mermaid-svg-ybMOQy3XgiFdqjMZ .cluster span{color:#333;}#mermaid-svg-ybMOQy3XgiFdqjMZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ybMOQy3XgiFdqjMZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ybMOQy3XgiFdqjMZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-ybMOQy3XgiFdqjMZ .icon-shape,#mermaid-svg-ybMOQy3XgiFdqjMZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ybMOQy3XgiFdqjMZ .icon-shape p,#mermaid-svg-ybMOQy3XgiFdqjMZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ybMOQy3XgiFdqjMZ .icon-shape .label rect,#mermaid-svg-ybMOQy3XgiFdqjMZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ybMOQy3XgiFdqjMZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ybMOQy3XgiFdqjMZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ybMOQy3XgiFdqjMZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HTTPS
TLS 终止后明文转发
/api 请求重写到 /
header: version=v2
→ 金丝雀版本
客户端
Ingress Controller
(Nginx)
web Service
api Service
canary Service
原理讲解
TLS 终止
TLS 终止在 Ingress 层面完成------客户端到 Ingress 是 HTTPS,Ingress 到后端 Pod 是 HTTP:
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-tls
spec:
tls:
- hosts:
- app.example.com
secretName: app-tls-secret # 包含证书和私钥的 Secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
#mermaid-svg-7u4TDkMgvaOCXDJH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7u4TDkMgvaOCXDJH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7u4TDkMgvaOCXDJH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7u4TDkMgvaOCXDJH .error-icon{fill:#552222;}#mermaid-svg-7u4TDkMgvaOCXDJH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7u4TDkMgvaOCXDJH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7u4TDkMgvaOCXDJH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7u4TDkMgvaOCXDJH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7u4TDkMgvaOCXDJH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7u4TDkMgvaOCXDJH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7u4TDkMgvaOCXDJH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7u4TDkMgvaOCXDJH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7u4TDkMgvaOCXDJH .marker.cross{stroke:#333333;}#mermaid-svg-7u4TDkMgvaOCXDJH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7u4TDkMgvaOCXDJH p{margin:0;}#mermaid-svg-7u4TDkMgvaOCXDJH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7u4TDkMgvaOCXDJH .cluster-label text{fill:#333;}#mermaid-svg-7u4TDkMgvaOCXDJH .cluster-label span{color:#333;}#mermaid-svg-7u4TDkMgvaOCXDJH .cluster-label span p{background-color:transparent;}#mermaid-svg-7u4TDkMgvaOCXDJH .label text,#mermaid-svg-7u4TDkMgvaOCXDJH span{fill:#333;color:#333;}#mermaid-svg-7u4TDkMgvaOCXDJH .node rect,#mermaid-svg-7u4TDkMgvaOCXDJH .node circle,#mermaid-svg-7u4TDkMgvaOCXDJH .node ellipse,#mermaid-svg-7u4TDkMgvaOCXDJH .node polygon,#mermaid-svg-7u4TDkMgvaOCXDJH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7u4TDkMgvaOCXDJH .rough-node .label text,#mermaid-svg-7u4TDkMgvaOCXDJH .node .label text,#mermaid-svg-7u4TDkMgvaOCXDJH .image-shape .label,#mermaid-svg-7u4TDkMgvaOCXDJH .icon-shape .label{text-anchor:middle;}#mermaid-svg-7u4TDkMgvaOCXDJH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7u4TDkMgvaOCXDJH .rough-node .label,#mermaid-svg-7u4TDkMgvaOCXDJH .node .label,#mermaid-svg-7u4TDkMgvaOCXDJH .image-shape .label,#mermaid-svg-7u4TDkMgvaOCXDJH .icon-shape .label{text-align:center;}#mermaid-svg-7u4TDkMgvaOCXDJH .node.clickable{cursor:pointer;}#mermaid-svg-7u4TDkMgvaOCXDJH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7u4TDkMgvaOCXDJH .arrowheadPath{fill:#333333;}#mermaid-svg-7u4TDkMgvaOCXDJH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7u4TDkMgvaOCXDJH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7u4TDkMgvaOCXDJH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7u4TDkMgvaOCXDJH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7u4TDkMgvaOCXDJH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7u4TDkMgvaOCXDJH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7u4TDkMgvaOCXDJH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7u4TDkMgvaOCXDJH .cluster text{fill:#333;}#mermaid-svg-7u4TDkMgvaOCXDJH .cluster span{color:#333;}#mermaid-svg-7u4TDkMgvaOCXDJH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7u4TDkMgvaOCXDJH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7u4TDkMgvaOCXDJH rect.text{fill:none;stroke-width:0;}#mermaid-svg-7u4TDkMgvaOCXDJH .icon-shape,#mermaid-svg-7u4TDkMgvaOCXDJH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7u4TDkMgvaOCXDJH .icon-shape p,#mermaid-svg-7u4TDkMgvaOCXDJH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7u4TDkMgvaOCXDJH .icon-shape .label rect,#mermaid-svg-7u4TDkMgvaOCXDJH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7u4TDkMgvaOCXDJH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7u4TDkMgvaOCXDJH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7u4TDkMgvaOCXDJH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} K8s 集群
公网
🔒 HTTPS
明文 HTTP
浏览器
Ingress
持有 TLS 证书
web Pod
HTTP
证书管理:生产中用 cert-manager 自动申请/续期 Let's Encrypt 证书:
yaml
# cert-manager 自动管理证书(注解)
metadata:
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
Rewrite:路径重写
后端服务通常不关心路由路径------/api/users 对后端来说就是 /users:
yaml
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- path: /api(/|$)(.*) # 捕获组
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 8080
请求路径 → 到达后端的路径
─────────────────────────────────────────
/api/users → /users
/api/orders/123 → /orders/123
/ → /(不走 rewrite)
Gateway API 等效 :HTTPRoute 通过
filters实现:
yamlfilters: - type: URLRewrite urlRewrite: path: type: ReplacePrefixMatch replacePrefixMatch: /
金丝雀灰度:基于 Header 分流
不切流量到新版本,仅让特定用户(带 header)看到新版本:
yaml
# 主 Ingress(稳定版本------所有流量默认走这里)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-stable
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v1
port:
number: 80
---
# 金丝雀 Ingress(紧带特定 header 的请求走 v2)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "x-version"
nginx.ingress.kubernetes.io/canary-by-header-value: "v2"
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v2
port:
number: 80
#mermaid-svg-b4ty3IL7GqXXgIRo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-b4ty3IL7GqXXgIRo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-b4ty3IL7GqXXgIRo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-b4ty3IL7GqXXgIRo .error-icon{fill:#552222;}#mermaid-svg-b4ty3IL7GqXXgIRo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-b4ty3IL7GqXXgIRo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-b4ty3IL7GqXXgIRo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-b4ty3IL7GqXXgIRo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-b4ty3IL7GqXXgIRo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-b4ty3IL7GqXXgIRo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-b4ty3IL7GqXXgIRo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-b4ty3IL7GqXXgIRo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-b4ty3IL7GqXXgIRo .marker.cross{stroke:#333333;}#mermaid-svg-b4ty3IL7GqXXgIRo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-b4ty3IL7GqXXgIRo p{margin:0;}#mermaid-svg-b4ty3IL7GqXXgIRo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-b4ty3IL7GqXXgIRo .cluster-label text{fill:#333;}#mermaid-svg-b4ty3IL7GqXXgIRo .cluster-label span{color:#333;}#mermaid-svg-b4ty3IL7GqXXgIRo .cluster-label span p{background-color:transparent;}#mermaid-svg-b4ty3IL7GqXXgIRo .label text,#mermaid-svg-b4ty3IL7GqXXgIRo span{fill:#333;color:#333;}#mermaid-svg-b4ty3IL7GqXXgIRo .node rect,#mermaid-svg-b4ty3IL7GqXXgIRo .node circle,#mermaid-svg-b4ty3IL7GqXXgIRo .node ellipse,#mermaid-svg-b4ty3IL7GqXXgIRo .node polygon,#mermaid-svg-b4ty3IL7GqXXgIRo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-b4ty3IL7GqXXgIRo .rough-node .label text,#mermaid-svg-b4ty3IL7GqXXgIRo .node .label text,#mermaid-svg-b4ty3IL7GqXXgIRo .image-shape .label,#mermaid-svg-b4ty3IL7GqXXgIRo .icon-shape .label{text-anchor:middle;}#mermaid-svg-b4ty3IL7GqXXgIRo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-b4ty3IL7GqXXgIRo .rough-node .label,#mermaid-svg-b4ty3IL7GqXXgIRo .node .label,#mermaid-svg-b4ty3IL7GqXXgIRo .image-shape .label,#mermaid-svg-b4ty3IL7GqXXgIRo .icon-shape .label{text-align:center;}#mermaid-svg-b4ty3IL7GqXXgIRo .node.clickable{cursor:pointer;}#mermaid-svg-b4ty3IL7GqXXgIRo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-b4ty3IL7GqXXgIRo .arrowheadPath{fill:#333333;}#mermaid-svg-b4ty3IL7GqXXgIRo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-b4ty3IL7GqXXgIRo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-b4ty3IL7GqXXgIRo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b4ty3IL7GqXXgIRo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-b4ty3IL7GqXXgIRo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b4ty3IL7GqXXgIRo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-b4ty3IL7GqXXgIRo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-b4ty3IL7GqXXgIRo .cluster text{fill:#333;}#mermaid-svg-b4ty3IL7GqXXgIRo .cluster span{color:#333;}#mermaid-svg-b4ty3IL7GqXXgIRo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-b4ty3IL7GqXXgIRo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-b4ty3IL7GqXXgIRo rect.text{fill:none;stroke-width:0;}#mermaid-svg-b4ty3IL7GqXXgIRo .icon-shape,#mermaid-svg-b4ty3IL7GqXXgIRo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b4ty3IL7GqXXgIRo .icon-shape p,#mermaid-svg-b4ty3IL7GqXXgIRo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-b4ty3IL7GqXXgIRo .icon-shape .label rect,#mermaid-svg-b4ty3IL7GqXXgIRo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b4ty3IL7GqXXgIRo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-b4ty3IL7GqXXgIRo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-b4ty3IL7GqXXgIRo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ✅ 是
❌ 否
请求到达
有 header
x-version: v2?
app-v2 (金丝雀)
app-v1 (稳定版)
Gateway API 等效 :直接在 HTTPRoute 中使用
weight:
yamlbackendRefs: - name: app-v1 port: 80 weight: 90 - name: app-v2 port: 80 weight: 10
限流:保护后端服务
yaml
metadata:
annotations:
# 每秒每 IP 最多 2 次请求
nginx.ingress.kubernetes.io/limit-rps: "2"
# burst = rate × multiplier,设为 1 即 burst=2
nginx.ingress.kubernetes.io/limit-burst-multiplier: "1"
超过限制后返回 503 Service Temporarily Unavailable。
Ingress 注解 vs Gateway API 对照表
| 功能 | Ingress 注解 | Gateway API |
|---|---|---|
| TLS | spec.tls + Secret |
Gateway listeners.tls + HTTPRoute |
| Rewrite | rewrite-target annotation |
HTTPRoute filters.URLRewrite |
| 金丝雀(header) | canary-by-header annotation |
HTTPRoute matches.headers |
| 金丝雀(权重) | canary-weight annotation |
HTTPRoute backendRefs.weight |
| 限流 | limit-rps annotation |
需 Controller 特定扩展 |
动手实验
配套实验位于
docs/labs/beginner/ingress-production/
步骤 1:部署实验环境
bash
cd docs/labs/beginner/ingress-production
bash setup.sh
步骤 2:验证 TLS 终止
bash
# 查看 TLS 配置
kubectl describe ingress app-tls
# 用 -k 忽略自签名证书验证
curl -k https://localhost/
步骤 3:验证 Rewrite
bash
# 请求 /app/api/hello → 后端收到 /hello(用 -k 跳过自签名证书验证)
curl -k https://localhost/app/api/hello
# 预期输出:API Response
步骤 4:验证金丝雀灰度
bash
# 不带 header → v1(稳定版)
curl -k https://localhost/
# 预期输出:Hello from v1
# 带 header → v2(金丝雀版本)
curl -k -H "x-version: v2" https://localhost/
# 预期输出:Hello from v2 (Canary!)
步骤 5:验证限流
bash
# 快速发送 20 个请求(限流配置:每秒 2 次,burst=2)
for i in $(seq 1 20); do curl -k -s -o /dev/null -w "%{http_code} " https://localhost/; done
echo ""
# 预期:前几个返回 200,后面的返回 503(限流)
步骤 6:清理
bash
bash teardown.sh
自检问题
-
基础 Ingress 上配置 TLS 后,流量从客户端到后端的加密范围是什么?
-
理解
rewrite-target: /$2中$2是什么意思?如果不加 rewrite 注解,/api/users的请求到了后端路径是什么? -
应用 你需要发布一个新版本,只想让 QA 团队验证。用什么方式做灰度最合适?为什么?
查看答案
-
TLS 的加密范围是客户端到 Ingress Controller(边缘终止 / Edge Termination)。Ingress 持有证书,解密 TLS 后以明文 HTTP 转发给后端 Pod。后端 Pod 不需要处理 TLS。这就是"TLS 终止"------加密在 Ingress 这里终止了。如果需要端到端加密(客户端 → Ingress → Pod 全程 HTTPS),可以配置 SSL Passthrough 或让后端也配置 TLS。
-
$2是正则表达式的第二个捕获组 。在path: /api(/|$)(.*)中,(/|$)是$1(匹配/或结尾),(.*)是$2(匹配路径剩余部分)。例如/api/users:$1=/,$2=users,rewrite 后变成/users。如果不加 rewrite 注解,/api/users到达后端的路径就是/api/users------后端需要在路由中处理/api前缀,不方便。 -
基于 header 的金丝雀灰度 最合适。让 QA 团队在浏览器或测试工具中带上特定 header(如
x-team: qa),只有他们的请求路由到新版本,其他用户完全不受影响。这比基于权重的金丝雀更精确------QA 可以反复测试新版本,而不用担心把普通用户随机分流到可能不稳定版本。
📚 本文来自 K8s Guide ------ 开源免费的 Kubernetes 中文学习指南
- 🗺️ 初学者轨道 + 面试轨道,从零基础到拿 Offer 一站式覆盖
- 🧪 每篇文章配套 Kind 实验脚本,本地一键运行
- 🔗 本文源码:docs/beginner/20-gateway-api.md
- ⭐ 如果对你有帮助,欢迎 Star! github.com/callmebg/k8s-guide