Mi chiamo Sergej, lavoro per ITSumma e voglio raccontarti come affrontiamo il backup in Kubernetes. Ultimamente mi occupo molto di consulenza per l'implementazione di diverse soluzioni DevOps per vari team e, in particolare, lavoro a stretto contatto su progetti che utilizzano K8s. Alla conferenza Uptime Day 4, dedicata al backup in architetture complesse, ho presentato una relazione sul backup del 'cubetto', e qui ne offro una libera interpretazione. Vorrei avvisarti in anticipo che non si tratta di una guida pratica, ma piuttosto di un riassunto delle riflessioni su questo tema.

In linea di principio, il monitoraggio e il backup sono i due strumenti principali per aumentare la resilienza di qualsiasi progetto. Ma nel kubernetes tutto si bilancia da solo, dirai tu, tutto si scalda autonomamente, e se succede qualcosa - si ripristina da solo... Cioè, a una prima indagine superficiale dell'argomento, la risposta di Internet alla domanda su come si approccia il backup in K8s è stata 'ma perché?'. Molti pensano che il kuber sia una sorta di oggetto magico che elimina tutti i problemi infrastrutturali e fa sì che il progetto non crolli mai. Ma... il mondo non è come sembra.
Come ci approcciavamo al processo di backup in passato? Avevamo piattaforme identiche per l'hosting - che fossero macchine virtuali o server fisici servers, a cui applicavamo tre pratiche di base:
- sincronizzazione del codice e della staticità
- sincronizzazione delle configurazioni
- replicazione del database
E voilà: in qualsiasi momento possiamo passare alla piattaforma di riserva, tutti sono felici, ci rialziamo e ci disperdiamo.

Cosa ci offrono per garantire la disponibilità continua della nostra applicazione Kubernetes? La prima cosa di cui parla la documentazione non ufficiale è di installare molte macchine, creando diversi master; il numero deve soddisfare le condizioni per raggiungere il quorum all'interno del cluster, e su ciascun master deve essere in esecuzione etcd, api, MC, scheduler… E sembra tutto fantastico: quando più nodi di lavoro o master escono di servizio, il nostro cluster si riequilibrerà e l'app continuerà a funzionare. Sembra magia! Ma spesso il nostro cluster si trova all'interno di un unico centro dati e questo può sollevare alcune domande. Cosa succede se arriva un escavatore e scava i cavi, se colpisce un fulmine, o c'è un diluvio universale? Tutto è andato, il nostro cluster non esiste più. Come possiamo affrontare la questione della ridondanza considerando questo aspetto del problema?
Prima di tutto, dovresti avere un altro cluster in riserva calda, ovvero un cluster a cui puoi passare in qualsiasi momento. Da un punto di vista di Kubernetes, le infrastrutture devono essere completamente identiche. Quindi, se ci sono plugin non standard per lavorare con il file system, soluzioni personalizzate per l'ingress, devono essere completamente identiche sui tuoi due (o tre, o dieci, a seconda di quanto hai a disposizione in termini di budget e risorse per gli amministratori) cluster. È necessario definire chiaramente due set di applicazioni (deployment, statefulset, daemonset, cronjob, ecc.): quali di esse possono funzionare in riserva costantemente e quali sarebbe meglio non avviare fino al passaggio effettivo.
Il nostro cluster di riserva deve essere completamente identico al nostro cluster di produzione? No. Se in passato, lavorando con progetti monolitici e infrastrutture hardware, mantenevamo un ambiente praticamente identico, nell'ambito di Kubernetes, ritengo che questo non debba essere il caso. Vediamo perché.
Ad esempio, iniziamo con le entità di base di Kubernetes: i deploy. Questi devono essere identici. Devono esserci applicazioni in esecuzione che possono in qualsiasi momento prendere in carico il traffico e consentire al nostro progetto di continuare a vivere. Quando parliamo dei file di configurazione, dobbiamo capire se debbano essere identici oppure no. In altre parole, se noi, persone intelligenti, non usiamo sostanze proibite e non conserviamo il database in K8s, allora nelle nostre configmaps devono esserci le impostazioni di accesso al database di produzione (il cui processo di backup è gestito separatamente). Di conseguenza, per garantire l'accesso al database di backup, dobbiamo avere un file di configurazione separato (configmap). Allo stesso modo, lavoriamo con i secret: le password di accesso al database, le API key; in qualsiasi momento può essere attivo o il secret di produzione o quello di backup. Pertanto, abbiamo già due entità Kubernetes, le cui versioni di backup non devono essere identiche a quelle di produzione. La prossima entità su cui soffermarsi è il cronjob. I cronjob di backup non devono assolutamente essere identici al set di cronjob del cluster di produzione! Se avviamo un cluster di backup e lo alziamo completamente con tutti i cronjob attivi, ad esempio, le persone riceveranno due email contemporaneamente invece di una. Oppure qualche sincronizzazione dei dati con fonti esterne avverrà due volte, e di conseguenza iniziamo a soffrire, piangere, urlare e arrabbiarci.

