Skip to content

第9章-Istio微服务

9.1 微服务基本介绍

9.2 微服务架构发展进程

9.3 Istio 概念、架构、组件详细介绍

9.4 k8s 平台安装 Istio

9.5 k8s 集群部署 bookinfo 在线书店,开启 istio 功能

9.6 通过 Istio 实现灰度发布

9.7 istio 核心功能演示

9.1 微服务基本介绍

​ 微服务是一种软件架构模式,它将大型的单一应用程序拆分成一组小型、独立的服务。每个服务都专注于执行一个特定的业务功能,并通过轻量级的通信机制进行相互协作。

​ 我们可以将大型的单一应用程序比喻为一座大厦,而微服务则是这座大厦中的许多独立的房间。每个房间代表一个微服务,拥有自己独立的功能和责任。这些微服务之间通过简单的方式进行通信,就像房间之间的门一样。

​ 微服务的好处在于它们的独立性和可扩展性。由于每个微服务都相对较小且专注于一个特定的任务, 开发团队可以更加灵活地独立开发、部署和扩展这些服务。此外,微服务可以使用不同的技术栈和编程语言,因为它们之间是通过通用的接口进行通信。

​ 总而言之,微服务是一种将大型应用程序拆分为小型、独立服务的架构模式,通过简单的通信机制使这些服务相互协作。它们提供了灵活性、可扩展性和模块化的好处,有助于构建可靠、可维护和高效的软件系统。

9.2 微服务架构发展进程

1、单体应用阶段:最初阶段,应用程序作为一个单一的、整体的实体进行开发和部署。所有的功能和业务逻辑都集中在一个应用程序中,这种架构简单直接,但随着应用程序的不断增长和复杂性的提高, 会面临扩展和维护困难的挑战。

2、服务拆分阶段:为了应对单体应用的扩展和维护困难,开始将应用程序拆分为一些较小的、独立的服务。这些服务通常按照业务功能或领域进行划分,每个服务具有自己的数据库和业务逻辑。这种拆分可以提高开发团队的独立性和效率,但服务之间的通信方式可能比较复杂。

3、微服务阶段:在微服务阶段,应用程序被进一步拆分为更小粒度的微服务。每个微服务都是一个独立的、自治的服务单元,具有清晰的职责和接口。微服务之间通过轻量级的通信机制(如 HTTP、消息队列等)进行交互。这种架构模式提供了更大的灵活性和可扩展性,使团队能够更独立地开发、部署和维护服务。

4、服务治理阶段:随着微服务数量的增加,服务之间的管理和协调变得更加复杂。在服务治理阶

段,引入了一些工具和技术来帮助管理微服务,如服务注册与发现、负载均衡、故障恢复和监控等这些工具和技术有助于提高系统的可靠性、可用性和可维护性

5、云原生阶段:随着云计算和容器技术的发展,微服务架构进一步演化为云原生架构。云原生架构借助容器化技术(如 Docker)和容器编排系统(如 Kubernetes),实现了更高级别的自动化、弹性和可观测性。它还鼓励采用持续交付和 DevOps 实践,使软件开发和部署更加快速和可靠

9.3 Istio 概念、架构、组件详细介绍

9.3.1 Istio 基本介绍

​ Istio 是一个开源的服务网格平台,它提供了一组功能丰富的服务网格组件,用于处理微服务之间的流量路由、负载均衡、故障恢复、认证授权、监控和日志等任务。

以下是一些关键特性和组件,可以帮助更好地理解 Istio:

1、代理模型:Istio 通过在每个微服务的容器之间插入一个智能代理(Envoy)来管理服务间的通信。这些代理具有丰富的功能,可以拦截和修改流量,提供负载均衡和故障恢复机制,并与 Istio 控制平面进行通信。

2、流量管理:Istio 允许你定义细粒度的流量路由规则,可以基于请求的属性(如请求头、路径等) 将请求导向不同的微服务版本或实例。它还支持灰度发布、A/B 测试和金丝雀部署等流量控制策略

3、安全性:Istio 提供了强大的安全功能,包括服务间的认证、授权和加密通信。它可以集成现有的身份验证和授权解决方案,如 JWT、OAuth 和自定义策略。此外,Istio 还提供了流量的加密和解密功能,以保护敏感数据的传输。

4、可观测性:Istio 通过收集、聚合和可视化服务间的指标、日志和分布式追踪数据,提供全面的可观测性。它整合了流行的监控和日志系统,如 Prometheus、Grafana 和 Jaeger,使你能够实时监控和调试微服务的性能和健康状态。

5、策略和控制:Istio 的控制平面允许你定义和执行各种策略,例如流量管理策略、安全策略和配额策略。你可以通过 Istio 的配置和规则来实现服务级别的访问控制、限流和审计等。

