天天看點

在K8S叢集中一步步建構一個複雜的MySQL資料庫文檔說明簡單部署擴充部署結束語

文檔說明

​ 本文面向容器初學者,作者先簡單的 用MySQL官方鏡像搭建一個可運作的單執行個體資料庫,而後考慮生産或現實需求,一步一步完善并揉合K8S多個技術,進而建構一個複雜且可供生産用的MySQL單執行個體庫。

簡單部署

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

# kubectl create -f - <<EOF
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 labels:
 app: mysql
 name: mysql
spec:
 replicas: 1
 selector:
 matchLabels:
 app: mysql
 template:
 metadata:
 labels:
 app: mysql
 spec:
 containers:
 - image: mysql
 name: mysql
 imagePullPolicy: IfNotPresent
 env:
 - name: MYSQL_ROOT_PASSWORD
 value: Changeme
EOF           

​ 注意:若你的K8S叢集是minishift、openshift、origin,因其為安全考慮,不允許容器以root使用者運作,而官方MySQL鏡像卻需root權限,故為使其能順利運作,我們需将anyuid scc賦予default serviceaccount:

# oc adm policy add-scc-to-user anyuid -z default           

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

# kubectl create -f - <<EOF
apiVersion: v1
kind: Service
metadata:
 labels:
 app: mysql
 name: mysql
spec:
 type: NodePort
 ports:
 - port: 3306
 nodePort: 30006
 protocol: TCP
 targetPort: 3306
 selector:
 app: mysql
EOF           

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

# kubectl get pod # 目前Pod名稱
NAME READY STATUS RESTARTS AGE
mysql-5b5668c448-t44ml 1/1 Running 0 3h

# 通過本機通路 # kubectl exec -it mysql-5b5668c448-t44ml -- mysql -uroot -pChangeme

mysql> select 1;
+---+
| 1 |
+---+
| 1 |
+---+

# 叢集内部通過mysql service通路: # kubectl exec -it mysql-5b5668c448-t44ml -- mysql -uroot -pChangeme -hmysql

mysql> select now();
+---------------------+
| now() |
+---------------------+
| 2018-05-21 07:19:14 |
+---------------------+

# 叢集外部,可通過任何一個K8S節點通路資料庫: # mysql -uroot -pChangeme -horigin-lb-01 -P30006

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+           

擴充部署

持久化存儲

​ 若要確定MySQL重新開機後資料仍然存在,我們需為其配置可持久化存儲,作者的實驗環境配置了GlusterFS分布式存儲,其支援K8S動态提供特性,故可執行如下指令建立PVC:

# kubectl create -f - <<EOF
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
 name: mysql
spec:
 accessModes:
 - ReadOnlyMany
 resources:
 requests:
 storage: 1Gi
 storageClassName: glusterfs-raid0
EOF           

​ 而後,調整Deploy并挂載卷:

spec: containers: - image: mysql ... volumeMounts: - name: mysql-data mountPath: /var/lib/mysql volumes: - name: mysql-data persistentVolumeClaim: claimName: mysql           

自定義配置檔案

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

apiVersion: v1 metadata: name: mysql-config data: custom.cnf: | [mysqld] default_storage_engine=innodb skip_external_locking lower_case_table_names=1 skip_host_cache skip_name_resolve kind: ConfigMap           

​ 将cm挂載到容器内:

spec: ... containers: - image: mysql ... volumeMounts: - name: mysql-config mountPath: /etc/mysql/conf.d/ ... volumes: - name: mysql-config configMap: name: mysql-config ...           

加密銘感資料

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

# echo -n Changeme | base64
Q2hhbmdlbWU=
# kubectl create -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
 name: mysql-user-pwd
data:
 mysql-root-pwd: Q2hhbmdlbWU=
 mysql-app-user-pwd: Q2hhbmdlbWU=
 mysql-test-user-pwd: Q2hhbmdlbWU=
EOF           

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

  • 鏡像初始化時自動建立MYSQL_DATABASE環境變量 1 設定的資料庫;
  • 鏡像初始化時将MYSQL_DATABASE資料庫賦予MYSQL_USER使用者;
  • root使用者及MYSQL_USER使用者,其密碼均通過secretKeyRef從secret擷取;
