istio(三)探索”目标规则(DestinationRule)”概念
接前文《istio(二)探索”虚拟服务”概念》,我们还有一点点剩余的内容需要继续探索,但相信你已经渐入佳境,所以下面的内容就单枪直入了。
本文要了解的概念叫做“目标规则”,英文DestinationRule,我们构造的场景是:
我们部署2套nginx deployment,分别叫做a版本和b版本,仅配置1个service同时负载均衡到2个版本的deployment。
当我们从ubuntu容器访问nginx.com时,如果访问的url是http://nginx.com/index.html则流量被路由给a版本deployment,否则路由给b版本deployment。
这其实展示了一种多版本小流量发布的效果,从中我们就理解了destinationRule。
准备工作
创建2套nginx deployment:
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 48 49 50 |
apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx-a spec: selector: matchLabels: run: my-nginx version: a replicas: 2 template: metadata: labels: run: my-nginx version: a spec: containers: - name: my-nginx image: nginx --- apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx-b spec: selector: matchLabels: run: my-nginx version: b replicas: 2 template: metadata: labels: run: my-nginx version: b spec: containers: - name: my-nginx image: nginx --- apiVersion: v1 kind: Service metadata: name: my-nginx spec: ports: - port: 80 protocol: TCP selector: run: my-nginx |
注意service通过run:my-nginx的标签selector同时关联到了2个deployment的POD,也就是说这个service关联了一共4个POD可以被istio服务发现。
同时注意到,两组POD的version标签分别是a和b,这样istio对这个service的4个POD又能进一步划分出2个a POD和2个b POD,这对于我们控制istio做蓝绿版本路由很关键。
创建ubuntu作为客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
apiVersion: apps/v1 kind: Deployment metadata: name: my-ubuntu spec: selector: matchLabels: run: my-ubuntu replicas: 1 template: metadata: labels: run: my-ubuntu spec: containers: - name: my-ubuntu image: ubuntu:18.04 command: ["/bin/bash"] args: ["-c", "while true;do sleep 1;done"] |
一会我们从ubuntu容器里调用nginx.com,然后被ubuntu容器里的istiod控制流量调度。
我们现在的POD列表:
1 2 3 4 5 6 7 |
root@debian:~/kubernetes/demo/desti# kubectl get pod NAME READY STATUS RESTARTS AGE my-nginx-a-674c6d4fb7-qhtzc 2/2 Running 0 37m my-nginx-a-674c6d4fb7-zxmwf 2/2 Running 0 37m my-nginx-b-96b46977d-f5q7p 2/2 Running 0 37m my-nginx-b-96b46977d-phcw8 2/2 Running 0 37m my-ubuntu-f464f6885-j4szh 2/2 Running 0 16m |
探索
我们知道istiod默认已经服务发现my-nginx service的4个POD了,但是我们一会要把/index.html路由到a版本的2个POD,其他URI则路由器到b版本的2个POD,显然我们要告诉istio如何从my-nginx的4个POD中划分出2组来,这就是destination rule要做的事情。
当然首先还是靠virtualservice做路由选路,之后才轮到destination rule做事。
配置规则
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 |
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: vs-nginx namespace: default spec: hosts: - nginx.com http: - name: route-a match: - uri: exact: /index.html route: - destination: host: my-nginx subset: a fault: delay: percentage: value: 100 fixedDelay: 2s - name: route-b route: - destination: host: my-nginx subset: b --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: dest-nginx spec: host: my-nginx subsets: - name: a labels: version: a - name: b labels: version: b |
virtualservice识别HTTP协议host是nginx.com的请求,然后配置了route-a和route-b两个路由规则:
- 如果URI是/index.html则把流量打给my-nginx这个service的subset=a的2个POD。
- 否则把流量打给my-nginx这个service的subset=b的2个POD。
这里destination的host是istio服务注册表中的服务,是要对应后端若干endpoint(POD)的,这里my-nginx实际就是default命名空间中my-nginx service的域名简写,istio能够根据它找到背后的POD列表。
一开始接触istio很容易搞不明白virtualservice的host和destination host的关系,前者是7层协议的识别与匹配的Host,后者是服务注册&发现的host(就是service域名或者通过serviceentry注册的外部域名)。
有了这个认识,我们就知道subset大概要表达的就是my-nginx这个service中的一个POD子集,这时候我们就需要引入DestinationRule了。
在DetinationRule中的host应该是istio服务发现&注册中存在的域名(就是service域名或者通过serviceentry注册的外部域名),然后subsets可以对该service域名关联的POD进一步根据label进行子集划分,这里我们就是根据version:a和version:b划分出来的,这样virtualservice中就可以通过subset字段分别路由了。
验证效果
进入ubuntu容器,安装好curl,然后多访问几次nginx.com/index.html:
1 |
root@my-ubuntu-f464f6885-jmkg5:/# curl nginx.com/index.html |
然后查看一下a组nginx有/index.html访问日志,b组完全没有。
如果反过来调用nginx.com的话,则会发现b组nginx有日志,a没有。
同时,你会发现请求nginx.com/index.html会卡顿1秒才返回,这是因为我们在virtualservice中注入了1秒的延迟,你可以回看上面的配置。
我们看一下istio下给ubuntu sidecar下发的路由(istioctl proxy-config route my-ubuntu-f464f6885-jmkg5 -o json ):
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
{ "name": "nginx.com:80", "domains": [ "nginx.com", "nginx.com:80" ], "routes": [ { "name": "route-a", "match": { "path": "/index.html", "caseSensitive": true }, "route": { "cluster": "outbound|80|a|my-nginx.default.svc.cluster.local", "timeout": "0s", "retryPolicy": { "retryOn": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", "numRetries": 2, "retryHostPredicate": [ { "name": "envoy.retry_host_predicates.previous_hosts" } ], "hostSelectionRetryMaxAttempts": "5", "retriableStatusCodes": [ 503 ] }, "maxGrpcTimeout": "0s" }, "metadata": { "filterMetadata": { "istio": { "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/vs-nginx" } } }, "decorator": { "operation": "my-nginx.default.svc.cluster.local:80/index.html" }, "typedPerFilterConfig": { "envoy.filters.http.fault": { "@type": "type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault", "delay": { "fixedDelay": "2s", "percentage": { "numerator": 10000, "denominator": "MILLION" } } } } }, { "name": "route-b", "match": { "prefix": "/" }, "route": { "cluster": "outbound|80|b|my-nginx.default.svc.cluster.local", "timeout": "0s", "retryPolicy": { "retryOn": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", "numRetries": 2, "retryHostPredicate": [ { "name": "envoy.retry_host_predicates.previous_hosts" } ], "hostSelectionRetryMaxAttempts": "5", "retriableStatusCodes": [ 503 ] }, "maxGrpcTimeout": "0s" }, |
可见我们在virtualservice中配的2个HTTP规则都在,并且分别打到了2个cluster:
- outbound|80|a|my-nginx.default.svc.cluster.local
- outbound|80|b|my-nginx.default.svc.cluster.local
然后再看一下istio下发的这两个cluster(istioctl proxy-config cluster my-ubuntu-f464f6885-jmkg5 -o json):
1 2 3 4 5 6 7 8 9 10 11 |
], "name": "outbound|80|a|my-nginx.default.svc.cluster.local", "type": "EDS", "edsClusterConfig": { "edsConfig": { "ads": {}, "initialFetchTimeout": "0s", "resourceApiVersion": "V3" }, "serviceName": "outbound|80|a|my-nginx.default.svc.cluster.local" }, |
和
1 2 3 4 5 6 7 8 9 10 |
"name": "outbound|80|b|my-nginx.default.svc.cluster.local", "type": "EDS", "edsClusterConfig": { "edsConfig": { "ads": {}, "initialFetchTimeout": "0s", "resourceApiVersion": "V3" }, "serviceName": "outbound|80|b|my-nginx.default.svc.cluster.local" }, |
可见2个Cluster都是直接从istiod那边EDS服务发现过来的,并且a和b两个subset在cluster服务发现的名字上都有体现,说明istiod那边已经做了区分。
总结
destination用来控制服务注册中某个名字(可以是service,也可以是外部服务serviceentry)的访问行为,而virtualservice用来控制7层流量的路由行为,它们俩管控的层级与概念完全不同,一定要注意理解这块,其他就没啥了。
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~

One thought on “istio(三)探索”目标规则(DestinationRule)”概念”