Se sei stato in giro nel mondo dei container, sapresti che è piuttosto difficile adottare un'automazione Kubernetes completa per un sistema di database in cluster, che comunemente aggiunge un livello di complessità al sistema basato su container architettura per queste applicazioni stateful. È qui che un operatore Kubernetes può aiutarci ad affrontare questo problema. Un operatore Kubernetes è un tipo speciale di controller introdotto per semplificare implementazioni complesse che sostanzialmente estende l'API Kubernetes con risorse personalizzate. Si basa sui concetti di base delle risorse e dei controller Kubernetes, ma include conoscenze specifiche del dominio o dell'applicazione per automatizzare l'intero ciclo di vita del software che gestisce.
Percona XtraDB Cluster Operator è un modo efficace per automatizzare le attività specifiche di Percona XtraDB Cluster come distribuzione, ridimensionamento, backup e aggiornamenti all'interno di Kubernetes, creato e gestito da Percona. Distribuisce il cluster in uno StatefulSet con un volume persistente, che ci consente di mantenere un'identità coerente per ogni Pod nel cluster e mantenere i nostri dati.
In questo post del blog testeremo l'implementazione di Percona XtraDB Cluster 8.0 in un ambiente containerizzato, orchestrato dall'operatore Percona XtraDB Cluster Kubernetes su Google Cloud Platform.
Creazione di un cluster Kubernetes su Google Cloud
In questa procedura dettagliata, utilizzeremo il cluster Kubernetes su Google Cloud perché è relativamente semplice e facile far funzionare Kubernetes. Accedi alla dashboard di Google Cloud Platform -> Compute -> Kubernetes Engine -> Crea cluster e ti verrà presentata la seguente finestra di dialogo:
Inserisci il nome del cluster Kubernetes, scegli la tua zona preferita e fai clic su "CREA " (Nella parte inferiore della pagina). In 5 minuti sarà pronto un cluster Kubernetes a 3 nodi. Ora, sulla tua workstation, installa l'SDK di gcloud come mostrato in questa guida, quindi estrai la configurazione di Kubernetes nella tua workstation:
$ 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.
Dovresti essere in grado di connetterti al cluster Kubernetes a questo punto. Esegui il comando seguente per verificare:
$ 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
L'output sopra indica che siamo in grado di connetterci al master Kubernetes e recuperare i nodi del cluster Kubernetes. Ora siamo pronti per eseguire i carichi di lavoro Kubernetes.
Distribuzione di un cluster Percona XtraDB su Kubernetes
Per l'implementazione del carico di lavoro, seguiremo le istruzioni indicate nella documentazione dell'operatore del cluster Percona XtraDB. Fondamentalmente, eseguiamo il seguente comando sulla nostra workstation per creare le risorse personalizzate, lo spazio dei nomi, il controllo degli accessi basato sui ruoli e anche l'operatore Kubernetes stesso:
$ 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
Successivamente, dobbiamo preparare le nostre password (si chiamano Secrets nel termine di Kubernetes) aggiornando i valori all'interno di deploy/secrets.yaml in un formato con codifica base64. Puoi utilizzare strumenti online come https://www.base64encode.org/ per crearne uno o utilizzare uno strumento da riga di comando come il seguente:
$ echo -n 'mypassword' | base64
bXlwYXNzd29yZA==
Quindi, aggiorna deploy/secrets.yaml, come mostrato di seguito:
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==
Quella sopra è una super semplificazione della gestione dei segreti, in cui impostiamo tutte le password in modo che siano le stesse per tutti gli utenti. In produzione, utilizzare una password più complessa e specificare una password diversa per ogni utente.
Ora possiamo inviare la configurazione segreta a Kubernetes:
$ kubectl apply -f deploy/secrets.yaml
Prima di procedere con la distribuzione di un cluster Percona XtraDB, è necessario rivedere la definizione di distribuzione predefinita all'interno di deploy/cr.yaml per il cluster. Ci sono molti oggetti Kubernetes che sono definiti qui, ma la maggior parte di essi è commentata. Per il nostro carico di lavoro, apporteremmo la modifica come di seguito:
$ 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
Abbiamo apportato alcune modifiche al cr.yaml fornito per farlo funzionare con la nostra applicazione, come mostrato sopra. Prima di tutto, dobbiamo commentare (o rimuovere) tutte le righe relative alla CPU, ad esempio [*].resources.requests.cpu:600m, per assicurarci che Kubernetes sia in grado di pianificare correttamente la creazione del pod su nodi con CPU limitata. Quindi dobbiamo aggiungere alcune opzioni di compatibilità per Percona XtraDB Cluster 8.0 basato su MySQL 8.0, per funzionare senza problemi con la nostra applicazione WordPress che implementeremo in seguito, come mostrato nel seguente estratto:
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
Quanto sopra corrisponderà al set di caratteri predefinito del server MySQL con il driver PHP MySQLi nel nostro contenitore WordPress. La sezione successiva è la distribuzione HAProxy in cui è impostato su "enabled:true". C'è anche una sezione ProxySQL con "enabled:false" - comunemente si sceglierebbe uno dei proxy inversi per ogni cluster. L'ultima sezione è la configurazione del backup, in cui vorremmo avere un backup giornaliero programmato alle 00:00 ogni giorno e conservare gli ultimi 5 backup.
Ora possiamo iniziare a distribuire il nostro cluster Percona XtraDB a 3 nodi:
$ kubectl apply -f deploy/cr.yaml
Il processo di creazione richiederà del tempo. L'operatore distribuirà i pod del cluster Percona XtraDB come un insieme con stato, il che significa la creazione di un pod alla volta e a ciascun pod nello StatefulSet verrà assegnato un ordinale intero, da 0 fino a N-1, che è univoco sul set. Il processo termina quando sia l'operatore che i Pod hanno raggiunto lo stato In esecuzione:
$ 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
Dato che questo operatore è una risorsa personalizzata, possiamo manipolare la risorsa perconaxtradbcluster in modo che apprezzi la risorsa Kubernetes standard:
$ kubectl get perconaxtradbcluster
NAME ENDPOINT STATUS PXC PROXYSQL HAPROXY AGE
cluster1 cluster1-haproxy.pxc ready 3 3 27h
Puoi anche usare il nome della risorsa più breve, "pxc", e provare con i seguenti comandi:
$ kubectl describe pxc
$ kubectl edit pxc
Guardando il set di carichi di lavoro, possiamo dire che l'operatore ha creato due StatefulSet:
$ 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'operatore creerà anche i servizi corrispondenti che effettueranno il bilanciamento del carico delle connessioni ai rispettivi pod:
$ 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
L'output sopra mostra che l'operatore ha creato 4 servizi:
- cluster1-haproxy - Il servizio per MySQL single-master (3306), protocollo Proxy (3309) e MySQL Admin (33062) con bilanciamento del carico - Una nuova porta amministrativa introdotta in MySQL 8.0.14 e versioni successive. Questo è il nome del servizio o l'indirizzo IP del cluster che le applicazioni devono connettersi per avere una connessione single-master al cluster Galera.
- cluster1-haproxy-replica - Il servizio per un multimaster MySQL con bilanciamento del carico (3306). Questo è il nome del servizio o l'indirizzo IP del cluster che le applicazioni devono connettersi per avere una connessione multi-master al cluster Galera con algoritmo di bilanciamento round-robin.
- cluster1-pxc - Il servizio per pod PXC con bilanciamento del carico, bypassando HAProxy. Connettendosi direttamente a questo servizio, Kubernetes instraderà la connessione in modalità round robin a tutti i pod PXC, in modo simile a ciò che fornisce cluster-haproxy-replicase. Il servizio non ha un indirizzo IP pubblico assegnato e non è disponibile all'esterno del cluster.
- cluster1-pxc-unready - Il servizio "non pronto" è necessario per il rilevamento dell'indirizzo del pod durante l'avvio dell'applicazione indipendentemente dallo stato del pod. I pod Proxysql e pxc dovrebbero conoscersi prima che il database diventi completamente operativo. Il servizio non pronto non ha un indirizzo IP pubblico assegnato e non è disponibile all'esterno del cluster.
Per connetterti tramite un client MySQL, esegui semplicemente il seguente comando:
$ kubectl run -i --rm --tty percona-client --image=percona:8.0 --restart=Never -- bash -il
Questo creerà un Pod transitorio e entrerà immediatamente nell'ambiente del contenitore. Quindi, esegui il comando client mysql standard con una credenziale adeguata:
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 |
+----------------+
Quando esaminiamo il posizionamento dei pod, tutti i pod Cluster Percona XtraDB si trovano su un host Kubernetes diverso:
$ 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>
Ciò migliorerà sicuramente la disponibilità del servizio, nel caso in cui uno degli host Kubernetes si interrompa.
Per scalare fino a 5 pod, è necessario preparare in anticipo altri 2 nuovi nodi Kubernetes per rispettare la configurazione dell'affinità del pod (impostazione predefinita per affinity.antiAffinityTopologyKey.topologyKey="kubernetes.io/hostname"). Quindi, eseguire il seguente comando patch per ridimensionare il cluster Percona XtraDB a 5 nodi:
$ kubectl patch pxc cluster1 \
--type='json' -p='[{"op": "replace", "path": "/spec/pxc/size", "value": 5 }]'
Monitora la creazione del pod utilizzando il comando 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>
Altri 2 nuovi Pod (cluster1-pxc-3 e cluster1-pxc-4) sono stati creati su altri 2 nuovi nodi Kubernetes (gke-my-k8s-cluster-pool-1-ab14a45e-h1pf e gke-my-k8s-cluster-pool-1-ab14a45e-01qn). Per ridimensionare, riportare semplicemente il valore a 3 nel comando patch sopra. Tieni presente che Percona XtraDB Cluster dovrebbe essere in esecuzione con un numero dispari di nodi per prevenire lo split brain.
Distribuzione di un'applicazione (WordPress)
In questo esempio, implementeremo un'applicazione WordPress sopra il nostro Percona XtraDB Cluster e HAProxy. Per prima cosa prepariamo il file di definizione YAML come il seguente:
$ 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
Prestare attenzione alle variabili di ambiente WORDPRESS_DB_HOST e WORDPRESS_DB_PASSWORD. La prima variabile in cui abbiamo definito "cluster1-haproxy" come host del database, invece di un singolo nodo del database e per il secondo abbiamo specificato la password di root indicando a Kubernetes di leggerla dall'oggetto my-cluster-secrets sotto la chiave "root", che equivale a "mypassword" (dopo che il valore base64 è stato decodificato). Saltiamo la definizione della variabile d'ambiente WORDPRESS_DB_USER poiché il valore predefinito è "root".
Ora possiamo creare la nostra applicazione:
$ kubectl apply -f wordpress-deployment.yaml
Controlla il servizio:
$ 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
A questo punto, possiamo connetterci alla nostra applicazione WordPress all'indirizzo http://35.200.78.195/ (l'indirizzo IP esterno) e iniziare a configurare l'applicazione WordPress. A questo punto, la nostra applicazione WordPress è connessa a uno dei Cluster Percona XtraDB (connessione single-master) tramite uno dei pod HAProxy.
Per ora è tutto. Per ulteriori informazioni, consultare la documentazione di Percona Kubernetes Operator per Percona XtraDB Cluster. Buona containerizzazione!