跳到主要内容

Istio DestinationRule 流量管控

· 阅读需 8 分钟

DestinationRule 是 VirtualService 路由生效后,配置应用于请求的策略集

dr 的作用:

  • 定义子集
  • 流量控制

![DestinationRule流量管控.png](/img/Istio DestinationRule 流量管控/DestinationRule流量管控.png)

DR的基本用法-定义子集

mkdir chap3 && cd chap3
cp chap2/vs.yaml chap2/mygw1.yaml chap3/

#清除之前环境的svc,再新建一个 svc 来负载 pod1 pod2
kubectl delete svc svc2
kubectl delete svc svc1

#给 pod1 pod2 设置同样的标签
kubectl label pod pod1 name=pod
kubectl label pod pod2 name=pod
kubectl get pods -l name=pod

#查看标签
kubectl get pods --show-label
#具有共同的标签 name=pod,不同的标签:run=pod1 ; run=pod2

# 虽然写的是pod1,但是还是根据selecter的标签
kubectl expose --name=svc1 pod pod1 --port=80 --selector=name=pod

#查看 svc1 关联的pod
kubectl describe svc svc1 #在Endpoints上可以看到已经关联了 pod1 pod2

# 编辑 vs yaml
vim vs.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1
subset: xx #subset是自定的标识
weight: 55
- destination:
host: svc1
subset: yy
weight: 45

# 启动 vs
kubectl apply -f vs.yaml

# 定义dr,编辑 dr yaml
vim dr.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1 #管理的是 svc1 的流量
subsets:
- name: xx #自定义,跟 vs 的 subset 做匹配
labels: #根据标签做匹配
#version: v1
run: pod1
- name: yy #自定义
labels:
#version: v2
run: pod2

# 启动 dr
kubectl apply -f dr.yaml
kubectl get dr

# 客户端测试
while true ; do curl aa.yuan.cc ; sleep 1 ; done

查看 kiali 权重图

![istio测试权重50](/img/Istio DestinationRule 流量管控/istio测试权重50.png)

蓝绿部署

# 定义dr,编辑 dr yaml
vim dr.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1 #管理的是 svc1 的流量
subsets:
- name: v1 #自定义,跟 vs 的 subset 做匹配
labels: #根据标签做匹配
#version: v1
run: pod1
- name: v2 #自定义
labels:
#version: v2
run: pod2

# 编辑 vs yaml
vim vs.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1
subset: v1 #subset是自定的标识
weight: 100
- destination:
host: svc1
subset: v2
weight: 0 # 在权重上做蓝绿部署,100 : 0

# 启动 vs,dr
kubectl apply -f dr.yaml
kubectl apply -f vs.yaml

# 查看 kiali 流量分布

金丝雀发布(灰度发布)

# 编辑 vs yaml
vim vs.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1
subset: v1
weight: 100
- destination:
host: svc1
subset: v2
weight: 0

# 流量的变化
1、v2 并没有对任何人开放
2、v2 对极少一部分人开放 #将权重从 100:0 切为 90:10
3、v2 给更多的人开放 #权重继续更改...

会话保持

会话保持的目的是,让同一个客户端访问的时候,访问到同一个pod上,istio用一致性哈希算法实现

了会话保持。

在trafficPolicy.loadBalancer.consistentHash里的字段包括:

  • httpHeaderName: 根据HTTP Header获取哈希值

  • httpCookie: 根据HTTP Cookie获取哈希值 (根据 Cookie 来做会话保持)

  • userSourceIp: 根据源IP获取哈希值 (根据 IP 地址来做的会话保持)

  • minimumRingSize: 哈希环所需的最小虚拟节点数量,默认值为1024

  • 使用httpCookie时的字段:

  • name: cookie的名称

  • path: 设置cookie的路径

  • ttl:cookie的生命期

while true ; do curl aa.rhce.cc --cookie "user=tester" --silent -w "Status: %{http_code}\n"; sleep 1 ; done
# 备份一下 vs yaml,恢复最初的 vs
mv vs.yaml vs-subset.yaml
cp ../chap2/vs.yaml .

# 编辑 vs yaml
vim vs.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1

