背景

使用者透過 Deployment、ReplicationController 可以方便地在 kubernetes 中部署一套高可用、可擴充套件的分散式無狀態服務。這類應用不在本地儲存資料,透過簡單的負載均衡策略可實現請求分發。隨著 k8s 的普及和雲原生架構的興起,越來越多的人希望把資料庫這類有狀態服務也透過 k8s 進行編排。但因為有狀態服務的複雜性,這一過程並不容易。本文將以最流行的開源資料庫 MySQL 為例,介紹如何在 k8s 上部署運維有狀態服務。本文所作的調研基於

k8s 1。13

使用 StatefulSet 部署 MySQL

本章將以 k8s 官方教程 Run a Replicated Stateful Application 中提供的樣例為基礎,介紹如何基於 StatefulSet 部署高可用 MySQL 服務。

StatefulSet 簡介

Deployment、ReplicationController 是為無狀態服務而設計的,它們中 pod 的名稱、主機名、儲存都是不穩定的,且 pod 的啟動、銷燬順序隨機,並不適合資料庫這樣的有狀態應用。為此,k8s 推出了面向有狀態服務的工作負載 StatefulSet。其管理的 pod 具有如下特點:

唯一性 - 對於包含 N 個副本的 StatefulSet,每個 pod 會被分配一個 [0,N)範圍內的唯一序號。

順序性 - StatefulSet 中 pod 的啟動、更新、銷燬預設都是按順序進行的。

穩定的網路身份標識 - pod 的主機名、DNS 地址不會隨著 pod 被重新排程而發生變化。

穩定的持久化儲存 - 當 pod 被重新排程後,仍然能掛載原有的 PersistentVolume,保證了資料的完整性和一致性。

服務部署

這裡介紹的高可用 MySQL 服務由一個 master 節點和多個從 master 上非同步複製資料的 slave 節點組成,即一主多從複製模型。其中,master 節點可用來處理使用者的讀寫請求,slave 節點只能用來處理使用者的讀請求。

為了部署這樣的服務,除了 StatefulSet 之外,還需要使用許多其它型別的 k8s 資源物件,包括 ConfigMap、Headless Service、ClusterIP Service 等。正是它們間的相互配合,才能讓 MySQL 這樣的有狀態服務有條件執行在 k8s 之上。

K8s 應用管理之道 - 有狀態服務

ConfigMap

為了便於維護應用配置,大型系統和分散式應用常常採用集中式的配置管理策略。在 k8s 環境下,使用者可以透過 ConfigMap 將配置和 pod 分離,這有助於保持工作負載的可移植性,使其配置更易於更改和管理。

樣例包含一個名為

mysql

的 ConfigMap,當 StatefulSet 中的 pod 啟動時,會根據自己的角色從 ConfigMap 中讀取合適的配置。

Headless Service

Headless Service 會為關聯的每一個 pod 提供對應的 DNS 地址,格式為

。這樣,客戶端就可以自由地選擇想要訪問的應用例項,同時也能夠解決分散式環境下不同例項之間身份識別的問題。

樣例包含一個名為

mysql

的 Headless Service,該 service 與 StatefulSet 中的 pod 相關聯,這些 pod 將被分配如下 DNS 地址

mysql-0。mysql

mysql-1。mysql

mysql-2。mysql

。這樣,客戶端就可以透過

mysql-0。mysql

訪問 master 節點,透過

mysql-1。mysql

mysql-2。mysql

訪問 slave 節點。

ClusterIP Service

為了方便只讀場景下的訪問,樣例提供了一個名為

mysql-read

的普通 service。該 service 擁有自己的 cluster IP,並會將請求分發至關聯的 pod(包括 master 和 slave),為使用者遮蔽 pod 的訪問細節。

StatefulSet

StatefulSet 是服務部署的關鍵,它管理的每個 pod 會被分配一個唯一的名稱,格式為

-

。樣例中的 StatefulSet 名為

mysql

,因此這些 pod 分別被命名為

mysql-0

mysql-1

mysql-2

。預設情況下,它們會按順序建立,並按逆序銷燬。

如下圖所示,一個 pod 包含 2 個 init container 和 2 個 app container,並且透過唯一的 PersistentVolumeClaim 和儲存卷供應方提供的 PersistentVolume 繫結。

K8s 應用管理之道 - 有狀態服務

和 Pod 相關的各個元件的功能如下:

容器

init-mysql

的主要功能是生成配置檔案。它會從 hostname 中提取 pod 序號,並將該序號存入檔案

/mnt/conf。d/server-id。cnf

中。另外,它還會根據節點型別將 master。cnf 或 slave。cnf 從 ConfigMap 中複製到