Istio 还有其他功能和扩展,如故障注入、自动缩放、跨多集群的支持等,使你能够更好地管理和监控微服务架构。通过使用 Istio,你可以减轻微服务架构中的许多常见管理挑战,提高系统的弹性和可靠性。

9.3.2 Istio 核心特性

流量治理(traffic management)

断路器(circuit breakers)、超时、重试、多路由规则、AB 测试、灰度发布、按照百分比分配流量等。

1) 断路器

互动 1:举个生活中的例子解释断路器

​ 当电路发生故障或异常时,伴随着电流不断升高,并且升高的电流有可能能损坏电路中的某些重要器件,也有可能烧毁电路甚至造成火灾。若电路中正确地安置了保险丝,那么保险丝就会在电流异常升高到一定的高度和热度的时候,自身熔断切断电流,从而起到保护电路安全运行的作用。

断路器也称为服务熔断,在多个服务调用的时候,服务 A 依赖服务 B,服务 B 依赖服务 C,如果服务 C 响应时间过长或者不可用,则会让服务 B 占用太多系统资源,而服务 A 也依赖服 B,同时也在占用大量的系统资源,造成系统雪崩的情况出现。 Istio 断路器通过网格中的边车对流量进行拦截判断处理,避免了在代码中侵入控制逻辑,非常方便的就实服务熔断的能力。

img

在微服务架构中,在高并发情况下,如果请求数量达到一定极限(可以自己设置阈值),超出了设置的阈值,断路器会自动开启服务保护功能,然后通过服务降级的方式返回一个友好的提示给客户端。假设当 10 个请求中,有 10%失败时,熔断器就会打开,此时再调用此服务,将会直接返回失败,不再调远程服务。直到 10s 钟之后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开。

互动 2:服务降级(提高用户体验效果)

​ 比如电商平台,在针对 618、双 11 的时候会有一些秒杀场景,秒杀的时候请求量大,可能会返回报错标志“当前请求人数多,请稍后重试”等,如果使用服务降级,无法提供服务的时候,消费者会调用降级的操作,返回服务不可用等信息,或者返回提前准备好的静态页面写好的信息。

2) 超时

什么时候需要用到超时?

​ 在生产环境中经常会碰到由于调用方等待下游的响应过长,堆积大量的请求阻塞了自身服务,造成雪崩的情况,通过超时处理来避免由于无限期等待造成的故障,进而增强服务的可用性。

通过例子来理解

在这里插入图片描述

nginx 服务设置了超时时间为 3 秒,如果超出这个时间就不在等待,返回超时错误

httpd 服务设置了响应时间延迟 5 秒,任何请求都需要等待 5 秒后才能返回

client 通过访问 nginx 服务去反向代理 httpd 服务,由于 httpd 服务需要 5 秒后才能返回,但nginx 服务只等待 3 秒,所以客户端会提示超时错误。

3) 重试

​ istio 重试机制就是如果调用服务失败,Envoy 代理尝试连接服务的最大次数。而默认情况下,Envoy 代理在失败后并不会尝试重新连接服务。举个例子:

img

​ 客户端调用 nginx,nginx 将请求转发给 tomcat。tomcat 通过故障注入而中止对外服务,nginx 设置如果访问 tomcat 失败则会重试 3 次。

4) 按照流量百分比代理

9.3.3 Istio 架构

img

istio 服务网格从逻辑上分为数据平面和控制平面

​ 1、数据平面由部署为 Sidecar 的一组智能代理(Envoy+Polit-agent)组成。这些代理承载并控制微服务之间的所有网络通信,管理入口和出口流量,类似于一线员工。

​ 2、控制平面管理并配置代理来进行流量路由。

​ 3、istio1.5+中使用了一个全新的部署模式,重建了控制平面,将原有的多个组件整合为一个单体结构istiod,这个组件是控制平面的核心,负责处理配置、证书分发、sidecar 注入等各种功能。istiod 是新本中最大的变化,以一个单体组件替代了原有的架构,在降低复杂度和维护难度的同时,也让易用性得到提升。需要注意的一点是,原有的多组件并不是被完全移除,而是在重构后以模块的形式整合在一起组成了 istiod。

数据平面:

Envoy 和 pilot-agent 打在同一个镜像中,即 Proxy。

9.4 在k8s 平台安装 Istio

9.4.1 准备安装 Istio 要的压缩包

官网下载地址:https://github.com/istio/istio

1、把压缩包上传到 k8s 的控制节点,手动解压:

[root@k8s-master01 istio]# tar -xf istio-1.13.1.tar.gz

2、切换到 istio 包所在目录下

Istio-1.13.1.tar.gz 解压的软件包包名是 istio-1.13.1,则:

[root@k8s-master01 istio]# cd istio-1.13.1

安装目录包含如下内容:

1) samples/目录下,有示例应用程序

2) bin/目录下,包含 istioctl 的客户端文件。istioctl 工具用于手动注入 Envoy sidecar 代理。