# 启动 vs
kubectl apply -f vs.yaml

# 修改 dr yaml,添加 trafficPolicy: httpCookie:
vim dr.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
trafficPolicy:
loadBalancer:
consistentHash:
httpCookie:
name: user
ttl: 60s
subsets:
- name: v1
labels:
run: pod1
- name: v2
labels:
run: pod2

# 启动 dr
kubectl apply -f dr.yaml

# 客户端测试,需要带着 cookie 做测试
while true ; do curl --cookie "user=tester" aa.rhce.cc ; sleep 1 ; done
# 返回结果一直是 pod1的111 或者是 pod2的222,不会切换,如果 cookie 的 value 不是 user,则会话不保持:
while true ; do curl --cookie "user11=tester" aa.rhce.cc ; sleep 1 ; done #这是不保持的

基于 ip 地址的会话保持

# 修改 dr yaml,添加 trafficPolicy:
vim dr.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
trafficPolicy:
loadBalancer:
consistentHash:
useSourceIp: true #根据 IP 地址来做的会话保持
#httpCookie:
# name: user
# ttl: 60s
subsets:
- name: v1
labels:
run: pod1
- name: v2
labels:
run: pod2

# 启动 dr
kubectl apply -f dr.yaml

# 客户端测试
while true ; do curl aa.rhce.cc ; sleep 1 ; done # IP不变,返回结果是固定的

# 清理环境
cp dr.yaml dr-session.yaml
kubectl delete -f dr.yaml

DR调度算法-RANDOM-ROUND_ROBIN

# 编写 dr-arithmetic yaml
cp dr.yaml dr-arithmetic.yaml
vim dr-arithmetic.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
trafficPolicy:
loadBalancer:
simple: RANDOM #随机策略
#simple: ROUND_ROBIN #轮询策略

-------------------------------------------------
# 针对于多个 svc ,但只需对某个 svc 做策略
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
subsets:
- name: v1
labels:
run: pod1
- name: v2
labels:
run: pod2
trafficPolicy:
loadBalancer:
simple: RANDOM #随机策略
#simple: ROUND_ROBIN #轮询策略

DR调度算法-LEAST_CONN

kubectl get pod -o wide # 查看pod在哪个节点

# 编写 dr-arithmetic yaml
vim dr-arithmetic.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
trafficPolicy:
loadBalancer:
simple: LEAST_CONN #当进行轮训的时候,先检测哪个pod负载较轻, 就把流量转发到哪个pod上去。

# 重启 dr arithmetic
kubectl apply -f dr-arithmetic.yaml

#在对应节点安装 webbench 压测工具
yum install ctags* gcc -y
mkdir -p -m 644 /usr/local/man/man1

wget wget http://www.ha97.com/code/webbench-1.5.tar.gz
tar zxvf webbench-1.5.tar.gz ; cd webbench-1.5

make && make install

# 查看 pod ip
kubectl get pods -owide

# 压测 pod2
webbench -c 500 -t 300 http://10.244.223.203/

# 查看 kiali
# 清理环境
kubectl delete -f dr-arithmetic.yaml

熔断

熔断的定义是在DR里定义的,主要有2部分

第一部分:

trafficPolicy:

用于定义连接池

连接池的定义分成两类:

  • TCP 连接
    • maxConncections: 到目标主机的最大连接数
    • connectTimeOut: TCP连接超时,最小值必须要大于1ms
  • Http 连接
    • http1MaxPendingReguests: 针对一个目标的HTTP请求最大排队数量,默认是1024
    • http2MaxRequests: 对一个后端的最大请求数
    • maxRequestsPerConnection:
    • maxReties: 在给定的时间,集群所有主机最大重试数,默认值为3

第二部分:

outlierDetection:

