WordPress ist die Mainstream-Plattform zum Bearbeiten und Veröffentlichen von Webinhalten. In diesem Tutorial erläutere ich, wie Sie mit Kubernetes eine WordPress-Bereitstellung mit hoher Verfügbarkeit (HA) erstellen.
WordPress besteht aus zwei Hauptkomponenten: dem WordPress-PHP-Server und der Datenbank, in der Benutzerinformationen, Beiträge und Website-Daten gespeichert werden. Wir müssen beide Komponenten der gesamten Anwendung gleichzeitig hochverfügbar und fehlertolerant machen.
Der Betrieb eines hochverfügbaren Dienstes kann schwierig sein, wenn sich Hardware und Adressen ändern: sehr schwierig zu warten. Mit Kubernetes und seinen leistungsstarken Netzwerkkomponenten können wir hochverfügbare WordPress-Sites und MySQL-Datenbanken bereitstellen, ohne (fast) eine einzige IP-Adresse eingeben zu müssen.
In diesem Tutorial zeige ich Ihnen, wie Sie Speicherklassen, Dienste, Konfigurationskarten und Sammlungen in Kubernetes erstellen, wie Sie hochverfügbares MySQL ausführen und wie Sie einen hochverfügbaren WordPress-Cluster in einen Datenbankdienst einbinden. Wenn Sie noch keinen Kubernetes-Cluster haben, können Sie diesen ganz einfach auf Amazon, Google oder Azure finden und starten oder Rancher Kubernetes Engine (RKE)
auf einem beliebigen Server verwenden Rancher Kubernetes Engine (RKE)
架构概述
现在我来简要介绍一下我们将要使用的技术及其功能:
WordPress应用程序文件的存储:具有GCE持久性磁盘备份的NFS存储
数据库集群:带有用于奇偶校验的xtrabackup的MySQL
应用程序级别:挂载到NFS存储的WordPress DockerHub映像
负载均衡和网络:基于Kubernetes的负载均衡器和服务网络
该体系架构如下所示:
在K8s中创建存储类、服务和配置映射
在Kubernetes中,状态集提供了一种定义pod初始化顺序的方法。我们将使用一个有状态的MySQL集合,因为它能确保我们的数据节点有足够的时间在启动时复制先前pods中的记录。我们配置这个状态集的方式可以让MySQL主机在其他附属机器之前先启动,因此当我们扩展时,可以直接从主机将克隆发送到附属机器上。
首先,我们需要创建一个持久卷存储类和配置映射,以根据需要应用主从配置。我们使用持久卷,避免数据库中的数据受限于集群中任何特定的pods。这种方式可以避免数据库在MySQL主机pod丢失的情况下丢失数据,当主机pod丢失时,它可以重新连接到带xtrabackup的附属机器,并将数据从附属机器拷贝到主机中。MySQL的复制负责主机-附属的复制,而xtrabackup负责附属-主机的复制。
要动态分配持久卷,我们使用GCE持久磁盘创建存储类。不过,Kubernetes提供了各种持久性卷的存储方案:
# storage-class.yamlkind: StorageClassapiVersion: storage.k8s.io/v1metadata: name: slowprovisioner: kubernetes.io/gce-pdparameters: type: pd-standard zone: us-central1-a
创建类,并且使用指令:$ kubectl create -f storage-class.yaml
部署它。
接下来,我们将创建configmap,它指定了一些在MySQL配置文件中设置的变量。这些不同的配置由pod本身选择有关,但它们也为我们提供了一种便捷的方式来管理潜在的配置变量。
创建名为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
创建configmap
并使用指令:$ kubectl create -f mysql-configmap.yaml
来部署它。
接下来我们要设置服务以便MySQL pods可以互相通信,并且我们的WordPress pod可以使用mysql-services.yaml
与MySQL通信。这也为MySQL服务启动了服务负载均衡器。
# 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
通过此服务声明,我们就为实现一个多写入、多读取的MySQL实例集群奠定了基础。这种配置是必要的,每个WordPress实例都可能写入数据库,所以每个节点都必须准备好读写。
执行命令 $ kubectl create -f mysql-services.yaml
来创建上述的服务。
到这为止,我们创建了卷声明存储类,它将持久磁盘交给所有请求它们的容器,我们配置了configmap
ArchitekturübersichtLassen Sie mich nun kurz die von uns verwendete Technologie und ihre Fähigkeiten vorstellen:
Speicherung von WordPress-Anwendungsdateien: NFS-Speicher mit GCE-Persistent-Disk-Backup
Datenbankcluster: mit für Parität bewährtem xtrabackup für MySQL Anwendungsebene: WordPress DockerHub-Image im NFS-Speicher bereitgestellt
Lastenausgleich und Netzwerk: Kubernetes-basierter Lastenausgleich und Servicenetzwerk 🎜🎜Die Architektur sieht folgendermaßen aus: 🎜 🎜 🎜 🎜In Kubernetes bieten Zustandssätze eine Möglichkeit, die Reihenfolge der Pod-Initialisierung zu definieren. Wir werden eine zustandsbehaftete MySQL-Sammlung verwenden, da diese sicherstellt, dass unsere Datenknoten beim Start genügend Zeit haben, Datensätze aus vorherigen Pods zu replizieren. Wir konfigurieren diesen Statussatz so, dass der MySQL-Master vor den anderen Slave-Maschinen gestartet wird, sodass bei der Skalierung Klone direkt vom Master an die Slave-Maschinen gesendet werden können. 🎜🎜Zuerst müssen wir eine persistente Volume-Speicherklasse und eine Konfigurationszuordnung erstellen, um die Master-Slave-Konfiguration nach Bedarf anzuwenden. Wir verwenden persistente Volumes, um zu verhindern, dass die Daten in der Datenbank an bestimmte Pods im Cluster gebunden werden. Dieser Ansatz verhindert, dass die Datenbank Daten verliert, wenn der MySQL-Host-Pod verloren geht. Wenn der Host-Pod verloren geht, kann er sich mit xtrabackup erneut mit dem Slave-Computer verbinden und die Daten vom Slave-Computer auf den Host kopieren. Die MySQL-Replikation ist für die Host-Slave-Replikation verantwortlich, und xtrabackup ist für die Slave-Host-Replikation verantwortlich. 🎜🎜Um persistente Volumes dynamisch zuzuweisen, erstellen wir eine Speicherklasse mit GCE Persistent Disk. Kubernetes bietet jedoch verschiedene Speicherlösungen für persistente Volumes: 🎜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 🎜 Erstellen Sie eine Klasse und stellen Sie sie mit dem Befehl bereit: <code>$ kubectl create -f storage-class.yaml</code>. 🎜🎜Als nächstes erstellen wir eine Konfigurationszuordnung, die einige Variablen angibt, die in der MySQL-Konfigurationsdatei festgelegt sind. Diese unterschiedlichen Konfigurationen werden vom Pod selbst ausgewählt, bieten uns aber auch eine bequeme Möglichkeit, potenzielle Konfigurationsvariablen zu verwalten. 🎜🎜Erstellen Sie eine YAML-Datei mit dem Namen <code>mysql-configmap.yaml</code>, um die Konfiguration wie folgt zu verwalten: 🎜<pre class="brush:php;toolbar:false"># 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> 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</ip>
Nach dem Login kopieren🎜Erstellen Sie configmap
und verwenden Sie den Befehl: $ kubectl create - f mysql-configmap.yaml
, um es bereitzustellen. 🎜🎜Als nächstes möchten wir Dienste einrichten, damit MySQL-Pods miteinander kommunizieren können und unsere WordPress-Pods über mysql-services.yaml
mit MySQL kommunizieren können. Dadurch wird auch der Dienst-Load-Balancer für den MySQL-Dienst gestartet. 🎜rrreee🎜Mit dieser Service-Deklaration haben wir den Grundstein für die Implementierung eines Multi-Write- und Multi-Read-MySQL-Instanzclusters gelegt. Diese Konfiguration ist notwendig, da jede WordPress-Instanz in die Datenbank schreiben darf und daher jeder Knoten zum Lesen und Schreiben bereit sein muss. 🎜🎜Führen Sie den Befehl $ kubectl create -f mysql-services.yaml
aus, um den oben genannten Dienst zu erstellen. 🎜🎜Bis zu diesem Punkt haben wir die Volume-Deklarationsspeicherklasse erstellt, die persistente Festplatten an alle Container weitergibt, die sie anfordern, wir haben die configmap
konfiguriert, einige Variablen in der MySQL-Konfigurationsdatei festgelegt und ein Netzwerk erstellt Der Layer-Dienst ist für den Lastenausgleich von Anforderungen an den MySQL-Server konfiguriert. Das Obige ist nur ein Framework zum Vorbereiten von Stateful-Sets. Wo der MySQL-Server tatsächlich läuft, werden wir als nächstes weiter diskutieren. 🎜配置有状态集的MySQL
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 🎜 Erstellen Sie eine Klasse und stellen Sie sie mit dem Befehl bereit: <code>$ kubectl create -f storage-class.yaml</code>. 🎜🎜Als nächstes erstellen wir eine Konfigurationszuordnung, die einige Variablen angibt, die in der MySQL-Konfigurationsdatei festgelegt sind. Diese unterschiedlichen Konfigurationen werden vom Pod selbst ausgewählt, bieten uns aber auch eine bequeme Möglichkeit, potenzielle Konfigurationsvariablen zu verwalten. 🎜🎜Erstellen Sie eine YAML-Datei mit dem Namen <code>mysql-configmap.yaml</code>, um die Konfiguration wie folgt zu verwalten: 🎜<pre class="brush:php;toolbar:false"># 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> 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</ip>
本节中,我们将编写一个YAML配置文件应用于使用了状态集的MySQL实例。
我们先定义我们的状态集:
1, 创建三个pods并将它们注册到MySQL服务上。
2, 按照下列模版定义每个pod:
♢ 为主机MySQL服务器创建初始化容器,命名为init-mysql
.
♢ 给这个容器使用mysql:5.7镜像
♢ 运行一个bash脚本来启动xtrabackup
♢ 为配置文件和configmap
挂载两个新卷
3, 为主机MySQL服务器创建初始化容器,命名为clone-mysql
.
♢ 为该容器使用Google Cloud Registry的xtrabackup:1.0
镜像
♢ 运行bash脚本来克隆上一个同级的现有xtrabackups
♢ 为数据和配置文件挂在两个新卷
♢ 该容器有效地托管克隆的数据,便于新的附属容器可以获取它
4, 为附属MySQL服务器创建基本容器
♢ 创建一个MySQL附属容器,配置它连接到MySQL主机
♢ 创建附属xtrabackup
容器,配置它连接到xtrabackup主机
5, 创建一个卷声明模板来描述每个卷,每个卷是一个10GB的持久磁盘
下面的配置文件定义了MySQL集群的主节点和附属节点的行为,提供了运行附属客户端的bash配置,并确保在克隆之前主节点能够正常运行。附属节点和主节点分别获得他们自己的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 <p>将该文件存为<code>mysql-statefulset.yaml</code>,输入<code>kubectl="" create="" -f="" mysql-statefulset.yaml</code>并让kubernetes部署你的数据库。<br>现在当你调用<code>$="" kubectl="" get="" pods</code>,你应该看到3个pods启动或者准备好,其中每个pod上都有两个容器。主节点pod表示为mysql-0,而附属的pods为<code>mysql-1</code>和<code>mysql-2</code>.让pods执行几分钟来确保<code>xtrabackup</code>服务在pod之间正确同步,然后进行wordpress的部署。<br>您可以检查单个容器的日志来确认没有错误消息抛出。 查看日志的命令为<code>$="" logs="" <container_name></container_name></code></p><p>主节点<code>xtrabackup</code>容器应显示来自附属的两个连接,并且日志中不应该出现任何错误。</p><h2>部署高可用的WordPress</h2><p>整个过程的最后一步是将我们的WordPress pods部署到集群上。为此我们希望为WordPress的服务和部署进行定义。</p><p>为了让WordPress实现高可用,我们希望每个容器运行时都是完全可替换的,这意味着我们可以终止一个,启动另一个而不需要对数据或服务可用性进行修改。我们也希望能够容忍至少一个容器的失误,有一个冗余的容器负责处理slack。</p><p>WordPress将重要的站点相关数据存储在应用程序目录<code>/var/www/html</code>中。对于要为同一站点提供服务的两个WordPress实例,该文件夹必须包含相同的数据。</p><p>当运行高可用WordPress时,我们需要在实例之间共享<code>/var/www/html</code>文件夹,因此我们定义一个NGS服务作为这些卷的挂载点。<br>下面是设置NFS服务的配置,我提供了纯英文的版本:</p><p><img src="https://img.php.cn/upload/image/456/243/673/1623137239811988.png" title="1623137239811988.png" alt="So führen Sie hochverfügbares WordPress und MySQL auf Kubernetes aus"></p><p><img src="https://img.php.cn/upload/image/229/961/277/1623137243449231.png" title="1623137243449231.png" alt="So führen Sie hochverfügbares WordPress und MySQL auf Kubernetes aus"></p><p><img src="https://img.php.cn/upload/image/737/299/982/1623137248223155.png" title="1623137248223155.png" alt="So führen Sie hochverfügbares WordPress und MySQL auf Kubernetes aus"></p><p><img src="https://img.php.cn/upload/image/282/959/213/1623137253150095.png" title="1623137253150095.png" alt="So führen Sie hochverfügbares WordPress und MySQL auf Kubernetes aus"></p><p>使用指令<code>$ kubectl create -f nfs.yaml</code>部署NFS服务。现在,我们需要运行<code>$ kubectl describe services nfs-server</code>获得IP地址,这在后面会用到。</p><p>注意:将来,我们可以使用服务名称讲这些绑定在一起,但现在你需要对IP地址进行硬编码。</p><pre class="brush:php;toolbar:false"># 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> 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</ip>
我们现在创建了一个持久卷声明,和我们之前创建的NFS服务建立映射,然后将卷附加到WordPress pod上,即/var/www/html
根目录,这也是WordPress安装的地方。这里保留了集群中WordPress pods的所有安装和环境。有了这些配置,我们就可以对任何WordPress节点进行启动和拆除,而数据能够留下来。因为NFS服务需要不断使用物理卷,该卷将保留下来,并且不会被回收或错误分配。
Verwenden Sie den Befehl $ kubectl create -f wordpress.yaml
, um eine WordPress-Instanz bereitzustellen. Bei der Standardbereitstellung wird nur eine WordPress-Instanz ausgeführt. Sie können den Befehl $ kubectl scale --replicas=<number of="" replicas=""></number>
$ kubectl create -f wordpress.yaml
部署WordPress实例。默认部署只会运行一个WordPress实例,可以使用指令$ kubectl scale --replicas=<number of="" replicas=""></number>
deployment/wordpress
扩展WordPress实例数量。
要获得WordPress服务负载均衡器的地址,你需要输入$ kubectl get services wordpress
并从结果中获取EXTERNAL-IP字段来导航到WordPress。
弹性测试
OK,现在我们已经部署好了服务,那我们来拆除一下它们,看看我们的高可用架构如何处理这些混乱。在这种部署方式中,唯一剩下的单点故障就是NFS服务(原因总结在文末结论中)。你应该能够测试其他任何的服务来了解应用程序是如何响应的。现在我已经启动了WordPress服务的三个副本,以及MySQL服务中的一个主两个附属节点。
首先,我们先kill掉其他而只留下一个WordPress节点,来看看应用如何响应:$ kubectl scale --replicas=1 deployment/wordpress
现在我们应该看到WordPress部署的pod数量有所下降。$ kubectl get pods
应该能看到WordPress pods的运行变成了1/1。
点击WordPress服务IP,我们将看到与之前一样的站点和数据库。如果要扩展复原,可以使用$ kubectl scale --replicas=3 deployment/wordpress
再一次,我们可以看到数据包留在了三个实例中。
下面测试MySQL的状态集,我们使用指令缩小备份的数量:$ kubectl scale statefulsets mysql --replicas=1
我们会看到两个附属从该实例中丢失,如果主节点在此时丢失,它所保存的数据将保存在GCE持久磁盘上。不过就必须手动从磁盘恢复数据。
如果所有三个MySQL节点都关闭了,当新节点出现时就无法复制。但是,如果一个主节点发生故障,一个新的主节点就会自动启动,并且通过xtrabackup重新配置来自附属节点的数据。因此,在运行生产数据库时,我不建议以小于3的复制系数来运行。在结论段中,我们会谈谈针对有状态数据有什么更好的解决方案,因为Kubernetes并非真正是为状态设计的。
结论和建议
到现在为止,你已经完成了在Kubernetes构建并部署高可用WordPress和MySQL的安装!
不过尽管取得了这样的效果,你的研究之旅可能还远没有结束。可能你还没注意到,我们的安装仍然存在着单点故障:NFS服务器在WordPress pods之间共享/var/www/html
deployment/wordpress Skalieren Sie die Anzahl der WordPress-Instanzen.
Um die Adresse des WordPress-Services-Load-Balancers zu erhalten, müssen Sie $ kubectl get services wordpress
Resilienztests
OK, jetzt, da wir die Dienste bereitgestellt haben, lassen Sie uns sie abreißen und sehen, wie unsere Hochverfügbarkeitsarchitektur mit dem Chaos umgeht. Bei dieser Bereitstellung ist der NFS-Dienst der einzige verbleibende Single Point of Failure (aus Gründen, die in der Schlussfolgerung am Ende des Artikels zusammengefasst sind). Sie sollten jeden anderen Dienst testen können, um zu sehen, wie die Anwendung reagiert. Jetzt habe ich drei Replikate des WordPress-Dienstes gestartet, sowie einen Master- und zwei Slave-Knoten des MySQL-Dienstes. Lassen Sie uns zunächst die anderen töten und nur einen WordPress-Knoten belassen, um zu sehen, wie die Anwendung reagiert.: $ kubectl Scale --replicas=1 Deployment/Wordpress
Jetzt sollten wir die Anzahl der Pods bei der WordPress-Bereitstellung sehen hat abgenommen. $ kubectl get pods
Sie sollten sehen, dass sich die Ausführung von WordPress-Pods auf 1/1 geändert hat.
Klicken Sie auf die WordPress-Dienst-IP und wir sehen die gleiche Website und Datenbank wie zuvor. Wenn Sie die Wiederherstellung skalieren möchten, können Sie $ kubectl scale --replicas=3bereitstellung/wordpress
verwenden. Auch hier können wir sehen, dass die Pakete in drei Instanzen übrig bleiben.