3、把 istioctl 这个可执行文件拷贝到/usr/bin/目录

[root@k8s-master01 istio-1.13.1]# cd bin/
[root@k8s-master01 bin]# cp -a istioctl /usr/local/bin/

9.4.2 安装 istio

1. 下载镜像:

上传镜像:

docker load -i examples-bookinfo-details.tar.gz 
docker load -i examples-bookinfo-reviews-v1.tar.gz 
docker load -i examples-bookinfo-productpage.tar.gz 
docker load -i examples-bookinfo-reviews-v2.tar.gz 
docker load -i examples-bookinfo-ratings.tar.gz 
docker load -i examples-bookinfo-reviews-v3.tar.gz 
docker load -i pilot.tar.gz
docker load -i proxyv2.tar.gz docker load -i httpbin.tar.gz

2. 安装

在 k8s 的控制节点 操作

$ pwd
/root/istio/istio-1.13.1
$ istioctl install -y --set profile=demo

看到如下,说明 istio 初始化完成:

✔ Istio core installed                                                           
✔ Istiod installed                                                               ✔ Ingress gateways installed                                                     ✔ Egress gateways installed                                                     ✔ Installation complete 

3. 验证 istio 是否部署成功

$ kubectl get pod -n istio-system
NAME                                   READY   STATUS        RESTARTS   AGE
istio-egressgateway-76c96658fd-7smgj   1/1     Running       0          95s
istio-ingressgateway-569d7bfb4-hzfct   1/1     Running       0          95s
istiod-74c64d89cb-lfpkl                1/1     Running       0          100s

4. 卸载 istio 集群,暂时不执行,记住这个命令即可

istioctl manifest generate --set profile=demo | kubectl delete -f -

9.5 在k8s 集群部署 bookinfo 在线书店,开启istio 功能

9.5.1 在线书店功能介绍

在线书店-bookinfo,该应用由四个单独的微服务构成,这个应用模仿在线书店的一个分类,显示一本书的信息,页面上会显示一本书的描述,书籍的细节(ISBN、页数等),以及关于这本书的一些评论。

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

1) productpage 这个微服务会调用 details 和 reviews 两个微服务,用来生成页面;

2) details 这个微服务中包含了书籍的信息;

3) reviews 这个微服务中包含了书籍相关的评论,它还会调用 ratings 微服务;

4) ratings 这个微服务中包含了由书籍评价组成的评级信息。

reviews 微服务有 3 个版本

1) v1 版本不会调用 ratings 服务;

2) v2 版本会调用 ratings 服务,并使用 1 5 个黑色星形图标来显示评分信息;

3) v3 版本会调用 ratings 服务,并使用 1 5 个红色星形图标来显示评分信息。

下图展示了这个应用的端到端架构

img

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

9.5.2 部署应用

​ 要在 Istio 中运行这一应用,无需对应用自身做出任何改变。 只要简单的在 Istio 环境中对服务进行配置和运行,具体一点说就是把 Envoy sidecar 注入到每个 pod 之中。 最终的部署结果将如下图所示:

img

​ 所有的微服务都和 Envoy sidecar 集成在一起,被集成服务所有的出入流量都被 envoy sidecar 所劫持,这样就为外部控制准备了所需的 Hook,然后就可以利用 Istio 控制平面为应用提供服务路由、遥测数据收集以及策略实施等功能。

9.5.3 启动应用服务

1. 进入 istio 安装目录。

2. istio 默认自动注入 sidecar

**需要为 default 命名空间打上标签 istio-injection=enabled **

$ kubectl label namespace default istio-injection=enabled

3. 使用 kubectl 部署应用

$ pwd
/root/istio/istio-1.13.1

$ vim samples/bookinfo/platform/kube/bookinfo.yaml

修改镜像:

        image: istio/examples-bookinfo-productpage-v1:1.16.2

img

$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml

上面的命令会启动全部的四个服务,其中也包括了 reviews 服务的三个版本(v1、v2 以及 v3)。

4.确认所有的服务和 Pod 都已经正确的定义和启动:

$ kubectl get service
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
details       ClusterIP   10.102.79.217    <none>        9080/TCP   6m17s
kubernetes    ClusterIP   10.96.0.1        <none>        443/TCP    21d
productpage   ClusterIP   10.109.97.72     <none>        9080/TCP   6m16s
ratings       ClusterIP   10.107.246.55    <none>        9080/TCP   6m17s
reviews       ClusterIP   10.102.248.105   <none>        9080/TCP   6m16s
$ kubectl get pod 
NAME                              READY   STATUS    RESTARTS   AGE
details-v1-567754d5d9-ztlwv       2/2     Running   0          8m6s
productpage-v1-857fcc5584-g4s4r   2/2     Running   0          8m6s
ratings-v1-94cb45f77-dbsnx        2/2     Running   0          8m6s
reviews-v1-6f9d875c6c-z7f4x       2/2     Running   0          8m6s
reviews-v2-7c77fcc7f7-kg7h9       2/2     Running   0          8m6s
reviews-v3-f4949cfcf-lz25h        2/2     Running   0          8m6s

