Kubernetes è un ottimo strumento per l'esecuzione di container Docker in un ambiente di produzione clusterizzato. Tuttavia, ci sono compiti che Kubernetes non è in grado di risolvere. Durante il deployment frequente in un ambiente operativo, abbiamo bisogno di un sistema di Blue/Green deployment completamente automatizzato per evitare downtime in questo processo, che deve anche gestire le richieste HTTP esterne e gestire il caricamento SSL. Ciò richiede l'integrazione con un bilanciatore di carico, come ha-proxy. Un'altra sfida è il ridimensionamento semi-automatico del cluster Kubernetes stesso quando si opera in un ambiente cloud, ad esempio una diminuzione parziale della dimensione del cluster durante la notte.
Sebbene Kubernetes non disponga di queste funzionalità direttamente 'out of the box', offre API che possono essere utilizzate per affrontare compiti simili. Gli strumenti per il deployment automatizzato Blue/Green e il ridimensionamento del cluster Kubernetes sono stati sviluppati all'interno del progetto Cloud RTI, creato sulla base di software open-source.
In questo articolo, la trascrizione video spiega come configurare Kubernetes insieme ad altri componenti open source per creare un ambiente di produzione che gestisce il codice dal commit git senza interruzioni in produzione.

Quindi, una volta che hai accesso alle tue applicazioni dal mondo esterno, puoi procedere con la configurazione completa dell'automazione, raggiungendo il punto in cui puoi eseguire un commit git e assicurarti che questo commit git si conclusa in produzione. Naturalmente, durante l'implementazione di questi passaggi e nel processo di deployment, non vogliamo affrontare interruzioni. Pertanto, ogni automazione in Kubernetes inizia con l'API.

Kubernetes non è uno strumento che può essere utilizzato in modo produttivo «così com'è». Certo, puoi farlo, utilizzare kubectl e così via, ma l'API è la parte più interessante e utile di questa piattaforma. Utilizzando l'API come un insieme di funzioni, puoi accedere praticamente a tutto ciò che desideri fare in Kubernetes. Anche kubectl stesso utilizza l'API REST.
È REST, quindi puoi utilizzare qualsiasi linguaggio e strumento per lavorare con questa API, ma le librerie client renderanno la tua vita molto più semplice. Il mio team ha scritto 2 di queste librerie: una per Java / OSGi e una per Go. La seconda non viene utilizzata spesso, ma in ogni caso hai a disposizione queste risorse utili. Sono progetti open-source con licenza parziale. Ci sono molte di queste librerie disponibili per diversi linguaggi, quindi puoi scegliere quelle più adatte.

Quindi, prima di iniziare l'automazione del deployment, è fondamentale assicurarsi che questo processo non subisca interruzioni. Ad esempio, il nostro team esegue il deployment in produzione a metà giornata, quando le persone utilizzano maggiormente le applicazioni, quindi è molto importante evitare ritardi in questo processo. Per prevenire downtime, si utilizzano 2 metodi: il deployment blue/green o l'aggiornamento rolling. 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 deployment hai versioni diverse dell'applicazione in esecuzione contemporaneamente. In tal caso, puoi aggiornare l'interfaccia utente mentre il backend continua a lavorare con la vecchia versione, comportando l'interruzione del funzionamento dell'applicazione. Pertanto, sotto il profilo della programmazione, lavorare in tali condizioni è piuttosto complicato.
Questa è una delle ragioni per cui preferiamo utilizzare il blue/green deployment per automatizzare il dispiegamento delle nostre applicazioni. Con questo approccio, è necessario assicurarsi che solo una versione dell'applicazione sia attiva in un dato momento.
Il meccanismo del blue/green deployment funziona nel seguente modo. Riceviamo il traffico per le nostre applicazioni attraverso ha-proxy, che lo instrada verso le repliche in esecuzione della stessa versione dell'applicazione.
Quando viene effettuato un nuovo dispiegamento, utilizziamo Deployer, a cui vengono forniti i nuovi componenti, e si occupa del deploy della nuova versione. Il deploy della nuova versione dell'applicazione significa che viene "attivato" un nuovo insieme di repliche, che vengono quindi avviate in un pod separato e nuovo. Tuttavia, ha-proxy non è a conoscenza di esse e al momento non instrada alcun carico di lavoro verso di esse.
Pertanto, è fondamentale eseguire prima il controllo della salute delle nuove versioni, per garantire che le repliche siano pronte a gestire il carico.

