云原生服务网格Istio实战

基础介绍

1、Istio的定义

Istio 是一个开源服务网格,它透明地分层到现有的分布式应用程序上。 Istio 强大的特性提供了一种统一和更有效的方式来保护、连接和监视服务。 Istio 是实现负载平衡、服务到服务身份验证和监视的路径------只需要很少或不需要更改服务代码。它强大的控制平面带来了重要的特点,包括:

  • 使用 TLS 加密、强身份认证和授权的集群内服务到服务的安全通信

  • 自动负载均衡的 HTTP, gRPC, WebSocket,和 TCP 流量

  • 通过丰富的路由规则、重试、故障转移和故障注入对流量行为进行细粒度控制

  • 一个可插入的策略层和配置 API,支持访问控制、速率限制和配额

  • 对集群内的所有流量(包括集群入口和出口)进行自动度量、日志和跟踪

官网地址:https://istio.io/latest/zh/about/service-mesh/

使用 Istio 前:

使用 Istio 后:

2、架构

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

  • 数据平面 由一组智能代理(Envoy)组成,被部署为 Sidecar。这些代理负责协调和控制微服务之间的所有网络通信。 它们还收集和报告所有网格流量的遥测数据。

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

https://istio.io/latest/zh/docs/ops/deployment/architecture/

下图展示了组成每个平面的不同组件:

3、Istio安装
3.1 下载Istio

1)下载指定版本

https://istio.io/latest/zh/docs/setup/getting-started/#download

bash 复制代码
# 指定版本下载  
# 要为 x86_64 架构下载 Istio 1.20.0,请运行
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.20.0 TARGET_ARCH=x86_64 sh -

如果下载不了,去https://github.com/istio/istio/releases/tag/1.20.0下载

wget https://github.com/istio/istio/releases/download/1.20.0/istio-1.20.0-linux-amd64.tar.gz

2)解压后进入安装目录

tar -zxvf istio-1.20.0-linux-amd64.tar.gz

安装目录包含:

  • bin: 存放istioctl二进制文件
  • manifests: istio 的内置chart目录,默认使用这些内置 chart 生成安装清单,安装过程也可以自行设置--manifests=other_manifests/参数来指定外部 chart
  • samples: istio 提供的一些示例应用程序
  • tools: 一些工具,如certs用于生成证书

3)为了方便使用istioctl,将bin目录添加到我们的环境变量中:

bash 复制代码
export PATH=$PWD/bin:$PATH

加入/etc/profile永久生效

bash 复制代码
[root@k8s-master01 istio-1.20.0]# vim /etc/profile
export PATH=/root/istio-1.20.0/bin:$PATH
[root@k8s-master01 istio-1.20.0]# source /etc/profile

3.2 执行安装

bash 复制代码
## istio提供的几种内置配置,这些配置文件提供了对 Istio 控制平面和 Istio 数据平面 Sidecar 的定制内容。
[root@master istio-1.15.3]# istioctl profile list
Istio configuration profiles:
    default
    demo
    empty
    external
    minimal
    openshift
    preview

1、default:根据IstioOperatorAPI 的默认设置启动组件。 建议用于生产部署和

Multicluster Mesh中的 Primary Cluster。您可以运行istioctl profile dump命令来查看默认设置。

2、demo:这一配置具有适度的资源需求,旨在展示 Istio 的功能。 它适合运行

Bookinfo应用程序和相关任务。此配置文件启用了高级别的追踪和访问日志,因此不适合进行性能测试。

3、minimal:与默认配置文件相同,但只安装了控制平面组件。 它允许您使用

Separate Profile配置控制平面和数据平面组件(例如 Gateway)。

4、remote:配置

Multicluster Mesh的 Remote Cluster。

5、empty:不部署任何东西。可以作为自定义配置的基本配置文件。

6、preview:预览文件包含的功能都是实验性。这是为了探索 Istio 的新功能。不确保稳定性、安全性和性能(使用风险需自负)。

标注✔的组件安装在每个配置文件中:

bash 复制代码
# 查看demo配置
istioctl profile dump demo

对于本次安装,我们采用demo配置组合。 选择它是因为它包含了一组专为测试准备的功能集合,另外还有用于生产或性能测试的配置组合。

bash 复制代码
$ istioctl install --set profile=demo -y
✔ Istio core installed
✔ Istiod installed
✔ Egress gateways installed
✔ Ingress gateways installed
✔ Installation complete

给命名空间添加标签,指示 Istio 在部署应用的时候,自动注入 Envoy 边车代理

bash 复制代码
$ kubectl label namespace default istio-injection=enabled
namespace/default labeled

4.部署Bookinfo示例应用

官网:https://istio.io/latest/zh/docs/examples/bookinfo/

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

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

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

reviews微服务有 3 个版本:

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

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

4.1 启动应用服务

1)进入 Istio 安装目录。

2)Istio 默认自动注入 Sidecar. 请为default命名空间打上标签istio-injection=enabled:

bash 复制代码
$ kubectl label namespace default istio-injection=enabled

3)使用 kubectl 部署应用:

bash 复制代码
$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml

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

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

bash 复制代码
$ kubectl get services
bash 复制代码
NAME          TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
details       ClusterIP   10.0.0.31    <none>        9080/TCP   6m
kubernetes    ClusterIP   10.0.0.1     <none>        443/TCP    7d
productpage   ClusterIP   10.0.0.120   <none>        9080/TCP   6m
ratings       ClusterIP   10.0.0.15    <none>        9080/TCP   6m
reviews       ClusterIP   10.0.0.170   <none>        9080/TCP   6m

还有:

bash 复制代码
$ kubectl get pods
bash 复制代码
NAME                                        READY     STATUS    RESTARTS   AGE
details-v1-1520924117-48z17                 2/2       Running   0          6m
productpage-v1-560495357-jk1lz              2/2       Running   0          6m
ratings-v1-734492171-rnr5l                  2/2       Running   0          6m
reviews-v1-874083890-f0qf0                  2/2       Running   0          6m
reviews-v2-1343845940-b34q5                 2/2       Running   0          6m
reviews-v3-1813607990-8ch52                 2/2       Running   0          6m

5)要确认 Bookinfo 应用是否正在运行,请在某个 Pod 中用curl命令对应用发送请求,例如ratings:

bash 复制代码
$ kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"
bash 复制代码
<title>Simple Bookstore App</title>

4.2 确定 Ingress 的 IP 和端口

现在 Bookinfo 服务启动并运行中,您需要使应用程序可以从外部访问 Kubernetes 集群, 例如使用浏览器。可以使用网关实现这个目标。

1)为 Bookinfo 应用程序定义一个网关:

使用以下命令创建Istio Gateway:

bash 复制代码
$ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
bash 复制代码
gateway.networking.istio.io/bookinfo-gateway created
virtualservice.networking.istio.io/bookinfo created

确认网关创建完成:

bash 复制代码
$ kubectl get gateway
bash 复制代码
NAME               AGE
bookinfo-gateway   32s

2)设置GATEWAY_URL:

bash 复制代码
$ export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT

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

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

bash 复制代码
$ curl -s "http://${GATEWAY_URL}/productpage" | grep -o "<title>.*</title>"
bash 复制代码
<title>Simple Bookstore App</title>

还可以用浏览器打开网址http://$GATEWAY_URL/productpage,来浏览应用的 Web 页面。 如果刷新几次应用的页面,就会看到productpage页面中会随机展示reviews服务的不同版本的效果 (红色、黑色的星形或者没有显示)。reviews服务出现这种情况是因为我们还没有使用 Istio 来控制版本的路由。

4.4 定义服务版本

在可以使用 Istio 控制 Bookinfo 版本路由之前,您需要定义可用的版本。

Istio 在目标规则中使用subsets定义服务的版本。运行以下命令为 Bookinfo 服务创建默认的目标规则:

bash 复制代码
$ kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml

等待几秒钟,以使目标规则生效。

您可以使用以下命令查看目标规则:

bash 复制代码
$ kubectl get destinationrules -o yaml

4.5 清理

结束对 Bookinfo 示例应用的体验之后,就可以使用下面的命令来完成应用的删除和清理了:

bash 复制代码
$ samples/bookinfo/platform/kube/cleanup.sh

流量管理

1、Gateway

在安装 Istio 的时候,安装了入口和出口网关。这两个网关实际都运行了一个 Envoy 代理实例,它们在网格的边缘作为负载均衡器运行。入口网关接收入站连接,而出口网关接收从集群出去的连接。

使用入口网关,我们可以对进入集群的流量应用路由规则。

我们可以有一个指向入口网关的单一外部 IP 地址,并根据Header将流量路由到集群内的不同服务。

使用 Gateway 资源来配置网关。

下面是一个网关资源的例子:

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
 kind: Gateway
 metadata:
   name: my-gateway
   namespace: default
 spec:
   selector:
     istio: ingressgateway
   servers:
   - port:
       number: 80
       name: http
       protocol: HTTP
     hosts:
     - dev.example.com
     - test.example.com

网关资源描述了负载均衡器的暴露端口、协议、SNI配置等。

GateWay字段名称、字段含义

metadata.name

Gateway 名称

metadata.namespace

Gateway 命名空间

spec.selector

Gateway 使用填写的标签键值对匹配配置下发的边缘代理网关实例

spec.servers.port.number

端口

spec.servers.port.protocol

通信协议,支持:HTTP, HTTPS, GRPC, HTTP2, MONGO, TCP, TLS,请注意同一网关同一端口的协议配置需要保持一致。

spec.servers.port.name

端口名称

spec.severs.hosts

域名,支持通配符 *

spec.servers.tls.httpsRedirect

值为 true 时,边缘代理网关会对所有 http 请求返回 301 重定向,要求客户端发起 https 请求

spec.servers.tls.mode

配置当前端口的 TLS 安全认证模式,如需要开启当前端口的安全认证则需要填写。支持:PASSTHROUGH, SIMPLE, MUTUAL, AUTO_PASSTHROUGH, ISTIO_MUTUAL

spec.servers.tls.credentialName

配置发现 TLS 证书密钥的 secret 的名称

spec.servers.tls.serverCertificate

设置端口的 TLS 证书密钥通过 file mount 形式(不推荐,推荐采用填写 credentialName 字段加载证书私钥)挂载时需要填写的证书路径字段,Istio 默认使用网关所在命名空间下 istio-ingressgateway-certs secret 加载证书至路径 /etc/istio/ingressgateway-certs

spec.servers.tls.privateKey

设置端口的 TLS 证书密钥通过 file mount 形式(不推荐,推荐采用填写 credentialName 字段加载证书私钥)挂载时需要填写的私钥路径字段,Istio 默认使用网关所在命名空间下 istio-ingressgateway-certs secret 加载私钥至路径 /etc/istio/ingressgateway-certs

spec.servers.tls.caCertificates

设置端口的 TLS 证书密钥通过 file mount 形式(不推荐,推荐采用填写 credentialName 字段加载证书私钥)挂载时需要填写的跟证书路径字段,Istio 默认使用网关所在命名空间下 istio-ingressgateway-ca-certs 加载根证书至路径 /etc/istio/ingressgateway-ca-certs,双向认证时需要配置根证书

SNI,即服务器名称指示,是TLS协议的扩展。它指示在"握手"过程开始时浏览器正在联系哪个主机名;允许服务器为多个站点安全地托管多个SSL证书,多个网站存在于同一IP地址上。

上述配置文件的解释:

  • 配置了一个代理,作为负载均衡器
  • 为入口暴露80端口
  • 配置应用于istio入口网关代理
  • hosts字段作为一个过滤器,只有以dev.example.com和test.example.com为目的地的流量会被允许通过。

为了控制和转发流量到集群内运行的实际 Kubernetes 服务,我们必须用特定的主机名(例如dev.example.com和test.example.com)配置一个VirtualService,然后将网关连接到它。

1.1 简单路由实例

部署一个tomcat,并通过istio网关进行访问。

tomcatdeploy.yaml :

bash 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: tomcat
  name: tomcat
  namespace: microservice
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tomcat
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: tomcat
    spec:
      containers:
      - image: hub.c.163.com/library/tomcat:latest
        imagePullPolicy: IfNotPresent
        name: tomcat
        ports:
          - containerPort: 8080
        resources: {}

创建一个tomcat service,tomcatsvc.yaml :

bash 复制代码
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    app: tomcat
  name: tomcat
  namespace: microservice
spec:
  ports:
  - port: 80
    name: tcp
    protocol: TCP
    targetPort: 8080
  selector:
    app: tomcat
status:
  loadBalancer: {}

部署一个Gateway资源:

ingressgateway80.yaml

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: ingressgateway80
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - '*'

这设置*,可以使用ip地址来访问入口网关,如果配置为具体域名,需要修改hosts文件(真实域名需要修改A记录)

现在还没有绑定VirtualService,所以网关还不知道将流量路由到哪里,为tomcat服务创建一个 VirtualService,并将其绑定到 Gateway 资源上:

tomcat-virtualservice.yaml:

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: tomcat-virtualservice
spec:
  hosts:
    - "*"  
  gateways: #把VirtualService绑定到ingressgateway80这个网关
    - ingressgateway80
  http:
    - route: #表示路由到tomcat这个service,microservice表示命名空间
        - destination:
            host: tomcat.microservice.svc.cluster.local
            port:
              number: 80

访问:curl -v 网关外部ip:80

注意其中的header,其中有一个server=istio-envoy,这表明请求经过了envoy代理

2、VirtualService详解

使用 VirtualService 资源在 Istio 服务网格中进行流量路由。通过 VirtualService,我们可以定义流量路由规则,并在客户端试图连接到服务时应用这些规则。

具体的配置含义:

VirtualService字段名称、字段说明

spec.hosts

定义路由规则关联一组的 hosts,可以是带有通配符的 DNS 名称或者 IP 地址(IP 地址仅能应用于来源流量为边缘代理网关)。该字段能应用于 HTTP 和 TCP 流量。在 Kubernetes 环境中,可以使用 service 的名称作为缩写,Istio 会按照 VirtualService所在 namespace 补齐缩写,例如在 default namespace 的 VirtualService 包含 host 缩写 reviews 会被补齐为 reviews.default.svc.cluster.local。为避免误配置,推荐填写 host 全称

spec.gateways

定义应用路由规则的来源流量,可以是一个或多个网关,或网格内部的 sidecar,指定方式为/,保留字段 mesh 表示网格内部所有的 sidecar,当该参数缺省时,会默认填写 mesh,即该路由规则的来源流量为网格内部所有 sidecar

spec.http

定义一组有序的(优先匹配靠前的路由规则)应用于 HTTP 流量的路由规则,HTTP 路由规则会应用于网格内部的 service 端口命名为 http-, http2-, grpc- 开头的流量以及来自 gateway 的协议为 HTTP, HTTP2, GRPC, TLS-Terminated-HTTPS 的流量

spec.http.match

定义路由的匹配规则列表,单个匹配规则项内所有条件是且关系,列表中多个匹配规则之间为或关系

spec.http.route

定义路由转发目的地列表,一条 HTTP 路由可以是重定向或转发(默认),转发的目的地可以是一个或多个服务(服务版本)。同时也可以配置权重、header 操作等行为

spec.http.redirect

定义路由重定向,一条 HTTP 路由可以是重定向或转发(默认),如规则中指定了 passthrough 选项,route、redirect 均会被忽略。可将 HTTP 301 重定向到另外的 URL 或 Authority

spec.http.rewrite

定义重写 HTTP URL 或 Authority headers,不能与重定向同时配置,重写操作会在转发前执行

spec.http.timeout

定义 HTTP 请求的超时时间

spec.http.retries

定义 HTTP 请求的重试策略

spec.http.fault

定义 HTTP 流量的故障注入策略,开启时超时和重试策略不会开启

spec.http.mirror

定义将 HTTP 流量复制到另一个指定的目的端,被复制的流量按照"best effort"原则,sidecar/网关不会等待复制流量的响应结果就会从源目的端返回响应。镜像流量的目的服务端会产生监控指标。

spec.http.mirrorPercent

定义流量镜像的复制百分比,缺省时复制100%的流量。最大值为100

spec.http.corsPolicy

定义 CORS 策略(跨域资源共享,Cross-Origin Resource Sharing,CORS)

spec.http.headers

定义 header 操作规则,包括 request 和 response header 的更新,增加,移除操作

spec.tcp

定义一组有序的(优先匹配靠前的路由规则)应用于 TCP 流量的路由规则,该路由规则会应用于任何非 HTTP 和 TLS 的端口

spec.tcp.match

定义 TCP 流量路由的匹配规则列表,单个匹配规则项内所有条件是且关系,列表中多个匹配规则之间为或关系

spec.tcp.route

定义 TCP 连接转发的目的端

spec.tls

定义一组有序的(优先匹配靠前的路由规则)应用于未终止的 TLS 或 HTTPS 流量的路由规则,该路由规则会应用于网格内部的 service 端口命名为 https-,tls- 开头的流量,来自 gateway 的端口协议为 HTTPS, TLS 的未终止加密流量,Service Entry 使用 HTTPS, TLS 协议的端口。当 https-, tls- 端口未关联 VirtualService 规则时将会被视为 TCP 流量

spec.tls.match

定义 TLS 流量路由的匹配规则列表,单个匹配规则项内所有条件是且关系,列表中多个匹配规则之间为或关系

spec.tls.route

定义连接转发的目的端

2.1 route详解

HTTPRoute规则的功能是:满足HTTPMatchRequest条件的流量都被路由到HTTPRouteDestination,执行重定向(HTTPRedirect)、重写(HTTPRewrite)、重试(HTTPRetry)、故障注入(HTTPFaultInjection)、跨站(CorsPolicy)策略等。HTTPRoute不仅可以做路由匹配,还可以做一些写操作来修改请求本身。

2.2 match

match是进行路由的匹配条件。

其支持以下一些字段的定义:

  • uri、scheme、method、authority:这四个字段都是StringMatch类型,匹配请求时都支持exact、prefix和regex三种模式的匹配。
bash 复制代码
- match:
  # 代表匹配uri以advertisment开头的请求
  - uri:
      prefix: "/advertisment"
  • headers:匹配请求中的Header,是一个map类型。map的key是字符串类型,value是StringMatch类型。即对于每一个Header的值,都可以使用精确、前缀和正则三种方式进行匹配。
bash 复制代码
- match:
# 匹配header key为source value为north
  - headers:
    source:
      exact: north
  • port:表示请求服务的端口。大部分服务只开放一个端口,这也是在微服务中推荐的做法,在这种场景下可以不指定port;
  • sourceLabels:是一个map类型的键值对,表示请求来源负载匹配标签。这在很多情况有用,可以对一组服务都打一个相同的标签,然后使用sourceLabels字段对这些服务实施相同的流量规则。在Kubernetes平台上,这里的Label就是Pod上的标签。
bash 复制代码
http:
- match:    
  - sourceLabels:
      app: testA
      version: v2
  • gateway:表示规则应用的Gateway名称,语义同VirtualService 上面的gateway定义,是一个更细的Match条件,会覆盖在VirtualService上配置的gateway。
    在VirtualService中match字段都是数组类型。HTTPMatchRequest中的诸多属性如uri、header、method等都是"与"逻辑,而数组中几个元素间关系是"或"逻辑。

例子:

bash 复制代码
- match:
  - heraders:
      source:
        exact: north
    uri:
      prefix: "/advertisment"
  - uri:
      prefix: "/forecast"

match包含两个HTTPMatchRequest元素,其条件的语义是:headers中的source取值为"north",且uri以"/advertisment"开头的请求,或者uri以"/forecast"开头的请求。

