Istio Ambient Mesh 介紹

語言: CN / TW / HK

1 Ambient Mesh 介紹

Istio 的傳統模式是將 Envoy proxy 作為 sidecar 部署在工作負載的 Pod 中,雖然與重構應用程序相比,sidecar 具有顯著的優勢,但是仍然會產生一些限制:

  • 侵入性:sidecar 必須通過修改 Kubernetes Pod 的配置和重定向流量來“注入”應用程序。因此,安裝和升級 sidecar 需要重新啟動 Pod,這將會對工作負載產生影響。
  • 資源利用率低:由於在每個工作負載 Pod 都注入了 sidecar 代理 ,因此 Pod 必須為 sidecar 預留足夠的 CPU 和內存資源,從而導致整個集羣的資源利用率不足。
  • 流量中斷:流量捕獲和 HTTP 處理通常是由 Istio 的 sidecar 完成的,計算需要消耗大量的資源,並且可能會破壞一些不符合 HTTP 實現的應用程序。

Istio ambient mesh 是 Istio 的一個無 sidecar 的數據平面,旨在降低基礎設施成本和提高性能。 它的本質是分離 sidecar proxy(Envoy)中的 L4 和 L7 功能,讓一部分僅需要安全功能的用户可以最小阻力(低資源消耗、運維成本)地使用 Istio service mesh。

ambient mesh 將 Istio 的功能拆分為 2 個不同的層次:

  • L4 安全覆蓋層:用户可以使用 TCP 路由,mTLS 和有限的可觀測性等功能。
  • L7 處理層:用户可以按需啟用 L7 功能,以獲得 Istio 的全部功能,例如限速,故障注入,負載均衡,熔斷等等。

ztunnel 是 ambient mesh 在每個節點上運行的共享代理,以 DaemonSet 的方式部署,處於類似於 CNI 的網格底層。ztunnel 在節點間構建零信任的隧道(zero-trust tunnel, ztunnel),負責安全地連接和驗證網格內的元素。在 ambient mesh 中的工作負載的所有流量會重定向到本地的 ztunnel 進行處理,ztunnel 識別流量的工作負載併為其選擇正確的證書以建立 mTLS 連接。

ztunnel 實現了服務網格中的核心功能:零信任,它會為啟用了 ambient mesh 的 Namespace 中的工作負載創建一個安全覆蓋層,提供 mTLS,遙測,認證和 L4 授權等功能,而無需終止或解析 HTTP。 在啟用 ambient mesh 和創建安全覆蓋層之後,可以選擇性地為 namespace 啟用 L7 功能,這允許命名空間實現全套的 Istio 功能,包括 Virtual Service、L7 遙測 和 L7 授權策略。waypoint proxy 可以根據所服務的 Namespace 的實時流量自動擴縮容,這將為用户節省大量的資源。

 Istio 會為根據服務的 service account 創建相應的 waypoint proxy,可以幫助用户在減少資源消耗的情況下同時儘可能地縮小故障域,參見下圖 Model III。  

2 Ambient Mesh 支持的環境和限制

目前已知 ambient mesh 僅支持以下環境,其他環境目前尚未經過測試。

  • GKE (without Calico or Dataplane V2)
  • EKS
  • kind

並且 ambient mesh 還有許多限制,例如:

  • AuthorizationPolicy 在某些情況下沒有預期的那麼嚴格,或者根本無效。
  • 在某些情況下直接訪問 Pod IP 而不是 Service 的請求將無效。
  •  ambient mesh 下的服務無法通過 LoadBalance 和 NodePort 的方式訪問,不過你可以部署一個入口網關(未啟用 ambient mesh)以從外部訪問服務;
  • STRICT mTLS 不能完全阻止明文流量。
  • 不支持 EnvoyFilter。

詳細説明請參見 Ambient Mesh[1]

3 使用 Eksctl 在 AWS 上創建 Kubernetes 集羣

在本示例中,將使用 eksctl 在 AWS 上創建 EKS 集羣來測試 Istio ambient mesh。eksctl[2] 是一個用於管理 EKS(Amazon 託管 Kubernetes 服務)的 CLI 工具。有關 eksctl 的安裝和使用參見 eksctl Getting started[3]

創建集羣配置文件 cluster.yaml,我們將創建一個 2 個 Worker 節點的 EKS 集羣,每個節點資源為 2C8G,集羣版本為 1.23。

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: aws-demo-cluster01
  region: us-east-1
  version: '1.23'

nodeGroups:
  - name: ng-1
    instanceType: m5.large
    desiredCapacity: 2
    volumeSize: 100
    ssh:
      allow: true # will use ~/.ssh/id_rsa.pub as the default ssh key

執行以下命令,創建 EKS 集羣。

