Istio边界流量-Ingress Gateway拓展用法

实例架构

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

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

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

reviews 微服务有 3 个版本:

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

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

创建实例

1
2
3
4
5
6
7
8
9
10
11
$ kubectl create namespace bookinfo
# istio 源码包中携带此实例
$ kubectl -n bookinfo create -f /opt/istio/samples/bookinfo/platform/kube/bookinfo.yaml
$ kubectl -n bookinfo get pods
NAME READY STATUS RESTARTS AGE
details-v1-5974b67c8-wclnd 1/1 Running 0 34s
productpage-v1-64794f5db4-jsdbg 1/1 Running 0 33s
ratings-v1-c6cdf8d98-jrfrn 1/1 Running 0 33s
reviews-v1-7f6558b974-kq6kj 1/1 Running 0 33s
reviews-v2-6cb6ccd848-qdg2k 1/1 Running 0 34s
reviews-v3-cc56b578-kppcx 1/1 Running 0 34s

使用ingress访问productpage服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cat > ingress-productpage.yaml <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: productpage
namespace: bookinfo
spec:
rules:
- host: bookinfo.od.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: productpage
port:
number: 9080
EOF

如何实现更细粒度的流量管控?

注入sidecar容器

如何注入sidecar容器

  1. 使用istioctl kube-inject

    1
    $ kubectl -n bookinfo apply -f <(istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml)
  2. 为命名空间打label

    1
    2
    # 给命名空间打标签,这样部署在该命名空间的服务会自动注入sidecar容器
    $ kubectl label namespace dafault istio-injection=enabled

注入bookinfo

1
$ kubectl -n bookinfo apply -f <(istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml) 

服务流量治理

作用:实现ingress解决不了的按照比例分配流量

配置访问productpage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cat > productpage-gateway.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: productpage-gateway
namespace: bookinfo
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- bookinfo.istio.com
EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat > productpage-virtualservice.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: vs-bookinfo
namespace: bookinfo
spec:
gateways:
- productpage-gateway
hosts:
- bookinfo.istio.com
http:
- route:
- destination:
host: productpage
port:
number: 9080
EOF

权重路由

需求一:只想访问reviews-v3,所有流量都调度到v3也就是红心上面。其他不调度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ cat > destination-rule-reviews.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
namespace: bookinfo
spec:
host: reviews
trafficPolicy:
loadBalancer:
simple: RANDOM
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3
EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cat > virtual-service-reviews-v3.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
namespace: bookinfo
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v3
EOF

应用资源配置清单

1
2
3
4
kubectl apply -f  destination-rule-reviews.yaml
kubectl apply -f virtual-service-reviews-v3.yaml

# 访问productpage测试,此时发现页面一直停在v3版本不会变化,需求实现。

需求二:实现如下流量分配:

reivews-v1 导入90%流量
reviews-v2 导入10%流量
reviews-v3 不导入流量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 同样的方式,刚才已经定义过v1、v2、v3的去向了,现在只需要增加v1和v2路由规则即可
$ cat > virtual-service-reviews-90-10.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
namespace: bookinfo
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 90
- destination:
host: reviews
subset: v2
weight: 10
EOF

$ kubectl apply -f virtual-service-reviews-90-10.yaml
# 此时,再次访问,v3就不会出现了

访问路径路由

需求:意思是根据不同路径访问不同页面

实现效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 修改外部流量进入网格后的规则

$ cat > virtualservice-bookinfo-with-uri-path.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: vs-bookinfo
namespace: bookinfo
spec:
gateways:
- productpage-gateway
hosts: # 列出VirtualService的hosts,可以是IP、DNS名称、FQDN或*
- book.istio.com
http: # 指定符合规则的流量到那些Destination,支持HTTP/1.1、HTTP2,及gRPC等协议
- name: productpage-route
match: # 指定具体的匹配规则
- uri:
prefix: /productpage
- uri:
prefix: /static
route:
- destination: # 满足条件会把流量打入具体的Destination
host: productpage
- name: reviews-route
match:
- uri:
prefix: /reviews
route:
- destination:
host: reviews
- name: ratings-route
match:
- uri:
prefix: /ratings
route:
- destination:
host: ratings
- route:
- destination:
host: productpage
port:
number: 9080
EOF

访问:

1
2
3
4
$ curl http://book.istio.com/ratings/1        
{"id":1,"ratings":{"Reviewer1":5,"Reviewer2":4}}
$ curl http://book.istio.com/reviews/1
{"id": "1","podname": "reviews-v3-6dc9897554-8g9v2","clustername": "null","reviews": [{ "reviewer": "Reviewer1", "text": "An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!", "rating": {"stars": 5, "color": "red"}},{ "reviewer": "Reviewer2", "text": "Absolutely fun and entertaining. The play lacks thematic depth when compared to other plays by Shakespeare.", "rating": {"stars": 4, "color": "red"}}]}

实际的访问对应为:

book.istio.com/productpage -> productpage:8090/productpage
book.istio.com/ratings -> ratings:9080/ratings
book.istio.com/reviews -> reviews:9080/reviews

我们可以发现virtualservice的配置中有并未指定service的port端口,转发同样可以生效?

注意,若service中只有一个端口,则不用显式指定端口号,会自动转发到该端口中

路径重写

如果想实现rewrite的功能,隐藏真实url路径名称,很简单:

book.istio.com/rate -> ratings:8090/ratings

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
- name: ratings-route
match:
- uri:
prefix: /rate
rewrite: # 如果match中存在多个uri是不能使用reweite的
uri: "/ratings"
route:
- destination:
host: ratings
...


# 验证
$ curl http://book.istio.com/rate/1
{"id":1,"ratings":{"Reviewer1":5,"Reviewer2":4}}

DestinationRule 转发策略

默认会使用轮询策略,此外也支持如下负载均衡模型,可以在 DestinationRule 中使用这些模型,将请求分发到特定的服务或服务子集。

NameDescription
UNSPECIFIEDNo load balancing algorithm has been specified by the user. Istio will select an appropriate default.
RANDOMThe random load balancer selects a random healthy host. The random load balancer generally performs better than round robin if no health checking policy is configured.
PASSTHROUGHThis option will forward the connection to the original IP address requested by the caller without doing any form of load balancing. This option must be used with care. It is meant for advanced use cases. Refer to Original Destination load balancer in Envoy for further details.
ROUND_ROBINA basic round robin load balancing policy. This is generally unsafe for many scenarios (e.g. when enpoint weighting is used) as it can overburden endpoints. In general, prefer to use LEAST_REQUEST as a drop-in replacement for ROUND_ROBIN.
LEAST_REQUESTThe least request load balancer spreads load across endpoints, favoring endpoints with the least outstanding requests. This is generally safer and outperforms ROUND_ROBIN in nearly all cases. Prefer to use LEAST_REQUEST as a drop-in replacement for ROUND_ROBIN.
LEAST_CONNDeprecated. Use LEAST_REQUEST instead.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: my-destination-rule
spec:
host: my-svc
trafficPolicy: #默认的负载均衡策略模型为随机
loadBalancer:
simple: RANDOM # 随机的策略
subsets:
- name: v1 #subset1,将流量转发到具有标签 version:v1 的 deployment 对应的服务上
labels:
version: v1
- name: v2 #subset2,将流量转发到具有标签 version:v2 的 deployment 对应的服务上,指定负载均衡为轮询
labels:
version: v2
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN # 轮询策略
- name: v3 #subset3,将流量转发到具有标签 version:v3 的 deployment 对应的服务上
labels:
version: v3

https://preliminary.istio.io/latest/zh/docs/reference/config/networking/destination-rule/#LoadBalancerSettings-SimpleLB

使用https

方式一:把证书绑定在外部的nginx中, nginx 443端口监听外网域名并转发请求到Istio Ingress网关IP+http端口 ,如果使用公有云lb的话(如slb,clb),可以在lb层绑定证书

方式二:在istio侧使用证书

https://istio.io/latest/docs/tasks/traffic-management/ingress/secure-ingress/

根据header头路由策略

该项目默认登录时会将用户名写入head的请求头,这个一般在代码中实现,我们可以根据这个特性,利用istop获取请求的head头信息。

需求:v2为正式版本,v3为测试版本,内测用户wangxiansen登录时访问v3界面,其他用户或者未登录访问v2界面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ cat > virtual-service-reviews-header.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
namespace: bookinfo
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: wangxiansen
route:
- destination:
host: reviews
subset: v3
- route:
- destination:
host: reviews
subset: v2
EOF

$ kubectl apply -f virtual-service-reviews-header.yaml

# https://github.com/nocalhost/bookinfo-productpage/blob/main/productpage.py
# 刷新观察http://book.istio.com.com/productpage

