Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Il primo passaggio della distribuzione a Kubernetes è l'inserimento dell'applicazione in un contenitore. In questa serie vedremo come creare un'immagine contenitore piccola e sicura.
Grazie a Docker, creare immagini container non è mai stato così semplice. Specifica un'immagine di base, aggiungi le modifiche e crea un contenitore.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Sebbene questa tecnica sia ottima per iniziare, l'utilizzo di immagini di base predefinite può portare a un lavoro non sicuro con immagini di grandi dimensioni piene di vulnerabilità.

Inoltre, la maggior parte delle immagini in Docker utilizza Debian o Ubuntu come immagine di base e, sebbene ciò fornisca un'eccellente compatibilità e una facile personalizzazione (un file Docker richiede solo due righe di codice), le immagini di base possono aggiungere centinaia di megabyte di carico aggiuntivo al contenitore. Ad esempio, un semplice file node.js per un'applicazione Go "hello-world" è di circa 700 megabyte, mentre la tua applicazione effettiva ha solo pochi megabyte.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Quindi tutto questo carico di lavoro aggiuntivo è uno spreco di spazio digitale e un ottimo nascondiglio per vulnerabilità e bug di sicurezza. Consideriamo quindi due modi per ridurre le dimensioni di un'immagine contenitore.

Il primo è l'uso di immagini base piccole, il secondo è l'uso del Builder Pattern. Utilizzare immagini di base più piccole è probabilmente il modo più semplice per ridurre le dimensioni del contenitore. Molto probabilmente, il linguaggio o lo stack in uso fornisce un'immagine dell'applicazione originale molto più piccola dell'immagine predefinita. Diamo un'occhiata al nostro contenitore node.js.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Per impostazione predefinita in Docker, la dimensione dell'immagine base node:8 è 670 MB e la dimensione dell'immagine node:8-alpine è solo 65 MB, ovvero 10 volte più piccola. Utilizzando l'immagine base alpina più piccola, ridurrai significativamente le dimensioni del tuo contenitore. Alpine è una distribuzione Linux piccola e leggera, molto popolare tra gli utenti Docker perché è compatibile con molte applicazioni mantenendo i contenitori piccoli. A differenza dell'immagine "nodo" Docker standard, "node:alpine" rimuove molti file e programmi di servizio, lasciando solo quelli sufficienti per eseguire l'applicazione.

Per passare a un'immagine di base più piccola, aggiorna semplicemente il Dockerfile per iniziare a lavorare con la nuova immagine di base:

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Ora, a differenza della vecchia immagine onbuild, devi copiare il codice nel contenitore e installare eventuali dipendenze. In un nuovo Dockerfile, il contenitore inizia con un'immagine node:alpine, quindi crea una directory per il codice, installa le dipendenze utilizzando il gestore pacchetti NPM e infine esegue server.js.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Questo aggiornamento dà come risultato un contenitore di dimensioni 10 volte più piccole. Se il tuo linguaggio di programmazione o stack non dispone della funzionalità di riduzione dell'immagine di base, utilizza Alpine Linux. Fornirà inoltre la possibilità di gestire completamente il contenuto del contenitore. Usare immagini di base piccole è un ottimo modo per creare rapidamente piccoli contenitori. Ma è possibile ottenere una riduzione ancora maggiore utilizzando il modello Builder.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Nei linguaggi interpretati, il codice sorgente viene prima passato all'interprete e poi eseguito direttamente. Nei linguaggi compilati, il codice sorgente viene prima convertito in codice compilato. Tuttavia, la compilazione spesso utilizza strumenti che non sono effettivamente necessari per eseguire il codice. Ciò significa che puoi rimuovere completamente questi strumenti dal contenitore finale. Puoi usare Builder Pattern per questo.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Il codice viene creato nel primo contenitore e compilato. Il codice compilato viene quindi inserito in un contenitore finale senza i compilatori e gli strumenti necessari per compilare quel codice. Eseguiamo un'applicazione Go attraverso questo processo. Per prima cosa passeremo dall'immagine onbuild ad Alpine Linux.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Nel nuovo Dockerfile, il contenitore inizia con un'immagine golang:alpine. Quindi crea una directory per il codice, la copia nel codice sorgente, crea quel codice sorgente ed esegue l'applicazione. Questo contenitore è molto più piccolo del contenitore onbuild, ma contiene comunque il compilatore e altri strumenti Go di cui non abbiamo realmente bisogno. Quindi estraiamo semplicemente il programma compilato e inseriamolo nel suo contenitore.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Potresti notare qualcosa di strano in questo file Docker: contiene due righe FROM. La prima sezione di 4 righe è esattamente identica al precedente Dockerfile, tranne per il fatto che utilizza la parola chiave AS per denominare questa fase. La sezione successiva ha una nuova riga FROM per iniziare una nuova immagine, dove invece dell'immagine golang:alpine utilizzeremo Raw alpine come immagine di base.

