La resilienza in OpenShift

  • Di
  • 2023-05-04 - 7 minuti
banner

Intro

OpenShift è una straordinaria piattaforma per la gestione dei container, che fornisce la potenza di Kubernetes con una serie di funzionalità che ne permettono la distribuzione e l’utilizzo sia su infrastrutture on-premise che su un ambiente cloud pubblico.

Ora, con OpenShift 4.10, è diventato semplicissimo creare un cluster: ci sono diversi installer o soluzioni che possiamo utilizzare all’interno del nostro ambiente in cloud e, voilà, questo si occupa (quasi) di tutto: creare e configurare i nodi, installare il prodotto e renderlo pronto per l’uso. Niente male, no?

Alla luce di tutta questa automazione, la domanda che può sorgente è: “cosa succede quando si prova a distruggere OpenShift?”

In questo articolo, vediamo cosa vuol dire testare la resilienza di OpenShift, passando per il concetto di Machine e Machinset, e facendo dei test: proviamo a distruggere parte del cluster, e vediamo cosa succede.

Test #1

Supponiamo di avere un tipico cluster OpenShift (su AWS) con la seguente configurazione: 3 nodi master e 3 nodi worker configurati tramite delle semplici Machine, di cui possiamo recuperare le informazioni tramite il comando oc get nodes (avendo i permessi di cluster-admin):

$ oc get nodes
>>>
NAME                                                STATUS   ROLES      AGE    
ip-xxx-yyy-zzz-10.eu-central-1.compute.internal     Ready    worker     2d   
ip-xxx-yyy-zzz-121.eu-central-1.compute.internal    Ready    worker     2d   
ip-xxx-yyy-zzz-233.eu-central-1.compute.internal    Ready    worker     2d   
ip-xxx-yyy-zzz-193.eu-central-1.compute.internal    Ready    master     2d   
ip-xxx-yyy-zzz-94.eu-central-1.compute.internal     Ready    master     2d   
ip-xxx-yyy-zzz-100.eu-central-1.compute.internal    Ready    master     2d   

Come primo test, andiamo a distruggere uno dei nodi master e rispondere alla domanda: “Cosa succede?”

Ci sono alcuni modi per compiere questo test: distruggere l’istanza su cui gira il nodo o anche renderla inaccessibile agli altri master cambiando le configurazioni di rete. In questo caso, eseguiamo un test semplice e distruggiamo la risorsa che si occupa di gestire il nodo, ossia la Machine.

Le Machine sono degli oggetti che descrivono una risorsa che incapsula e definisce un nodo. In altre parole, rappresenta la macchina virtuale, o l’istanza con le relative risorse in termini di CPU, memoria e storage.

I nodi, quindi, rappresentano una Machine configurata per l’utilizzo con OpenShift, ovvero un’istanza che contiene i servizi necessari per eseguire i pod ed è gestita dai componenti master.

Le differenze sorgono a livello di astrazione: un nodo in OpenShift è esattamente la stessa cosa rispetto ad un nodo in Kubernetes e normalmente ci si riferisce all’astrazione di un “nodo” worker/master quando si parla di un “cluster”. L’astrazione del nodo esiste solo all’interno dei limiti di un cluster in esecuzione.

Una Machine è un’astrazione introdotta dalla definizione di Cluster API per permettere a chi definisce l’infrastruttura host richiesta di eseguire i
nodi utilizzando le specifiche della macchina.

Quindi troviamo tutte le Machines eseguendo il seguente comando:

$ oc get machines --all-namespaces
>>>
NAMESPACE               NAME                                           PHASE     TYPE         REGION         ZONE                     AGE
openshift-machine-api   my-cluster-master-0                            Running   m5.xlarge    eu-central-1   eu-central-1b           2d
openshift-machine-api   my-cluster-master-1                            Running   m5.xlarge    eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-master-2                            Running   m5.xlarge    eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-worker-eu-central-1b-xxxxx          Running   c5.4xlarge   eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-worker-eu-central-1b-yyyyy          Running   c5.4xlarge   eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-worker-eu-central-1b-zzzzz          Running   c5.4xlarge   eu-central-1   eu-central-1b            2d

Per distruggere dunque un nodo master, e quindi la relativa istanza, andiamo ad eseguire il comando:

$ oc delete machine my-cluster-master-0
>>>
machine.machine.openshift.io "my-cluster-master-0" deleted

