WordPress est la plate-forme grand public pour l'édition et la publication de contenu Web. Dans ce didacticiel, je vais vous expliquer comment utiliser Kubernetes pour créer un déploiement WordPress à haute disponibilité (HA).
WordPress se compose de deux composants principaux : le serveur WordPress PHP et la base de données utilisée pour stocker les informations sur les utilisateurs, les publications et les données du site Web. Nous devons rendre les deux composants de l’ensemble de l’application à la fois hautement disponibles et tolérants aux pannes.
Gérer un service hautement disponible peut être difficile lorsque le matériel et les adresses changent : très difficile à maintenir. Avec Kubernetes et ses puissants composants réseau, nous pouvons déployer des sites WordPress et des bases de données MySQL hautement disponibles sans (presque) avoir à saisir une seule adresse IP.
Dans ce tutoriel, je vais vous montrer comment créer des classes de stockage, des services, des cartes de configuration et des collections dans Kubernetes, comment exécuter MySQL hautement disponible et comment monter un cluster WordPress hautement disponible sur un service de base de données. Si vous n'avez pas encore de cluster Kubernetes, vous pouvez facilement les trouver et les démarrer sur Amazon, Google ou Azure, ou l'utiliser sur n'importe quel serveur Rancher Kubernetes Engine (RKE)
Présentation de l'architecture
Laissons maintenant Jetons un coup d'œil brièvement aux technologies que nous utiliserons et à leurs capacités :
Stockage pour les fichiers d'application WordPress : stockage NFS avec sauvegardes de disque persistantes GCE
Cluster de base de données : MySQL
application avec xtrabackup pour la parité Niveau programme : Image WordPress DockerHub montée sur le stockage NFS
Équilibrage de charge et mise en réseau : équilibreur de charge et réseau de services basés sur Kubernetes
L'architecture ressemble à ceci :
Création de classes de stockage, de services et de mappages de configuration dans les K8
Dans Kubernetes, les ensembles d'états permettent de définir l'ordre d'initialisation des pods. Nous utiliserons une collection MySQL avec état car elle garantit que nos nœuds de données disposent de suffisamment de temps pour répliquer les enregistrements des pods précédents au démarrage. Nous configurons cet ensemble d'états de manière à ce que le maître MySQL soit démarré avant les autres machines esclaves. Ainsi, lors d'une mise à l'échelle, les clones peuvent être envoyés directement du maître aux machines esclaves.
Tout d'abord, nous devons créer une classe de stockage de volume persistant et une carte de configuration pour appliquer la configuration maître-esclave selon les besoins. Nous utilisons des volumes persistants pour empêcher les données de la base de données d'être liées à des pods spécifiques du cluster. Cette approche empêche la base de données de perdre des données en cas de perte du pod hôte MySQL. Lorsque le pod hôte est perdu, il peut se reconnecter à la machine esclave avec xtrabackup et copier les données de la machine esclave vers l'hôte. La réplication MySQL est responsable de la réplication hôte-esclave et xtrabackup est responsable de la réplication hôte-esclave.
Pour allouer dynamiquement des volumes persistants, nous créons une classe de stockage utilisant des disques persistants GCE. Cependant, Kubernetes propose diverses solutions de stockage pour les volumes persistants :
# storage-class.yamlkind: StorageClassapiVersion: storage.k8s.io/v1metadata: name: slowprovisioner: kubernetes.io/gce-pdparameters: type: pd-standard zone: us-central1-a
Créez une classe et déployez-la à l'aide de la commande : $ kubectl create -f storage-class.yaml
.
Ensuite, nous allons créer une carte de configuration, qui spécifie certaines variables définies dans le fichier de configuration MySQL. Ces différentes configurations sont choisies par le pod lui-même, mais elles nous fournissent également un moyen pratique de gérer les variables de configuration potentielles.
Créez un fichier YAML nommé mysql-configmap.yaml
pour gérer la configuration comme suit :
# 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
Créez configmap
et déployez-le à l'aide de la commande : $ kubectl create -f mysql-configmap.yaml
.
Ensuite, nous souhaitons configurer le service pour que les pods MySQL puissent communiquer entre eux, et que nos pods WordPress puissent communiquer avec MySQL en utilisant mysql-services.yaml
. Cela démarre également l'équilibreur de charge de service pour le service 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
Avec cette déclaration de service, nous avons jeté les bases de l'implémentation d'un cluster d'instances MySQL multi-écritures et multi-lectures. Cette configuration est nécessaire car chaque instance WordPress peut écrire dans la base de données, chaque nœud doit donc être prêt à lire et à écrire.
Exécutez la commande $ kubectl create -f mysql-services.yaml
pour créer le service ci-dessus.
Jusqu'à présent, nous avons créé la classe de stockage de déclaration de volume, qui remet les disques persistants à tous les conteneurs qui les demandent, nous avons configuré configmap
, défini certaines variables dans le fichier de configuration MySQL et nous avons configuré un réseau Un service de couche est fourni, qui est responsable des demandes d'équilibrage de charge vers le serveur MySQL. Ce qui précède n'est qu'un cadre pour préparer des ensembles avec état là où le serveur MySQL s'exécute réellement, nous continuerons à en discuter ensuite.
配置有状态集的MySQL
本节中,我们将编写一个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="Comment exécuter WordPress et MySQL hautement disponibles sur Kubernetes"></p><p><img src="https://img.php.cn/upload/image/229/961/277/1623137243449231.png" title="1623137243449231.png" alt="Comment exécuter WordPress et MySQL hautement disponibles sur Kubernetes"></p><p><img src="https://img.php.cn/upload/image/737/299/982/1623137248223155.png" title="1623137248223155.png" alt="Comment exécuter WordPress et MySQL hautement disponibles sur Kubernetes"></p><p><img src="https://img.php.cn/upload/image/282/959/213/1623137253150095.png" title="1623137253150095.png" alt="Comment exécuter WordPress et MySQL hautement disponibles sur Kubernetes"></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服务需要不断使用物理卷,该卷将保留下来,并且不会被回收或错误分配。
Déployez une instance WordPress à l'aide de la commande $ kubectl create -f wordpress.yaml
. Le déploiement par défaut n'exécutera qu'une seule instance WordPress. Vous pouvez utiliser la commande $ kubectl scale --replicas=<number of="" replicas=""></number>
deployment/wordpress
pour augmenter le nombre d'instances WordPress.
Pour obtenir l'adresse de l'équilibreur de charge du service WordPress, vous devez accéder à WordPress en tapant $ kubectl get services wordpress
et en obtenant le champ EXTERNAL-IP à partir des résultats.
Tests de résilience
OK, maintenant que nous avons déployé les services, démontons-les et voyons comment notre architecture haute disponibilité gère le désordre. Dans ce déploiement, le seul point de défaillance restant est le service NFS (pour des raisons résumées dans la conclusion en fin d'article). Vous devriez pouvoir tester n'importe quel autre service pour voir comment l'application répond. Maintenant, j'ai démarré trois répliques du service WordPress, ainsi qu'un nœud maître et deux nœuds esclaves du service MySQL.
Tout d’abord, tuons les autres et ne laissons qu’un seul nœud WordPress pour voir comment l’application répond:$ kubectl scale --replicas=1 deployment/wordpress
Nous devrions désormais constater une diminution du nombre de pods déployés par WordPress. $ kubectl get pods
Vous devriez voir les pods WordPress fonctionner à 1/1.
Cliquez sur l'IP du service WordPress et nous verrons le même site et la même base de données qu'avant. Si vous souhaitez prolonger la récupération, vous pouvez utiliser $ kubectl scale --replicas=3 deployment/wordpress
Encore une fois, nous pouvons voir que le paquet est laissé dans trois instances.
Ci-dessous pour tester l'ensemble d'états MySQL, nous utilisons la commande pour réduire le nombre de sauvegardes : $ kubectl scale statefulsets mysql --replicas=1
Nous verrons que deux esclaves sont perdus de l'instance. Si le nœud maître est perdu à ce moment-là, les données qu'il contient seront enregistrées sur le disque persistant GCE. Cependant, les données doivent être restaurées manuellement à partir du disque.
Si les trois nœuds MySQL sont en panne, la réplication ne sera pas possible lorsque de nouveaux nœuds apparaîtront. Cependant, si un nœud maître tombe en panne, un nouveau nœud maître est automatiquement démarré et les données des nœuds esclaves sont reconfigurées via xtrabackup. Par conséquent, lors de l’exécution d’une base de données de production, je ne recommanderais pas de l’exécuter avec un facteur de réplication inférieur à 3. Dans le paragraphe de conclusion, nous parlerons des meilleures solutions pour les données avec état, puisque Kubernetes n'est pas vraiment conçu pour l'état.
Conclusion et recommandations
À présent, vous avez terminé la création et le déploiement d'une installation WordPress et MySQL hautement disponible sur Kubernetes !
Mais malgré ces résultats, votre parcours de recherche est peut-être loin d’être terminé. Au cas où vous ne l'auriez pas remarqué, notre installation présente toujours un seul point de défaillance : le serveur NFS partageant le répertoire /var/www/html
entre les pods WordPress. Ce service représente un point de défaillance unique car s'il n'est pas exécuté, le répertoire html sera manquant sur les pods qui l'utilisent. Dans le tutoriel, nous avons choisi une image très stable pour le serveur, qui peut être utilisée dans un environnement de production, mais pour un déploiement réel en production, vous pouvez envisager d'utiliser GlusterFS pour activer la multi-lecture et la multi-écriture sur le répertoire partagé par WordPress. exemple.
Ce processus implique l'exécution d'un cluster de stockage distribué sur Kubernetes, qui n'est pas réellement construit avec Kubernetes. Ainsi, même s'il fonctionne bien, il n'est pas idéal pour les déploiements à long terme.
Pour la base de données, je recommande personnellement d'utiliser un service de base de données relationnelle gérée pour héberger l'instance MySQL, car qu'il s'agisse du CloudSQL de Google ou du RDS d'AWS, ils offrent une haute disponibilité et un traitement redondant à un prix plus raisonnable, et non devez vous soucier de l’intégrité des données. Kuberntes n'est pas conçu autour d'applications avec état, et tout état qui y est intégré est plutôt une réflexion après coup. Il existe aujourd'hui un certain nombre de solutions disponibles qui peuvent vous fournir l'assurance dont vous avez besoin lors du choix d'un service de base de données.
En d'autres termes, ce qui est introduit ci-dessus est un processus idéal qui crée un exemple Kubernetes pertinent et réaliste à partir de tutoriels Kubernetes et d'exemples trouvés sur le Web, et inclut toutes les nouvelles fonctionnalités de Kubernetes 1.8.x.
J'espère qu'à travers ce guide, vous pourrez vivre des expériences surprenantes lors du déploiement de WordPress et MySQL. Bien sûr, j'espère que tout fonctionne normalement.