嵌入式技术
一、背景
我们都知道集群中安装了istio后,只需要给 namespace 打上istio-injection=enabled 这个标签,之后这个namespace下的所有pod都会注入边车容器istio-proxy(存量pod需要重启才能生效,新下发的pod会直接注入sidecar容器)。那么1)sidecar 是如何自动注入的?2)我们都知道istio-proxy可以拦截流量,具体是如何拦截流量的?3)注入了sidecar之后,不想拦截特定流量如何处理?
二、sidecar容器如何注入
在创建Pod的请求到达Kube-apiserver后,首先进行认证鉴权,然后在准入控制阶段 kube-apiserver以REST的方式同步调用sidecar-injector webhook服务进行init容器与istio-proxy容器的注入,最后将Pod对象持久化存储到Etcd中。对应MutatingWebhookConfiguration配置如下:
apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: name: istio-revision-tag-default webhooks: ...... - admissionReviewVersions: - v1beta1 - v1 clientConfig: caBundle: xxx service: name: istiod namespace: istio-system path: /inject port: 443 failurePolicy: Fail matchPolicy: Equivalent name: namespace.sidecar-injector.istio.io namespaceSelector: matchExpressions: - key: istio-injection operator: In values: - enabled rules: - apiGroups: - "" apiVersions: - v1 operations: - CREATE resources: - pods scope: '*' ... ...
由配置可知,webhook服务由istiod提供,在istio 1.5 版本之后sidecar-injector 被编译到istiod进程中。sidecar-injector 对标签匹配 istio-injection : enabled 的命名空间的Pod资源对象的创建生效。
业务pod被自动注入istio-init container和 istio-proxy container
# initContainer 配置 ,用于初始化pod网络,负责对Pod配置定制的iptables规则,所以也需要被赋予NET_ADMIN权限 initContainers: - name: istio-init image: docker.io/istio/proxyv2:1.19.0 args: - istio-iptables - '-p' - '15001' - '-z' - '15006' - '-u' - '1337' - '-m' - REDIRECT - '-i' - '*' - '-x' - '' - '-b' - '*' - '-d' - 15090,15021,15020 - '--log_output_level=default:info' securityContext: capabilities: add: - NET_ADMIN - NET_RAW drop: - ALL ...... # istio-proxy container配置, containers: - name: istio-proxy image: docker.io/istio/proxyv2:1.19.0 args: - proxy - sidecar - '--domain' - $(POD_NAMESPACE).svc.cluster.local - '--proxyLogLevel=warning' - '--proxyComponentLogLevel=misc:error' - '--log_output_level=default:info' ports: - name: http-envoy-prom containerPort: 15090 protocol: TCP env: ...... volumeMounts: # istio-proxy 容器挂载的证书及配置文件 - name: workload-socket mountPath: /var/run/secrets/workload-spiffe-uds - name: credential-socket mountPath: /var/run/secrets/credential-uds - name: workload-certs mountPath: /var/run/secrets/workload-spiffe-credentials - name: istiod-ca-cert mountPath: /var/run/secrets/istio - name: istio-data mountPath: /var/lib/istio/data - name: istio-envoy#Envoy的启动配置文件 envoy-rev0.json mountPath: /etc/istio/proxy - name: istio-token# Envoy 访问istiod用的token mountPath: /var/run/secrets/tokens - name: istio-podinfo# 以文件形式保存 Pod自身服务的信息,包含annotations和labels文件,这两个文件将被pilot-agent读取 mountPath: /etc/istio/pod - name: kube-api-access-xsnpl readOnly: true mountPath: /var/run/secrets/kubernetes.io/serviceaccount securityContext: ...... runAsUser: 1337 # Sidecar运行用户 runAsGroup: 1337
三、istio拦截原理
在完成Sidecar自动注入后,业务在Pod运行期间收发的网络流量将被透明的拦截进Sidecar。其流量拦截基于iptables规则,拦截应用容器的Inbound流量或Outbound流量。主要分为两大部分:
istio-init容器用于设置pod中的iptables转发规则,将流量先导入给istio-proxy(envoy)
Sidecar容器 istio-proxy拦截流量
3.1 istio-init容器分析
istio-init容器会在pod网络协议栈完成iptables规则配置操作后退出,该容器的启动命令(istio-iptables命令封装了一些iptables规则)
istio-iptables -p 15001 -z 15006 -u 1337 -m REDIRECT -i '*' -x "" -b '*' -d 15090,15021,15020
让sidecar代理可以拦截所有进出Pod的流量,除了15090,15021,15020端口的所有入站流量都被重定向到15006端口(sidecar),还可以拦截应用容器的出流量,这些流量经过sidcar(通过15001端口监听)处理后才能出站。
-z 15006 表示将进入应用容器的所有流量都转发到sidecar的15006端口
-u 1337 指定不应用重定向的uid,默认是1337,即使用istio-proxy用户身份运行
-m REDIRECT 表示使用REDIRECT模式重定向流量
-p 15001 将所有出战流量都重定向到sidecar的15001端口
-d 15090,15021,15020 表示排除该三个端口,所有的入流量都会被重定向处理。15020和15090都是遥测暴露指标的端口,15021监控检查的端口
-i "*" 表示重定向所有的出站流量
-x "" 表示排除指定的网端ip地址,对出站流量不进行重定向处理。为空 没有需要排除的ip
-b "*" 表示重定向所有入站流量到Envoy
3.2 sidecar拦截规则分析
sidecar与用户进程共享同一个网络命名空间,工作在相同的网络协议栈上。sidecar 对协议栈iptables规则的配置,将影响用户应用程序报文的流向,可以透明的拦截用户报文,并进行七层处理。进入被注入sidecar的容器网络命名空间(可以用nsenter命令从宿主机进入,或者直接进入有iptables模块的容器),查看对应iptables规则
# iptables -t nat -S -P PREROUTING ACCEPT -P INPUT ACCEPT -P OUTPUT ACCEPT -P POSTROUTING ACCEPT -N ISTIO_INBOUND -N ISTIO_IN_REDIRECT -N ISTIO_OUTPUT -N ISTIO_REDIRECT -A PREROUTING -p tcp -j ISTIO_INBOUND -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 -A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT -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 -p tcp -m tcp ! --dport 15008 -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 -p tcp -m tcp ! --dport 15008 -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 -A ISTIO_OUTPUT -j ISTIO_REDIRECT -A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
-P PREROUTING ACCEPT : 接受进入PREROUTING链的报文,其他链同理
-N ISTIO_INBOUND : 声明一个自定义的链 ISTIO_INBOUND
-A PREROUTING -p tcp -j ISTIO_INBOUND : 将进入PREROUTING链的TCP流量跳转到ISTIO_INBOUND链 做进一步处理
-A ISTIO_INBOUND -p tcp -m tcp --dport 15008 -j RETURN : 对进入ISTIO_INBOUND链的目标端口为15008的TCP流量不做特殊处理,直接让其通过
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006 : 对进入ISTIO_IN_REDIRECT链的TCP流量进行报文修改,REDIRECT 对应DNAT修改方式,修改目标端口为15006
-A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN: 对进入ISTIO_OUTPUT链的源地址为127.0.0.6的报文且目标网络设备为lo本地设备的流量,不进行特殊处理
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -p tcp -m tcp ! --dport 15008 -m owner --uid-owner 1337 -j ISTIO_IN_REDIRECT:
对进入ISTIO_OUTPUT链且目标地址虽然不然127.0.0.1,但判断目标网络设备为本地,即pod自身地址的报文,若报文发送进程为uid=1337,则Envoy转到ISTIO_IN_REDIRECT链继续处理
3.2.1 Inbound场景流量规则详细分析
Inbound流量指从Pod外进入Pod内的流量。比如客户端访问服务端,应用数据报文在进入服务端时就会被拦截,从而进入envoy 15006监听端口来处理,同时例如15008 mtls隧道端口,15090,15020遥测监控端口,15021健康检查端口,均不会被拦截
此时服务端POD接受流量,流量首先会经过prerouting链处理。然后命中iptables规则: -A PREROUTING -p tcp -j ISTIO_INBOUND ,将进入PREROUTING链的TCP流量跳转到ISTIO_INBOUND链 做进一步处理。接着命中iptables规则: -A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT,如果目标端口不是15008,15090,15020,15021,则将流量跳转到ISTIO_IN_REDIRECT链进行处理。最后命中iptables规则: -A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006,报文被拦截并通过DNAT方式修改目标地址,将访问端口置为15006。
此时流量被拦截至Envoy容器进行处理,Envoy容器将流量发送给业务容器进行处理。命中规则: -A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN ,表示Envoy发送给Pod后端backend容器的流量不再被拦截。
业务容器接受到报文后,会进行回包。
存在一些PassthroughCluster场景,例如直接访问podIP,此时Inbound请求根据目标地址没有找到后端服务时,下游请求将被转发到Envoy内置的PassthroughCluster服务中,最后按照原始目标地址转发下游请求。
Envoy发送报文,命中iptables规则: -A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN。处理Passthroughcluster流量时,源ip会被置为127.0.0.6。此时在output链处理阶段,被转发的请求会被放行。同时因为目标网络涉别为lo,可以访问到本pod内未注册的后端服务。
3.2.1 Outbound场景流量规则详细分析
上图模拟了两个服务之间的通信,主要是描述东西向流量客户端访问服务端其中outbound流量的过程。(istio中很多流量治理的功能都是在outbound过程生效)
客户端frontend通过service访问服务端(svcName:port)。四元组信息: srcIP: ip1 srcPort: port1 ==> dst:backend ,dscPort:port2。服务端域名经过DNS解析后,得到clusterIP,然后封装发送SYN报文,随后报文被pod内的iptables规则拦截。首先命中该规则: -A OUTPUT -p tcp -j ISTIO_OUTPUT,将进入output链的tcp流量转发到istio_output链做进一步处理;随后命中该规则: -A ISTIO_OUTPUT -j ISTIO_REDIRECT , 将所有出站流量(非本地的)转发给istio_redirect链来处理;最后命中该规则: -A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001 ,对进入ISTIO_REDIRECT链的TCP流量进行报文修改,目标端口重定向到15001。
客户端Envoy接受到报文,开始针对流量进行治理。在Envoy中会根据配下发的配置还原目标服务的ClusterIp和port。随后根据Envoy内配置的负载均衡策略选择一个后端实例IP作为目的IP,并创建连接
客户端Envoy准备发送报文。istio-proxy的运行用户和用户组均为1337。四元组信息: srcIP:ip1, srcPort:随机端口(port3) ==> dstIP:ip2,dstPort:port2。命中规则: -A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN ,对进入istio_output链,报文发送用户为1337的流量 放行通过。
服务端backend的Envoy接受流量转发给backend,服务端backend已接受到报文,开始回包给客户端。四元组信息: scrIP:ip2, srcPort:port2 ==> dstIP:ip1,dstPort:port3
服务端Envoy发送流量。此时envoy会将报文转发给业务容器。四元组信息: scrIP: 127.0.0.1, srcPort:15001 ==> dstIP:ip1,dstPort:port1
3.3 如何放行部分流量不被envoy拦截
在实际的使用过程中,如果业务容器访问某些应用不希望流量被拦截,该怎么才能做到呢?istio中根据流量拦截的原理是iptables规则的配置。我们可以给应用加上对应的规则即可,例如出站流量对端口8080不做拦截,出站流量对172.16.2.0/24目标网段不做拦截。
就需要配置以下规则: 因为所有的出站包都会转发到ISTIO_OUTPUT链上,所以基于该链配置放行规则
-A ISTIO_OUTPUT -p tcp -m tcp --dport 8080 -j RETURN -A ISTIO_OUTPUT -d 172.16.2.0/24 -j RETURN
istio提供了基于podAnnotation配置的方式控制拦截行为: https://istio.io/latest/docs/reference/config/annotations/ 为对应工作负载添加
traffic.sidecar.istio.io/excludeOutboundPorts : 出站流量放行的端口 traffic.sidecar.istio.io/excludeOutboundIPRanges : 出站流量放行的ip地址
登录pod中查看iptables规则: iptables -t nat -L -nv ,可以发现在ISTIO_OUTPUT 新增了两条放行规则
审核编辑:黄飞
全部0条评论
快来发表一下你的评论吧 !