eksctl create cluster -f cluster.yaml

創建完成後,查看 EKS 集羣。

> eksctl get cluster
NAME			REGION		EKSCTL CREATED
aws-demo-cluster01	us-east-1	True

執行以下命令,將 aws-demo-cluster01 集羣的 kubeconfig 文件更新到 ~/.kube/config 文件中,讓我們本地的 kubectl 工具可以訪問到 aws-demo-cluster01 集羣。

aws eks update-kubeconfig --region us-east-1 --name aws-demo-cluster01

有關 aws CLI 工具的安裝參見 Installing or updating the latest version of the AWS CLI[4],aws CLI 的認證參見 Configuration basics[5]

4 下載 Istio

根據對應操作系統下載支持 ambient mesh 的 istioctl 二進制文件和示例資源文件,參見 Istio 下載[6]。其中 istioctl 的二進制文件可以在 bin 目錄中找到,示例資源文件可以在 samples 目錄中找到。

5 部署示例應用

部署 Istio 示例的 Bookinfo 應用程序,以及 sleep 和 notsleep 兩個客户端。sleep 和 notsleep 可以執行 curl 命令來發起 HTTP 請求。

kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
kubectl apply -f https://raw.githubusercontent.com/linsun/sample-apps/main/sleep/sleep.yaml
kubectl apply -f https://raw.githubusercontent.com/linsun/sample-apps/main/sleep/notsleep.yaml

當前我們部署的 istio 和應用的 Pod 和 Service 如下所示。

6 部署 Istio

執行以下命令,安裝 Istio,並指定 profile=ambient 參數部署 ambient mesh 相關的組件。

istioctl install --set profile=ambient

如果安裝成功將會輸出以下結果。

✔ Istio core installed
✔ Istiod installed
✔ Ingress gateways installed
✔ CNI installed
✔ Installation complete

安裝完成以後我們在 istio-system 命名空間內可以看到以下組件:

  • istiod:Istio 的核心組件。
  • istio-ingressgateway:管理進出集羣的南北向流量,在本示例中我們不會用到 istio-ingressgateway。
  • istio-cni:為加入 ambient mesh 的 Pod 配置流量重定向,將 Pod 的進出流量重定向到相同節點的 ztunnel 上。
  • ztunnel:ztunnel 在節點間構建零信任的隧道,提供 mTLS,遙測,認證和 L4 授權等功能。
> kubectl get pod -n istio-system
NAME                                   READY   STATUS    RESTARTS   AGE
istio-cni-node-gfmqp                   1/1     Running   0          100s
istio-cni-node-t2flv                   1/1     Running   0          100s
istio-ingressgateway-f6d95c86b-mfk4t   1/1     Running   0          101s
istiod-6c99d96db7-4ckbm                1/1     Running   0          2m23s
ztunnel-fnjg2                          1/1     Running   0          2m24s
ztunnel-k4jhb                          1/1     Running   0          2m24s                       

7 抓包設置

為了更直觀地觀察流量的訪問情況 ,我們可以對 Pod 進行抓包,但是應用 Pod 並沒有安裝相關的抓包工具,這時候我們可以使用 kubectl debug 工具創建一個 ephemeral 臨時容器共享容器的命名空間來進行調試。有關 kubectl debug 詳情請參見調試運行中的 Pod[7]

在 4 個終端分別執行以下命令,對 sleep 和 productpage 以及兩個節點上的 ztunnel Pod 進行抓包。--image 參數指定臨時容器的鏡像,這裏使用的 nicolaka/netshoot 鏡像中預裝了 tcpdump, tshark, termshark 等常用的網絡抓包工具。

kubectl debug -it sleep-55697f8897-n2ldz  --image=nicolaka/netshoot 
kubectl debug -it productpage-v1-5586c4d4ff-z8jbb --image=nicolaka/netshoot
kubectl debug -it -n istio-system ztunnel-fnjg2 --image=nicolaka/netshoot 
kubectl debug -it -n istio-system ztunnel-k4jhb --image=nicolaka/netshoot

在 4 個終端分別執行 termshark -i eth0 命令,對 Pod 的 eth0 網卡進行抓包。由於 istio-cni 會持續對 ztunnel 發起路徑為 /healthz/ready 的HTTP 健康探測,為了避免該流量影響我們的觀察,在 2 個 ztunnel Pod 中的 termshark Filter 框中設置以下過濾條件。

# ztunnel-fnjg2,sleep 所在節點的 ztunnel
ip.addr==192.168.58.148 || ip.addr==192.168.13.108

# ztunnel-k4jhb,productpage 所在節點的 ztunnel
ip.addr==192.168.13.108

8 未使用 Ambient Mesh 管理流量