Tutti i componenti di distribuzione devono supportare una qualche forma di health check. Può trattarsi di un semplice controllo HTTP in cui si ottiene un codice di stato 200, oppure di un controllo più approfondito in cui si verifica la connessione tra le repliche e il database e altri servizi, la stabilità delle connessioni nell'ambiente dinamico, e se tutto si avvia e funziona correttamente. Questo processo può essere piuttosto complesso.

Dopo che il sistema ha verificato il funzionamento di tutte le repliche aggiornate, Deployer aggiornerà la configurazione e trasmetterà il confd corretto, che riconfigurerà ha-proxy.

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

Questo meccanismo non è una caratteristica esclusiva di Kubernetes. Il concetto di Blue/green deployment esiste da molto tempo e ha sempre utilizzato un bilanciatore di carico. Inizialmente si dirige tutto il traffico verso la vecchia versione dell'applicazione, e dopo l'aggiornamento si trasferisce completamente sulla nuova versione. Questo principio è utilizzato non solo in Kubernetes.
Ora vi presenterò un nuovo componente di distribuzione - Deployer, che esegue il controllo delle funzionalità, riconfigura il proxy e così via. Questo è un concetto che non è rivolto al mondo esterno e esiste all'interno di Kubernetes. Vi mostrerò come creare il proprio concetto di Deployer utilizzando strumenti open-source.
Quindi, la prima cosa che fa Deployer è creare un controller di replica RC, utilizzando l'API di Kubernetes. Quest'API crea pod e servizi per la successiva distribuzione, ovvero crea un nuovo cluster per le nostre applicazioni. Una volta che RC verifica che le repliche siano state avviate, eseguirà un controllo delle loro funzionalità tramite il Health check. A questo scopo, Deployer utilizza il comando GET /health. Questo esegue i componenti di controllo appropriati e verifica tutti gli elementi che garantiscono il funzionamento del cluster.

Dopo che tutti i nodi hanno segnalato il loro stato di «salute», Deployer crea un nuovo elemento di configurazione: uno storage distribuito etcd, che viene utilizzato internamente in Kubernetes, inclusa la memorizzazione della configurazione del bilanciatore di carico. Registriamo i dati in etcd e un piccolo strumento confd monitora etcd per eventuali nuovi dati.
Se rileva eventuali modifiche alla configurazione originale, 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 ai nuovi servizi, che garantiscono il funzionamento della nuova versione delle nostre applicazioni.

Come potete vedere, nonostante l'abbondanza di componenti, non c'è nulla di complicato. Dovete solo prestare maggiore attenzione all'API e a etcd. Voglio parlarvi di un deployer open-source che utilizziamo noi stessi: Amdatu Kubernetes Deployer.

È uno strumento per l'orchestrazione dei deployment di Kubernetes, dotato delle seguenti funzionalità:
- implementazione di Blue/Green deployment;
- configurazione del bilanciatore di carico esterno;
- gestione dei manifesti di deployment;
- gestione del deployment effettivo;
- verifica della funzionalità dei controlli di salute durante il deployment;
- introduzione di variabili d'ambiente nei pod.
Questo Deployer è costruito sopra l'API di Kubernetes e fornisce un'API REST per gestire i manifesti e i deployment, oltre a un'API Websocket per lo streaming dei log durante il deployment.
Inserisce i dati di configurazione del bilanciatore di carico in etcd, pertanto puoi evitare di utilizzare ha-proxy con supporto 'out of the box', facilitando l'uso del tuo file di configurazione del bilanciatore. Amdatu Deployer è scritto in Go, proprio come Kubernetes, ed è concesso in licenza Apache.
Prima di iniziare ad utilizzare questa versione del deployer, ho utilizzato il seguente manifesto di deployment, in cui sono specificati i parametri necessari.

