Kubernetes è uno strumento eccellente per eseguire contenitori Docker in un ambiente produttivo clusterizzato. Tuttavia, esistono compiti che Kubernetes non è in grado di gestire. Con distribuzioni frequenti in un ambiente di lavoro, abbiamo bisogno di un deployment Blue/Green completamente automatizzato per evitare inattività in questo processo, durante il quale è anche necessario gestire le richieste HTTP esterne e gestire il caricamento SSL. Ciò richiede integrazione con un bilanciatore di carico, come ha-proxy. Un altro compito è il ridimensionamento semi-automatico del cluster Kubernetes stesso quando opera in un ambiente cloud, ad esempio, una riduzione parziale della dimensione del cluster durante la notte.
Sebbene Kubernetes non disponga di queste funzionalità 'out of the box', fornisce un'API che può essere utilizzata per affrontare tali compiti. Gli strumenti per il deployment Blue/Green automatizzato e il ridimensionamento del cluster Kubernetes sono stati sviluppati nell'ambito del progetto Cloud RTI, che è stato creato sulla base di open-source.
In questo articolo, nella trascrizione del video, si discute di come configurare Kubernetes insieme ad altri componenti open source per ottenere un ambiente pronto per la produzione, che riceve il codice dalla modifica git commit senza downtime in produzione.

Quindi, una volta che hai accesso alle tue applicazioni dal mondo esterno, puoi iniziare a configurare completamente l'automazione, portandola a uno stato in cui puoi eseguire un git commit e assicurarti che quel git commit si concluda in produzione. Naturalmente, durante l'implementazione di questi passaggi e durante il deployment, non vogliamo affrontare downtime. Quindi, ogni automazione in Kubernetes inizia con l'API.

Kubernetes non è uno strumento che può essere utilizzato produttivamente 'out of the box'. Certo, puoi farlo, utilizzare kubectl e così via, ma l'API rimane la parte più interessante e utile di questa piattaforma. Usando l'API come un insieme di funzioni, puoi accedere praticamente a tutto quello che desideri fare in Kubernetes. Anche kubectl utilizza REST API.
Questo è REST, quindi puoi utilizzare qualsiasi linguaggio e strumento per lavorare con questa API, ma le librerie utente possono semplificarti significativamente la vita. Il mio team ha scritto 2 di queste librerie: una per Java / OSGi e una per Go. Quest'ultima non viene utilizzata spesso, ma in ogni caso hai a disposizione queste cose utili. Rappresentano un progetto open-source parzialmente licenziato. Ci sono molte di queste librerie per diversi linguaggi, quindi puoi scegliere quelle più adatte.

Quindi, prima di iniziare l'automazione del deployment, è necessario assicurarsi che questo processo non sarà soggetto a fermi. Ad esempio, il nostro team effettua il deployment in produzione a metà giornata, quando le persone utilizzano al massimo le applicazioni, quindi è molto importante evitare ritardi in questo processo. Per evitare fermi, vengono utilizzati 2 metodi: il deployment blue/green o l'aggiornamento continuo rolling update. Nel secondo caso, se hai 5 repliche dell'applicazione in esecuzione, vengono aggiornate una dopo l'altra. Questo metodo funziona molto bene, ma non è adatto se durante il processo di deployment hai versioni diverse dell'applicazione in esecuzione contemporaneamente. In tal caso, puoi aggiornare l'interfaccia utente mentre il backend funziona con la vecchia versione, e il funzionamento dell'applicazione sarà interrotto. Pertanto, dal punto di vista della programmazione, lavorare in tali condizioni è piuttosto complicato.
Questa è una delle ragioni per cui preferiamo utilizzare il deployment blue/green per automatizzare il deployment delle nostre applicazioni. Con questo metodo, devi assicurarti che in un dato momento sia attiva solo una versione dell'applicazione.
Il meccanismo del deployment blue/green funziona come segue. Riceviamo il traffico per le nostre applicazioni tramite ha-proxy, che lo indirizza alle repliche dell'applicazione della stessa versione in esecuzione.
Quando viene effettuato un nuovo deployment, utilizziamo Deployer, al quale vengono forniti nuovi componenti, e lui effettua il deployment della nuova versione. Il deployment della nuova versione dell'applicazione significa che viene "sollevato" un nuovo set di repliche, dopo di che queste repliche della nuova versione vengono avviate in un pod separato e nuovo. Tuttavia, ha-proxy non sa nulla di esse e non indirizza ancora alcun carico di lavoro a loro.
Pertanto, è necessario innanzitutto eseguire un controllo di funzionalità delle nuove versioni health checking, per assicurarsi che le repliche siano pronte a gestire il carico.

