摘要:Kubernetes 很多看起來比較“繁瑣”的設計的主要目的,都是希望為開發者提供更多的“可擴充套件性”,給使用者帶來更多的“穩定性”和“安全感”。
本文分享自華為雲社群《如何在 Kubernetes 叢集中搭建一個複雜的 MySQL 資料庫?》,作者:zuozewei 。
前言
實際生產環境中,為了穩定和高可用,運維團隊一般不會把 MySQL 資料庫部署在 Kubernetes 叢集中,一般是用雲廠商的資料庫或者自己在高效能機器(如裸金屬伺服器)上搭建。
但是,對於測試開發環境,我們完全可以把 MySQL 部署到各自的 Kubernetes 叢集中,非常有助於提升運維效率,而且還有助於Kubernetes 使用的經驗積累。
簡易部署
如下所示,我們僅需設定 root 使用者密碼(環境變數 MYSQL_ROOT_PASSWORD), 便可輕鬆的使用 MySQL 官方映象構建一個 MySQL 資料庫。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: mysql-min
name: mysql-min
spec:
replicas: 1
selector:
matchLabels:
app: mysql-min
template:
metadata:
labels:
app: mysql-min
spec:
containers:
- image: centos/mysql-57-centos7:latest
name: mysql-min
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_ROOT_PASSWORD
value: admin@123
建立一 Service 以便叢集內外均可訪問資料庫,其中叢集外需通過 nodePort 設定的 30336 埠訪問。
apiVersion: v1
kind: Service
metadata:
labels:
app: mysql-min
release: mysql-min
name: mysql-min
namespace: default
spec:
ports:
- name: mysql
port: 3306
protocol: TCP
nodePort: 30336
targetPort: mysql
selector:
app: mysql-min
#目前sessionAffinity可以提供"None""ClientIP"兩種設定:
#None: 以round robin的方式輪詢下面的Pods。
#ClientIP: 以client ip的方式固定request到同一臺機器。
sessionAffinity: None
type: NodePort
#status:
# loadBalancer: {}
接著,訪問資料庫並驗證其執行正常:
# kubectl get pod # 當前Pod名稱
NAME READY STATUS RESTARTS AGE
mysql-min-5b5668c448-t44ml 1/1 Running 0 3h
# 通過本機訪問
# kubectl exec -it mysql-min-5b5668c448-t44ml -- mysql -uroot -padmin@123
mysql> select 1;
+---+
| 1 |
+---+
| 1 |
+---+
# 叢集內部通過mysql service訪問:
# kubectl exec -it mysql-min-5b5668c448-t44ml -- mysql -uroot -padmin@123 -hmysql
mysql> select now();
+---------------------+
| now() |
+---------------------+
| 2021-03-13 07:19:14 |
+---------------------+
# 叢集外部,可通過任何一個 K8S 節點訪問資料庫:
# mysql -uroot -padmin@123 -hworker-1 -P30336
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
擴充套件部署
持久化儲存
若要確保 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 來模擬本地磁碟,如下所示:
# 在node-1上執行
$ mkdir /mnt/disks
$ for vol in vol1 vol2 vol3; do
mkdir /mnt/disks/$vol
mount -t tmpfs $vol /mnt/disks/$vol
done
需要注意的是,如果你希望其他節點也能支援 Local Persistent Volume 的話,那就需要為它們也執行上述操作,並且確保這些磁碟的名字(vol1、vol2 等)都不重複。接下來,我們就可以為這些本地磁碟定義對應的 PV 了,如下所示:
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-min-pv-local
namespace: default
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
storageClassName: "mysql-min-storageclass-local"
persistentVolumeReclaimPolicy: Retain
#表示使用本地儲存
local:
path: /mnt/disks/vol1
#使用local pv時必須定義nodeAffinity,Kubernetes Scheduler需要使用PV的nodeAffinity描述資訊來保證Pod能夠排程到有對應local volume的Node上。
#建立local PV之前,你需要先保證有對應的storageClass已經建立。
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
# pod 需要分不到的主機名,這臺主機上開啟了 local-pv 資源。
- node-1
可以看到,這個 PV 的定義裡:local 欄位,指定了它是一個 Local Persistent Volume;而 path 欄位,指定的正是這個 PV 對應的本地磁碟的路徑,即:/mnt/disks/vol1。
當然了,這也就意味著如果 Pod 要想使用這個 PV,那它就必須執行在 node-1 上。所以,在這個 PV 的定義裡,需要有一個 nodeAffinity 欄位指定 node-1 這個節點的名字。這樣,排程器在排程 Pod 的時候,就能夠知道一個 PV 與節點的對應關係,從而做出正確的選擇。這正是 Kubernetes 實現“在排程的時候就考慮 Volume 分佈”的主要方法。
接下來要建立一個 StorageClass 來描述這個 PV,如下所示:
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: mysql-min-storageclass-local
#指定儲存類的供應者,比如aws, nfs等,具體取值參考官方說明。
#儲存類有一個供應者的引數域,此引數域決定PV使用什麼儲存卷外掛。引數必需進行設定
#由於demo中使用的是本地儲存,所以這裡寫kubernetes.io/no-provisioner.
provisioner: kubernetes.io/no-provisioner
#volumeBindingMode 引數將延遲PVC繫結,直到 pod 被排程。
volumeBindingMode: WaitForFirstConsumer
這個 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 了,如下所示:
apiVersion: v1
items:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
#當啟用PVC 保護 alpha 功能時,如果使用者刪除了一個 pod 正在使用的 PVC,則該 PVC 不會被立即刪除。PVC 的刪除將被推遲,直到 PVC 不再被任何 pod 使用。
#可以看到,當 PVC 的狀態為 Teminatiing 時,PVC 受到保護,Finalizers 列表中包含 kubernetes.io/pvc-protection:
finalizers:
- kubernetes.io/pvc-protection
labels:
app: mysql-min
release: mysql-min
name: mysql-min
namespace: default
spec:
#PV 的訪問模式(accessModes)有三種:
#ReadWriteOnce(RWO):是最基本的方式,可讀可寫,但只支援被單個 Pod 掛載。
#ReadOnlyMany(ROX):可以以只讀的方式被多個 Pod 掛載。
#ReadWriteMany(RWX):這種儲存可以以讀寫的方式被多個 Pod 共享。
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: mysql-min-storageclass-local
#表示使用本地磁碟,實際生產中一般都使用nfs。
volumeMode: Filesystem
volumeName: mysql-min-pv-local
# status:
# accessModes:
# - ReadWriteOnce
# capacity:
# storage: 1Gi
kind: List
可以看到,這個 PVC 沒有任何特別的地方。唯一需要注意的是,它宣告的 storageClassName 是 mysql-min-storageclass-local。所以,將來 Kubernetes 的 Volume Controller 看到這個 PVC 的時候,不會為它進行繫結操作。
最後,我們建立 Local Persistent Volume 資原始檔:
kubectl apply -f mysql-min-pv-local.yaml
kubectl apply -f mysql-min-storageclass-local.yaml
kubectl apply -f mysql-min-pvc.yaml
而後,調整 Deploy 並掛載卷:
spec:
containers:
- image: centos/mysql-57-centos7:latest
...
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumes:
- name: data
persistentVolumeClaim:
claimName: mysql-min
自定義配置檔案
通過建立 configmap 並掛載到容器中,我們可自定義 MySQL 配置檔案。如下所示,名為 mysql-config 的 cm 包含一個 my.cnf 檔案:
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
my.cnf: |
[mysqld]
default_storage_engine=innodb
skip_external_locking
lower_case_table_names=1
skip_host_cache
skip_name_resolve
max_connections=2000
innodb_buffer_pool_size=8589934592
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
query_cache_type=0
innodb_flush_log_at_trx_commit = 0
sync_binlog = 0
query_cache_size = 104857600
slow_query_log =1
slow_query_log_file=/var/lib/mysql/slow-query.log
log-error=/var/lib/mysql/mysql.err
long_query_time = 0.02
table_open_cache_instances=16
table_open_cache = 6000
skip-grant-tables
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
將 configmap 掛載到容器內:
spec:
...
containers:
- image: centos/mysql-57-centos7:latest
...
volumeMounts:
- name: mysql-config
mountPath: /etc/my.cnf.d/my.cnf
subPath: my.cnf
...
volumes:
- name: mysql-config
- name: mysql-config
configMap:
name: mysql-config
...
設定容器時區
最傻瓜也最方便的處理方式,設定宿主機時區和時間檔案與容器的對映。
spec:
...
containers:
- image: centos/mysql-57-centos7:latest
...
volumeMounts:
- name: localtime
readOnly: true
mountPath: /etc/localtime
...
volumes:
- name: localtime
hostPath:
type: File
path: /etc/localtime
...
加密敏感資料
使用者密碼等敏感資料以 Secret 加密儲存,而後被 Deployment 通過 volume 掛載或環境變數引用。如本例,我們建立root、user使用者,將使用者的密碼加密儲存:
apiVersion: v1
data:
#將mysql資料庫的所有user的password配置到secret,統一管理
mysql-password: YWRtaW4=
mysql-root-password: OVplTmswRGdoSA==
kind: Secret
metadata:
labels:
app: mysql-min
release: mysql-min
name: mysql-min
namespace: default
#Secret有三種型別:
#Opaque:base64編碼格式的Secret,用來儲存密碼、金鑰等;但資料也通過base64 –decode解碼得到原始資料,所有加密性很弱。
#kubernetes.io/dockerconfigjson:用來儲存私有docker registry的認證資訊。
#kubernetes.io/service-account-token: 用於被serviceaccount引用。serviceaccout建立時Kubernetes會預設建立對應的secret。Pod如果使用了serviceaccount,對應的secret會自動掛載到Pod目錄/run/secrets/ kubernetes.io/serviceaccount中。
type: Opaque
Secret 建立完成後,我們將使用者明文密碼從 Deployment 去除,採用環境變數方式引用 Secret 資料,參見如下 Yaml 修改:
root 使用者及 MYSQL_USER 使用者,其密碼均通過 secretKeyRef 從 secret 獲取。
spec:... containers: - image: centos/mysql-57-centos7:latest name: mysql-min imagePullPolicy: IfNotPresent env: #password儲存在secret中 - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: key: mysql-root-password name: mysql-min - name: MYSQL_PASSWORD valueFrom: secretKeyRef: key: mysql-password name: mysql-min - name: MYSQL_USER value: zuozewei
容器健康檢查
K8S 映象控制器可通過 livenessProbe 判斷容器是否異常,進而決定是否重建容器;而 Service 服務可通過 readinessProbe 判斷容器服務是否正常,從而確保服務可用性。
本例配置的 livenessProbe 與 readinessProbe 是一樣的,即連續 3 次查詢資料庫失敗,則定義為異常。對 livenessProbe 與readinessProbe 詳細用法,不在本文的討論範圍內,可參考 K8S 官方文件:
- Configure Liveness and Readiness Probes
- Pod Lifecycle
spec:
containers:
image: centos/mysql-57-centos7:latest
...
#kubelet 使用 liveness probe(存活探針)來確定何時重啟容器。例如,當應用程式處於執行狀態但無法做進一步操作,liveness 探針將捕獲到 deadlock,重啟處於該狀態下的容器,使應用程式在存在 bug 的情況下依然能夠繼續執行下去
livenessProbe:
exec:
command:
- /bin/sh
- "-c"
- MYSQL_PWD="${MYSQL_ROOT_PASSWORD}"
- mysql -h 127.0.0.1 -u root -e "SELECT 1"
failureThreshold: 3 #探測成功後,最少連續探測失敗多少次才被認定為失敗。預設是 3。最小值是 1。
initialDelaySeconds: 30 #容器啟動後第一次執行探測是需要等待多少秒。
periodSeconds: 10 #執行探測的頻率。預設是10秒,最小1秒。
successThreshold: 1 #探測失敗後,最少連續探測成功多少次才被認定為成功。預設是 1。對於 liveness 必須是 1。最小值是 1。
timeoutSeconds: 5 #探測超時時間。預設1秒,最小1秒。
#Kubelet 使用 readiness probe(就緒探針)來確定容器是否已經就緒可以接受流量。只有當 Pod 中的容器都處於就緒狀態時 kubelet 才會認定該 Pod處於就緒狀態。該訊號的作用是控制哪些 Pod應該作為service的後端。如果 Pod 處於非就緒狀態,那麼它們將會被從 service 的 load balancer中移除。
readinessProbe:
exec:
command:
- /bin/sh
- "-c"
- MYSQL_PWD="${MYSQL_ROOT_PASSWORD}"
- mysql -h 127.0.0.1 -u root -e "SELECT 1"
failureThreshold: 3
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
容器初始化
容器的一些初始化操作顯然適合通過 InitContainer 來完成,這裡的 initContainer 是為了保證在 POD 啟動前,PV盤 要先行繫結成功,同時為了避免 MySQL 資料庫目錄內的 lost+found 目錄被誤認為是資料庫,初始化容器中將其刪除;
#Init 容器支援應用容器的全部欄位和特性,包括資源限制、資料卷和安全設定。 然而,Init 容器對資源請求和限制的處理稍有不同,在下面 資源 處有說明。 而且 Init 容器不支援 Readiness Probe,因為它們必須在 Pod 就緒之前執行完成。
#如果為一個 Pod 指定了多個 Init 容器,那些容器會按順序一次執行一個。 每個 Init 容器必須執行成功,下一個才能夠執行。 當所有的 Init 容器執行完成時,Kubernetes 初始化 Pod 並像平常一樣執行應用容器。
#mysql這裡的initContainer是為了保證在POD啟動前,PV盤要先行繫結成功。
initContainers:
- command:
- rm
- -fr
- /var/lib/mysql/lost+found
image: busybox:1.29.3
imagePullPolicy: IfNotPresent
name: remove-lost-found
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/lib/mysql
name: data
restartPolicy: Always
#scheduler 是 kubernetes 的排程器,主要的任務是把定義的 pod 分配到叢集的節點上。
schedulerName: default-scheduler
securityContext: {}
#如果您的Pod通常需要超過30秒才能關閉,請確保增加優雅終止寬限期。可以通過在Pod YAML中設定terminationGracePeriodSeconds選項來實現.
#如果容器在優雅終止寬限期後仍在執行,則會傳送SIGKILL訊號並強制刪除。與此同時,所有的Kubernetes物件也會被清除。
terminationGracePeriodSeconds: 30
#定義資料卷PVC,與PV匹配。
volumes:
- name: data
persistentVolumeClaim:
claimName: mysql-min
- name: mysql-config
configMap:
name: mysql-config
- name: localtime
hostPath:
type: File
path: /etc/localtime
完整Deployment
通過如上多步調整,MySQL 資料庫的 Deplyment 如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
generation: 1
labels:
app: mysql-min
release: mysql-min
name: mysql-min
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: mysql-min
strategy:
rollingUpdate:
maxSurge: 1 #滾動升級時會先啟動1個pod
maxUnavailable: 1 #滾動升級時允許的最大Unavailable的pod個數
type: RollingUpdate #滾動升級
template:
metadata:
labels:
app: mysql-min
spec:
containers:
- env:
#password儲存在secret中
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
key: mysql-root-password
name: mysql-min
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
key: mysql-password
name: mysql-min
- name: MYSQL_USER
value: apollo
image: centos/mysql-57-centos7:latest
imagePullPolicy: IfNotPresent
#kubelet 使用 liveness probe(存活探針)來確定何時重啟容器。例如,當應用程式處於執行狀態但無法做進一步操作,liveness 探針將捕獲到 deadlock,重啟處於該狀態下的容器,使應用程式在存在 bug 的情況下依然能夠繼續執行下去
livenessProbe:
exec:
command:
- /bin/sh
- "-c"
- MYSQL_PWD="${MYSQL_ROOT_PASSWORD}"
- mysql -h 127.0.0.1 -u root -e "SELECT 1"
failureThreshold: 3 #探測成功後,最少連續探測失敗多少次才被認定為失敗。預設是 3。最小值是 1。
initialDelaySeconds: 30 #容器啟動後第一次執行探測是需要等待多少秒。
periodSeconds: 10 #執行探測的頻率。預設是10秒,最小1秒。
successThreshold: 1 #探測失敗後,最少連續探測成功多少次才被認定為成功。預設是 1。對於 liveness 必須是 1。最小值是 1。
timeoutSeconds: 5 #探測超時時間。預設1秒,最小1秒。
name: mysql-min
ports:
- containerPort: 3306
name: mysql
protocol: TCP
#Kubelet 使用 readiness probe(就緒探針)來確定容器是否已經就緒可以接受流量。只有當 Pod 中的容器都處於就緒狀態時 kubelet 才會認定該 Pod處於就緒狀態。該訊號的作用是控制哪些 Pod應該作為service的後端。如果 Pod 處於非就緒狀態,那麼它們將會被從 service 的 load balancer中移除。
readinessProbe:
exec:
command:
- /bin/sh
- "-c"
- MYSQL_PWD="${MYSQL_ROOT_PASSWORD}"
- mysql -h 127.0.0.1 -u root -e "SELECT 1"
failureThreshold: 3
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
requests:
cpu: 100m
memory: 256Mi
#為了達到一個相當高水平的實用性,特別是為了積極開發應用,快速除錯失敗是很重要的。除了一般的日誌採集,Kubernetes還能通過查出重大錯誤原因來加速除錯,並在某種程度上通過kubectl或者UI陳列出來。可以指定一個’terminationMessagePath’來讓容器寫下它的“death rattle“,比如宣告失敗訊息,堆疊跟蹤,免責條款等等。預設途徑是‘/dev/termination-log’。
terminationMessagePath: /dev/termination-log
# 此欄位預設為 “File“,這意味著僅從終止訊息檔案中檢索終止訊息。 通過將 terminationMessagePolicy 設定為 “FallbackToLogsOnError“,你就可以告訴 Kubernetes,在容器因錯誤退出時,如果終止訊息檔案為空,則使用容器日誌輸出的最後一塊作為終止訊息。 日誌輸出限制為 2048 位元組或 80 行,以較小者為準。
terminationMessagePolicy: File
#要使用的資料盤目錄,在initContainer中會關聯此處目錄。
volumeMounts:
- mountPath: /var/lib/mysql
name: data
- name: mysql-config
mountPath: /etc/my.cnf.d/my.cnf
subPath: my.cnf
- name: localtime
readOnly: true
mountPath: /etc/localtime
dnsPolicy: ClusterFirst
#Init 容器支援應用容器的全部欄位和特性,包括資源限制、資料卷和安全設定。 然而,Init 容器對資源請求和限制的處理稍有不同,在下面 資源 處有說明。 而且 Init 容器不支援 Readiness Probe,因為它們必須在 Pod 就緒之前執行完成。
#如果為一個 Pod 指定了多個 Init 容器,那些容器會按順序一次執行一個。 每個 Init 容器必須執行成功,下一個才能夠執行。 當所有的 Init 容器執行完成時,Kubernetes 初始化 Pod 並像平常一樣執行應用容器。
#mysql這裡的initContainer是為了保證在POD啟動前,PV盤要先行繫結成功。
initContainers:
- command:
- rm
- -fr
- /var/lib/mysql/lost+found
image: busybox:1.29.3
imagePullPolicy: IfNotPresent
name: remove-lost-found
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/lib/mysql
name: data
restartPolicy: Always
#scheduler 是 kubernetes 的排程器,主要的任務是把定義的 pod 分配到叢集的節點上。
schedulerName: default-scheduler
securityContext: {}
#如果您的Pod通常需要超過30秒才能關閉,請確保增加優雅終止寬限期。可以通過在Pod YAML中設定terminationGracePeriodSeconds選項來實現.
#如果容器在優雅終止寬限期後仍在執行,則會傳送SIGKILL訊號並強制刪除。與此同時,所有的Kubernetes物件也會被清除。
terminationGracePeriodSeconds: 30
#定義資料卷PVC,與PV匹配。
volumes:
- name: data
persistentVolumeClaim:
claimName: mysql-min
- name: mysql-config
configMap:
name: mysql-config
- name: localtime
hostPath:
type: File
path: /etc/localtime
建立此 Deployment 後,我們有如下元件:
# kubectl get all,pvc,cm,secret -l app=mysql-min
# MySQL pod:
NAME READY STATUS RESTARTS AGE
pod/mysql-min-f9c9b7b5-q9br4 1/1 Running 6 14d
# Service:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/mysql-min NodePort 10.96.184.130 <none> 3306:30336/TCP 16d
# MySQL Deployment:
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/mysql-min 1/1 1 1 16d
# 副本集ReplicaSet被Deployment呼叫,其是自動生成的
NAME DESIRED CURRENT READY AGE
replicaset.apps/mysql-min-587cf9fd48 0 0 0 16d
replicaset.apps/mysql-min-589bf8cdc5 0 0 0 16d
replicaset.apps/mysql-min-6b7447c7dd 0 0 0 14d
replicaset.apps/mysql-min-6cc9887459 0 0 0 16d
replicaset.apps/mysql-min-7759579d77 0 0 0 16d
replicaset.apps/mysql-min-84d4d6bd56 0 0 0 15d
replicaset.apps/mysql-min-f9c9b7b5 1 1 1 14d
# Pvc:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/mysql-min Bound mysql-min-pv-local 5Gi RWO mysql-min-storageclass-local 16d
# Secret:
NAME TYPE DATA AGE
secret/mysql-min Opaque 2 16d
定期自動備份
考慮到資料安全性,我們定期備份資料庫,在K8S叢集中,我們可配置 CronJob 實現自動備份作業。首先,建立一個持久化儲存供備份用:
apiVersion: v1
items:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
#當啟用PVC 保護 alpha 功能時,如果使用者刪除了一個 pod 正在使用的 PVC,則該 PVC 不會被立即刪除。PVC 的刪除將被推遲,直到 PVC 不再被任何 pod 使用。
#可以看到,當 PVC 的狀態為 Teminatiing 時,PVC 受到保護,Finalizers 列表中包含 kubernetes.io/pvc-protection:
finalizers:
- kubernetes.io/pvc-protection
labels:
app: mysql-min
release: mysql-min
name: mysql-min-backup
namespace: default
spec:
#PV 的訪問模式(accessModes)有三種:
#ReadWriteOnce(RWO):是最基本的方式,可讀可寫,但只支援被單個 Pod 掛載。
#ReadOnlyMany(ROX):可以以只讀的方式被多個 Pod 掛載。
#ReadWriteMany(RWX):這種儲存可以以讀寫的方式被多個 Pod 共享。
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: mysql-min-storageclass-nfs
#表示使用本地磁碟,實際生產中一般都使用nfs。
volumeMode: Filesystem
volumeName: mysql-min-pv-local
# status:
# accessModes:
# - ReadWriteOnce
# capacity:
# storage: 1Gi
kind: List
繼而,配置實際的自動化作業任務,如下所示,每天凌晨零點點將使用 mysqldump 備份 mall 資料庫。
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: mysql-backup
spec:
schedule: "0 0 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: mysql-min-backup
imagePullPolicy: IfNotPresent
image: centos/mysql-57-centos7:latest
env:
#password儲存在secret中
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
key: mysql-root-password
name: mysql-min
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
key: mysql-password
name: mysql-min
- name: MYSQL_HOST
value: mysql-min
command:
- /bin/sh
- -c
- |
set -ex
mysqldump --host=$MYSQL_HOST --user=$MYSQL_ROOT_PASSWORD \
--password=$mysql-root-password \
--routines --databases mall --single-transaction \
> /mysql-backup/mysql-`date +"%Y%m%d"`.sql
volumeMounts:
- name: mysql-min-backup
mountPath: /mysql-min-backup
restartPolicy: OnFailure
volumes:
- name: mysql-min-backup
persistentVolumeClaim:
claimName: mysql-min-backup
小結
Kubernetes 很多看起來比較“繁瑣”的設計的主要目的,都是希望為開發者提供更多的“可擴充套件性”,給使用者帶來更多的“穩定性”和“安全感”。這兩個能力的高低,是衡量開源基礎設施專案水平的重要標準。 示例中揉合 Kubernetes 多項技術,構建了一個複雜且可做生產使用的單例項資料庫。
本文原始碼:https://github.com/zuozewei/b...
參考資料:
[1]:《深入剖析Kubernetes》