E cosa succede dopo che ho distrutto uno dei nodi master?

Niente.

Come visibile, OpenShift non ha creato un nuovo master, ma sta lavorando con soli due:

$ oc get nodes
>>>
NAME                                                STATUS   ROLES      AGE    
ip-xxx-yyy-zzz-10.eu-central-1.compute.internal     Ready    worker     2d   
ip-xxx-yyy-zzz-121.eu-central-1.compute.internal    Ready    worker     2d   
ip-xxx-yyy-zzz-233.eu-central-1.compute.internal    Ready    worker     2d   
ip-xxx-yyy-zzz-193.eu-central-1.compute.internal    Ready    master     2d   
ip-xxx-yyy-zzz-94.eu-central-1.compute.internal     Ready    master     2d   

Cos’è successo: dopo aver chiesto a OpenShift di eliminare l’istanza, questa non è stata creata nuovamente per rimpiazzare la precedente, perché esattamente come per un Pod in Kubernetes, una risorsa di tipo Machine non ha un meccanismo di ripristino.

Quindi, quando viene eliminata o muore, è morta.

Per fornire la dovuta resilienza alle risorse Machine, è necessario utilizzare un MachineSet, così come avviene tramite l’uso di un ReplicaSet per un Pod).

Quindi, diamo un’occhiata ai MachineSet nell’ambiente:

$ oc get machinesets --all-namespaces
>>>
NAMESPACE               NAME                                             DESIRED   CURRENT   READY   AVAILABLE   AGE
openshift-machine-api   my-cluster-worker-eu-central-1b                   3         3         3       3           2d

Dall’output è possibile vedere che ci sono MachineSet per i nodi worker, ma non per il master. A questo punto è facile immaginare che il master sia così configurato perché non abbia bisogno di scalabilità: solitamente nell’architettura di un cluster ce ne saranno sempre -minimo- 3, quindi OpenShift ha deciso di non creare un MachineSet per il master.

Questo vuol dire che attualmente il cluster rimane operativo, ma ci troviamo su un terreno molto rischioso. Se perdiamo un altro master (per qualsiasi motivo), etcd smetterà di funzionare, e di conseguenza OpenShift.

Come ritornare alla situazione precedente? Semplice.

Ricostruiamo il terzo master. Ancora una volta, seguiamo il concetto di Machine per creare l’istanza e avremo bisogno della definizione di una risorsa da poter utilizzare per crearne una nuova. Quindi, eseguiamo i seguenti comandi per creare il file YAML da modificare:

$ oc get machine ip-xxx-yyy-zzz-94.eu-central-1.compute.internal -n openshift-machine-api -o yaml > master-1.yaml

Quindi ora ho un file YAML con la definizione di uno degli altri master, che posso modificare per creare il master mancante.

Sarà sufficiente cambiare il campo metadata.name ed eseguire il seguente comando:

$ oc create -f master1.yaml

Una volta che questo sarà creato, verrà rilevato dal cluster come nuovo nodo master e aggiunto all’infrastruttura:

$ oc get machines --all-namespaces
>>>
NAMESPACE               NAME                                           PHASE     TYPE         REGION         ZONE                     AGE
openshift-machine-api   my-cluster-master-0                            Running   m5.xlarge    eu-central-1   eu-central-1b           2d
openshift-machine-api   my-cluster-master-1                            Running   m5.xlarge    eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-master-2                            Running   m5.xlarge    eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-worker-eu-central-1b-xxxxx          Running   c5.4xlarge   eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-worker-eu-central-1b-yyyyy          Running   c5.4xlarge   eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-worker-eu-central-1b-zzzzz          Running   c5.4xlarge   eu-central-1   eu-central-1b            2d
$ oc get nodes
>>>
NAME                                                STATUS   ROLES      AGE    
ip-xxx-yyy-zzz-10.eu-central-1.compute.internal     Ready    worker     2d   
ip-xxx-yyy-zzz-121.eu-central-1.compute.internal    Ready    worker     2d   
ip-xxx-yyy-zzz-233.eu-central-1.compute.internal    Ready    worker     2d   
ip-xxx-yyy-zzz-193.eu-central-1.compute.internal    Ready    master     2d   
ip-xxx-yyy-zzz-94.eu-central-1.compute.internal     Ready    master     2d   
ip-xxx-yyy-zzz-175.eu-central-1.compute.internal    Ready    master     2d   

