Istio 1.10 讓 StatefulSets 更容易使用

了解如何使用 Istio 1.10 輕鬆部署 StatefulSets。

2021 年 5 月 19 日 | 作者:Lin Sun - Solo.io、Christian Posta - Solo.io、John Howard - Google、Zhonghu Xu - Huawei

Kubernetes 的 StatefulSets 通常用於管理有狀態的應用程式。除了管理一組 Pods 的部署和擴展之外,StatefulSets 還保證這些 Pods 的順序和唯一性。常用於 StatefulSets 的應用程式包括 ZooKeeper、Cassandra、Elasticsearch、Redis 和 NiFi。

Istio 社群一直在逐步進展,朝向對 StatefulSets 的零配置支援;從自動 mTLS,到消除創建 DestinationRuleServiceEntry 資源的需求,到最近在 Istio 1.10 中 pod 網路變更

在服務網格中使用 StatefulSet 有什麼獨特之處?StatefulSet pod 是從相同的規格建立的,但它們不是可互換的:每個 pod 都有一個持久的識別符號,在任何重新排程中都會保持不變。在 StatefulSet 中執行的應用程式通常是那些需要在它們的 pod 之間進行通訊的應用程式,而且由於它們來自硬編碼 IP 位址的世界,因此可能只監聽 pod IP,而不是 0.0.0.0

例如,ZooKeeper 的預設配置是不監聽所有 IP 以進行仲裁通訊

quorumListenOnAllIPs=false

在過去的幾個版本中,Istio 社群已回報了許多問題,與在 StatefulSets 中執行應用程式的支援相關。

在 Istio 1.10 之前,StatefulSets 的運作情況

在執行 Kubernetes 1.19 的 GKE 叢集中,我們安裝了 Istio 1.9.5。我們在 default 命名空間中啟用自動 sidecar 注入,然後使用 Bitnami 提供的 Helm 圖表,以及用於互動式偵錯的 Istio sleep pod 安裝了 ZooKeeper

$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm install my-release bitnami/zookeeper --set replicaCount=3
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/sleep/sleep.yaml

幾分鐘後,所有 pod 都會順利啟動 sidecar 代理

$ kubectl get pods,svc
NAME                             READY   STATUS    RESTARTS   AGE
my-release-zookeeper-0           2/2     Running   0          3h4m
my-release-zookeeper-1           2/2     Running   0          3h4m
my-release-zookeeper-2           2/2     Running   0          3h5m
pod/sleep-8f795f47d-qkgh4        2/2     Running   0          3h8m

NAME                            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                            AGE
my-release-zookeeper            ClusterIP   10.100.1.113   <none>        2181/TCP,2888/TCP,3888/TCP         3h
my-release-zookeeper-headless   ClusterIP   None           <none>        2181/TCP,2888/TCP,3888/TCP         3h
service/sleep                   ClusterIP   10.100.9.26    <none>        80/TCP                             3h

我們的 ZooKeeper 服務是否正常運作且狀態為 Running?讓我們來看看!ZooKeeper 監聽 3 個連接埠

預設情況下,ZooKeeper 安裝會將連接埠 2181 配置為監聽 0.0.0.0,但連接埠 2888 和 3888 僅監聽 pod IP。讓我們從其中一個 ZooKeeper pod 檢查這些連接埠的網路狀態

$ kubectl exec my-release-zookeeper-1 -c istio-proxy -- netstat -na | grep -E '(2181|2888|3888)'
tcp        0      0 0.0.0.0:2181            0.0.0.0:*               LISTEN
tcp        0      0 10.96.7.7:3888          0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:2181          127.0.0.1:37412         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37486         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37456         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37498         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37384         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37514         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37402         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37434         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37526         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37374         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37442         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37464         TIME_WAIT

在連接埠 2888 或 3888 上沒有任何 ESTABLISHED 連線。接下來,讓我們取得 ZooKeeper 伺服器狀態

$ kubectl exec my-release-zookeeper-1 -c zookeeper -- /opt/bitnami/zookeeper/bin/zkServer.sh status
/opt/bitnami/java/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/bitnami/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Error contacting service. It is probably not running.

從上述輸出中,您可以看到 ZooKeeper 服務無法正常運作。讓我們檢查其中一個 ZooKeeper pod 的叢集配置

