解密 Istio 的 Sidecar 注入模型
解開 Istio 如何將其資料平面元件插入現有部署中的奧秘。
Istio 服務網格架構的簡單概述總是從描述控制平面和資料平面開始。
重要的是要了解 Sidecar 注入到應用程式 Pod 中是自動發生的,儘管也可以手動注入。流量會從應用程式服務導向這些 Sidecar,而開發人員無需擔心。一旦應用程式連接到 Istio 服務網格,開發人員就可以開始使用並享受服務網格所提供的一切好處。但是,資料平面是如何實現的,以及真正需要什麼才能使其無縫運作?在這篇文章中,我們將深入探討 Sidecar 注入模型的具體細節,以清楚了解 Sidecar 注入的工作原理。
Sidecar 注入
簡單來說,Sidecar 注入就是將額外容器的配置新增至 Pod 範本。Istio 服務網格所需的額外容器為:
istio-init
這個初始化容器用於設定 iptables
規則,以便入站/出站流量將通過 Sidecar 代理。初始化容器與應用程式容器的不同之處如下:
- 它會在應用程式容器啟動之前執行,而且始終會執行完成。
- 如果有很多初始化容器,則每個容器都應該成功完成,才能啟動下一個容器。
因此,您可以了解這種容器如何完美地適用於不需要成為實際應用程式容器一部分的設定或初始化作業。在這種情況下,istio-init
正好執行此操作,並設定 iptables
規則。
istio-proxy
這是實際的 Sidecar 代理 (基於 Envoy)。
手動注入
在手動注入方法中,您可以使用 istioctl
修改 Pod 範本,並新增先前提及的兩個容器的配置。對於手動和自動注入,Istio 都會從 istio-sidecar-injector
設定檔對應表 (configmap) 和網格的 istio
configmap 中取得設定。
讓我們看看 istio-sidecar-injector
configmap 的配置,以了解實際情況。
$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}'
SNIPPET from the output:
policy: enabled
template: |-
initContainers:
- name: istio-init
image: docker.io/istio/proxy_init:1.0.2
args:
- "-p"
- [[ .MeshConfig.ProxyListenPort ]]
- "-u"
- 1337
.....
imagePullPolicy: IfNotPresent
securityContext:
capabilities:
add:
- NET_ADMIN
restartPolicy: Always
containers:
- name: istio-proxy
image: [[ if (isset .ObjectMeta.Annotations "sidecar.istio.io/proxyImage") -]]
"[[ index .ObjectMeta.Annotations "sidecar.istio.io/proxyImage" ]]"
[[ else -]]
docker.io/istio/proxyv2:1.0.2
[[ end -]]
args:
- proxy
- sidecar
.....
env:
.....
- name: ISTIO_META_INTERCEPTION_MODE
value: [[ or (index .ObjectMeta.Annotations "sidecar.istio.io/interceptionMode") .ProxyConfig.InterceptionMode.String ]]
imagePullPolicy: IfNotPresent
securityContext:
readOnlyRootFilesystem: true
[[ if eq (or (index .ObjectMeta.Annotations "sidecar.istio.io/interceptionMode") .ProxyConfig.InterceptionMode.String) "TPROXY" -]]
capabilities:
add:
- NET_ADMIN
restartPolicy: Always
.....
如您所見,configmap 包含 istio-init
初始化容器和 istio-proxy
代理容器的配置。配置包括容器映像的名稱和引數,例如攔截模式、功能等。
從安全角度來看,重要的是要注意 istio-init
需要 NET_ADMIN
功能才能修改 Pod 命名空間內的 iptables
,如果以 TPROXY
模式配置,istio-proxy
也需要。由於這僅限於 Pod 的命名空間,因此應該沒有問題。但是,我注意到最近的 OpenShift 版本可能存在一些問題,需要採取變通方法。其中一個選項在本篇文章的末尾提到。
若要修改目前的 Pod 範本以進行 Sidecar 注入,您可以
$ istioctl kube-inject -f demo-red.yaml | kubectl apply -f -
或
若要使用修改後的 configmap 或本機 configmap
從 configmap 建立
inject-config.yaml
和mesh-config.yaml
$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yaml $ kubectl -n istio-system get configmap istio -o=jsonpath='{.data.mesh}' > mesh-config.yaml
修改現有的 Pod 範本,在我的例子中是
demo-red.yaml
$ istioctl kube-inject --injectConfigFile inject-config.yaml --meshConfigFile mesh-config.yaml --filename demo-red.yaml --output demo-red-injected.yaml
套用
demo-red-injected.yaml
$ kubectl apply -f demo-red-injected.yaml
如上所示,我們使用 sidecar-injector
和網格配置建立新的範本,然後使用 kubectl
套用該新範本。如果我們查看注入的 YAML 檔案,它會包含 Istio 特定容器的配置,如我們上面所討論的。一旦我們套用注入的 YAML 檔案,我們會看到兩個容器正在執行。其中一個是實際的應用程式容器,另一個是 istio-proxy
Sidecar。
$ kubectl get pods | grep demo-red
demo-red-pod-8b5df99cc-pgnl7 2/2 Running 0 3d
計數不是 3,因為 istio-init
容器是初始化類型的容器,它會在完成其應執行的工作後結束,也就是在 Pod 內設定 iptable
規則。為了確認初始化容器已結束,讓我們看看 kubectl describe
的輸出。
$ kubectl describe pod demo-red-pod-8b5df99cc-pgnl7
SNIPPET from the output:
Name: demo-red-pod-8b5df99cc-pgnl7
Namespace: default
.....
Labels: app=demo-red
pod-template-hash=8b5df99cc
version=version-red
Annotations: sidecar.istio.io/status={"version":"3c0b8d11844e85232bc77ad85365487638ee3134c91edda28def191c086dc23e","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs...
Status: Running
IP: 10.32.0.6
Controlled By: ReplicaSet/demo-red-pod-8b5df99cc
Init Containers:
istio-init:
Container ID: docker://bef731eae1eb3b6c9d926cacb497bb39a7d9796db49cd14a63014fc1a177d95b
Image: docker.io/istio/proxy_init:1.0.2
Image ID: docker-pullable://docker.io/istio/proxy_init@sha256:e16a0746f46cd45a9f63c27b9e09daff5432e33a2d80c8cc0956d7d63e2f9185
.....
State: Terminated
Reason: Completed
.....
Ready: True
Containers:
demo-red:
Container ID: docker://8cd9957955ff7e534376eb6f28b56462099af6dfb8b9bc37aaf06e516175495e
Image: chugtum/blue-green-image:v3
Image ID: docker-pullable://docker.io/chugtum/blue-green-image@sha256:274756dbc215a6b2bd089c10de24fcece296f4c940067ac1a9b4aea67cf815db
State: Running
Started: Sun, 09 Dec 2018 18:12:31 -0800
Ready: True
istio-proxy:
Container ID: docker://ca5d690be8cd6557419cc19ec4e76163c14aed2336eaad7ebf17dd46ca188b4a
Image: docker.io/istio/proxyv2:1.0.2
Image ID: docker-pullable://docker.io/istio/proxyv2@sha256:54e206530ba6ca9b3820254454e01b7592e9f986d27a5640b6c03704b3b68332
Args:
proxy
sidecar
.....
State: Running
Started: Sun, 09 Dec 2018 18:12:31 -0800
Ready: True
.....
如輸出中所見,istio-init
容器的 State
為 Terminated
,而 Reason
為 Completed
。唯一兩個正在執行的容器是主要應用程式 demo-red
容器和 istio-proxy
容器。
自動注入
大多數時候,您不希望每次部署應用程式時都使用 istioctl
命令手動注入 Sidecar,而是希望 Istio 自動將 Sidecar 注入到您的 Pod 中。這是建議的方法,若要使其正常運作,您只需要使用 istio-injection=enabled
標籤標示您要部署應用程式的命名空間即可。
標示後,Istio 會自動為您部署在該命名空間中的任何 Pod 注入 Sidecar。在以下範例中,Sidecar 會自動注入到部署在 istio-dev
命名空間中的 Pod 中。
$ kubectl get namespaces --show-labels
NAME STATUS AGE LABELS
default Active 40d <none>
istio-dev Active 19d istio-injection=enabled
istio-system Active 24d <none>
kube-public Active 40d <none>
kube-system Active 40d <none>
但這是如何運作的?若要深入了解,我們需要了解 Kubernetes 允許控制器。
對於自動 Sidecar 注入,Istio 依賴 Mutating Admission Webhook
。讓我們看看 istio-sidecar-injector
變更 Webhook 配置的詳細資訊。
$ kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml
SNIPPET from the output:
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"admissionregistration.k8s.io/v1beta1","kind":"MutatingWebhookConfiguration","metadata":{"annotations":{},"labels":{"app":"istio-sidecar-injector","chart":"sidecarInjectorWebhook-1.0.1","heritage":"Tiller","release":"istio-remote"},"name":"istio-sidecar-injector","namespace":""},"webhooks":[{"clientConfig":{"caBundle":"","service":{"name":"istio-sidecar-injector","namespace":"istio-system","path":"/inject"}},"failurePolicy":"Fail","name":"sidecar-injector.istio.io","namespaceSelector":{"matchLabels":{"istio-injection":"enabled"}},"rules":[{"apiGroups":[""],"apiVersions":["v1"],"operations":["CREATE"],"resources":["pods"]}]}]}
creationTimestamp: 2018-12-10T08:40:15Z
generation: 2
labels:
app: istio-sidecar-injector
chart: sidecarInjectorWebhook-1.0.1
heritage: Tiller
release: istio-remote
name: istio-sidecar-injector
.....
webhooks:
- clientConfig:
service:
name: istio-sidecar-injector
namespace: istio-system
path: /inject
name: sidecar-injector.istio.io
namespaceSelector:
matchLabels:
istio-injection: enabled
rules:
- apiGroups:
- ""
apiVersions:
- v1
operations:
- CREATE
resources:
- pods
在這裡,您可以看到 Webhook namespaceSelector
標籤,該標籤與 istio-injection: enabled
標籤的 Sidecar 注入相符。在這種情況下,您還可以看到建立 Pod 時所執行的作業和資源。當 apiserver
收到符合其中一項規則的請求時,apiserver
會將允許檢閱請求傳送至 clientConfig:
配置中指定的 Webhook 服務,其中包含 name: istio-sidecar-injector
鍵值對。我們應該能夠看到此服務正在 istio-system
命名空間中執行。
$ kubectl get svc --namespace=istio-system | grep sidecar-injector
istio-sidecar-injector ClusterIP 10.102.70.184 <none> 443/TCP 24d
此配置最終所執行的操作與我們在手動注入中所看到的操作非常相似。只是它在 Pod 建立期間自動完成,因此您不會在部署中看到變更。您需要使用 kubectl describe
來查看 Sidecar 代理和初始化代理。
自動 Sidecar 注入不僅依賴 Webhook 的 namespaceSelector
機制,還依賴預設注入原則和每個 Pod 的覆寫註解。
如果您再次查看 istio-sidecar-injector
ConfigMap,它會定義預設注入原則。在我們的例子中,預設會啟用。
$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}'
SNIPPET from the output:
policy: enabled
template: |-
initContainers:
- name: istio-init
image: "gcr.io/istio-release/proxy_init:1.0.2"
args:
- "-p"
- [[ .MeshConfig.ProxyListenPort ]]
您也可以在 Pod 範本中使用註解 sidecar.istio.io/inject
來覆寫預設原則。以下範例會停用 Deployment
中 Pod 的自動 Sidecar 注入。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: ignored
spec:
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
spec:
containers:
- name: ignored
image: tutum/curl
command: ["/bin/sleep","infinity"]
此範例顯示了許多變數,這些變數取決於是否在您的命名空間、ConfigMap 或 Pod 中控制自動 Sidecar 注入,它們是
- Webhook
namespaceSelector
(istio-injection: enabled
) - 預設原則 (在 ConfigMap
istio-sidecar-injector
中設定) - 每個 Pod 的覆寫註解 (
sidecar.istio.io/inject
)
注入狀態表清楚地顯示了根據上述變數值所產生的最終注入狀態。
從應用程式容器到 Sidecar 代理的流量
現在我們清楚了解如何將 Sidecar 容器和初始化容器注入應用程式資訊清單,Sidecar 代理如何擷取進出容器的入站和出站流量?我們確實簡要提到這是通過在 Pod 命名空間內設定 iptable
規則來完成的,而這又是通過 istio-init
容器來完成的。現在,是時候驗證命名空間內實際更新的內容了。
讓我們進入我們在前一節中部署的應用程式 Pod 命名空間,並查看已設定的 iptables。我將使用 nsenter
示範一個範例。或者,您可以以權限模式進入容器以查看相同的資訊。對於無法存取節點的使用者,使用 exec
進入 Sidecar 並執行 iptables
更為實用。
$ docker inspect b8de099d3510 --format '{{ .State.Pid }}'
4125
$ nsenter -t 4215 -n 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 80 -j ISTIO_IN_REDIRECT
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15001
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -j ISTIO_REDIRECT
-A ISTIO_OUTPUT -m owner --uid-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
上面的輸出清楚地顯示,所有傳入連接埠 80 的流量 (這是我們的 red-demo
應用程式正在接聽的連接埠) 現在都 REDIRECTED
到連接埠 15001
,這是 istio-proxy
(Envoy 代理) 正在接聽的連接埠。對於傳出的流量,情況也是如此。
這讓我們來到了本篇文章的結尾。我希望它有助於解開 Istio 如何將 Sidecar 代理注入到現有部署中,以及 Istio 如何將流量路由到代理的奧秘。