Istio 數據平面 Pod 啟動過程詳解

語言: CN / TW / HK

本文將為你講解:

  • Istio 中 sidecar 自動注入過程
  • Istio 中的 init 容器啟動過程
  • 啟用了 Sidecar 自動注入的 Pod 的啟動流程

下圖中展示了 Istio 數據平面中的 Pod 啟動完後的組件。

Istio 數據平面 Pod 內部組件

Istio 中的 sidecar 注入

Istio 中提供了以下兩種 sidecar 注入方式:

不論是手動注入還是自動注入,sidecar 的注入過程都需要遵循如下步驟:

  1. Kubernetes 需要了解待注入的 sidecar 所連接的 Istio 集羣及其配置;
  2. Kubernetes 需要了解待注入的 sidecar 容器本身的配置,如鏡像地址、啟動參數等;
  3. Kubernetes 根據 sidecar 注入模板和以上配置填充 sidecar 的配置參數,將以上配置注入到應用容器的一側;

使用下面的命令可以手動注入 sidecar。

istioctl kube-inject -f ${YAML_FILE} | kuebectl apply -f -

該命令會使用 Istio 內置的 sidecar 配置來注入,下面使用 Istio詳細配置請參考 Istio 官網

注入完成後您將看到 Istio 為原有 pod template 注入了 initContainer 及 sidecar proxy相關的配置。

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 中文指南/雲原生應用架構實踐手冊

Init 容器解析

Istio 在 pod 中注入的 Init 容器名為 istio-init ,我們在上面 Istio 注入完成後的 YAML 文件中看到了該容器的啟動命令是:

istio-iptables -p 15001 -z 15006 -u 1337 -m REDIRECT -i '*' -x "" -b '*' -d 15090,15020

我們再檢查下該容器的 Dockerfile 看看 ENTRYPOINT 是怎麼確定啟動時執行的命令。

# 前面的內容省略
# The pilot-agent will bootstrap Envoy.
ENTRYPOINT ["/usr/local/bin/pilot-agent"]

我們看到 istio-init 容器的入口是 /usr/local/bin/istio-iptables 命令行,該命令行工具的代碼的位置在 Istio 源碼倉庫的 tools/istio-iptables 目錄。

注意:在 Istio 1.1 版本時還是使用 isito-iptables.sh 命令行來操作 IPtables。

Init 容器啟動入口

Init 容器的啟動入口是 istio-iptables 命令行,該命令行工具的用法如下:

$ istio-iptables [flags]
  -p: 指定重定向所有 TCP 流量的 sidecar 端口(默認為 $ENVOY_PORT = 15001)
  -m: 指定入站連接重定向到 sidecar 的模式,“REDIRECT” 或 “TPROXY”(默認為 $ISTIO_INBOUND_INTERCEPTION_MODE)
  -b: 逗號分隔的入站端口列表,其流量將重定向到 Envoy(可選)。使用通配符 “*” 表示重定向所有端口。為空時表示禁用所有入站重定向(默認為 $ISTIO_INBOUND_PORTS)
  -d: 指定要從重定向到 sidecar 中排除的入站端口列表(可選),以逗號格式分隔。使用通配符“*” 表示重定向所有入站流量(默認為 $ISTIO_LOCAL_EXCLUDE_PORTS)
  -o:逗號分隔的出站端口列表,不包括重定向到 Envoy 的端口。
  -i: 指定重定向到 sidecar 的 IP 地址範圍(可選),以逗號分隔的 CIDR 格式列表。使用通配符 “*” 表示重定向所有出站流量。空列表將禁用所有出站重定向(默認為 $ISTIO_SERVICE_CIDR)
  -x: 指定將從重定向中排除的 IP 地址範圍,以逗號分隔的 CIDR 格式列表。使用通配符 “*” 表示重定向所有出站流量(默認為 $ISTIO_SERVICE_EXCLUDE_CIDR)。
  -k:逗號分隔的虛擬接口列表,其入站流量(來自虛擬機的)將被視為出站流量。
  -g:指定不應用重定向的用户的 GID。(默認值與 -u param 相同)
  -u:指定不應用重定向的用户的 UID。通常情況下,這是代理容器的 UID(默認值是 1337,即 istio-proxy 的 UID)。
  -z: 所有進入 pod/VM 的 TCP 流量應被重定向到的端口(默認 $INBOUND_CAPTURE_PORT = 15006)。