Voilà: ambiente ripristinato, con 3 nodi master.

Spiegazione

La documentazione dell’architettura di Openshift 4 descrive i master come un gruppo di macchine che non fanno parte di un set di macchine, questa è l’architettura supportata, testata e consigliata. Lo stesso documento afferma che il numero di master deve essere esattamente 3:

Come visto, esiste una procedura di ripristino per sostituire un master in errore con uno nuovo, e questo prevede la creazione del nuovo master come risorsa Machine, anche se potrebbe venire in mente di utilizzare un MachineSet per mantenerne la configurazione e la gestione del numero di repliche. Tuttavia, la configurazione risultante non è ufficialmente supportata da Red Hat, e per diversi motivi.

Avere i master definiti come un MachineSet non offre vantaggi evidenti ma aggiunge ulteriori rischi, per diversi motivi:

  • La presenza di più di tre master non è consigliata o supportata, pertanto il ridimensionamento dei master potrebbe causare problemi nel cluster.
  • Il ridimensionamento dei nodi master per il ripristino da una situazione di errore di uno di essi richiede comunque l’applicazione manuale della procedura di ripristino di emergenza di cui sopra, pertanto non è possibile alcun ripristino automatico.
  • Dopo che il MachineSet di un master è stato ridimensionato e la procedura di ripristino di emergenza è stata applicata con successo per sostituire un master in errore, a un certo punto il MachineSet dovrebbe essere
    ridimensionato per rimuovere la macchina in errore, tuttavia si tratta di un’operazione rischiosa, è necessario
    adottare misure aggiuntive per evitare di rimuovere la macchina sana invece di quella guasta.

Test #2

E allora, cosa succederebbe se eliminassimo un worker?

Eseguiamo lo stesso test di prima, ma stavolta eliminiamo uno dei nodi applicativi. Come visto, esistono molti modi per farlo, e andremo a utilizzare la CLI di OpenShift e il concetto di Machine:

$ oc delete machine my-cluster-worker-eu-central-1b-xxxxx 
>>>
machine.machine.openshift.io "my-cluster-worker-eu-central-1b-xxxxx " deleted

Ora, se controlliamo nuovamente il numero di Machine, ce ne dovremmo aspettare 2, giusto?

Sbagliato.

Vediamo le Machine:

$ oc get machines -n openshift-machine-api
>>>
NAMESPACE               NAME                                           PHASE     TYPE         REGION         ZONE                     AGE
openshift-machine-api   my-cluster-master-0                            Running   m5.xlarge    eu-central-1   eu-central-1b           2d
openshift-machine-api   my-cluster-master-1                            Running   m5.xlarge    eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-master-2                            Running   m5.xlarge    eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-worker-eu-central-1b-xxxxx          Running   c5.4xlarge   eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-worker-eu-central-1b-yyyyy          Running   c5.4xlarge   eu-central-1   eu-central-1b            2d
openshift-machine-api   my-cluster-worker-eu-central-1b-AAAAA          Running   c5.4xlarge   eu-central-1   eu-central-1b            2m

Dall’ultima riga, noteremo che OpenShift ha creato immediatamente un’altra istanza del worker node, nella stessa zona di disponibilità.

Questo è il potere delle risorse di tipo Machine e dei MachineSets.

Allo stesso modo in cui un ReplicaSet fornisce resilienza a un Pod, un MachineSet fornisce resilienza a una Machine.

Utilizzando il principio Kubernetes dello stato desiderato, un MachineSet dichiarerà quante istanze in una determinata zona di disponibilità vogliamo e quindi, non appena distruggiamo l’istanza corrispondente, il MachineSet distribuirà rapidamente un’altra macchina in AWS e la configurerà come worker node.

Niente male, no?

Risorse utili

Post correlati

Partners

Community, aziende e persone che supportano attivamente il blog

Logo di Codemotion
Logo di GrUSP
Logo di Python Milano
Logo di Schrodinger Hat
Logo di Python Biella Group
Logo di Fuzzy Brains
Logo di Django Girls
Logo di Improove
Logo del libro open source
Logo di NgRome

Iscriviti alla newsletter

Per non perderti gli ultimi articoli e per vincere biglietti e gadget TheRedCode

Riceverai una volta al mese (o anche meno) gli articoli più interessanti pubblicati sul blog, e potrai provare a vincere un biglietto per uno dei prossimi eventi!

Andiamo!