如何在Kubernetes上運(yùn)行高可用的WordPress和MySQL

下面由WordPress教程欄目給大家介紹在kubernetes上運(yùn)行高可用的wordpressmysql,希望對需要的朋友有所幫助!

WordPress是用于編輯和發(fā)布Web內(nèi)容的主流平臺。在本教程中,我將逐步介紹如何使用Kubernetes來構(gòu)建高可用性(HA)WordPress部署。


WordPress由兩個(gè)主要組件組成:WordPress php服務(wù)器和用于存儲用戶信息、帖子和網(wǎng)站數(shù)據(jù)的數(shù)據(jù)庫。我們需要讓整個(gè)應(yīng)用程序中這兩個(gè)組件在高可用的同時(shí)都具備容錯(cuò)能力。

在硬件和地址發(fā)生變化的時(shí)候,運(yùn)行高可用服務(wù)可能會很困難:非常難維護(hù)。借助Kubernetes以及其強(qiáng)大的網(wǎng)絡(luò)組件,我們可以部署高可用的WordPress站點(diǎn)和MySQL數(shù)據(jù)庫,而無需(幾乎無需)輸入單個(gè)IP地址。

在本教程中,我將向你展示如何在Kubernetes中創(chuàng)建存儲類、服務(wù)、配置映射和集合,如何運(yùn)行高可用MySQL,以及如何將高可用WordPress集群掛載到數(shù)據(jù)庫服務(wù)上。如果你還沒有Kubernetes集群,你可以在Amazon、Google或者azure上輕松找到并且啟動它們,或者在任意的服務(wù)器上使用rancher Kubernetes Engine (RKE)

架構(gòu)概述

現(xiàn)在我來簡要介紹一下我們將要使用的技術(shù)及其功能:
WordPress應(yīng)用程序文件的存儲:具有GCE持久性磁盤備份的NFS存儲
數(shù)據(jù)庫集群:帶有用于奇偶校驗(yàn)的xtrabackup的MySQL
應(yīng)用程序級別:掛載到NFS存儲的WordPress DockerHub映像
負(fù)載均衡和網(wǎng)絡(luò):基于Kubernetes的負(fù)載均衡器和服務(wù)網(wǎng)絡(luò)

該體系架構(gòu)如下所示:

如何在Kubernetes上運(yùn)行高可用的WordPress和MySQL

在K8s中創(chuàng)建存儲類、服務(wù)和配置映射

在Kubernetes中,狀態(tài)集提供了一種定義pod初始化順序的方法。我們將使用一個(gè)有狀態(tài)的MySQL集合,因?yàn)樗艽_保我們的數(shù)據(jù)節(jié)點(diǎn)有足夠的時(shí)間在啟動時(shí)復(fù)制先前pods中的記錄。我們配置這個(gè)狀態(tài)集的方式可以讓MySQL主機(jī)在其他附屬機(jī)器之前先啟動,因此當(dāng)我們擴(kuò)展時(shí),可以直接從主機(jī)將克隆發(fā)送到附屬機(jī)器上。

首先,我們需要?jiǎng)?chuàng)建一個(gè)持久卷存儲類和配置映射,以根據(jù)需要應(yīng)用主從配置。我們使用持久卷,避免數(shù)據(jù)庫中的數(shù)據(jù)受限于集群中任何特定的pods。這種方式可以避免數(shù)據(jù)庫在MySQL主機(jī)pod丟失的情況下丟失數(shù)據(jù),當(dāng)主機(jī)pod丟失時(shí),它可以重新連接到帶xtrabackup的附屬機(jī)器,并將數(shù)據(jù)從附屬機(jī)器拷貝到主機(jī)中。MySQL的復(fù)制負(fù)責(zé)主機(jī)-附屬的復(fù)制,而xtrabackup負(fù)責(zé)附屬-主機(jī)的復(fù)制。