E come ci propongono di organizzare un cluster di riserva le persone su Internet? La seconda risposta più popolare dopo "perché?" è l'uso della Kubernetes Federation.
Che cos'è? È, per così dire, un grande meta-cluster. Se rappresentiamo l'architettura di Kubernetes — dove abbiamo un master e diverse nodi — dal punto di vista della federazione abbiamo anch'esso un master e diverse nodi, solo che ogni nodo è un cluster separato. In altre parole, lavoriamo con le stesse entità e gli stessi primitivi come con un singolo Kubernetes, solo che gestiamo non le nostre macchine fisiche, ma interi cluster. Nell'ambito della federazione avviene una completa sincronizzazione delle risorse federative dai genitori ai figli. Ad esempio, se abbiamo avviato un deployment tramite federazione — verrà distribuito su ogni nostro cluster secondario. Se prendiamo un configmap o un segreto e lo distribuiamo tramite federazione — questi si propagheranno a tutti i nostri cluster secondari; inoltre, la federazione permette di personalizzare le nostre risorse sui figli. Dunque, prendiamo un configmap, lo distribuiamo tramite federazione e poi, se dobbiamo fare delle modifiche su cluster specifici, ci rechiamo a modificare su un cluster separato e quella modifica non verrà più sincronizzata altrove.
La Federazione di Kubernetes è uno strumento relativamente recente e non supporta ancora l'intero set di risorseofferto da K8s. Al momento della pubblicazione di una delle prime versioni della documentazione, si parlava di supporto solo per i config-map, il deployment sotto replica-set e ingress. I segreti non erano supportati e nemmeno il lavoro con i volumi. Un set di risorse troppo limitato. Specialmente se ci piace sperimentare, ad esempio passando le nostre risorse personalizzate a Kubernetes tramite la custom resource definition; nella federazione, queste non possono essere incluse. In altre parole, sembra una soluzione vicina alla realtà, ma ci porta a colpirci periodicamente sui piedi. D'altra parte, la federazione ci permette di gestire in modo flessibile il nostro replica-set. Ad esempio, se vogliamo avere 10 repliche della nostra applicazione, per default la federazione suddividerà questo numero proporzionalmente tra i vari cluster. E tutto ciò può essere configurato! Possiamo ad esempio indicare che nel cluster di produzione devono esserci 6 repliche della nostra applicazione, mentre in quello di riserva, per risparmiare risorse o per divertimento, solo 4 repliche. Anche questa è una soluzione piuttosto comoda. Tuttavia, con la federazione dobbiamo adottare nuove soluzioni, dover fare dei deployment mentre siamo in corsa, costringendoci a riflettere un po' di più...
Possiamo affrontare il processo di riserva di Kubernetes in maniera più semplice? Quali strumenti abbiamo a disposizione?
Innanzitutto, abbiamo sempre un qualche sistema di CI/CD, quindi non andiamo manualmente a scrivere sui server create/apply. Il sistema genera file yaml per i nostri container.
In secondo luogo, ci sono diversi cluster e abbiamo uno o più registry (se siamo intelligenti) che abbiamo anche riservato. E c'è un'ottima utilità, kubectl, che può lavorare con più cluster contemporaneamente.