Uno dei parametri importanti di questo codice è l'inclusione del flag "useHealthCheck". Dobbiamo specificare che, durante il processo di deployment, è necessario effettuare un controllo dello stato. Questo parametro può essere disattivato quando nel deployment vengono utilizzati contenitori di terze parti che non richiedono verifiche. In questo descrittore è anche indicato il numero di repliche e l'URL del frontend necessario per ha-proxy. Alla fine, viene menzionato il flag della specifica del pod "podspec", che si rivolge a Kubernetes per ottenere informazioni sulla configurazione delle porte, sull'immagine e così via. Si tratta di un descrittore piuttosto semplice in formato JSON.
Un altro strumento che fa parte del progetto open-source Amdatu è Deploymentctl. Dispone di un'interfaccia utente UI per configurare il deployment, conserva la cronologia dei deployment e contiene webhook per callback da parte di utenti e sviluppatori esterni. È possibile non utilizzare l'UI, poiché Amdatu Deployer è un REST API, ma questa interfaccia può semplificare notevolmente il deployment senza dover ricorrere a nessun API. Deploymentctl è scritto in OSGi/Vertx utilizzando Angular 2.
Ora mostrerò quanto detto sopra sullo schermo, utilizzando una registrazione pre-creata, così non dovrete aspettare. Deployremo una semplice applicazione in Go. Non preoccupatevi se non avete mai avuto a che fare con Go, si tratta di un'applicazione molto semplice, quindi dovrebbe esservi tutto chiaro.

Qui creiamo un server HTTP che risponde solo a /health, quindi questa applicazione controlla solo lo stato del health check e nient'altro. Se il controllo ha esito positivo, verrà mostrata la struttura JSON qui sotto. Essa contiene la versione dell'applicazione che sarà distribuita dal deployer, un messaggio che vedete nella parte superiore del file, e un valore booleano che indica se la nostra applicazione è attiva o meno.
Con l'ultima riga sono stato un po' furbo, perché ho inserito un valore booleano fisso in cima al file, che in seguito mi aiuterà a distribuire anche un'applicazione "non sana". Più tardi ne parleremo.
Quindi, iniziamo. Per prima cosa controlliamo la presenza di eventuali pod in esecuzione usando il comando ~ kubectl get pods e, non ricevendo risposta dall'URL del frontend, ci assicuriamo che non ci siano deployment in corso al momento.

Successivamente, sullo schermo vedete l'interfaccia Deploymentctl di cui parlavo, in cui vengono impostati i parametri di distribuzione: namespace, nome dell'applicazione, versione di distribuzione, numero di repliche, URL del frontend, nome del container, immagine, limiti delle risorse, numero di porta per il health check, e così via. I limiti delle risorse sono molto importanti, poiché permettono di sfruttare al massimo la capacità dell'hardware. Qui è possibile anche visualizzare il registro di distribuzione.

Se adesso ripetiamo il comando ~ kubectl get pods, possiamo vedere che il sistema 'si blocca' per 20 secondi, durante i quali avviene la riconfigurazione dell'ha-proxy. Dopo di che, il pod si avvia e la nostra replica può essere vista nel registro di distribuzione.

Ho tagliato dal video l'attesa di 20 secondi, e ora vedete sullo schermo che la prima versione dell'applicazione è stata distribuita. Tutto questo è stato fatto solo tramite 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 colloca nel registro Docker, dopodiché premiamo nuovamente il pulsante «Deploy» nella finestra Deploymentctl. In questo modo, viene avviato automaticamente il log del deployment proprio come è avvenuto per il deployment della prima versione dell'applicazione.

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

Il bilanciatore di carico attende finché non viene eseguita la verifica di salute, dopodiché reindirizzerà il traffico alla nuova versione. Dopo 20 secondi passiamo a curl e vediamo che ora abbiamo distribuito la versione 2 dell'applicazione, mentre la prima è stata eliminata.