要?jiǎng)討B(tài)分配持久卷,我們使用GCE持久磁盤創(chuàng)建存儲類。不過,Kubernetes提供了各種持久性卷的存儲方案:

# storage-class.yamlkind: StorageClassapiVersion: storage.k8s.io/v1metadata:  name: slowprovisioner: kubernetes.io/gce-pdparameters:  type: pd-standard  zone: us-central1-a

創(chuàng)建類,并且使用指令:$ kubectl create -f storage-class.yaml部署它。

接下來,我們將創(chuàng)建configmap,它指定了一些在MySQL配置文件中設(shè)置的變量。這些不同的配置由pod本身選擇有關(guān),但它們也為我們提供了一種便捷的方式來管理潛在的配置變量。

創(chuàng)建名為mysql-configmap.yaml的YAML文件來處理配置,如下:

# mysql-configmap.yamlapiVersion: v1kind: ConfigMapmetadata:  name: mysql  labels:    app: mysqldata:  master.cnf: |    # Apply this config only on the master.    [mysqld]    log-bin    skip-host-cache    skip-name-resolve  slave.cnf: |    # Apply this config only on slaves.    [mysqld]    skip-host-cache    skip-name-resolve

創(chuàng)建configmap并使用指令:$ kubectl create -f mysql-configmap.yaml
來部署它。

接下來我們要設(shè)置服務(wù)以便MySQL pods可以互相通信,并且我們的WordPress pod可以使用mysql-services.yaml與MySQL通信。這也為MySQL服務(wù)啟動了服務(wù)負(fù)載均衡器。

# mysql-services.yaml# Headless service for stable DNS entries of StatefulSet members.apiVersion: v1kind: Servicemetadata:  name: mysql  labels:    app: mysqlspec:  ports:  - name: mysql    port: 3306  clusterIP: None  selector:    app: mysql

通過此服務(wù)聲明,我們就為實(shí)現(xiàn)一個(gè)多寫入、多讀取的MySQL實(shí)例集群奠定了基礎(chǔ)。這種配置是必要的,每個(gè)WordPress實(shí)例都可能寫入數(shù)據(jù)庫,所以每個(gè)節(jié)點(diǎn)都必須準(zhǔn)備好讀寫。

執(zhí)行命令 $ kubectl create -f mysql-services.yaml來創(chuàng)建上述的服務(wù)。

到這為止,我們創(chuàng)建了卷聲明存儲類,它將持久磁盤交給所有請求它們的容器,我們配置了configmap,在MySQL配置文件中設(shè)置了一些變量,并且我們配置了一個(gè)網(wǎng)絡(luò)層服務(wù),負(fù)責(zé)對MySQL服務(wù)器請求的負(fù)載均衡。上面說的這些只是準(zhǔn)備有狀態(tài)集的框架, MySQL服務(wù)器實(shí)際在哪里運(yùn)行,我們接下來將繼續(xù)探討。

配置有狀態(tài)集的MySQL

本節(jié)中,我們將編寫一個(gè)YAML配置文件應(yīng)用于使用了狀態(tài)集的MySQL實(shí)例。
我們先定義我們的狀態(tài)集:
1, 創(chuàng)建三個(gè)pods并將它們注冊到MySQL服務(wù)上。
2, 按照下列模版定義每個(gè)pod:
? 為主機(jī)MySQL服務(wù)器創(chuàng)建初始化容器,命名為init-mysql.
? ?給這個(gè)容器使用mysql:5.7鏡像
? ?運(yùn)行一個(gè)bash腳本來啟動xtrabackup
? ?為配置文件和configmap掛載兩個(gè)新卷

3, 為主機(jī)MySQL服務(wù)器創(chuàng)建初始化容器,命名為clone-mysql.
? ?為該容器使用Google Cloud Registry的xtrabackup:1.0鏡像
? 運(yùn)行bash腳本來克隆上一個(gè)同級的現(xiàn)有xtrabackups
? ?為數(shù)據(jù)和配置文件掛在兩個(gè)新卷
? ?該容器有效地托管克隆的數(shù)據(jù),便于新的附屬容器可以獲取它

