istio灰度发布部署演示文档
·
istio灰度发布部署演示文档
概述
本文档基于kubernetes v1.32.8 + istio-1.28.0 实现部署和服务网格的灰度发布验证。
Istio 作为 Kubernetes 生态中的服务网格(Service Mesh),核心能力之一是无需修改业务代码即可实现灵活、精细的灰度发布(又称金丝雀发布 / Canary Release)。其本质是通过拦截服务间的网络流量,按照预设规则(如权重、用户标签、请求参数等)将流量动态分配到不同版本的服务实例,实现风险可控的版本迭代。
-
核心原理:
- 服务版本隔离:通过
DestinationRule为目标服务定义多个 Subset(如vtestion: v1、vtestion: v2),每个 Subset 对应一个版本的服务实例(Pod 需通过标签区分版本,如app: test-app, vtestion: v1) - 流量路由规则:通过
VirtualService定义流量分配策略(如权重、匹配条件),将符合规则的流量路由到指定 Subset - 动态生效:Istiod 监听
VirtualService和DestinationRule的变更,实时将配置推送到所有相关 Envoy 代理,无需重启服务即可生效
- 服务版本隔离:通过
-
关键组件:
- Envoy: 数据平面代理(Sidecar 注入到每个 Pod),拦截进出流量,执行路由规则
- Istiod: 控制平面,负责将路由规则、服务发现信息转换为 Envoy 可识别的配置并推送
- VirtualService: Istio 核心路由配置资源,定义 “流量如何分配到不同服务版本”
- DestinationRule: 定义服务的子集(Subset),将不同版本的服务实例分组(如 v1、v2)
- Service: Kubernetes 原生 Service,负责服务实例的注册与健康检查(Istio 依赖其获取服务端点)
- Gateway: 集群边缘流量入口,接管外部请求并接入 Istio 网格,是外部流量灰度的前提
-
常见灰度发布策略
- 权重路由(最常用): 按比例分配流量到不同版本,适用于逐步放量验证(如: 将 10% 流量导向 v2 版本,90% 流量保留在 v1 版本)
- 基于请求匹配的路由(精准测试): 根据请求的特定属性(如 Header、参数、Cookie)定向路由,适用于定向测试(如内部员工、特定用户群体)
1. 获取软件和镜像
1.1 下载 istio 软件
# 在可访问公网的服务器下载 istio
curl -L https://istio.io/downloadIstio | ISTIO_VtestION=1.28.0 TARGET_ARCH=x86_64 sh -
# 或者直接下面的复制到浏览器下载
https://github.com/istio/istio/releases/download/1.28.0/istio-1.28.0-linux-amd64.tar.gz
1.2 下载 istio 需要的镜像
# 1. 在能访问公网的docker上下载istio 镜像,上传到私有harbor仓库
docker pull docker.io/istio/install-cni:1.28.0
docker pull docker.io/istio/proxyv2:1.28.0
docker pull docker.io/istio/pilot:1.28.0
# 2. 给镜像打 tag
docker tag docker.io/istio/install-cni:1.28.0 192.168.133.123/repo/istio/install-cni:1.28.0
docker tag docker.io/istio/proxyv2:1.28.0 192.168.133.123/repo/istio/proxyv2:1.28.0
docker tag docker.io/istio/pilot:1.28.0 192.168.133.123/repo/istio/pilot:1.28.0
# 3. 推送镜像到私有仓库
docker push 192.168.133.123/repo/istio/install-cni:1.28.0
docker push 192.168.133.123/repo/istio/proxyv2:1.28.0
docker push 192.168.133.123/repo/istio/pilot:1.28.0
# 4. 下载 可观测性组件镜像,上传到私有harbor仓库 (可选)
docker pull docker.io/grafana/grafana:12.0.1
docker pull quay.io/kiali/kiali:v2.17
docker pull ghcr.io/prometheus-operator/prometheus-config-reloader:v0.85.0
docker pull prom/prometheus:v3.5.0
docker tag docker.io/grafana/grafana:12.0.1 192.168.133.123/repo/istio/grafana:12.0.1
docker tag quay.io/kiali/kiali:v2.17 192.168.133.123/repo/istio/kiali:v2.17
docker tag ghcr.io/prometheus-operator/prometheus-config-reloader:v0.85.0 192.168.133.123/repo/istio/prometheus-config-reloader:v0.85.0
docker tag prom/prometheus:v3.5.0 192.168.133.123/repo/istio/prometheus:v3.5.0
docker push 192.168.133.123/repo/istio/grafana:12.0.1
docker push 192.168.133.123/repo/istio/kiali:v2.17
docker push 192.168.133.123/repo/istio/prometheus-config-reloader:v0.85.0
docker push 192.168.133.123/repo/istio/prometheus:v3.5.0
2. 部署 istio
# 在master01
# 解压软件包,并复制 执行命令到 /usr/local/bin/ 目录
tar -xzf istio-1.28.0-linux-amd64.tar.gz
cd istio-1.28.0
cp bin/istioctl /usr/local/bin/
# 使用私有harbor仓库镜像安装 Istio
istioctl install --set hub=192.168.133.123/repo/istio --set tag=1.28.0 --set components.cni.enabled=true --set components.egressGateways[0].enabled=true --set components.egressGateways[0].name=istio-egressgateway
# 安装参数说明
--set hub=192.168.133.123/repo/istio -> 表示对应 Istio 镜像的仓库地址
--set tag=1.28.0 -> 表示tag 对应镜像的版本标签,指定安装 Istio 1.28.0 版本
--set components.cni.enabled=true -> 表示启用 Istio CNI 组件,提升集群安全性、简化网络配置、避免权限相关的部署报错
--set components.egressGateways[0].enabled=true -> 启用 Istio 出口网关 ,当弃用该参数,需要指定以下参数
--set components.egressGateways[0].name=istio-egressgateway 和上一行参数是一起使用的
# 在master01
# 以下是部署过程(示例参考)
[root@kube istio-1.28.0]# cd /root/istio-1.28.0
[root@kube istio-1.28.0]# pwd
/root/istio-1.28.0
[root@kube istio-1.28.0]# ls
bin LICENSE manifests manifest.yaml README.md samples tools
[root@kube istio-1.28.0]#
[root@kube istio-1.28.0]# istioctl install \
> --set hub=192.168.133.123/repo/istio \
> --set tag=1.28.0 \
> --set components.cni.enabled=true \
> --set components.egressGateways[0].enabled=true \
> --set components.egressGateways[0].name=istio-egressgateway
|\
| \
| \
| \
/|| \
/ || \
/ || \
/ || \
/ || \
/ || \
/______||__________\
____________________
\__ _____/
\_____/
This will install the Istio 1.28.0 profile "default" into the cluster. Proceed? (y/N) y
✔ Istio core installed ⛵️
✔ CNI installed 🪢
✔ Istiod installed 🧠
✔ Egress gateways installed 🛫
✔ Ingress gateways installed 🛬
✔ Installation complete
[root@kube istio-1.28.0]#
# 在master01
# 出现以下结果说明 istio 已安装完成,可以使用了。
[root@kube istio-1.28.0]#
[root@kube istio-1.28.0]# kubectl -n istio-system get po
NAME READY STATUS RESTARTS AGE
istio-cni-node-zhqgm 1/1 Running 0 64s
istio-egressgateway-77dcfdbfbc-4zqlv 1/1 Running 0 62s
istio-ingressgateway-5857bb6896-8bb27 1/1 Running 0 62s
istiod-d7b699bb9-mpp7t 1/1 Running 0 64s
[root@kube istio-1.28.0]#
[root@kube istio-1.28.0]# kubectl -n istio-system get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-egressgateway ClusterIP 10.233.57.219 <none> 80/TCP,443/TCP 64s
istio-ingressgateway LoadBalancer 10.233.58.242 <pending> 15021:31219/TCP,80:30376/TCP,443:31543/TCP 62s
istiod ClusterIP 10.233.61.148 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 62s
istiod-revision-tag-default ClusterIP 10.233.54.38 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 64s
[root@kube istio-1.28.0]#
# # 在master01 ,卸载命令,
# istioctl uninstall --purge
3. 部署 istio 可观测平台(可选)
istio 可观测平台,主要是由grafana、prometheus以及kiali组成,通过kiali的web控制端可以比较方便的查看流量流转拓扑。
# 在master01
# 1. 替换 grafana.yaml 和 kiali.yaml 以及 prometheus.yaml 的镜像为私有镜像
cd /root/istio-1.28.0/samples/addons
[root@kube addons]# pwd
/root/istio-1.28.0/samples/addons
[root@kube addons]# ls
extras grafana.yaml jaeger.yaml kiali.yaml loki.yaml prometheus.yaml README.md
[root@kube addons]#
[root@kube addons]# sed -i 's@docker.io/grafana/grafana:12.0.1@192.168.133.123/repo/istio/grafana:12.0.1@g' grafana.yaml
[root@kube addons]# sed -i 's@quay.io/kiali/kiali:v2.17@192.168.133.123/repo/istio/kiali:v2.17@g' kiali.yaml
[root@kube addons]# sed -i 's@prom/prometheus:v3.5.0@192.168.133.123/repo/istio/prometheus:v3.5.0@g' prometheus.yaml
[root@kube addons]# sed -i 's@ghcr.io/prometheus-operator/prometheus-config-reloader:v0.85.0@192.168.133.123/repo/istio/prometheus-config-reloader:v0.85.0@g' prometheus.yaml
[root@kube addons]#
# 在master01
# 2. 运行 istio 可观察性应用
cd /root/istio-1.28.0/samples/addons
kubectl apply -f grafana.yaml -f kiali.yaml -f prometheus.yaml
# 在master01
[root@kube addons]# # 查看 istio 的 pod 运行状况(以下是正常运行的pod示例参考)
[root@kube addons]# kubectl -n istio-system get po
NAME READY STATUS RESTARTS AGE
grafana-7c7f97cdd8-7kf7k 1/1 Running 0 3m58s
istio-cni-node-zhqgm 1/1 Running 0 92m
istio-egressgateway-77dcfdbfbc-4zqlv 1/1 Running 0 92m
istio-ingressgateway-5857bb6896-8bb27 1/1 Running 0 92m
istiod-d7b699bb9-mpp7t 1/1 Running 0 92m
kiali-8774dbdf7-5pvgw 1/1 Running 0 3m58s
prometheus-5d655c5778-j2q2l 2/2 Running 0 3m58s
[root@kube addons]#
# 在master01
# 3.1. 创建 kiali 用于外部访问的的服务,使用户可登录 kiali web 控制台。
# 以下文件 有两处可以修改,其他参数不要修改
cd /root/istio-1.28.0/samples/addons
# cat kiali-nodeport.yaml
---
apiVtestion: v1
kind: Service
metadata:
labels:
app: kiali
app.kubernetes.io/instance: kiali
app.kubernetes.io/name: kiali
name: kiali-nodeport # 替换成自定义kiali 的 名字
namespace: istio-system
spec:
ports:
- appProtocol: http
name: http
nodePort: 30706 # 自定义kiali 的 nodeport
port: 20001
protocol: TCP
targetPort: 20001
selector:
app.kubernetes.io/instance: kiali
app.kubernetes.io/name: kiali
type: NodePort
status:
loadBalancer: {}
---
# 在master01
# 3.2. 获取登录 kiali web 控制台地址,访问地址: 节点IP:30706(如: 192.168.133.100:30706)
kubectl apply -f kiali-nodeport.yaml
[root@kube addons]#
[root@kube addons]# kubectl -n istio-system get svc|grep kiali-nodeport
kiali-nodeport NodePort 10.233.21.93 <none> 20001:30706/TCP 10m
[root@kube addons]#
4. 灰度发布(基于请求头及URI匹配)
# 在master01
# 创建测试用的命名空间: demo
kubectl create ns demo
# 为目标命名空间demo启用 Istio 自动注入(仅需执行一次)
kubectl label namespace demo istio-injection=enabled --overwrite
4.1 创建测试应用
# 在master01 , 在/root/huidu目录 创建用于验证灰度的发布的文件
mkdir -p /root/huidu && cd /root/huidu
---
# cat test-app-v1.yaml
apiVtestion: apps/v1
kind: Deployment
metadata:
name: test-app-v1 # v1 版本
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: test-app # 两个版本应用的同名标签
vtestion: v1 # v1 版本 特有的标签
template:
metadata:
labels:
app: test-app # 两个版本应用的同名标签
vtestion: v1 # v1 版本 特有的标签
spec:
containtest:
- name: test-app
image: 192.168.133.123/repo/nginx # 用 nginx 简化测试,实际可替换为你的应用
ports:
- containerPort: 80
# 自定义响应内容,支持 /test 路径访问,明确区分版本
command:
- sh
- -c
- |
# 创建 /test 目录并写入版本内容
mkdir -p /usr/share/nginx/html/test
echo 'test-app-v1' > /usr/share/nginx/html/test/index.html
# 覆盖 Nginx 默认配置,支持 /test 路径访问
cat > /etc/nginx/conf.d/default.conf << EOF
server {
listen 80;
server_name localhost;
# 根路径重定向到 /test(可选,方便测试)
location / {
return 301 /test/;
}
# 处理 /test 路径的请求
location /test/ {
root /usr/share/nginx/html;
index index.html;
}
}
EOF
# 启动 Nginx 前台运行
nginx -g 'daemon off;'
---
# cat test-app-v2.yaml
apiVtestion: apps/v1
kind: Deployment
metadata:
name: test-app-v2 # v2 版本
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: test-app # 两个版本应用的同名标签
vtestion: v2 # v2 版本 特有的标签
template:
metadata:
labels:
app: test-app # 两个版本应用的同名标签
vtestion: v2 # v2 版本 特有的标签
spec:
containtest:
- name: test-app
image: 192.168.133.123/repo/nginx
ports:
- containerPort: 80
# 自定义响应内容,支持 /test 路径访问,明确区分版本
command:
- sh
- -c
- |
# 创建 /test 目录并写入版本内容
mkdir -p /usr/share/nginx/html/test
echo 'test-app-v2 (canary)' > /usr/share/nginx/html/test/index.html
# 覆盖 Nginx 默认配置,支持 /test 路径访问
cat > /etc/nginx/conf.d/default.conf << EOF
server {
listen 80;
server_name localhost;
# 根路径重定向到 /test(可选,方便测试)
location / {
return 301 /test/;
}
# 处理 /test 路径的请求
location /test/ {
root /usr/share/nginx/html;
index index.html;
}
}
EOF
# 启动 Nginx 前台运行
nginx -g 'daemon off;'
---
# cat test-app-svc.yaml
apiVtestion: v1
kind: Service
metadata:
name: test-app
namespace: demo
spec:
selector:
app: test-app # 匹配两个版本的 Pod
ports:
- port: 80
targetPort: 80
name: tcp-80
type: ClusterIP
---
# 运行 v1 、v2 版本的应用以及 他们共同的 svc
kubectl apply -f test-app-v1.yaml -f test-app-v2.yaml -f test-app-svc.yaml
# test-app-v1 和 test-app-v2 的POD内部访问地址是: http://127.0.0.1/test/ (以下是示例参考,请按照实际pod为准)
[root@kube huidu]#
[root@kube huidu]# kubectl -n demo get po -owide|grep test-app
test-app-v1-788c57499-v7qgd 2/2 Running 0 34m 10.233.106.75 kube <none> <none>
test-app-v2-5f6d998b6d-q58j6 2/2 Running 0 34m 10.233.106.77 kube <none> <none>
[root@kube huidu]#
[root@kube huidu]# kubectl -n demo exec -it test-app-v1-788c57499-v7qgd -- sh
# curl http://127.0.0.1/test/
test-app-v1
# exit
[root@kube huidu]#
[root@kube huidu]# kubectl -n demo exec -it test-app-v2-5f6d998b6d-q58j6 -- sh
# curl http://127.0.0.1/test/
test-app-v2 (canary)
# exit
[root@kube huidu]#
# 备注: 万一 test-app-v1 和 test-app-v2 的容器数量和上面的结果不一样(不是 2/2 ),需要执行以下命令重新注入sidecar容器
# 为目标命名空间demo启用 Istio 自动注入(仅需执行一次)
kubectl label namespace demo istio-injection=enabled --overwrite
# 重启 test-app-v1 和 test-app-v2 生效
kubectl -n demo rollout restart deploy test-app-v1 test-app-v2
4.2 配置 灰度规则
核心配置:通过 VirtualService 定义流量规则,根据 X-User 请求头分发流量和uri前缀实现灰度发布。
---
# cat test-app-dr.yaml
# 1. DestinationRule(服务子集定义)
apiVtestion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: test-app-dr
namespace: demo
spec:
host: test-app # 填写 test-app service 名
subsets:
- name: v1 # 自定义子集名字
labels:
vtestion: v1 # 版本v1
- name: v2 # 自定义子集名字
labels:
vtestion: v2 # 版本v2
---
# cat test-app-vs.yaml
# 2. VirtualService(核心路由:适配 Service 端口 80)
apiVtestion: networking.istio.io/v1
kind: VirtualService
metadata:
name: test-app-vs
namespace: demo
spec:
hosts:
- "test.app.com" # 访问域名
gateways:
- test-app-gateway # gw 的名字
http:
- match:
- headtest:
X-User:
exact: "test" #
uri:
prefix: "/test/" # uri 路径前缀匹配
route:
- destination:
host: test-app # 匹配 test-app Service 名
subset: v2 # v2 版本
port:
number: 80 # 匹配 test-app Service 的 80 端口
- match:
- uri:
prefix: "/test/" # uri 路径前缀匹配
route:
- destination:
host: test-app # 匹配 test-app Service 名
subset: v1 # v1 版本
port:
number: 80 # 匹配 test-app Service 的 80 端口
weight: 100 # 权重是 100,表示当请求头不匹配的时候,默认全部流量用于访问 v1 版本
- destination:
host: test-app # 匹配 test-app Service 名
subset: v2 # v1 版本
port:
number: 80 # 匹配 test-app Service 的 80 端口
weight: 0 # 待 基于 v2 的请求头验证正常后,将 该(v2)权重设置为100, v1 版本权重设置为 0,就可以实现流量切换。
---
# cat test-app-gw.yaml
# 3. Gateway(适配 IngressGateway Pod 端口 80)
apiVtestion: networking.istio.io/v1
kind: Gateway
metadata:
name: test-app-gateway # 自定义名称,与vs应用的保持一致
namespace: demo
spec:
selector:
istio: ingressgateway # 填写istio-ingressgateway应用的标签
servtest:
- port:
number: 80 # 网关对外暴露的端口(外部访问用 80,和 Service 端口一致)
name: http
protocol: HTTP
hosts:
- "test.app.com" # 允许你的域名访问
---
# 4. 生效灰度规则
[root@kube huidu]#
[root@kube huidu]# kubectl apply -f test-app-dr.yaml -f test-app-vs.yaml -f test-app-gw.yaml -n demo
destinationrule.networking.istio.io/test-app-dr created
virtualservice.networking.istio.io/test-app-vs created
gateway.networking.istio.io/test-app-gateway created
[root@kube huidu]#
[root@kube huidu]# kubectl -n demo get gw,vs,dr
NAME AGE
gateway.networking.istio.io/test-app-gateway 5s
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/test-app-vs ["test-app-gateway"] ["test.app.com"] 5s
NAME HOST AGE
destinationrule.networking.istio.io/test-app-dr test-app 5s
[root@kube huidu]#
4.3 生效灰度规则
# 1. 确认 istio-ingressgateway 的 对外端口
[root@kube huidu]#
[root@kube huidu]# kubectl -n istio-system get svc|grep ingress
istio-ingressgateway LoadBalancer 10.233.58.242 <pending> 15021:31219/TCP,80:30376/TCP,443:31543/TCP 20h
[root@kube huidu]#
# 从上面的内容可知 istio-ingressgateway 的访问地址是: http://IP:30376
# 本地节点IP是 192.168.133.100, istio-ingressgateway 的访问地址则是: http://192.168.133.100:30376/
# 2. 配置 hosts解析
[root@kube huidu]#
[root@kube huidu]# cat /etc/hosts |grep test.app.com
192.168.133.100 test.app.com other.app.com
[root@kube huidu]#
4.4 灰度验证过程
# 1. 无请求头 → 访问 v1 版本(默认路由)
curl http://test.app.com:30376/test/
# 预期输出:test-app-v1
[root@kube huidu]#
[root@kube huidu]# curl http://test.app.com:30376/test/
test-app-v1
[root@kube huidu]#
[root@kube huidu]# curl http://test.app.com:30376/test/
test-app-v1
[root@kube huidu]# curl http://test.app.com:30376/test/
test-app-v1
[root@kube huidu]# curl http://test.app.com:30376/test/
test-app-v1
[root@kube huidu]#
# 2. 带 X-User: test 请求头 → 访问 v2 版本(灰度路由)
curl -H "X-User: test" http://test.app.com:30376/test/
# 预期输出:test-app-v2 (canary)
[root@kube huidu]#
[root@kube huidu]# curl -H "X-User: test" http://test.app.com:30376/test/
test-app-v2 (canary)
[root@kube huidu]# curl -H "X-User: test" http://test.app.com:30376/test/
test-app-v2 (canary)
[root@kube huidu]# curl -H "X-User: test" http://test.app.com:30376/test/
test-app-v2 (canary)
[root@kube huidu]# curl -H "X-User: test" http://test.app.com:30376/test/
test-app-v2 (canary)
[root@kube huidu]#
# 3. 不带 X-User: test 请求头 → 访问(走默认路由) (如: X-User: test01 和 X-User01: test )
curl -H "X-User: test01" http://test.app.com:30376/test/
# 预期输出:test-app-v1 (请求头不匹配,走默认路由)
[root@kube huidu]#
[root@kube huidu]# curl -H "X-User: test01" http://test.app.com:30376/test/
test-app-v1
[root@kube huidu]# curl -H "X-User: test01" http://test.app.com:30376/test/
test-app-v1
[root@kube huidu]# curl -H "X-User: test01" http://test.app.com:30376/test/
test-app-v1
[root@kube huidu]#
curl -H "X-User01: test" http://test.app.com:30376/test/
# 预期输出:test-app-v1 (请求头不匹配,走默认路由)
[root@kube huidu]#
[root@kube huidu]# curl -H "X-User01: test" http://test.app.com:30376/test/
test-app-v1
[root@kube huidu]# curl -H "X-User01: test" http://test.app.com:30376/test/
test-app-v1
[root@kube huidu]# curl -H "X-User01: test" http://test.app.com:30376/test/
test-app-v1
[root@kube huidu]#
# 3. 其他域名访问 → 被 Gateway 拦截,无法访问到 test-app应用(验证域名拦截了)
curl http://other.app.com:30376/test/ # 替换为 other.app.com 的域名(未在灰度规则配置的域名)
# 预期输出:既不是test-app-v1,也不是test-app-v2 (canary)--无结果
[root@kube huidu]#
[root@kube huidu]# curl http://other.app.com:30376/test/
[root@kube huidu]# curl http://other.app.com:30376/test/
[root@kube huidu]# curl http://other.app.com:30376/test/
[root@kube huidu]#
4.5 流量切换
当基于请求头+URI 验证 v2 版本正常后,将全部流量切换到 v2 版本。
切换流量的时候只需要调整 test-app-vs.yaml 文件,将流量全部指向 v2 版本,配置如下:
# cat test-app-vs-v2.yaml
# 1. VirtualService(核心路由:所有流量全量走v2版本)
apiVtestion: networking.istio.io/v1
kind: VirtualService
metadata:
name: test-app-vs # 保持原名称,可直接覆盖替换原有VS
namespace: demo
spec:
hosts:
- "test.app.com" # 访问域名
gateways:
- test-app-gateway # 关联的Gateway名字
http:
# 仅保留核心规则:所有/test/前缀的请求全量走v2
- match:
- uri:
prefix: "/test/" # 匹配/test/前缀的所有请求
route:
- destination:
host: test-app # 目标Service名
subset: v2 # 仅保留v2版本
port:
number: 80 # 目标Service的80端口
weight: 100 # 全量流量走v2(可省略,单目标时weight默认100)
# 2、此时 test-app-vs-v2.yaml 和 下文的test-app-vs.yaml 是等效的。
# cat test-app-vs.yaml
# 2. VirtualService(核心路由:所有流量全量走v2版本)
apiVtestion: networking.istio.io/v1
kind: VirtualService
metadata:
name: test-app-vs
namespace: demo
spec:
hosts:
- "test.app.com" # 访问域名
gateways:
- test-app-gateway # gw 的名字
http:
- match:
- headtest:
X-User:
exact: "test" #
uri:
prefix: "/test/" # uri 路径前缀匹配
route:
- destination:
host: test-app # 匹配 test-app Service 名
subset: v2 # v2 版本
port:
number: 80 # 匹配 test-app Service 的 80 端口
- match:
- uri:
prefix: "/test/" # uri 路径前缀匹配
route:
- destination:
host: test-app # 匹配 test-app Service 名
subset: v1 # v1 版本
port:
number: 80 # 匹配 test-app Service 的 80 端口
weight: 0 # 权重是 100,表示当请求头不匹配的时候,默认全部流量用于访问 v1 版本
- destination:
host: test-app # 匹配 test-app Service 名
subset: v2 # v1 版本
port:
number: 80 # 匹配 test-app Service 的 80 端口
weight: 100 # 待 基于 v2 的请求头验证正常后,将 该(v2)权重设置为100, v1 版本权重设置为 0,就可以实现流量切换。
---
# 将流量全部切换到 v2 版本
kubectl -n demo apply -f test-app-vs-v2.yaml
# 此时访问 全部流量都转到 v2 版本了 ;此时访问域名 http://test.app.com:30376/test/ 所有流量切换到了 v2 版本
[root@kube huidu]#
[root@kube huidu]# kubectl apply -f test-app-vs-v2.yaml
virtualservice.networking.istio.io/test-app-vs configured
[root@kube huidu]#
[root@kube huidu]#
[root@kube huidu]# curl http://test.app.com:30376/test/
test-app-v2 (canary)
[root@kube huidu]# curl http://test.app.com:30376/test/
test-app-v2 (canary)
[root@kube huidu]# curl http://test.app.com:30376/test/
test-app-v2 (canary)
[root@kube huidu]# curl -H "X-User: test" http://test.app.com:30376/test/
test-app-v2 (canary)
[root@kube huidu]# curl -H "X-User: test" http://test.app.com:30376/test/
test-app-v2 (canary)
[root@kube huidu]# curl -H "X-User: test" http://test.app.com:30376/test/
test-app-v2 (canary)
[root@kube huidu]#
4.6 灰度发布基本流程概述
# 第 4 章 创建的验证文件都保存在master01 的 /root/huidu 目录,并且都在该目录操作验证过程
初始状态(所有流量走 v1) -> 部署 v2 版本 -> 携带 X-User:test 请求头验证 v2(普通流量仍走 v1) -> v2 验证通过 -> 全量切换(v1=0%、v2=100%) -> 移除 v1 路由规则,完成灰度发布
5. 灰度发布+ingress(基于请求头及URI) – 依赖第 4 章的配置
1. 基于已有的ingress-nginx,将ingress-nginx的流量转发给istio-ingressgateway
2. 完全基于第 4 章 灰度发布(基于请求头及URI)的全部配置,配置内容完全一致
3. 在 demo 命名空间创建 ExternalName 服务和 ingress
# 登录master01 , 在 /root/huidu 目录操作
cd /root/huidu
5.1 创建 ExternalName 服务
# 创建 ExternalName 服务,实现ingress跨命名空间访问 istio-ingressgateway 服务
# cat istio-ingressgateway-proxy.yaml
---
apiVtestion: v1
kind: Service
metadata:
name: istio-ingressgateway-proxy # service 名
namespace: demo
spec:
# 指向 istio-system 命名空间的 istio-ingressgateway 服务
externalName: istio-ingressgateway.istio-system.svc.cluster.local
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
sessionAffinity: None
type: ExternalName
status:
loadBalancer: {}
---
[root@kube huidu]#
[root@kube huidu]# kubectl apply -f istio-ingressgateway-proxy.yaml
service/istio-ingressgateway-proxy changed
[root@kube huidu]#
[root@kube huidu]# kubectl get -f istio-ingressgateway-proxy.yaml
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway-proxy ExternalName <none> istio-ingressgateway.istio-system.svc.cluster.local 80/TCP 85s
[root@kube huidu]#
5.2 创建 ingress 服务
# cat test-app-ing.yaml
---
apiVtestion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-app
namespace: demo
spec:
ingressClassName: nginx
rules:
- host: test.app.com
http:
paths:
- backend:
service:
name: istio-ingressgateway-proxy # 指向 ExternalName 类型服务
port:
number: 80
path: /test/
pathType: Prefix
status:
loadBalancer: {}
---
[root@kube huidu]#
[root@kube huidu]# kubectl create -f test-app-ing.yaml
ingress.networking.k8s.io/test-app created
[root@kube huidu]#
[root@kube huidu]# kubectl get -f test-app-ing.yaml
NAME CLASS HOSTS ADDRESS PORTS AGE
test-app nginx test.app.com 80 7s
[root@kube huidu]#
5.3 基于 ingress-nginx访问验证灰度发布
# 1. 确认 ingress-nginx 入口信息和 ingress 规则域名信息 (nodeport是: 32455; 域名是: test.app.com)
[root@kube huidu]#
[root@kube huidu]# cat /etc/hosts |grep demo
192.168.133.100 test.app.com other.app.com
[root@kube huidu]#
[root@kube huidu]# kubectl -n ingress-nginx get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.233.32.231 <pending> 80:32455/TCP,443:31420/TCP 119m
ingress-nginx-controller-admission ClusterIP 10.233.17.90 <none> 443/TCP 119m
[root@kube huidu]#
[root@kube huidu]# kubectl -n demo get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
test-app nginx test.app.com 80 3m10s
[root@kube huidu]#
# 2. 无请求头 → 访问 v1 版本(默认路由)
curl http://test.app.com:32455/test/
# 预期输出:test-app-v1
[root@kube huidu]#
[root@kube huidu]# curl http://test.app.com:32455/test/
test-app-v1
[root@kube huidu]# curl http://test.app.com:32455/test/
test-app-v1
[root@kube huidu]# curl http://test.app.com:32455/test/
test-app-v1
[root@kube huidu]# curl http://test.app.com:32455/test/
test-app-v1
[root@kube huidu]#
# 3. 带 X-User: test 请求头 → 访问 v2 版本(灰度路由)
curl -H "X-User: test" http://test.app.com:32455/test/
# 预期输出:test-app-v2 (canary)
[root@kube huidu]#
[root@kube huidu]# curl -H "X-User: test" http://test.app.com:32455/test/
test-app-v2 (canary)
[root@kube huidu]# curl -H "X-User: test" http://test.app.com:32455/test/
test-app-v2 (canary)
[root@kube huidu]# curl -H "X-User: test" http://test.app.com:32455/test/
[root@kube huidu]#
curl -H "X-User01: test" http://test.app.com:32455/test/
# 预期输出:test-app-v1 (请求头不匹配,走默认路由)
[root@kube huidu]#
[root@kube huidu]# curl -H "X-User01: test" http://test.app.com:32455/test/
test-app-v1
[root@kube huidu]# curl -H "X-User01: test" http://test.app.com:32455/test/
test-app-v1
[root@kube huidu]# curl -H "X-User01: test" http://test.app.com:32455/test/
test-app-v1
[root@kube huidu]#
5. 灰度发布+nginx (基于请求头及URI) – 依赖第 4 章的配置
1. 直接使用外部的 nginx 七层反向代理,将流量转发给 istio-ingressgateway
1. 完全基于第 4 章 灰度发布(基于请求头及URI)的全部配置,配置内容完全一致
# 登录 nginx 服务器,并配置 nginx 服务器 域名解析(验证环境,由于nginx 服务器和master01 是同一台服务器,域名解析如下)
# 配置nginx服务器 hosts解析
[root@kube huidu]#
[root@kube huidu]# cat /etc/hosts |grep test.app.com
192.168.133.100 test.app.com other.app.com
[root@kube huidu]#
5.1 创建 nginx 反向代理(七层)
# cat /usr/local/nginx/conf/nginx.conf
events {}
http {
# istio-ingressgateway 的入口
upstream ingressgateway {
server 192.168.133.100:30376 max_fails=2 fail_timeout=10s;
}
# test.app.com 转发规则(核心)
server {
listen 80;
server_name test.app.com;
location / {
proxy_pass http://ingressgateway;
proxy_http_vtestion 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Cookie $http_cookie;
}
}
# other.app.com 转发规则(核心)
server {
listen 80;
server_name other.app.com;
location / {
proxy_pass http://ingressgateway;
proxy_http_vtestion 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Cookie $http_cookie;
}
}
}
[root@kube conf]#
[root@kube conf]# /usr/local/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/nginx-1.20.2/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx-1.20.2/conf/nginx.conf test is successful
[root@kube conf]#
[root@kube conf]# /usr/local/nginx/sbin/nginx
[root@kube conf]#
[root@kube conf]# ps -ef |grep nginx|head -n 2
root 14471 14328 0 00:12 ? 00:00:00 nginx: master process nginx -g daemon off;
101 14483 14471 0 00:12 ? 00:00:00 nginx: worker process
[root@kube conf]#
5.2 基于 外部 nginx 访问验证灰度发布(无ingress-nginx)
# 1. 无请求头 → 访问 v1 版本(默认路由)
curl http://test.app.com/test/
# 预期输出:test-app-v1
[root@kube huidu]#
[root@kube huidu]# curl http://test.app.com/test/
test-app-v1
[root@kube huidu]# curl http://test.app.com/test/
test-app-v1
[root@kube huidu]# curl http://test.app.com/test/
[root@kube huidu]#
# 2. 带 X-User: test 请求头 → 访问 v2 版本(灰度路由)
curl -H "X-User: test" http://test.app.com/test/
# 预期输出:test-app-v2 (canary)
[root@kube huidu]#
[root@kube huidu]# curl -H "X-User: test" http://test.app.com/test/
test-app-v2 (canary)
[root@kube huidu]# curl -H "X-User: test" http://test.app.com/test/
test-app-v2 (canary)
[root@kube huidu]# curl -H "X-User: test" http://test.app.com/test/
[root@kube huidu]#
curl -H "X-User01: test" http://test.app.com/test/
# 预期输出:test-app-v1 (请求头不匹配,走默认路由)
[root@kube huidu]#
[root@kube huidu]# curl -H "X-User01: test" http://test.app.com/test/
test-app-v1
[root@kube huidu]# curl -H "X-User01: test" http://test.app.com/test/
test-app-v1
[root@kube huidu]# curl -H "X-User01: test" http://test.app.com/test/
test-app-v1
[root@kube huidu]#
# 3. 其他域名访问 → 被 Gateway 拦截,无法访问到 test-app应用(gateway验证域名限制)
curl http://other.app.com/test/ # 替换为 other.app.com 的域名(未在灰度规则配置的域名)
# 预期输出:既不是test-app-v1,也不是test-app-v2 (canary) ; 而是 404 (因为外部nginx不存在 /test/资源)
[root@kube huidu]# curl http://other.app.com/test/
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
[root@kube huidu]#
6. 使用istio-ingressgateway网关
# 1. istio-ingressgateway 部署在指定的节点
# 给指定的节点打上标签(如:kube 节点)
kubectl label nodes kube istio-ingressgateway=true
# 2. 以下文件是在已有的 istio-ingressgateway 应用的基础添加4点内容分别是:
# 1. 多副本运行 istio-ingressgateway 应用
# 2. 节点选择:仅调度到打了 istio-ingressgateway=true 标签的节点
# 3. Pod反亲和:尽量分散到不同带有指定标签的节点
# 4. 通过pod所在节点 IP+80 访问 istio-ingressgateway 应用
# 在 master01 ,获取 istio-ingressgateway 应用运行文件内容
cd /root/huidu
kubectl -n istio-system get deploy istio-ingressgateway -oyaml > istio-ingressgateway-new.yaml
---
# 3. 以下文件是添加了 4 点内容的 参考yaml。按照实际环境参考一下文件修改,只需要在对应位置 添加以下标有 序号的 4点内容,其他部分的保证和原来一致即可
# istio-ingressgateway-new.yaml
apiVtestion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "3"
creationTimestamp: "2025-12-03T05:10:25Z"
generation: 3
labels:
app: istio-ingressgateway
app.kubernetes.io/instance: istio
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: istio-ingressgateway
app.kubernetes.io/part-of: istio
app.kubernetes.io/vtestion: 1.28.0
helm.sh/chart: istio-ingress-1.28.0
istio: ingressgateway
istio.io/dataplane-mode: none
istio.io/rev: default
operator.istio.io/component: IngressGateways
operator.istio.io/managed: Reconcile
operator.istio.io/vtestion: 1.28.0
release: istio
name: istio-ingressgateway
namespace: istio-system
spec:
progressDeadlineSeconds: 600
replicas: 1 # 1. 建议副本数和节点选择数保持一致,并且不小于2(本文档环境以 一个副本为例)
revisionHistoryLimit: 10
selector:
matchLabels:
app: istio-ingressgateway
istio: ingressgateway
strategy:
rollingUpdate:
maxSurge: 100%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
annotations:
istio.io/rev: default
prometheus.io/path: /stats/prometheus
prometheus.io/port: "15020"
prometheus.io/scrape: "true"
sidecar.istio.io/inject: "false"
creationTimestamp: null
labels:
app: istio-ingressgateway
app.kubernetes.io/instance: istio
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: istio-ingressgateway
app.kubernetes.io/part-of: istio
app.kubernetes.io/vtestion: 1.28.0
chart: gateways
helm.sh/chart: istio-ingress-1.28.0
heritage: Tiller
install.operator.istio.io/owning-resource: unknown
istio: ingressgateway
istio.io/dataplane-mode: none
istio.io/rev: default
operator.istio.io/component: IngressGateways
release: istio
service.istio.io/canonical-name: istio-ingressgateway
service.istio.io/canonical-revision: latest
sidecar.istio.io/inject: "false"
spec:
# 2. 节点选择:仅调度到打了 istio-ingressgateway=true 标签的节点
nodeSelector:
istio-ingressgateway: "true"
# 2. 节点选择:仅调度到打了 istio-ingressgateway=true 标签的节点
# 3. Pod反亲和:尽量分散到不同带有指定标签的节点
affinity:
nodeAffinity: {}
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- istio-ingressgateway
- key: istio
operator: In
values:
- ingressgateway
topologyKey: kubernetes.io/hostname
# 3. Pod反亲和:尽量分散到不同带有指定标签的节点
containtest:
- args:
- proxy
- router
- --domain
- $(POD_NAMESPACE).svc.cluster.local
- --proxyLogLevel=warning
- --proxyComponentLogLevel=misc:error
- --log_output_level=default:info
env:
- name: PILOT_CERT_PROVIDER
value: istiod
- name: CA_ADDR
value: istiod.istio-system.svc:15012
- name: NODE_NAME
valueFrom:
fieldRef:
apiVtestion: v1
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
apiVtestion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVtestion: v1
fieldPath: metadata.namespace
- name: INSTANCE_IP
valueFrom:
fieldRef:
apiVtestion: v1
fieldPath: status.podIP
- name: HOST_IP
valueFrom:
fieldRef:
apiVtestion: v1
fieldPath: status.hostIP
- name: ISTIO_CPU_LIMIT
valueFrom:
resourceFieldRef:
divisor: "0"
resource: limits.cpu
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
apiVtestion: v1
fieldPath: spec.serviceAccountName
- name: ISTIO_META_WORKLOAD_NAME
value: istio-ingressgateway
- name: ISTIO_META_OWNER
value: kubernetes://apis/apps/v1/namespaces/istio-system/deployments/istio-ingressgateway
- name: ISTIO_META_MESH_ID
value: cluster.local
- name: TRUST_DOMAIN
value: cluster.local
- name: ISTIO_META_UNPRIVILEGED_POD
value: "true"
- name: ISTIO_META_CLUSTER_ID
value: Kubernetes
- name: ISTIO_META_NODE_NAME
valueFrom:
fieldRef:
apiVtestion: v1
fieldPath: spec.nodeName
image: 192.168.133.123/repo/istio/proxyv2:1.28.0
imagePullPolicy: IfNotPresent
name: istio-proxy
ports:
- containerPort: 15021
protocol: TCP
- containerPort: 8080
hostPort: 8080 # 4. 保证可通过pod 所在节点的 IP+8080 端口访问 (前提是 节点不存在8080端口,避免端口冲突,可改成80端口)
protocol: TCP
- containerPort: 8443
protocol: TCP
- containerPort: 15090
name: http-envoy-prom
protocol: TCP
readinessProbe:
failureThreshold: 30
httpGet:
path: /healthz/ready
port: 15021
scheme: HTTP
initialDelaySeconds: 1
periodSeconds: 2
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: "2"
memory: 1Gi
requests:
cpu: 100m
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/workload-spiffe-uds
name: workload-socket
- mountPath: /var/run/secrets/credential-uds
name: credential-socket
- mountPath: /var/run/secrets/workload-spiffe-credentials
name: workload-certs
- mountPath: /etc/istio/proxy
name: istio-envoy
- mountPath: /etc/istio/config
name: config-volume
- mountPath: /var/run/secrets/istio
name: istiod-ca-cert
- mountPath: /var/run/secrets/tokens
name: istio-token
readOnly: true
- mountPath: /var/lib/istio/data
name: istio-data
- mountPath: /etc/istio/pod
name: podinfo
- mountPath: /etc/istio/ingressgateway-certs
name: ingressgateway-certs
readOnly: true
- mountPath: /etc/istio/ingressgateway-ca-certs
name: ingressgateway-ca-certs
readOnly: true
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
serviceAccount: istio-ingressgateway-service-account
serviceAccountName: istio-ingressgateway-service-account
terminationGracePeriodSeconds: 30
volumes:
- emptyDir: {}
name: workload-socket
- emptyDir: {}
name: credential-socket
- emptyDir: {}
name: workload-certs
- configMap:
defaultMode: 420
name: istio-ca-root-cert
name: istiod-ca-cert
- downwardAPI:
defaultMode: 420
items:
- fieldRef:
apiVtestion: v1
fieldPath: metadata.labels
path: labels
- fieldRef:
apiVtestion: v1
fieldPath: metadata.annotations
path: annotations
name: podinfo
- emptyDir: {}
name: istio-envoy
- emptyDir: {}
name: istio-data
- name: istio-token
projected:
defaultMode: 420
sources:
- serviceAccountToken:
audience: istio-ca
expirationSeconds: 43200
path: istio-token
- configMap:
defaultMode: 420
name: istio
optional: true
name: config-volume
- name: ingressgateway-certs
secret:
defaultMode: 420
optional: true
secretName: istio-ingressgateway-certs
- name: ingressgateway-ca-certs
secret:
defaultMode: 420
optional: true
secretName: istio-ingressgateway-ca-certs
---
# 4. 重建 istio-ingressgateway 应用
cd /root/huidu
kubectl delete -f istio-ingressgateway-new.yaml # 需要把旧的 istio-ingressgateway 应用先删掉再重新apply,直接apply 容易失败
kubectl create -f istio-ingressgateway-new.yaml # 重新生效 istio-ingressgateway 应用
# 以下是 重建应用操作参考
root@kube huidu]#
[root@kube huidu]# kubectl delete -f istio-ingressgateway-new.yaml
deployment.apps "istio-ingressgateway" deleted
[root@kube huidu]#
[root@kube huidu]# kubectl create -f istio-ingressgateway-new.yaml
deployment.apps/istio-ingressgateway created
[root@kube huidu]#
[root@kube huidu]# kubectl -n istio-system get po -owide |grep istio-ingressgateway
istio-ingressgateway-ff4f9889c-lmclj 1/1 Running 0 3m4s 10.233.106.82 kube <none> <none>
[root@kube huidu]#
# 5. 验证方法
# 以下分别是 节点IP+端口80 以及 pod IP+8080 端口 两种验证方法
[root@kube huidu]# kubectl -n istio-system get po -owide |grep istio-ingressgateway
istio-ingressgateway-ff4f9889c-lmclj 1/1 Running 0 3m4s 10.233.106.82 kube <none> <none>
[root@kube huidu]#
[root@kube huidu]# telnet 10.233.106.82 8080 # 1. pod IP + 8080 端口验证
Trying 10.233.106.82...
Connected to 10.233.106.82.
Escape character is '^]'.
^]
telnet> quit
Connection closed.
[root@kube huidu]#
[root@kube huidu]# telnet 192.168.133.100 80 # 2. 节点IP + 80 端口验证
Trying 192.168.133.100...
Connected to 192.168.133.100.
Escape character is '^]'.
^]
telnet> quit
Connection closed.
[root@kube huidu]#
# 通过以上两种方法验证,并且出现类似以上结果,说明 应用重建完成,并且生效了。
7. 附:基于访问源IP实现灰度发布
# 1. 基于访问源 IP 实现灰度发布的核心原理,是通过 Istio VirtualService(简称 VS)从请求头(X-Forwarded-For/X-Real-IP)中提取客户端真实源 IP,结合 IP 匹配规则实现差异化路由;同时要求 Istio IngressGateway 需通过hostPort方式将业务端口暴露至 Pod 所在的节点上(而非 Service 的 NodePort/LoadBalancer 等方式),即直接通过「节点 IP + 业务端口(第9章示例中IngressGateway在节点暴露的是 8080 端口,下文将使用8080端口演示)」访问 IngressGateway。
# 2. 登录master01节点,基于第4章节内容(前提是IngressGateway已按照第6章的步修改了IngressGateway 实现通过hostPort方式将业务端口暴露至 Pod 所在的节点 )修改文件 test-app-vs.yaml
cd /root/huidu
vim test-app-vs.yaml
---
# cat test-app-vs.yaml
# 3. VirtualService(核心路由:适配 Service 端口 80)
apiVtestion: networking.istio.io/v1
kind: VirtualService
metadata:
name: test-app-vs
namespace: demo
spec:
hosts:
- "test.app.com" # 访问域名
gateways:
- test-app-gateway # gw 的名字
http:
- match:
- headtest:
# X-User:
# exact: "test" #
x-forwarded-for:
prefix: "192.168.140.101" # prefix: "192.168.140." # 表示以 192.168.140. 开头的客户端IP
uri:
prefix: "/test/" # uri 路径前缀匹配
route:
- destination:
host: test-app # 匹配 test-app Service 名
subset: v2 # v2 版本
port:
number: 80 # 匹配 test-app Service 的 80 端口
- match:
- uri:
prefix: "/test/" # uri 路径前缀匹配
route:
- destination:
host: test-app # 匹配 test-app Service 名
subset: v1 # v1 版本
port:
number: 80 # 匹配 test-app Service 的 80 端口
weight: 100 # 权重是 100,表示当请求头不匹配的时候,默认全部流量用于访问 v1 版本
- destination:
host: test-app # 匹配 test-app Service 名
subset: v2 # v1 版本
port:
number: 80 # 匹配 test-app Service 的 80 端口
weight: 0 # 待 基于 v2 的请求头验证正常后,将 该(v2)权重设置为100, v1 版本权重设置为 0,就可以实现流量切换。
---
# 4 .生效 test-app-vs.yaml 文件规则
kubectl apply -f test-app-vs.yaml
# 5. 确认环境信息 (注意本节点是master01节点 IP为192.168.140.101,是灰度的源IP )
[root@master01 huidu]#
[root@master01 huidu]# # 确认 ingressgateway 实例运行节点
[root@master01 huidu]# kubectl -n istio-system get po -owide |grep ingressgateway
istio-ingressgateway-668c45f865-2vztg 1/1 Running 0 132m 10.233.96.8 node03 <none> <none>
[root@master01 huidu]# # 确认 node03 的IP
[root@master01 huidu]# kubectl get no -owide
NAME STATUS ROLES AGE VtestION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VtestION CONTAINER-RUNTIME
master01 Ready control-plane 126d v1.28.0 192.168.140.101 <none> Red Hat Enterprise Linux 9.5 (Plow) 5.14.0-570.12.1.el9_6.x86_64 containerd://1.6.9
master02 Ready control-plane 126d v1.28.0 192.168.140.102 <none> Red Hat Enterprise Linux 9.5 (Plow) 5.14.0-570.12.1.el9_6.x86_64 containerd://1.6.9
master03 Ready control-plane 126d v1.28.0 192.168.140.103 <none> Red Hat Enterprise Linux 9.5 (Plow) 5.14.0-570.12.1.el9_6.x86_64 containerd://1.6.9
node01 Ready <none> 126d v1.28.0 192.168.140.104 <none> Red Hat Enterprise Linux 9.5 (Plow) 5.14.0-570.12.1.el9_6.x86_64 containerd://1.6.9
node02 Ready <none> 126d v1.28.0 192.168.140.105 <none> Red Hat Enterprise Linux 9.5 (Plow) 5.14.0-570.12.1.el9_6.x86_64 containerd://1.6.9
node03 Ready <none> 126d v1.28.0 192.168.140.106 <none> Red Hat Enterprise Linux 9.5 (Plow) 5.14.0-570.12.1.el9_6.x86_64 containerd://1.6.9
[root@master01 huidu]#
[root@master01 huidu]# # 域名解析到 ingressgateway 实例对应的节点IP
[root@master01 huidu]# cat /etc/hosts|grep demo
192.168.140.106 test.app.com
[root@master01 huidu]#
[root@master01 huidu]#
# 6. 在 192.168.140.101 节点验证
# 1. 客户端IP(192.168.140.171) → 访问 v2 版本
curl http://test.app.com:8080/test/
# 预期输出:test-app-v2 (canary)
[root@master01 huidu]# # 域名解析到 ingressgateway 实例对应的节点IP
[root@master01 huidu]# cat /etc/hosts|grep demo
192.168.140.176 test.app.com
[root@master01 huidu]#
[root@master01 huidu]#
[root@master01 huidu]# # 在 192.168.140.101 节点验证(灰度规则的IP)
[root@master01 huidu]# curl http://test.app.com:8080/test/
test-app-v2 (canary)
[root@master01 huidu]# curl http://test.app.com:8080/test/
test-app-v2 (canary)
[root@master01 huidu]# curl http://test.app.com:8080/test/
test-app-v2 (canary)
[root@master01 huidu]#
# 7. 在 192.168.140.102 节点验证(非灰度规则的IP)
# 1. 客户端IP(192.168.140.102) → 访问 v1 版本(默认路由)
curl http://test.app.com:8080/test/
# 预期输出:test-app-v1
[root@master02 ~]#
[root@master02 ~]#
[root@master02 ~]# # 域名解析到 ingressgateway 实例对应的节点IP
[root@master02 ~]#
[root@master02 ~]# cat /etc/hosts|grep demo
192.168.140.176 test.app.com
[root@master02 ~]#
[root@master02 ~]# # 在 192.168.140.102 节点验证(非灰度规则的IP)
[root@master02 ~]#
[root@master02 ~]# curl http://test.app.com:8080/test/
test-app-v1
[root@master02 ~]# curl http://test.app.com:8080/test/
test-app-v1
[root@master02 ~]# curl http://test.app.com:8080/test/
test-app-v1
[root@master02 ~]#
[root@master02 ~]#
根据验证结果可以得知,可以通过 从 Istio VirtualService(简称 VS)从请求头(X-Forwarded-For/X-Real-IP)中提取客户端源 IP来实现基于客户端IP的恢复发布规则。
更多推荐
所有评论(0)