前面介绍了Istio依赖的Envoy的工作原理,接下来通过实际例子演示Istio是如何完成流量劫持以及流量转发的。首先准备部署两个pod,一个nginx pod作为服务端,一个toolbox pod作为客户端,toolbox只是一个能支持linux命令的容器而已。下面是两个pod的部署文件。通过下面的文件启动nginx和toolbox pod。
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
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
接着是在集群上安装Istio。安装成功后可以看到有3个istio相关的pod成功启动
curl -L https://istio.io/downloadIstio | sh -
cd istio-1.xx.0
cp bin/istioctl /usr/local/bin
istioctl install --set profile=demo -y
接着启动前面定义的nginx和toolbox pod,如果对namespace添加istio-injection=enabled的标签,那么该namespace下的pod istio都会自动注入。
kubectl create ns sidecar
kubectl label ns sidecar istio-injection=enabled
kubectl apply -f nginx.yaml -n sidecar
kubectl apply -f toolbox-default.yaml -n sidecar
查看启动的pod,可以看到两个pod的container数量不再是1,而是2.
挑选一种一个pod例如toolboxpod,查看pod的details,可以看到除了toolbox自身定义的container外,还启动了istio-proxy的container,这个container本质就是启动了一个envoy的proxy。
除了这个container外,还有个initcontainer,对Iptables做了一些配置修改。
接下来可以继续看看,pod中的iptables配置具体长什么样子。左边是通过命令查看到了iptables规则,右边是解释每条规则的含义。
crictl pods|grep podName
//get containerId of pod
crictl inspectp containerId | grep pid
//get pid
nsenter -t pid -n iptables
//get iptables configuration
通过查看iptables规则可以知道,对于模拟客户端的toolboxpod来说,如果要访问服务nginx,等于是想往外发送请求,那么根据iptables规则,会执行ISTIO_OUTPUT,而ISTIO_OUTPUT又会窒息ISTIO_REDIRECT, ISTIO_REDIRECT最终转发到15001端口上。
接着查看15001端口的配置信息。
(istioctl pc listener -n sidecar toolbox-68f79dd5f8-q5nvd --port 15001 -ojson)
可以看到15001是一个虚拟端口,不是真实的服务端口,另外,该配置中有个属性useOritinalDst=true表示转发到虚拟端口15001上的请求,原来的目的地址是什么,那么转发到15001上后,仍然发送请求到原来的目的地址上,原来的目的访问地址是想访问nginx服务的80端口。
接着查看80端口的配置信息,查看80端口配置,就有真实的路由配置了,路由配置名称是routeConfigName=80.
(istioctl pc listener -n sidecar toolbox-68f79dd5f8-q5nvd --port 80 -ojson)
接着查看路由名称等于80的配置信息,该配置文件中有很多路由配置信息,其中关于nginx的,如果是访问nginx,那么交由cluster=nginx.sidecar.svc.cluster.local处理(需要注意一点,前面介绍envoy时提到过envoy中cluster的含义是一堆ip地址的集合)
(istioctl pc route -n sidecar toolbox-68f79dd5f8-q5nvd --name=80 -ojson)
接着查看cluster包含的IP地址信息就知道最终请求发送的目的地IP地址了。
istioctl pc cluster -n sidecar toolbox-68f79dd5f8-8gkp9
istioctl pc endpoint -n sidecar toolbox-68f79dd5f8-8gkp9
Cluster包含的目标地址正是nginx服务Pod的地址。故最终请求能发送到nginx上。当请求往外发时,最终还是会follow iptables的规则,再看iptables的规则,有一条outbound的规则是:如果owner=1337,那么就直接往外发,而envoy的进程号就是1337
上面详细介绍了发送请求出去的过程,对于服务端来说是接收请求,即Iptables中走的是Inbound的规则。 对于Inbound的请求,通过iptable会将请求发送到envoy的15006端口上,该端口发现需要访问的服务就是本机存在的服务,故把请求转发到本机的nginx pod处理。
对于上面的toolbox(模拟客户端),nginx(模拟服务端)的访问关系的示意图如下所示:
对于Istio sidecar流量劫持的示意图,这里还有一个详细示意图,采用的例子是Istio官网的bookInfo应用。
以上就是对Istio sidecar如何进行流量劫持的讲解,透彻理解流量劫持过程,有助于在真实项目中如果遇到流量未进入预期目的地址的情况,那么可以进行组层排查。