2.3 路由目标(RouteDestination)

route字段是一个HTTPRouteDestination类型的数组,表示满足条件的流量目标。

在HTTPRouteDestination中主要有三个字段:destination(请求目标)、weight(权重)、和headers(HTTP头操作),destination是必选字段。

  • destination: 这个字段是一个Destination类型结构,通过host、subset和port三个属性来描述。表示最终将流量路由到此目标。host是Destination必选字段,表示在Istio中注册的服务名,建议写全域名。subset表示在host上定义的一个子集。例如,在灰度发布中将版本定义为subset,配置路由策略会将流量转发到不同版本的subset上。
bash 复制代码
.....
spec:
  hosts:
  - forecast
  http:
  - route:
    - destination:
        host: forecast
        subset: v2
    - destination:
        host: forecast
        subset: v1
  • weight: 表示流量分配的比例,在一个route下多个destination的weight总和要求是100。默认100,必填字段。示例:从原有的v1版本中切分20%的流量到v2版本,这也是灰度发布常用的一个流量策略,即不区分内容,平等的从总流量中切出一部分流量给新版本。
bash 复制代码
.....
spec:
  hosts:
  - forecast
  http:
  - route:
    - destination:
        host: forecast
        subset: v2
      weight: 20
    - destination:
        host: forecast
        subset: v1
      weight: 80
  • headers:提供了对HTTP header的一种操作机制,可以修改一次HTTP请求中的Request或Response的值,包含request和response两个字段。
  • request:表示在发请求给目标地址时修改Request的header。
  • response:表示在返回应答时修改Response的header
    对应的类型都是HeaderOperations类型,使用set、add、remove字段来定义对Header的操作。
  • set:使用map上的key和value覆盖Request和Response中对应的Header
  • add:追加map上的key和value到原有的Header
  • remove:删除在列表中华指定的Header

2.4 HTTP重定向(HTTPRedirect)

Istio对于HTTP除了做流量路由,还可以做适当的其他操作,很多原来需要在代码中进行的HTTP操作,在使用Istio后通过这些配置都可以达到同样的效果。

通过HTTPRedirect可以发送一个301重定向的应答给服务调用方,简单讲就是从一个URL到另外一个URL的永久重定向。用户输入一个URL,通过HTTPRedirect可以将其跳转到另一个URL。比较常见的场景:有一个在线网站,网址变了,通过这样的重定向,可以在用户输入老地址时跳转到新地址。比如:

示例:

bash 复制代码
apiVersion:networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: forecast
  namespace: weather
spec:
  hosts:
  - forecast
  http:
  - match:
    - uri: 
        prefix: /advertisement
    redirect:
      uri: /recommendation/activity
      authority: new-forecast

对forecast服务所有前缀是/advertisement的请求都会被重定向到new-forecast的/recommendation/activity地址。

2.5 HTTP重写(HTTPRewrite)

通过HTTP重写可以在将请求转发给目标服务前修改HTTP请求中指定部分的内容,不同于重定向,用户是可见,HTTP重写对用户是不可见的,因为是在服务端进行的。

和重定向配置类似,重写HTTP也包括uri和authority这两个字段。

  • uri:重写URL中的Path部分
  • authority:重写URL中的Authority

和HTTPRedirect规则稍有不同的是,HTTPRedirect的uri只能替换全部的Path,HTTPRewrite的uri是可以重写前缀的,即如果原来匹配条件是前缀匹配,则修改后只修改匹配到的前缀。

示例:

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: forecast
  namespace: weather
spec:
  hosts:
  - forecast
  http:
  - match:
    - uri:
        prefix: /advertisement
    rewrite:
      uri: /recommendation/activity
    route:
    - destination:
        host: forecast

前缀匹配/advertisement的请求,其请求uri中的这部分前缀会被/recommendation/activity替换

2.6 HTTP重试(HTTPRetry)

HTTP重试是解决很多请求异常最直接、简单的方法,尤其是在工作环境比较复杂的场景下,可提高总体的服务质量。

但是重试使用不当也会有问题,最糟糕的情况是重试一直不成功,反而增加延迟和性能开销。所以,根据系统运行环境、服务自身特点,配置适当的重试规则显得尤为重要。

HTTPRetry可以定义请求失败时的重试策略,重试策略包括重试次数、超时、重试条件等,这里分别描述相应的三个字段。

  • attempts:必选字段,定义重试的次数
  • perTryTimeout:每次重试超时的时间,单位可以是ms、s、m和h
  • retryOn:进行重试的条件,可以是多个条件,以逗号分隔

其中重试条件retryOn的取值可以包括以下几种:

  • 5xx:在上游服务返回5xx应答码,或者在没有返回时重试
  • gateway-error:类似于5xx异常,只对502、503和504应答码进行重试。
  • connect-failure:在链接上游服务失败时重试
  • retriable-4xx:在上游服务返回可重试的4xx应答码时执行重试。
  • refused-stream:在上游服务使用REFUSED_STREAM错误码重置时执行重试。
  • cancelled:gRPC应答的Header中状态码是cancelled时执行重试。
  • deadline-exceeded:在gRPC应答的Header中状态码是deadline-exceeded时执行重试
  • internal:在gRPC应答的Header中状态码是internal时执行重试
  • resource-exhausted:在gRPC应答的Header中状态码是resource-exhausted时执行重试
  • unavailable:在gRPC应答的Header中状态码是unavailable时执行重试。

示例:

配置了一个重试策略,在5xx,connect-failure条件下进行最多5次重试,每次重试超时为3秒

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
medata:
  name: forecast
  namespace: weather
spec:
  hosts:
  - forecast
  http:
  - route:
    - destination:
        host: forecast
    retries:
      attempts: 5
      perTryTimeout: 3s
      retryOn: 5xx,connect-failure

2.7 HTTP流量镜像(Mirror)

HTTP流量镜像指的是将流量转发到原目标地址的同时将流量给另外一个目标地址镜像一份。把生产环境中的实际流量镜像一份到另外一个系统上,完全不会对生产系统产生影响,这里只是镜像了一份流量,数据面代理只需要关注原来转发的流量就可以,不用等待镜像目标地址的返回。

bash 复制代码
apiVersion: networking.istio.io:v1alpha3
kind: VirtualService
metadata:
  name: forecast
  namespace: weather
spec:
  hosts:
    - forecast
  http:
  - route:
    - destination:
      host: forecast
      subset: v1
    mirror:
      host: forecast
      subset: v2

2.8 HTTP故障注入(HTTPFaultInjection)

HTTPFaultInjection通过delay和abort两个字段设置延时和中止两种故障,分别表示Proxy延迟转发HTTP请求和中止HTTP请求。