​ 用于定义熔断的条件,达到什么条件就开始熔断

  • consecutiveErrors: 超过这错误数量之后,主机会被移除连接池。默认是5,当上游服务是

    http服务时,5xx返回代码会记录为错误。当上游主机提供的是TCP服务时,TCP连接超时和连接错

    误被标记为错误。

  • interval: 在移除检测之间的时间间隔,默认是10s,必须要>=1ms

  • baseEjectionTime: 最小的移除时间长度。主机每次被移除后的间隔时间等于被移除的次数和

    最小移除时间的乘积。这样的实现,让系统能够自动增加不健康上游服务实例的间隔时间,默认时

    间为30s。

  • maxEjectionPercent: 上游服务的负载均衡池中允许被移除的主机的最大百分比,默认是10%。

# 编辑 dr rongduan 
vim dr-rongduan.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 1 #针对一个目标的HTTP请求最大排队数量,最大为1(根据业务而设置)
maxRequestsPerConnection: 1
outlierDetection:
consecutiveGatewayErrors: 1
interval: 10s
baseEjectionTime: 3m
maxEjectionPercent: 100
#subsets:
#- name: v2
# labels:
# run: pod2

# 启动 dr-rongduan
kubectl apply -f dr-rongduan.yaml

# 下载 fortio 测试
rpm -i https://github.com/fortio/fortio/releases/download/v1.30.0/fortio-1.30.0-1.x86_64.rpm

# fortio测试
fortio load -c 5 -n 20 -qps 0 http://aa.yuan.cc
# 命令解释如下: -c 表示并发数 -n 一共多少请求 -qps 每秒查询数,0 表示不限制
# 可以看到 Code 200:(25.0 %) ; Code 503:(75.0 %)

# 如果对 dr-rongduan.yaml 进行修改 http1MaxPendingRequests: 5,那么再次执行 fortio 并发为 5,则 Code 状态码都为 200

Brew 安装 Nginx

· 阅读需 1 分钟

brew 安装 nginx

  • 由于 brew 安装的 nginx 系统命令和 linux systemd 管理的有所区别,做一个小记录
# 安装 nginx
brew install nginx

nginx #启动服务
nginx -s stop # 停止服务
nginx -s reload # 重新加载
nginx -s reopen # 重新启动
nginx -s quit # 退出

/usr/local/etc/nginx # nginx安装目录

# 如果你不想/需要后台服务,你可以运行:
/usr/local/opt/nginx/bin/nginx -g daemon off

Istio VirtualService 流量管控

· 阅读需 8 分钟

架构示意图 ![VirtualService流量管理.png](/img/Istio Virtualservice 流量管控/VirtualService流量管理.png)

创建网关

Gateway 为HTTP/TCP 流量配置负载均衡器,最常见的是在网格边缘的操作,以启用应用程序的入口流量

创建 gateway

mkdir chap2
cd chap2
cp ../chap1/pod1.yaml ./

kubectl exec -it pod1 -- sh -c "echo 111 > /usr/share/nginx/html/index.html"
kubectl get pods -o wide
curl -s 10.244.223.200
#curl出111

kubectl expose --name=svc1 pod pod1 --port=80

kubectl get svc -n istio-system

kubectl get gw #查看gateway 并没有,需要创建
# No resource found in ns1 namespace

# 编辑 gateway yaml
vim mygw1.yaml

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mygw
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http-1
protocol: HTTP
hosts:
- "aa.yuan.cc"
# 网关里的hosts必须要满足FQDN

# 可以看到我们的路口网关,与上面的创建的gateway的标签是匹配上的
kubectl get pods -n istio-system -l istio=ingressgateway

# 创建 gateway
kubectl apply -f mygw1.yaml
kubectl get gw

virtualService 的基本使用

创建 vs1

kubectl get vs 
# No resource found in ns1 namespace

# 编写 vs1 yaml
vim vs1.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1

# 创建 vs1
kubectl apply -f vs1.yaml
kubectl get vs

测试流量是否跑通

# 找一个服务器客户端
vim /etc/hosts
#写入口网关规定的ip地址
192.168.15.230 aa.yuan.cc aa
192.168.15.230 bb.yuan.cc bb

curl aa.rhce.cc
# 111
curl -I bb.rhce.cc
# bb 是访问不了的,404 ; 因为在网关中没有定义 bb

# 在gateway 中添加 bb 的配置
vim mygw1.yaml

- port:
number: 80
name: http-2
protocol: HTTP
hosts:
- "bb.yuan.cc"