更多支持的匹配类型可以在此处查看。

https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPMatchRequest

故障注入

故障注入与超时机制

在一个微服务架构的系统中,为了让系统达到较高的健壮性要求,通常需要对系统做定向错误测试。比如电商中的订单系统、支付系统等若出现故障那将是非常严重的生产事故,因此必须在系统设计前期就需要考虑多样性的异常故障并对每一种异常设计完善的恢复策略或优雅的回退策略,尽全力规避类似事故的发生,使得当系统发生故障时依然可以正常运作。而在这个过程中,服务故障模拟一直以来是一个非常繁杂的工作。

istio提供了无侵入式的故障注入机制,让开发测试人员在不用调整服务程序的前提下,通过配置即可完成对服务的异常模拟。目前,包含两类:

  • abort:非必配项,配置一个 Abort 类型的对象。用来注入请求异常类故障。简单的说,就是用来模拟上游服务对请求返回指定异常码时,当前的服务是否具备处理能力。
  • delay:非必配项,配置一个 Delay 类型的对象。用来注入延时类故障。通俗一点讲,就是人为模拟上游服务的响应时间,测试在高延迟的情况下,当前的服务是否具备容错容灾的能力。

延迟与超时

目前针对wangxiansen登录用户,访问服务的示意为:

1
2
3
productpage --> reviews v2 --> ratings
\
-> details

可以通过如下方式,为ratings服务注入2秒的延迟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ cat > virtualservice-ratings-2s-delay.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
namespace: bookinfo
spec:
hosts:
- ratings
http:
- fault:
delay:
percentage:
value: 100
fixedDelay: 2s
match:
- headers:
end-user:
exact: wangxiansen
route:
- destination:
host: ratings
- route:
- destination:
host: ratings
EOF

$ kubectl apply -f virtualservice-ratings-2s-delay.yaml
# 再次访问http://book.istio.com/productpage,用wangxiansen用户登录可以明显感觉2s的延迟

可以查看对应的envoy的配置:

1
$ istioctl pc r ratings-v1-5967f59c58-9lmc5.bookinfo --name 9080 -ojson

此时的调用为:

1
2
3
productpage --> reviews v2 -(延迟2秒)-> ratings
\
-> details

此时,为reviews服务添加请求超时时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ kubectl -n bookinfo edit vs reviews
...
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: wangxiansen
route:
- destination:
host: reviews
subset: v3
- route:
- destination:
host: reviews
subset: v2
timeout: 0.5s
...

此使的调用关系为:

1
2
3
productpage -(0.5秒超时)-> reviews v2 -(延迟2秒)-> ratings
\
-> details

此时,如果使用wangxiansen用户,则会出现只延迟,不会失败的情况。

删除延迟:

1
$ kubectl -n bookinfo delete vs ratings

状态码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ cat > virtualservice-details-aborted.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: details
namespace: bookinfo
spec:
hosts:
- details
http:
- fault:
abort:
percentage:
value: 50
httpStatus: 500
route:
- destination:
host: details
EOF
$ kubectl apply -f virtualservice-details-aborted.yaml

# 再次刷新查看details的状态,查看productpage的日志
$ kubectl -n bookinfo logs -f $(kubectl -n bookinfo get po -l app=productpage -ojsonpath='{.items[0].metadata.name}') -c istio-proxy
[2023-03-01T04:40:17.274Z] "GET /details/0 HTTP/1.1" 500 FI fault_filter_abort - "-" 0 18 0 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36" "3791d5f0-9b41-9cc6-9ade-fe351ae6107e" "details:9080" "-" outbound|9080||details.bookinfo.svc.cluster.local - 192.168.137.113:9080 172.7.100.4:59160 - -

流量镜像

介绍

很多情况下,当我们对服务做了重构,或者我们对项目做了重大优化时,怎么样保证服务是健壮的呢?在传统的服务里,我们只能通过大量的测试,模拟在各种情况下服务的响应情况。虽然也有手工测试、自动化测试、压力测试等一系列手段去检测它,但是测试本身就是一个样本化的行为,即使测试人员再完善它的测试样例,无法全面的表现出线上服务的一个真实流量形态 。