Tutti i componenti del deployment devono supportare qualche forma di health check. Questo può essere un controllo HTTP molto semplice in cui si riceve un codice di stato 200, o un controllo più approfondito in cui si verifica la connessione delle repliche al database e ad altri servizi, la stabilità delle connessioni nell'ambiente dinamico, se tutto viene avviato e funziona correttamente. Questo processo può essere piuttosto complesso.

Dopo che il sistema si sarà assicurato della funzionalità di tutte le repliche aggiornate, Deployer aggiornerà la configurazione e passerà il confd corretto, il quale riconfigurerà ha-proxy.

Solo dopo questo il traffico sarà diretto nel pod con le repliche della nuova versione, mentre il vecchio pod scomparirà.

Questo meccanismo non è una peculiarità di Kubernetes. Il concetto di Blue/green deployment esiste da molto tempo e ha sempre utilizzato un bilanciatore di carico. Inizialmente si indirizza tutto il traffico alla vecchia versione dell'applicazione, e dopo l'aggiornamento lo si sposta completamente sulla nuova versione. Questo principio non è utilizzato solo in Kubernetes.
Ora vi presenterò un nuovo componente di deployment – Deployer, che esegue i controlli di funzionalità, riconfigura il proxy e così via. È un concetto che non riguarda il mondo esterno e esiste all'interno di Kubernetes. Vi mostrerò come creare il proprio concetto Deployer utilizzando strumenti open-source.
Quindi, la prima cosa che fa Deployer è creare un controller di replicazione RC utilizzando l'API Kubernetes. Questa API crea pod e servizi per il successivo deployment, ovvero crea un nuovo cluster per le nostre applicazioni. Una volta che l'RC verifica che le repliche siano state avviate, eseguirà un controllo di salute Health check. A tal fine, nel Deployer viene utilizzato il comando GET /health. Questo avvia i componenti di verifica appropriati e controlla tutti gli elementi che garantiscono il funzionamento del cluster.

Dopo che tutti i pod hanno segnalato il loro "stato di salute", Deployer crea un nuovo elemento di configurazione: un archivio distribuito etcd, utilizzato all'interno di Kubernetes, incluso per memorizzare la configurazione del bilanciatore di carico. Scriviamo i dati in etcd, e un piccolo strumento confd tiene traccia di etcd per eventuali nuovi dati.
Se rileva delle modifiche alla configurazione iniziale, genera un nuovo file di configurazione e lo trasmette a ha-proxy. In questo caso, ha-proxy si riavvia senza perdere alcuna connessione e indirizza il carico verso i nuovi services, che garantiscono il funzionamento della nuova versione delle nostre applicazioni.

Come vedete, nonostante la varietà di componenti, non c'è nulla di complicato. È sufficiente prestare maggiore attenzione all'API e a etcd. Voglio parlarvi di un deployer open-source che utilizziamo noi stessi: Amdatu Kubernetes Deployer.

Questo è uno strumento per l'orchestrazione dei deployment di Kubernetes, dotato delle seguenti funzionalità:
- deployment Blue/Green;
- configurazione di un bilanciatore di carico esterno;
- gestione dei descrittori di deployment;
- gestione del deployment effettivo;
- verifica di salute Health checks durante il deployment;
- inserimento di variabili d'ambiente nei pod.
Questo Deployer è costruito sulla base dell'API Kubernetes e offre un'API REST per la gestione dei descrittori e dei deployment, oltre a un'API Websocket per i log in tempo reale durante il processo di deployment.
Inserisce i dati di configurazione del bilanciatore di carico in etcd, quindi puoi non utilizzare ha-proxy con supporto "pronto all'uso", ma facilmente utilizzare il tuo file di configurazione del bilanciatore. Amdatu Deployer è scritto in Go, proprio come Kubernetes, e ha licenza Apache.
Prima di iniziare a utilizzare questa versione del deployer, ho usato il seguente descrittore di distribuzione, in cui sono indicati i parametri necessari.

