istio-0.8长期支持版微服务实验

简介

本实验通过在k8s上部署istio,实现微服务的基础功能。其中会涉及到服务的限流,超时,熔断,降级,流量分隔,A/B测试等功能。实验之前需要安装k8s和istio,请参考之前文章。注意开启istio的自动注入功能,并在 default namespace 启用自动注入功能。

本实验的服务间调用关系如下:

本实验采用时下流行的前后端分离模式

前端项目基于vue/react实现

前端调用python实现的API接口

python服务调用后端node实现的服务和lua实现的服务

node服务调用go实现的服务

  • —->service-js
  • —->service-python
    • —->service-lua
    • —->service-node
      • —->service-go

本实验使用的语言技术栈:

  • vue/react
  • python2/3
  • node8/10
  • openresty1.11 /1.13
  • go1.10/1.9

架构图如下:

istio-0.8版本配置发生很大的变化,由原来的v1alpha1升级到了v1alpha3,主要变化如下

  • 使用virtualservicedestinationrule 代替原来的routerule
  • 使用gateway代替了原来的ingress

每个virtualservice都要指定要去向哪一个destinationrulevirtualservice指定访问哪个地址时会使用这个路由,相当于nginx上配置的vhosts

下载实验仓库

1
2
git clone https://github.com/mgxian/istio-test
cd istio-test && git checkout v2

部署服务

1
2
3
4
5
6
7
8
9
10
kubectl apply -f service/go/v1/go-v1.yml
kubectl apply -f service/go/v2/go-v2.yml
kubectl apply -f service/python/v1/python-v1.yml
kubectl apply -f service/python/v2/python-v2.yml
kubectl apply -f service/js/v1/js-v1.yml
kubectl apply -f service/js/v2/js-v2.yml
kubectl apply -f service/node/v1/node-v1.yml
kubectl apply -f service/node/v2/node-v2.yml
kubectl apply -f service/lua/v1/lua-v1.yml
kubectl apply -f service/lua/v2/lua-v2.yml

创建Gateway

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 使用istio提供的Gateway功能
# 暴露js和python服务让k8s集群外部访问
istioctl create -f istio/gateway.yml
istioctl create -f istio/gateway-virtualservice.yml

# 查看
istioctl get gateway
istioctl get virtualservice

# 测试访问
INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
NODE_NAME=$(kubectl get no | grep '<none>' | head -1 | awk '{print $1}')
NODE_IP=$(ping -c 1 $NODE_NAME | grep PING | awk '{print $3}' | tr -d '()')
export GATEWAY_URL=$NODE_IP:$INGRESS_PORT
echo "curl -I http://$GATEWAY_URL/"
echo "curl -I http://$NODE_IP/"

# 访问返回404表示正确

配置测试访问环境

1
2
3
4
5
6
7
8
9
10
11
12
# 配置hosts解析
# 11.11.11.112为其中一个node的ip
11.11.11.112 istio-test.will

curl -I http://istio-test.will/

# 使用curl
curl -I istio-test.will
curl -s istio-test.will | egrep "vue|React"

# 此时如果用浏览器,可能会出会页面显示不正常的情况。
# 因为此时请求会轮流分发到后端js服务的v1/v2版本,因此css/js并不能正常加载

流量管理

根据请求的信息,把流量路由到服务的不同版本。实验过程如果没有达到预期效果,很有可能是因为存在路由规则冲突,而且没有设置优先级,可以先删除之前设置的路由规则或者把优先级设置高一点。

把所有流量导向v1版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 清理之前创建的gateway相关的路由规则
istioctl delete -f istio/gateway-virtualservice.yml

# 创建路由规则
istioctl create -f istio/gateway-virtualservice-v1.yml
istioctl create -f istio/route-rule-all-v1.yml

# 查看路由规则
istioctl get virtualservice
istioctl get destinationrule

# 访问浏览器测试
http://istio-test.will/

# 此时你会看到react app的界面
# 点击发射按钮,会发送ajax请求到python服务
# 由于把所有流量都导向了v1版本
# 多次点击发射按钮会得到一样的内容
# react----->Python2.7.15----->Gogo1.9.6

# 清除路由规则
istioctl delete -f istio/route-rule-all-v1.yml
istioctl delete -f istio/gateway-virtualservice-v1.yml

根据请求把流量导向不同版本(A/B测试)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 创建路由规则
# 根据浏览器的不同返回不同内容
istioctl create -f istio/route-rule-js-by-agent.yml

# 查看路由规则
istioctl get virtualservice
istioctl get destinationrule

# 使用访问浏览器
# 如果你用chrome浏览器你会看到react app的界面
# 如果你用firefox浏览器你会看到vue app的界面
# 多次点击发射按钮,会获取到不同的内容