4, 為附屬M(fèi)ySQL服務(wù)器創(chuàng)建基本容器
? ?創(chuàng)建一個(gè)MySQL附屬容器,配置它連接到MySQL主機(jī)
? ?創(chuàng)建附屬xtrabackup容器,配置它連接到xtrabackup主機(jī)

5, 創(chuàng)建一個(gè)卷聲明模板來描述每個(gè)卷,每個(gè)卷是一個(gè)10GB的持久磁盤
下面的配置文件定義了MySQL集群的主節(jié)點(diǎn)和附屬節(jié)點(diǎn)的行為,提供了運(yùn)行附屬客戶端的bash配置,并確保在克隆之前主節(jié)點(diǎn)能夠正常運(yùn)行。附屬節(jié)點(diǎn)和主節(jié)點(diǎn)分別獲得他們自己的10GB卷,這是他們在我們之前定義的持久卷存儲類中請求的。

apiVersion: apps/v1beta1kind: StatefulSetmetadata:  name: mysqlspec:  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

將該文件存為mysql-statefulset.yaml,輸入kubectl=”” create=”” -f=”” mysql-statefulset.yaml并讓kubernetes部署你的數(shù)據(jù)庫。
現(xiàn)在當(dāng)你調(diào)用$=”” kubectl=”” get=”” pods,你應(yīng)該看到3個(gè)pods啟動或者準(zhǔn)備好,其中每個(gè)pod上都有兩個(gè)容器。主節(jié)點(diǎn)pod表示為mysql-0,而附屬的pods為mysql-1和mysql-2.讓pods執(zhí)行幾分鐘來確保xtrabackup服務(wù)在pod之間正確同步,然后進(jìn)行wordpress的部署。
您可以檢查單個(gè)容器的日志來確認(rèn)沒有錯(cuò)誤消息拋出。 查看日志的命令為$=”” logs=””

主節(jié)點(diǎn)xtrabackup容器應(yīng)顯示來自附屬的兩個(gè)連接,并且日志中不應(yīng)該出現(xiàn)任何錯(cuò)誤。

部署高可用的WordPress

整個(gè)過程的最后一步是將我們的WordPress pods部署到集群上。為此我們希望為WordPress的服務(wù)和部署進(jìn)行定義。

為了讓W(xué)ordPress實(shí)現(xiàn)高可用,我們希望每個(gè)容器運(yùn)行時(shí)都是完全可替換的,這意味著我們可以終止一個(gè),啟動另一個(gè)而不需要對數(shù)據(jù)或服務(wù)可用性進(jìn)行修改。我們也希望能夠容忍至少一個(gè)容器的失誤,有一個(gè)冗余的容器負(fù)責(zé)處理slack。

WordPress將重要的站點(diǎn)相關(guān)數(shù)據(jù)存儲在應(yīng)用程序目錄/var/www/html中。對于要為同一站點(diǎn)提供服務(wù)的兩個(gè)WordPress實(shí)例,該文件夾必須包含相同的數(shù)據(jù)。

當(dāng)運(yùn)行高可用WordPress時(shí),我們需要在實(shí)例之間共享/var/www/html文件夾,因此我們定義一個(gè)NGS服務(wù)作為這些卷的掛載點(diǎn)。
下面是設(shè)置NFS服務(wù)的配置,我提供了純英文的版本:

如何在Kubernetes上運(yùn)行高可用的WordPress和MySQL

如何在Kubernetes上運(yùn)行高可用的WordPress和MySQL

如何在Kubernetes上運(yùn)行高可用的WordPress和MySQL

如何在Kubernetes上運(yùn)行高可用的WordPress和MySQL

使用指令$ kubectl create -f nfs.yaml部署NFS服務(wù)?,F(xiàn)在,我們需要運(yùn)行$ kubectl describe services nfs-server獲得IP地址,這在后面會用到。

