service mesh istio微服务实验

1,110 阅读6分钟
原文链接: www.maogx.win

简介

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

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

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

前端项目基于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

架构图如下:

下载实验仓库

git clone https://github.com/mgxian/istio-test

部署服务

cd istio-test
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

暴露服务

# 使用istio提供的ingress功能
# 暴露js和python服务让k8s集群外部访问
kubectl apply -f istio/ingress-python.yml
kubectl apply -f istio/ingress-js.yml

# 查看
kubectl get ingress

测试访问

# 配置hosts解析
# 11.11.11.112为其中一个node的ip
11.11.11.112 istio-test.will

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

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

流量管理

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

把所有流量导向v1版本

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

# 查看路由规则
istioctl get routerule

# 访问浏览器测试
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

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

# 创建路由规则
# 根据浏览器的不同返回不同内容
istioctl create -f istio/route-rule-js-by-agent.yml

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


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

# 此步骤创建的第一个路由规则保留不删除,为下面做实验提供方便
istioctl delete -f istio/route-rule-python-by-header.yml

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

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

# 此时规则如下
# 所有chrome浏览器都走v1版本服务
# 所有firefox浏览器都走v2版本服务
# react----->Python2.7.15----->Gogo1.9.6
# vue----->Python3.6.5----->Gogo1.10.2

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

指定权重进行流量分隔

# 指定权重把流量分隔
# 25%流量路由到v1版本
# 75%流量路由到v2版本

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

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

集群内访问公开服务

# 默认情况下,启用了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 egressrule

# 测试
# 使用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
# 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

故障管理

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

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

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

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

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

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

超时模拟

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

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

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

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

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

模拟服务500故障

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

# 所有请求延迟5秒钟,然后失败其中的10%
...
route:
- labels:
version: v1
httpFault:
delay:
fixedDelay: 5s
abort:
percent: 10
httpStatus: 400

熔断器

# 熔断器规则需要应用到路由规则上
# 需要先配置至少一个路由规则

# 设置路由规则
istioctl create -f istio/route-rule-go-default.yml

# 设置熔断规则
istioctl create -f istio/route-rule-go-cb.yml

# 查看规则
istioctl get destinationpolicy

# 创建测试用的fortio
kubectl apply -f <(istioctl kube-inject --debug -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-default.yml
istioctl delete -f istio/route-rule-go-cb.yml

限流

动态设置服务qps

# 创建service-python默认路由
# 经测试,一定要配置路由规则,否则无法完成限流
# 所以极有可能限流是配置在路由规则上的
# 在路由时进行限流统计
istioctl create -f istio/route-rule-python-default.yml

# 配置一个速率限制的memquota适配器
# 默认设置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 <(istioctl kube-inject -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-python/env

# 测试
# 会出现部分请求不正常
# python 返回 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-python/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/route-rule-python-default.yml
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

流量镜像

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

# 创建默认策略
# 默认所有流量路由到v1
istioctl create -f istio/route-rule-go-default.yml

# 创建测试用的fortio
kubectl apply -f <(istioctl kube-inject -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-default.yml
istioctl delete -f istio/route-rule-go-mirror.yml

清理

# 删除相关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/ingress-python.yml
kubectl delete -f istio/ingress-js.yml
istioctl delete routerule $(istioctl get routerule | grep RouteRule | awk '{print $1}')

参考文档