天天看點

想提高運維效率,那就把MySQL資料庫部署到Kubernetes 叢集中

摘要:Kubernetes 很多看起來比較“繁瑣”的設計的主要目的,都是希望為開發者提供更多的“可擴充性”,給使用者帶來更多的“穩定性”和“安全感”。

本文分享自華為雲社群《如何在 Kubernetes 叢集中搭建一個複雜的 MySQL 資料庫?》,作者:zuozewei 。

實際生産環境中,為了穩定和高可用,運維團隊一般不會把 MySQL 資料庫部署在 Kubernetes 叢集中,一般是用雲廠商的資料庫或者自己在高性能機器(如裸金屬伺服器)上搭建。

但是,對于測試開發環境,我們完全可以把 MySQL 部署到各自的 Kubernetes 叢集中,非常有助于提升運維效率,而且還有助于Kubernetes 使用的經驗積累。

​如下所示,我們僅需設定 root 使用者密碼(環境變量 MYSQL_ROOT_PASSWORD), 便可輕松的使用 MySQL 官方鏡像建構一個 MySQL 資料庫。

​建立一 Service 以便叢集内外均可通路資料庫,其中叢集外需通過 nodePort 設定的 30336 端口通路。

接着,通路資料庫并驗證其運作正常:

若要確定 MySQL 重新開機後資料仍然存在,我們需為其配置可持久化存儲,我這裡的實驗環境使用的是 Local Persistent Volume,也就是說,我希望 Kubernetes 能夠直接使用主控端上的本地磁盤目錄,而不依賴于遠端存儲服務,來提供“持久化”的容器 Volume。這樣做的好處很明顯,由于這個 Volume 直接使用的是本地磁盤,尤其是 SSD 盤,它的讀寫性能相比于大多數遠端存儲來說,要好得多。這個需求對本地實體伺服器部署的私有 Kubernetes 叢集來說,非常常見。

值得指出的是其次,相比于正常的 PV,一旦這些節點當機且不能恢複時,本地存儲 Volume 的資料就可能丢失。這就要求使用 其的應用必須具備資料備份和恢複的能力,允許你把這些資料定時備份在其他位置。

不難想象, Local Persistent Volume 的設計,主要面臨兩個難點。

第一個難點在于:如何把本地磁盤抽象成 PV。

可能你會說,Local Persistent Volume 不就等同于 hostPath 加 NodeAffinity 嗎?

比如,一個 Pod 可以聲明使用類型為 Local 的 PV,而這個 PV 其實就是一個 hostPath 類型的 Volume。如果這個 hostPath 對應的目錄,已經在節點 A 上被事先建立好了。那麼,我隻需要再給這個 Pod 加上一個 nodeAffinity=nodeA,不就可以使用這個 Volume 了嗎?

事實上,你絕不應該把一個主控端上的目錄當作 PV 使用。這是因為,這種本地目錄的存儲行為完全不可控,它所在的磁盤随時都可能被應用寫滿,甚至造成整個主控端當機。而且,不同的本地目錄之間也缺乏哪怕最基礎的 I/O 隔離機制。

是以,一個 本地存儲 Volume 對應的存儲媒體,一定是一塊額外挂載在主控端的磁盤或者塊裝置(“額外”的意思是,它不應該是主控端根目錄所使用的主硬碟)。這個原則,我們可以稱為“一個 PV 一塊盤”。

第二個難點在于:排程器如何保證 Pod 始終能被正确地排程到它所請求的本地 Volume 所在的節點上呢?

造成這個問題的原因在于,對于正常的 PV 來說,Kubernetes 都是先排程 Pod 到某個節點上,然後,再通過“兩階段處理”來“持久化”這台機器上的 Volume 目錄,進而完成 Volume 目錄與容器的綁定挂載。

可是,對于 Local PV 來說,節點上可供使用的磁盤(或者塊裝置),必須是運維人員提前準備好的。它們在不同節點上的挂載情況可以完全不同,甚至有的節點可以沒這種磁盤。

是以,這時候,排程器就必須能夠知道所有節點與 Local Persistent Volume 對應的磁盤的關聯關系,然後根據這個資訊來排程 Pod。

這個原則,我們可以稱為“在排程的時候考慮 Volume 分布”。在 Kubernetes 的排程器裡,有一個叫作 VolumeBindingChecker 的過濾條件專門負責這個事情。在 Kubernetes v1.11 中,這個過濾條件已經預設開啟了。

基于上述講述,在開始使用 Local Persistent Volume 之前,你首先需要在叢集裡配置好磁盤或者塊裝置。在公有雲上,這個操作等同于給虛拟機額外挂載一個磁盤,比如 GCE 的 Local SSD 類型的磁盤就是一個典型例子。

而在我們部署的私有環境中,你有兩種辦法來完成這個步驟。

第一種,當然就是給你的主控端挂載并格式化一個可用的本地磁盤,這也是最正常的操作;

第二種,對于實驗環境,你其實可以在主控端上挂載幾個 RAM Disk(記憶體盤)來模拟本地磁盤。

接下來,我會使用第二種方法,在我們之前部署的 Kubernetes 叢集上進行實踐。首先,在名叫 node-1 的主控端上建立一個挂載點,比如 /mnt/disks;然後,用幾個 RAM Disk 來模拟本地磁盤,如下所示:

需要注意的是,如果你希望其他節點也能支援 Local Persistent Volume 的話,那就需要為它們也執行上述操作,并且確定這些磁盤的名字(vol1、vol2 等)都不重複。接下來,我們就可以為這些本地磁盤定義對應的 PV 了,如下所示:

可以看到,這個 PV 的定義裡:local 字段,指定了它是一個 Local Persistent Volume;而 path 字段,指定的正是這個 PV 對應的本地磁盤的路徑,即:/mnt/disks/vol1。

當然了,這也就意味着如果 Pod 要想使用這個 PV,那它就必須運作在 node-1 上。是以,在這個 PV 的定義裡,需要有一個 nodeAffinity 字段指定 node-1 這個節點的名字。這樣,排程器在排程 Pod 的時候,就能夠知道一個 PV 與節點的對應關系,進而做出正确的選擇。這正是 Kubernetes 實作“在排程的時候就考慮 Volume 分布”的主要方法。

接下來要建立一個 StorageClass 來描述這個 PV,如下所示:

這個 StorageClass 的名字,叫作 local-storage。需要注意的是,在它的 provisioner 字段,我們指定的是 no-provisioner。這是因為 Local Persistent Volume 目前尚不支援 Dynamic Provisioning,是以它沒辦法在使用者建立 PVC 的時候,就自動建立出對應的 PV。也就是說,我們前面建立 PV 的操作,是不可以省略的。

與此同時,這個 StorageClass 還定義了一個 volumeBindingMode=WaitForFirstConsumer 的屬性。它是 Local Persistent Volume 裡一個非常重要的特性,即:延遲綁定。

通過這個延遲綁定機制,原本實時發生的 PVC 和 PV 的綁定過程,就被延遲到了 Pod 第一次排程的時候在排程器中進行,進而保證了這個綁定結果不會影響 Pod 的正常排程。

接下來,我們隻需要定義一個非常普通的 PVC,就可以讓 Pod 使用到上面定義好的 Local Persistent Volume 了,如下所示:

可以看到,這個 PVC 沒有任何特别的地方。唯一需要注意的是,它聲明的 storageClassName 是 mysql-min-storageclass-local。是以,将來 Kubernetes 的 Volume Controller 看到這個 PVC 的時候,不會為它進行綁定操作。

最後,我們建立 Local Persistent Volume 資源檔案:

而後,調整 Deploy 并挂載卷:

通過建立 configmap 并挂載到容器中,我們可自定義 MySQL 配置檔案。如下所示,名為 mysql-config 的 cm 包含一個 my.cnf 檔案:

将 configmap 挂載到容器内:

最傻瓜也最友善的處理方式,設定主控端時區和時間檔案與容器的映射。

使用者密碼等敏感資料以 Secret 加密儲存,而後被 Deployment 通過 volume 挂載或環境變量引用。如本例,我們建立root、user使用者,将使用者的密碼加密儲存:

Secret 建立完成後,我們将使用者明文密碼從 Deployment 去除,采用環境變量方式引用 Secret 資料,參見如下 Yaml 修改:

root 使用者及 MYSQL_USER 使用者,其密碼均通過 secretKeyRef 從 secret 擷取。

K8S 鏡像控制器可通過 livenessProbe 判斷容器是否異常,進而決定是否重建容器;而 Service 服務可通過 readinessProbe 判斷容器服務是否正常,進而確定服務可用性。

​本例配置的 livenessProbe 與 readinessProbe 是一樣的,即連續 3 次查詢資料庫失敗,則定義為異常。對 livenessProbe 與readinessProbe 詳細用法,不在本文的讨論範圍内,可參考 K8S 官方文檔:

Configure Liveness and Readiness Probes

Pod Lifecycle

容器的一些初始化操作顯然适合通過 InitContainer 來完成,這裡的 initContainer 是為了保證在 POD 啟動前,PV盤 要先行綁定成功,同時為了避免 MySQL 資料庫目錄内的 lost+found 目錄被誤認為是資料庫,初始化容器中将其删除;

通過如上多步調整,MySQL 資料庫的 Deplyment 如下所示:

建立此 Deployment 後,我們有如下元件:

考慮到資料安全性,我們定期備份資料庫,在K8S叢集中,我們可配置 CronJob 實作自動備份作業。首先,建立一個持久化存儲供備份用:

繼而,配置實際的自動化作業任務,如下所示,每天淩晨零點點将使用 mysqldump 備份 mall 資料庫。

​Kubernetes 很多看起來比較“繁瑣”的設計的主要目的,都是希望為開發者提供更多的“可擴充性”,給使用者帶來更多的“穩定性”和“安全感”。這兩個能力的高低,是衡量開源基礎設施項目水準的重要标準。 示例中揉合 Kubernetes 多項技術,建構了一個複雜且可做生産使用的單執行個體資料庫。

本文源碼:https://github.com/zuozewei/blog-example/tree/master/Kubernetes/k8s-mysql-pv-local

[1]:《深入剖析Kubernetes》

點選關注,第一時間了解華為雲新鮮技術~