天天看點

Kubernetes-部署高可用的MySQL 1、MySQL簡介 2、MySQL的高可用方案 3、安裝部署 4、了解有狀态Pod的初始化 5、MySQL部署環境驗證 6、擴容slave的數量

1、MySQL簡介

MySQL 是一個開源的關系型資料庫管理系統,使用标準的sql語言,由瑞典 MySQL AB 公司開發,目前屬于 Oracle 公司。能夠 支援大型的資料庫,可以處理上千萬條的資料記錄。可以運作于在Windows、Linux等多種系統上;支援C、C++、Python、Java、Perl、PHP、Eiffel、Ruby和Tcl等程式設計語言。對于32位系統,MySQL的表檔案最大可支援4GB,對于64位系統,MySQL支援最大的表檔案為8TB。

2、MySQL的高可用方案

本文的MySQL高可用方案為主從複制+讀寫分離,即由單一的master和多個slave所構成。其中,用戶端通過master對資料庫進行寫操作,通過slave端進行讀操作。master出現問題後,可以将應用切換到slave端。 此方案是MySQL官方提供的一種高可用解決方案,節點間的資料同步采用MySQL Replication技術。

MySQL Replication從一個MySQL資料庫伺服器(master)的資料複制到一個或多個MySQL資料庫伺服器(slave)。在預設情況下,複制是異步的;slave不需要一直接收來自主機的更新。根據配置,可以複制資料庫中的所有資料庫、標明的資料庫,或者特定的表。

Kubernetes-部署高可用的MySQL 1、MySQL簡介 2、MySQL的高可用方案 3、安裝部署 4、了解有狀态Pod的初始化 5、MySQL部署環境驗證 6、擴容slave的數量

MySQL中複制的優點包括:

  • 擴容解決方案:在多個slave之間擴充負載以提高性能。在這種模式下,所有的寫入和更新操作都必須在主伺服器上進行。然而,讀取操作通過slave鏡像。該模型可以提高寫入操作的性能,同時,也能夠通過增加slave的節點數量,進而顯著地提升讀取速度。
  • 資料安全:資料從master被複制到slave,并且slave可以暫停複制過程。是以,可以在不損壞master的情況下,在slave上運作備份服務。
  • 分析:現場資料可以在master上建立,而對資訊的分析可以在slave進行,而不影響master的性能。
  • 遠端資料分發:可以使用複制為遠端站點建立本地資料的副本,而不必一直通過通路master。

此高可用的解決方案适用于對資料實時性要求不是特别嚴格的場景,在使用時可以通過廉價的硬體來擴充slave的節點數量,将讀壓力分散到多台slave的機器上面。此方案能夠在很大的程度上解決資料庫讀取資料的壓力瓶頸問題,這是因為在大多的應用系統中,讀壓力要比寫壓力大很多多。

3、安裝部署

3.1 環境要求

  • 已有Kubernetes 1.6+環境;
  • 在Kubernetes中提供多個(具體數量根據有狀态副本集的個數而定)容量大于10g的持久化存儲卷。

3.2 部署MySql

此示例由一個ConfigMap、兩個Service和一個StatefulSet所組成。

3.2.1 建立ConfigMap

通過YAML檔案建立名為mysql的ConfigMap:

$ kubectl create -f {path}/mysql-configmap.yaml --namespace=kube-public       
apiVersion: v1 kind: ConfigMap metadata: name: mysql labels: app: mysql data: master.cnf: | # Apply this config only on the master. [mysqld] log-bin
 log_bin_trust_function_creators=1
 lower_case_table_names=1 slave.cnf: | # Apply this config only on slaves. [mysqld] super-read-only
 log_bin_trust_function_creators=1      

3.2.2 建立Services

通過yaml檔案建立mysql和mysql-read這兩個Service:

$ kubectl create -f {path}/mysql-services.yaml --namespace=kube-public       
# Headless service for stable DNS entries of StatefulSet members. apiVersion: v1 kind: Service metadata:  name: mysql  labels:  app: mysql spec:  ports:  - name: mysql  port: 3306  clusterIP: None  selector:  app: mysql --- # Client service for connecting to any MySQL instance for reads. # For writes, you must instead connect to the master: mysql-0.mysql. apiVersion: v1 kind: Service metadata:  name: mysql-read  labels:  app: mysql spec:  ports:  - name: mysql  port: 3306  selector:  app: mysql      

StatefulSet控制器為Pod建立了一個DNS條目,而Headless服務為DNS條目提供一個主機。因為Headless服務的名稱為mysql,其他Pod通過<pod-name>.mysql通路此Pod。用戶端通路被稱為mysql-read,用戶端服務通過通路mysql-read讀取資料。通過連接配接myql執行寫入資料的操作。

3.2.3 建立StatefulSet

通過yaml檔案建立名為mysql的StatefulSet:

$ kubectl create -f {path}/mysql-statefulset.yaml --namespace=kube-public       
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: mysql
spec:
 selector:
 matchLabels:
 app: mysql
 serviceName: mysql
 replicas: 3 template:
 metadata:
 labels:
 app: mysql
 spec:
 initContainers: - name: init-mysql
 image: mysql:5.7
 command: - bash
 - "-c" - | set -ex
 # Generate mysql server-id from pod ordinal index. [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
 ordinal=${BASH_REMATCH[1]}
 echo [mysqld] > /mnt/conf.d/server-id.cnf
 # Add an offset to avoid reserved server-id=0 value.
 echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
 # Copy appropriate conf.d files from config-map to emptyDir. if [[ $ordinal -eq 0 ]]; then
 cp /mnt/config-map/master.cnf /mnt/conf.d/ else
 cp /mnt/config-map/slave.cnf /mnt/conf.d/ fi
 volumeMounts: - name: conf
 mountPath: /mnt/conf.d
 - name: config-map
 mountPath: /mnt/config-map
 - name: clone-mysql
 image: gcr.io/google-samples/xtrabackup:1.0
 command: - bash
 - "-c" - | set -ex
 # Skip the clone if data already exists. [[ -d /var/lib/mysql/mysql ]] && exit 0 # Skip the clone on master (ordinal index 0). [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
 ordinal=${BASH_REMATCH[1]} [[ $ordinal -eq 0 ]] && exit 0 # Clone data from previous peer.
 ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
 # Prepare the backup.
 xtrabackup --prepare --target-dir=/var/lib/mysql
 volumeMounts: - name: data
 mountPath: /var/lib/mysql
 subPath: mysql
 - name: conf
 mountPath: /etc/mysql/conf.d
 containers: - name: mysql
 image: mysql:5.7
 env: - name: MYSQL_ALLOW_EMPTY_PASSWORD
 value: "1"
 ports: - name: mysql
 containerPort: 3306
 volumeMounts: - name: data
 mountPath: /var/lib/mysql
 subPath: mysql
 - name: conf
 mountPath: /etc/mysql/conf.d
 resources:
 requests:
 cpu: 500m
 memory: 1Gi
 livenessProbe: exec:
 command: ["mysqladmin", "ping"]
 initialDelaySeconds: 30
 periodSeconds: 10
 timeoutSeconds: 5
 readinessProbe: exec: # Check we can execute queries over TCP (skip-networking is off).
 command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
 initialDelaySeconds: 5
 periodSeconds: 2
 timeoutSeconds: 1 - name: xtrabackup
 image: gcr.io/google-samples/xtrabackup:1.0
 ports: - name: xtrabackup
 containerPort: 3307
 command: - bash
 - "-c" - | set -ex
 cd /var/lib/mysql
 # Determine binlog position of cloned data, if any. if [[ -f xtrabackup_slave_info ]]; then # XtraBackup already generated a partial "CHANGE MASTER TO" query # because we're cloning from an existing slave.
 mv xtrabackup_slave_info change_master_to.sql.in # Ignore xtrabackup_binlog_info in this case (it's useless).
 rm -f xtrabackup_binlog_info
 elif [[ -f xtrabackup_binlog_info ]]; then # We're cloning directly from master. Parse binlog position. [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
 rm xtrabackup_binlog_info
 echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
 MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in fi # Check if we need to complete a clone by starting replication. if [[ -f change_master_to.sql.in ]]; then
 echo "Waiting for mysqld to be ready (accepting connections)" until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done
 echo "Initializing replication from clone position" # In case of container restart, attempt this at-most-once.
 mv change_master_to.sql.in change_master_to.sql.orig
 mysql -h 127.0.0.1 <<EOF
 $(<change_master_to.sql.orig),
 MASTER_HOST='mysql-0.mysql',
 MASTER_USER='root',
 MASTER_PASSWORD='',
 MASTER_CONNECT_RETRY=10;
 START SLAVE;
 EOF
 fi # Start a server to send backups when requested by peers. exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
 "xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"
 volumeMounts: - name: data
 mountPath: /var/lib/mysql
 subPath: mysql
 - name: conf
 mountPath: /etc/mysql/conf.d
 resources:
 requests:
 cpu: 100m
 memory: 100Mi
 volumes: - name: conf
 emptyDir: {} - name: config-map
 configMap:
 name: mysql
 volumeClaimTemplates: - metadata:
 name: data
 spec:
 accessModes: ["ReadWriteOnce"]
 resources:
 requests:
 storage: 10Gi      

通過執行如下的指令可以檢視啟動過程:

$ kubectl get pods -l app=mysql --watch --namespace=kube-public      

在啟動後,應該能夠看到如下的資訊:

Kubernetes-部署高可用的MySQL 1、MySQL簡介 2、MySQL的高可用方案 3、安裝部署 4、了解有狀态Pod的初始化 5、MySQL部署環境驗證 6、擴容slave的數量