拓展:解决k8s中命名空间无法删除的问题

解决k8s中命名空间无法删除的问题

想要去删除k8s中的一个指定命名空间,刚开始使用命令

$ kubectl delete ns qatest
## 或者使用
kubectl  delete ns qatest --force --grace-period=0

使用以上两种命令均无法成功删除命名空间,只会使命名空间的状态为Terminating状态

使用以下方法成功删除

1)使用命令

$ kubectl get namespace devlopment -o json > temp.json

编辑temp.json,删除以下部分:

image-20230910214113960

2)使用kubectl代理,执行命名

$ kubectl proxy --port=8080

并开启一个新终端,访问测试

$ curl http://localhost:8080/api/

3)使用http接口进行删除

curl -k -H "Content-Type: application/json" -X PUT --data-binary @temp.json http://127.0.0.1:8080/api/v1/namespaces/qatest/finalize

5. 确认 Bookinfo 应用是否正在运行

在某个 Pod 中用 curl 命令对应用发送请求,例如 ratings:

$ kubectl exec -it $(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}') -c ratings -- curl productpage:9080/productpage | grep -o "<title>.*</title>" 

显示如下:

img

6. 确定 Ingress 的 IP 和端口

​ 现在 Bookinfo 服务已经启动并运行,你需要使应用程序可以从 Kubernetes 集群外部访问,例如从浏览器访问,那可以用 Istio Gateway 来实现这个目标。

1) 为应用程序定义 Ingress 网关:

$ pwd
/root/istio/istio-1.13.1

$ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml

2) 确认网关创建完成:

$ kubectl get gateway
NAME               AGE
bookinfo-gateway   35s

3)确定 ingress ip 和端口

​ 执行如下指令,明确自身 Kubernetes 集群环境支持外部负载均衡:

kubectl get svc istio-ingressgateway -n istio-system

image-20230910222619148

​ 如果 EXTERNAL-IP 值已设置,说明环境正在使用外部负载均衡,可以用其为 ingress gateway 提供服务。 如果 EXTERNAL-IP 值为(或持续显示), 说明环境没有提供外部负载均衡,无法使用 ingress gateway。在这种情况下,你可以使用服务的 NodePort 访问网关。

若自身环境未使用外部负载均衡器,需要通过 node port 访问。执行如下命令。设置 ingress 端口:

$ export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')


## 仅查看端口
$ kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}'
30554
$ export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')


## 仅查看端口
$ kubectl -n istio-system get service istio-ingressgateway -o jsonpath=ec.ports[?(@.name=="https")].nodePort}'
30968

4)设置 GATEWAY_URL

[root@k8s-master01 istio-1.13.1]# INGRESS_HOST=192.168.2.201

#192.168.2.201是安装 istio 的机器,即 k8s 控制节点的 ip 

$ export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT

$ echo $GATEWAY_URL  ## 显示如下:

192.168.2.201:30554

确认可以从集群外部访问应用

可以用 curl 命令来确认是否能够从集群外部访问 Bookinfo 应用程序:

curl -s http://${GATEWAY_URL}/productpage | grep -o "<title>.*</title>"

显示如下:

<title>Simple Bookstore App</title>

还可以用浏览器打开网址

image-20230914210456347

​ http://$GATEWAY_URL/productpage,也就是 192.168.2.201:30554/productpage来浏览应用的 Web 页面。

​ 如果刷新几次应用的页面,就会看到productpage 页面中会随机展示 reviews 服务的不同版本的效果(红色、黑色的星形或者没有显示)

9.5.4 卸载 bookinfo 服务

暂时先不要卸载

可以使用下面的命令来完成应用的删除和清理了:

1.删除路由规则,并销毁应用的 Pod

$ sh samples/bookinfo/platform/kube/cleanup.sh

2.确认应用已经关停

$ kubectl get virtualservices       #-- there should be no virtual services 
$ kubectl get destinationrules      #-- there should be no destination rules 
$ kubectl get gateway               #-- there should be no gateway
$ kubectl get pods                  #-- the Bookinfo pods should be deleted

9.6 通过 Istio 实现灰度发布

9.6.1 什么是灰度发布?

灰度发布也叫金丝雀部署 ,是指通过控制流量的比例,实现新老版本的逐步更替。

​ 比如对于服务 A 有 version1、 version2 两个版本 , 当前两个版本同时部署,但是 version1 比例90% ,version2 比例 10% ,看运行效果,如果效果好逐步调整流量占比 80~20 ,70~30 ·····10~90 ,0,100 ,最终 version1 版本下线。

灰度发布的特点:

1) 新老板共存

