K8s 应用管理之道 - 有状态服务
背景
用戶通過 Deployment、ReplicationController 可以方便地在 kubernetes 中部署一套高可用、可擴展的分布式無狀態(tài)服務。這類應用不在本地存儲數(shù)據(jù),通過簡單的負載均衡策略可實現(xiàn)請求分發(fā)。隨著 k8s 的普及和云原生架構的興起,越來越多的人希望把數(shù)據(jù)庫這類有狀態(tài)服務也通過 k8s 進行編排。但因為有狀態(tài)服務的復雜性,這一過程并不容易。本文將以最流行的開源數(shù)據(jù)庫 MySQL 為例,介紹如何在 k8s 上部署運維有狀態(tài)服務。本文所作的調(diào)研基于k8s 1.13。
使用 StatefulSet 部署 MySQL
本章將以 k8s 官方教程 Run a Replicated Stateful Application 中提供的樣例為基礎,介紹如何基于 StatefulSet 部署高可用 MySQL 服務。
StatefulSet 簡介
Deployment、ReplicationController 是為無狀態(tài)服務而設計的,它們中 pod 的名稱、主機名、存儲都是不穩(wěn)定的,且 pod 的啟動、銷毀順序隨機,并不適合數(shù)據(jù)庫這樣的有狀態(tài)應用。為此,k8s 推出了面向有狀態(tài)服務的工作負載 StatefulSet。其管理的 pod 具有如下特點:
服務部署
這里介紹的高可用 MySQL 服務由一個 master 節(jié)點和多個從 master 上異步復制數(shù)據(jù)的 slave 節(jié)點組成,即一主多從復制模型。其中,master 節(jié)點可用來處理用戶的讀寫請求,slave 節(jié)點只能用來處理用戶的讀請求。
為了部署這樣的服務,除了 StatefulSet 之外,還需要使用許多其它類型的 k8s 資源對象,包括 ConfigMap、Headless Service、ClusterIP Service 等。正是它們間的相互配合,才能讓 MySQL 這樣的有狀態(tài)服務有條件運行在 k8s 之上。
ConfigMap
為了便于維護應用配置,大型系統(tǒng)和分布式應用常常采用集中式的配置管理策略。在 k8s 環(huán)境下,用戶可以通過 ConfigMap 將配置和 pod 分離,這有助于保持工作負載的可移植性,使其配置更易于更改和管理。
樣例包含一個名為mysql的 ConfigMap,當 StatefulSet 中的 pod 啟動時,會根據(jù)自己的角色從 ConfigMap 中讀取合適的配置。
Headless Service
Headless Service 會為關聯(lián)的每一個 pod 提供對應的 DNS 地址,格式為<pod-name>.<service-name>。這樣,客戶端就可以自由地選擇想要訪問的應用實例,同時也能夠解決分布式環(huán)境下不同實例之間身份識別的問題。
樣例包含一個名為mysql的 Headless Service,該 service 與 StatefulSet 中的 pod 相關聯(lián),這些 pod 將被分配如下 DNS 地址mysql-0.mysql、mysql-1.mysql、mysql-2.mysql。這樣,客戶端就可以通過mysql-0.mysql訪問 master 節(jié)點,通過mysql-1.mysql或mysql-2.mysql訪問 slave 節(jié)點。
ClusterIP Service
為了方便只讀場景下的訪問,樣例提供了一個名為mysql-read的普通 service。該 service 擁有自己的 cluster IP,并會將請求分發(fā)至關聯(lián)的 pod(包括 master 和 slave),為用戶屏蔽 pod 的訪問細節(jié)。
StatefulSet
StatefulSet 是服務部署的關鍵,它管理的每個 pod 會被分配一個唯一的名稱,格式為<statefulset-name>-<ordinal-index>。樣例中的 StatefulSet 名為mysql,因此這些 pod 分別被命名為mysql-0,mysql-1和mysql-2。默認情況下,它們會按順序創(chuàng)建,并按逆序銷毀。
如下圖所示,一個 pod 包含 2 個 init container 和 2 個 app container,并且通過唯一的 PersistentVolumeClaim 和存儲卷供應方提供的 PersistentVolume 綁定。
和 Pod 相關的各個組件的功能如下:
- 容器init-mysql的主要功能是生成配置文件。它會從 hostname 中提取 pod 序號,并將該序號存入文件/mnt/conf.d/server-id.cnf中。另外,它還會根據(jù)節(jié)點類型將 master.cnf 或 slave.cnf 從 ConfigMap 中拷貝到/mnt/conf目錄下。
- 容器clone-mysql的主要功能是克隆數(shù)據(jù)。Pod 編號為N+1的clone-mysql會將數(shù)據(jù)從編號為N的 pod 中克隆至綁定的 PersistentVolume 里。
- Init container 運行完成后,app container 開始運行。容器mysql負責運行著真正的 mysqld 服務。
- 容器xtrabackup以 sidecar 模式運行。當它發(fā)現(xiàn)容器mysql中的 mysqld 就緒后,會通過命令START SLAVE啟動 slave 節(jié)點的數(shù)據(jù)復制流程。另外,它還會監(jiān)聽來自其它 pod 的數(shù)據(jù)克隆請求。
- StatefulSet 通過 volumeClaimTemplates 為每一個 pod 關聯(lián)了一個獨有的 PVC,樣例中編號為N的 pod 關聯(lián)了名為data-mysql-N的 PVC,而這個 PVC 又會和存儲系統(tǒng)提供的 PV 綁定。正是這種機制,保證了 pod 被重新調(diào)度后仍然能掛載原有的數(shù)據(jù)。
服務運維
為了保證服務性能、提升系統(tǒng)可靠性,部署工作完成后還需要相應的運維支撐。對于數(shù)據(jù)庫服務,常見的運維工作包括服務故障恢復、服務擴容縮容、服務狀態(tài)監(jiān)控、數(shù)據(jù)備份恢復等。
服務故障恢復
服務在遇到故障時能否自愈,是判斷一個系統(tǒng)自動化程度的關鍵指標。在當前架構下,MySQL 服務在遇到宿主機宕機,master 或 slave 節(jié)點崩潰等問題時能自動恢復。在上述問題發(fā)生后,k8s 會重新調(diào)度遇到問題的 pod,讓其重新運行。由于使用了 StatefulSet,這些 pod 的名稱、主機名和存儲會與原來保持一致。
服務擴容縮容
在 MySQL 一主多從復制模型下,擴容縮容意味著調(diào)整 slave 節(jié)點個數(shù)。得益于 StatefulSet 對 pod 啟動、銷毀順序的保證,通過如下命令就可以輕松實現(xiàn)服務的擴容縮容。
kubectl scale statefulset mysql --replicas=<NumOfReplicas>服務狀態(tài)監(jiān)控
要保證服務的穩(wěn)定性離不開對服務運行狀態(tài)的監(jiān)控。除了通過就緒探針和活性探針檢測服務是否正常外,往往還需要更細粒度的監(jiān)控指標。用戶可以借助 mysqld-exporter 將 MySQL 的核心指標暴露給 prometheus,然后基于 prometheus 做監(jiān)控告警。對于 mysqld-exporter,推薦以 sidecar 模式和 mysqld 容器部署在同一個 pod 中。
數(shù)據(jù)備份恢復
數(shù)據(jù)的備份和恢復是保障數(shù)據(jù)安全的有效手段。這里,用戶可以直接使用存儲卷暴露的接口或者利用 VolumeSnapshot 功能完成數(shù)據(jù)的備份恢復工作,下面分別進行介紹。
使用存儲卷接口
許多存儲卷供應方都提供了保存數(shù)據(jù)快照和基于快照恢復數(shù)據(jù)的功能,這些功能通常以接口的形式暴露給用戶。采樣這種方式要求用戶熟悉對應存儲卷供應方提供的操作接口。例如服務選用了阿里云云盤作為外部存儲卷,需要用戶了解云盤提供的快照接口。
使用 VolumeSnapshot
K8s v1.12 引入了 3 個快照相關的資源對象VolumeSnapshot、VolumeSnapshotContent、VolumeSnapshotClass,通過它們提供了快照操作的標準方法。這樣,用戶可以在不感知外部存儲卷的情況下,為存放 MySQL 數(shù)據(jù)的存儲卷創(chuàng)建快照,或者基于快照恢復數(shù)據(jù)。
相比直接使用底層存儲卷接口,使用 VolumeSnapshot 顯然是更為理想的方法。但目前 VolumeSnapshot 還處在 Alpha 階段,支持標準快照操作的外部存儲卷也有限,這些都限制了當下 VolumeSnapshot 的應用場景。想進一步了解 VolumeSnapshot 可參考文檔 Volume Snapshots。
使用 Operator 部署 MySQL
雖然用戶可以基于 StatefulSet 在 k8s 中部署運維一套高可用 MySQL 服務,但過程相對復雜。用戶既要熟悉各種 k8s 資源對象,又要學習很多 MySQL 的操作細節(jié),同時還需維護一套復雜的管理腳本。為了降低在 k8s 中部署復雜應用的門檻,誕生了 Kubernetes Operator。
Operator 簡介
Operator 是由 CoreOS 公司推出的,用來打包、部署和管理需要運行在 k8s 之上的復雜應用的一種方法。Operator 將運維人員對軟件操作的知識代碼化,同時綜合運用 k8s 各種資源對象來實現(xiàn)復雜應用的部署和運維。
Operator 通過 CustomResourceDefinition 為服務定義了新的資源對象,同時通過自定義控制器來保證應用處于預期狀態(tài)。
Operator 的工作流程可抽象成以下三個步驟:
Oracle MySQL Operator
對于 MySQL 服務,已經(jīng)有很多優(yōu)秀的開源 Operator 解決方案,包括 grtl/mysql-operator、oracle/mysql-operator、presslabs/mysql-operator、kubedb/mysql 等。本節(jié)將要介紹的 Oracle MySQL Operator 便是這些開源解決方案的典型代表。
Oracle MySQL Operator 工作原理
Oracle MySQL Operator 支持以下 2 種 MySQL 部署模式。
- Primary - 服務由一個可讀寫的主節(jié)點和多個只讀的從節(jié)點組成。
- Multi-Primary - 集群中各節(jié)點角色相同,沒有主從的概念,每個節(jié)點都可以處理用戶的讀寫請求。
下圖展示了 Multi-Primary 模式下 Operator 的工作原理。
下列流程是理解 Operator 工作原理的關鍵:
使用 k8s 的 CustomResourceDefinition(CRD) 定義若干和 MySQL 部署運維相關的資源對象。
- mysqlclusters - 用于描述集群的期望狀態(tài),包括部署模式、節(jié)點個數(shù)等。
- mysqlbackups - 用于描述按需備份策略,可以配置備份數(shù)據(jù)的存放地點,如 AWS S3。
- mysqlrestores - 用于描述數(shù)據(jù)恢復策略,需要配置備份數(shù)據(jù)和目標集群。
- mysqlbackupschedules - 用于描述定期備份策略,可以配置備份的時間間隔。
服務部署
得益于 Operator 對復雜部署細節(jié)的封裝,現(xiàn)在創(chuàng)建一個集群變得非常簡單。例如,通過如下配置就可以部署一個包含 3 個節(jié)點的 MySQL Multi-Primary 集群。
apiVersion: mysql.oracle.com/v1alpha1 kind: Cluster metadata:name: mysql-multimaster-cluster spec:multiMaster: truemembers: 3服務運維
在 Operator 模式下,同樣會涉及服務故障恢復、服務擴容縮容、服務狀態(tài)監(jiān)控、數(shù)據(jù)備份恢復等運維工作。
服務故障恢復
由于 StatefulSet 的存在,當某個 MySQL 服務實例崩潰時,k8s 會對其重新調(diào)度。另外,如果 StatefulSet 被誤刪,Operator 也會對其進行重建。
服務擴容縮容
通過更改資源對象 MySQLCluster 的字段spec.members可以輕松實現(xiàn)服務擴容縮容。這里只對用戶暴露了 MySQLCluster,屏蔽了底層 k8s 資源對象。
服務狀態(tài)監(jiān)控
可以在 k8s 中部署 Prometheus 監(jiān)控 Operator 和各個 MySQL 集群的狀態(tài)。具體步驟可參考文檔 Monitoring。
數(shù)據(jù)備份恢復
可以通過資源對象 MySQLBackup、MySQLRestore 對數(shù)據(jù)進行備份和恢復,為用戶屏蔽了不同存儲卷操作上的差別。另外,還可以通過 MySQLBackupSchedule 創(chuàng)建定時備份任務。
例如,以下配置可以每隔 30 分鐘對 MySQL 集群mysql-cluster的數(shù)據(jù)庫test進行備份。
[...] kind: BackupSchedule spec:schedule: '*/30 * * * *'backupTemplate:cluster:name: mysql-clusterexecutor:provider: mysqldumpdatabases:- test [...]總結
本文分別介紹了通過原生 k8s 資源對象 StatefulSet 以及通過 MySQL Operator 部署運維一套高可用 MySQL 服務的方法和原理。可以看到 Operator 屏蔽了復雜應用的編排細節(jié),大大降低了它們在 k8s 中的使用門檻。如果您有其它復雜應用需要部署,建議采用 Operator 方式。
參考資料
- 親歷者說:Kubernetes API 與 Operator,不為人知的開發(fā)者戰(zhàn)爭
- Running MySQL on Kubernetes using an operator
擴展閱讀
- K8s 應用管理之道 - 升級篇(一)
- K8s 應用管理之道 - 升級篇(二)
總結
以上是生活随笔為你收集整理的K8s 应用管理之道 - 有状态服务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 红米Note 13 Pro+设计曝光!边
- 下一篇: 舜宇光学科技发布盈利警告,预计上半年盈利