Dunque, secondo me, la soluzione più semplice e corretta per costruire un cluster di backup è un deployment parallelo primitivo. C'è un certo pipeline nel sistema ci/cd; prima costruiamo i nostri container, testiamo e distribuiamo le applicazioni tramite kubectl su più cluster indipendenti. Possiamo realizzare distribuzioni simultanee su più cluster. Di conseguenza, gestiamo anche la consegna delle configurazioni in questa fase. Possiamo definire in anticipo un set di configurazioni per il nostro cluster di produzione, un set di configurazioni per il cluster di backup e, a livello di sistema ci/cd, distribuire l'ambiente di produzione nel cluster di produzione, l'ambiente di backup nel cluster di backup. Rispetto alla federazione, non è necessario andare su ogni cluster figlio e ridefinire qualcosa dopo aver stabilito la risorsa federativa. Lo abbiamo fatto in anticipo. Che bravi siamo.
Ma... c'è... avevo scritto, c'è 'la radice di tutti i mali', ma in realtà ce ne sono due. In primo luogo, il file system. C'è un certo PV, o utilizziamo un'archiviazione esterna. Se memorizziamo file all'interno del cluster, dobbiamo agire secondo le vecchie pratiche rimaste dai tempi delle infrastrutture fisiche: per esempio, sincronizzare con lsync. O qualsiasi altro workaround a vostra scelta. Distribuiamo tutto su altre macchine e viviamo.
In secondo luogo, e, in realtà, un punto di conflitto anche più importante, è il database. Se siamo persone intelligenti e non teniamo il database nel kubernetes, il processo di backup dei dati avviene secondo il vecchio schema — replica master-slave, poi commutazione, recupereremo la replica e vivremo bene. Ma se teniamo il nostro DB all'interno del cluster, ci sono molte soluzioni pronte per organizzare la stessa replica master-slave, molte soluzioni per alzare DB all'interno di kubernetes.
Di backup dei database si sono già letti miliardi di rapporti, scritti miliardi di articoli, non c'è nulla di nuovo da questo punto di vista. In generale, seguite i vostri sogni, vivete come volete, inventate anche qualche workaround complesso, ma pensate sempre a come farete tutti i vostri backup.
Ora parliamo di come, in linea di principio, avverrà il processo di switch verso il sito di backup in caso di incendio. In primo luogo, distribuiamo in parallelo applicazioni stateless. Queste non influiscono sulla logica di business delle nostre applicazioni e del nostro progetto; possiamo mantenere continuamente due set di applicazioni in esecuzione, e possono iniziare ad accettare traffico. È molto importante, durante il processo di switch verso il sito di backup, controllare se è necessario ridefinire le configurazioni. Ad esempio, abbiamo un cluster Kubernetes di produzione, un cluster Kubernetes di backup, un database esterno master e un database master di backup. Abbiamo quattro opzioni su come queste applicazioni in produzione possono iniziare a interagire tra loro. Potrebbe essere necessario fare uno switch del database, e quindi bisognerebbe reindirizzare il traffico del cluster di produzione al nuovo database, oppure potrebbe guastarsi il cluster — e siamo passati al backup, ma continuiamo a lavorare con il database di produzione. Inoltre, c'è il terzo scenario in cui sia questo che quello si guastano, e facciamo lo switch di entrambe le applicazioni, ridefinendo la nostra configurazione affinché le nuove applicazioni possano lavorare già con il nuovo database.
E quindi, quali conclusioni possiamo trarre da tutto questo?

Conclusione prima: è bello avere un backup. Ma è costoso. In ideale non dovremmo avere solo un backup. Idealmente dovremmo avere più backup. In primo luogo, il backup dovrebbe trovarsi in almeno un centro dati diverso e, in secondo luogo, idealmente, presso un altro fornitore. È capitato spesso — e nella mia esperienza è successo. Purtroppo non posso nominare progetti, specialmente quando è scoppiato un incendio nel centro dati... Io: passiamo al backup! E i server di backup erano nelle stesse rack...
Oppure immaginate che Amazon venga bloccato in Russia (e questo è successo). Ecco, che utilità ha il nostro backup in un altro Amazon? Anche questo è inaccessibile. Quindi ripeto: manteniamo un backup, almeno in un altro centro dati, e preferibilmente — presso un altro fornitore.
Second output: if you have an application in Kubernetes communicating with external sources (this can be a database or some external API), definitely define it as a service with an external endpoint, so that when switching, you don’t have to redeploy 15 of your applications that are connecting to the same database. Define the database as a separate service and connect to it as if it were inside your cluster: if your database goes down, you simply change the IP in one place and continue to operate smoothly.
And lastly: I love Kubernetes, just like I love experimenting with it. I also enjoy sharing the results of these experiments and my personal experiences. That’s why I recorded a series of webinars about K8s, welcome to for more details.
Fonte: habr.com