Uno dei parametri importanti di questo codice è l'attivazione del flag "useHealthCheck". Dobbiamo specificare che durante il processo di distribuzione è necessario effettuare il controllo di funzionalità. Questo parametro può essere disattivato quando nella distribuzione vengono utilizzati contenitori di terze parti che non necessitano di essere controllati. In questo descrittore sono indicati anche il numero di repliche e l'URL del frontend necessario per ha-proxy. Infine, è indicato il flag della specifica del pod "podspec", che si rivolge a Kubernetes per ottenere informazioni sulla configurazione delle porte, sull'immagine, ecc. Questo è un descrittore piuttosto semplice in formato JSON.
Un altro strumento che fa parte del progetto open-source Amdatu è Deploymentctl. Ha un'interfaccia utente UI per configurare la distribuzione, memorizza la cronologia delle distribuzioni e contiene webhook per i callback da parte di utenti e sviluppatori esterni. Puoi anche non utilizzare l'UI, poiché il Deployer Amdatu è essenzialmente un'API REST, ma questa interfaccia può semplificarti notevolmente la distribuzione senza dover ricorrere a nessuna API. Deploymentctl è scritto in OSGi/Vertx utilizzando Angular 2.
Ora mostrerò quanto detto sopra sullo schermo, utilizzando una registrazione preesistente, così non dovrete aspettare. Distribuiremo una semplice applicazione in Go. Non preoccupatevi se non avete mai avuto a che fare con Go, è un'applicazione molto semplice, quindi tutto dovrebbe esservi chiaro.

Qui stiamo creando un server HTTP che risponde solo a /health, quindi questa applicazione verifica solo il controllo di stato e nient'altro. Se il controllo passa, viene attivata la struttura JSON mostrata in basso. Contiene la versione dell'applicazione che sarà distribuita dal deployer, un messaggio che vedete nella parte superiore del file e un tipo di dato booleano — se la nostra applicazione è funzionante oppure no.
Con l'ultima riga ho un po' barato, perché ho inserito in cima al file un valore booleano fisso, che mi aiuterà successivamente a distribuire anche un'applicazione "non sana". Più tardi ci occuperemo di questo.
Iniziamo. Prima di tutto, controlliamo la presenza di eventuali pod in esecuzione utilizzando il comando ~ kubectl get pods e, in assenza di risposta dall'URL del frontend, ci assicuriamo che non siano in corso distribuzioni al momento.

Poi, sullo schermo vedrete l'interfaccia Deploymentctl che ho menzionato, in cui vengono impostati i parametri di distribuzione: il namespace, il nome dell'applicazione, la versione della distribuzione, il numero di repliche, l'URL del frontend, il nome del contenitore, l'immagine, i limiti delle risorse, il numero di porta per il controllo di salute, ecc. I limiti delle risorse sono molto importanti perché consentono di sfruttare al massimo la potenza di calcolo disponibile. Qui è possibile anche visualizzare il registro della distribuzione Deployment log.

Se ora ripetiamo il comando ~ kubectl get pods, si nota che il sistema 'si blocca' per 20 secondi, durante i quali avviene la riconfigurazione di ha-proxy. Dopo di ciò, il pod viene avviato e la nostra replica può essere vista nel registro della distribuzione.

Ho tagliato dal video l'attesa di 20 secondi, e ora vedete sullo schermo che la prima versione dell'applicazione è stata distribuita. Tutto ciò è stato fatto solamente utilizzando l'interfaccia utente.

Ora proviamo la seconda versione. A questo scopo, modifico il messaggio dell'applicazione da 'Hello, Kubernetes!' a 'Hello, Deployer!', il sistema crea questa immagine e la posiziona nel registro Docker, dopo di che premere ancora una volta il pulsante 'Deploy' nella finestra di Deploymentctl. In questo modo il registro della distribuzione si avvierà automaticamente esattamente come è avvenuto durante la distribuzione della prima versione dell'applicazione.

Il comando ~ kubectl get pods mostra che attualmente sono in esecuzione 2 versioni dell'applicazione, tuttavia il frontend mostra che abbiamo ancora in esecuzione la versione 1.

Il bilanciatore di carico attende che venga eseguito il controllo di salute, dopodiché reindirizzerà il traffico sulla nuova versione. Dopo 20 secondi ci spostiamo su curl e vediamo che ora è distribuita la versione 2 dell'applicazione, mentre la prima è stata rimossa.

