以往有很多文章講解 Istio 是如何做 Sidecar 注入的,但是沒有講解注入之後 Sidecar 工作的細節。本文將帶大家詳細瞭解 Istio 是如何將 Envoy 作為 Sidecar 的方式注入到應用程式 Pod 中,及 Sidecar 是如何做劫持流量的。
本文轉載自:jimmysong.io/posts/envoy…
在講解 Istio 如何將 Envoy 代理注入到應用程式 Pod 中之前,我們需要先了解以下幾個概念:
Sidecar 模式:容器應用模式之一,Service Mesh 架構的一種實現方式。
Init 容器:Pod 中的一種專用的容器,在應用程式容器啟動之前執行,用來包含一些應用映象中不存在的實用工具或安裝指令碼。
iptables:流量劫持是通過 iptables 轉發實現的。
檢視目前 productpage-v1-745ffc55b7-2l2lw
Pod 中執行的容器:
$ kubectl -n default get pod productpage-v1-745ffc55b7-2l2lw -o=jsonpath='{..spec.containers[*].name}'
productpage istio-proxy複製程式碼
productpage
即應用容器,istio-proxy
即 Envoy 代理的 sidecar 容器。另外該 Pod 中實際上還執行過一個 Init 容器,因為它執行結束就自動終止了,所以我們看不到該容器的存在。關注 jsonpath
的用法請參考 JSONPath Support。
Sidecar 模式
在瞭解 Istio 使用 Sidecar 注入之前,需要先說明下什麼是 Sidecar 模式。Sidecar 是容器應用模式的一種,也是在 Service Mesh 中發揚光大的一種模式,詳見 Service Mesh 架構解析,其中詳細描述了節點代理和 Sidecar 模式的 Service Mesh 架構。
使用 Sidecar 模式部署服務網格時,無需在節點上執行代理(因此您不需要基礎結構的協作),但是叢集中將執行多個相同的 Sidecar 副本。從另一個角度看:我可以為一組微服務部署到一個服務網格中,你也可以部署一個有特定實現的服務網格。在 Sidecar 部署方式中,你會為每個應用的容器部署一個伴生容器。Sidecar 接管進出應用容器的所有流量。在 Kubernetes 的 Pod 中,在原有的應用容器旁邊執行一個 Sidecar 容器,可以理解為兩個容器共享儲存、網路等資源,可以廣義的將這個注入了 Sidecar 容器的 Pod 理解為一臺主機,兩個容器共享主機資源。
例如下圖 SOFAMesh & SOFA MOSN—基於Istio構建的用於應對大規模流量的Service Mesh解決方案的架構圖中描述的,MOSN 作為 Sidecar 的方式和應用執行在同一個 Pod 中,攔截所有進出應用容器的流量,SOFAMesh 相容 Istio,其中使用 Go 語言開發的 SOFAMosn 替換了 Envoy。
注意:下文中所指的 Sidecar 都是指的 Envoy 代理容器。
Init 容器
Init 容器是一種專用容器,它在應用程式容器啟動之前執行,用來包含一些應用映象中不存在的實用工具或安裝指令碼。
一個 Pod 中可以指定多個 Init 容器,如果指定了多個,那麼 Init 容器將會按順序依次執行。只有當前面的 Init 容器必須執行成功後,才可以執行下一個 Init 容器。當所有的 Init 容器執行完成後,Kubernetes 才初始化 Pod 和執行應用容器。
Init 容器使用 Linux Namespace,所以相對應用程式容器來說具有不同的檔案系統檢視。因此,它們能夠具有訪問 Secret 的許可權,而應用程式容器則不能。
在 Pod 啟動過程中,Init 容器會按順序在網路和資料卷初始化之後啟動。每個容器必須在下一個容器啟動之前成功退出。如果由於執行時或失敗退出,將導致容器啟動失敗,它會根據 Pod 的 restartPolicy
指定的策略進行重試。然而,如果 Pod 的 restartPolicy
設定為 Always,Init 容器失敗時會使用 RestartPolicy
策略。
在所有的 Init 容器沒有成功之前,Pod 將不會變成 Ready
狀態。Init 容器的埠將不會在 Service 中進行聚集。 正在初始化中的 Pod 處於 Pending
狀態,但應該會將 Initializing
狀態設定為 true。Init 容器執行完成以後就會自動終止。
關於 Init 容器的詳細資訊請參考 Init 容器 - Kubernetes 中文指南/雲原生應用架構實踐手冊。
Sidecar 注入示例分析
我們看下 Istio 官方示例 bookinfo
中 productpage
的 YAML 配置,關於 bookinfo
應用的詳細 YAML 配置請參考 bookinfo.yaml。
apiVersion: v1
kind: Service
metadata:
name: productpage
labels:
app: productpage
spec:
ports:
- port: 9080
name: http
selector:
app: productpage
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: productpage-v1
spec:
replicas: 1
template:
metadata:
labels:
app: productpage
version: v1
spec:
containers:
- name: productpage
image: istio/examples-bookinfo-productpage-v1:1.8.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080複製程式碼
再檢視下 productpage
容器的 Dockerfile。
FROM python:2.7-slim
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY productpage.py /opt/microservices/
COPY templates /opt/microservices/templates
COPY requirements.txt /opt/microservices/
EXPOSE 9080
WORKDIR /opt/microservices
CMD python productpage.py 9080複製程式碼
我們看到 Dockerfile
中沒有配置 ENTRYPOINT
,所以 CMD
的配置 python productpage.py 9080
將作為預設的 ENTRYPOINT
,記住這一點,再看下注入 sidecar 之後的配置。
$ istioctl kube-inject -f yaml/istio-bookinfo/bookinfo.yaml複製程式碼
我們只擷取其中與 productpage
相關的 Service
和 Deployment
配置部分。
apiVersion: v1
kind: Service
metadata:
name: productpage
labels:
app: productpage
spec:
ports:
- port: 9080
name: http
selector:
app: productpage
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
name: productpage-v1
spec:
replicas: 1
strategy: {}
template:
metadata:
annotations:
sidecar.istio.io/status: '{"version":"fde14299e2ae804b95be08e0f2d171d466f47983391c00519bbf01392d9ad6bb","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}'
creationTimestamp: null
labels:
app: productpage
version: v1
spec:
containers:
- image: istio/examples-bookinfo-productpage-v1:1.8.0
imagePullPolicy: IfNotPresent
name: productpage
ports:
- containerPort: 9080
resources: {}
- args:
- proxy
- sidecar
- --configPath
- /etc/istio/proxy
- --binaryPath
- /usr/local/bin/envoy
- --serviceCluster
- productpage
- --drainDuration
- 45s
- --parentShutdownDuration
- 1m0s
- --discoveryAddress
- istio-pilot.istio-system:15007
- --discoveryRefreshDelay
- 1s
- --zipkinAddress
- zipkin.istio-system:9411
- --connectTimeout
- 10s
- --statsdUdpAddress
- istio-statsd-prom-bridge.istio-system:9125
- --proxyAdminPort
- "15000"
- --controlPlaneAuthPolicy
- NONE
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: INSTANCE_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: ISTIO_META_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: ISTIO_META_INTERCEPTION_MODE
value: REDIRECT
image: jimmysong/istio-release-proxyv2:1.0.0
imagePullPolicy: IfNotPresent
name: istio-proxy
resources:
requests:
cpu: 10m
securityContext:
privileged: false
readOnlyRootFilesystem: true
runAsUser: 1337
volumeMounts:
- mountPath: /etc/istio/proxy
name: istio-envoy
- mountPath: /etc/certs/
name: istio-certs
readOnly: true
initContainers:
- args:
- -p
- "15001"
- -u
- "1337"
- -m
- REDIRECT
- -i
- '*'
- -x
- ""
- -b
- 9080,
- -d
- ""
image: jimmysong/istio-release-proxy_init:1.0.0
imagePullPolicy: IfNotPresent
name: istio-init
resources: {}
securityContext:
capabilities:
add:
- NET_ADMIN
privileged: true
volumes:
- emptyDir:
medium: Memory
name: istio-envoy
- name: istio-certs
secret:
optional: true
secretName: istio.default
status: {}複製程式碼
我們看到 Service 的配置沒有變化,所有的變化都在 Deployment
裡,Istio 給應用 Pod 注入的配置主要包括:
Init 容器
istio-init
:用於給 Sidecar 容器即 Envoy 代理做初始化,設定 iptables 埠轉發Envoy sidecar 容器
istio-proxy
:執行 Envoy 代理
接下來將分別解析下這兩個容器。
Init 容器解析
Istio 在 Pod 中注入的 Init 容器名為 istio-init
,我們在上面 Istio 注入完成後的 YAML 檔案中看到了該容器的啟動引數:
-p 15001 -u 1337 -m REDIRECT -i '*' -x "" -b 9080 -d ""複製程式碼
我們再檢查下該容器的 Dockerfile 看看 ENTRYPOINT
是什麼以確定啟動時執行的命令。
FROM ubuntu:xenial
RUN apt-get update && apt-get install -y \
iproute2 \
iptables \
&& rm -rf /var/lib/apt/lists/*
ADD istio-iptables.sh /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/istio-iptables.sh"]複製程式碼
我們看到 istio-init
容器的入口是 /usr/local/bin/istio-iptables.sh
指令碼,再按圖索驥看看這個指令碼里到底寫的什麼,該指令碼的位置在 Istio 原始碼倉庫的 tools/deb/istio-iptables.sh,一共 300 多行,就不貼在這裡了。下面我們就來解析下這個啟動指令碼。
Init 容器啟動入口
Init 容器的啟動入口是 /usr/local/bin/istio-iptables.sh
指令碼,該指令碼的用法如下:
$ istio-iptables.sh -p PORT -u UID -g GID [-m mode] [-b ports] [-d ports] [-i CIDR] [-x CIDR] [-h]
-p: 指定重定向所有 TCP 流量的 Envoy 埠(預設為 $ENVOY_PORT = 15001)
-u: 指定未應用重定向的使用者的 UID。通常,這是代理容器的 UID(預設為 $ENVOY_USER 的 uid,istio_proxy 的 uid 或 1337)
-g: 指定未應用重定向的使用者的 GID。(與 -u param 相同的預設值)
-m: 指定入站連線重定向到 Envoy 的模式,“REDIRECT” 或 “TPROXY”(預設為 $ISTIO_INBOUND_INTERCEPTION_MODE)
-b: 逗號分隔的入站埠列表,其流量將重定向到 Envoy(可選)。使用萬用字元 “*” 表示重定向所有埠。為空時表示禁用所有入站重定向(預設為 $ISTIO_INBOUND_PORTS)
-d: 指定要從重定向到 Envoy 中排除(可選)的入站埠列表,以逗號格式分隔。使用萬用字元“*” 表示重定向所有入站流量(預設為 $ISTIO_LOCAL_EXCLUDE_PORTS)
-i: 指定重定向到 Envoy(可選)的 IP 地址範圍,以逗號分隔的 CIDR 格式列表。使用萬用字元 “*” 表示重定向所有出站流量。空列表將禁用所有出站重定向(預設為 $ISTIO_SERVICE_CIDR)
-x: 指定將從重定向中排除的 IP 地址範圍,以逗號分隔的 CIDR 格式列表。使用萬用字元 “*” 表示重定向所有出站流量(預設為 $ISTIO_SERVICE_EXCLUDE_CIDR)。
環境變數位於 $ISTIO_SIDECAR_CONFIG(預設在:/var/lib/istio/envoy/sidecar.env)複製程式碼
通過檢視該指令碼你將看到,以上傳入的引數都會重新組裝成 iptables
命令的引數。
再參考 istio-init
容器的啟動引數,完整的啟動命令如下:
$ /usr/local/bin/istio-iptables.sh -p 15001 -u 1337 -m REDIRECT -i '*' -x "" -b 9080 -d ""複製程式碼
該容器存在的意義就是讓 Envoy 代理可以攔截所有的進出 Pod 的流量,即將入站流量重定向到 Sidecar,再攔截應用容器的出站流量經過 Sidecar 處理後再出站。
命令解析
這條啟動命令的作用是:
將應用容器的所有流量都轉發到 Envoy 的 15001 埠。
使用
istio-proxy
使用者身份執行, UID 為 1337,即 Envoy 所處的使用者空間,這也是istio-proxy
容器預設使用的使用者,見 YAML 配置中的runAsUser
欄位。使用預設的
REDIRECT
模式來重定向流量。將所有出站流量都重定向到 Envoy 代理。
將所有訪問 9080 埠(即應用容器
productpage
的埠)的流量重定向到 Envoy 代理。
因為 Init 容器初始化完畢後就會自動終止,因為我們無法登陸到容器中檢視 iptables 資訊,但是 Init 容器初始化結果會保留到應用容器和 Sidecar 容器中。
istio-proxy 容器解析
為了檢視 iptables 配置,我們需要登陸到 Sidecar 容器中使用 root 使用者來檢視,因為 kubectl
無法使用特權模式來遠端操作 docker 容器,所以我們需要登陸到 productpage
Pod 所在的主機上使用 docker
命令登陸容器中檢視。
檢視 productpage
Pod 所在的主機。
$ kubectl -n default get pod -l app=productpage -o wide
NAME READY STATUS RESTARTS AGE IP NODE
productpage-v1-745ffc55b7-2l2lw 2/2 Running 0 1d 172.33.78.10 node3複製程式碼
從輸出結果中可以看到該 Pod 執行在 node3
上,使用 vagrant
命令登陸到 node3
主機中並切換為 root 使用者。
$ vagrant ssh node3
$ sudo -i複製程式碼
檢視 iptables 配置,列出 NAT(網路地址轉換)表的所有規則,因為在 Init 容器啟動的時候選擇給 istio-iptables.sh
傳遞的引數中指定將入站流量重定向到 Envoy 的模式為 “REDIRECT”,因此在 iptables 中將只有 NAT 表的規格配置,如果選擇 TPROXY
還會有 mangle
表配置。iptables
命令的詳細用法請參考 iptables,規則配置請參考 iptables 規則配置。
理解 iptables
iptables
是 Linux 核心中的防火牆軟體 netfilter 的管理工具,位於使用者空間,同時也是 netfilter 的一部分。Netfilter 位於核心空間,不僅有網路地址轉換的功能,也具備資料包內容修改、以及資料包過濾等防火牆功能。
在瞭解 Init 容器初始化的 iptables 之前,我們先來了解下 iptables 和規則配置。
iptables 中的表
Init 容器中使用的的 iptables 版本是 v1.6.0
,共包含 5 張表:
raw
用於配置資料包,raw
中的資料包不會被系統跟蹤。filter
是用於存放所有與防火牆相關操作的預設表。nat
用於 網路地址轉換(例如:埠轉發)。mangle
用於對特定資料包的修改(參考損壞資料包)。security
用於強制訪問控制 網路規則。
注:在本示例中只用到了 nat
表。
不同的表中的具有的鏈型別如下表所示:
規則名稱 | raw | filter | nat | mangle | security |
---|---|---|---|---|---|
PREROUTING | ✓ | ✓ | ✓ | ||
INPUT | ✓ | ✓ | ✓ | ✓ | |
OUTPUT | ✓ | ✓ | ✓ | ✓ | |
POSTROUTING | ✓ | ✓ | |||
FORWARD | ✓ | ✓ | ✓ | ✓ |
下圖是 iptables 的呼叫鏈順序。
關於 iptables 的詳細介紹請參考常見 iptables 使用規則場景整理。
iptables 命令
iptables
命令的主要用途是修改這些表中的規則。iptables
命令格式如下:
$ iptables [-t 表名] 命令選項[鏈名][條件匹配][-j 目標動作或跳轉]複製程式碼
Init 容器中的 /istio-iptables.sh
啟動入口指令碼就是執行 iptables 初始化的。
理解 iptables 規則
檢視 istio-proxy
容器中的預設的 iptables 規則,預設檢視的是 filter 表中的規則。
$ iptables -L -v
Chain INPUT (policy ACCEPT 350K packets, 63M bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 18M packets, 1916M bytes)
pkts bytes target prot opt in out source destination複製程式碼
我們看到三個預設的鏈,分別是 INPUT、FORWARD 和 OUTPUT,每個鏈中的第一行輸出表示鏈名稱(在本例中為INPUT/FORWARD/OUTPUT),後跟預設策略(ACCEPT)。
下圖是 iptables 的建議結構圖,流量在經過 INPUT 鏈之後就進入了上層協議棧,比如
每條鏈中都可以新增多條規則,規則是按照順序從前到後執行的。我們來看下規則的表頭定義。
pkts:處理過的匹配的報文數量
bytes:累計處理的報文大小(位元組數)
target:如果報文與規則匹配,指定目標就會被執行。
prot:協議,例如
tdp
、udp
、icmp
和all
。opt:很少使用,這一列用於顯示 IP 選項。
in:入站網路卡。
out:出站網路卡。
source:流量的源 IP 地址或子網,後者是
anywhere
。destination:流量的目的地 IP 地址或子網,或者是
anywhere
。
還有一列沒有表頭,顯示在最後,表示規則的選項,作為規則的擴充套件匹配條件,用來補充前面的幾列中的配置。prot
、opt
、in
、out
、source
和 destination
和顯示在 destination
後面的沒有表頭的一列擴充套件條件共同組成匹配規則。當流量匹配這些規則後就會執行 target
。
關於 iptables 規則請參考常見iptables使用規則場景整理。
target 支援的型別
target
型別包括 ACCEPT、REJECT
、DROP
、LOG
、SNAT
、MASQUERADE
、DNAT
、REDIRECT
、RETURN
或者跳轉到其他規則等。只要執行到某一條鏈中只有按照順序有一條規則匹配後就可以確定報文的去向了,除了 RETURN
型別,類似程式語言中的 return
語句,返回到它的呼叫點,繼續執行下一條規則。target
支援的配置詳解請參考 iptables 詳解(1):iptables 概念。
從輸出結果中可以看到 Init 容器沒有在 iptables 的預設鏈路中建立任何規則,而是建立了新的鏈路。
檢視 iptables nat 表中注入的規則
Init 容器通過向 iptables nat 表中注入轉發規則來劫持流量的,下圖顯示的是 productpage 服務中的 iptables 流量劫持的詳細過程。
Init 容器啟動時命令列引數中指定了 REDIRECT
模式,因此只建立了 NAT 表規則,接下來我們檢視下 NAT 表中建立的規則,這是全文中的重點部分,前面講了那麼多都是為它做鋪墊的。下面是檢視 nat 表中的規則,其中鏈的名字中包含 ISTIO
字首的是由 Init 容器注入的,規則匹配是根據下面顯示的順序來執行的,其中會有多次跳轉。
# 檢視 NAT 表中規則配置的詳細資訊
$ iptables -t nat -L -v
# PREROUTING 鏈:用於目標地址轉換(DNAT),將所有入站 TCP 流量跳轉到 ISTIO_INBOUND 鏈上
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
2 120 ISTIO_INBOUND tcp -- any any anywhere anywhere
# INPUT 鏈:處理輸入資料包,非 TCP 流量將繼續 OUTPUT 鏈
Chain INPUT (policy ACCEPT 2 packets, 120 bytes)
pkts bytes target prot opt in out source destination
# OUTPUT 鏈:將所有出站資料包跳轉到 ISTIO_OUTPUT 鏈上
Chain OUTPUT (policy ACCEPT 41146 packets, 3845K bytes)
pkts bytes target prot opt in out source destination
93 5580 ISTIO_OUTPUT tcp -- any any anywhere anywhere
# POSTROUTING 鏈:所有資料包流出網路卡時都要先進入POSTROUTING 鏈,核心根據資料包目的地判斷是否需要轉發出去,我們看到此處未做任何處理
Chain POSTROUTING (policy ACCEPT 41199 packets, 3848K bytes)
pkts bytes target prot opt in out source destination
# ISTIO_INBOUND 鏈:將所有目的地為 9080 埠的入站流量重定向到 ISTIO_IN_REDIRECT 鏈上
Chain ISTIO_INBOUND (1 references)
pkts bytes target prot opt in out source destination
2 120 ISTIO_IN_REDIRECT tcp -- any any anywhere anywhere tcp dpt:9080
# ISTIO_IN_REDIRECT 鏈:將所有的入站流量跳轉到本地的 15001 埠,至此成功的攔截了流量懂啊 Envoy
Chain ISTIO_IN_REDIRECT (1 references)
pkts bytes target prot opt in out source destination
2 120 REDIRECT tcp -- any any anywhere anywhere redir ports 15001
# ISTIO_OUTPUT 鏈:選擇需要重定向到 Envoy(即本地) 的出站流量,所有非 localhost 的流量全部轉發到 ISTIO_REDIRECT。為了避免流量在該 Pod 中無限迴圈,所有到 istio-proxy 使用者空間的流量都返回到它的呼叫點中的下一條規則,本例中即 OUTPUT 鏈,因為跳出 ISTIO_OUTPUT 規則之後就進入下一條鏈 POSTROUTING。如果目的地非 localhost 就跳轉到 ISTIO_REDIRECT;如果流量是來自 istio-proxy 使用者空間的,那麼就跳出該鏈,返回它的呼叫鏈繼續執行下一條規則(OUPT 的下一條規則,無需對流量進行處理);所有的非 istio-proxy 使用者空間的目的地是 localhost 的流量就跳轉到 ISTIO_REDIRECT
Chain ISTIO_OUTPUT (1 references)
pkts bytes target prot opt in out source destination
0 0 ISTIO_REDIRECT all -- any lo anywhere !localhost
40 2400 RETURN all -- any any anywhere anywhere owner UID match istio-proxy
0 0 RETURN all -- any any anywhere anywhere owner GID match istio-proxy
0 0 RETURN all -- any any anywhere localhost
53 3180 ISTIO_REDIRECT all -- any any anywhere anywhere
# ISTIO_REDIRECT 鏈:將所有流量重定向到 Envoy(即本地) 的 15001 埠
Chain ISTIO_REDIRECT (2 references)
pkts bytes target prot opt in out source destination
53 3180 REDIRECT tcp -- any any anywhere anywhere redir ports 15001複製程式碼
iptables
顯示的鏈的順序,即流量規則匹配的順序。其中要特別注意 ISTIO_OUTPUT
鏈中的規則配置。為了避免流量一直在 Pod 中無限迴圈,所有到 istio-proxy 使用者空間的流量都返回到它的呼叫點中的下一條規則,本例中即 OUTPUT 鏈,因為跳出 ISTIO_OUTPUT
規則之後就進入下一條鏈 POSTROUTING
。
ISTIO_OUTPUT
鏈規則匹配的詳細過程如下:
如果目的地非 localhost 就跳轉到 ISTIO_REDIRECT 鏈
所有來自 istio-proxy 使用者空間的流量跳轉到它的呼叫點
OUTPUT
繼續執行OUTPUT
鏈的下一條規則,因為OUTPUT
鏈中沒有下一條規則了,所以會繼續執行POSTROUTING
鏈然後跳出 iptables,直接訪問目的地如果目的地是 localhost 但是流量又不是來自 istio-proxy 使用者空間的就跳轉到
ISTIO_REDIRECT
鏈
以上 iptables 規則都是 Init 容器啟動的時使用 istio-iptables.sh 指令碼生成的,詳細過程可以檢視該指令碼。
檢視 Envoy 執行狀態
首先檢視 proxyv2
映象的 Dockerfile。
FROM istionightly/base_debug
ARG proxy_version
ARG istio_version
# 安裝 Envoy
ADD envoy /usr/local/bin/envoy
# 使用環境變數的方式明文指定 proxy 的版本/功能
ENV ISTIO_META_ISTIO_PROXY_VERSION "1.1.0"
# 使用環境變數的方式明文指定 proxy 明確的 sha,用於指定版本的配置和除錯
ENV ISTIO_META_ISTIO_PROXY_SHA $proxy_version
# 環境變數,指定明確的構建號,用於除錯
ENV ISTIO_META_ISTIO_VERSION $istio_version
ADD pilot-agent /usr/local/bin/pilot-agent
ADD envoy_pilot.yaml.tmpl /etc/istio/proxy/envoy_pilot.yaml.tmpl
ADD envoy_policy.yaml.tmpl /etc/istio/proxy/envoy_policy.yaml.tmpl
ADD envoy_telemetry.yaml.tmpl /etc/istio/proxy/envoy_telemetry.yaml.tmpl
ADD istio-iptables.sh /usr/local/bin/istio-iptables.sh
COPY envoy_bootstrap_v2.json /var/lib/istio/envoy/envoy_bootstrap_tmpl.json
RUN chmod 755 /usr/local/bin/envoy /usr/local/bin/pilot-agent
# 將 istio-proxy 使用者加入 sudo 許可權以允許執行 tcpdump 和其他除錯命令
RUN useradd -m --uid 1337 istio-proxy && \
echo "istio-proxy ALL=NOPASSWD: ALL" >> /etc/sudoers && \
chown -R istio-proxy /var/lib/istio
# 使用 pilot-agent 來啟動 Envoy
ENTRYPOINT ["/usr/local/bin/pilot-agent"]複製程式碼
該容器的啟動入口是 pilot-agent
命令,根據 YAML 配置中傳遞的引數,詳細的啟動命令入下:
/usr/local/bin/pilot-agent proxy sidecar --configPath /etc/istio/proxy --binaryPath /usr/local/bin/envoy --serviceCluster productpage --drainDuration 45s --parentShutdownDuration 1m0s --discoveryAddress istio-pilot.istio-system:15007 --discoveryRefreshDelay 1s --zipkinAddress zipkin.istio-system:9411 --connectTimeout 10s --statsdUdpAddress istio-statsd-prom-bridge.istio-system:9125 --proxyAdminPort 15000 --controlPlaneAuthPolicy NONE複製程式碼
主要配置了 Envoy 二進位制檔案的位置、服務發現地址、服務叢集名、監控指標上報地址、Envoy 的管理埠、熱重啟時間等,詳細用法請參考 Istio官方文件 pilot-agent 的用法。
pilot-agent
是容器中 PID 為 1 的啟動程式,它啟動時又建立了一個 Envoy 程式,如下:
/usr/local/bin/envoy -c /etc/istio/proxy/envoy-rev0.json --restart-epoch 0 --drain-time-s 45 --parent-shutdown-time-s 60 --service-cluster productpage --service-node sidecar~172.33.78.10~productpage-v1-745ffc55b7-2l2lw.default~default.svc.cluster.local --max-obj-name-len 189 -l warn --v2-config-only複製程式碼
我們分別解釋下以上配置的意義。
-c /etc/istio/proxy/envoy-rev0.json
:配置檔案,支援.json
、.yaml
、.pb
和.pb_text
格式,pilot-agent
啟動的時候讀取了容器的環境變數後建立的。--restart-epoch 0
:Envoy 熱重啟週期,第一次啟動預設為 0,每熱重啟一次該值加 1。--drain-time-s 45
:熱重啟期間 Envoy 將耗盡連線的時間。--parent-shutdown-time-s 60
: Envoy 在熱重啟時關閉父程式之前等待的時間。--service-cluster productpage
:Envoy 執行的本地服務叢集的名字。--service-node sidecar~172.33.78.10~productpage-v1-745ffc55b7-2l2lw.default~default.svc.cluster.local
:定義 Envoy 執行的本地服務節點名稱,其中包含了該 Pod 的名稱、IP、DNS 域等資訊,根據容器的環境變數拼出來的。-max-obj-name-len 189
:cluster/route_config/listener 中名稱欄位的最大長度(以位元組為單位)-l warn
:日誌級別--v2-config-only
:只解析 v2 引導配置檔案
詳細配置請參考 Envoy 的命令列選項。
檢視 Envoy 的配置檔案 /etc/istio/proxy/envoy-rev0.json
。
{
"node": {
"id": "sidecar~172.33.78.10~productpage-v1-745ffc55b7-2l2lw.default~default.svc.cluster.local",
"cluster": "productpage",
"metadata": {
"INTERCEPTION_MODE": "REDIRECT",
"ISTIO_PROXY_SHA": "istio-proxy:6166ae7ebac7f630206b2fe4e6767516bf198313",
"ISTIO_PROXY_VERSION": "1.0.0",
"ISTIO_VERSION": "1.0.0",
"POD_NAME": "productpage-v1-745ffc55b7-2l2lw",
"istio": "sidecar"
}
},
"stats_config": {
"use_all_default_tags": false
},
"admin": {
"access_log_path": "/dev/stdout",
"address": {
"socket_address": {
"address": "127.0.0.1",
"port_value": 15000
}
}
},
"dynamic_resources": {
"lds_config": {
"ads": {}
},
"cds_config": {
"ads": {}
},
"ads_config": {
"api_type": "GRPC",
"refresh_delay": {"seconds": 1, "nanos": 0},
"grpc_services": [
{
"envoy_grpc": {
"cluster_name": "xds-grpc"
}
}
]
}
},
"static_resources": {
"clusters": [
{
"name": "xds-grpc",
"type": "STRICT_DNS",
"connect_timeout": {"seconds": 10, "nanos": 0},
"lb_policy": "ROUND_ROBIN",
"hosts": [
{
"socket_address": {"address": "istio-pilot.istio-system", "port_value": 15010}
}
],
"circuit_breakers": {
"thresholds": [
{
"priority": "default",
"max_connections": "100000",
"max_pending_requests": "100000",
"max_requests": "100000"
},
{
"priority": "high",
"max_connections": "100000",
"max_pending_requests": "100000",
"max_requests": "100000"
}]
},
"upstream_connection_options": {
"tcp_keepalive": {
"keepalive_time": 300
}
},
"http2_protocol_options": { }
}
,
{
"name": "zipkin",
"type": "STRICT_DNS",
"connect_timeout": {
"seconds": 1
},
"lb_policy": "ROUND_ROBIN",
"hosts": [
{
"socket_address": {"address": "zipkin.istio-system", "port_value": 9411}
}
]
}
]
},
"tracing": {
"http": {
"name": "envoy.zipkin",
"config": {
"collector_cluster": "zipkin"
}
}
},
"stats_sinks": [
{
"name": "envoy.statsd",
"config": {
"address": {
"socket_address": {"address": "10.254.109.175", "port_value": 9125}
}
}
}
]
}複製程式碼
下圖是使用 Istio 管理的 bookinfo 示例的訪問請求路徑圖。
圖片來自 Istio 官方網站
對照 bookinfo 示例的 productpage 的檢視建立的連線。在 productpage-v1-745ffc55b7-2l2lw
Pod 的 istio-proxy
容器中使用 root 使用者檢視開啟的埠。
$ lsof -i
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
envoy 11 istio-proxy 9u IPv4 73951 0t0 TCP localhost:15000 (LISTEN) # Envoy admin 埠
envoy 11 istio-proxy 17u IPv4 74320 0t0 TCP productpage-v1-745ffc55b7-2l2lw:46862->istio-pilot.istio-system.svc.cluster.local:15010 (ESTABLISHED) # 15010:istio-pilot 的 grcp-xds 埠
envoy 11 istio-proxy 18u IPv4 73986 0t0 UDP productpage-v1-745ffc55b7-2l2lw:44332->istio-statsd-prom-bridge.istio-system.svc.cluster.local:9125 # 給 Promethues 傳送 metric 的埠
envoy 11 istio-proxy 52u IPv4 74599 0t0 TCP *:15001 (LISTEN) # Envoy 的監聽埠
envoy 11 istio-proxy 53u IPv4 74600 0t0 UDP productpage-v1-745ffc55b7-2l2lw:48011->istio-statsd-prom-bridge.istio-system.svc.cluster.local:9125 # 給 Promethues 傳送 metric 埠
envoy 11 istio-proxy 54u IPv4 338551 0t0 TCP productpage-v1-745ffc55b7-2l2lw:15001->172.17.8.102:52670 (ESTABLISHED) # 52670:Ingress gateway 埠
envoy 11 istio-proxy 55u IPv4 338364 0t0 TCP productpage-v1-745ffc55b7-2l2lw:44046->172.33.78.9:9091 (ESTABLISHED) # 9091:istio-telemetry 服務的 grpc-mixer 埠
envoy 11 istio-proxy 56u IPv4 338473 0t0 TCP productpage-v1-745ffc55b7-2l2lw:47210->zipkin.istio-system.svc.cluster.local:9411 (ESTABLISHED) # 9411: zipkin 埠
envoy 11 istio-proxy 58u IPv4 338383 0t0 TCP productpage-v1-745ffc55b7-2l2lw:41564->172.33.84.8:9080 (ESTABLISHED) # 9080:details-v1 的 http 埠
envoy 11 istio-proxy 59u IPv4 338390 0t0 TCP productpage-v1-745ffc55b7-2l2lw:54410->172.33.78.5:9080 (ESTABLISHED) # 9080:reivews-v2 的 http 埠
envoy 11 istio-proxy 60u IPv4 338411 0t0 TCP productpage-v1-745ffc55b7-2l2lw:35200->172.33.84.5:9091 (ESTABLISHED) # 9091:istio-telemetry 服務的 grpc-mixer 埠
envoy 11 istio-proxy 62u IPv4 338497 0t0 TCP productpage-v1-745ffc55b7-2l2lw:34402->172.33.84.9:9080 (ESTABLISHED) # reviews-v1 的 http 埠
envoy 11 istio-proxy 63u IPv4 338525 0t0 TCP productpage-v1-745ffc55b7-2l2lw:50592->172.33.71.5:9080 (ESTABLISHED) # reviews-v3 的 http 埠複製程式碼
從輸出級過上可以驗證 Sidecar 是如何接管流量和與 istio-pilot 通訊,及向 Mixer 做遙測資料匯聚的。感興趣的讀者可以再去看看其他幾個服務的 istio-proxy 容器中的 iptables 和埠資訊。
參考
SOFAMesh & SOFA MOSN—基於Istio構建的用於應對大規模流量的Service Mesh解決方案 - jimmysong.io
How To List and Delete Iptables Firewall Rules - digitalocean.com
ServiceMesher社群資訊
微信群:聯絡我入群
Slack:servicemesher.slack.com 需要邀請才能加入
Twitter: twitter.com/servicemesh…
GitHub:github.com/
更多Service Mesh諮詢請掃碼關注微信公眾號ServiceMesher。