由於當前 default Namespace 還沒有加入 ambient mesh,此時應用的流量並不會經過 ztunnel,Pod 之間通過 kubernetes 的 Service 進制進行通信,Pod 之間的流量也不會進行 mTLS 加密,而是以明文的方式進行傳播。

使用 sleep 向 productpage 發起一次請求。

kubectl exec deploy/sleep -- curl -s http://productpage:9080/ | head -n1

# 返回結果,響應結果的第一行內容
<!DOCTYPE html>

查看 sleep 和 productpage 的抓包結果可以看到,sleep (192.168.58.148) 訪問 productpage Service 名稱 DNS 解析後的 service IP(10.100.171.143),經過 kubernetes Service 的轉發後,最終訪問到 productpage 的實際 Pod IP (192.168.13.108)。

此時 ambient mesh 還未接管 default Namespace 的流量,因此在 ztunnel 上不會抓到相關的數據包。

9 將 Default Namespace 加入 Ambient Mesh(L4 功能)

為 default Namespace 添加 istio.io/dataplane-mode=ambient 標籤,表示將該 Namespace 加入到 ambient mesh 中。

kubectl label namespace default istio.io/dataplane-mode=ambient

一旦 Namespace 加入 ambient mesh,istio-cni DaemonSet 就會為該 Namespace 中的 Pod 設置 iptables 重定向規則,將 Pod 的所有出入流量重定向到運行在相同節點的 ztunnel 上。

9.1 MTLS 流量加密

ztunnel 會為啟用了 ambient mesh 的 Namespace 中的工作負載創建一個安全覆蓋層,提供 mTLS,遙測,認證和 L4 授權等功能。

為了方便查看,可以先清除先前在 sleep 和 productpage 上抓到的報文。

然後使用 sleep 向 productpage 發起一次請求。

kubectl exec deploy/sleep -- curl -s http://productpage:9080/ | head -n1

在 sleep 和 productpage 上依然可以抓到明文的數據包,只是這回在 productpage 上抓到的數據包的源 IP 變為的 sleep 所在節點的 ztunnel 的 IP 地址。

在 sleep 節點所在的 ztunnel 上我們可以抓到 sleep 發過來的明文的數據包,ztunnel 會對數據包進行加密以後發送給 productpage 節點上的 ztunnel。productpage 節點上的 ztunnel 收到加密的數據包後,進行解密,然後發送給 productpage。

我們還可以在 sleep 和 productpage 所在節點的 ztunnel 的日誌中看到訪問記錄。查看 outbound 方向的流量日誌(sleep -> sleep node 上的 ztunnel)。

kubectl logs -n istio-system ztunnel-fnjg2 -f

我們可以看到 outbound 流量的日誌中有(no waypoint proxy)的字樣,ambient mesh 默認只進行 L4 處理,不會進行 L7 處理。因此此時流量只會通過 ztunnel ,不會經過 waypoint proxy。

查看 inbound 方向的流量日誌(productpage 上的 ztunnel -> productpage)。

kubectl logs -n istio-system ztunnel-k4jhb -f

9.2 L4 授權策略

安全覆蓋層可以實現簡單的 L4 授權策略,如下所示創建一個 AuthorizationPolicy,只允許 Service Account 是 sleep 的用户訪問標籤是 app=productpage 應用。

kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: productpage-viewer
 namespace: default
spec:
 selector:
   matchLabels:
     app: productpage
 action: ALLOW
 rules:
 - from:
   - source:
       principals: ["cluster.local/ns/default/sa/sleep"]
EOF

分別在 sleep 和 notsleep 上執行以下請求,由於當前還沒有啟用 L7 處理,因此還無法針對 HTTP 請求方法,路徑等條件進行限制。

# 成功
kubectl exec deploy/sleep -- curl -s http://productpage:9080/ | head -n1
# 成功
kubectl exec deploy/sleep -- curl -XDELETE -s http://productpage:9080/ | head -n1
# 失敗,只允許 sa 是 sleep 的用户
kubectl exec deploy/notsleep -- curl -s http://productpage:9080/ | head -n1

10 啟用 L7 功能

要為服務啟用 L7 網格能力,需要顯式創建一個 Gateway,注意創建的 Gateway 資源中的 gatewayClassName 必須設置為 istio-mesh,這樣 Istio 才會為 productpage 創建對應的 waypoint proxy。任何發往 productpage 服務的流量都將經過 waypoint proxy 這個 L7 代理進行處理。

kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: Gateway
metadata:
 name: productpage
 annotations:
   istio.io/service-account: bookinfo-productpage
spec:
 gatewayClassName: istio-mesh
EOF

查看 Istio 為 productpage 創建的 waypoint proxy。

從 sleep 訪問 productpage。

kubectl exec deploy/sleep -- curl -s http://productpage:9080/ | head -n1

