1. 程式人生 > >StatefulSet: Kubernetes 中對有狀態應用的執行和伸縮_Kubernetes中文社群

StatefulSet: Kubernetes 中對有狀態應用的執行和伸縮_Kubernetes中文社群

在最新發布的 Kubernetes 1.5 我們將過去的 PetSet 功能升級到了 Beta 版本,並重新命名為StatefulSet。除了依照社群民意改了名字之外,這一 API 物件並沒有太大變化,不過我們在向集合裡部署 Pod 的過程中加入了“每索引最多一個”的語義。有了順序部署、順序終結、唯一網路名稱以及持久穩定的儲存,我們認為,對於大量的有狀態容器化負載,我們已經具備了一定的支援能力。我們並不是宣稱這一功能已經完全完成,但是我們相信他已經處於一個可用狀態,並且我們會在推動其正式釋出的過程中保持其相容性。

StatefulSet 的採用時機

在 Kubernetes 中,Deployment 和 ReplicaSets 都是執行無狀態應用的有效手段。但這兩種方式對於有狀態應用來說就不太合適了。StatefulSet 的目的就是給為數眾多的有狀態負載提供正確的控制器支援。然而需要注意的是,不一定所有的有儲存應用都是適合移植到 Kubernetes 上的,在移植儲存層和編排框架之前,需要回答以下幾個問題。

應用是否可以使用遠端儲存?

目前,我們推薦用遠端儲存來使用 StatefulSets,就要對因為網路造成的儲存效能損失有一個準備:即使是專門優化的例項,也無法同本地載入的 SSD 相提並論。你的雲中的網路儲存,能夠滿足 SLA 要求麼?如果答案是肯定的,那麼利用 StatefulSet 執行這些應用,就能夠獲得自動化的優勢。如果應用所在的 Node 發生故障,包含應用的 Pod 會排程到其他 Node 上,在這之後會重新載入他的網路儲存以及其中的資料。

這些應用是否有伸縮需求?

用 StatefulSet 執行應用會帶來什麼好處呢?你的整個組織是否只需要一個應用例項?對該應用的伸縮是否會引起問題?如果你只需要較少的應用例項數量,這些例項能夠滿足組織現有的需要,而且可以預見的是,應用的負載不會很快增長,那麼你的本地應用可能無需移植。

然而,如果你的系統是微服務所構成的生態系統,就會比較頻繁的交付新服務,如果更近一步,服務是有狀態的,那麼 Kubernetes 的自動化和健壯性特性會對你的系統有很大幫助。如果你已經在使用 Kubernetes 來管理你的無狀態服務,你可能會想要在同一個體系中管理你的有狀態應用。

預期效能增長的重要性?

Kubernetes 還不支援網路或儲存在 Pod 之間的隔離。如果你的應用不巧和嘈雜的鄰居共享同一個節點,會導致你的 QPS 下降。解決方式是把 Pod 排程為該 Node 的唯一租戶(獨佔伺服器),或者使用互斥規則來隔離會爭用網路和磁碟的 Pod,但是這就意味著使用者必須鑑別和處置(競爭)熱點。

如果榨乾有狀態應用的最大 QPS 不是你的首要目標,而且你願意也有能力處理競爭問題,似的有狀態應用能夠達到 SLA 需要,又如果對服務的移植、伸縮和重新排程是你的主要需求,Kubernetes 和 StatefulSet 可能就是解決問題的好方案了。

你的應用是否需要特定的硬體或者例項型別

如果你的有狀態應用在高階硬體或高規格例項上執行,而其他應用在通用硬體或者低規格例項上執行,你可能不想部署一個異構的叢集。如果可以把所有應用都部署到統一例項規格的例項上,那麼你就能夠從 Kubernetes 獲得動態資源排程和健壯性的好處。

實踐環節 – ZooKeeper

有兩個原因讓 [ZooKeeper] 成為 StatefulSet 的好例子。首先,StatefulSet 在其中演示了執行分散式、強一致性儲存的應用的能力;其次,ZooKeeper 也是 Apache Hadoop 和 Apache Kafka 在 Kubernetes 上執行的前置條件。在 Kubernetes 文件中有一個 深度教程 說明了在 Kubernetes 叢集上部署 ZooKeeper Ensemble 的過程,這裡會簡要描述一下其中的關鍵特性。

建立 ZooKeeper 的 Ensemble

建立 Ensemble 很容易,只要用 kubectl create 來根據定義來建立物件就可以了。