延迟故障注入HTTPFaultInjection中的延迟故障使用HTTPFaultInjection.Delay 类型描述延时故障,表示在发送请求前进行一段延时,模拟网络、远端服务负载均衡等各种原因导致的失败,主要是如下两个字段:

  • fixedDelay:一个必选字段,表示延迟时间,单位可以是毫秒、秒、分钟和小时,要求至少大于1毫秒
  • percentage:配置的延迟故障作用在多少的比例的请求上,通过这种方式可以只让部分请求发生故障。

示例:forecast服务v1版本上的1.5%的请求产生10秒的延时

bash 复制代码
......
route:
- destination:
    host: forecast
    subset: v1
fault:
  delay:
    percenttage:
      value: 1.5
    fixedDelay: 10s

请求中止故障注入HTTPFaultInjection 使用HTTPFaultInjection.Abort 描述中止故障,模拟服务端异常,给调用的客户端返回预先定义的错误状态码,主要有以下两个字段:

  • httpStatus:是一个必选字段,表示中止的HTTP状态码
  • percentage:配置中止故障作用在多少比例的请求上,通过这种方式可以只让部分请求发生故障,用法通延迟故障

示例:让forecast服务v1版本上1.5%的请求返回"500"代码

bash 复制代码
......
route:
- destination:
    host: forecast
    subset: v1
fault:
  abort:
    percnetage: 
      value: 1.5
    httpStatus: 500

2.9 HTTP跨域资源共享(CorsPolicy)

当一个资源向该资源所在服务器的不同的域发起请求时,就会产生一个跨域的HTTP请求。出于安全考虑,浏览器会限制从脚本发起的跨域HTTP请求。通过跨域资源共享CORS(Cross Origin Resource Sharing)机制可允许web应用服务器进行跨域访问控制,使跨域数据传递安全进行。在实现上是在HTTP Header中追加一些额外的信息来通知浏览器准许以上访问。

在 VirtualService 中可以对满足条件的请求配置跨域资源共享。有allowOrigin、allowMethods、allowHeader、exposeHeader、maxAge、allowCredentials,其实都是被转化为 Access-Control-* 的Header。 如下所示,允许源自news.com的GET方法的请求的访问:

bash 复制代码
......
http:
- route:
  - destination:
      host: forecast
    corsPolicy:
      allowOrigin:
      - news.com
      allowMethods:
      - GET
      maxAge: "2d"

3、DestinationRule

在讲解virtualService中,路由目标对象destination中会包含Service子集的subset字段,这个服务子集就是通过DestinationRule定义的。

比如:

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
 kind: DestinationRule
 metadata:
   name: customers-destination
 spec:
   host: customers.default.svc.cluster.local
   subsets:
   - name: v1
     labels:
       version: v1
   - name: v2
     labels:
       version: v2

上述定义了两个子集,v1和v2,同时根据标签来决定哪个Pod需要包含在子集中。

DestinationRule字段名称、字段含义

spec.host

关联 DestinationRule 配置的服务名称,可以是自动发现的服务(例如 Kubernetes service name),或通过 ServiceEntry 声明的 hosts。如填写的服务名无法在上述源中找到,则该 DestinationRule 中定义的规则无效

spec.subsets

定义服务的版本(subsets),版本可通过标签键值对匹配匹配服务中的endpoints。可以在 subsets 级覆盖流量策略配置

spec.trafficPolicy

定义流量策略,包括负载均衡、连接池、健康检查、TLS 策略

spec.trafficPolicy.loadBalancer

配置负载均衡算法,可配置:简单负载均衡算法(round robin, least conn, random...),一致性哈希(会话保持,支持按 header name,cookie,IP,query parameter 哈希),地域感知负载均衡算法

spec.trafficPolicy.connectionPool

配置与上游服务的连接量,可设置 TCP/HTTP 的连接池

spec.trafficPolicy.outlierDetection

配置从负载均衡池中驱逐不健康的 hosts

spec.trafficPolicy.tls

连接上游服务的 client 端 TLS 相关配置,与 PeerAuthentication 策略(server 端 TLS 模式配置)配合使用

spec.trafficPolicy.portLevelSettings

配置端口级别的流量策略,端口级别的流量策略会覆盖服务 / subsets 级别的流量策略配置

通过 DestinationRule,我们可以定义设置,如负载均衡配置、连接池大小、局部异常检测等,在路由发生后应用于流量。我们可以在trafficPolicy字段下设置流量策略。以下是这些设置:

  • 负载均衡器设置
  • 连接池设置
  • 局部异常点检测
  • 客户端 TLS 设置
  • 端口流量策略

3.1 负载均衡器设置

通过负载均衡器设置,我们可以控制目的地使用哪种负载均衡算法。

示例:

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
 kind: DestinationRule
 metadata:
   name: customers-destination
 spec:
   host: customers.default.svc.cluster.local
   trafficPolicy:
     loadBalancer:
       simple: ROUND_ROBIN #将负载均衡算法设置为轮询方式
   subsets:
   - name: v1
     labels:
       version: v1
   - name: v2
     labels:
       version: v2
  • simple字段:提供的负载均衡算法如下:

ROUND_ROBIN:轮询算法,如果未指定,则默认采用这种算法。

LEAST_CONN:最少连接算法,算法实现是从两个随机选择的服务后端选择一个活动请求数较少 的后端实例。

RANDOM:从可用的健康实例中随机选择一个。

PASSTHROUGH:直接转发连接到客户端连接的目标地址,即没有做负载均衡。

  • consistentHash字段:一致性hash,并根据 HTTP 头、cookies 或其他请求属性提供会话亲和性。

httpHeaderName: 基于Header。

httpCookie: 基于Cookie。

useSourceIp:基于源IP计算哈希值。

minimumRingSize:哈希环上虚拟节点数的最小值,节点数越多则负载均衡越精细。

bash 复制代码
trafficPolicy:
   loadBalancer:
     consistentHash:
       httpCookie:
         name: location
         ttl: 4s

3.2 连接池配置

可以在 TCP 和 HTTP 层面应用于上游服务的每个主机,我们可以用它们来控制连接量

TCP连接池配置

  • maxConnections:上游服务的所有实例建立的最大连接数,默认是 1024,属于 TCP层的配置,对于 HTTP,只用于 HTTP/1.1,因为 HTTP/2对每个主机都使用单个连接。
  • connectTimeout:TCP连接超时,表示主机网络连接超时,可以改善因调用服务变慢导致整个链路变慢的情况。
  • tcpKeepalive:设置TCP keepalives,是Istio1.1新支持的配置,定期给对端发送一个keepalive的探测包,判断连接是否可用。