# 清除路由规则
istioctl delete -f istio/route-rule-js-by-agent.yml

# 根据前端app不同使用不同版本的python服务
istioctl create -f istio/route-rule-python-by-header.yml

# 清除路由规则
istioctl delete -f istio/route-rule-python-by-header.yml

根据源服务把流量导向不同版本

1
2
3
4
5
6
7
8
9
10
# 先创建如下路由方便测试访问
# 根据浏览器的不同返回不同内容
istioctl create -f istio/route-rule-js-by-agent.yml

# 创建路由规则
istioctl create -f istio/route-rule-go-by-source.yml

# 清除路由规则
istioctl delete -f istio/route-rule-js-by-agent.yml
istioctl delete -f istio/route-rule-go-by-source.yml

指定权重进行流量分隔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 指定权重把流量分隔
# 25%流量路由到v1版本
# 75%流量路由到v2版本

# 先创建如下路由方便测试访问
# 根据浏览器的不同返回不同内容
istioctl create -f istio/route-rule-js-by-agent.yml

# 创建路由规则
istioctl create -f istio/route-rule-go-v1-v2.yaml

# 清除路由规则
istioctl delete -f istio/route-rule-js-by-agent.yml
istioctl delete -f istio/route-rule-go-v1-v2.yaml

集群内访问公开服务

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
# 默认情况下,启用了istio的服务是无法访问外部url的
# 如果需要访问外部url,需要使用egress进行配置
# egress同样支持设置路由规则

# http
istioctl create -f istio/egress-rule-http-bin.yml

# tcp
istioctl create -f istio/egress-rule-tcp-wikipedia.yml

# 查看
istioctl get serviceentry

# 测试
# 使用exec进入作为测试源使用的pod
kubectl apply -f istio/sleep.yaml
kubectl get pods
export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
kubectl exec -it $SOURCE_POD -c sleep bash

# http测试
curl http://httpbin.org/headers
curl http://httpbin.org/delay/5

# tcp测试
curl -o /dev/null -s -w "%{http_code}\n" https://www.wikipedia.org
curl -s https://en.wikipedia.org/wiki/Main_Page | grep articlecount | grep 'Special:Statistics'

# 清理
istioctl delete -f istio/egress-rule-http-bin.yml
istioctl delete -f istio/egress-rule-tcp-wikipedia.yml
kubectl delete -f istio/sleep.yaml

故障管理

  • 调用超时设置和重试设置
  • 故障注入,模拟服务故障

设置超时时间与模拟服务超时故障

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 先创建如下路由方便测试访问
# 根据浏览器的不同返回不同内容
istioctl create -f istio/route-rule-js-by-agent.yml

# 设置python服务超时时间
istioctl create -f istio/route-rule-node-timeout.yml

# 模拟go服务超时故障
istioctl create -f istio/route-rule-go-delay.yml

# 使用浏览器访问并打开调试面板查看网络标签(按F12键)
# 多次点击发射按钮观察响应时间
# 会看到部分50%的请求会返回500错误

# 清除路由规则
istioctl delete -f istio/route-rule-js-by-agent.yml
istioctl delete -f istio/route-rule-node-timeout.yml
istioctl delete -f istio/route-rule-go-delay.yml

超时模拟

设置重试与模拟服务500故障

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 先创建如下路由方便测试访问
# 根据浏览器的不同返回不同内容
istioctl create -f istio/route-rule-js-by-agent.yml

# 设置python服务超时时间
istioctl create -f istio/route-rule-node-retry.yml

# 模拟go服务超时故障
istioctl create -f istio/route-rule-go-abort.yml

# 使用浏览器访问并打开调试面板查看网络标签(按F12键)
# 多次点击发射按钮观察响应时间
# 会看到部分请求会返回500错误

# 清除路由规则
istioctl delete -f istio/route-rule-js-by-agent.yml
istioctl delete -f istio/route-rule-node-retry.yml
istioctl delete -f istio/route-rule-go-abort.yml

模拟服务500故障

超时和服务故障模拟配合使用

1
2
3
4
5
6
7
8
9
10
11
# 所有请求延迟5秒钟,然后失败其中的10%
...
route:
- labels:
version: v1
httpFault:
delay:
fixedDelay: 5s
abort:
percent: 10
httpStatus: 400

熔断器

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
# 设置熔断规则
istioctl create -f istio/route-rule-go-cb.yml

# 查看规则
istioctl get destinationrule

# 创建测试用的fortio
kubectl apply -f istio/fortio-deploy.yaml

# 正常访问测试
FORTIO_POD=$(kubectl get pod | grep fortio | awk '{ print $1 }')
kubectl exec -it $FORTIO_POD -c fortio /usr/local/bin/fortio -- load -curl http://service-go/env