$ kubectl create -f https://raw.githubusercontent.com/kubernetes/kubernetes.github.io/master/docs/tutorials/stateful-application/zookeeper.yaml
service "zk-headless" created
configmap "zk-config" created
poddisruptionbudget "zk-budget" created
statefulset "zk" created

接下來 StatefulSet 控制器開始順序建立各個 Pod,在建立後續 Pod 之前,首先要等前面的 Pod 執行成功並進入到就緒狀態。

$ kubectl get -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         7s
zk-0      0/1       ContainerCreating   0         7s
zk-0      0/1       Running   0         38s
zk-0      1/1       Running   0         58s
zk-1      0/1       Pending   0         1s
zk-1      0/1       Pending   0         1s
zk-1      0/1       ContainerCreating   0         1s
zk-1      0/1       Running   0         33s
zk-1      1/1       Running   0         51s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         25s
zk-2      1/1       Running   0         40s

檢查一下 StatefulSet 中每個 Pod 的主機名稱,你會發現 Pod 的主機名也包含了 Pod 的順序:

$ for i in 0 1 2; do kubectl exec zk-$i -- hostname; done
zk-0
zk-1
zk-2

ZooKeeper 在一個名為 “myid” 的檔案中儲存了每個伺服器的唯一識別符號。這個識別符號只是自然數。在 Ensemble 的伺服器中,”myid” 檔案中儲存的數字就是 Pod 主機名中的順序號加一。

$ for i in 0 1 2; do echo "myid zk-$i";kubectl exec zk-$i -- cat /var/lib/zookeeper/data/myid; done
myid zk-0
1
myid zk-1
2
myid zk-2
3

基於主機名,每個 Pod 都有獨立的網路地址,這個網域由 zk-headless 這一 Headless 服務所控制。

$  for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done
zk-0.zk-headless.default.svc.cluster.local
zk-1.zk-headless.default.svc.cluster.local
zk-2.zk-headless.default.svc.cluster.local

Pod 具有了唯一的序號和網路地址,就可以用來在 ZooKeeper 的配置檔案中設定 Ensemble 成員了。

kubectl exec zk-0 -- cat /opt/zookeeper/conf/zoo.cfg
clientPort=2181
dataDir=/var/lib/zookeeper/data
dataLogDir=/var/lib/zookeeper/log
tickTime=2000
initLimit=10
syncLimit=2000
maxClientCnxns=60
minSessionTimeout= 4000
maxSessionTimeout= 40000
autopurge.snapRetainCount=3
autopurge.purgeInteval=1
server.1=zk-0.zk-headless.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-headless.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-headless.default.svc.cluster.local:2888:3888

StatefulSet 讓使用者可以用穩定、可重複的方式來部署 ZooKeeper。不會建立具有重複 ID 的伺服器,伺服器之間可以通過穩定的網路地址互相通訊,因為 Ensemble 具有穩定的成員關係,因此 Leader 選拔和寫入複製能力也得到了保障。

檢查 Ensemble 工作狀況的最簡單方式就是向一臺伺服器寫入一個值,然後從另一臺伺服器中讀取。可以利用 ZooKeeper 自帶的 “zkCli.sh” 指令碼來建立包含資料的 ZNode。

$  kubectl exec zk-0 zkCli.sh create /hello world
...

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
Created /hello

使用同一指令碼,可以從 Ensemble 另外一臺伺服器中讀取資料。

$  kubectl exec zk-1 zkCli.sh get /hello 
...

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
world
...

可以用刪除 zk StatefulSet 的方式停掉 Ensemble。

$  kubectl delete statefulset zk
statefulset "zk" deleted

級聯刪除會銷燬 StatefulSet 中的每個 Pod,並且按照建立順序的反序來執行,只有在成功終結後面一個之後,才會繼續下一個刪除操作。

$  kubectl get pods -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          14m
zk-1      1/1       Running   0          13m
zk-2      1/1       Running   0          12m
NAME      READY     STATUS        RESTARTS   AGE
zk-2      1/1       Terminating   0          12m
zk-1      1/1       Terminating   0         13m
zk-0      1/1       Terminating   0         14m
zk-2      0/1       Terminating   0         13m
zk-2      0/1       Terminating   0         13m
zk-2      0/1       Terminating   0         13m
zk-1      0/1       Terminating   0         14m
zk-1      0/1       Terminating   0         14m
zk-1      0/1       Terminating   0         14m
zk-0      0/1       Terminating   0         15m
zk-0      0/1       Terminating   0         15m
zk-0      0/1       Terminating   0         15m

可以使用 kubectl apply 命令來重建 zk StatefulSet,並重新部署 Ensemble。