Questa è stata la distribuzione di un'applicazione 'sana' — healthy. Vediamo cosa accadrà se per la nuova versione dell'applicazione modifico il valore del parametro Healthy da true a false, ovvero cercherò di distribuire un'applicazione non sana, che non ha superato il controllo di funzionamento. Questo può accadere se durante lo sviluppo dell'applicazione sono state commesse delle errori di configurazione, e in tale stato è stata inviata in produzione.
Come potete vedere, il deployment passa attraverso tutte le fasi precedentemente indicate, e ~ kubectl get pods mostra che entrambe le pod sono in esecuzione. Ma a differenza del precedente deployment, i log mostrano uno stato di timeout. Questo significa che, poiché il controllo di health check non è andato a buon fine, la nuova versione dell'app non può essere distribuita. Di conseguenza, vedete che il sistema è tornato a usare la vecchia versione dell'app, mentre la nuova versione è stata semplicemente rimossa.

La cosa positiva è che anche se avete un enorme numero di richieste simultanee che arrivano all'app, queste non noteranno nemmeno un'interruzione durante l'esecuzione della procedura di deployment. Se testate questa applicazione utilizzando il framework Gatling, che le invia il massimo numero possibile di richieste, nessuna di queste verrà scartata. Questo significa che i nostri utenti non noteranno nemmeno l'aggiornamento delle versioni in tempo reale. Se fallirà, il lavoro continuerà sulla vecchia versione, se avrà successo, gli utenti passeranno alla nuova versione.
C'è solo una cosa che può portare al fallimento: se il controllo di health check è andato a buon fine e l'app ha avuto un crash non appena ha ricevuto il carico di lavoro, cioè il collasso si verificherà solo dopo che il deployment è completato. In questo caso dovrete tornare manualmente alla vecchia versione. Quindi, abbiamo esaminato come utilizzare Kubernetes con strumenti open-source progettati per questo. La procedura di deployment sarà molto più semplice se integrate questi strumenti nei pipeline di Build/Deploy. A questo punto, potete utilizzare sia l'interfaccia utente che automatizzare completamente questo processo, applicando, ad esempio, un commit al master.

Il nostro Build Server creerà un'immagine Docker, la caricherà su Docker Hub o su qualsiasi altro registro che utilizzate. Docker Hub supporta webhook, quindi possiamo avviare un deployment remoto tramite Deployer seguendo il percorso sopra indicato. In questo modo è possibile automatizzare completamente il deployment dell'app in un potenziale ambiente di produzione.
Passiamo a esaminare il tema successivo: la scalabilità del cluster Kubernetes. Noterò che il comando kubectl è il comando per la scalabilità. Con esso, è possibile aumentare facilmente il numero di repliche nel cluster che abbiamo. Tuttavia, nella pratica, di solito desideriamo aumentare il numero dei nodi, non dei pod.

In questo contesto, durante l'orario lavorativo potrebbe essere necessaria un'espansione, mentre durante la notte, per ridurre i costi dei servizi Amazon, sarà necessario ridurre il numero delle istanze dell'applicazione in esecuzione. Ciò non significa che sia sufficiente scalare solo il numero dei pod, perché anche se uno dei nodi è inattivo, dovrete comunque pagare Amazon per esso. Cioè, insieme alla scalabilità dei pod, dovrete scalare anche il numero delle macchine utilizzate.
Questo può causare difficoltà, perché indipendentemente dal fatto che stiamo utilizzando Amazon o un altro servizio cloud, Kubernetes non sa nulla sul numero delle macchine in uso. Non esiste uno strumento interno per scalare il sistema a livello di nodi.

Quindi dovremo occuparci sia dei nodi che dei pod. Possiamo facilmente scalare l'avvio di nuovi nodi utilizzando l'API di AWS e i gruppi di scalabilità per configurare il numero di nodi di lavoro di Kubernetes. È anche possibile utilizzare cloud-init o uno script simile per registrare i nodi nel cluster Kubernetes.
Una nuova macchina si avvia nel gruppo di scaling, si autoinizializza come nodo, si registra nel registro del master e inizia a lavorare. Dopodiché, è possibile aumentare il numero di repliche da utilizzare sui nodi creati. La riduzione delle dimensioni richiede maggiori sforzi, in quanto è necessario assicurarsi che tale passo non porti alla distruzione di applicazioni già in esecuzione dopo lo spegnimento delle macchine 'non necessarie'. Per prevenire tale scenario, è necessario portare i nodi allo stato di 'unschedulable'. Ciò significa che il pianificatore per impostazione predefinita, durante la pianificazione dei pod DaemonSet, ignorerà questi nodi. Il pianificatore non rimuoverà nulla da questi server, ma non avvierà nemmeno nuovi container su di essi. Il passo successivo consiste nel drenare il nodo, ovvero spostare i pod in esecuzione su un'altra macchina o su altri nodi dotati di capacità sufficiente per questo. Dopo aver verificato che non ci siano più container su questi nodi, è possibile eliminarli da Kubernetes. Dopodiché, per Kubernetes, smetteranno semplicemente di esistere. Infine, è necessario utilizzare l'API AWS per disattivare i nodi o le macchine non necessarie.
Puoi utilizzare Amdatu Scalerd, un altro strumento open-source per il scaling, simile all'API AWS. Offre una CLI per aggiungere o rimuovere nodi dal cluster. Una delle sue caratteristiche interessanti è la possibilità di configurare il pianificatore tramite il seguente file json.

