一、回顾四层负载均衡器 Service
Service 自带四层(L4)负载均衡能力,它会维护一个后端 Pod 列表(通过 Endpoints 或 EndpointSlice),并按照轮询等策略将请求分发到多个 Pod,解决了 Pod IP 地址不固定的问题,并且同时通过 NodePort 方式将服务暴露到集群之外,从而实现了如下的数据流:
客户端请求 → Node节点的IP:端口 → Service的IP:端口 → Pod的IP:端口
Service 四层负载均衡存在的问题
| 问题 | 说明 |
|---|---|
| 端口管理混乱 | 一个 Service 需要对应一个宿主机端口,十个 Service 需要十个不同端口,客户端无法管理这么多端口 |
| 无法智能路由 | Service 只能做四层转发(L4),无法识别域名、URL 路径等 HTTP 层信息,无法实现基于内容的智能路由 |
Service vs Ingress 对比
| 对比维度 | Service(主要指 LoadBalancer 类型) | Ingress |
|---|---|---|
| 工作层级 | 第四层(L4),基于 IP 地址、端口号分发,不解析 HTTP 协议内容 | 第七层(L7),工作在应用层,能识别域名、URL 路径、HTTP 头等 |
| 核心能力 | 流量分发与内部服务发现。支持轮询等简单的 L4 负载均衡策略 | 高级路由 。支持基于域名、路径的智能路由,如将 a.com/web 发往 Service A,a.com/api 发往 Service B |
| 入口方式 | 每个 LoadBalancer Service 通常都有一个独立的公网 IP 地址 | 多个 Service 可共享一个公网 IP(Ingress Controller 的 IP),通过不同规则区分 |
| 协议支持 | 支持广泛的 TCP/UDP 协议,适合数据库、消息队列等非 HTTP 应用 | 原生支持 HTTP/HTTPS,对 gRPC 的支持取决于具体的 Controller 实现 |
二、Ingress 七层负载均衡介绍
2.1 Ingress 介绍
Ingress 是 Kubernetes 中专门用来管理集群外部流量如何访问集群内部服务的 API 资源(规则文件)。它工作在第七层(应用层,HTTP/HTTPS),能够提供比 Service 更智能、更灵活的路由能力。
Ingress 规则是"全集群生效的逻辑规则",它不依赖于具体的物理节点。无论流量从哪个节点进入,只要集群中的 Ingress Controller 正常运行,规则就会执行。你只需维护一份 Ingress 文件,就能为整个集群提供七层路由能力
2.2 Ingress Controller 介绍
Ingress Controller 是一个七层负载均衡调度器 。客户端的请求先到达这个七层负载均衡调度器,由七层负载均衡器再反向代理到后端 Pod。
简单说,Ingress是规则文件,Ingress Controller 是规则的执行者
注意:Ingress Controller 在执行Ingress规则时,我们是观测不到的,它是在后台起作用的
Ingress Controller 是什么
简单说,Ingress Controller 是一个运行在 Kubernetes 集群中的 Pod ,这个 Pod 里运行着一个成熟的七层负载均衡软件(比如 Nginx 或 Traefik)。你可以把它想象成一台"智能路由网关"------所有外部 HTTP/HTTPS 流量进入集群后,第一个接住它们的就是这个 Pod
它是怎么实现"实时更新"的
关键在于它内部有一个 "控制循环(Control Loop)"。这个循环会持续 watch 集群 API Server:
监听:它会时刻盯着 Ingress、Service 和 Endpoint 三类资源的变化。
翻译:一旦发现新规则(比如你创建了一个 Ingress 规则),它立刻把 K8s 的抽象语法,翻译成 Nginx 能理解的具体的 nginx.conf 配置文件。
加载:翻译完成后,它会自动执行 nginx -s reload,让新规则立即生效,整个过程无需手动干预
为什么后端需要加 Service
回忆一下service的特性:service的IP(ClusterIP)是固定的,它的域名(..svc.cluster.local)也是固定的,解析service的域名可以得到service的IP。 service可以通过标签选择器可以访问到带有特定标签的pod,因此访问service就可以固定的访问到特定的pod
因为后端 Pod 的 IP 是动态变化的(比如 Pod 重启或漂移)。如果在 Nginx 的 upstream 里直接写死 Pod IP,Pod 一重启规则就失效了。而 Service(ClusterIP)是固定的,相当于在 Nginx 和 Pod 之间加了一层"静态中间层"。Ingress Controller 只需要把流量转发给 Service 的 ClusterIP,然后由 kube-proxy 完成到实际 Pod 的最终分发
text
┌─────────────────────────────────────────────────────────────────────────────┐
│ Ingress Controller 工作机制 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 监听阶段: │
│ Ingress Controller (Pod) → Watch API Server → 发现新的 Ingress 规则 │
│ ↓ │
│ 2. 翻译阶段: │
│ 将 K8s 规则翻译为 → 生成 nginx.conf 配置文件 │
│ ↓ │
│ 3. 生效阶段: │
│ 自动执行 nginx -s reload → 新规则加载完成 │
│ ↓ │
│ 4. 流量转发阶段: │
│ 客户端请求 → Ingress Controller (Nginx) → Service → Pod │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
一句话总结:Ingress 是规则,Ingress Controller 是具体的负载均衡器。
2.3 Ingress 规则文件字段解析
好的,我来帮你解析 kubectl explain Ingress 输出的这些字段:
1、顶层字段(Ingress 资源本身)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
apiVersion |
string | ✅ | API 版本,固定为 networking.k8s.io/v1 |
kind |
string | ✅ | 资源类型,固定为 Ingress |
metadata |
Object | ✅ | 元数据,包含 name、namespace、labels、annotations 等 |
spec |
Object | ✅ | 核心部分:定义 Ingress 的路由规则(域名、路径、后端服务等) |
status |
Object | ❌ | 只读字段:由 Ingress Controller 自动填充,记录当前 Ingress 的负载均衡器地址、路由状态等信息 |
注意 :
status字段由系统自动维护,用户无需(也无法)手动配置。
2、spec 字段(Ingress 核心配置)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
defaultBackend |
Object | ❌ | 默认后端 :当请求不匹配任何 rules 中的规则时,转发到该后端。如果未设置,不匹配的请求将由 Ingress Controller 自行处理(通常返回 404)。 |
ingressClassName |
string | ❌ | 指定 Ingress Controller :值为集群中已存在的 IngressClass 名称(如 nginx),告诉集群由哪个 Ingress Controller 来执行该规则。 ⚠️ 这是 新版本推荐写法 ,替代旧的 kubernetes.io/ingress.class 注解。 |
rules |
\[\]Object | ❌ | 路由规则列表 :定义具体的域名、路径与后端服务的映射关系。如果没有配置 rules,则必须配置 defaultBackend。 |
tls |
\[\]Object | ❌ | HTTPS 配置:定义 TLS 证书和对应的域名。配置后,Ingress Controller 会为该域名提供 HTTPS 服务(通常使用 443 端口)。 |
3、rules 子字段(路由规则)
rules 是一个数组,每个元素包含以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
host |
string | 域名匹配 :指定需要匹配的域名(如 nginx.jx.com)。如果留空,则匹配所有域名的请求。 |
http |
Object | HTTP 路由配置:定义该域名下的路径匹配规则和对应的后端服务。 |
4、http 子字段(路径匹配)
| 字段 | 类型 | 说明 |
|---|---|---|
paths |
\[\]Object | 路径规则列表:定义该域名下具体的路径匹配规则和对应的后端服务。请求按顺序匹配,命中第一个即转发。 |
paths 中的每个元素包含:
| 字段 | 类型 | 说明 |
|---|---|---|
path |
string | 匹配的路径 :如 /、/api、/product 等。配合 pathType 决定匹配方式。 |
pathType |
string | 路径匹配类型 : • Prefix:前缀匹配(/api 匹配 /api、/api/v1、/api/v1/user) • Exact:精确匹配(必须完全一致) • ImplementationSpecific:由 Ingress Controller 自行解释(如 Nginx 支持正则表达式) |
backend |
Object | 后端服务:指定将请求转发到哪个 Service 的哪个端口。 |
backend 包含:
| 字段 | 说明 |
|---|---|
service.name |
后端 Service 的名称(必须与 Ingress 在同一命名空间) |
service.port.number 或 service.port.name |
后端 Service 的端口号或端口名称 |
5、tls 子字段(HTTPS 配置)
tls 是一个数组,每个元素包含:
| 字段 | 说明 |
|---|---|
hosts |
\[\]string:需要启用 HTTPS 的域名列表 |
secretName |
存储 TLS 证书和私钥的 Secret 名称(类型必须为 kubernetes.io/tls) |
6、defaultBackend 字段(默认后端)
当请求不匹配任何 rules 时,转发到该后端。结构同 backend:
| 字段 | 说明 |
|---|---|
service.name |
默认后端的 Service 名称 |
service.port.number 或 service.port.name |
默认后端的 Service 端口 |
范例
假设你有一个域名 myapp.com,背后有三个微服务:
前端服务(frontend-svc):处理主页面,路径为 /
API 服务(api-svc):处理业务接口,路径为 /api
后台管理(admin-svc):处理管理后台,路径为 /admin
通过 Ingress 实现同一个域名、不同路径,路由到不同 Service。
bash
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress # Ingress 名称
spec:
ingressClassName: nginx # 使用 Nginx Ingress Controller(必须与集群中安装的 Controller 一致)
rules:
- host: myapp.com # 域名:只有访问 myapp.com 的请求才匹配
http:
paths:
# ---------- 规则1:根路径 → 前端服务 ----------
- path: / # 匹配所有以 "/" 开头的请求
pathType: Prefix # 前缀匹配
backend:
service:
name: frontend-svc # 后端 Service 名称
port:
number: 80 # 后端 Service 端口
# ---------- 规则2:/api/* → API 服务 ----------
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
# ---------- 规则3:/admin/* → 后台管理服务 ----------
- path: /admin
pathType: Prefix
backend:
service:
name: admin-svc
port:
number: 80
快速查询技巧
如果你想查看某个字段的更详细定义,可以继续使用 kubectl explain:
bash
# 查看 rules 的详细结构
kubectl explain Ingress.spec.rules
# 查看 http 的详细结构
kubectl explain Ingress.spec.rules.http
# 查看 backend 的详细结构
kubectl explain Ingress.spec.rules.http.paths.backend
三、安装 Ingress Controller
3.1 下载和安装
bash
# 创建目录
[root@hd1 ~]# mkdir ingress-controller
[root@hd1 ~]# cd ingress-controller/
# 获取 ingress-nginx,本次案例使用的是 1.8.1 版本
# 下载前建议先查看兼容性:https://github.com/kubernetes/ingress-nginx/releases
[root@hd1 ingress-controller]# wget https://github.com/kubernetes/ingress-nginx/archive/refs/tags/controller-v1.8.1.tar.gz
注意,下载的 Ingress Controller 版本要和我们的k8s集群版本相对应

