一、背景
我們都知道集群中安裝了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 新增了兩條放行規則
審核編輯:黃飛
評論
查看更多