Istio 的频率限制
本来这个应该是作为第三天“零散功能点”介绍的,结果目标规则部分遇到一个 bug 一直没得到修正,就拖 着了——然后后来发现自己这个想法挺无知的——零散的功能点非常多,非常大,而且文档非常弱,很难搞,只好 逐个介绍了。
简介
调用频率限制这个功能其实也是比较常见的东西了。这里就不多做介绍了。下面简单粗暴的介绍一下测试要完 成的目标。
测试中我们将使用两个服务,服务叫 workload,客户端叫 sleep,workload 服务正常返回群众喜闻乐见 的 “Hello World”,而 sleep 仅用来做 Shell 方便测试。
测试过程将为 workload 建立规则:
- 对于任意访问,十秒钟之内仅能访问两次;
- 对于来自 sleep 的访问,十秒钟之内仅能访问一次
下面所有内容都在 default 命名空间进行
建立测试环境
编辑 workload.yaml 以及 sleep.yaml 之后, 我们使用如下命令运行(这两部分源码,只有些乌七八糟的标签和命名有点问题,其他很普通。 见最后页)
istioctl kube-inject -f workload.yaml | kubectl apply -f -
istioctl kube-inject -f sleep.yaml | kubectl apply -f -
kubectl get pods
确定 Pod 成功启动,也可以使用 curl 测试 NodePort,会得到 40x 的错误
页面。
建立规则
- quota.yaml
这部分内容规则来自:https://istio.io/docs/reference/config/mixer/template/quota.html
这个对象用于设置检测服务来源、目标的标准维度,下面我们设置的是利用服务名称来检测。
这部分可能存在 bug,官方介绍的 source 定义是
source.labels["app"] | source.service | "unknown"
我的理解是说首先获取服务 Pod 的 app 标签内容,如果没有,再获取服务名称。但是后面我们会看到, 删除source.labels["app"]
的定义之后,最终生效的依旧是 app 标签,这也是为什么给 workload 加上这么多奇怪标签的原因。
apiVersion: config.istio.io/v1alpha2
kind: quota
metadata:
name: requestcount
spec:
dimensions:
source: source.service | "unknown"
sourceVersion: source.labels["version"] | "unknown"
destination: destination.service | "unknown"
- handler.yaml
这一对象的定义来自:https://istio.io/docs/reference/config/mixer/adapters/memquota.html
下面的代码对应我们之前的目的,任意访问都是每 10 秒钟两次,而特定源和目标的访问则是每 10 秒钟一 次。
其中 quotas 部分,name 字段的来源是上面的 quota.yaml
这个不知道是不是 bug,必须写上半个 fqdn。
dimentions 字段则需要使用 quota.yaml 定义的维度。这里的维度可以比 quota.yaml 中定义的 维度宽松(也就是少,例如定义三个,只使用其中的两个),可以理解,这也符合匹配的规则。
apiVersion: config.istio.io/v1alpha2
kind: memquota
metadata:
name: handler
spec:
quotas:
- name: requestcount.quota.default
maxAmount: 2
validDuration: 10s
overrides:
- dimensions:
destination: workload-pod.default.svc.cluster.local
source: sleep.default.svc.cluster.local
maxAmount: 1
validDuration: 10s
- rule.yaml
这部分的定义参考:https://istio.io/docs/reference/config/mixer/policy-and-telemetry-rules.html#istio.mixer.v1.config.Rule
这个对象的作用就是,对于符合筛选条件的服务调用,使用对应的 handler 进行处理。
这里的 handler 和 requestcount 两个字段的内容,也需要是实例名称.对象类型
的方式。
apiVersion: config.istio.io/v1alpha2
kind: rule
metadata:
name: quota
spec:
actions:
- handler: handler.memquota
instances:
- requestcount.quota
测试运行
集群外
使用 curl 重复访问 NodePort 三次,会发现第三次出现:RESOURCE_EXHAUSTED:Quota is exhausted for: RequestCount#
这说明我们设置的通用规则生效了。
集群内
在 sleep pod 中同样执行 curl 三次,发现第二次就开始出现超限说明。
上面两种情况,在超过时间窗口限制之后,都会自动恢复。
幕后
定义 quota 对象,在系统中会展现为一系列的计数器,计数器的维度就是quota 中的维度的笛卡尔积。如果在 validDuration 的时间窗口过期之前调用次数超过了 maxAmount 规定,Mixer 就会返回 RESOURCE_EXHAUSTED 给 Envoy,Envoy 则会反馈 429 代码给调用方。
结论
文档不完善的开源系统。。坑真大啊!
源码
Workload 的 Deployment 和 Service:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: workload-app
version: http200
name: workload-200
spec:
replicas: 1
selector:
matchLabels:
app: workload-pod
version: http200
template:
metadata:
creationTimestamp: null
labels:
app: workload-pod
version: http200
spec:
containers:
- image: php:7.0-apache
imagePullPolicy: IfNotPresent
name: http200
ports:
- containerPort: 80
name: http
protocol: TCP
dnsPolicy: ClusterFirst
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
labels:
app: workload-service
version: http200
name: workload-200
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
nodePort: 30200
selector:
app: workload-pod
version: http200
sessionAffinity: None
type: NodePort
Sleep 的定义
apiVersion: v1
kind: Service
metadata:
name: sleep
labels:
app: sleep
spec:
ports:
- port: 80
name: http
selector:
app: sleep
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: sleep
spec:
replicas: 1
template:
metadata:
labels:
app: sleep
spec:
containers:
- name: sleep
image: tutum/curl
command: ["/bin/sleep","infinity"]
imagePullPolicy: IfNotPresent