以上傳入的參數都會重新組裝成 iptables 規則,關於該命令的詳細用法請訪問 tools/istio-iptables/pkg/cmd/root.go

該容器存在的意義就是讓 sidecar 代理可以攔截所有的進出 pod 的流量,15090 端口(Mixer 使用)和 15092 端口(Ingress Gateway)除外的所有入站(inbound)流量重定向到 15006 端口(sidecar),再攔截應用容器的出站(outbound)流量經過 sidecar 處理(通過 15001 端口監聽)後再出站。關於 Istio 中端口用途請參考 Istio 官方文檔

命令解析

這條啟動命令的作用是:

  • 將應用容器的所有流量都轉發到 sidecar 的 15006 端口。
  • 使用 istio-proxy 用户身份運行, UID 為 1337,即 sidecar 所處的用户空間,這也是 istio-proxy 容器默認使用的用户,見 YAML 配置中的 runAsUser 字段。
  • 使用默認的 REDIRECT 模式來重定向流量。
  • 將所有出站流量都重定向到 sidecar 代理(通過 15001 端口)。

因為 Init 容器初始化完畢後就會自動終止,因為我們無法登陸到容器中查看 iptables 信息,但是 Init 容器初始化結果會保留到應用容器和 sidecar 容器中。

Pod 啟動流程

啟用了 Sidecar 自動注入的 Pod 啟動流程如下:

  1. Init 容器先啟動,向 Pod 中注入 iptables 規則,進行透明流量攔截。
  2. 隨後,Kubernetes 會根據 Pod Spec 中容器的聲明順序依次啟動容器,但這是非阻塞的,無法保證第一個容器啟動完成後才啟動下一個。 istio-proxy 容器啟動時, pilot-agent 將作為 PID 1 號進程,它是 Linux 用户空間的第一個進程,負責拉起其他進程和處理殭屍進程。 pilot-agent 將生成 Envoy bootstrap 配置並拉起 envoy 進程;應用容器幾乎跟 istio-proxy 容器同時啟動,為了防止 Pod 內的容器在還沒啟動好的情況而接收到外界流量,這時候就緒探針就派上用場了。Kubernetes 會在 istio-proxy 容器的 15021 端口進行就緒檢查,直到 isito-proxy 啟動完成後 kubelet 才會將流量路由到 Pod 內。
  3. 在 Pod 啟動完成後, pilot-agent 將變為守護進程監視系統其他進程,除此之外,該進程還為 Envoy 提供 Bootstrap 配置、證書、健康檢查、配置熱加載、身份支持及進程生命週期管理等。

Pod 內容器啟動順序問題

在 Pod 啟動的過程中存在容器啟動順序問題,假設下面這種情況,應用容器先啟動,請求其他服務,這時候 istio-proxy 容器還沒啟動完成,那麼該請求將會失敗,如果你的應用的健壯性不足,甚至可能導致應用容器崩潰,進而 Pod 重啟。對於這種情況的解決方案是:

  • 修改應用程序,增加超時重試。
  • 增加應用容器中進程的啟動延遲,比如增加 sleep 時間。
  • 在應用容器中增加一個 postStart 配置,檢測應用進程是否啟動完成,只有當檢測成功時,Kubernetes 才會將 Pod 的狀態標記為 Running

總結

這篇文章帶領大家瞭解了 Istio 數據平面中的 Pod 啟動過程,還有因為 Pod 內容器啟動順序帶來的問題。

參考