bash 复制代码
spec:
   host: myredissrv.prod.svc.cluster.local
   trafficPolicy:
     connectionPool:
       tcp:
         maxConnections: 50
         connectTimeout: 25ms
         tcpKeepalive:
           probes: 5
           time: 3600
           interval: 60s

http连接池配置

  • http1MaxPendingRequests: 最大等待 HTTP 请求数,默认值是 1024,只适用于HTTP/1.1 的服务,因为 HTTP/2 协议的请求在到来时会立即复用连接,不会在连接池等待。
  • http2MaxRequests:最大请求数,默认是1024。只适用于HTTP/2服务,因为HTTP/1.1使用最大连接数maxConnections即可,表示上游服务的所有实例处理的最大请求数
  • maxRequestsPerConnection:每个连接的最大请求数。HTTP/1.1和HTTP/2连接池都遵循此参数。如果没有设置,则没有限制。设置为1时表示每个连接只处理一个请求,也就是禁用了Keep-alive
  • maxRetries:最大重试次数,默认是3,表示服务可以执行的最大重试次数。如果调用端因为偶尔抖动导致请求直接失败,则可能会带来业务损失,一般建议配置重试,若重试成功则可正常返回数据, 只不过比原来响应得慢一点,但重试次数太多会影响性能,要谨慎使用。
  • idleTimeout:空闲超时,定义在多长时间内没有活动请求则关闭连接。
    http可以和tcp配置使用,示例:
bash 复制代码
spec:
   host: myredissrv.prod.svc.cluster.local
   trafficPolicy:
     connectionPool:
       tcp:
         maxConnections: 80
         connectTimeout: 25ms
       http:
         http2MaxRequests: 800
         maxRequestsPerConnection: 10

配置最大80个连接,只允许最多有800个并发请求,每个连接 的请求数不超过10个,连接超时是25毫秒。

3.3 异常点检测

异常点检测是一个断路器的实现,它跟踪上游服务中每个主机(Pod)的状态。如果一个主机开始返回 5xx HTTP 错误,它就会在预定的时间内被从负载均衡池中弹出。对于 TCP 服务,Envoy 将连接超时或失败计算为错误。

两种健康检查:

  • 主动型的健康检查:定期探测目标服务实例,根据应答来判断服务实例的健康状态。如负载均衡器中的健康检查
  • 被动型的健康检查:通过实际的访问情况来找出不健康的实例,如isito中的异常点检查。

异常实例检查相关的配置:

  • consecutiveErrors:实例被驱逐前的连续错误次数,默认是 5。对于 HTTP 服务,返回 502、503 和 504 的请求会被认为异常;对于 TCP 服务,连接超时或者连接错误事件会被认为异常。
  • interval:驱逐的时间间隔,默认值为10秒,要求大于1毫秒,单位可以是时、分、毫秒。
  • baseEjectionTime:最小驱逐时间。一个实例被驱逐的时间等于这个最小驱逐时间乘以驱逐的次数。这样一个因多次异常被驱逐的实例,被驱逐的时间会越来越长。默认值为30秒,要求大于1毫秒,单 位可以是时、分、毫秒。
  • maxEjectionPercent:指负载均衡池中可以被驱逐的故障实例的最大比例,默认是10%,设置这个值是为了避免太多的服务实例被驱逐导致服务整体能力下降。
  • minHealthPercent:最小健康实例比例,是Istio 1.1新增的配置。当负载均衡池中的健康实例数的 例大于这个比例时,异常点检查机制可用;当可用实例数的比例小于这个比例时,异常点检查功能将被禁用,所有服务实例不管被认定为健康还是不健康,都可以接收请求。参数的默认值为50%。

下面是一个例子,它设置了 500 个并发的 HTTP2 请求(http2MaxRequests)的限制,每个连接不超过 10 个请求(maxRequestsPerConnection)到该服务。每 5 分钟扫描一次上游主机(Pod)(interval),如果其中任何一个主机连续失败 10 次(contracticalErrors),Envoy 会将其弹出 10 分钟(baseEjectionTime)。

bash 复制代码
trafficPolicy:
   connectionPool:
     http:
       http2MaxRequests: 500
       maxRequestsPerConnection: 10
   outlierDetection:
     consecutiveErrors: 10
     interval: 5m
     baseEjectionTime: 10m

3.4 TLS 设置

包含任何与上游服务连接的 TLS 相关设置。下面是一个使用提供的证书配置 mTLS 的例子:

bash 复制代码
trafficPolicy:
   tls:
     mode: MUTUAL
     clientCertificate: /etc/certs/cert.pem
     privateKey: /etc/certs/key.pem
     caCertificates: /etc/certs/ca.pem

mtls : 双向认证,客户机和服务器都通过证书颁发机构彼此验证身份。

由同一个 root ca 生成两套证书,即客户端证书和服务端证书。客户端使用 https 访问服务端时,双方会交换证书,并进行认证,认证通过方可通信。

其他支持的 TLS 模式有DISABLE(没有 TLS 连接),SIMPLE(在上游端点发起 TLS 连接),以及ISTIO_MUTUAL(与MUTUAL类似,使用 Istio 的 mTLS 证书)。

3.5 端口流量策略

在端口上配置流量策略,且端口上流量策略会覆盖全局的流量策略。关于配置方法与TrafficPolicy没有大差别,一个关键的差别字段就是port。

示例:

bash 复制代码
trafficPolicy:
   connectionPool:
     tcp:
       maxConnections: 80
   portLevelSettings:
   - port:
       number: 80
     loadBalancer:
       simple: LEAST_CONN
     connectionPool:
       tcp:
         maxConnections: 100
   - port:
       number: 8000
     loadBalancer:
       simple: ROUND_ROBIN

3.6 服务子集

Subset,定义服务的子集。

属性:

  • name: Subset的名字,为必选字段。通过VirtualService引用的就是这个名字。
  • labels:Subset上的标签,通过一组标签定义了属于这个Subset的服务实例。比如最常用的标识服务版本的Version标签。
  • trafficPolicy:应用到这个Subset上的流量策略。

示例:

bash 复制代码
.....
spec:
  hosts: forecast
  subsets:
  - name: v2
    labels:
      version: v2
    trafficPolicy:
     connectionPool:
       tcp:
         maxConnections: 80    # 给一个特定的Subset配置最大连接数

4. 实战

使用权重在不同的服务版本之间路由流量

先部署一个Gateway

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
         - '*'
bash 复制代码
[root@master weight]# kubectl apply -f gateway.yaml
#将default加入istio中
[root@master weight]# kubectl label namespace default istio-injection=enabled

部署一个应用(deploy和service)