/mnt/conf

目錄下。

容器

clone-mysql

的主要功能是克隆資料。Pod 編號為

N+1

clone-mysql

會將資料從編號為

N

的 pod 中克隆至繫結的 PersistentVolume 裡。

Init container 執行完成後,app container 開始執行。容器

mysql

負責執行著真正的 mysqld 服務。

容器

xtrabackup

以 sidecar 模式執行。當它發現容器

mysql

中的 mysqld 就緒後,會透過命令

START SLAVE

啟動 slave 節點的資料複製流程。另外,它還會監聽來自其它 pod 的資料克隆請求。

StatefulSet 透過 volumeClaimTemplates 為每一個 pod 關聯了一個獨有的 PVC,樣例中編號為

N

的 pod 關聯了名為

data-mysql-N

的 PVC,而這個 PVC 又會和儲存系統提供的 PV 繫結。正是這種機制,保證了 pod 被重新排程後仍然能掛載原有的資料。

服務運維

為了保證服務效能、提升系統可靠性,部署工作完成後還需要相應的運維支撐。對於資料庫服務,常見的運維工作包括服務故障恢復、服務擴容縮容、服務狀態監控、資料備份恢復等。

服務故障恢復

服務在遇到故障時能否自愈,是判斷一個系統自動化程度的關鍵指標。在當前架構下,MySQL 服務在遇到宿主機宕機,master 或 slave 節點崩潰等問題時能自動恢復。在上述問題發生後,k8s 會重新排程遇到問題的 pod,讓其重新執行。由於使用了 StatefulSet,這些 pod 的名稱、主機名和儲存會與原來保持一致。

服務擴容縮容

在 MySQL 一主多從複製模型下,擴容縮容意味著調整 slave 節點個數。得益於 StatefulSet 對 pod 啟動、銷燬順序的保證,透過如下命令就可以輕鬆實現服務的擴容縮容。

kubectl scale statefulset mysql ——replicas=

服務狀態監控

要保證服務的穩定性離不開對服務執行狀態的監控。除了透過就緒探針和活性探針檢測服務是否正常外,往往還需要更細粒度的監控指標。使用者可以藉助 mysqld-exporter 將 MySQL 的核心指標暴露給 prometheus,然後基於 prometheus 做監控告警。對於 mysqld-exporter,推薦以 sidecar 模式和 mysqld 容器部署在同一個 pod 中。

資料備份恢復

資料的備份和恢復是保障資料安全的有效手段。這裡,使用者可以直接使用儲存卷暴露的介面或者利用 VolumeSnapshot 功能完成資料的備份恢復工作,下面分別進行介紹。

使用儲存卷介面

許多儲存卷供應方都提供了儲存資料快照和基於快照恢復資料的功能,這些功能通常以介面的形式暴露給使用者。取樣這種方式要求使用者熟悉對應儲存卷供應方提供的操作介面。例如服務選用了阿里云云盤作為外部儲存卷,需要使用者瞭解雲盤提供的快照介面。

使用 VolumeSnapshot

K8s v1。12 引入了 3 個快照相關的資源物件

VolumeSnapshot

VolumeSnapshotContent

VolumeSnapshotClass

,透過它們提供了快照操作的標準方法。這樣,使用者可以在不感知外部儲存卷的情況下,為存放 MySQL 資料的儲存卷建立快照,或者基於快照恢復資料。

相比直接使用底層儲存卷介面,使用 VolumeSnapshot 顯然是更為理想的方法。但目前 VolumeSnapshot 還處在 Alpha 階段,支援標準快照操作的外部儲存卷也有限,這些都限制了當下 VolumeSnapshot 的應用場景。想進一步瞭解 VolumeSnapshot 可參考文件 Volume Snapshots。

使用 Operator 部署 MySQL

雖然使用者可以基於 StatefulSet 在 k8s 中部署運維一套高可用 MySQL 服務,但過程相對複雜。使用者既要熟悉各種 k8s 資源物件,又要學習很多 MySQL 的操作細節,同時還需維護一套複雜的管理指令碼。為了降低在 k8s 中部署複雜應用的門檻,誕生了 Kubernetes Operator。

Operator 簡介

Operator 是由 CoreOS 公司推出的,用來打包、部署和管理需要執行在 k8s 之上的複雜應用的一種方法。Operator 將運維人員對軟體操作的知識程式碼化,同時綜合運用 k8s 各種資源物件來實現複雜應用的部署和運維。

Operator 透過 CustomResourceDefinition 為服務定義了新的資源物件,同時透過自定義控制器來保證應用處於預期狀態。