2) 可以实时根据反馈动态调整占比

3) 理论上不存在服务完全宕机的情况。

4) 适合于服务的平滑升级与动态更新。

9.6.2 使用 istio 进行金丝雀发布

工作节点上传镜像并导入:

$ docker load -i canary-v2.tar.gz
$ docker load -i canary-v1.tar.gz

创建金丝雀服务

$ cat deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: appv1
  labels:
    app: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: v1
      apply: canary
  template:
    metadata:
      labels:
        app: v1
        apply: canary
    spec:
      containers:
      - name: nginx
        image: xuegod/canary:v1
        ports:
        - containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: appv2
  labels:
    app: v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: v2
      apply: canary
  template:
    metadata:
      labels:
        app: v2
        apply: canary
    spec:
      containers:
      - name: nginx
        image: xuegod/canary:v2
        ports:
        - containerPort: 80

更新:

$ kubectl apply -f deployment.yaml

创建 service

$ cat service.yaml

apiVersion: v1
kind: Service
metadata:
  name: canary
  labels:
    apply: canary
spec:
  selector:
    apply: canary
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

更新 service.yaml 文件

$ kubectl apply -f service.yaml

查询canary svc,测试访问

$ kubectl get svc |grep canary |awk -F " " '{print $3}'
$ curl $(kubectl get svc |grep canary |awk -F " " '{print $3}')

创建 gateway

$ cat gateway.yaml 

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: canary-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"

更新 gateway.yaml

$ kubectl apply -f gateway.yaml

创建 virtualservice

$ cat virtual.yaml

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: canary
spec:
  hosts:
  - "*"
  gateways:
  - canary-gateway
  http:
  - route:
    - destination:
        host: canary.default.svc.cluster.local
        subset: v1
      weight: 90
    - destination:
        host: canary.default.svc.cluster.local
        subset: v2
      weight: 10
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: canary
spec:
  host: canary.default.svc.cluster.local
  subsets:
  - name: v1
    labels:
      app: v1
  - name: v2
    labels:
      app: v2

更新 virtual.yaml 文件

$ kubectl apply -f virtual.yaml

获取 Ingress_port:

$ kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}'


30554

显示结果是 30554

验证金丝雀发布效果:

[root@k8s-master01 huidu]# for i in `seq 1 1000`;do curl 192.168.2.201:30554;done > 1.txt


[root@k8s-master01 huidu]# cat 1.txt |grep v1|wc -l
915
[root@k8s-master01 huidu]# cat 1.txt |grep v2|wc -l
85

打开 1.txt 可以看到结果有 约90% 次出现 v1,%10 次出现 canary-v2,符合我们预先设计的流量走向。

9.7 istio 核心功能演示

9.7.1 断路器

官网:https://istio.io/latest/zh/docs/tasks/traffic-management/circuit-breaking/

​ 断路器是创建弹性微服务应用程序的重要模式。断路器使应用程序可以适应网络故障和延迟等网络不良影响。

测试断路器:

1、在 k8s 集群创建后端服务
$ cd istio-1.13.1
$ cat samples/httpbin/httpbin.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - name: http
    port: 8000
    targetPort: 80
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      serviceAccountName: httpbin
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        ports:
        - containerPort: 80

把 httpbin.tar.gz 上传到 工作 节点,手动解压:

$ docker load -i httpbin.tar.gz


$ kubectl apply -f samples/httpbin/httpbin.yaml
## 该 httpbin 应用程序充当后端服务。
2、配置断路器

以免大量请求超出服务处理上线,整个服务崩掉

创建一个目标规则,在调用 httpbin 服务时应用断路器设置:

$ vim destination.yaml

apiVersion:  networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: httpbin
spec:
  host: httpbin
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 1
      http:
        http1MaxPendingRequests: 1
        maxRequestsPerConnection: 1
    outlierDetection:
      consecutiveGatewayErrors: 1
      interval: 1s
      baseEjectionTime: 3m
      maxEjectionPercent: 100
### destination.yaml 解释

apiVersion:  networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: httpbin
spec:
  host: httpbin
  trafficPolicy:
    connectionPool:
## 连接池(TCP | HTTP)配置,例如:连接数、并发请求等tcp:
      tcp:
        maxConnections: 1
## TCP 连接池中的最大连接请求数,当超过这个值,会返回 503 代码。如两个请求过来,就会有一个请求返回 503。
      http:
        http1MaxPendingRequests: 1
## 连接到目标主机的最大挂起请求数,也就是待处理请求数。这里的目标指的是 virtualservice 路由规则中配置的 destination。
        maxRequestsPerConnection: 1
## 连接池中每个连接最多处理 1 个请求后就关闭,并根据需要重新创建连接池中的连接
    outlierDetection:
## 异常检测配置,传统意义上的熔断配置,即对规定时间内服务错误数的监测 
      consecutiveGatewayErrors: 1