bash
# 解压缩软件包
[root@hd1 ~]# tar xf controller-v1.8.1.tar.gz
[root@hd1 ~]# cd ingress-nginx-controller-v1.8.1/deploy/static/provider/cloud/
[root@hd1 cloud]# ls
deploy.yaml kustomization.yaml
3.2 修改镜像源(国内加速)
由于默认镜像源可能无法访问,需要修改为国内镜像源:
bash
[root@hd1 cloud]# cat deploy.yaml | grep -n image
441: image: registry.cn-shenzhen.aliyuncs.com/xiaohh-docker/ingress-nginx-controller:v1.8.1
442: imagePullPolicy: IfNotPresent
538: image: registry.cn-shenzhen.aliyuncs.com/xiaohh-docker/ingress-nginx-kube-webhook-certgen:v20230407
539: imagePullPolicy: IfNotPresent
587: image: registry.cn-shenzhen.aliyuncs.com/xiaohh-docker/ingress-nginx-kube-webhook-certgen:v20230407
588: imagePullPolicy: IfNotPresent
如果在应用文件时无法正常使用,可参考下文踩坑记录
bash
# 部署 Ingress Controller
[root@k8s-master01 cloud]# kubectl apply -f deploy.yaml
# 查看 Pod 状态
[root@hd1 cloud]# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-tm8rz 0/1 Completed 0 4m22s
ingress-nginx-admission-patch-pmqzx 0/1 Completed 1 4m22s
ingress-nginx-controller-ccb6c97f4-zn97f 1/1 Running 0 4m22s
# 查看 Service
[root@k8s-master01 cloud]# kubectl -n ingress-nginx get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.10.103.132 <pending> 80:31502/TCP,443:31020/TCP 96s
ingress-nginx-controller-admission ClusterIP 10.10.227.21 <none> 443/TCP 96s
注意 :如果 EXTERNAL-IP 显示 <pending>,说明当前环境不支持 LoadBalancer 类型。对于非公有云环境(如本地虚拟机、私有云),需要修改 Service 类型为 NodePort。
bash
# 修改 ingress-nginx-controller 服务的类型为 NodePort
[root@h1 cloud]# kubectl edit svc ingress-nginx-controller -n ingress-nginx
# 将 type: LoadBalancer 改为 type: NodePort
修改后的 Service 配置示例:
yaml
spec:
type: NodePort # 原来是 LoadBalancer,改为 NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 31875 # 随机分配,也可手动指定
- name: https
port: 443
targetPort: 443
nodePort: 32463
再次查看,确认已经变成 NodePort 即可
bash
[root@hd1 cloud]# kubectl -n ingress-nginx get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.96.132.197 <none> 80:31886/TCP,443:31662/TCP 6m2s
ingress-nginx-controller-admission ClusterIP 10.96.236.12 <none> 443/TCP 6m1s
踩坑记录
创建 Ingress Controller 的时候,出现了问题
bash
[root@hd1 cloud]# kubectl apply -f deploy.yaml
[root@hd1 cloud]# kubectl -n ingress-nginx get pod
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-fv6jr 0/1 ImagePullBackOff 0 85s
ingress-nginx-admission-patch-frrvs 0/1 ImagePullBackOff 0 85s
ingress-nginx-controller-ccb6c97f4-pjd8z 0/1 ContainerCreating 0 85s
这里第三个pod一直是ContainerCreating状态。查看详细信息:
bash
[root@hd1 cloud]# kubectl describe pod ingress-nginx-controller-ccb6c97f4-pjd8z -n ingress-nginx
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 115s default-scheduler Successfully assigned ingress-nginx/ingress-nginx-controller-ccb6c97f4-pjd8z to hd2
Warning FailedMount 51s (x8 over 115s) kubelet MountVolume.SetUp failed for volume "webhook-cert" : secret "ingress-nginx-admission" not found
Error from server (NotFound): pods "get" not found
Pod 无法启动不是因为镜像拉取失败(虽然那两个 Admission Pod 确实失败了),而是因为 ingress-nginx-controller 缺少了一个名为 ingress-nginx-admission 的 Secret,这个 Secret 本应由 admission-create 这个 Job 在启动时生成,但由于它自己也没启动成功(可能卡在 ContainerCreating),导致依赖链断裂
重启之后:
bash
# 1. 删除当前的 Ingress Controller 部署
[root@hd1 cloud]# kubectl delete -f deploy.yaml
# 2. 等待几秒后,重新部署
[root@hd1 cloud]# kubectl apply -f deploy.yaml
依旧不行。
反复试错不下1十遍,终于找到了解决办法!按步骤照做!
步骤 1:清除掉之前的数据
bash
[root@hd1 cloud]# kubectl apply -f deploy.yaml
步骤 2:生成一对匹配的 TLS 证书
bash
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-keyout ingress-nginx-admission.key \
-out ingress-nginx-admission.crt \
-subj "/CN=ingress-nginx-admission.default.svc"
步骤 3:手动创建命名空间(如果已存在则跳过)
bash
kubectl create namespace ingress-nginx --dry-run=client -o yaml | kubectl apply -f -
步骤 4:用这对文件创建 Secret
bash
kubectl create secret tls ingress-nginx-admission -n ingress-nginx \
--key ingress-nginx-admission.key \
--cert ingress-nginx-admission.crt
步骤 5:应用
bash
[root@hd1 cloud]# kubectl apply -f deploy.yaml
#查看(需要等待一小会儿)
[root@hd1 cloud]# kubectl -n ingress-nginx get pod
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-2lq6c 0/1 CrashLoopBackOff 4 (68s ago) 3m10s
ingress-nginx-admission-patch-rv6bk 0/1 CrashLoopBackOff 4 (87s ago) 3m10s
ingress-nginx-controller-ccb6c97f4-qx2zv 1/1 Running 0 3m10s
问题解决
3.3 查看各种资源
bash
# 查看集群已经存在的 IngressClass
[root@hd1 cloud]# kubectl get ingressclass
NAME CONTROLLER PARAMETERS AGE
nginx k8s.io/ingress-nginx <none> 6m19s
# 查看 Ingress Controller Service
[root@hd1 cloud]# kubectl -n ingress-nginx get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.96.132.197 <none> 80:31886/TCP,443:31662/TCP 6m32s
ingress-nginx-controller-admission ClusterIP 10.96.236.12 <none> 443/TCP 6m31s
# 查看 Ingress Controller Pod
[root@hd1 cloud]# kubectl -n ingress-nginx get pod
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-ccb6c97f4-qx2zv 1/1 Running 0 18m
四、准备业务环境
4.1 准备一个普通的 Service 和三个对应的 Pod
准备三个 Pod ,作为测试用的后端应用
这个ClusterIP 类型的 Service,作为 3 个 Nginx Pod 的统一访问入口
yaml
# nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deploy
name: nginx-deploy
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deploy
template:
metadata:
labels:
app: nginx-deploy
spec:
containers:
- image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx-deploy
name: nginx-svc
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-deploy
type: ClusterIP #类型:服务暴露在集群内部
bash
# 创建资源
[root@hd1 cloud]# kubectl apply -f nginx.yaml
deployment.apps/nginx-deploy created
service/nginx-svc created
# 查看 Pod
[root@hd1 cloud]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deploy-7576f7f4fd-2gmdp 1/1 Running 0 10s
nginx-deploy-7576f7f4fd-7pnrv 1/1 Running 0 10s
nginx-deploy-7576f7f4fd-lqxc5 1/1 Running 0 10s
# 查看 Service
[root@hd1 cloud]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14d
nginx-svc ClusterIP 10.96.232.179 <none> 80/TCP 34s
4.2 配置 Ingress 规则
yaml
# ingress-http.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress # 资源类型:Ingress。表示这是一个七层路由规则的定义
metadata:
name: nginx-ingress #Ingress 资源的名称。此名称在命名空间内必须唯一
spec:
ingressClassName: nginx #指定使用哪个 Ingress Controller 来执行该规则
rules: # rules ,规则,是一个数组,可以定义多条路由规则
- host: nginx.jx.com # 指定匹配的域名。只有访问该域名的请求才会应用下面的路径规则。若省略此字段,则匹配所有域名的请求
http: # 匹配 HTTP 协议的请求
paths:
- backend: # 指定请求转发到的后端服务(Service)
service:
name: nginx-svc # 后端 Service 的名称。该 Service 必须与 Ingress 在同一个命名空间
port:
number: 80 # 后端 Service 的端口号。Ingress Controller 会将请求转发到该端口
path: / # 匹配的路径,这里表示匹配所有以 "/" 开头的请求
pathType: Prefix
定义了一条七层路由规则,将访问 nginx.jx.com 的 HTTP 请求,转发到后端的 nginx-svc Service 的 80 端口,最终由 3 个 Nginx Pod 提供服务
bash
# 创建 Ingress
[root@k8s-master01 ~]# kubectl create -f ingress-http.yaml
ingress.extensions/ingress-http created
# 查看 Ingress
[root@hd1 ~]# kubectl get ingress nginx-ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx-ingress nginx nginx.jx.com 10.96.132.197 80 4h28m
# 查看 Ingress 详情
[root@hd1 ~]# kubectl get ingress nginx-ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx-ingress nginx nginx.jx.com 10.96.132.197 80 4h28m
[root@hd1 ~]# kubectl describe ingress nginx-ingress
Name: nginx-ingress
Labels: <none>
Namespace: default
Address: 10.96.132.197
Ingress Class: nginx
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
nginx.jx.com
/ nginx-svc:80 (10.244.169.85:80,10.244.59.170:80,10.244.59.171:80)
Annotations: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 4m49s (x3 over 10m) nginx-ingress-controller Scheduled for sync
踩坑记录

