🏆 跟着 Bookinfo
项目学习 Istio
的功能
🏢 官方介绍: https://istio.io/v1.18/zh/docs/examples/bookinfo/
这个应用模仿在线书店的一个分类,显示一本书的信息。 页面上会显示一本书的描述,书籍的细节(ISBN、页数等),以及关于这本书的一些评论。
Bookinfo
应用分为四个单独的微服务:
productpage
:这个微服务会调用details
和reviews
两个微服务,用来生成页面。details
:这个微服务中包含了书籍的信息。reviews
:这个微服务中包含了书籍相关的评论。它还会调用ratings
微服务。ratings
:这个微服务中包含了由书籍评价组成的评级信息。
reviews
微服务有 3 个版本:
- v1 版本不会调用
ratings
服务。 - v2 版本会调用
ratings
服务,并使用 1 到 5 个黑色星形图标来显示评分信息。 - v3 版本会调用
ratings
服务,并使用 1 到 5 个红色星形图标来显示评分信息。
Bookinfo 应用中的几个微服务是由不同的语言编写的。 这些服务对 Istio 并无依赖,但是构成了一个有代表性的服务网格的例子: 它由多个服务、多个语言构成,并且 reviews
服务具有多个版本
由于开始安装的时候就部署了 bookinfo
并且注入了 enovy
,可以进入 kiali
查看 graph
,选择 default
名称空间,可以看到如下图,
🌟 开始使用
下面配置在
istio
的samples
中的bookinfo
中都有模板,可以根据自身实际需求修改
🍪 配置bookinfo应用
架构流程图
1️⃣ 由于是使用 kind
搭建 K8S
在搭建 Istio
,需要使用 ingress
代理 istio-ingressGateway
(单独的 Envoy
代理)的 80
端口如下配置,此配置的意思是访 问bookinfo.istio.io
的流量全部丢给 istio-ingressgateway
这个 svc
,而这个 svc
是作用在 istio
的网关代理上的
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: istio-ingressgateway
namespace: istio-system ##注意名称空间
labels:
app: istio-ingressgateway
spec:
ingressClassName: nginx
rules:
- host: bookinfo.istio.io
http:
paths:
- backend:
service:
name: istio-ingressgateway ## 代理istio的出口网关
port:
number: 80
path: /
pathType: ImplementationSpecific
2️⃣ 当配置 ingress
将 bookinfo.istio.io
请求丢该 istio
网关代理之后,istio
网关代理并不知道如何处理这个请求,此时就需要配置网关配置和虚拟服务了,网关代理配置如下,表示网关会监听去往 bookinfo.istio.io
的路由,从 label
为 istio: ingressgateway
网关的 80
端口
root@Tc-Server:/opt/istio/bookinfo-test# cat bookinfo-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: bookinfo-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "bookinfo.istio.io"
监听到要去往"
bookinfo.istio.io
"的流量后,网关现在并不知道发往哪里,需要配置 VirtualService
来匹配这些路由,配置如下,表示来自 bookinfo-gateway
网关配置的 bookinfo.istio.io
的流量且路径为 /productpage
、/static
,/login
....的流量发往 productpage
这个 svc
的 9080
端口
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
name: bookinfo
spec:
hosts:
- "bookinfo.istio.io"
gateways:
- bookinfo-gateway
http:
- match:
- uri:
exact: /productpage
- uri:
prefix: /static
- uri:
exact: /login
- uri:
exact: /logout
- uri:
prefix: /api/v1/products
route:
- destination:
host: productpage
port:
number: 9080
此时配置完成后就可以通过http://bookinfo.istio.io/productpage 访问了,并且持续刷新会发现星星要么没有要么颜色不一样;
⚠️ 注意这里需要修改hosts文件
127.0.0.1 bookinfo.istio.io
,在次注意本项目是在windows
中使用wsl
,并且使用kind
创建的K8S
集群
使用如下命令让他一直循环访问
while true; do sleep 1;curl -s -o /dev/null http://bookinfo.istio.io/productpage; done
进入 kiali
就会得到如下图
接下来就可以愉快的学习和操作 istio
了
🍰 使用 Istio
实现灰度发布
解释灰度发布: https://support.huaweicloud.com/usermanual-asm/asm_01_0035.html
很疑惑?为什么会没有
destinationRule
呢,不是说要和 VirtualService
配合使用吗?为什么 VirtualService
出现了它还没有出现?别急接下来他就要大杀四方了
什么是灰度发布? 比如一个应用需要上线一个新版本,如果直接从旧版本切换到新版本,一旦发生
Bug
,对用户的影响是非常大的, 有时候遇到长时间无法解决的棘手 bug
,就不得不回滚到旧版本;现在有种解决办法就是先把部分小流量引入新版本,比如将百分之10的请求给新版本,90给旧,然后一段时间后增加到30给新版本,然后50,然后70....以致流量全部推至新版本,这可以初略的理解为灰度发布,也叫金丝雀发布
接下来旧通过
DestinationRule
和 VirtualService
去实现 bookinfo
应用的灰度发布,首先 reviews
有三个不同版本,假设 v1
版本为初始版本,接下来要上线的为 v2
版本
1️⃣ 使用 DestinationRule
创建一条 reviews
的目标规则并且将不同版本分组,首先查看一下他们的 labels
,他们的 version
分别对应了不同版本
root@Tc-Server:/opt/istio/bookinfo-test# kubectl get pod --show-labels | grep reviews|grep version
reviews-v1-777df99c6d-c85lh 2/2 Running 2 (4h38m ago) 23h app=reviews,pod-template-hash=777df99c6d,security.istio.io/tlsMode=istio,service.istio.io/canonical-name=reviews,service.istio.io/canonical-revision=v1,version=v1
reviews-v2-cdd8fb88b-v4x2k 2/2 Running 2 (4h38m ago) 23h app=reviews,pod-template-hash=cdd8fb88b,security.istio.io/tlsMode=istio,service.istio.io/canonical-name=reviews,service.istio.io/canonical-revision=v2,version=v2
reviews-v3-58b6479b-gx8xv 2/2 Running 2 (4h38m ago) 23h app=reviews,pod-template-hash=58b6479b,security.istio.io/tlsMode=istio,service.istio.io/canonical-name=reviews,service.istio.io/canonical-revision=v3,version=v3
接下来通过这些
labels
将他们分组,也就是发往 reviews
的目标规则被分成了 3
组,之前默认是轮询负载均衡,有了这些就可以控制一些更加精细的步骤了
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: bookinfo-destaionrule-all
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3
2️⃣ 上层可以使用 VirtualService
来控制这些目标规则,比如现在公司使用的就是旧版本 V1
,也就是所有往 reviews
(这个hosts必须存在在 kubernetes
的注册中心,也就是存在于 kubernetes
的 svc
)的路由都往 v1
去,配置如下
注意
hosts
指定了这个VirtualService
规则应用到的目标服务,必须存在于kubernetes
,而host
指使用哪条DestinationRule
的主机名,比如上面那条目标规则的host
是reviews
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
配置完成可以查看
kiali
面板(也可以去浏览器直接访问,会发现没有星星了),会发现路由规则只到 v1
,而 v1
不调用 ratings
所以不会出现星星
2️⃣ 好此时 v2
(boom!v2
版本全新上线,一闪一闪亮晶晶,书籍评论增加星星,可以直观感受到书的评分)版本推出上线,我之前说过不可能直接将所有流量都引入 v2
,可以先将百分之20的流量引入,监测一下稳定性,配置如下,使用 weight
配置权重即可
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 80
- destination:
host: reviews
subset: v2
weight: 20
此时你在去浏览器刷新就会又百分之 30
的记录可以刷出小星星(黑色的)了,进入 kiali
查看可以看到路由以及分配到 v2
了,但是也是仅仅百分之20左右
然后就可以继续调整策略不断推进50-50
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 50
- destination:
host: reviews
subset: v2
weight: 50
20-80
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 20
- destination:
host: reviews
subset: v2
weight: 80
最后直接到 v2
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v2
🥘 实现 A/B
测试
名词解释: https://www.oracle.com/cn/cx/marketing/what-is-ab-testing/
现在需要发布
v3
版本,v3
版本将黑色小星星改为了红色,在不确定用户是否喜欢或者能够接收这个红色小星星的情况下我就需要用户到 A/B
测试,先招聘一部分人内测,这部分内测人员使用 v3
版本,而其余普通用户则继续访问 v2
版本,直到 V3
版本,反馈较好且无问题,就全部切换为 v3
1️⃣ 现在我招到一名内测人员他叫 jason
,现在需要做的就是将 jason
用户的路由丢给 reviews v3
,配置如下,可以通过匹配请求头部来匹配
Istio
对用户身份没有任何特殊的内置机制。事实上,productpage
服务在所有到reviews
服务的HTTP
请求中都增加了一个自定义的end-user
请求头,
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v3
- route:
- destination:
host: reviews
subset: v2
接下来在浏览器中登录 jason
用户,没有密码直接输入用户即可
登录之后小星星变红了,jason
开启了它的内测之旅
💣 Istio
实现故障注入
在分布式运算中有八宗罪:
- 网络是可靠的
- 带宽是无限的
- 网络是安全的
- 拓扑结构是一成不变的
- 总会有一个管理员
- 不必考虑传输成本
- 网络都是同质化的
在程序运行中可能一个短暂的网络波动、依赖的第三方服务有问题等都会拖垮自身服务,这样就需要测试服务的健壮性,
Istio
可以使用故障注入来模拟一些故障,比如注入延迟或者注入中断等
1️⃣ 注入延迟
🏢 https://istio.io/latest/zh/docs/tasks/traffic-management/fault-injection/
由于 reviewsv2
和 v3
都需要指向 ratings v1
,为了测试它的的弹性,向它注入一个延迟,这个配置将 jason
用户的请求注入一个7秒的延迟,普通用户不做任何限制,percentage
百分之多少的请求会延迟
注意
reviews:v2
服务对ratings
服务的调用具有 10 秒的硬编码连接超时。 因此,尽管引入了 7 秒的延迟,它应该仍然是没有任何错误的,顶多加载慢
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- match:
- headers:
end-user:
exact: jason
fault:
delay:
fixedDelay: "7s"
percentage:
value: 100
route:
- destination:
host: ratings
subset: v1
- route:
- destination:
host: ratings
subset: v1
打开浏览器登录 jason
用户会发现 ratings
部分显示 unavailable
我们引入的 7 秒延迟不会影响到
reviews
服务,因为 reviews
和 ratings
服务间的超时被硬编码为 10 秒。但是,在 productpage
和 reviews
服务之间也有一个3秒的硬编码的超时,再加1次重试,一共6秒。结果,productpage
对 reviews
的调用在6秒后提前超时并抛出错误了,这样整个 Bug
就被排查出来了
🏢 官网的解决办法:
但是,
reviews
服务的v3
版本已经修复了这个问题。reviews:v3
服务已将reviews
与ratings
的超时时间从 10 秒降低为 2.5 秒,因此它可以兼容(小于)下游productpage
请求的超时时间。如果您按照流量转移任务所述将所有流量转移到
reviews:v3
, 您可以尝试修改延迟规则为任何低于 2.5 秒的数值,例如 2 秒,然后确认端到端的流程没有任何错误
2️⃣ 注入中断
如果我就像看看这个服务出现故障是什么情况,可以直接为 rating
注入一个中断,如下代码即可
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- match:
- headers:
end-user:
exact: jason
fault:
delay:
fixedDelay: "7s"
percentage:
value: 100
route:
- destination:
host: ratings
subset: v1
- route:
- destination:
host: ratings
subset: v1
进入浏览器不会出现延迟等待则直接出现了 unavailable
⏲ 防止请求超时
如果一个你调用一个外部服务的时候,发现外部服务不可用或者很慢的情况,以为你调用这个外部服务慢可能会导致整个系统速度慢或者出错(就像注入延迟部分),这时候就可以使用
istio
来对这些请求慢或者不成功的配置一个超时时间,一旦超过这个时间我就会放弃这次请求;或者自动触发第二次请求尝试等
1️⃣ 先给 rating
注入一个 2s
的延迟时间(注入此时 jason
用户的路由时去往 reviews-v3
的),2s
延时时间不会让 ratings unavailable
,而是会正常显示
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- match:
- headers:
end-user:
exact: jason
fault:
delay:
fixedDelay: "2s"
percentage:
value: 100
route:
- destination:
host: ratings
subset: v1
- route:
- destination:
host: ratings
subset: v1
2️⃣ 注入一个请求超时时间,如果请求时间一旦大于 0.5s
立即终止请求
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v3
timeout: 0.5s
此时再去浏览器访问,不会延迟很久刷新一下 1s
就可以看见页面,但是 ratings
是不可用状态,,在注入延迟中提到 productpage
和 reviews
之间也有一个 3s
的延迟时间,如果将超时设置为大于 3 秒(比如 4 秒), 则超时将不会有任何影响
还有一点关于 Istio 中超时控制方面的补充说明,除了像本文一样在路由规则中进行超时设置之外, 还可以进行请求一级的设置,只需在应用的对外请求中加入
x-envoy-upstream-rq-timeout-ms
请求头即可。在这个请求头中的超时设置单位是毫秒而不是秒。
💥 熔断操作
🏢https://istio.io/latest/zh/docs/tasks/traffic-management/circuit-breaking/#see-also
熔断器又叫断路器,是一种保护机制,用于保护服务调用链路中的服务不被过多的请求压垮。当服务调用链路中的某个服务出现异常时,断路器会将该服务的调用请求拒绝,从而保护服务调用链路中的其他服务不被压垮。
1️⃣ 在 istio
中可用配置熔断规则来对请求进行”熔断“,比如如下配置对 rating
进行熔断配置,具体配如下
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: ratings
spec:
host: ratings
trafficPolicy:
connectionPool:
tcp:
maxConnections: 3 ##并发数量
http:
http1MaxPendingRequests: 1 ##最大带处理请求数
maxRequestsPerConnection: 1 ##每个请求最大的连接数
outlierDetection: ##熔断探测配置
consecutive5xxErrors: 1 ##如果连续出现的错误超过一次,就会被熔断
interval: 10s ##每10s探测一次后端实列
baseEjectionTime: 3m ##熔断时间
maxEjectionPercent: 100 ##被熔断实列最大的一次百分比
subsets:
- labels:
version: v1
name: v1
配置完成后依然可用正常在浏览器中请求,此时就需要部署一个 fortio
来测试
2️⃣ 部署 fortio
,在 httpbin
服务目录中有
kubectl apply -f istio-1.18.2/samples/httpbin/sample-client/fortio-deploy.yaml
##查看是否成功部署
root@Tc-Server:/opt/istio/bookinfo-test# kubectl get pod | grep fortio
fortio-deploy-585989db7f-vgmdp 2/2 Running 0 15m
##部署完成之后测试是否可用正常访问 ratings
服务
root@Tc-Server:/opt/istio/bookinfo-test# kubectl exec fortio-deploy-585989db7f-vgmdp -c fortio -- /usr/bin/fortio curl -quiet http://ratings:9080/ratings/0
30
{"id":0,"ratings":{"Reviewer1":5,"Reviewer2":4}}
0
HTTP/1.1 200 OK
content-type: application/json
date: Sun, 27 Oct 2024 14:37:59 GMT
x-envoy-upstream-service-time: 9
server: envoy
transfer-encoding: chunked
接下来就可以开始测试熔断是否配置成功,还是使用 fortio
-c
表示并发数,-n
表示请求数,这里先发起1并发20请求查看,重点查看 Code
,可以看到 100%
,访问成功
Code 200 : 20 (100.0 %)
增加到2-20
Code 200 : 14 (70.0 %)
Code 503 : 6 (30.0 %)
可以看到有 503
的请求,说明触发了熔断可以查看详情
root@Tc-Server:/opt/istio/bookinfo-test# kubectl exec fortio-deploy-585989db7f-vgmdp -c istio-proxy -- pilot-agent request GET stats | grep ratings | grep pending
cluster.outbound|9080|v1|ratings.default.svc.cluster.local.circuit_breakers.default.remaining_pending: 1
cluster.outbound|9080|v1|ratings.default.svc.cluster.local.circuit_breakers.default.rq_pending_open: 0
cluster.outbound|9080|v1|ratings.default.svc.cluster.local.circuit_breakers.high.rq_pending_open: 0
cluster.outbound|9080|v1|ratings.default.svc.cluster.local.upstream_rq_pending_active: 0
cluster.outbound|9080|v1|ratings.default.svc.cluster.local.upstream_rq_pending_failure_eject: 0
cluster.outbound|9080|v1|ratings.default.svc.cluster.local.upstream_rq_pending_overflow: 0
cluster.outbound|9080|v1|ratings.default.svc.cluster.local.upstream_rq_pending_total: 0
cluster.outbound|9080||ratings.default.svc.cluster.local.circuit_breakers.default.remaining_pending: 1
cluster.outbound|9080||ratings.default.svc.cluster.local.circuit_breakers.default.rq_pending_open: 0
cluster.outbound|9080||ratings.default.svc.cluster.local.circuit_breakers.high.rq_pending_open: 0
cluster.outbound|9080||ratings.default.svc.cluster.local.upstream_rq_pending_active: 0
cluster.outbound|9080||ratings.default.svc.cluster.local.upstream_rq_pending_failure_eject: 0
cluster.outbound|9080||ratings.default.svc.cluster.local.upstream_rq_pending_overflow: 105
cluster.outbound|9080||ratings.default.svc.cluster.local.upstream_rq_pending_total: 196
ratings.default.svc.cluster.local.upstream_rq_pending_overflow: 105
表示触发了 105
次熔断