spec: ... containers: - image: mysql name: mysql imagePullPolicy: IfNotPresent env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-user-pwd key: mysql-root-pwd - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: mysql-user-pwd key: mysql-app-user-pwd - name: MYSQL_USER value: app - name: MYSQL_DATABASE value: appdb           

容器健康檢查

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

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

spec: containers: - image: mysql ... livenessProbe: exec: command: - /bin/sh - "-c" - MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" - mysql -h 127.0.0.1 -u root -e "SELECT 1" initialDelaySeconds: 30 timeoutSeconds: 5 successThreshold: 1 failureThreshold: 3 readinessProbe: exec: command: - /bin/sh - "-c" - MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" - mysql -h 127.0.0.1 -u root -e "SELECT 1" initialDelaySeconds: 10 timeoutSeconds: 1 successThreshold: 1 failureThreshold: 3 ...           

容器初始化

假設,我們有這樣的需求:“初始部署MySQL時,其已包應用所需的資料庫、使用者、權限、表結構與資料”。研究MySQL官方鏡像的

Dockerfile

可知,資料庫初始化時将自動執行目錄/docker-entrypoint-initdb.d内的.sh、.sql、.sql.gz檔案,鑒于此,我們可有如下兩種方法:

  1. 基于官方鏡像重新編寫Dockerfile,将腳本copy到新鏡像/docker-entrypoint-initdb.d目錄,因需編譯新鏡像,故此方法不靈活;
  2. 初始化容器(initContainers)在正常容器(containers)前運作,故在初始化容器中可将腳本拷貝到共享目錄,而後MySQL鏡像挂載此目錄到/docker-entrypoint-initdb.d,此方法靈活。

本例,作者采用初始化容器方案,功能如下:

  • 初始化與正常容器共享名為mysql-initdb的emptyDir,均被挂載到/docker-entrypoint-initdb.d目錄;
  • 初始化容器将.sql檔案置于共享的/docker-entrypoint-initdb.d目錄,其含初始化testdb與appdb資料庫;
  • 為了避免MySQL資料庫目錄内的lost+found目錄被誤認為是資料庫,初始化容器中将其删除;
spec: initContainers: - name: mysql-init image: busybox imagePullPolicy: IfNotPresent env: - name: MYSQL_TEST_USER_PASSWORD valueFrom: secretKeyRef: name: mysql-user-pwd key: mysql-test-user-pwd command:  - sh - "-c" - | set -ex rm -fr /var/lib/mysql/lost+found cat > /docker-entrypoint-initdb.d/mysql-testdb-init.sql <<EOF create database testdb default character set utf8; grant all on testdb.* to 'test'@'%' identified by '$MYSQL_TEST_USER_PASSWORD'; flush privileges; EOF cat > /docker-entrypoint-initdb.d/mysql-appdb-init.sql <<EOF create table app(id int); insert into app values(1); commit; EOF volumeMounts: - name: mysql-data mountPath: /var/lib/mysql - name: mysql-initdb mountPath: /docker-entrypoint-initdb.d containers: - image: mysql volumeMounts: - name: mysql-initdb mountPath: /docker-entrypoint-initdb.d ... volumes: - name: mysql-data persistentVolumeClaim: claimName: mysql - name: mysql-initdb emptyDir: {} ...           

完整Deployment

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

apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: app: mysql name: mysql spec: replicas: 1 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: initContainers: - name: mysql-init image: busybox imagePullPolicy: IfNotPresent env: - name: MYSQL_TEST_USER_PASSWORD valueFrom: secretKeyRef: name: mysql-user-pwd key: mysql-test-user-pwd command:  - sh - "-c" - | set -ex rm -fr /var/lib/mysql/lost+found cat > /docker-entrypoint-initdb.d/mysql-testdb-init.sql <<EOF create database testdb default character set utf8; grant all on testdb.* to 'test'@'%' identified by '$MYSQL_TEST_USER_PASSWORD'; flush privileges; EOF cat > /docker-entrypoint-initdb.d/mysql-appdb-init.sql <<EOF create table app(id int); insert into app values(1); commit; EOF volumeMounts: - name: mysql-data mountPath: /var/lib/mysql - name: mysql-initdb mountPath: /docker-entrypoint-initdb.d containers: - image: mysql name: mysql imagePullPolicy: IfNotPresent env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-user-pwd key: mysql-root-pwd - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: mysql-user-pwd key: mysql-app-user-pwd - name: MYSQL_USER value: app - name: MYSQL_DATABASE value: appdb volumeMounts: - name: mysql-data mountPath: /var/lib/mysql - name: mysql-initdb mountPath: /docker-entrypoint-initdb.d - name: mysql-config mountPath: /etc/mysql/conf.d/ ports: - name: mysql containerPort: 3306 command: - /bin/sh - "-c" - MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" - mysql -h 127.0.0.1 -u root -e "SELECT 1" initialDelaySeconds: 30 timeoutSeconds: 5 successThreshold: 1 failureThreshold: 3 readinessProbe: exec: command: - /bin/sh - "-c" - MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" - mysql -h 127.0.0.1 -u root -e "SELECT 1" initialDelaySeconds: 10 timeoutSeconds: 1 successThreshold: 1 failureThreshold: 3 volumes: - name: mysql-data persistentVolumeClaim: claimName: mysql - name: mysql-initdb emptyDir: {} - name: mysql-config configMap: name: mysql-config           

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

# kubectl get all,pvc,cm,secret # MySQL Deployment:
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/mysql 1 1 1 1 1m

# RS被Deployment調用,其是自動生成的
NAME DESIRED CURRENT READY AGE
rs/mysql-998977cdd 1 1 1 1m

# Pod:
NAME READY STATUS RESTARTS AGE
po/mysql-998977cdd-v2ks2 1/1 Running 1 1m

# Service:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/mysql NodePort 172.30.3.200 <none> 3306:30006/TCP 8h

# Pvc:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc/mysql Bound pvc-fe.. 1Gi ROX glusterfs-raid0 2m

# Configmap:
NAME DATA AGE
cm/mysql-config 1 6h

# Secret:
NAME TYPE DATA AGE
secrets/mysql-user-pwd Opaque 3 1h           

定期自動備份

​ 考慮到資料安全性,我們定期備份資料庫,在K8S叢集中,我們可配置

CronJob

實作自動備份作業。首先,建立一個持久化存儲供備份用:

# kubectl create -f - <<EOF
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
 name: mysql-backup
spec:
 accessModes:
 - ReadWriteOnce
 resources:
 requests:
 storage: 2Gi
 storageClassName: glusterfs-raid0
EOF           

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

# kubectl create -f - <<EOF
apiVersion: batch/v1beta1
kind: CronJob
metadata:
 name: mysql-backup
spec:
 schedule: "0 0 * * *"
 jobTemplate:
 spec:
 template:
 spec:
 containers:
 - name: mysql-backup
 imagePullPolicy: IfNotPresent
 image: mysql
 env:
 - name: MYSQL_BACKUP_USER
 value: root
 - name: MYSQL_BACKUP_USER_PASSWORD
 valueFrom:
 secretKeyRef:
 name: mysql-user-pwd
 key: mysql-root-pwd
 - name: MYSQL_HOST
 value: mysql
 command:
 - /bin/sh
 - -c
 - |
 set -ex
 mysqldump --host=$MYSQL_HOST --user=$MYSQL_BACKUP_USER \
 --password=$MYSQL_BACKUP_USER_PASSWORD \
 --routines --databases appdb --single-transaction \
 > /mysql-backup/mysql-`date +"%Y%m%d"`.sql
 volumeMounts:
 - name: mysql-backup
 mountPath: /mysql-backup
 restartPolicy: OnFailure
 volumes:
 - name: mysql-backup
 persistentVolumeClaim:
 claimName: mysql-backup
EOF           

結束語

​ 本文揉合K8S多項技術,建構了一個複雜且可做生産使用的範例,當然,此庫是單執行個體資料庫,倘若需建構資料庫高可用方案,需部署如MySQL HA、PXC叢集,其中自動作業備份範例僅使用mysqldump備份,在生産環境不是很實用,我們需要考慮使用xtrabackup備份以及mysqlbinlog備份日志。

本文轉自SegmentFault-

在K8S叢集中一步步建構一個複雜的MySQL資料庫