# 重新启动
kubectl apply -f mygw1.yaml

# 此时再次访问 bb.yuan.cc 可以访问通嘛? 还是不行的,因为 vs 没有定义,要不你需要在原本 vs 中加上 bb 的 hosts,要不你就需要再添加一个 vs 来指明 bb
curl -I bb.rhce.cc

# 编写 vs1 yaml
vim vs1.yaml

spec:
hosts:
- "aa.yuan.cc"
- "bb.yuan.cc"

# 重新启动
kubectl apply -f vs1.yaml
curl -I bb.rhce.cc

-------------------------------------------------------
上述是为了测试,如果遇到这种需求可以使用通配符,在gateway yaml中将hosts改为:”*.yuan.cc“

带有权重的 virtualService

创建 svc2

# 创建 pod2
sed 's/pod1/pod2/' pod1.yaml | kubectl apply -f -
kubectl get pods

# 更改 pod2 web 页面
kubectl exec -it pod2 -- sh -c "echo 222 > /usr/share/nginx/html/index.html"

# 创建 svc2
kubectl expose --name=svc2 pod pod2 --port=80
kubectl get svc
curl <CLUSTER IP> # 222

修改 vs1

# 编写 vs1 yaml,添加 svc2,并设置权重
vim vs1.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1
weight: 65
- route:
- destination:
host: svc2
weight: 35

# 重新启动 vs1
kubectl apply -f vs1.yaml

测试 vs 权重

# 在客户端服务器中访问请求
while true ; do
> curl aa.yuan.cc ; sleep 1
> done

# 去 kiali 上添加上 istio-system 的 namespace ,在 Show Edge Labels 中勾选 请求分布

查看 kiali 权重图

故障注入

fault : delay 测试svc1的延迟

vim vs1.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- fault:
delay:
percent: 100
fixedDelay: 1s
route:
- destination:
host: svc1
#此route需要在fault里

# 重启vs1
kubectl apply -f vs1.yaml

# 在客户端进行测试
curl aa.yuan.cc. #有了一定的延迟,延期了 fixedDelay 的 1s 的时间

fault : abort 模拟中断

vim vs1.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- fault:
#delay:
# percent: 100
# fixedDelay: 1s
abort:
percentage:
value: 100
httpStatus: 503
route:
- destination:
host: svc1

# 重启 vs1
kubectl apply -f vs1.yaml

# client 测试访问
curl -I aa.yuan.cc # 返回503

超时

创建 pod 和 svc

sed 's/pod1/pod3/' pod1.yaml | kubectl apply -f -
sed 's/pod1/pod4/' pod1.yaml | kubectl apply -f -
kubectl get pods

#把 pod4 nginx界面改为444
kubectl exec -it pod4 -- sh -c "echo 444 > /usr/share/nginx/html/index.html"

# 创建 svc
kubectl expose --name=svc3 pod pod3 --port=80
kubectl expose --name=svc4 pod pod4 --port=80

# 修改 pod3 中 nginx 配置文件
kubectl cp pod3:/etc/nginx/conf.d/default.conf default.conf
vim default.conf

location / {
#root /usr/share/nginx/html;
#index index.html index.htm
proxy_pass http://svc4;
proxy_http_version 1.1;
}

kubectl cp default.conf pod3:/etc/nginx/conf.d/default.conf
kubectl exec -it pod3 -- bash
nginx -t
nginx -s reload

# 测试结果
kubectl get svc
curl `svc3的IP` # 会显示 svc4 -> pod4 的444结果

创建 vs2

kubectl get vs
#先删除之前用的vs1
kubectl delete -f vs1.yaml

# 创建vs2
vim vs2.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs1
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc3 #映射到svc3
timeout: 3s

# 启动 vs2
kubectl apply -f vs2.yaml

# client 服务器执行测试
curl aa.yuan.cc. #444,目前还是正常的

创建 vs3

vim vs3.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs2
spec:
hosts:
- "svc4"
http:
- fault:
delay:
percent: 100
fixedDelay: 2s
route:
- destination:
host: svc4

# 启动 vs3
kubectl apply -f vs3.yaml