Il codice mostrato riduce della metà la capacità del cluster durante il periodo notturno. È configurato sia per il numero di repliche esistenti che per la capacità desiderata del cluster Amazon. L'uso di questo pianificatore ridurrà automaticamente il numero di nodi di notte e li aumenterà al mattino, consentendo di risparmiare sui costi di utilizzo dei nodi di un servizio cloud come Amazon. Questa funzione non è integrata in Kubernetes, ma l'uso di Scalerd ti permetterà di scalare questa piattaforma come vuoi.
Vorrei attirare la vostra attenzione sul fatto che molte persone mi dicono: «Tutto ciò va bene, ma che dire del mio database, che di solito rimane in uno stato statico?» Come è possibile avviare qualcosa di simile in un ambiente dinamico come Kubernetes? A mio avviso, non dovreste farlo, non dovreste cercare di organizzare il funzionamento di un repository dati in Kubernetes. Tecnologicamente è possibile, e ci sono guide sull'argomento su Internet, ma complicherà seriamente la vostra vita.
Sì, in Kubernetes esiste il concetto di storage persistente e potreste provare a eseguire repository dati come Mongo o MySQL, ma è un compito piuttosto laborioso. Questo perché i repository dati non supportano completamente l'interazione con un ambiente dinamico. La maggior parte dei database richiede una notevole configurazione, compresa la configurazione manuale del cluster, non ama il ridimensionamento automatico e altre cose simili.
Quindi non complicatevi la vita cercando di avviare un repository dati in Kubernetes. Organizzate il loro funzionamento in modo tradizionale utilizzando i servizi abituali e semplicemente date a Kubernetes l'opportunità di utilizzarli.

Per concludere, voglio presentarvi la piattaforma Cloud RTI basata su Kubernetes, su cui lavora il mio team. Essa garantisce una gestione centralizzata dei log, il monitoraggio delle applicazioni e dei cluster e offre molte altre funzioni utili che vi torneranno utili. Utilizza diversi strumenti open-source, come Grafana, per visualizzare il monitoraggio.


È stata posta la domanda su perché utilizzare un bilanciatore di carico ha-proxy con Kubernetes. È una buona domanda, perché attualmente ci sono 2 livelli di bilanciamento del carico. I servizi di Kubernetes sono ancora su indirizzi IP virtuali. Non potete usarli per le porte delle macchine host esterne, perché se Amazon sovraccarica il suo host cloud, l'indirizzo cambia. Ecco perché posizioniamo ha-proxy davanti ai servizi: per creare una struttura più statica per il flusso di traffico senza interruzioni con Kubernetes.
Un'altra buona domanda è: come si può gestire la modifica dello schema del database durante un deployment blue/green? Il fatto è che, indipendentemente dall'uso di Kubernetes, la modifica dello schema del database è un compito complesso. È necessario garantire la compatibilità tra il vecchio e il nuovo schema, dopo di che sarà possibile aggiornare il database e successivamente aggiornare le applicazioni stesse. È possibile eseguire un «hot swapping» del database e quindi aggiornare le applicazioni. Conosco persone che hanno caricato un intero nuovo cluster di database con un nuovo schema, questa è un'opzione se si dispone di un database schemaless come Mongo, ma in ogni caso non è un compito semplice. Se non ci sono altre domande, grazie per l'attenzione!

Un po' di pubblicità 🙂
Grazie per rimanere con noi. Ti piacciono i nostri articoli? Vuoi vedere più contenuti interessanti? Supportaci effettuando un ordine o raccomandandoci a qualcuno. , unica alternativa ai server entry-level, concepita da noi per te: (sono disponibili opzioni con RAID1 e RAID10, fino a 24 core e fino a 40GB DDR4).
Dell R730xd a metà prezzo nel data center Equinix Tier IV ad Amsterdam? Solo da noi nei Paesi Bassi! Dell R420 — 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — a partire da $99! Leggi di
Fonte: habr.com