查看 outbound 方向的流量日誌(sleep -> sleep node 上的 ztunnel -> waypoint proxy)。

kubectl logs -n istio-system ztunnel-fnjg2 -f

從下面的日誌中可以看到(to server waypoint proxy)的字樣,説明請求發往 waypoint proxy 進行處理。

查看 inbound 方向的流量日誌(productpage 上的 ztunnel -> productpage)。

kubectl logs -n istio-system ztunnel-k4jhb -f

10.1 L7 授權策略

接下來更新 AuthorizationPolicy 只允許 Service Account 是 sleep 的用户通過 GET 的方式訪問標籤是 app=productpage 應用。

kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: productpage-viewer
 namespace: default
spec:
 selector:
   matchLabels:
     app: productpage
 action: ALLOW
 rules:
 - from:
   - source:
       principals: ["cluster.local/ns/default/sa/sleep"]
   to:
   - operation:
       methods: ["GET"]
EOF

分別在 sleep 和 notsleep 上執行以下請求,這次在 sleep 上執行 HTTP DELETE 請求也會被拒絕了。

# 成功
kubectl exec deploy/sleep -- curl -s http://productpage:9080/ | head -n1
# 失敗,RBAC 錯誤,因為不是 GET 請求
kubectl exec deploy/sleep -- curl -X DELETE -s http://productpage:9080/ | head -n1
# 失敗,RBAC 錯誤,只允許 sa 是 sleep 的用户
kubectl exec deploy/notsleep -- curl -s http://productpage:9080/  | head -n1

10.2 可觀測性

在 productpage waypoint proxy 上可以查看所有對 productpage 服務請求的 L7 指標。

kubectl exec deploy/bookinfo-productpage-waypoint-proxy -- curl -s http://localhost:15020/stats/prometheus | grep istio_requests_total

10.3 流量控制

首先為 reviews 服務創建一個 gateway,啟用 L7 能力。

kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: Gateway
metadata:
 name: reviews
 annotations:
   istio.io/service-account: bookinfo-reviews
spec:
 gatewayClassName: istio-mesh
EOF

然後分別創建 VirtualService 和 DestinationRule 來控制流量以 90/10 的比例發往 v1 版本和 v2 版本的 reviews 服務。

kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 90
    - destination:
        host: reviews
        subset: v2
      weight: 10
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3
EOF

執行以下命令,從 sleep 往 productpage 發送 10 個請求,可以看到大約有 10% 的流量流向了 reviews-v2。

# 注意訪問路徑是 http://productpage:9080/productpage,會調用 reviews 服務
kubectl exec -it deploy/sleep -- sh -c 'for i in $(seq 1 10); do curl -s http://productpage:9080/productpage | grep reviews-v.-; done'

# 返回結果
 <u>reviews-v1-7598cc9867-dh7hp</u>
 <u>reviews-v1-7598cc9867-dh7hp</u>
 <u>reviews-v1-7598cc9867-dh7hp</u>
 <u>reviews-v1-7598cc9867-dh7hp</u>
 <u>reviews-v2-6bdd859457-7lxhc</u>
 <u>reviews-v1-7598cc9867-dh7hp</u>
 <u>reviews-v1-7598cc9867-dh7hp</u>
 <u>reviews-v1-7598cc9867-dh7hp</u>
 <u>reviews-v1-7598cc9867-dh7hp</u>
 <u>reviews-v2-6bdd859457-7lxhc</u>

10.4 故障注入

為 productpage 服務創建一個 VirtualService,在請求中注入 5s 的延時。

kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: productpage
spec:
  hosts:
    - productpage
  http:
  - route:
    - destination:
        host: productpage
    fault:
      delay:
         percentage:
            value: 100.0
         fixedDelay: 5s
EOF

從 sleep 訪問 productpage,可以看到請求消耗的時間大約在 5s 左右。

> kubectl exec deploy/sleep -- time curl -s http://productpage:9080 | head -n 1

# 返回結果
<!DOCTYPE html>
real	0m 5.04s
user	0m 0.00s
sys	    0m 0.00s

11 清理環境

# 卸載 Istio
istioctl uninstall -y --purge && istioctl delete ns istio-system
# 刪除示例應用
kubectl delete -f samples/bookinfo/platform/kube/bookinfo.yaml
kubectl delete -f https://raw.githubusercontent.com/linsun/sample-apps/main/sleep/sleep.yaml
kubectl delete -f https://raw.githubusercontent.com/linsun/sample-apps/main/sleep/notsleep.yaml
# 刪除集羣
eksctl delete cluster aws-demo-cluster01

12 體驗 Demo

想要快速體驗 ambient mesh 的朋友也可以在 solo.io 官網[8] 上嘗試上手教程。

13 參考資料