K8s 應用管理之道 - 有狀態服務

Operator 的工作流程可抽象成以下三個步驟:

Observe - 透過 k8s API 觀察目標物件的狀態。

Analyze - 分析當前狀態與期望狀態的差別。

Act - 執行編排操作,將當前狀態調整為期望狀態。

Oracle MySQL Operator

對於 MySQL 服務,已經有很多優秀的開源 Operator 解決方案,包括 grtl/mysql-operator、oracle/mysql-operator、presslabs/mysql-operator、kubedb/mysql 等。本節將要介紹的 Oracle MySQL Operator 便是這些開源解決方案的典型代表。

Oracle MySQL Operator 工作原理

Oracle MySQL Operator 支援以下 2 種 MySQL 部署模式。

Primary - 服務由一個可讀寫的主節點和多個只讀的從節點組成。

Multi-Primary - 叢集中各節點角色相同,沒有主從的概念,每個節點都可以處理使用者的讀寫請求。

下圖展示了 Multi-Primary 模式下 Operator 的工作原理。

K8s 應用管理之道 - 有狀態服務

下列流程是理解 Operator 工作原理的關鍵:

使用 k8s 的 CustomResourceDefinition(CRD) 定義若干和 MySQL 部署運維相關的資源物件。

mysqlclusters - 用於描述叢集的期望狀態,包括部署模式、節點個數等。

mysqlbackups - 用於描述按需備份策略,可以配置備份資料的存放地點,如 AWS S3。

mysqlrestores - 用於描述資料恢復策略,需要配置備份資料和目標叢集。

mysqlbackupschedules - 用於描述定期備份策略,可以配置備份的時間間隔。

在 k8s 中部署一個 Operator 例項,該 Operator 會持續監聽針對這些資源物件的 CRUD 操作,並觀察物件狀態。

當用戶執行了某項操作,例如建立一個 MySQL 叢集時,一個新的 MySQLCluster 資源物件會被建立。當 operator 監聽到了 MySQLCluster 的建立事件後,會根據使用者配置建立符合需求的叢集。這裡建立了一個基於 Group Replication 的高可用 MySQL 叢集,使用了 StatefulSet、Headless service 等原生 k8s 資源物件。

當 Operator 觀察到 MySQLCluster 的當前狀態與期望狀態存在差別時,會執行相應的編排操作,保證狀態的一致性。

服務部署

得益於 Operator 對複雜部署細節的封裝,現在建立一個叢集變得非常簡單。例如,透過如下配置就可以部署一個包含 3 個節點的 MySQL Multi-Primary 叢集。

apiVersion: mysql。oracle。com/v1alpha1

kind: Cluster

metadata:

name: mysql-multimaster-cluster

spec:

multiMaster: true

members: 3

服務運維

在 Operator 模式下,同樣會涉及服務故障恢復、服務擴容縮容、服務狀態監控、資料備份恢復等運維工作。

服務故障恢復

由於 StatefulSet 的存在,當某個 MySQL 服務例項崩潰時,k8s 會對其重新排程。另外,如果 StatefulSet 被誤刪,Operator 也會對其進行重建。

服務擴容縮容

透過更改資源物件 MySQLCluster 的欄位

spec。members

可以輕鬆實現服務擴容縮容。這裡只對使用者暴露了 MySQLCluster,遮蔽了底層 k8s 資源物件。

服務狀態監控

可以在 k8s 中部署 Prometheus 監控 Operator 和各個 MySQL 叢集的狀態。具體步驟可參考文件 Monitoring。

資料備份恢復

可以透過資源物件 MySQLBackup、MySQLRestore 對資料進行備份和恢復,為使用者遮蔽了不同儲存卷操作上的差別。另外,還可以透過 MySQLBackupSchedule 建立定時備份任務。

例如,以下配置可以每隔 30 分鐘對 MySQL 叢集

mysql-cluster

的資料庫

test

進行備份。

[。。。]

kind: BackupSchedule

spec:

schedule: ‘*/30 * * * *’

backupTemplate:

cluster:

name: mysql-cluster

executor:

provider: mysqldump

databases:

- test

[。。。]

總結

本文分別介紹了透過原生 k8s 資源物件 StatefulSet 以及透過 MySQL Operator 部署運維一套高可用 MySQL 服務的方法和原理。可以看到 Operator 遮蔽了複雜應用的編排細節,大大降低了它們在 k8s 中的使用門檻。如果您有其它複雜應用需要部署,建議採用 Operator 方式。

原文連結

更多技術乾貨敬請關注雲棲社群知乎機構號:阿里云云棲社群 - 知乎