一般情況下 kubernetes 可以通過 replicaset 以一個 pod 模闆建立多個 pod 副本,但是它們都是無狀态的,任何時候它們都可以被一個全新的 pod 替換。然而有狀态的 pod 需要另外的方案確定當一個有狀态的 pod 挂掉後,這個 pod 執行個體需要在别的節點上重建,但是新的執行個體必須與被替換的執行個體擁有相同的名稱、網絡辨別和狀态。這就是 statefulset 管理 pod 的手段。
對于容器叢集,有狀态服務的挑戰在于,通常叢集中的任何節點都并非100%可靠的,服務所需的資源也會動态地更新改變。當節點由于故障或服務由于需要更多的資源而無法繼續運作在原有節點上時,叢集管理系統會為該服務重新配置設定一個新的運作位置,進而確定從整體上看,叢集對外的服務不會中斷。若采用本地存儲,當服務漂移後資料并不會随着服務轉移到新的節點,重新開機服務就會出現資料丢失的困境。
本文目的是通過一個 mysql 的主從叢集搭建,深入了解 kubernetes 的 statfulset 管理。為了降低實驗的外部依賴,存儲層面上,我采用的是本地存儲,當然生産上不建議這樣做,生産環境的存儲推薦官方介紹到的的 gce、nfs、ceph等存儲方案,因為這些方案支援動态供給的特性,允許開發人員通過 pvc 的定義,快速實作資料有效存儲,是以你絕不應該把一個主控端上的目錄當作 pv 使用, 隻是本文用于實驗需要,采用 <code>local persistent volume</code> 的手段,目的隻是為了驗證 statefulset 的狀态管理功能。
kubernetes master
kubernetes node(測試示範,所有的副本都會在其上運作)
kubernetes dns 服務已開啟
搭建一個主從複制(master-slave)的 mysql 叢集
從節點可以水準擴充
所有的寫操作隻能在主節點上執行
讀操作可以在主從節點上執行
從節點能同步主節點的資料
為了快速搭建測試環境,我們這裡使用了<code>本地存儲</code>,也就是說,使用者希望 kubernetes 能夠直接使用主控端上的本地磁盤目錄,而不依賴于遠端存儲服務,來提供<code>持久化</code>的容器 volume。不過這裡有個難點:
“ 我們把存儲固定在一個節點上,但是pod在排程的時候,是飄來飄去的,怎麼能讓pod通過pvc也能固定在pv上? ”
給這個 pod 加上一個 nodeaffinity 行不行?
當然行,但是這變相破壞了開發人員對資源對象的定義規範了,開發人員應該不需要時刻考慮排程的細節。排程的改動應該交給運維就行。是以我們為了實作本地存儲,我們采用了 <code>延遲綁定</code> 的方法。方法很簡單,我們都知道 <code>storageclass</code> 一般由運維人員設計,我們隻需要在storageclass 指定 <code>no-provisioner</code>。這是因為 <code>local persistent volume</code> 目前尚不支援 <code>dynamic provisioning</code>,是以它沒辦法在使用者建立 pvc 的時候,就自動建立出對應的 pv。與此同時,這個 storageclass 還定義了一個 <code>volumebindingmode=waitforfirstconsumer</code> 的屬性。它是 local persistent volume 裡一個非常重要的特性,即:<code>延遲綁定</code>
01-persistentvolume-1.yaml
01-persistentvolume-2.yaml
01-persistentvolume-3.yaml
記住,這是在生産上不推薦的做法,我隻是實驗用途才這樣手動預先建立,正規的做法應該通過storageclass采用 dynamic provisioning, 而不是 static provisioning 機制生産pv。
02-storageclass.yaml
執行建立
03-mysql-namespace.yaml
04-mysql-configmap.yaml
建立執行
05-mysql-secret.yaml
06-mysql-services.yaml
使用者所有寫請求,必須以 dns 記錄的方式直接通路到 master 節點,也就是 mysql-0.mysql 這條 dns 記錄。
使用者所有讀請求,必須通路自動配置設定的 dns 記錄可以被轉發到任意一個 master 或 slave 節點上,也就是 mysql-read 這條 dns 記錄
mysql-statefulset.yaml
整體的statefulset有兩個replicas,一個master, 一個slave,然後使用 init-mysql 這個 initcontainers 進行配置檔案的初始化。接着使用 clone-mysql 這個 <code>initcontainers</code> 進行資料的傳輸;同時使用 xtrabackup 這個 sidecar 容器進行sql初始化和資料傳輸功能
建立 statefulset
可以看到,statefulset 啟動成功後,會有兩個pod運作。
接下來,我們可以嘗試向這個mysql叢集fa起請求,執行一些<code>sql操作</code>來驗證它是否正常
服務驗證
驗證主從狀态
接下來,我們通過master容器建立資料庫和表、插入資料庫
然後,我們觀察slave節點是否都同步到資料了
當看到輸出結果,主從同步正常了
擴充從節點
在有了 statefulset 以後,你就可以像 deployment 那樣,非常友善地擴充這個 mysql 叢集,比如
這時候,一個新的mysql-2就建立出來了,我們繼續驗證新擴容的節點是否都同步到主節點的資料
當看到輸出結果,主從同步正常了。也就是說從 statefulset 為我們新建立的 mysql-2 上,同樣可以讀取到之前插入的記錄。也就是說,我們的資料備份和恢複,都是有效的。