Raw Alpine Linux non ha alcun certificato SSL installato, il che causerà il fallimento della maggior parte delle chiamate API su HTTPS, quindi installiamo alcuni certificati CA root.

Ora arriva la parte divertente: per copiare il codice compilato dal primo contenitore al secondo è sufficiente utilizzare il comando COPY situato alla riga 5 della seconda sezione. Verrà copiato solo un file dell'applicazione e non influirà sugli strumenti dell'utilità Go. Il nuovo file Docker a più fasi conterrà un'immagine del contenitore di soli 12 megabyte, rispetto all'immagine del contenitore originale che era di 700 megabyte, il che è una grande differenza!
Pertanto, l'utilizzo di immagini di base piccole e Builder Pattern è un ottimo modo per creare contenitori molto più piccoli senza molto lavoro.
È possibile che, a seconda dello stack dell'applicazione, esistano altri modi per ridurre le dimensioni dell'immagine e del contenitore, ma i contenitori piccoli offrono davvero un vantaggio misurabile? Diamo un'occhiata a due aree in cui i piccoli contenitori sono estremamente efficaci: prestazioni e sicurezza.

Per valutare l'aumento delle prestazioni, considerare la durata del processo di creazione di un contenitore, inserimento nel registro (push) e quindi recupero da lì (pull). Puoi vedere che un contenitore più piccolo ha un netto vantaggio rispetto a un contenitore più grande.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Docker memorizzerà nella cache i livelli in modo che le build successive saranno molto veloci. Tuttavia, molti sistemi CI utilizzati per creare e testare i contenitori non memorizzano nella cache i livelli, quindi si ottiene un notevole risparmio di tempo. Come puoi vedere, il tempo per costruire un contenitore grande, a seconda della potenza della tua macchina, va da 34 a 54 secondi e, quando si utilizza un contenitore, ridotto utilizzando il modello Builder, da 23 a 28 secondi. Per operazioni di questo tipo l'aumento di produttività sarà del 40-50%. Quindi pensa solo a quante volte costruisci e testi il ​​tuo codice.

Dopo aver creato il contenitore, devi eseguire il push della sua immagine (immagine del contenitore push) nel registro del contenitore in modo da poterlo utilizzare nel tuo cluster Kubernetes. Ti consiglio di utilizzare Google Container Registry.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Con Google Container Registry (GCR), paghi solo per l'archiviazione grezza e il networking e non sono previsti costi aggiuntivi per la gestione dei contenitori. È privato, sicuro e molto veloce. GCR utilizza molti trucchi per velocizzare l'operazione di pull. Come puoi vedere, l'inserimento di un contenitore Docker Container Image utilizzando go:onbuild richiederà dai 15 ai 48 secondi, a seconda delle prestazioni del computer, e la stessa operazione con un contenitore più piccolo richiederà dai 14 ai 16 secondi, e per macchine meno produttive il vantaggio nella velocità operativa aumenta di 3 volte. Per le macchine più grandi, il tempo è più o meno lo stesso, poiché GCR utilizza una cache globale per un database condiviso di immagini, il che significa che non è necessario caricarle affatto. In un computer a basso consumo, la CPU è il collo di bottiglia, quindi il vantaggio di utilizzare contenitori piccoli è qui molto maggiore.

