Si vous avez été dans le monde des conteneurs, vous sauriez qu'il est assez difficile d'adopter une automatisation complète de Kubernetes pour un système de base de données en cluster, ce qui ajoute généralement un niveau de complexité au conteneur. architecture pour ces applications avec état. C'est là qu'un opérateur Kubernetes peut nous aider à résoudre ce problème. Un opérateur Kubernetes est un type spécial de contrôleur introduit pour simplifier les déploiements complexes qui étend essentiellement l'API Kubernetes avec des ressources personnalisées. Il s'appuie sur les concepts de base des ressources et des contrôleurs Kubernetes, mais inclut des connaissances spécifiques au domaine ou à l'application pour automatiser l'ensemble du cycle de vie du logiciel qu'il gère.
Percona XtraDB Cluster Operator est un moyen astucieux d'automatiser les tâches spécifiques de Percona XtraDB Cluster telles que le déploiement, la mise à l'échelle, les sauvegardes et les mises à niveau au sein de Kubernetes, construit et maintenu par Percona. Il déploie le cluster dans un StatefulSet avec un volume persistant, ce qui nous permet de maintenir une identité cohérente pour chaque pod du cluster et nos données à conserver.
Dans cet article de blog, nous allons tester le déploiement de Percona XtraDB Cluster 8.0 dans un environnement conteneurisé, orchestré par Percona XtraDB Cluster Kubernetes Operator sur Google Cloud Platform.
Créer un cluster Kubernetes sur Google Cloud
Dans cette procédure pas à pas, nous allons utiliser le cluster Kubernetes sur Google Cloud, car il est relativement simple et facile de faire fonctionner Kubernetes. Connectez-vous à votre tableau de bord Google Cloud Platform -> Calcul -> Kubernetes Engine -> Créer un cluster, et la boîte de dialogue suivante s'affichera :
Entrez simplement le nom du cluster Kubernetes, choisissez votre zone préférée et cliquez sur "CRÉER " (au bas de la page). Dans 5 minutes, un cluster Kubernetes à 3 nœuds sera prêt. Maintenant, sur votre poste de travail, installez le SDK gcloud comme indiqué dans ce guide, puis importez la configuration Kubernetes sur votre poste de travail :
$ gcloud container clusters get-credentials my-k8s-cluster --zone asia-northeast1-a --project s9s-qa
Fetching cluster endpoint and auth data.
kubeconfig entry generated for my-k8s-cluster.
Vous devriez pouvoir vous connecter au cluster Kubernetes à ce stade. Exécutez la commande suivante pour vérifier :
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-my-k8s-cluster-default-pool-b80902cd-gp09 Ready <none> 139m v1.16.13-gke.401
gke-my-k8s-cluster-default-pool-b80902cd-jdc3 Ready <none> 139m v1.16.13-gke.401
gke-my-k8s-cluster-default-pool-b80902cd-rdv8 Ready <none> 139m v1.16.13-gke.401
La sortie ci-dessus signifie que nous pouvons nous connecter au maître Kubernetes et récupérer les nœuds du cluster Kubernetes. Nous sommes maintenant prêts à exécuter les charges de travail Kubernetes.
Déployer un cluster Percona XtraDB sur Kubernetes
Pour le déploiement de la charge de travail, nous allons suivre les instructions indiquées dans la documentation Percona XtraDB Cluster Operator. Fondamentalement, nous exécutons la commande suivante sur notre poste de travail pour créer les ressources personnalisées, l'espace de noms, le contrôle d'accès basé sur les rôles et également l'opérateur Kubernetes lui-même :
$ git clone -b v1.6.0 https://github.com/percona/percona-xtradb-cluster-operator
$ cd percona-xtradb-cluster-operator/
$ kubectl apply -f deploy/crd.yaml
$ kubectl create namespace pxc
$ kubectl config set-context $(kubectl config current-context) --namespace=pxc
$ kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value core/account)
$ kubectl apply -f deploy/rbac.yaml
$ kubectl apply -f deploy/operator.yaml
Ensuite, nous devons préparer nos mots de passe (cela s'appelle Secrets dans le terme Kubernetes) en mettant à jour les valeurs dans deploy/secrets.yaml dans un format encodé en base64. Vous pouvez utiliser des outils en ligne comme https://www.base64encode.org/ pour en créer un ou utiliser un outil de ligne de commande comme celui-ci :
$ echo -n 'mypassword' | base64
bXlwYXNzd29yZA==
Ensuite, mettez à jour le fichier deploy/secrets.yaml, comme indiqué ci-dessous :
apiVersion: v1
kind: Secret
metadata:
name: my-cluster-secrets
type: Opaque
data:
root: bXlwYXNzd29yZA==
xtrabackup: bXlwYXNzd29yZA==
monitor: bXlwYXNzd29yZA==
clustercheck: bXlwYXNzd29yZA==
proxyadmin: bXlwYXNzd29yZA==
pmmserver: bXlwYXNzd29yZA==
operator: bXlwYXNzd29yZA==
Ce qui précède est une super simplification de la gestion des secrets, où nous définissons tous les mots de passe pour qu'ils soient identiques pour tous les utilisateurs. En production, veuillez utiliser un mot de passe plus complexe et spécifier un mot de passe différent pour chaque utilisateur.
Maintenant, nous pouvons envoyer la configuration secrète à Kubernetes :
$ kubectl apply -f deploy/secrets.yaml
Avant de poursuivre le déploiement d'un cluster Percona XtraDB, nous devons revoir la définition de déploiement par défaut dans deploy/cr.yaml pour le cluster. De nombreux objets Kubernetes sont définis ici, mais la plupart d'entre eux sont commentés. Pour notre charge de travail, nous ferions la modification comme ci-dessous :
$ cat deploy/cr.yaml
apiVersion: pxc.percona.com/v1-6-0
kind: PerconaXtraDBCluster
metadata:
name: cluster1
finalizers:
- delete-pxc-pods-in-order
spec:
crVersion: 1.6.0
secretsName: my-cluster-secrets
vaultSecretName: keyring-secret-vault
sslSecretName: my-cluster-ssl
sslInternalSecretName: my-cluster-ssl-internal
allowUnsafeConfigurations: false
updateStrategy: SmartUpdate
upgradeOptions:
versionServiceEndpoint: https://check.percona.com
apply: recommended
schedule: "0 4 * * *"
pxc:
size: 3
image: percona/percona-xtradb-cluster:8.0.20-11.1
configuration: |
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
collation-server = utf8_unicode_ci
character-set-server = utf8
default_authentication_plugin = mysql_native_password
resources:
requests:
memory: 1G
affinity:
antiAffinityTopologyKey: "kubernetes.io/hostname"
podDisruptionBudget:
maxUnavailable: 1
volumeSpec:
persistentVolumeClaim:
resources:
requests:
storage: 6Gi
gracePeriod: 600
haproxy:
enabled: true
size: 3
image: percona/percona-xtradb-cluster-operator:1.6.0-haproxy
resources:
requests:
memory: 1G
affinity:
antiAffinityTopologyKey: "kubernetes.io/hostname"
podDisruptionBudget:
maxUnavailable: 1
gracePeriod: 30
backup:
image: percona/percona-xtradb-cluster-operator:1.6.0-pxc8.0-backup
storages:
fs-pvc:
type: filesystem
volume:
persistentVolumeClaim:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 6Gi
schedule:
- name: "daily-backup"
schedule: "0 0 * * *"
keep: 5
storageName: fs-pvc
Nous avons apporté quelques modifications au cr.yaml fourni pour le faire fonctionner avec notre application, comme indiqué ci-dessus. Tout d'abord, nous devons commenter (ou supprimer) toutes les lignes liées au CPU, par exemple [*].resources.requests.cpu:600m, pour nous assurer que Kubernetes est en mesure de planifier correctement la création du pod sur les nœuds avec un CPU limité. Ensuite, nous devons ajouter des options de compatibilité pour Percona XtraDB Cluster 8.0 qui est basé sur MySQL 8.0, pour fonctionner en douceur avec notre application WordPress que nous allons déployer plus tard, comme le montre l'extrait suivant :
Configuration configuration: |
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
collation-server = utf8_unicode_ci
character-set-server = utf8
default_authentication_plugin = mysql_native_password
Ce qui précède correspondra au jeu de caractères par défaut du serveur MySQL avec le pilote PHP MySQLi dans notre conteneur WordPress. La section suivante est le déploiement HAProxy où il est défini sur "enabled:true". Il existe également une section ProxySQL avec "enabled:false" - généralement, on choisirait l'un des proxys inverses pour chaque cluster. La dernière section est la configuration de la sauvegarde, où nous aimerions avoir une sauvegarde quotidienne planifiée à 12h00 tous les jours et conserver les 5 dernières sauvegardes.
Nous pouvons maintenant commencer à déployer notre cluster Percona XtraDB à 3 nœuds :
$ kubectl apply -f deploy/cr.yaml
Le processus de création prendra un certain temps. L'opérateur déploiera les pods du cluster Percona XtraDB en tant qu'ensemble avec état, ce qui signifie qu'une création de pod à la fois et chaque pod dans le StatefulSet se verra attribuer un ordinal entier, de 0 à N-1, qui est unique sur l'ensemble. Le processus est terminé lorsque l'opérateur et les pods ont atteint leur état d'exécution :
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
cluster1-haproxy-0 2/2 Running 0 71m
cluster1-haproxy-1 2/2 Running 0 70m
cluster1-haproxy-2 2/2 Running 0 70m
cluster1-pxc-0 1/1 Running 0 71m
cluster1-pxc-1 1/1 Running 0 70m
cluster1-pxc-2 1/1 Running 0 69m
percona-xtradb-cluster-operator-79d786dcfb-6clld 1/1 Running 0 121m
Étant donné que cet opérateur est une ressource personnalisée, nous pouvons manipuler la ressource perconaxtradbcluster pour qu'elle ressemble à la ressource Kubernetes standard :
$ kubectl get perconaxtradbcluster
NAME ENDPOINT STATUS PXC PROXYSQL HAPROXY AGE
cluster1 cluster1-haproxy.pxc ready 3 3 27h
Vous pouvez également utiliser le nom de ressource plus court, "pxc", et essayer avec les commandes suivantes :
$ kubectl describe pxc
$ kubectl edit pxc
En regardant l'ensemble de charge de travail, nous pouvons dire que l'opérateur a créé deux StatefulSets :
$ kubectl get statefulsets -o wide
NAME READY AGE CONTAINERS IMAGES
cluster1-haproxy 3/3 26h haproxy,pxc-monit percona/percona-xtradb-cluster-operator:1.6.0-haproxy,percona/percona-xtradb-cluster-operator:1.6.0-haproxy
cluster1-pxc 3/3 26h pxc percona/percona-xtradb-cluster:8.0.20-11.2
L'opérateur créera également les services correspondants qui équilibreront la charge des connexions aux pods respectifs :
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cluster1-haproxy ClusterIP 10.40.9.177 <none> 3306/TCP,3309/TCP,33062/TCP 3h27m
cluster1-haproxy-replicas ClusterIP 10.40.0.236 <none> 3306/TCP 3h27m
cluster1-pxc ClusterIP None <none> 3306/TCP,33062/TCP 3h27m
cluster1-pxc-unready ClusterIP None <none> 3306/TCP,33062/TCP 3h27m
La sortie ci-dessus montre que l'opérateur a créé 4 services :
- cluster1-haproxy - Le service pour un maître unique MySQL à charge équilibrée (3306), le protocole Proxy (3309) et MySQL Admin (33062) - Un nouveau port administratif introduit dans MySQL 8.0.14 et versions ultérieures. Il s'agit du nom de service ou de l'adresse IP du cluster auquel les applications doivent se connecter pour avoir une connexion maître unique au cluster Galera.
- cluster1-haproxy-replicas - Le service pour un multi-maître MySQL à charge équilibrée (3306). Il s'agit du nom de service ou de l'adresse IP du cluster auquel les applications doivent se connecter pour avoir une connexion multimaître au cluster Galera avec un algorithme d'équilibrage à tour de rôle.
- cluster1-pxc - Le service pour les pods PXC à charge équilibrée, en contournant HAProxy. En se connectant directement à ce service, Kubernetes acheminera la connexion de manière circulaire vers tous les pods PXC, comme ce que fournit cluster-haproxy-replicase. Le service n'a pas d'adresse IP publique attribuée et n'est pas disponible en dehors du cluster.
- cluster1-pxc-unready - Le service "non prêt" est nécessaire pour la découverte d'adresse de pod lors du démarrage de l'application, quel que soit l'état du pod. Les pods proxysql et pxc doivent se connaître avant que la base de données ne devienne pleinement opérationnelle. Le service non prêt n'a pas d'adresse IP publique attribuée et n'est pas disponible en dehors du cluster.
Pour vous connecter via un client MySQL, exécutez simplement la commande suivante :
$ kubectl run -i --rm --tty percona-client --image=percona:8.0 --restart=Never -- bash -il
Cela créera un pod transitoire et entrera immédiatement dans l'environnement du conteneur. Ensuite, exécutez la commande client mysql standard avec les informations d'identification appropriées :
bash-4.2$ mysql -uroot -pmypassword -h cluster1-haproxy -P3306 -e 'SELECT @@hostname'
mysql: [Warning] Using a password on the command line interface can be insecure.
+----------------+
| @@hostname |
+----------------+
| cluster1-pxc-0 |
+----------------+
Lorsque nous examinons le placement des pods, tous les pods du cluster Percona XtraDB sont situés sur un hôte Kubernetes différent :
$ kubectl get pods -o wide --selector=app.kubernetes.io/component=pxc
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cluster1-pxc-0 1/1 Running 0 67m 10.36.2.5 gke-my-k8s-cluster-default-pool-b80902cd-gp09 <none> <none>
cluster1-pxc-1 1/1 Running 0 66m 10.36.1.10 gke-my-k8s-cluster-default-pool-b80902cd-rdv8 <none> <none>
cluster1-pxc-2 1/1 Running 0 65m 10.36.0.11 gke-my-k8s-cluster-default-pool-b80902cd-jdc3 <none> <none>
Cela améliorera certainement la disponibilité du service, au cas où l'un des hôtes Kubernetes tomberait en panne.
Pour évoluer jusqu'à 5 pods, nous devons au préalable préparer 2 nouveaux nœuds Kubernetes supplémentaires afin de respecter la configuration d'affinité du pod (par défaut, affinity.antiAffinityTopologyKey.topologyKey="kubernetes.io/hostname"). Ensuite, exécutez la commande de correctif suivante pour faire évoluer le cluster Percona XtraDB à 5 nœuds :
$ kubectl patch pxc cluster1 \
--type='json' -p='[{"op": "replace", "path": "/spec/pxc/size", "value": 5 }]'
Surveillez la création du pod à l'aide de la commande kubectl get pods :
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cluster1-pxc-0 1/1 Running 0 27h 10.36.2.5 gke-my-k8s-cluster-default-pool-b80902cd-gp09 <none> <none>
cluster1-pxc-1 1/1 Running 0 27h 10.36.1.10 gke-my-k8s-cluster-default-pool-b80902cd-rdv8 <none> <none>
cluster1-pxc-2 1/1 Running 0 27h 10.36.0.11 gke-my-k8s-cluster-default-pool-b80902cd-jdc3 <none> <none>
cluster1-pxc-3 1/1 Running 0 30m 10.36.7.2 gke-my-k8s-cluster-pool-1-ab14a45e-h1pf <none> <none>
cluster1-pxc-4 1/1 Running 0 13m 10.36.5.3 gke-my-k8s-cluster-pool-1-ab14a45e-01qn <none> <none>
2 autres nouveaux pods (cluster1-pxc-3 et cluster1-pxc-4) ont été créés sur 2 autres nouveaux nœuds Kubernetes (gke-my-k8s-cluster-pool-1-ab14a45e-h1pf et gke-my-k8s-cluster-pool-1-ab14a45e-01qn). Pour réduire, remettez simplement la valeur à 3 dans la commande patch ci-dessus. Notez que le cluster Percona XtraDB doit fonctionner avec un nombre impair de nœuds pour éviter le split-brain.
Déployer une application (WordPress)
Dans cet exemple, nous allons déployer une application WordPress sur notre cluster Percona XtraDB et HAProxy. Commençons par préparer le fichier de définition YAML comme suit :
$ cat wordpress-deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
selector:
app: wordpress
tier: frontend
type: LoadBalancer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: frontend
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: cluster1-haproxy
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-cluster-secrets
key: root
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wp-pv-claim
Faites attention aux variables d'environnement WORDPRESS_DB_HOST et WORDPRESS_DB_PASSWORD. La première variable où nous avons défini "cluster1-haproxy" comme hôte de base de données, au lieu d'un nœud de base de données individuel et pour ce dernier nous avons spécifié le mot de passe root en demandant à Kubernetes de le lire à partir de l'objet my-cluster-secrets sous la clé "root", qui équivaut à "mypassword" (après le décodage de la valeur base64). Nous ignorons la définition de la variable d'environnement WORDPRESS_DB_USER puisque la valeur par défaut est "root".
Nous pouvons maintenant créer notre application :
$ kubectl apply -f wordpress-deployment.yaml
Vérifier le service :
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cluster1-haproxy ClusterIP 10.40.9.177 <none> 3306/TCP,3309/TCP,33062/TCP 4h42m
cluster1-haproxy-replicas ClusterIP 10.40.0.236 <none> 3306/TCP 4h42m
cluster1-pxc ClusterIP None <none> 3306/TCP,33062/TCP 4h42m
cluster1-pxc-unready ClusterIP None <none> 3306/TCP,33062/TCP 4h42m
wordpress LoadBalancer 10.40.13.205 35.200.78.195 80:32087/TCP 4h39m
À ce stade, nous pouvons nous connecter à notre application WordPress à l'adresse http://35.200.78.195/ (l'adresse IP externe) et commencer à configurer l'application WordPress. À ce stade, notre application WordPress est connectée à l'un des clusters Percona XtraDB (connexion à maître unique) via l'un des pods HAProxy.
C'est tout pour le moment. Pour plus d'informations, consultez la documentation Percona Kubernetes Operator for Percona XtraDB Cluster. Bonne conteneurisation !