istio(四)外部如何访问集群中的服务 — ingressgateway
接前文《istio(三)探索”目标规则(DestinationRule)”概念》,此前我们主要分析了istio如何实现集群内服务间的调用或者是集群内调用外部服务,本文则分析外部如何调用集群内的服务,这是依靠istio的ingress-gateway网关组件实现的。
准备工作
istio已经帮我们部署了一个叫做istio-ingressgateway的deployment,它充当集群边缘的反向代理:
1 2 3 4 5 |
root@debian:~/kubernetes/demo/gateway# kubectl get deployment -n istio-system NAME READY UP-TO-DATE AVAILABLE AGE istio-egressgateway 1/1 1 1 15d istio-ingressgateway 1/1 1 1 15d istiod 1/1 1 1 15d |
1 2 3 4 5 |
root@debian:~/kubernetes/demo/gateway# kubectl get services -n istio-system -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR istio-egressgateway ClusterIP 10.110.58.240 <none> 80/TCP,443/TCP 15d app=istio-egressgateway,istio=egressgateway istio-ingressgateway LoadBalancer 10.104.34.183 <pending> 15021:32496/TCP,80:32013/TCP,443:31319/TCP,31400:30620/TCP,15443:30884/TCP 15d app=istio-ingressgateway,istio=ingressgateway istiod ClusterIP 10.98.203.126 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 15d app=istiod,istio=pilot |
我们看到istio-ingressgateway这个service向集群外暴露访问地址和端口,包括80、443这些经典端口,因此我们从集群外肯定可以访问到ingress gateway服务。
其实ingress gateway也是跑的envoy,同样是从istiod那边下发规则,只不过和应用POD中的sidecar envoy角色不一样,在这里充当集群边缘代理角色。
接下来,我们要实现的效果是:
1,从集群外调用ingress-gateway访问http://nginx.com时,由gateway将请求转发给k8s集群内的my-nginx deployment。
2,从集群外调用ingress-gateway访问https://nginx.com时,由gateway将请求转给俄罗斯的nginx官网。
我们先部署my-nginx,这样istio才能服务发现这个K8S服务:
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 |
apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: selector: matchLabels: run: my-nginx replicas: 1 template: metadata: labels: run: my-nginx 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 |
然后我们将nginx.com解析到ingress gateway的loadbalancer ip,这样后续就可以通过gateway反向代理访问到背后的真实服务了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
root@debian:~/kubernetes/demo/gateway# cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 debian # The following lines are desirable for IPv6 capable hosts ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters #199.232.69.194 github.global.ssl.fastly.net #140.82.112.3 github.com 127.0.0.1 host.minikube.internal 192.168.2.200 control-plane.minikube.internal 10.104.34.183 nginx.com |
然后我们开始探索如何配置ingress gateway实现反向代理。
探索
gateway的对外监听
我们为上面的istio-ingressgateway配置一个gateway规则,这样istio-ingressgateway就会按我们的要求进行端口监听,等待外部流量进入:
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: Gateway metadata: name: nginx-gw spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - "nginx.com" - port: number: 443 name: https protocol: HTTPS hosts: - "nginx.com" tls: mode: PASSTHROUGH |
selector过滤该gateway规则下发到哪一组istio-ingressgateway POD,这是因为istio没有限制我们部署多套不同的边缘istio gateway,可能我们会有为不同业务隔离网关的需求。
接着,我们创建了2个路由规则:
- 监听80端口,识别HTTP协议,尝试匹配HOST是nginx.com,则命中该路由。
- 监听443端口,从TLS的SNI中提取HOST,尝试匹配HOST是nginx.com,则命中该路由,因为TLS流量没法中间劫持明文,所以做TCP连接的流量透传(passthrough)。
gateway的向内转发
当ingress gateway程序完成路由匹配后,它会将请求进行向内转发,目标是host: nginx.com,此时ingress gateway是主动发起连接的一方,会开始走virtualservice进行clientside的路由匹配,我们需要进一步配置virtualservice:
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 |
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: vs-nginx spec: hosts: - nginx.com gateways: - nginx-gw http: - route: - destination: host: my-nginx tls: - match: - port: 443 sniHosts: - nginx.com route: - destination: host: nginx.com --- apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: nginx-svcentry spec: hosts: - nginx.com location: MESH_EXTERNAL ports: - number: 443 protocol: TLS name: https resolution: DNS |
这里有2条路由规则+1个外部服务注册:
- serviceEntry向istio服务注册中心注册了nginx.com这样一个服务名,因此在virtualservice中有一条路由规则通过SNI识别nginx.com访问然后destination给nginx.com这个服务名,也就是这个serviceentry,从而转发TCP透明转发给了俄罗斯的nginx官网。
- 另外一条路由就是匹配HTTP的host是nginx.com,然后destination给my-nginx这个k8s service。
因此,当ingress gateway向集群内转发时,就会被上述virtualservice进行计算,然后决定流量的转发去向,要么是HTTPS发往俄罗斯,要么是HTTP发往集群内my-nginx。
验证效果
我们可以直接从宿主机上调用nginx.com,查看HTTP和HTTPS两种情况的表现(在文章开始的时候,我们已经将nginx.com解析到istio ingress gateway的对外IP)。
这是HTTP访问,的确是my-nginx的返回页面:
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 |
root@debian:~/kubernetes/demo/gateway# curl http://nginx.com <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html> |
然后访问HTTPS,的确是俄罗斯官网返回的重定向HTML:
1 2 3 4 5 6 7 8 |
root@debian:~/kubernetes/demo/gateway# curl https://nginx.com <html> <head><title>301 Moved Permanently</title></head> <body> <center><h1>301 Moved Permanently</h1></center> <hr><center>nginx/1.19.10</center> </body> </html> |
说明我们对istio-ingressgateway的工作流程理解正确,我们可以进一步看一下gateway里面下发的envoy规则。
首先是listener:
1 2 3 4 5 6 |
root@debian:~/kubernetes/demo/gateway# istioctl proxy-config listener -nistio-system istio-ingressgateway-8657768d87-l8gcb ADDRESS PORT MATCH DESTINATION 0.0.0.0 8080 ALL Route: http.80 0.0.0.0 8443 SNI: nginx.com Cluster: outbound|443||nginx.com 0.0.0.0 15021 ALL Inline Route: /healthz/ready* 0.0.0.0 15090 ALL Inline Route: /stats/prometheus* |
其实istio ingress gateway的service映射端口是从80->8080,443->8443,所以我们看到gateway内是监听的8080和8443端口,分别来应对HTTP和HTTPS流量。
对于8080端口,我们看到它其实走的是Route http.80路由规则,也就是对80端口访问的进一步路由,一会我们会看到。
对于443端口,我们看到它直接下发了一个listener去match SNI中nginx.com,然后直接destination到了outbound|443||nginx.com这个外部集群(俄罗斯官网)。
接着看route 80规则:
1 2 3 4 5 |
root@debian:~/kubernetes/demo/gateway# istioctl proxy-config route -nistio-system istio-ingressgateway-8657768d87-l8gcb NAME DOMAINS MATCH VIRTUAL SERVICE http.80 nginx.com /* vs-nginx.default * /healthz/ready* * /stats/prometheus* |
看到http.80路由会对nginx.com识别,然后向集群内转发时经过vs-nginx这个virtualservice进一步路由,我们可以看JSON发现细节:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
istioctl proxy-config route -nistio-system istio-ingressgateway-8657768d87-l8gcb -o json [ { "name": "http.80", "virtualHosts": [ { "name": "nginx.com:80", "domains": [ "nginx.com", "nginx.com:*" ], "routes": [ { "match": { "prefix": "/" }, "route": { "cluster": "outbound|80||my-nginx.default.svc.cluster.local", |
在Route时匹配nginx.com:80的HTTP请求,会转发给outbound|80||my-nginx.default.svc.cluster.local集群,也就是my-nginx。
总结
ingress网关处于集群边缘,将流量从集群外引入集群内的对应服务,可以从4层或者7层来做规则,结合virtualservice和destinationrule可以实现有趣的效果。
istio还有一个egress网关,可以强制引导集群内调用外部服务的流量经过egress gateway离开集群,从而实时访问外部的强制管控能力,由于用的较少就不专门写了。
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~