bash 复制代码
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: web-frontend
   labels:
     app: web-frontend
 spec:
   replicas: 1
   selector:
     matchLabels:
       app: web-frontend
   template:
     metadata:
       labels:
         app: web-frontend
         version: v1
     spec:
       containers:
         - image: web-frontend:1.0.0
           imagePullPolicy: IfNotPresent
           name: web
           ports:
             - containerPort: 8080
           env:
             - name: CUSTOMER_SERVICE_URL
               value: 'http://customers.default.svc.cluster.local'
---
 kind: Service
 apiVersion: v1
 metadata:
   name: web-frontend
   labels:
     app: web-frontend
 spec:
   selector:
     app: web-frontend
   ports:
     - port: 80
       name: http
       targetPort: 8080
bash 复制代码
kubectl apply -f web-frontend.yaml

CUSTOMER_SERVICE_URL是说上述的web-frontend服务需要访问一个customers的服务,所以我们需要部署一个customer的服务

部署 Customers 服务的 v1版本

bash 复制代码
apiVersion: apps/v1
kind: Deployment             
metadata:
  name: customers-v1
  labels:
    app: customers
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: customers
      version: v1
  template:
    metadata:
      labels:
        app: customers
        version: v1
    spec:
      containers:
        - image: customers:1.0.0
          imagePullPolicy: IfNotPresent
          name: svc
          ports:
            - containerPort: 3000
---
kind: Service
apiVersion: v1
metadata:
  name: customers
  labels:
    app: customers
spec:
  selector:
    app: customers
  ports:
    - port: 80
      name: http
      targetPort: 3000
bash 复制代码
kubectl apply -f customers-v1.yaml

为web-frontend创建一个 VirtualService,并将其绑定到 Gateway 资源上

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: web-frontend
spec:
  hosts:
    - '*'
  gateways:
    - gateway
  http:
    - route:
        - destination:
            host: web-frontend.default.svc.cluster.local
            port:
              number: 80
bash 复制代码
kubectl apply -f web-frontend-vs.yaml

访问gateway

创建 DestinationRule,并定义两个子集,代表 v1 和 v2 版本

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: customers
spec:
  host: customers.default.svc.cluster.local
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2
bash 复制代码
kubectl apply -f customers-dr.yaml 

创建 VirtualService 并在目标中指定 v1子集

bash 复制代码
 apiVersion: networking.istio.io/v1alpha3
 kind: VirtualService
 metadata:
   name: customers
 spec:
   hosts:
     - 'customers.default.svc.cluster.local'
   http:
     - route:
         - destination:
             host: customers.default.svc.cluster.local
             port:
               number: 80
             subset: v1

每当有请求被发送到 Kubernetes Customers 服务时,它将被路由到同一服务的 v1子集

bash 复制代码
kubectl apply -f customers-vs.yaml

部署 v2 版的 Customers 服务

bash 复制代码
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: customers-v2
   labels:
     app: customers
     version: v2
 spec:
   replicas: 1
   selector:
     matchLabels:
       app: customers
       version: v2
   template:
     metadata:
       labels:
         app: customers
         version: v2
     spec:
       containers:
         - image: customers:2.0.0
           imagePullPolicy: IfNotPresent
           name: svc
           ports:
             - containerPort: 3000
bash 复制代码
kubectl apply -f customers-v2.yaml

使用weight字段并修改 VirtualService,使 50% 的流量被发送到 v1 子集,另 50% 发送到 v2 子集

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: customers
spec:
  hosts:
    - 'customers.default.svc.cluster.local'
  http:
    - route:
        - destination:
            host: customers.default.svc.cluster.local
            port:
              number: 80
            subset: v1
          weight: 50
        - destination:
            host: customers.default.svc.cluster.local
            port:
              number: 80
            subset: v2
          weight: 50
bash 复制代码
kubectl apply -f customers-50-50.yaml

测试刷新页面,在v1和v2版本之间切换,更改权重 刷新页面观察

使用请求属性在多个服务版本之间路由流量

部署 Gateway

bash 复制代码
 apiVersion: networking.istio.io/v1alpha3
 kind: Gateway
 metadata:
   name: gateway
 spec:
   selector:
     istio: ingressgateway
   servers:
     - port:
         number: 80
         name: http
         protocol: HTTP
       hosts:
         - '*'
bash 复制代码
kubectl apply -f gateway.yaml 

部署Web前端、Customers v1、Customers v2,以及相应的 VirtualServices 和 DestinationRule。一旦一切部署完毕,所有流量将被路由到

bash 复制代码
Customers v1。
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: web-frontend
   labels:
     app: web-frontend
 spec:
   replicas: 1
   selector:
     matchLabels:
       app: web-frontend
   template:
     metadata:
       labels:
         app: web-frontend
         version: v1
     spec:
       containers:
         - image: web-frontend:1.0.0
           imagePullPolicy: IfNotPresent
           name: web
           ports:
             - containerPort: 8080
           env:
             - name: CUSTOMER_SERVICE_URL
               value: 'http://customers.default.svc.cluster.local'
---
 kind: Service
 apiVersion: v1
 metadata:
   name: web-frontend
   labels:
     app: web-frontend
 spec:
   selector:
     app: web-frontend
   ports:
     - port: 80
       name: http
       targetPort: 8080
---
 apiVersion: networking.istio.io/v1alpha3
 kind: VirtualService
 metadata:
   name: web-frontend
 spec:
   hosts:
     - '*'
   gateways:
     - gateway
   http:
     - route:
         - destination:
             host: web-frontend.default.svc.cluster.local
             port:
               number: 80
bash 复制代码
kubectl apply -f web-frontend.yaml
bash 复制代码
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: customers-v1
   labels:
     app: customers
     version: v1
 spec:
   replicas: 1
   selector:
     matchLabels:
       app: customers
       version: v1
   template:
     metadata:
       labels:
         app: customers
         version: v1
     spec:
       containers:
         - image: customers:1.0.0
           imagePullPolicy: IfNotPresent
           name: svc
           ports:
             - containerPort: 3000
---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: customers-v2
   labels:
     app: customers
     version: v2
 spec:
   replicas: 1
   selector:
     matchLabels:
       app: customers
       version: v2
   template:
     metadata:
       labels:
         app: customers
         version: v2
     spec:
       containers:
         - image: customers:2.0.0
           imagePullPolicy: IfNotPresent
           name: svc
           ports:
             - containerPort: 3000
---
 kind: Service
 apiVersion: v1
 metadata:
   name: customers
   labels:
     app: customers
 spec:
   selector:
     app: customers
   ports:
     - port: 80
       name: http
       targetPort: 3000