Se utilizzi GCR, ti consiglio vivamente di utilizzare Google Container Builder (GCB) come parte del tuo sistema di compilazione.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Come puoi vedere, il suo utilizzo consente di ottenere risultati molto migliori nel ridurre la durata dell'operazione Build+Push rispetto anche a una macchina produttiva: in questo caso, il processo di creazione e invio di contenitori all'host è accelerato di quasi 2 volte . Inoltre, ricevi 120 minuti di costruzione gratuiti ogni giorno, che nella maggior parte dei casi coprono le tue esigenze di costruzione di container.

Poi arriva la metrica prestazionale più importante: la velocità di recupero o download dei contenitori Pull. E se non ti interessa molto il tempo impiegato in un'operazione push, la durata del processo pull ha un impatto serio sulle prestazioni complessive del sistema. Supponiamo che tu abbia un cluster di tre nodi e uno di essi fallisce. Se utilizzi un sistema di gestione come Google Kubernetes Engine, sostituirà automaticamente il nodo morto con uno nuovo. Tuttavia, questo nuovo nodo sarà completamente vuoto e dovrai trascinarvi tutti i tuoi contenitori affinché inizi a funzionare. Se l'operazione di pull richiede abbastanza tempo, il cluster verrà eseguito con prestazioni inferiori per tutto il tempo.

Esistono molti casi in cui ciò può accadere: aggiunta di un nuovo nodo a un cluster, aggiornamento dei nodi o persino passaggio a un nuovo contenitore per la distribuzione. Pertanto, ridurre al minimo il tempo di estrazione diventa un fattore chiave. È innegabile che un contenitore piccolo si scarica molto più velocemente di uno grande. Se esegui più contenitori in un cluster Kubernetes, il risparmio di tempo può essere significativo.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Dai un'occhiata a questo confronto: un'operazione di pull su contenitori di piccole dimensioni richiede 4-9 volte meno tempo, a seconda della potenza della macchina, rispetto alla stessa operazione utilizzando go:onbuild. L'utilizzo di immagini di base di contenitori di piccole dimensioni condivise riduce significativamente il tempo e la velocità con cui i nuovi nodi Kubernetes possono essere distribuiti e messi online.

Consideriamo la questione della sicurezza. I contenitori più piccoli sono considerati molto più sicuri di quelli più grandi perché hanno una superficie di attacco minore. É davvero? Una delle funzionalità più utili di Google Container Registry è la possibilità di scansionare automaticamente i contenitori alla ricerca di vulnerabilità. Alcuni mesi fa ho creato sia contenitori onbuild che multistage, quindi vediamo se ci sono delle vulnerabilità lì.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Il risultato è sorprendente: solo 3 vulnerabilità medie sono state rilevate in un contenitore piccolo, mentre in un contenitore grande sono state trovate 16 vulnerabilità critiche e 376 altre vulnerabilità. Se osserviamo il contenuto di un grande contenitore, possiamo vedere che la maggior parte dei problemi di sicurezza non hanno nulla a che fare con la nostra applicazione, ma riguardano programmi che nemmeno utilizziamo. Quindi, quando le persone parlano di un'ampia superficie di attacco, questo è ciò che intendono.

Migliori pratiche Kubernetes. Creazione di piccoli contenitori

Il punto è chiaro: costruisci contenitori piccoli perché forniscono vantaggi reali in termini di prestazioni e sicurezza al tuo sistema.

Migliori pratiche Kubernetes. Organizzazione di Kubernetes con namespace

Alcuni annunci 🙂

Grazie per stare con noi. Ti piacciono i nostri articoli? Vuoi vedere contenuti più interessanti? Sostienici effettuando un ordine o raccomandando agli amici, cloud VPS per sviluppatori da $ 4.99, un analogo unico dei server entry-level, che è stato inventato da noi per te: Tutta la verità su VPS (KVM) E5-2697 v3 (6 core) 10 GB DDR4 480 GB SSD 1 Gbps da $ 19 o come condividere un server? (disponibile con RAID1 e RAID10, fino a 24 core e fino a 40 GB DDR4).

Dell R730xd 2 volte più economico nel data center Equinix Tier IV ad Amsterdam? Solo qui 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV da $199 In Olanda! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - da $99! Leggi Come costruire Infrastructure Corp. classe con l'utilizzo di server Dell R730xd E5-2650 v4 del valore di 9000 euro per un centesimo?

Fonte: habr.com

Aggiungi un commento