## 连续错误数 1,即连续返回 502-504 状态码的 Http 请求错误数
      interval: 1s
## 错误异常的扫描间隔 1s,即在 interval(1s)内连续发生consecutiveGatewayErrors(1)个错误,则触发服务熔断
      baseEjectionTime: 3m
## 基本驱逐时间 3 分钟,实际驱逐时间为 baseEjectionTime*驱逐次数
      maxEjectionPercent: 100
## 最大驱逐百分比 100%
$ kubectl apply -f destination.yaml 
3、添加客户端访问 httpbin 服务

​ 创建一个客户端以将流量发送给 httpbin 服务。该客户端是一个简单的负载测试客户端,Fortio 可以控制连接数,并发数和 HTTP 调用延迟。使用此客户端来“跳闸”在 DestinationRule 中设置的断路器策略。

部署 fortio 客户端:

把 fortio.tar.gz 上传到 工作节点 节点,手动解压:

$ docker load -i fortio.tar.gz
$ kubectl apply -f samples/httpbin/sample-client/fortio-deploy.yaml

使用 fortio 客户端工具调用 httpbin:

[root@k8s-master01 istio-1.13.1]# kubectl get pod
NAME                              READY   STATUS    RESTARTS   AGE
appv1-746865b8c4-sdvfs            2/2     Running   0          12m
appv2-66995c8c5b-lwhvq            2/2     Running   0          12m
details-v1-567754d5d9-hrvxj       2/2     Running   0          33s
fortio-deploy-7dcd84c469-2ptkg    2/2     Running   0          82s
httpbin-85d76b4bb6-b7476          2/2     Running   0          3m53s
productpage-v1-857fcc5584-qhsm4   2/2     Running   0          33s
ratings-v1-94cb45f77-tq2zd        2/2     Running   0          33s
reviews-v1-6f9d875c6c-m6r8h       2/2     Running   0          33s
reviews-v2-7c77fcc7f7-7px88       2/2     Running   0          33s
reviews-v3-f4949cfcf-wf64n        2/2     Running   0          33s
kubectl exec $(kubectl get pod |grep fortio |awk -F " " '{print $1}') -c fortio -- /usr/bin/fortio curl http://httpbin:8000/get

显示如下:

HTTP/1.1 200 OK
server: envoy
date: Thu, 14 Sep 2023 13:31:39 GMT
content-type: application/json
content-length: 594
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 2

{
  "args": {}, 
  "headers": {
    "Host": "httpbin:8000", 
    "User-Agent": "fortio.org/fortio-1.17.1", 
    "X-B3-Parentspanid": "a0377da700bb7298", 
    "X-B3-Sampled": "1", 
    "X-B3-Spanid": "b9ed59aad9587016", 
    "X-B3-Traceid": "ed7a15421d31226ea0377da700bb7298", 
    "X-Envoy-Attempt-Count": "1", 
    "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=1b7a933fd482b13c77ba522812f61497451fd586d381fe525e1bd597729eebb8;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/default"
  }, 
  "origin": "127.0.0.6", 
  "url": "http://httpbin:8000/get"
}
4、触发断路器

​ 在 DestinationRule 设置中,指定了 maxConnections: 1 和 http1MaxPendingRequests: 1。这些规则表明,如果超过一个以上的连接并发请求,则 istio-proxy 在为进一步的请求和连接打开路由时,应该会看到下面的情况 。

以两个并发连接(-c 2)和发送 20 个请求(-n 20)调用服务:

$ kubectl exec -it fortio-deploy-7dcd84c469-2ptkg -c fortio -- /usr/bin/fortio load -c 2 -qps 0 -n 20 -loglevel Warning http://httpbin:8000/get

显示如下:

13:32:53 I logger.go:127> Log level is now 3 Warning (was 2 Info)
Fortio 1.17.1 running at 0 queries per second, 4->4 procs, for 20 calls: http://httpbin:8000/get
Starting at max qps with 2 thread(s) [gomax 4] for exactly 20 calls (10 per thread + 0)
13:32:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
13:32:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
Ended after 8.154699ms : 20 calls. qps=2452.6
Aggregated Function Time : count 20 avg 0.00076311425 +/- 0.0002641 min 0.000375541 max 0.001333243 sum 0.015262285
## range, mid point, percentile, count
>= 0.000375541 <= 0.001 , 0.000687771 , 85.00, 17
> 0.001 <= 0.00133324 , 0.00116662 , 100.00, 3
## target 50% 0.000726799
## target 75% 0.000921943
## target 90% 0.00111108
## target 99% 0.00131103
## target 99.9% 0.00133102
Sockets used: 20 (for perfect keepalive, would be 2)
Jitter: false
Code 503 : 20 (100.0 %)
Response Header Sizes : count 20 avg 0 +/- 0 min 0 max 0 sum 0
Response Body/Total Sizes : count 20 avg 153 +/- 0 min 153 max 153 sum 3060
All done 20 calls (plus 0 warmup) 0.763 ms avg, 2452.6 qps