---
 apiVersion: networking.istio.io/v1alpha3
 kind: VirtualService
 metadata:
   name: customers
 spec:
   hosts:
     - 'customers.default.svc.cluster.local'
   http:
     - route:
         - destination:
             host: customers.default.svc.cluster.local
             port:
               number: 80
             subset: v1
---
 apiVersion: networking.istio.io/v1alpha3
 kind: DestinationRule
 metadata:
   name: customers
 spec:
   host: customers.default.svc.cluster.local
   subsets:
     - name: v1
       labels:
         version: v1
     - name: v2
       labels:
         version: v2
bash 复制代码
kubectl apply -f customers.yaml

此时测试,流量都来自于v1.

更新VirtualService,如果请求中包含一个 headeruser: debug,就把流量路由到 Customers v2,没有就路由到v1

bash 复制代码
 apiVersion: networking.istio.io/v1alpha3
 kind: VirtualService
 metadata:
   name: customers
 spec:
   hosts:
     - 'customers.default.svc.cluster.local'
   http:
   - match:
     - headers:
         user:
           exact: debug
     route:
     - destination:
         host: customers.default.svc.cluster.local
         port:
           number: 80
         subset: v2
   - route:
       - destination:
           host: customers.default.svc.cluster.local
           port:
             number: 80
           subset: v1
bash 复制代码
kubectl apply -f customers-vs.yaml

测试curl -H "user: debug" http://GATEWAY_URL/

5、实战:观察故障注入

部署 Web 前端和 Customer V1 服务。然后,我们将在 Zipkin、Kiali 和 Grafana 中注入故障和延迟,并观察它们

部署Gateway

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: ingressgateway80
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - '*'
bash 复制代码
kubectl apply -f ingressgateway80.yaml 

部署 Web Frontend、Service 和 VirtualService。

bash 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-frontend
  labels:
    app: web-frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web-frontend
  template:
    metadata:
      labels:
        app: web-frontend
        version: v1
    spec:
      containers:
        - image: web-frontend:1.0.0
          imagePullPolicy: Always
          name: web
          ports:
            - containerPort: 8080
          env:
            - name: CUSTOMER_SERVICE_URL
              value: 'http://customers.default.svc.cluster.local'
---
kind: Service
apiVersion: v1
metadata:
  name: web-frontend
  labels:
    app: web-frontend
spec:
  selector:
    app: web-frontend
  ports:
    - port: 80
      name: http
      targetPort: 8080
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: web-frontend
spec:
  hosts:
    - '*'
  gateways:
    - ingressgateway80
  http:
    - route:
        - destination:
            host: web-frontend.default.svc.cluster.local
            port:
              number: 80
bash 复制代码
kubectl apply -f web-frontend.yaml

部署 Customers v1 和相应的资源

bash 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: customers-v1
  labels:
    app: customers
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: customers
      version: v1
  template:
    metadata:
      labels:
        app: customers
        version: v1
    spec:
      containers:
        - image: customers:1.0.0
          imagePullPolicy: Always
          name: svc
          ports:
            - containerPort: 3000
---
kind: Service
apiVersion: v1
metadata:
  name: customers
  labels:
    app: customers
spec:
  selector:
    app: customers
  ports:
    - port: 80
      name: http
      targetPort: 3000
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: customers
spec:
  host: customers.default.svc.cluster.local
  subsets:
    - name: v1
      labels:
        version: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: customers
spec:
  hosts:
    - 'customers.default.svc.cluster.local'
  http:
    - route:
        - destination:
            host: customers.default.svc.cluster.local
            port:
              number: 80
            subset: v1
      fault:
        delay:
          percent: 50
          fixedDelay: 5s
bash 复制代码
kubectl apply -f customers-delay.yaml

测试另开一个窗口,循环请求

while true; do curl http://$GATEWAY_URL/; done

打开Grafana ,进入Istio Service Dashboard,你会发现客户端请求持续时间图上的持续时间增加了

打开zipkin,选择serviceName和web-frontend.default,然后添加minDuration条件,输入 5s,点击搜索按钮,找到 trace。

点击其中一个 trace,打开详细信息页面。在详情页上,我们会发现持续时间是 5 秒。

单个 trace 有 4 个 span------点击 第3 个span,代表从web-frontend到客户服务的请求。你会注意到在细节中,response_flags标签设置为DI。"DI" 代表 "延迟注入",表示该请求被延迟了。

再次更新 VirtualService,这一次,我们将注入一个故障,对 50% 的请求返回 HTTP 500

bash 复制代码
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: customers
spec:
  hosts:
    - 'customers.default.svc.cluster.local'
  http:
    - route:
        - destination:
            host: customers.default.svc.cluster.local
            port:
              number: 80
            subset: v1
      fault:
        abort:
          httpStatus: 500
          percentage: 
            value: 50
bash 复制代码
kubectl apply -f customers-fault.yaml

回到 Grafana 并打开 Istio Service Dashboard,我们会注意到客户端的成功率在下降。

在 Zipkin 中也有类似的情况。如果我们再次搜索 trace(我们可以删除最小持续时间),我们会发现有错误的 trace 会以红色显示打开 Kiali,通过点击 Graph 项查看服务图。

你会注意到web-frontend服务有一个红色的边框

点击web-frontend服务,看看右边的侧边栏,你会发现 HTTP 请求的细节。图中显示了成功和失败的百分比,这两个数字都在 50% 左右,这与我们在 VirtualService 中设置的百分比值相一致。

相关推荐
小扳2 小时前
微服务篇-深入了解 MinIO 文件服务器(你还在使用阿里云 0SS 对象存储图片服务?教你使用 MinIO 文件服务器:实现从部署到具体使用)
java·服务器·分布式·微服务·云原生·架构
aherhuo13 小时前
kubevirt网络
linux·云原生·容器·kubernetes
catoop13 小时前
K8s 无头服务(Headless Service)
云原生·容器·kubernetes
小峰编程14 小时前
独一无二,万字详谈——Linux之文件管理
linux·运维·服务器·云原生·云计算·ai原生
运维小文16 小时前
K8S中的PV、PVC介绍和使用
docker·云原生·容器·kubernetes·存储
ζั͡山 ั͡有扶苏 ั͡✾16 小时前
Kubeadm+Containerd部署k8s(v1.28.2)集群(非高可用版)
云原生·容器·kubernetes
Hadoop_Liang16 小时前
Kubernetes ConfigMap的创建与使用
云原生·容器·kubernetes
老猿讲编程17 小时前
技术发展历程:从 CORBA 到微服务
微服务·云原生·架构
小诸葛的博客1 天前
istio-proxy不打印访问日志怎么解决?
云原生·istio