4、了解有狀态Pod的初始化

StatefulSet控制器按Pod的序号索引一次啟動一個Pod,控制器每個Pod指派一個唯一的、穩定的名稱,名稱的格式為<statefulset-name>-<ordinal-index>。在此示例中,Pod的名稱為mysql-0,此節點為master主節點;mysql-1和mysql-2,這兩個節點為slave從節點。

4.1 建立配置檔案

在開始啟動Pod規格中的任何容器之前,Pod首先會按照YAML配置中定義的順序運作初始化容器。

第一個初始化容器為init-mysql,将以順序索引建立MySQL配置檔案。

腳本從Pod名稱的結尾處擷取并确定它的順序索引,順序索引通過hostname指令擷取。然後,它會按照順序儲存在conf.d目錄下的server-id.cnf檔案中。此行為将StatefulSet控制器提供的唯一和穩定的身份辨別轉為mysql服務Id的域。在init-mysql容器中,腳本使用來自于ConfigMap中master.cnf或slave.cnf。

在此例子的拓撲關系中,存在一個MySQL master節點和多個MySQL slave節點,腳本簡單的指派順序0給主節點。這能夠保證MySQL主節點在建立從節點之前就已經準備就緒。

4.2 克隆已存在的資料

一般來說,當一個新的Pod加入進來作為從節點時,必須假設MySQL master已經有關于它的資料。也假設slave副本的日志必須重新開始的。這些假設對于StatefulSet的擴縮容是很關鍵。

第二個初始化容器是clone-mysql,它在空的PersistentVolume上執行克隆從節點Pod的行為。這意味着它将從已在運作的Pod中拷貝資料,是以,它的目前狀态能夠與從master開始的副本節點一緻。

MySQL自身并沒有提供能夠做到上述能力的機制,是以,此例子使用開源的Percona XtraBackup工具來實作。在克隆的過程中,為了對MySQL主節點影響的最小化,腳本會要求每一個新的Pod從順序索引值小的Pod中進行克隆。這樣做的原因是,StatefulSet控制器需要一直保證Pod N需要在Pod N+1之前準備就緒。

4.3 啟動副本

在初始化容器完成後,容器将正常運作。MySQL Pod由運作實際mysqld服務的MySQL容器組成,xtrabacekup容器隻是作為備份的工具。xtrabackup負責監控克隆資料檔案,并确定是否在從節點初始化MySQL副本。如果需要,它将等待MySQL就緒,然後執行 CHANGE MASTER TO和START SLAVE指令。

一旦一個從節點開始複制,它将記住MySQL master,并自動進行重新連接配接,因為從節點尋找主節點作為穩定DNS名稱(mysql-0.mysql),它們自動的發現主節點。最後,在啟動副本後,xtrabackup容器也監聽來自于其它Pod對資料克隆的請求。

5、MySQL部署環境驗證

1)通過運作一個臨時的容器(使用mysql:5.7鏡像),使用MySQL 用戶端發送測試請求給MySQL master節點(主機名為mysql-0.mysql;跨命名空間的話,主機名請使用mysql-0.mysql.kube-public)

$ kubectl run mysql-client --image=mysql:5.7 -it --rm --restart=Never -- mysql -h mysql-0.mysql.kube-public      
CREATE DATABASE demo; 
CREATE TABLE demo.messages (message VARCHAR(250)); 
INSERT INTO demo.messages VALUES ('hello');      

在master節點上建立demo資料庫,并建立一個隻有message字段的demo.messages的表,并為message字段插入hello值。

Kubernetes-部署高可用的MySQL 1、MySQL簡介 2、MySQL的高可用方案 3、安裝部署 4、了解有狀态Pod的初始化 5、MySQL部署環境驗證 6、擴容slave的數量

2)使用主機名為mysql-read來發送測試請求給伺服器:

$ kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never -- mysql -h mysql-read.kube-public      
Kubernetes-部署高可用的MySQL 1、MySQL簡介 2、MySQL的高可用方案 3、安裝部署 4、了解有狀态Pod的初始化 5、MySQL部署環境驗證 6、擴容slave的數量

6、擴容slave的數量

1)對于mysql副本,通過添加從節點進行擴容。

$ kubectl scale statefulset mysql --replicas=5 --namespace=kube-public       

2)通過下面的指令檢視新的Pod:

$ kubectl get pods -l app=mysql --watch --namespace=kube-public       
Kubernetes-部署高可用的MySQL 1、MySQL簡介 2、MySQL的高可用方案 3、安裝部署 4、了解有狀态Pod的初始化 5、MySQL部署環境驗證 6、擴容slave的數量

3)縮容也是無縫的:

$ kubectl scale statefulset mysql --replicas=3 --namespace=kube-public      

本文轉自kubernetes中文社群-

Kubernetes-基于RKE進行Kubernetes的安裝部署