一、背景
我們都知道集群中安裝了istio后,只需要給 namespace 打上istio-injection=enabled?這個標(biāo)簽,之后這個namespace下的所有pod都會注入邊車容器istio-proxy(存量pod需要重啟才能生效,新下發(fā)的pod會直接注入sidecar容器)。那么1)sidecar 是如何自動注入的?2)我們都知道istio-proxy可以攔截流量,具體是如何攔截流量的?3)注入了sidecar之后,不想攔截特定流量如何處理?
二、sidecar容器如何注入
在創(chuàng)建Pod的請求到達Kube-apiserver后,首先進行認(rèn)證鑒權(quán),然后在準(zhǔn)入控制階段 kube-apiserver以REST的方式同步調(diào)用sidecar-injector webhook服務(wù)進行init容器與istio-proxy容器的注入,最后將Pod對象持久化存儲到Etcd中。對應(yīng)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服務(wù)由istiod提供,在istio 1.5 版本之后sidecar-injector 被編譯到istiod進程中。sidecar-injector 對標(biāo)簽匹配 istio-injection : enabled 的命名空間的Pod資源對象的創(chuàng)建生效。
業(yè)務(wù)pod被自動注入istio-init container和 istio-proxy container
# initContainer 配置 ,用于初始化pod網(wǎng)絡(luò),負(fù)責(zé)對Pod配置定制的iptables規(guī)則,所以也需要被賦予NET_ADMIN權(quán)限 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自身服務(wù)的信息,包含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自動注入后,業(yè)務(wù)在Pod運行期間收發(fā)的網(wǎng)絡(luò)流量將被透明的攔截進Sidecar。其流量攔截基于iptables規(guī)則,攔截應(yīng)用容器的Inbound流量或Outbound流量。主要分為兩大部分:
istio-init容器用于設(shè)置pod中的iptables轉(zhuǎn)發(fā)規(guī)則,將流量先導(dǎo)入給istio-proxy(envoy)
Sidecar容器 istio-proxy攔截流量
3.1 istio-init容器分析
istio-init容器會在pod網(wǎng)絡(luò)協(xié)議棧完成iptables規(guī)則配置操作后退出,該容器的啟動命令(istio-iptables命令封裝了一些iptables規(guī)則)
istio-iptables -p 15001 -z 15006 -u 1337 -m REDIRECT -i '*' -x "" -b '*' -d 15090,15021,15020
讓sidecar代理可以攔截所有進出Pod的流量,除了15090,15021,15020端口的所有入站流量都被重定向到15006端口(sidecar),還可以攔截應(yīng)用容器的出流量,這些流量經(jīng)過sidcar(通過15001端口監(jiān)聽)處理后才能出站。
-z 15006 表示將進入應(yīng)用容器的所有流量都轉(zhuǎn)發(fā)到sidecar的15006端口
-u 1337?指定不應(yīng)用重定向的uid,默認(rèn)是1337,即使用istio-proxy用戶身份運行
-m REDIRECT 表示使用REDIRECT模式重定向流量
-p 15001 將所有出戰(zhàn)流量都重定向到sidecar的15001端口
-d 15090,15021,15020 表示排除該三個端口,所有的入流量都會被重定向處理。15020和15090都是遙測暴露指標(biāo)的端口,15021監(jiān)控檢查的端口
-i "*" 表示重定向所有的出站流量
-x "" 表示排除指定的網(wǎng)端ip地址,對出站流量不進行重定向處理。為空 沒有需要排除的ip
-b "*" 表示重定向所有入站流量到Envoy
3.2 sidecar攔截規(guī)則分析
sidecar與用戶進程共享同一個網(wǎng)絡(luò)命名空間,工作在相同的網(wǎng)絡(luò)協(xié)議棧上。sidecar 對協(xié)議棧iptables規(guī)則的配置,將影響用戶應(yīng)用程序報文的流向,可以透明的攔截用戶報文,并進行七層處理。進入被注入sidecar的容器網(wǎng)絡(luò)命名空間(可以用nsenter命令從宿主機進入,或者直接進入有iptables模塊的容器),查看對應(yīng)iptables規(guī)則
# 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流量跳轉(zhuǎn)到ISTIO_INBOUND鏈 做進一步處理
-A ISTIO_INBOUND -p tcp -m tcp --dport 15008 -j RETURN : 對進入ISTIO_INBOUND鏈的目標(biāo)端口為15008的TCP流量不做特殊處理,直接讓其通過
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006 : 對進入ISTIO_IN_REDIRECT鏈的TCP流量進行報文修改,REDIRECT 對應(yīng)DNAT修改方式,修改目標(biāo)端口為15006
-A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN: 對進入ISTIO_OUTPUT鏈的源地址為127.0.0.6的報文且目標(biāo)網(wǎng)絡(luò)設(shè)備為lo本地設(shè)備的流量,不進行特殊處理
-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鏈且目標(biāo)地址雖然不然127.0.0.1,但判斷目標(biāo)網(wǎng)絡(luò)設(shè)備為本地,即pod自身地址的報文,若報文發(fā)送進程為uid=1337,則Envoy轉(zhuǎn)到ISTIO_IN_REDIRECT鏈繼續(xù)處理
3.2.1 Inbound場景流量規(guī)則詳細分析
Inbound流量指從Pod外進入Pod內(nèi)的流量。比如客戶端訪問服務(wù)端,應(yīng)用數(shù)據(jù)報文在進入服務(wù)端時就會被攔截,從而進入envoy 15006監(jiān)聽端口來處理,同時例如15008 mtls隧道端口,15090,15020遙測監(jiān)控端口,15021健康檢查端口,均不會被攔截
此時服務(wù)端POD接受流量,流量首先會經(jīng)過prerouting鏈處理。然后命中iptables規(guī)則: -A PREROUTING -p tcp -j ISTIO_INBOUND ,將進入PREROUTING鏈的TCP流量跳轉(zhuǎn)到ISTIO_INBOUND鏈 做進一步處理。接著命中iptables規(guī)則: -A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT,如果目標(biāo)端口不是15008,15090,15020,15021,則將流量跳轉(zhuǎn)到ISTIO_IN_REDIRECT鏈進行處理。最后命中iptables規(guī)則: -A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006,報文被攔截并通過DNAT方式修改目標(biāo)地址,將訪問端口置為15006。
此時流量被攔截至Envoy容器進行處理,Envoy容器將流量發(fā)送給業(yè)務(wù)容器進行處理。命中規(guī)則: -A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN ,表示Envoy發(fā)送給Pod后端backend容器的流量不再被攔截。
業(yè)務(wù)容器接受到報文后,會進行回包。
存在一些PassthroughCluster場景,例如直接訪問podIP,此時Inbound請求根據(jù)目標(biāo)地址沒有找到后端服務(wù)時,下游請求將被轉(zhuǎn)發(fā)到Envoy內(nèi)置的PassthroughCluster服務(wù)中,最后按照原始目標(biāo)地址轉(zhuǎn)發(fā)下游請求。
Envoy發(fā)送報文,命中iptables規(guī)則:?-A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN。處理Passthroughcluster流量時,源ip會被置為127.0.0.6。此時在output鏈處理階段,被轉(zhuǎn)發(fā)的請求會被放行。同時因為目標(biāo)網(wǎng)絡(luò)涉別為lo,可以訪問到本pod內(nèi)未注冊的后端服務(wù)。
3.2.1 Outbound場景流量規(guī)則詳細分析
上圖模擬了兩個服務(wù)之間的通信,主要是描述東西向流量客戶端訪問服務(wù)端其中outbound流量的過程。(istio中很多流量治理的功能都是在outbound過程生效)
客戶端frontend通過service訪問服務(wù)端(svcName:port)。四元組信息:?srcIP: ip1 srcPort: port1 ==> dst:backend ,dscPort:port2。服務(wù)端域名經(jīng)過DNS解析后,得到clusterIP,然后封裝發(fā)送SYN報文,隨后報文被pod內(nèi)的iptables規(guī)則攔截。首先命中該規(guī)則: -A OUTPUT -p tcp -j ISTIO_OUTPUT,將進入output鏈的tcp流量轉(zhuǎn)發(fā)到istio_output鏈做進一步處理;隨后命中該規(guī)則: -A ISTIO_OUTPUT -j ISTIO_REDIRECT , 將所有出站流量(非本地的)轉(zhuǎn)發(fā)給istio_redirect鏈來處理;最后命中該規(guī)則: -A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001 ,對進入ISTIO_REDIRECT鏈的TCP流量進行報文修改,目標(biāo)端口重定向到15001。
客戶端Envoy接受到報文,開始針對流量進行治理。在Envoy中會根據(jù)配下發(fā)的配置還原目標(biāo)服務(wù)的ClusterIp和port。隨后根據(jù)Envoy內(nèi)配置的負(fù)載均衡策略選擇一個后端實例IP作為目的IP,并創(chuàng)建連接
客戶端Envoy準(zhǔn)備發(fā)送報文。istio-proxy的運行用戶和用戶組均為1337。四元組信息: srcIP:ip1, srcPort:隨機端口(port3) ==> dstIP:ip2,dstPort:port2。命中規(guī)則: -A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN ,對進入istio_output鏈,報文發(fā)送用戶為1337的流量 放行通過。
服務(wù)端backend的Envoy接受流量轉(zhuǎn)發(fā)給backend,服務(wù)端backend已接受到報文,開始回包給客戶端。四元組信息:?scrIP:ip2, srcPort:port2 ==> dstIP:ip1,dstPort:port3
服務(wù)端Envoy發(fā)送流量。此時envoy會將報文轉(zhuǎn)發(fā)給業(yè)務(wù)容器。四元組信息:?scrIP: 127.0.0.1, srcPort:15001 ==> dstIP:ip1,dstPort:port1
3.3 如何放行部分流量不被envoy攔截
在實際的使用過程中,如果業(yè)務(wù)容器訪問某些應(yīng)用不希望流量被攔截,該怎么才能做到呢?istio中根據(jù)流量攔截的原理是iptables規(guī)則的配置。我們可以給應(yīng)用加上對應(yīng)的規(guī)則即可,例如出站流量對端口8080不做攔截,出站流量對172.16.2.0/24目標(biāo)網(wǎng)段不做攔截。
就需要配置以下規(guī)則: 因為所有的出站包都會轉(zhuǎn)發(fā)到ISTIO_OUTPUT鏈上,所以基于該鏈配置放行規(guī)則
-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/? 為對應(yīng)工作負(fù)載添加
traffic.sidecar.istio.io/excludeOutboundPorts : 出站流量放行的端口 traffic.sidecar.istio.io/excludeOutboundIPRanges : 出站流量放行的ip地址
登錄pod中查看iptables規(guī)則: iptables -t nat -L -nv ,可以發(fā)現(xiàn)在ISTIO_OUTPUT 新增了兩條放行規(guī)則
審核編輯:黃飛
評論
查看更多