# client 服务器执行测试
curl aa.yuan.cc #444,目前还是正常的,只是有 2s 的延迟

#修改 vs3 的延迟时间
vim vs3.yaml

fixedDelay: 5s

# 配置生效
kubectl apply -f vs3.yaml

# client 服务器执行测试
curl -I aa.yuan.cc #失败

重试

# 修改 vs2 yaml
vim vs2.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs1
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc3 #映射到svc3
timeout: 3s
retries:
attempts: 3 #重试三次
perTryTimeout: 3s #指在这个时间之内去重试,如果超过这个时间就不会再去重试
retryOn: 5xx #如果vs3返回此状态吗,那么则重试3次

# 修改 vs3 yaml
vim vs3.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs2
spec:
hosts:
- "svc4"
http:
- fault:
#delay:
# percent: 100
# fixedDelay: 2s
abort:
percentage:
value: 100
httpStatus: 503
route:
- destination:
host: svc4

# 重启 vs2 vs3
kubectl apply -f vs2.yaml
kubectl apply -f vs3.yaml

# 查看日志
kubectl logs -f pod3
# 客户端访问
curl aa.yuan.cc
# 切回 pod3 日志,可以看到503的日志,并且是访问一次,重试了3次

# 清理环境
kubectl delete pod pod3
kubectl delete pod pod4
kubectl delete svc svc3
kubectl delete svc svc4
kubectl delete -f vs2.yaml
kubectl delete -f vs3.yaml

http 重写

创建 vs , 还原一下环境

kubectl get vs
#No resources found in ns1 namespace
kubectl get gw
#mygw
kubectl get pod
#pod1
#pod2
kubectl get svc
#svc1
#svc2

cp vs1.yaml vs.yaml
# 修改 vs yaml
vim vs.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1

# 启动 vs
kubectl apply -f vs.yaml

修改 vs


# 在 pod1 nginx 中添加 html文件
kubectl exec -it pod1 -- bash
cd /usr/share/nginx/html
mkdir demo1 demo2
echo aaa > demo1/index.html
echo bbb > demo2/index.html

#客户端测试
curl aa.yuan.cc/demo1/index.html
curl aa.yuan.cc/demo2/index.html

# 创建 vs-rewrite yaml
cp vs.yaml vs-rewrite.yaml

vim vs-rewrite.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- match:
- uri:
prefix: /demo1/
rewrite:
uri: /demo2/ #从demo1跳转至demo2
route:
- destination:
host: svc1

# 启动 vs-rewrite
kubectl apply -f vs-rewrite.yaml

# 客户端测试,或者去 kiali 查看
curl aa.yuan.cc/demo1/index.html #返回bbb
curl aa.yuan.cc/demo2/index.html #没有返回,代表demo2不会匹配上

#如果说svc1的 route 在 match 之上,那么 match 规则 则不会生效,在route上就已经路由过去了
http:
- route:
- destination:
host: svc1
- match:
- uri:
prefix: /demo1/
rewrite:
uri: /demo2/

基于内容转发

cp vs.yaml vs-content.yaml

#编写 vs-content yaml
vim vs-content.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- match:
- headers:
User-Agent:
regex: .*(Chrome/([\d.]+)).* #通过 chrome 访问的则访问 svc1,否则访问 svc2
route:
- destination:
host: svc1
- route:
- destination:
host: svc2

## 客户端测试
#模拟非 chrome
curl --user-agent "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTMLme/31.0.1650.63 Safari/537.36" -I aa.yuan.cc
# 返回 222,反问到了 pod2

#模拟 chrome
curl --user-agent 'Chrome/54.0 (Windows NT 10.0)' aa.yuan.cc
#返回 111,访问到了 pod1

影子测试(流量镜像)

cp vs.yaml vs-mirror.yaml

# 编辑 vs-mirror
vim vs-mirror.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1
mirror:
host: svc2

# 启动 vs-mirror
kubectl apply -f vs-mirror.yaml

# 客户端测试,查看pod2是否有复制一份流量过来
curl --user-agent 'Chrome/54.0 (Windows NT 10.0)' aa.yuan.cc
kubectl logs -f pod2