• Istio中流量劫持机制


    Istio流量劫持机制

    在Service Mesh架构中,Istio通过流量劫持机制,让Pod中的应用访问其他服务时都经过Istio代理,实现对流量的控制。基于Istio可以实现请求路由、服务发现和负载均衡、故障处理、故障注入和规则配置等功能。,因为所有Pod中的进出流量都经过Istio的数据面,所以也可以实现日志记录和追踪。如下是Istio的架构图:
    20220924221438
    下边通过一个示例操作,看下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
    
    1. nginx.yaml

    部署一个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
    
    1. toolbox.yaml

    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看下环境情况:
    20220924223845

    1. 可以看到两个Pod分别为 nginx-deployment-85b98978db-48m47(IP:10.10.1.5) 和 toolbox-78555898fb-b9qxq(IP:10.10.1.6)
    2. 创建的Service的IP为10.102.37.221
    3. Service的Endpoints目前有一个10.10.1.5,就是运行Nginx的那个Pod,当然,也可以创建多个服务,统一提供服务。
      因为我现在登录在Kubernetes集群的Master节点,其实现在就可以执行 curl http://10.102.37.221访问Nginx服务。

    20220924224351

    注入Sidecar

    刚才创建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的运行状态:
    20220924231208
    其中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

    Init container

    通过查看 toolbox-78555898fb-b9qxq 这个pod的配置,在配置中可以看到istio-init的容器配置信息:
    20220924230225
    容器中执行了 istio-iptables 配置了iptables规则,也正是因为这个配置劫持了Pod进出的流量,下边我们再仔细看规则的内容。istio-init 在Pod启动时执行,执行完就自动退出了。

    istio-proxy container

    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
    
    
    
    Welcome to nginx!
    
    
    
    

    Welcome to nginx!

    If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

    For online documentation and support please refer to nginx.org.
    Commercial support is available at nginx.com.

    Thank you for using nginx.

    执行上边操作后,请求从客户端到服务端的大概流程如下:
    20220924234447
    其中有两个地方被iptables规则劫持:

    1. 发起curl请求默认请求的是80端口,但是会被iptables OUTPUT劫持转发到envoy监听的15001端口
    2. 请求到达Nginx时,目标端口为80,但是会被iptables PREROUTING劫持转到envoy监听的15006端口。

    Iptables规则

    下边看下iptables规则的内容是什么,因为Pod中的命令不全,所以使用nsenter命令在宿主机上进入容器查看。首先获取容器的PID
    20220924235412

    我的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 Config

    • 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

    同样,我们看下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。

    • route “80”

    执行 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

    执行如下命令过滤出我们需要的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。分析过程和客户端一样,就不再赘述。

    Service Mesh 涉及的网络栈

    上边的请求流程,在两个Pod中都经过了多次协议栈的处理。
    20220925131320
    就像上图一样,请求一次要经过多次网络栈的处理,对性能肯定会有影响,既然有问题,就会出现技术去解决,Cilium就可以实现加速,具体架构图如下:
    20220925131811
    Cilium底层基于Linux内核的新技术eBPF,保持好奇心,多多探索吧,哈哈。

  • 相关阅读:
    计算经纬度坐标之间的真实距离
    计算机毕业设计django基于python企业对账分析系统(源码+系统+mysql数据库+Lw文档)
    Arduino开发实例-旋转编码器RGB-LED调光
    python之排序
    Verilog task使用说明
    阿里云一键登录(号码认证服务)
    力扣labuladong——一刷day07
    springboot180基于spring boot的医院挂号就诊系统
    金三银四好像消失了,IT行业何时复苏!
    2022版:Gitee在Android studio上的使用
  • 原文地址:https://blog.csdn.net/laing92/article/details/127036964