Si è trattato di un deployment di un'applicazione "sana" — healthy. Vediamo cosa succede se modifico il valore del parametro Healthy da true a false per la nuova versione dell'applicazione, provando a distribuire un'applicazione non sana, che non ha superato il controllo di funzionalità. Questo può accadere se, durante la fase di sviluppo, sono stati commessi errori di configurazione e l'applicazione è stata inviata in produzione in quel modo.
Come potete vedere, il deployment attraversa tutte le fasi sopra indicate, e ~ kubectl get pods mostra che entrambi i pod sono in esecuzione. Ma a differenza del precedente deployment, il log mostra uno stato di timeout. Questo significa che, a causa del fallimento del controllo di integrità, la nuova versione dell'applicazione non può essere distribuita. Di conseguenza, vedete che il sistema è tornato a utilizzare la vecchia versione dell'applicazione, mentre la nuova versione è stata semplicemente rimossa.

Il vantaggio è che anche se ricevi un numero enorme di richieste simultanee nell'applicazione, queste non noteranno nemmeno un'interruzione durante il processo di implementazione. Se testi questa applicazione utilizzando il framework Gatling, che gli invia il massimo numero possibile di richieste, nessuna di queste verrà scartata. Ciò significa che i nostri utenti non noteranno nemmeno gli aggiornamenti delle versioni in tempo reale. Se la modifica dovesse fallire, si continuerà a lavorare sulla vecchia versione; se avrà successo, gli utenti passeranno alla nuova versione.
C'è solo una cosa che può portare a un fallimento: se il health check è passato con successo, ma l'applicazione si è bloccata non appena ha ricevuto il carico di lavoro, significa che il collasso si verificherà solo dopo il completamento del deployment. In questo caso, dovrai tornare manualmente a una versione precedente. Abbiamo quindi visto come utilizzare Kubernetes con gli strumenti open-source a lui dedicati. La procedura di deployment sarà molto più semplice se integrerai questi strumenti nei pipeline di creazione/deployment. Puoi utilizzare sia l'interfaccia utente sia automatizzare completamente questo processo applicando, ad esempio, un commit al master.

Il nostro server di build creerà un'immagine Docker, la inserirà in Docker Hub o in qualsiasi altro registro che utilizzi. Docker Hub supporta webhook, quindi possiamo avviare un deployment remoto tramite Deployer come indicato sopra. In questo modo è possibile automatizzare completamente il deployment dell'applicazione in un ambiente di produzione potenziale.
Passiamo a considerare il tema successivo: la scalabilità del cluster Kubernetes. È importante notare che il comando kubectl è uno strumento per la scalabilità. Con esso, possiamo facilmente aumentare il numero di repliche nel nostro cluster esistente. Tuttavia, nella pratica, vogliamo generalmente aumentare il numero di nodi, piuttosto che di pod.

In questo contesto, durante le ore lavorative potrebbe essere necessario aumentare il numero di istanze, mentre durante la notte, per ridurre i costi dei servizi Amazon, si potrebbe voler diminuire il numero di istanze dell'applicazione. Questo non significa che sia sufficiente scalare solo il numero dei pod, poiché anche se uno dei nodi è inattivo, si dovrà comunque pagare per esso a Amazon. Quindi, oltre a scalare i pod, sarà necessario scalare anche il numero di macchine utilizzate.
Questo può presentare delle difficoltà, perché indipendentemente dal fatto che si utilizzi Amazon o un altro servizio cloud, Kubernetes non ha alcuna conoscenza riguardo al numero di macchine in uso. Non esiste uno strumento in grado di scalare il sistema a livello di nodi.