这个报错是一个典型的 Admission Webhook 证书错误。意思是:当你创建 Ingress 时,Kubernetes API Server 尝试调用 Nginx Ingress Controller 的 validate.nginx.ingress.kubernetes.io Webhook 进行规则校验,但 TLS 握手失败了,具体原因是 tls: internal error
结合我们之前排错的记录,推测问题的根源在于:Webhook 使用的证书(ingress-nginx-admission Secret)与当前 Ingress Controller 的配置不匹配、已损坏或未正确加载。换句话说,这次出错原因就是我们上次排错留下的技术负债
实验好几种方法,都没有作用,只能采用临时避险的方式跳过这一步验证了:
直接禁用 Webhook(临时避险)
bash
[root@hd1 ~]# kubectl delete validatingwebhookconfiguration ingress-nginx-admission
validatingwebhookconfiguration.admissionregistration.k8s.io "ingress-nginx-admission" deleted
这里我们直接删除 Webhook ,API Server 不再向 Ingress Controller 发起任何校验请求,也就跳过了这一步的安全审查。
再次创建:
bash
[root@hd1 ~]# kubectl create -f ingress-http.yaml
ingress.networking.k8s.io/nginx-ingress created
这次成功了
4.3 测试
Linux 主机测试
bash
# 修改 /etc/hosts 文件,将域名解析到集群节点 IP
[root@hd1 cloud]# vim /etc/hosts
192.168.1.12 nginx.jx.com
#查看Ingress Controller 的 NodePort
[root@hd1 provider]# kubectl get svc -n ingress-nginx ingress-nginx-controller
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.96.132.197 <none> 80:31886/TCP,443:31662/TCP 118m
# 用域名测试(端口为 Ingress Controller 的 NodePort)
[root@hd1 provider]# curl nginx.jx.com:31886
<h1>Welcome to nginx!</h1>
Windows 主机测试
修改本地 hosts 文件,增加如下一行(IP 为 K8s 任意节点 IP):
192.168.1.12 nginx.jx.com
浏览器访问 nginx.jx.com,应能看到 Nginx 欢迎页。
⚠️ 常见问题:测试失败的原因
默认 K8s 中的 Service 有一个属性叫做 externalTrafficPolicy(外部流量策略):
Cluster(默认策略):将流量路由到所有端点(节点)Local:仅将流量路由到具有相关 Pod 的节点上(若节点上没有相关的 Pod 则丢弃流量),但 保留流量的源 IP
解决方法:如果是本地测试环境,应使用 baremetal (裸金属/本地环境)目录的配置文件
或者修改 ingress-nginx-controller Service 的 externalTrafficPolicy 为 Cluster:
bash
[root@hd1 ~]# kubectl edit svc ingress-nginx-controller -n ingress-nginx
修改内容:
yaml
spec:
clusterIP: 10.96.78.1
clusterIPs:
- 10.96.78.1
externalTrafficPolicy: Cluster # 将 Local 改成 Cluster
internalTrafficPolicy: Cluster
修改后重新测试即可。
而本章节我们所安装的Ingress Controller路径为cloud ,它的默认策略就是Local;但是我们本应该安装到baremetal目录,它没有externalTrafficPolicy这一字段,所以默认就是Cluster
五、三个微服务的七层代理
5.1 案例一:基于请求路径转发不同服务
场景描述:
假设有一个网站,网站包含多个微服务,每个微服务对应不同的 URI 路径:
- 请求路径 / → 转发到网站前端服务 (protal)
- 请求路径 /product → 转发到产品服务 (product)
- 请求路径 /developer → 转发到开发服务 (developer)
步骤 1:删除原有部分资源
bash
#删除Ingress
[root@hd1 cloud]# kubectl delete ingress nginx-ingress
ingress.networking.k8s.io "nginx-ingress" deleted
#删除deployment
[root@hd1 cloud]# kubectl delete deployment nginx-deploy
deployment.apps "nginx-deploy" deleted
步骤 2:创建三个服务
服务 1:protal(前端服务)
yaml
# service1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: app1
name: app1
spec:
replicas: 1
selector:
matchLabels:
app: app1
template:
metadata:
labels:
app: app1
spec:
containers:
- image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
labels:
app: app1
name: protal
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: app1
type: ClusterIP
bash
[root@h1 ingress]# kubectl apply -f service1.yaml
服务 2:product(产品服务)
yaml
# service2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: app2
name: app2
spec:
replicas: 1
selector:
matchLabels:
app: app2
template:
metadata:
labels:
app: app2
spec:
containers:
- image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
labels:
app: app2
name: product
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: app2
type: ClusterIP
bash
[root@h1 ingress]# kubectl apply -f service2.yaml
服务 3:developer(开发服务)
yaml
# service3.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: app3
name: app3
spec:
replicas: 1
selector:
matchLabels:
app: app3
template:
metadata:
labels:
app: app3
spec:
containers:
- image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
labels:
app: app3
name: developer
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: app3
type: ClusterIP
bash
[root@h1 ingress]# kubectl apply -f service3.yaml
至此,我们创建了三个app服务,每一个app服务对应一个service
步骤 3:验证服务
bash
# 查看三个 Pod
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
app1-7bbc67d96d-svg94 1/1 Running 0 4m21s
app2-6555f4f95f-89www 1/1 Running 0 4m13s
app3-859cb4c9d6-tkm27 1/1 Running 0 7s
# 修改每个 Pod 的网页内容以区分不同服务
[root@h1 ingress]# echo "app1 ----" > index.html
[root@h1 ingress]# kubectl cp index.html app1-7bbc67d96d-svg94:/usr/share/nginx/html/
[root@h1 ingress]# echo "app2 ----" > index.html
[root@h1 ingress]# kubectl cp index.html app2-6555f4f95f-89www:/usr/share/nginx/html/
[root@h1 ingress]# echo "app3 ----" > index.html
[root@h1 ingress]# kubectl cp index.html app3-859cb4c9d6-tkm27:/usr/share/nginx/html/
# 查看三个 Service
[root@hd1 ~]# kubectl get svc -l app
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
developer ClusterIP 10.96.22.103 <none> 80/TCP 103s
nginx-svc ClusterIP 10.96.232.179 <none> 80/TCP 19h
product ClusterIP 10.96.15.185 <none> 80/TCP 5m49s
protal ClusterIP 10.96.254.188 <none> 80/TCP 5m57s
# 分别访问三个 Service
[root@h1 ingress]# curl 10.96.254.188
app1 ----
[root@h1 ingress]# curl 10.96.15.185
app2 ----
[root@h1 ingress]# curl 10.96.22.103
app3 ----
步骤 4:配置 Ingress 规则(基于路径转发)
这个规则会根据用户访问的不同url来指向不同的服务
当用户访问的路径带有product,如:www.jx.com/product/... 则指向product主机,当用户访问
www.jx.com/developer/... 则指向developer主机
yaml
# test1.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress1
namespace: default
annotations: #注解
nginx.ingress.kubernetes.io/rewrite-target: /$2 #地址重写
nginx.ingress.kubernetes.io/use-regex: "true" #使用正则表达式
spec:
ingressClassName: nginx
rules:
- host: www.jx.com #解析的域名
http:
paths: #路径匹配规则
- path: /product(/|$)(.*) #正则匹配。&是截至符,匹配:域名/product/ 或者 域名/product/xxx.xxx
pathType: ImplementationSpecific
backend:
service:
name: product
port:
number: 80
- path: /developer(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: developer
port:
number: 80
- path: /
pathType: Prefix #前缀匹配。匹配所有以 / 开头的路径
backend:
service:
name: protal
port:
number: 80
因为使用了正则表达式,所以需要用 ImplementationSpecific 类型而不能使用 Prefix。rewrite-target: /$2 表示将匹配的路径重写为第二个捕获组的内容,确保请求路径正确转发。
配置中/product 这个字段的唯一作用就是作为路由匹配的条件(或者说是一个路由标签),地址重写才导向真正的地址文件。
例如:
用户请求的 URL 是 http://www.jx.com/product/api/v1/orders
客户端请求路径为: /product/api/v1/orders
正则匹配拆分: /product + 1=/ + 2=api/v1/orders
重写后: /api/v1/orders,剥离了 /product 前缀,只保留实际业务路径
bash
[root@h1 ingress]# kubectl apply -f test1.yaml
ingress.networking.k8s.io/nginx-ingress created
# 修改 hosts 文件
[root@h1 ingress]# cat /etc/hosts | grep www
192.168.1.12 www.jx.com
#查看我们的nodeport:
[root@hd1 ~]# kubectl get svc -n ingress-nginx ingress-nginx-controller
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.96.132.197 <none> 80:31886/TCP,443:31662/TCP 3h14m
# 测试
[root@hd1 ~]# curl www.jx.com:31886
app1 ----
[root@hd1 ~]# curl www.jx.com:31886/product
app2 ----
5.2 案例一(扩展):基于域名转发不同服务
这个案例很简单,就是根据访问的不同域名指向不同的服务
更好的方案是:一个服务对应一个域名,如 dev.jx.com、www.jx.com、pro.jx.com。
yaml
# test1-domain.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress1
namespace: default
spec:
ingressClassName: nginx # 指定使用 Nginx Ingress Controller
rules:
- host: www.jx.com # ① 匹配域名 www.jx.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: protal
port:
number: 80
- host: pro.jx.com # ② 匹配域名 pro.jx.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: product
port:
number: 80
- host: dev.jx.com # ③ 匹配域名 dev.jx.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: developer
port:
number: 80
5.3 案例二:Ingress 配置 HTTPS
HTTPS 已经成为网站的标准配置。将网站配置为 HTTPS,需要准备域名证书文件,可以使用 openssl 工具生成自签名证书,然后将证书内容保存在 Secret 对象中,供 Ingress 引用。
生成证书和私钥
bash
# 方式一:使用 openssl 生成自签名证书(一步到位)
[root@h1 ingress]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout private.key -out selfsigned.crt
# 期间会提示输入证书信息,CN(Common Name)需要填写域名,如 blog.jx.com
# 查看生成的文件
[root@h1 ingress]# ls
private.key selfsigned.crt
# 方式二:分步生成(更详细的流程,可选)
# a. 生成私钥
[root@h1 ingress]# openssl genpkey -algorithm RSA -out private.key
# b. 创建证书签名请求
[root@h1 ingress]# openssl req -new -key private.key -out csr.csr
# c. 自签名证书
[root@h1 ingress]# openssl x509 -req -days 365 -in csr.csr -signkey private.key -out selfsigned.crt
创建 TLS Secret
bash
# 创建 TLS 类型的 Secret
[root@h1 ingress]# kubectl create secret tls jx --key=private.key --cert=selfsigned.crt
secret/jx created
# 查看 Secret
[root@h1 ingress]# kubectl get secret jx
NAME TYPE DATA AGE
jx kubernetes.io/tls 2 10s
配置 Ingress 使用 HTTPS
yaml
# ingress-blog.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpsingress
namespace: default
spec:
ingressClassName: nginx
tls:
- hosts:
- blog.jx.com
secretName: jx
rules:
- host: blog.jx.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: product
port:
number: 80
tls.hosts用于指定应用这个 TLS 配置的域名列表,secretName指向包含证书和私钥的 Secret 名称。
bash
# 更新资源
[root@h1 ingress]# kubectl apply -f ingress-blog.yaml
ingress.networking.k8s.io/httpsingress created
# 查看 Ingress(多了一个 443 端口)
[root@h1 ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
httpsingress nginx blog.jx.com 10.96.212.255 80, 443 69s
nginx-ingress1 nginx www.jx.com 10.96.212.255 80 68m
# 查看 HTTPS 对应的 NodePort
[root@h1 ingress]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.96.212.255 <none> 80:31875/TCP,443:32463/TCP 6h27m
测试 HTTPS
bash
# Linux 测试(-k 表示忽略证书验证)
[root@h1 ingress]# curl -k -H "Host: blog.jx.com" https://192.168.1.12:32463
app2 ----
Windows 客户端测试 :修改 hosts 文件 192.168.1.12 blog.jx.com,浏览器访问 https://blog.jx.com:32463。
⚠️ 由于使用的是自签名证书,浏览器会提示"您的连接不是私密连接",点击"高级"→"继续访问"即可。生产环境应使用受信任的 CA 签发的证书。
5.4 案例三:Ingress 自定义配置(重定向)
Ingress Nginx 控制器使用注解(annotations)来实现配置的更精细控制,比如超时、重定向等。
场景 :将网站 nginx.jx.com 重定向到 www.jx.com。
yaml
# ingress-rewrite.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress1
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: http://www.jx.com
spec:
ingressClassName: nginx
rules:
- host: nginx.jx.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: protal
port:
number: 80
bash
# 测试
[root@h1 ingress]# curl nginx.jx.com:31886
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
5.5 会话保持(Session Affinity)
用于确保客户端请求在一定时间范围内都被转发到同一台后端服务器。在无会话保持的情况下,负载均衡器可能将客户端的请求分发到不同的后端服务器,导致会话数据丢失。
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpsingress
namespace: default
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
spec:
ingressClassName: nginx
# ... 其他规则
5.6 灰度发布(金丝雀发布)
使用 Nginx Ingress 实现灰度发布的适用场景主要取决于业务流量切分的策略。目前 Nginx Ingress 支持基于 Header 、Cookie 和 服务权重 三种流量切分策略。
灰度发布注解说明
| 注解 | 说明 |
|---|---|
nginx.ingress.kubernetes.io/canary |
开启 Canary,值为 "true" |
nginx.ingress.kubernetes.io/canary-weight |
基于权重的流量切分,权重值为 0-100 的正整数,按百分比将流量进行路由 |
nginx.ingress.kubernetes.io/canary-by-header |
基于 HTTP 请求头的流量切分,可选值为 always 和 never |
nginx.ingress.kubernetes.io/canary-by-header-value |
与 canary-by-header 配合使用,当请求头值匹配时路由到 Canary 版本 |
nginx.ingress.kubernetes.io/canary-by-header-pattern |
通过正则表达式匹配 HTTP 请求头 |
nginx.ingress.kubernetes.io/canary-by-header-cookie |
基于 Cookie 的流量切分,可选值为 always 和 never |
5.6.1 基于权重的流量切分
场景:网站希望升级新版本能做到平滑升级,使用户不会感知到这一点。通过 Ingress 基于流量的切分功能,逐步将流量路由到新版本,直至完成全部流量切换。
text
┌─────────────────────────────────────────────────────────────────────────────┐
│ 基于权重的金丝雀发布流程 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ 20%流量 ┌─────────────────────┐ │
│ │ 客户端 │ ──────────────→│ Canary (v2 版本) │ │
│ │ 请求 │ │ Service: product │ │
│ └──────────┘ └─────────────────────┘ │
│ │ │
│ │ 80%流量 │
│ ↓ │
│ ┌─────────────────────────────────────────┐ │
│ │ Stable (v1 版本) │ │
│ │ Service: protal │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
步骤 1:部署 v1 版本(稳定版)
这里这个svc是我们在5.1步骤2创建的
bash
# 验证当前 v1 版本(protal 服务)
[root@hd1 ~]# kubectl get svc | grep protal
protal ClusterIP 10.96.254.188 <none> 80/TCP 37m
[root@hd1 ~]# curl 10.96.254.188
app1 -----
步骤 2:创建基础 Ingress 规则
yaml
# blog-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog
spec:
ingressClassName: nginx
rules:
- host: blog.com.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: protal
port:
number: 80
- 定义稳定版路由:所有访问 blog.com.cn 的请求,全部转发到 protal 服务(v1 版本)
bash
[root@h1 ingress]# kubectl apply -f blog-ingress.yaml
# 修改 hosts 文件
[root@hd1 ~]# cat /etc/hosts | grep blog.com.c
192.168.1.12 blog.com.cn
# 测试当前版本
[root@hd1 ~]# curl -s -H "Host: blog.com.cn" http://192.168.1.11:31886
app1 ----
步骤 3:部署 v2 版本(Canary 版本)
这里的v2版本用之前创建的app2代替
bash
# 验证 v2 版本(product 服务)
[root@hd1 ~]# kubectl get pod | grep app2
app2-6555f4f95f-89www 1/1 Running 0 49m
[root@hd1 ~]# kubectl get svc | grep product
product ClusterIP 10.96.15.185 <none> 80/TCP 50m
[root@hd1 ~]# curl 10.96.15.185
app2 ----
步骤 4:创建 Canary Ingress(20% 流量到 v2)
定义金丝雀路由:将 20% 的流量切到 product 服务(v2 版本),其余 80% 仍由基础规则(blog-ingress)转发到 protal 服务
yaml
# blog-ingress-canary.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog-canary
annotations:
# 开启 Canary
nginx.ingress.kubernetes.io/canary: "true"
# 设置权重:20% 流量到 v2 版本
nginx.ingress.kubernetes.io/canary-weight: "20"
spec:
ingressClassName: nginx
rules:
- host: blog.com.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: product
port:
number: 80
金丝雀 Ingress 可以和基础 Ingress 同时存在,而且正是为了"切割流量"而设计的
bash
[root@h1 ingress]# kubectl apply -f blog-ingress-canary.yaml
此时,20% 的流量被路由到 v2 版本,80% 的流量路由到 v1 版本。
bash
[root@hd1 ~]# curl -s -H "Host: blog.com.cn" http://192.168.1.11:31886
app1 ----
步骤 5:全部流量切换到 v2 版本
这是稳定版本的 Ingress 规则的更新版。更新完成后,将原有的 Ingress 对象指向 v2 版本:
yaml
# blog-ingress.yaml(更新)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog # 保持同名(覆盖更新)
spec:
ingressClassName: nginx
rules:
- host: blog.com.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: product # 从 protal 改为 product
port:
number: 80
全量切换:将基础规则从 protal 改为 product,此时 100% 流量走 v2 版本
bash
#应用
[root@h1 ingress]# kubectl apply -f blog-ingress.yaml
#删掉之前的流量分流的Ingress
[root@h1 ingress]# kubectl delete -f blog-ingress-canary.yaml
#访问验证,流量已经全部被指向app2了
[root@hd1 ~]# curl -s -H "Host: blog.com.cn" http://192.168.1.11:31886
app2 ----
至此,灰度发布升级完成。
5.6.2 基于客户端请求的流量切分
场景 :当 HTTP 请求头包含 canary=always 时,流量被路由到新版本。这种策略比较适合将特定的客户端路由到新版本中,以便进行具体的测试和验证。
举个简单的例子:VIP用户与普通用户看的东西是不一样的
text
┌─────────────────────────────────────────────────────────────────────────────┐
│ 基于请求头的金丝雀发布流程 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ │
│ │ 普通用户 │ ────────→ v2 版本 (稳定版) │
│ └──────────┘ │
│ │
│ ┌──────────┐ Header: canary=always │
│ │ 测试用户 │ ────────────────→ v3 版本 (Canary 版) │
│ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
步骤 1:部署 v3 版本
这个developer就是我们之前部署的app3
bash
# 验证 v3 版本(developer 服务)
[root@hd1 ~]# kubectl get svc | grep developer
developer ClusterIP 10.96.22.103 <none> 80/TCP 72m
[root@hd1 ~]# curl 10.96.22.103
app3 ----
步骤 2:创建 Canary Ingress(基于 Header)
当访问 blog.com.cn 的请求带有特定的 HTTP 请求头(canary: always)时,将该请求转发到金丝雀版本(developer 服务);否则,请求走稳定版(由基础 Ingress blog-ingress 决定)
yaml
# blog-ingress-canary.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog-canary
annotations:
# 开启 Canary
nginx.ingress.kubernetes.io/canary: "true"
# 基于请求头进行流量切分,检查请求中是否包含名为 "canary" 的 Header
nginx.ingress.kubernetes.io/canary-by-header: "canary"
#当 "canary" 请求头的值为 "always" 时,流量才被路由到这个金丝雀版本
nginx.ingress.kubernetes.io/canary-by-header-value: "always"
spec:
ingressClassName: nginx
rules:
- host: blog.com.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: developer # ⚠️ 金丝雀版本的后端 Service
port:
number: 80
bash
[root@h1 ingress]# kubectl apply -f blog-ingress-canary.yaml
步骤 3:测试
-s:静默模式
-H:自定义请求头
bash
# 测试 1:不带特殊 Header,路由到稳定版
[root@hd1 ~]# curl -s -H "Host: blog.com.cn" http://192.168.1.11:31886
app2 ----
# 测试 2:带 Header "canary: always",路由到 Canary 版
[root@hd1 ~]# curl -s -H "Host: blog.com.cn" -H "canary: always" http://192.168.1.11:31886
app3 ----
如果测试的时候出了问题,无法正常切换,就清理一下其他的ingress规则,避免干扰
步骤 4:全量切换并下线 Canary
bash
# 修改原 Ingress 指向 v3 版本
[root@h1 ingress]# cat blog-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog
spec:
ingressClassName: nginx
rules:
- host: blog.com.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: developer
port:
number: 80
# 应用更新并删除 Canary Ingress
[root@h1 ingress]# kubectl apply -f blog-ingress.yaml
[root@h1 ingress]# kubectl delete -f blog-ingress-canary.yaml
ingress.networking.k8s.io "blog-canary" deleted
#访问验证
[root@hd1 ~]# curl -s -H "Host: blog.com.cn" http://192.168.1.11:31886
app3 ----
总结
这两种金丝雀更新策略思路上是一致的,一个是先进行流量分割,让一部分用户体验新的业务;一个是通过不同请求头访问不同的版本业务,方便内部人员测试功能。最后的操作都是一致的,就是修改完基础的ingress规则,把版本改成最新的之后,然后删掉金丝雀规则就行了
六、扩展:常见新版本发布策略总结
| 策略 | 回滚速度 | 风险等级 | 实现复杂度 | 流量切换方式 | 主要适用场景 |
|---|---|---|---|---|---|
| 替换部署 (Recreate) | 慢(需重新部署旧版本) | 高(中断期间服务不可用) | 低 | 一次性销毁全部旧 Pod → 创建新 Pod | 开发/测试环境,或可接受停机的批处理/夜间任务 |
| 滚动发布 (Rolling Update) | 中等(需反向滚动) | 中等(新旧版本短暂共存) | 低(K8s 原生支持) | 逐步替换实例,始终保持部分副本在线 | 大部分常规业务,可容忍短暂混合版本 |
| 蓝绿部署 (Blue/Green) | 秒级(切回蓝环境) | 低(验证后一次性切换) | 中(需流量调度层) | 切换负载均衡器指向(全量瞬间切换) | 对稳定性要求极高、资源充足的核心系统 |
| 金丝雀发布 (Canary) | 快(停止放量或切回) | 极低(仅影响小部分用户) | 高(需流量拆分、监控、自动决策) | 按比例(如 1%→10%→100%)逐步增加新版本流量 | 大型分布式系统、希望真实生产流量验证 |
| A/B 测试 (A/B Testing) | 快(调整分流规则即可) | 低(仅影响特定用户组) | 高(需基于用户特征路由) | 基于用户属性(地域、ID、Cookie 等)定向分流 | 前端、推荐系统、业务功能效果对比 |
七、关键要点速查
Ingress vs Ingress Controller
| 概念 | 本质 | 类比 |
|---|---|---|
| Ingress | 路由规则(YAML 资源配置) | 类似于 Nginx 的配置文件(定义域名、路径 → 后端) |
| Ingress Controller | 实际的负载均衡器进程 | 类似于实际运行的 Nginx 进程,读取配置并处理流量 |
常用 Ingress 注解
| 注解 | 用途 |
|---|---|
nginx.ingress.kubernetes.io/rewrite-target |
URL 重写目标 |
nginx.ingress.kubernetes.io/use-regex |
启用正则表达式匹配 |
nginx.ingress.kubernetes.io/affinity |
会话保持(设为 cookie) |
nginx.ingress.kubernetes.io/canary |
开启金丝雀发布 |
nginx.ingress.kubernetes.io/canary-weight |
金丝雀流量权重(0-100) |
nginx.ingress.kubernetes.io/canary-by-header |
基于请求头的金丝雀路由 |
常用命令
bash
# 查看 Ingress
kubectl get ingress
kubectl describe ingress <name>
# 查看 Ingress Controller
kubectl -n ingress-nginx get pod
kubectl -n ingress-nginx get svc
# 创建 TLS Secret
kubectl create secret tls <secret-name> --key=<key-file> --cert=<cert-file>
# 修改 Ingress Controller Service 类型
kubectl edit svc ingress-nginx-controller -n ingress-nginx
八、常见问题排查
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
curl 访问返回 404 |
hosts 文件未配置或域名错误 | 检查 /etc/hosts 是否正确配置 |
| 访问返回 502 Bad Gateway | 后端 Service 或 Pod 不存在 | 检查 kubectl get svc 和 kubectl get endpoints |
| 访问返回 Connection Refused | NodePort 端口未正确暴露 | 检查 kubectl get svc -n ingress-nginx 的 PORT(S) 列 |
| HTTPS 访问证书错误 | 使用了自签名证书 | 使用 -k 参数忽略证书验证,或导入 CA 证书 |
| 访问极其卡顿或超时 | externalTrafficPolicy 为默认值 |
修改为 Local 模式 |
| Ingress 规则不生效 | IngressClass 不匹配 | 检查 spec.ingressClassName: nginx 是否正确 |
九、关键补充说明与谬误纠正
1. 关于 externalTrafficPolicy: Local 的说明
在 4.3 节测试中,当访问失败时需要修改 Service 的 externalTrafficPolicy。这里补充说明两种策略的区别:
| 策略 | 行为 | 优点 | 缺点 |
|---|---|---|---|
| Cluster(默认) | 流量可转发到集群内任意节点上的 Pod | 负载均衡更均匀 | 丢失客户端真实 IP |
| Local | 只转发到本节点上有对应 Pod 的后端 | 保留客户端真实 IP | 若节点上没有对应 Pod,则丢弃流量 |
适用建议 :在 Ingress 场景下,通常需要获取客户端真实 IP(用于日志记录、限流等),因此建议设置为
Local。
2. 关于 Ingress 正则表达式匹配
在 5.1 节中,使用了 pathType: ImplementationSpecific 配合正则表达式。各 pathType 的区别:
| pathType | 说明 |
|---|---|
Prefix |
前缀匹配,如 / 匹配所有以 / 开头的路径 |
Exact |
精确匹配,路径必须完全相同 |
ImplementationSpecific |
由 Ingress Controller 自行解释,通常支持正则表达式 |
在 Nginx Ingress 中,如需使用正则表达式,必须配合注解
nginx.ingress.kubernetes.io/use-regex: "true"。
3. 关于 rewrite-target 注解的说明
rewrite-target: /$2 的工作原理:
- 正则表达式
/product(/|$)(.*)匹配路径(/|$)匹配/或字符串结尾 → 捕获为$1(.*)匹配剩余路径 → 捕获为$2
/$2表示将路径重写为/+ 第二个捕获组的内容
示例:
- 请求
/product/api/v1→ 重写为/api/v1 - 请求
/product/→ 重写为/
这样可以确保请求路径正确转发到后端服务,而不会把 /product 路径也带到后端。
4. 关于自签名证书
在 5.3 节中,使用 openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout private.key -out selfsigned.crt 生成的是自签名证书 ,浏览器会提示不安全。生产环境应使用受信任的 CA(如 Let's Encrypt、云厂商证书服务)签发的证书。自签名证书仅适用于开发测试环境。
5. 关于 Ingress Controller Service 修改
文中将 Ingress Controller 的 Service 类型从 LoadBalancer 改为 NodePort,这仅适用于非公有云环境 (如本地虚拟机、裸金属服务器)。如果在云环境(阿里云、AWS 等)中,建议保留 LoadBalancer 类型,云厂商会自动分配公网 IP 和负载均衡器。
6. 关于证书生成的两种方式
文中 5.3 节提供了两种生成证书的方式:
- 一步到位 (
openssl req -x509 ...):直接生成自签名证书,适合快速测试 - 分步生成(生成私钥 → 生成 CSR → 签名):更详细的流程,适合需要了解证书原理的场景
两种方式生成的证书效果相同,推荐使用一步到位的方式,更简洁高效。
7. 关于灰度发布的注解使用场景总结
| 注解组合 | 适用场景 |
|---|---|
canary: "true" + canary-weight: "20" |
按百分比灰度放量,适合逐步验证 |
canary: "true" + canary-by-header: "canary" + canary-by-header-value: "always" |
内部测试用户验证,适合 QA 团队提前测试 |
canary: "true" + canary-by-header-pattern: ".*" |
基于正则表达式匹配更复杂的 Header 规则 |
canary: "true" + canary-by-header-cookie: "always" |
基于 Cookie 进行灰度,适合特定浏览器会话的测试 |
十、总结
Kubernetes Ingress 是集群七层负载均衡的核心组件,通过 Ingress 资源定义路由规则,由 Ingress Controller 实际执行流量转发。相比 Service 的四层负载均衡,Ingress 能够基于域名、路径等 HTTP 层信息进行智能路由,并且多个服务可以共享同一个入口 IP 和端口。
本文涵盖的核心知识点:
- Ingress 与 Service 的区别:L7 vs L4
- Ingress 与 Ingress Controller 的关系:规则 vs 执行者
- 多服务路由:基于路径和基于域名
- HTTPS 配置:通过 TLS Secret 实现
- 自定义配置:通过注解实现重定向、会话保持等
- 灰度发布:基于权重和基于 Header 的金丝雀发布
- 常见发布策略对比:Recreate、RollingUpdate、蓝绿、金丝雀、A/B 测试