只有 20%成功了,其余的都断开了

Response Header Sizes : count 20 avg 0 +/- 0 min 0 max 0 sum 0
Response Body/Total Sizes : count 20 avg 153 +/- 0 min 153 max 153 sum 3060
All done 20 calls (plus 0 warmup) 0.763 ms avg, 2452.6 qps

9.7.2 超时

​ 在生产环境中经常会碰到由于调用方等待下游的响应过长,堆积大量的请求阻塞了自身服务,造成雪崩的情况,通过超时处理来避免由于无限期等待造成的故障,进而增强服务的可用性,Istio 使用虚拟服务来优雅实现超时处理。

​ 下面例子模拟客户端调用 nginx,nginx 将请求转发给 tomcat。nginx 服务设置了超时时间为 2 秒,如果超出这个时间就不在等待,返回超时错误。tomcat 服务设置了响应时间延迟 10 秒,任何请求都需要等待 10 秒后才能返回。client 通过访问 nginx 服务去反向代理 tomcat 服务,由于 tomcat 服务需要 10 秒后才能返回,但 nginx 服务只等待 2 秒,所以客户端会提示超时错误。

1、上传镜像,yaml文件

把 busybox.tar.gz、 nginx.tar.gz、 tomcat-app.tar.gz 上传到 工作 节点

$ docker load -i nginx.tar.gz 
$ docker load -i busybox.tar.gz 
$ docker load -i tomcat-app.tar.gz 

控制节点创建目录

$ pwd
/root/istio/timeout

$ /root/istio/timeout

nginx-deployment.yaml

$ cat nginx-deployment.yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-tomcat
  labels:
    server: nginx
    app: web
spec:
  replicas: 1
  selector:
    matchLabels:
      server: nginx
      app: web
  template:
    metadata:
      name: nginx
      labels: 
        server: nginx
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        imagePullPolicy: IfNotPresent
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat
  labels:
    server: tomcat
    app: web
spec:
  replicas: 1
  selector:
    matchLabels:
      server: tomcat
      app: web
  template:
    metadata:
      name: tomcat
      labels: 
        server: tomcat
        app: web
    spec:
      containers:
      - name: tomcat
        image: docker.io/kubeguide/tomcat-app:v1 
        imagePullPolicy: IfNotPresent

nginx-tomcat-svc.yaml

$ cat nginx-tomcat-svc.yaml

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  selector:
    server: nginx
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: tomcat-svc
spec:
  selector:
    server: tomcat
  ports:
  - name: http
    port: 8080
    targetPort: 8080
    protocol: TCP

virtual-tomcat.yaml

$ cat virtual-tomcat.yaml

---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: nginx-vs
spec:
  hosts:
  - nginx-svc
  http:
  - route:
    - destination: 
        host: nginx-svc
    timeout: 2s
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: tomcat-vs
spec:
  hosts:
  - tomcat-svc
  http:
  - fault:
      delay:
        percentage:
          value: 100
        fixedDelay: 10s
    route:
    - destination:
        host: tomcat-svc 

virtual-tomcat.yaml 资源清单重点知识讲解

第一:故障注入:

  http:
  - fault:
      delay:
        percentage:
          value: 100
        fixedDelay: 10s

该设置说明每次调用 tomcat-svc 的 k8s service,都会延迟 10s 才会调用。

第二:调用超时:

  hosts:
  - nginx-svc
  http:
  - route:
    - destination: 
        host: nginx-svc
    timeout: 2s

该设置说明调用 nginx-svc 的 k8s service,请求超时时间是 2s。

2、部署 tomcat、nginx 服务

​ 需要对 nginx-deployment.yaml 资源文件进行 Istio 注入,将 nginx、tomcat 都放入到网格中。可以采用手工注入 Istio 方式。

$ kubectl apply -f nginx-deployment.yaml 

查看 Istio 注入情况:

[root@k8s-master01 timeout]# kubectl get pod |grep tomcat
nginx-tomcat-d4d765748-ttfsj      2/2     Running   0          40s
tomcat-8484c667bf-xsg45           2/2     Running   0          40s

3、部署 nginx 和 tomcat 的 service

$ kubectl apply -f nginx-tomcat-svc.yaml 

4、部署虚拟服务

$ kubectl apply -f virtual-tomcat.yaml

5、设置超时时间

$ kubectl exec -it $(kubectl get pod |grep nginx-tomcat|awk -F " " '{print $1}') -- bash
## cat > /etc/nginx/conf.d/default.conf << EOF
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
         proxy_pass http://tomcat-svc:8080;
         proxy_http_version 1.1;
    }

    #error_page  404              /404.html;

    ## redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    ## proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    ##    proxy_pass   http://127.0.0.1;
    #}

    ## pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    ##    root           html;
    ##    fastcgi_pass   127.0.0.1:9000;
    ##    fastcgi_index  index.php;
    ##    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    ##    include        fastcgi_params;
    #}

    ## deny access to .htaccess files, if Apache's document root
    ## concurs with nginx's one
    #
    #location ~ /\.ht {
    ##    deny  all;
    #}
}