注意:將來,我們可以使用服務(wù)名稱講這些綁定在一起,但現(xiàn)在你需要對IP地址進(jìn)行硬編碼。

# wordpress.yamlapiVersion: v1kind: Servicemetadata:  name: wordpress  labels:    app: wordpressspec:  ports:    - port: 80  selector:    app: wordpress    tier: frontend  type: LoadBalancer---apiVersion: v1kind: PersistentVolumemetadata:  name: nfsspec:  capacity:    storage: 20G  accessModes:    - ReadWriteMany  nfs:    # FIXME: use the right IP    server: <ip of="" the="" nfs="" service="">    path: "/"---apiVersion: v1kind: PersistentVolumeClaimmetadata:  name: nfsspec:  accessModes:    - ReadWriteMany  storageClassName: ""  resources:    requests:      storage: 20G---apiVersion: apps/v1beta1 # for versions before 1.8.0 use apps/v1beta1kind: Deploymentmetadata:  name: wordpress  labels:    app: wordpressspec:  selector:    matchLabels:      app: wordpress      tier: frontend  strategy:    type: Recreate  template:    metadata:      labels:        app: wordpress        tier: frontend    spec:      containers:      - image: wordpress:4.9-apache        name: wordpress        env:        - name: WORDPRESS_DB_HOST          value: mysql        - name: WORDPRESS_DB_PASSWORD          value: ""        ports:        - containerPort: 80          name: wordpress        volumeMounts:        - name: wordpress-persistent-storage          mountPath: /var/www/html      volumes:      - name: wordpress-persistent-storage        persistentVolumeClaim:            claimName: nfs

我們現(xiàn)在創(chuàng)建了一個(gè)持久卷聲明,和我們之前創(chuàng)建的NFS服務(wù)建立映射,然后將卷附加到WordPress pod上,即/var/www/html根目錄,這也是WordPress安裝的地方。這里保留了集群中WordPress pods的所有安裝和環(huán)境。有了這些配置,我們就可以對任何WordPress節(jié)點(diǎn)進(jìn)行啟動和拆除,而數(shù)據(jù)能夠留下來。因?yàn)镹FS服務(wù)需要不斷使用物理卷,該卷將保留下來,并且不會被回收或錯(cuò)誤分配。

使用指令$ kubectl create -f wordpress.yaml部署WordPress實(shí)例。默認(rèn)部署只會運(yùn)行一個(gè)WordPress實(shí)例,可以使用指令$ kubectl scale –replicas=
deployment/wordpress擴(kuò)展WordPress實(shí)例數(shù)量。

要獲得WordPress服務(wù)負(fù)載均衡器的地址,你需要輸入$ kubectl get services wordpress
并從結(jié)果中獲取EXTERNAL-IP字段來導(dǎo)航到WordPress。

彈性測試

OK,現(xiàn)在我們已經(jīng)部署好了服務(wù),那我們來拆除一下它們,看看我們的高可用架構(gòu)如何處理這些混亂。在這種部署方式中,唯一剩下的單點(diǎn)故障就是NFS服務(wù)(原因總結(jié)在文末結(jié)論中)。你應(yīng)該能夠測試其他任何的服務(wù)來了解應(yīng)用程序是如何響應(yīng)的。現(xiàn)在我已經(jīng)啟動了WordPress服務(wù)的三個(gè)副本,以及MySQL服務(wù)中的一個(gè)主兩個(gè)附屬節(jié)點(diǎn)。

首先,我們先kill掉其他而只留下一個(gè)WordPress節(jié)點(diǎn),來看看應(yīng)用如何響應(yīng):$ kubectl scale –replicas=1 deployment/wordpress現(xiàn)在我們應(yīng)該看到WordPress部署的pod數(shù)量有所下降。$ kubectl get pods應(yīng)該能看到WordPress pods的運(yùn)行變成了1/1。