Dunque, dovremo occuparci sia dei nodi che dei pod. Possiamo facilmente scalare il lancio di nuovi nodi utilizzando l'API di AWS e le istanze del gruppo di scaling 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 registra come nodo, si iscrive nel registro del master e inizia a lavorare. Successivamente, è possibile aumentare il numero di repliche da utilizzare sui nodi creati. La riduzione della scala richiede maggiore attenzione, poiché è necessario garantire che tale operazione non porti alla distruzione delle applicazioni già in esecuzione dopo la disattivazione delle macchine "non necessarie". Per prevenire tale scenario, è necessario portare i nodi allo stato di "unschedulable". Questo significa che il pianificatore predefinito ignorerà questi nodi quando pianifica i pod DaemonSet. Il pianificatore non rimuoverà nulla da questi server, ma non avvierà nuovi contenitori lì. Il passo successivo consiste nel drenare il nodo, ossia spostare i pod attivi su un'altra macchina o su altri nodi che abbiano sufficiente capacità per farlo. Una volta verificato che non ci sono più contenitori su questi nodi, possono essere eliminati da Kubernetes. Dopo di che, per Kubernetes, semplicemente smetteranno di esistere. Infine, è necessario utilizzare l'API AWS per disattivare i nodi o le macchine non necessari.
Puoi utilizzare Amdatu Scalerd, un altro strumento open-source per la scalabilità simile all'API di AWS. Offre un'interfaccia a riga di comando per aggiungere o rimuovere nodi in un cluster. Una caratteristica interessante è la possibilità di configurare il pianificatore tramite il seguente file json.

Il codice mostrato riduce della metà la capacità del cluster durante le ore notturne. È configurato sia per il numero di repliche esistenti che per la capacità desiderata del cluster Amazon. Utilizzare questo pianificatore ridurrà automaticamente il numero di nodi durante la 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 meglio credi.
Vorrei attirare la vostra attenzione sul fatto che molte persone mi dicono: «Va bene, ma cosa ne è del mio database, che di solito rimane in uno stato statico?» Come si può eseguire qualcosa di simile in un ambiente così dinamico come Kubernetes? A mio avviso, non dovreste farlo, non dovreste cercare di organizzare il funzionamento di un data store in Kubernetes. Tecnicamente è possibile, e ci sono guide in merito su Internet, ma complicherebbe seriamente la vostra vita.
Sì, in Kubernetes esiste il concetto di storage persistente e potete tentare di far funzionare data store come Mongo o MySQL, ma è un compito piuttosto impegnativo. Questo perché i data store non supportano completamente l'interazione con un ambiente dinamico. La maggior parte dei database richiede una configurazione significativa, compresa la configurazione manuale del cluster, non ama l'auto-scaling e altre cose simili.
Quindi non complicatevi la vita cercando di avviare uno storage in Kubernetes. Organizzate il loro funzionamento nel modo tradizionale con l'uso di servizi familiari e semplicemente consentite a Kubernetes di utilizzarli.

In conclusione, voglio presentarvi la piattaforma Cloud RTI basata su Kubernetes, su cui lavora il mio team. Essa fornisce una gestione centralizzata dei log, il monitoraggio delle applicazioni e dei cluster e possiede molte altre funzioni utili che vi saranno preziose. Utilizza vari strumenti open-source, come Grafana per la visualizzazione del monitoraggio.


È sorta una 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 si trovano ancora su indirizzi IP virtuali. Non potete usarli per le porte delle macchine ospitanti esterne, perché se Amazon sovraccarica il proprio host cloud, l'indirizzo cambia. Ecco perché posizioniamo ha-proxy davanti ai servizi — per creare una struttura più statica per un'interazione continua del traffico con Kubernetes.
Un'altra buona domanda è: come possiamo gestire le modifiche allo schema del database durante il blue/green deployment? Infatti, 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, dopodiché è possibile aggiornare il database e successivamente aggiornare le applicazioni stesse. È possibile eseguire un 'hot swapping' del database e poi aggiornare le applicazioni. Conosco persone che hanno avviato un nuovo cluster 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 essere con noi. Ti piacciono i nostri articoli? Vuoi vedere più contenuti interessanti? Supportaci effettuando un ordine o raccomandandoci ai tuoi conoscenti, , un'alternativa unica ai server entry-level, che abbiamo creato per te: (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! Scopri di più su
Fonte: habr.com