流量镜像的设计,让这类问题得到了最大限度的解决。流量镜像讲究的不再是使用少量样本去评估一个服务的健壮性,而是在不影响线上坏境的前提下将线上流量持续的镜像到我们的预发布坏境中去,让重构后的服务在上线之前就结结实实地接受一波真实流量的冲击与考验,让所有的风险全部暴露在上线前夕,通过不断的暴露问题,解决问题让服务在上线前夕就拥有跟线上服务一样的健壮性。由于测试坏境使用的是真实流量,所以不管从流量的多样性,真实性,还是复杂性上都将能够得以展现,同时预发布服务也将表现出其最真实的处理能力和对异常的处理能力。

实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 准备httpbin v1
$ cat > httpbin-v1.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin-v1
namespace: bookinfo
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
EOF

$ istioctl kube-inject -f httpbin-v1.yaml | kubectl create -f -
$ curl $(kubectl -n bookinfo get po -l version=v1,app=httpbin -ojsonpath='{.items[0].status.podIP}')/headers
{
"headers": {
"Accept": "*/*",
"Host": "172.7.100.3",
"User-Agent": "curl/7.29.0",
"X-B3-Sampled": "1",
"X-B3-Spanid": "e1ab3a80b34475ac",
"X-B3-Traceid": "41308935982594d1e1ab3a80b34475ac"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 准备httpbin v2
$ cat > httpbin-v2.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin-v2
namespace: bookinfo
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v2
template:
metadata:
labels:
app: httpbin
version: v2
spec:
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
EOF

$ istioctl kube-inject -f httpbin-v2.yaml | kubectl create -f -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Service文件
$ cat > httpbin-svc.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: httpbin
namespace: bookinfo
labels:
app: httpbin
spec:
ports:
- name: http
port: 8000
targetPort: 80
selector:
app: httpbin
EOF

$ kubectl apply -f httpbin-svc.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ cat > httpbin-destinationRule.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: httpbin
namespace: bookinfo
spec:
host: httpbin
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
EOF

$ kubectl apply -f httpbin-destinationRule.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 使用www.bookinfo.com/httpbin访问,因此直接修改bookinfo这个virtualservice即可
$ kubectl -n bookinfo get vs
NAME GATEWAYS HOSTS
bookinfo [bookinfo-gateway] [bookinfo.com]
gateway-front-tomcat [productpage-gateway] [bookinfo.luffy.com]
reviews [reviews]
$ kubectl -n bookinfo edit vs bookinfo
#添加httpbin的规则
...
- match:
- uri:
prefix: /httpbin
name: httpbin-route
rewrite:
uri: /
route:
- destination:
host: httpbin
subset: v1
...
# 访问http://www.bookinfo.com/httpbin/headers,查看日志

此时发起请求,查看httpbin-v1的日志有刷,v2没反应,因为上面vs规则只匹配了v1,现在我们希望在访问v1的同时,流量也转发v2一份

流量镜像配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 为httpbin-v1添加mirror设置,mirror点为httpbin-v2
$ kubectl -n bookinfo edit vs bookinfo
...
- match:
- uri:
prefix: /httpbin
name: httpbin-route
rewrite:
uri: /
route:
- destination:
host: httpbin
subset: v1
mirror:
host: httpbin
subset: v2
mirror_percent: 100
...

此时访问页面即v1服务,v2后台日期同步刷新,说明接收到流量镜像。

重试与熔断

重试

在网络环境不稳定的情况下,会出现暂时的网络不可达现象,这时需要重试机制,通过多次尝试来获取正确的返回信息。 istio 可以通过简单的配置来实现重试功能,让开发人员无需关注重试部分的代码实现,专心实现业务代码。

准备实践

浏览器访问http://www.bookinfo.com/httpbin/status/502

1
2
3
# 此时查看httpbin-v1的日志,显示一条状态码为502的日志
$ kubectl -n bookinfo logs -f httpbin-v1-5967569c54-sp874 -c istio-proxy
[2023-03-01T05:16:41.929Z] "GET /httpbin/status/502 HTTP/1.1" 502 - via_upstream - "-" 0 0 1 1 "10.1.1.1,172.7.100.1" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36" "fa5bee29-d86b-9022-87b5-d635dfd6c005" "book.istio.com" "172.7.100.7:80" inbound|80|| 127.0.0.6:40871 172.7.100.7:80 172.7.100.1:0 outbound_.8000_.v1_.httpbin.bookinfo.svc.cluster.local default

我们为httpbin服务设置重试机制,这里设置如果服务在 2 秒内没有返回正确的返回值,就进行重试,重试的条件为返回码为5xx,重试 3 次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ kubectl -n bookinfo edit vs bookinfo
...
- match:
- uri:
prefix: /httpbin
mirror:
host: httpbin
subset: v2
mirror_percent: 100
name: httpbin-route
retries:
attempts: 3
perTryTimeout: 2s
retryOn: 5xx
rewrite:
uri: /
route:
- destination:
host: httpbin
subset: v1
...

# 再次查看httpbin-v1的日志,显示四条状态码为502的日志

熔断

熔断(Circuit Breaker),原是指当电流超过规定值时断开电路,进行短路保护或严重过载保护的机制 。对于微服务系统而言,熔断尤为重要,它可以使系统在遭遇某些模块故障时,通过服务降级等方式来提高系统核心功能的可用性,得以应对来自故障、潜在峰值或其他未知网络因素的影响。

准备环境

Istio 是通过 Envoy Proxy 来实现熔断机制的,Envoy 强制在网络层面配置熔断策略,这样就不必为每个应用程序单独配置或重新编程。下面就通过一个示例来演示如何为 Istio 网格中的服务配置熔断的连接数、请求数和异常检测。istio的熔断本质是一种限流

创建测试客户端

我们已经为 httpbin 服务设置了熔断策略,接下来创建一个 Java 客户端,用来向后端服务发送请求,观察是否会触发熔断策略。这个客户端可以控制连接数量、并发数、待处理请求队列,使用这一客户端,能够有效的触发前面在目标规则中设置的熔断策略。该客户端的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
deployment yaml 内容如下:

# cat > httpbin-client-deploy.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin-client-v1
namespace: bookinfo
spec:
replicas: 1
selector:
matchLabels:
app: httpbin-client-v1
version: v1
template:
metadata:
labels:
app: httpbin-client-v1
version: v1
spec:
containers:
- image: ceposta/http-envoy-client-standalone:latest
imagePullPolicy: IfNotPresent
name: httpbin-client
command: ["/bin/sleep","infinity"]
EOF

这里我们会把给客户端也进行 Sidecar 的注入,以此保证 Istio 对网络交互的控制:

1
$ kubectl apply -f <(istioctl kube-inject -f httpbin-client-deploy.yaml)

验证

先尝试通过单线程(NUM_THREADS=1)创建一个连接,并进行 5 次调用(默认值:NUM_CALLS_PER_CLIENT=5):

1
2
$ CLIENT_POD=$(kubectl get pod -n bookinfo | grep httpbin-client | awk '{ print $1 }')
$ kubectl -n bookinfo exec -it $CLIENT_POD -c httpbin-client -- sh -c 'export URL_UNDER_TEST=http://httpbin:8000/get export NUM_THREADS=1 && java -jar http-client.jar'

下面尝试把线程数提高到 2:

1
$ kubectl -n bookinfo exec -it $CLIENT_POD -c httpbin-client -- sh -c 'export URL_UNDER_TEST=http://httpbin:8000/get export NUM_THREADS=2 && java -jar http-client.jar'

创建DestinationRule, 针对 httpbin 服务设置熔断策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: httpbin
namespace: bookinfo
spec:
host: httpbin
trafficPolicy:
connectionPool:
tcp:
maxConnections: 1
http:
http1MaxPendingRequests: 1
maxRequestsPerConnection: 1
EOF
  • maxConnections : 限制对后端服务发起的 HTTP/1.1 连接数,如果超过了这个限制,就会开启熔断。
  • maxPendingRequests : 限制待处理请求列表的长度, 如果超过了这个限制,就会开启熔断。
  • maxRequestsPerConnection : 在任何给定时间内限制对后端服务发起的 HTTP/2 请求数,如果超过了这个限制,就会开启熔断。

可以查看设置的熔断策略在envoy的配置片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ istioctl pc cluster httpbin-client-v1-56b86fb85c-vg5pp.bookinfo --fqdn httpbin.bookinfo.svc.cluster.local -ojson
...
"connectTimeout": "10s",
"maxRequestsPerConnection": 1,
"circuitBreakers": {
"thresholds": [
{
"maxConnections": 1,
"maxPendingRequests": 1,
"maxRequests": 4294967295,
"maxRetries": 4294967295
}
]
},
...

istio的熔断策略本质上是一种代理层面的补救措施,不会侵入代码层,若要想真正实现异常规避,最好的方式还是通过代码实现重试或者异常处理比较有效