# 测试熔断 2并发
kubectl exec -it $FORTIO_POD -c fortio /usr/local/bin/fortio -- load -c 2 -qps 0 -n 20 -loglevel Warning http://service-go/env

# 测试熔断 3并发
kubectl exec -it $FORTIO_POD -c fortio /usr/local/bin/fortio -- load -c 3 -qps 0 -n 20 -loglevel Warning http://service-go/env

# 增加并发会看到失败的请求占比增高

# 查看状态
# upstream_rq_pending_overflow 表示被熔断的请求数
kubectl exec -it $FORTIO_POD -c istio-proxy -- sh -c 'curl localhost:15000/stats' | grep service-go | grep pending

# 清理
kubectl delete -f istio/fortio-deploy.yaml
istioctl delete -f istio/route-rule-go-cb.yml

限流

动态设置服务qps

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
44
45
46
47
# 配置 memquota, quota, rule, QuotaSpec, QuotaSpecBinding 启用限速
# 默认设置500qps
istioctl create -f istio/ratelimit-handler.yaml

# 配置速率限制实例和规则
istioctl create -f istio/ratelimit-rule-service-go.yaml

# 查看
kubectl get memquota -n istio-system
kubectl get quota -n istio-system
kubectl get rule -n istio-system
kubectl get quotaspec -n istio-system
kubectl get quotaspecbinding -n istio-system

# 创建测试用的fortio
kubectl apply -f istio/fortio-deploy.yaml

# 正常访问测试
FORTIO_POD=$(kubectl get pod | grep fortio | awk '{ print $1 }')
kubectl exec -it $FORTIO_POD -c fortio /usr/local/bin/fortio -- load -curl http://service-node/env

# 测试
# 会出现部分请求不正常
# node 返回 code 500
# go 返回 code 429
kubectl exec -it $FORTIO_POD -c fortio /usr/local/bin/fortio -- load -qps 20 -n 100 -loglevel Warning http://service-node/env
kubectl exec -it $FORTIO_POD -c fortio /usr/local/bin/fortio -- load -qps 50 -n 100 -loglevel Warning http://service-go/env


# 清理
istioctl delete -f istio/ratelimit-handler.yaml
istioctl delete -f istio/ratelimit-rule-service-go.yaml
kubectl delete -f istio/fortio-deploy.yaml


# 带条件的速率限制
apiVersion: config.istio.io/v1alpha2
kind: rule
metadata:
name: quota
namespace: istio-system
spec:
match: source.namespace != destination.namespace
actions:
- handler: handler.memquota
instances:
- requestcount.quota

流量镜像

复制服务的流量到别一个镜像服务,一般用于线上新上服务的测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 创建测试用的fortio
kubectl apply -f istio/fortio-deploy.yaml

# 正常访问测试
FORTIO_POD=$(kubectl get pod | grep fortio | awk '{ print $1 }')
kubectl exec -it $FORTIO_POD -c fortio /usr/local/bin/fortio -- load -curl http://service-go/env

# 查看v1的日志
kubectl logs -f $(kubectl get pods | grep service-go-v1 | awk '{print $1}'| head -n 1) -c service-go

# 查看v2的日志
# 再开一个终端查看日志
kubectl logs -f $(kubectl get pods | grep service-go-v2 | awk '{print $1}'| head -n 1) -c service-go

# 创建镜像规则
istioctl create -f istio/route-rule-go-mirror.yml

# 测试多次访问
kubectl exec -it $FORTIO_POD -c fortio /usr/local/bin/fortio -- load -c 10 -qps 0 -t 10s -loglevel Warning http://service-go/env

# 清理
kubectl delete -f istio/fortio-deploy.yaml
istioctl delete -f istio/route-rule-go-mirror.yml

清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 删除相关deploy和svc
kubectl delete -f service/go/v1/go-v1.yml
kubectl delete -f service/go/v2/go-v2.yml
kubectl delete -f service/python/v1/python-v1.yml
kubectl delete -f service/python/v2/python-v2.yml
kubectl delete -f service/js/v1/js-v1.yml
kubectl delete -f service/js/v2/js-v2.yml
kubectl delete -f service/node/v1/node-v1.yml
kubectl delete -f service/node/v2/node-v2.yml
kubectl delete -f service/lua/v1/lua-v1.yml
kubectl delete -f service/lua/v2/lua-v2.yml

# 清除路由规则
kubectl delete -f istio/gateway.yml
kubectl delete -f istio/gateway-virtualservice.yml
istioctl delete destinationrule $(istioctl get destinationrule | grep 'service-' | awk '{print $1}')
istioctl delete virtualservice $(istioctl get virtualservice | grep 'service-' | awk '{print $1}')

参考文档