🏆 跟着 Bookinfo项目学习 Istio的功能

🏢 官方介绍: https://istio.io/v1.18/zh/docs/examples/bookinfo/

这个应用模仿在线书店的一个分类,显示一本书的信息。 页面上会显示一本书的描述,书籍的细节(ISBN、页数等),以及关于这本书的一些评论。

Bookinfo 应用分为四个单独的微服务:

  • productpage:这个微服务会调用 detailsreviews 两个微服务,用来生成页面。
  • details:这个微服务中包含了书籍的信息。
  • reviews:这个微服务中包含了书籍相关的评论。它还会调用 ratings 微服务。
  • ratings:这个微服务中包含了由书籍评价组成的评级信息。

reviews 微服务有 3 个版本:

  • v1 版本不会调用 ratings 服务。
  • v2 版本会调用 ratings 服务,并使用 1 到 5 个黑色星形图标来显示评分信息。
  • v3 版本会调用 ratings 服务,并使用 1 到 5 个红色星形图标来显示评分信息。

Bookinfo Application without Istio

Bookinfo 应用中的几个微服务是由不同的语言编写的。 这些服务对 Istio 并无依赖,但是构成了一个有代表性的服务网格的例子: 它由多个服务、多个语言构成,并且 reviews 服务具有多个版本

由于开始安装的时候就部署了 bookinfo并且注入了 enovy,可以进入 kiali查看 graph,选择 default名称空间,可以看到如下图,

image-20241026221509353

🌟 开始使用

下面配置在 istiosamples中的 bookinfo中都有模板,可以根据自身实际需求修改

🍪 配置bookinfo应用

架构流程图

image-20241027182443508

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️⃣ 当配置 ingressbookinfo.istio.io请求丢该 istio网关代理之后,istio网关代理并不知道如何处理这个请求,此时就需要配置网关配置和虚拟服务了,网关代理配置如下,表示网关会监听去往 bookinfo.istio.io的路由,从 labelistio: 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这个 svc9080端口

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就会得到如下图

image-20241027144522970

接下来就可以愉快的学习和操作 istio

🍰 使用 Istio实现灰度发布

image-20241027183718742

解释灰度发布: https://support.huaweicloud.com/usermanual-asm/asm_01_0035.html

很疑惑?为什么会没有

destinationRule呢,不是说要和 VirtualService配合使用吗?为什么 VirtualService出现了它还没有出现?别急接下来他就要大杀四方了

什么是灰度发布? 比如一个应用需要上线一个新版本,如果直接从旧版本切换到新版本,一旦发生

Bug,对用户的影响是非常大的, 有时候遇到长时间无法解决的棘手 bug,就不得不回滚到旧版本;现在有种解决办法就是先把部分小流量引入新版本,比如将百分之10的请求给新版本,90给旧,然后一段时间后增加到30给新版本,然后50,然后70....以致流量全部推至新版本,这可以初略的理解为灰度发布,也叫金丝雀发布

接下来旧通过

DestinationRuleVirtualService去实现 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的注册中心,也就是存在于 kubernetessvc)的路由都往 v1去,配置如下

注意 hosts指定了这个 VirtualService 规则应用到的目标服务,必须存在于 kubernetes,而 host指使用哪条 DestinationRule的主机名,比如上面那条目标规则的 hostreviews

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews 
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
配置完成可以查看

kiali面板(也可以去浏览器直接访问,会发现没有星星了),会发现路由规则只到 v1,而 v1不调用 ratings所以不会出现星星

image-20241027160923476

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左右

image-20241027162705137

然后就可以继续调整策略不断推进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测试

image-20241027191704704

名词解释: 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用户,没有密码直接输入用户即可

image-20241027190609685

登录之后小星星变红了,jason开启了它的内测之旅

image-20241027190658745

💣 Istio实现故障注入

在分布式运算中有八宗罪:

  1. 网络是可靠的
  2. 带宽是无限的
  3. 网络是安全的
  4. 拓扑结构是一成不变的
  5. 总会有一个管理员
  6. 不必考虑传输成本
  7. 网络都是同质化的
在程序运行中可能一个短暂的网络波动、依赖的第三方服务有问题等都会拖垮自身服务,这样就需要测试服务的健壮性,

Istio可以使用故障注入来模拟一些故障,比如注入延迟或者注入中断等

1️⃣ 注入延迟

🏢 https://istio.io/latest/zh/docs/tasks/traffic-management/fault-injection/

由于 reviewsv2v3都需要指向 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

image-20241027204935506
我们引入的 7 秒延迟不会影响到

reviews 服务,因为 reviewsratings 服务间的超时被硬编码为 10 秒。但是,在 productpagereviews服务之间也有一个3秒的硬编码的超时,再加1次重试,一共6秒。结果,productpagereviews的调用在6秒后提前超时并抛出错误了,这样整个 Bug就被排查出来了

🏢 官网的解决办法:

但是,

reviews 服务的 v3 版本已经修复了这个问题。 reviews:v3 服务已将 reviewsratings 的超时时间从 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是不可用状态,,在注入延迟中提到 productpagereviews之间也有一个 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次熔断