$  kubectl apply -f http://k8s.io/docs/tutorials/stateful-application/zookeeper.yaml
service "zk-headless" configured
configmap "zk-config" configured
statefulset "zk" created

如果使用 “zkCli.sh” 指令碼來嘗試獲取刪除 StatefulSet 之前寫入的資料,會發現資料依然存在。

$  kubectl exec zk-2 zkCli.sh get /hello 
...

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
world
...

及時所有的 Pod 都被銷燬,他們一旦被重新排程,StatefulSet 也能保證 Ensemble 能夠選拔新的 Leader 並繼續提供服務。

Node 故障的容錯

ZooKeeper 會在 Ensmble 的伺服器中複製他的狀態機,用於應對 Node 故障。預設情況下 Kubernetes 排程器可以在同一個 Node 上部署屬於 zk StatefulSet 的多個 Pod,假設 zk-0 和 zk-1 兩個 Pod 被部署在同一個 Node 上,如果這一 Node 出現故障,ZooKeepers Ensemble 會因為數量不足造成無法提交寫入,ZooKeeper 會出現服務中斷,直到 Pod 被重新排程。

在叢集中,建議為關鍵程序預留更多資源,這樣就能保證故障情況發生的時候能夠迅速重新排程 Pod,縮短故障時間。

如果這樣無法 SLA 規定的停機時間,那麼就應該使用 PodAntiAffinity( Pod 互斥性)註解。用來建立 Ensemble 的定義檔案中就包含了這樣的註解,他會要求 Kubernetes 排程器不要把 zk StatefulSet 中的多個 Pod 部署在同一 Node 上。

計劃內維護的容錯

用於建立 ZooKeeper Ensemble 的描述檔案還建立了一個 PodDistruptionBudget( Pod 中斷預算 )物件:zk-budget。zk-budget 用於指示 Kubernetes, 這一服務能夠容忍的中斷 Pod (不健康 Pod)的上限。

{
  "podAntiAffinity": {
    "requiredDuringSchedulingRequiredDuringExecution": [
      {
        "labelSelector": {
          "matchExpressions": [
            {
              "key": "app",
              "operator": "In",
              "values": [
                "zk-headless"
              ]
            }
          ]
        },
        "topologyKey": "kubernetes.io/hostname"
      }
    ]
  }
}
$ kubectl get poddisruptionbudget zk-budget
NAME        MIN-AVAILABLE   ALLOWED-DISRUPTIONS   AGE
zk-budget   2               1                     2h

zk-budget 定義,至少要有兩個處於可用狀態的成員才能保障 Ensemble 的健康。如果在離線之前對 Node 進行 Drain 操作,如果這一操作過程中終止的 Pod 會違反預算,Drain 操作就會失敗。如果使用 kubectl drain,來對 Node 進行 cordon 操作並驅逐所有其中執行的 Node,PodDistruption 讓你可以確認這一操作不會中斷有狀態應用的服務。

更進一步

因為 Kubernetes 的開發工作目標是可用,我們希望獲得更多來自使用者的設想。如果你想要幫我們處理問題,可以看看 GitHub 上關於 statful 的 Issues。然而為了 API 的易於理解,我們並不準備實現所有的功能請求。我們會優先實現一些能夠對所有有狀態應用產生改善的功能,例如滾動更新支援、Node 升級的整合、使用高速的本地儲存等。StatefulSet 的目的在於支援儘可能多而不是全部的有狀態應用。基於這些考慮,我們會避免依賴隱藏特性或者技術的方式來充實 StatefulSet。每個人都可以開發一個想 StatefulSets 的控制器。我們稱之為 “making it forkable”。明年,我們希望更多的流行的有狀態應用能夠有自己的社群支援的獨立控制器或 “操作器”。我們已經聽說了 etcd、Redis 和 ZooKeeper 的自定義控制器的開發工作。我們期待更多類似案例不斷湧現。

ETCD 和 Prometheus 的 Operator 來自 CoreOS,演示了一種超出 StatefulSet 能力的自動化和整合能力。另一方面,使用 Deployment 和 StatefulSet 這樣的通用控制器能夠用同一種物件管理大量有狀態應用。Kubernetes 使用者有了執行有狀態應用的能力,而且可以自行在兩種方式之中進行選擇。

原作者: Kenneth Owens & Eric Tune

原文: http://blog.kubernetes.io/2016/12/statefulset-run-scale-stateful-applications-in-kubernetes.html

譯文:http://blog.fleeto.us/translation/statefulset-run-and-scale-stateful-applications-easily-kubernetes