EOF

img

         proxy_pass http://tomcat-svc:8080;
         proxy_http_version 1.1;

编辑完后,再执行如下语句验证配置和让配置生效:

## nginx -t

## nginx -s reload

这样,整个样例配置和部署都完成了。

6、验证超时

登录 client,执行如下语句:

$ kubectl run busybox --image busybox:1.28 --restart=Never --rm -it busybox -- sh


$ time wget -q -O - http://nginx-svc
wget: server returned error: HTTP/1.1 504 Gateway Timeout
Command exited with non-zero status 1
real    0m 2.00s
user    0m 0.00s
sys 0m 0.00s


$ while true; do wget -q -O - http://nginx-svc; done
wget: server returned error: HTTP/1.1 504 Gateway Timeout
wget: server returned error: HTTP/1.1 504 Gateway Timeout
wget: server returned error: HTTP/1.1 504 Gateway Timeout
wget: server returned error: HTTP/1.1 504 Gateway Timeout
wget: server returned error: HTTP/1.1 504 Gateway Timeout

​ 每隔 2 秒,由于 nginx 服务的超时时间到了而 tomcat 未有响应,则提示返回超时错误。验证故障注入效果,执行如下语句:

$ wget -q -O - http://tomcat-svc:8080 

...
real    0m 10.02s
user    0m 0.00s
sys 0m 0.00s

9.7.3 故障注入和重试

​ Istio 重试机制就是如果调用服务失败,Envoy 代理尝试连接服务的最大次数。而默认情况下,Envoy 代理在失败后并不会尝试重新连接服务,除非我们启动 Istio 重试机制。

​ 下面例子模拟客户端调用 nginx,nginx 将请求转发给 tomcat。tomcat 通过故障注入而中止对外服务,nginx 设置如果访问 tomcat 失败则会重试 3 次。

$ cd /root/timeout/ 
$ kubectl delete -f .
$ kubectl apply -f nginx-deployment.yaml
$ kubectl apply -f nginx-tomcat-svc.yaml 
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
busybox 2/2 Running 0 55m
nginx-7f6496574c-zbtqj 2/2 Running 0 10m
tomcat-86ddb8f5c9-dqxcq 2/2 Running 0 35m
$ vim virtual-attempt.yaml

---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: nginx-vs
spec:
  hosts:
  - nginx-svc
  http:
  - route:
    - destination: 
        host: nginx-svc
    retries:
      attempts: 3
      perTryTimeout: 2s
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: tomcat-vs
spec:
  hosts:
  - tomcat-svc
  http:
  - fault:
      abort:
        percentage:
          value: 100
        httpStatus: 503
    route:
    - destination:
        host: tomcat-svc 
$ kubectl apply -f virtual-attempt.yaml

上诉虚拟服务资源清单解读:

第一:故障注入。

​ 该虚拟服务的作用对象就是 tomcat-svc。使用此故障注入后,在网格中该tomcat 就是不可用的。

      abort:
        percentage:
          value: 100
        httpStatus: 503

​ abort 是模拟 tomcat 服务始终不可用,该设置说明每次调用 tomcat-svc 的 k8s service, 100%都会返回错误状态码 503。

第二:调用超时:

  hosts:
  - nginx-svc
  http:
  - route:
    - destination: 
        host: nginx-svc
    retries:
      attempts: 3
      perTryTimeout: 2s

​ 该设置说明调用 nginx-svc 的 k8s service,在初始调用失败后最多重试 3 次来连接到服务子集,每个重试都有 2 秒的超时。

测试

$ kubectl exec -it nginx-tomcat-7dd6f74846-rdqqf -- /bin/sh 
## apt-get update

## apt-get install vim -y
## vi /etc/nginx/conf.d/default.conf
## nginx -t
## nginx -s reload

img

验证重试是否生效

$ kubectl run busybox --image busybox:1.28 -- restart=Never --rm -it busybox -- sh

## wget -q -O - http://nginx-svc
$ kubectl logs -f nginx-tomcat-d4d765748-mrhdp -c istio-proxy

执行结果如下:

请求三次,请求失败,第四次返回503

image-20230914222632769

由上图可知,重试设置生效。

总结:

9.1 微服务基本介绍

9.2 微服务架构发展进程

9.3 Istio 概念、架构、组件详细介绍

9.4 k8s 平台安装 Istio

9.5 k8s 集群部署 bookinfo 在线书店,开启 istio 功能

9.6 通过 Istio 实现灰度发布

9.7 istio 核心功能演示