La resilienza in OpenShift
Published: 2023-10-03 14:23:49

---
title: La resilienza in OpenShift
published: true
date: 2023-05-04 00:00:00 UTC
tags: 
canonical_url: 
---

# 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](https://docs.openshift.com/container-platform/4.10/rest_api/machine_apis/machine-apis-index.html)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](https://github.com/kubernetes-sigs/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?

<ins>Niente.</ins>

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](https://docs.openshift.com/container-platform/4.10/machine_management/creating-infrastructure-machinesets.html), 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:

![](https://theredcode.it/img/openshift-master-nodes-number.png)

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?

<ins>Sbagliato.</ins>

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

- [Documentazione ufficiale](https://access.redhat.com/documentation/it-it/openshift_container_platform/4.1/html/architecture/control-plane)
- [Docker](https://amzn.to/3cSVvUG) - per cominciare bene con Docker e Kubernetes
- [Kubernetes](https://amzn.to/44bLfyR) - Guida per gestire e orchestrare i container