$ istioctl proxy-config cluster my-release-zookeeper-1 --port 3888 --direction inbound -o json
[
    {
        "name": "inbound|3888||",
        "type": "STATIC",
        "connectTimeout": "10s",
        "loadAssignment": {
            "clusterName": "inbound|3888||",
            "endpoints": [
                {
                    "lbEndpoints": [
                        {
                            "endpoint": {
                                "address": {
                                    "socketAddress": {
                                        "address": "127.0.0.1",
                                        "portValue": 3888
                                    }
                                }
                            }
                        }
                    ]
                }
            ]
        },
...

這裡有趣的是,連接埠 3888 的入站端點為 127.0.0.1。這是因為 Envoy 代理,在 Istio 1.10 之前的版本中,會將入站流量重新導向至 loopback 介面,如我們關於此變更的部落格文章中所述。

使用 Istio 1.10 的 StatefulSets 的運作情況

現在,我們已將叢集升級至 Istio 1.10,並將 default 命名空間配置為啟用 1.10 sidecar 注入。讓我們滾動重新啟動 ZooKeeper StatefulSet,以更新 pod 以使用新版本的 sidecar 代理

$ kubectl rollout restart statefulset my-release-zookeeper

一旦 ZooKeeper pod 達到執行狀態,讓我們從任何一個 ZooKeeper pod 檢查這 3 個連接埠的網路連線

$ kubectl exec my-release-zookeeper-1 -c istio-proxy -- netstat -na | grep -E '(2181|2888|3888)'
tcp        0      0 0.0.0.0:2181            0.0.0.0:*               LISTEN
tcp        0      0 10.96.8.10:2888         0.0.0.0:*               LISTEN
tcp        0      0 10.96.8.10:3888         0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.6:42571         10.96.8.10:2888         ESTABLISHED
tcp        0      0 10.96.8.10:2888         127.0.0.6:42571         ESTABLISHED
tcp        0      0 127.0.0.6:42655         10.96.8.10:2888         ESTABLISHED
tcp        0      0 10.96.8.10:2888         127.0.0.6:42655         ESTABLISHED
tcp        0      0 10.96.8.10:37876        10.96.6.11:3888         ESTABLISHED
tcp        0      0 10.96.8.10:44872        10.96.7.10:3888         ESTABLISHED
tcp        0      0 10.96.8.10:37878        10.96.6.11:3888         ESTABLISHED
tcp        0      0 10.96.8.10:44870        10.96.7.10:3888         ESTABLISHED
tcp        0      0 127.0.0.1:2181          127.0.0.1:54508         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54616         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54664         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54526         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54532         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54578         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54634         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54588         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54610         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54550         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54560         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54644         TIME_WAIT

連接埠 2888 和 3888 上都有 ESTABLISHED 連線!接下來,讓我們檢查 ZooKeeper 伺服器狀態

$ kubectl exec my-release-zookeeper-1 -c zookeeper -- /opt/bitnami/zookeeper/bin/zkServer.sh status
/opt/bitnami/java/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/bitnami/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: follower

ZooKeeper 服務現在正在執行!

我們可以從 sleep pod 連線至每個 ZooKeeper pod,並執行以下命令來探索 StatefulSet 中每個 pod 的伺服器狀態。請注意,無需為任何 ZooKeeper pod 建立 ServiceEntry 資源,我們可以從 sleep pod 使用其 DNS 名稱 (例如 my-release-zookeeper-0.my-release-zookeeper-headless) 直接呼叫這些 pod。

$ kubectl exec -it deploy/sleep -c sleep -- sh  -c 'for x in my-release-zookeeper-0.my-release-zookeeper-headless my-release-zookeeper-1.my-release-zookeeper-headless my-release-zookeeper-2.my-release-zookeeper-headless; do echo $x; echo srvr|nc $x 2181; echo; done'
my-release-zookeeper-0.my-release-zookeeper-headless
Zookeeper version: 3.7.0-e3704b390a6697bfdf4b0bef79e3da7a4f6bac4b, built on 2021-03-17 09:46 UTC
Latency min/avg/max: 1/7.5/20
Received: 3845
Sent: 3844
Connections: 1
Outstanding: 0
Zxid: 0x200000002
Mode: follower
Node count: 6

my-release-zookeeper-1.my-release-zookeeper-headless
Zookeeper version: 3.7.0-e3704b390a6697bfdf4b0bef79e3da7a4f6bac4b, built on 2021-03-17 09:46 UTC
Latency min/avg/max: 0/0.0/0
Received: 3856
Sent: 3855
Connections: 1
Outstanding: 0
Zxid: 0x200000002
Mode: follower
Node count: 6

my-release-zookeeper-2.my-release-zookeeper-headless
Zookeeper version: 3.7.0-e3704b390a6697bfdf4b0bef79e3da7a4f6bac4b, built on 2021-03-17 09:46 UTC
Latency min/avg/max: 0/0.0/0
Received: 3855
Sent: 3854
Connections: 1
Outstanding: 0
Zxid: 0x200000002
Mode: leader
Node count: 6
Proposal sizes last/min/max: 48/48/48

現在我們的 ZooKeeper 服務正在執行,讓我們使用 Istio 來保護與常規和無頭服務的所有通訊。將相互 TLS 套用至 default 命名空間

$ kubectl apply -n default -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
  name: "default"
spec:
  mtls:
    mode: STRICT
EOF

繼續從 sleep pod 發送一些流量,並啟動 Kiali 儀表板以視覺化 default 命名空間中的服務

Visualize the ZooKeeper Services in Kiali
在 Kiali 中視覺化 ZooKeeper 服務

流量流程上的鎖定圖示表示連線是安全的。

總結

透過 Istio 1.10 中的新網路變更,具有 sidecar 的 Kubernetes pod 與沒有 sidecar 的 pod 具有相同的網路行為。此變更使有狀態應用程式可以在 Istio 中正常運作,如我們在本篇文章中向您展示的一樣。我們相信這是朝向 Istio 提供透明服務網格和零配置 Istio 目標邁出的一大步。

分享這篇文章