赞
踩
在Service Mesh架构中,Istio通过流量劫持机制,让Pod中的应用访问其他服务时都经过Istio代理,实现对流量的控制。基于Istio可以实现请求路由、服务发现和负载均衡、故障处理、故障注入和规则配置等功能。,因为所有Pod中的进出流量都经过Istio的数据面,所以也可以实现日志记录和追踪。如下是Istio的架构图:
下边通过一个示例操作,看下Istio是怎么实现流量劫持的,流量被劫持后又是怎么转到目的地的。
部署两个Pod,分别表示客户端和服务端,通过客户端发起请求,然后看请求整个链路的转发过程。
# 创建一个namespace
kubectl create ns sidecar
# 注入sidecar
kubectl label ns sidecar istio-injection=enabled
# 部署nginx pod
kubectl apply -f nginx.yaml -n sidecar
# 部署toolbox pod
kubectl apply -f toolbox.yaml -n sidecar
部署一个nginx pod,并创建service。
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx --- apiVersion: v1 kind: Service metadata: name: nginx spec: ports: - name: http port: 80 protocol: TCP targetPort: 80 selector: app: nginx
toolbox其实就是一个centos,可以在其中执行curl发送请求。
apiVersion: apps/v1 kind: Deployment metadata: name: toolbox spec: replicas: 1 selector: matchLabels: app: toolbox template: metadata: labels: app: toolbox access: "true" spec: containers: - name: toolbox image: centos command: - tail - -f - /dev/null
执行kubectl get pod,deployment,svc,endpoints -n sidecar -o wide
看下环境情况:
curl http://10.102.37.221
访问Nginx服务。刚才创建namespace后,执行了 kubectl label ns sidecar istio-injection=enabled
可以看下sidecar namespace的配置,执行kubectl get ns sidecar -oyaml
:
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: "2022-09-24T08:12:14Z"
labels:
istio-injection: enabled
kubernetes.io/metadata.name: sidecar
name: sidecar
resourceVersion: "8032"
uid: 3fdb6ab1-71b6-44ae-83c8-636dfa266147
spec:
finalizers:
- kubernetes
status:
phase: Active
通过把istio-injection设置为enabled,这样在本namespace中的pod都会注入Sidecar。注入Sidecar后Pod有什么变化呢,可以看下 toolbox-78555898fb-b9qxq 这个pod的运行状态:
其中READY中都是2/2,说明Pod中有两个容器,并且状态都是running,查看Pod中都是什么容器。
查看Pod里初始化容器:
kubectl get pods toolbox-78555898fb-b9qxq -n sidecar -o jsonpath={.spec.initContainers[*].name}
输出为:istio-init
查看Pod里业务容器:
kubectl get pods toolbox-78555898fb-b9qxq -n sidecar -o jsonpath={.spec.containers[*].name}
输出:toolbox 和 istio-proxy
通过查看 toolbox-78555898fb-b9qxq 这个pod的配置,在配置中可以看到istio-init的容器配置信息:
容器中执行了 istio-iptables
配置了iptables规则,也正是因为这个配置劫持了Pod进出的流量,下边我们再仔细看规则的内容。istio-init 在Pod启动时执行,执行完就自动退出了。
istio-proxy在Pod主要做流量代理,通过运行Envoy,可以根据Envoy配置实现流量的灵活控制。配置内容比较多这里就不展示了。所以 toolbox-78555898fb-b9qxq 中展示READY的两个容器就是toolbox 和 istio-proxy。
在 toolbox-78555898fb-b9qxq 中访问 nginx-deployment-85b98978db-48m47,这样在客户端和服务端中都会发生流量劫持。
baihl@baihl-master:~$ kubectl exec -it toolbox-78555898fb-b9qxq -n sidecar sh kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead. sh-4.4# curl http://10.102.37.221 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } 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>
执行上边操作后,请求从客户端到服务端的大概流程如下:
其中有两个地方被iptables规则劫持:
下边看下iptables规则的内容是什么,因为Pod中的命令不全,所以使用nsenter命令在宿主机上进入容器查看。首先获取容器的PID
我的Kubernetes环境分为master和node节点,容器是运行在node节点,所以就在node节点操作
根据上图,toolbox的容器Pid为36009:
sudo nsenter -t 36009 -n iptables-legacy-save
查看到的iptables规则如下:
# Generated by iptables-save v1.8.7 on Sat Sep 24 23:53:39 2022 *nat :PREROUTING ACCEPT [9019:541231] :INPUT ACCEPT [9012:540720] :OUTPUT ACCEPT [722:64866] :POSTROUTING ACCEPT [725:65046] :ISTIO_INBOUND - [0:0] :ISTIO_IN_REDIRECT - [0:0] :ISTIO_OUTPUT - [0:0] :ISTIO_REDIRECT - [0:0] # 入流量匹配这条,走到ISTIO_INBOUND -A PREROUTING -p tcp -j ISTIO_INBOUND # 出流量匹配这条,走到ISTIO_OUTPUT -A OUTPUT -p tcp -j ISTIO_OUTPUT -A ISTIO_INBOUND -p tcp -m tcp --dport 15008 -j RETURN -A ISTIO_INBOUND -p tcp -m tcp --dport 15090 -j RETURN -A ISTIO_INBOUND -p tcp -m tcp --dport 15021 -j RETURN -A ISTIO_INBOUND -p tcp -m tcp --dport 15020 -j RETURN # 入流量匹配这条,走到ISTIO_IN_REDIRECT -A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT # 目标端口转换为 15006,针对curl请求目标端口 80 --> 15006 -A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006 -A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN -A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --uid-owner 1337 -j ISTIO_IN_REDIRECT -A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 1337 -j RETURN -A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN -A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --gid-owner 1337 -j ISTIO_IN_REDIRECT -A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 1337 -j RETURN -A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN -A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN # 以上的都不匹配,走到ISTIO_REDIRECT -A ISTIO_OUTPUT -j ISTIO_REDIRECT # 目标端口转换为15001,针对curl请求目标端口 80 --> 15001 -A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001 COMMIT # Completed on Sat Sep 24 23:53:39 2022
在toolbox中发起 curl http://10.102.37.221
,默认的端口为80,经过iptables OUTPUT规则目标端口转换为15001,请求被转到envoy,在envoy中有listener监听15001。
请求到达Envoy,被监听的15001接收,下边看看Envoy listener 15001的配置,同样配置较多,只展示关键部分:
istioctl pc listener -n sidecar toolbox-78555898fb-b9qxq --port 15001 -ojson
{
"name": "virtualOutbound",
"address": {
"socketAddress": {
"address": "0.0.0.0",
"portValue": 15001
}
},
"useOriginalDst": true
}
配置中的useOriginalDst设置为true,表示获取原始目的端口,就是被iptables规则转换前的80端口,看到这个listener的配置为virtualOutbound,就像它的名字一样是个虚拟机,所以获取出来原始的端口80后,就会再被listener 80配置处理。
同样,我们看下listener 80的配置:
istioctl pc listener -n sidecar toolbox-78555898fb-b9qxq --port 80 -ojson
只看关键配置:
{
"name": "0.0.0.0_80",
"address": {
"socketAddress": {
"address": "0.0.0.0",
"portValue": 80
}
},
"routeConfigName": "80"
}
经过listener 80后,匹配routeConfigName “80”,继续看route。
执行 istioctl pc route -n sidecar toolbox-78555898fb-b9qxq --name=80 -ojson
查看如下:
{ "name": "nginx.sidecar.svc.cluster.local:80", "domains": [ "nginx.sidecar.svc.cluster.local", "nginx.sidecar.svc.cluster.local:80", "nginx", "nginx:80", "nginx.sidecar.svc", "nginx.sidecar.svc:80", "nginx.sidecar", "nginx.sidecar:80", "10.102.37.221", "10.102.37.221:80" ], "routes": [ { "name": "default", "match": { "prefix": "/" }, "route": { "cluster": "outbound|80||nginx.sidecar.svc.cluster.local",
根据上边的配置,请求会选择 "cluster": "outbound|80||nginx.sidecar.svc.cluster.local"
处理。现在我们就看下选择的cluster正式的endpoint是什么。
执行如下命令过滤出我们需要的endpoints:
istioctl pc endpoints -n sidecar toolbox-78555898fb-b9qxq --cluster="outbound|80||nginx.sidecar.svc.cluster.local" -ojson
根据配置可以看到最终选择后端为10.10.1.5,就是我们部署的nginx Pod的地址。
{ "name": "outbound|80||nginx.sidecar.svc.cluster.local", "addedViaApi": true, "hostStatuses": [ { "address": { "socketAddress": { "address": "10.10.1.5", "portValue": 80 } }, "stats": [...], "healthStatus": { "edsHealthStatus": "HEALTHY" }, "weight": 1, "locality": {} } ], "circuitBreakers": {...}, "observabilityName": "outbound|80||nginx.sidecar.svc.cluster.local" }
经过以上流程,请求就从toolbox Pod中出去了,请求的目标地址变为 10.10.1.5,目标端口为80。
请求到达服务端一样会经过iptables规则和Envoy代理,才能最终到达Nginx。分析过程和客户端一样,就不再赘述。
上边的请求流程,在两个Pod中都经过了多次协议栈的处理。
就像上图一样,请求一次要经过多次网络栈的处理,对性能肯定会有影响,既然有问题,就会出现技术去解决,Cilium就可以实现加速,具体架构图如下:
Cilium底层基于Linux内核的新技术eBPF,保持好奇心,多多探索吧,哈哈。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。