點(diǎn)擊WordPress服務(wù)IP,我們將看到與之前一樣的站點(diǎn)和數(shù)據(jù)庫。如果要擴(kuò)展復(fù)原,可以使用$ kubectl scale –replicas=3 deployment/wordpress再一次,我們可以看到數(shù)據(jù)包留在了三個(gè)實(shí)例中。

下面測試MySQL的狀態(tài)集,我們使用指令縮小備份的數(shù)量:$ kubectl scale statefulsets mysql –replicas=1我們會看到兩個(gè)附屬從該實(shí)例中丟失,如果主節(jié)點(diǎn)在此時(shí)丟失,它所保存的數(shù)據(jù)將保存在GCE持久磁盤上。不過就必須手動從磁盤恢復(fù)數(shù)據(jù)。

如果所有三個(gè)MySQL節(jié)點(diǎn)都關(guān)閉了,當(dāng)新節(jié)點(diǎn)出現(xiàn)時(shí)就無法復(fù)制。但是,如果一個(gè)主節(jié)點(diǎn)發(fā)生故障,一個(gè)新的主節(jié)點(diǎn)就會自動啟動,并且通過xtrabackup重新配置來自附屬節(jié)點(diǎn)的數(shù)據(jù)。因此,在運(yùn)行生產(chǎn)數(shù)據(jù)庫時(shí),我不建議以小于3的復(fù)制系數(shù)來運(yùn)行。在結(jié)論段中,我們會談?wù)勧槍τ袪顟B(tài)數(shù)據(jù)有什么更好的解決方案,因?yàn)镵ubernetes并非真正是為狀態(tài)設(shè)計(jì)的。

結(jié)論和建議

到現(xiàn)在為止,你已經(jīng)完成了在Kubernetes構(gòu)建并部署高可用WordPress和MySQL的安裝!

不過盡管取得了這樣的效果,你的研究之旅可能還遠(yuǎn)沒有結(jié)束??赡苣氵€沒注意到,我們的安裝仍然存在著單點(diǎn)故障:NFS服務(wù)器在WordPress pods之間共享/var/www/html目錄。這項(xiàng)服務(wù)代表了單點(diǎn)故障,因?yàn)槿绻鼪]有運(yùn)行,在使用它的pods上html目錄就會丟失。教程中我們?yōu)榉?wù)器選擇了非常穩(wěn)定的鏡像,可以在生產(chǎn)環(huán)境中使用,但對于真正的生產(chǎn)部署,你可以考慮使用GlusterFS對WordPress實(shí)例共享的目錄開啟多讀多寫。

這個(gè)過程涉及在Kubernetes上運(yùn)行分布式存儲集群,實(shí)際上這不是Kubernetes構(gòu)建的,因此盡管它運(yùn)行良好,但不是長期部署的理想選擇。

對于數(shù)據(jù)庫,我個(gè)人建議使用托管的關(guān)系數(shù)據(jù)庫服務(wù)來托管MySQL實(shí)例,因?yàn)闊o論是Google的CloudSQL還是AWS的RDS,它們都以更合理的價(jià)格提供高可用和冗余處理,并且不需擔(dān)心數(shù)據(jù)的完整性。Kuberntes并不是圍繞有狀態(tài)的應(yīng)用程序設(shè)計(jì)的,任何建立在其中的狀態(tài)更多都是事后考慮。目前有大量的解決方案可以在選擇數(shù)據(jù)庫服務(wù)時(shí)提供所需的保證。

也就是說,上面介紹的是一種理想的流程,由Kubernetes教程、web中找到的例子創(chuàng)建一個(gè)有關(guān)聯(lián)的現(xiàn)實(shí)的Kubernetes例子,并且包含了Kubernetes 1.8.x中所有的新特性。

我希望通過這份指南,你能在部署WordPress和MySQL時(shí)獲得一些驚喜的體驗(yàn),當(dāng)然,更希望你的運(yùn)行一切正常。

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊11 分享