Best practice per la creazione di container

Last reviewed 2023-02-28 UTC

Questo articolo descrive una serie di best practice per la creazione dei container. Queste pratiche coprono un'ampia gamma di obiettivi, dall'accorciamento dei tempi di creazione alla creazione di immagini più piccole e più resilienti, con l'obiettivo di rendere i container più semplici da creare (ad esempio con Cloud Build) e più semplici da eseguire in Google Kubernetes Engine (GKE).

Queste best practice non hanno la stessa importanza. Ad esempio, potresti eseguire correttamente un carico di lavoro di produzione senza alcuni di questi, ma altri sono fondamentali. In particolare, l'importanza delle best practice relative alla sicurezza è soggettiva. La loro implementazione dipende dall'ambiente e dai vincoli.

Per ottenere il massimo da questo articolo, è necessaria una conoscenza di Docker e Kubernetes. Alcune best practice illustrate qui si applicano anche ai container Windows, ma la maggior parte presuppone che tu stia lavorando con i container Linux. Per consigli sull'esecuzione e l'utilizzo dei container, consulta Best practice per l'utilizzo dei container.

Crea una singola app per container

Importanza: ALTA

Quando inizi a lavorare con i container, è un errore comune trattarli come macchine virtuali (VM) in grado di eseguire molte cose diverse contemporaneamente. Un container può funzionare in questo modo, ma farlo riduce gran parte dei vantaggi del modello di container. Prendiamo ad esempio uno stack Apache/MySQL/PHP classico: potresti avere la tentazione di eseguire tutti i componenti in un singolo container. Tuttavia, la best practice prevede l'utilizzo di due o tre container diversi: uno per Apache, uno per MySQL e potenzialmente uno per PHP se esegui PHP-FPM.

Poiché un container è progettato per avere lo stesso ciclo di vita dell'app che ospita, ciascuno dei tuoi container deve contenere una sola app. Quando un container viene avviato, dovrebbe esserlo anche l'app e quando l'app si interrompe, così come il container. Il seguente diagramma mostra questa best practice.

Diagramma che mostra il processo di avvio senza un'immagine personalizzata.

Figura 1. Il container a sinistra segue la best practice. a differenza del contenitore sulla destra.

Se un contenitore contiene più app, queste potrebbero avere cicli di vita diversi o trovarsi in stati diversi. Ad esempio, potresti ritrovarti con un container in esecuzione, ma con uno dei suoi componenti principali che si è arrestato in modo anomalo o non risponde. Senza un controllo di integrità aggiuntivo, il sistema di gestione dei container complessivo (Docker o Kubernetes) non è in grado di determinare se il container è integro. Nel caso di Kubernetes, significa che se un componente di base non risponde, Kubernetes non riavvia automaticamente il container.

Nelle immagini pubbliche potresti vedere le seguenti azioni, ma non seguire l'esempio:

Gestire correttamente PID 1, gestione del segnale e processi zombie

Importanza: ALTA

Gli indicatori Linux sono il modo principale per controllare il ciclo di vita dei processi all'interno di un container. In linea con la best practice precedente, per collegare strettamente il ciclo di vita dell'app al container in cui si trova, assicurati che l'app gestisca correttamente i segnali Linux. Il segnale Linux più importante è SIGTERM perché termina un processo. La tua app potrebbe anche ricevere un segnale SIGKILL, che viene utilizzato per terminare il processo in modo non controllato, oppure un segnale SIGINT, che viene inviato quando digiti Ctrl+C e di solito viene trattato come SIGTERM.

Gli identificatori di processo (PID) sono identificatori univoci che il kernel Linux fornisce a ciascun processo. I PID hanno uno spazio dei nomi, il che significa che un container ha il proprio set di PID mappati ai PID sul sistema host. Il primo processo avviato all'avvio di un kernel Linux ha il PID 1. Per un normale sistema operativo, si tratta del sistema init, ad esempio systemd o SysV. Allo stesso modo, il primo processo avviato in un container ottiene il PID 1. Docker e Kubernetes usano gli indicatori per comunicare con i processi all'interno dei container, in particolare per terminarli. Sia Docker che Kubernetes possono inviare indicatori solo al processo che ha PID 1 all'interno di un container.

Nel contesto dei container, PID e indicatori Linux creano due problemi da considerare.

Problema 1: in che modo il kernel Linux gestisce i segnali

Il kernel Linux gestisce i segnali in modo diverso per il processo con PID 1 rispetto ad altri processi. I gestori di indicatori non vengono registrati automaticamente per questo processo, il che significa che indicatori come SIGTERM o SIGINT non avranno alcun effetto per impostazione predefinita. Per impostazione predefinita, devi terminare i processi utilizzando SIGKILL, impedendo così un arresto controllato. A seconda dell'app, l'utilizzo di SIGKILL può causare errori rivolti agli utenti, scritture interrotte (per i datastore) o avvisi indesiderati nel sistema di monitoraggio.

Problema 2: in che modo i sistemi di inizializzazione classici gestiscono i processi orfani

Anche i sistemi di inizializzazione classici come systemd vengono utilizzati per rimuovere (raccogliere) i processi orfani zombie. I processi orfani, ovvero i cui genitori sono deceduti, vengono ricollegati al processo con PID 1, che dovrebbe raccoglierli quando muoiono. A tale scopo, viene usato un normale sistema di inizializzazione. Ma in un container, questa responsabilità ricade su qualunque processo abbia PID 1. Se questo processo non gestisce correttamente la raccolta, rischi di esaurire la memoria o alcune altre risorse.

Questi problemi hanno diverse soluzioni comuni, descritte nelle sezioni seguenti.

Soluzione 1: esegui come PID 1 e registra i gestori dei segnali

Questa soluzione risolve solo il primo problema. È valido se l'app genera processi secondari in modo controllato (come spesso accade), evitando il secondo problema.

Il modo più semplice per implementare questa soluzione è avviare il processo con le istruzioni CMD e/o ENTRYPOINT nel Dockerfile. Ad esempio, nel seguente Dockerfile, nginx è il primo e unico processo da avviare.

FROM debian:11

RUN apt-get update && \
    apt-get install -y nginx

EXPOSE 80

CMD [ "nginx", "-g", "daemon off;" ]

A volte potrebbe essere necessario preparare l'ambiente nel container affinché il processo venga eseguito correttamente. In questo caso, la best practice prevede che il container avvii uno script shell all'avvio. Questo script shell ha il compito di preparare l'ambiente e avviare il processo principale. Tuttavia, se scegli questo approccio, lo script shell ha un PID 1, non il tuo processo, motivo per cui devi utilizzare il comando exec integrato per avviare il processo dallo script della shell. Il comando exec sostituisce lo script con il programma desiderato. Il processo eredita quindi il PID 1.

Soluzione 2: abilita la condivisione dello spazio dei nomi di processo in Kubernetes

Quando abiliti la condivisione dello spazio dei nomi di processo per un pod, Kubernetes utilizza un singolo spazio dei nomi di processo per tutti i container nel pod. Il container dell'infrastruttura di pod Kubernetes diventa PID 1 e raccoglie automaticamente i processi orfani.

Soluzione 3: utilizza un sistema di inizializzazione specializzato

Come in un ambiente Linux più classico, puoi anche utilizzare un sistema di inizializzazione per risolvere questi problemi. Tuttavia, i normali sistemi di inizializzazione come systemd o SysV sono troppo complessi e di grandi dimensioni proprio per questo scopo, motivo per cui consigliamo di utilizzare un sistema di inizializzazione come tini, creato appositamente per i container.

Se utilizzi un sistema di inizializzazione specializzato, il processo di inizializzazione ha PID 1 e svolge le seguenti operazioni:

  • Registra i gestori di indicatori corretti.
  • Garantisce che gli indicatori funzionino per la tua app.
  • Ripete eventuali processi zombie finali.

Puoi utilizzare questa soluzione in Docker stesso utilizzando l'opzione --init del comando docker run. Per utilizzare questa soluzione in Kubernetes, devi installare il sistema init nell'immagine container e utilizzarlo come punto di ingresso per il container.

Ottimizza per la cache di build Docker

Importanza: ALTA

La cache di build di Docker può accelerare la creazione di immagini container. Le immagini vengono create livello per livello e, in un Dockerfile, ogni istruzione crea un livello nell'immagine risultante. Durante una build, quando possibile, Docker riutilizza un livello di una build precedente e salta un passaggio potenzialmente costoso. Docker può usare la cache di build solo se è stata utilizzata in tutti i passaggi di build precedenti. Sebbene questo comportamento sia generalmente positivo, per velocizzare l'esecuzione delle build, devi considerare alcuni casi.

Ad esempio, per trarre il massimo vantaggio dalla cache di build Docker, devi posizionare i passi di build che cambiano spesso in fondo al Dockerfile. Se li posizioni all'inizio, Docker non può utilizzare la cache di build per gli altri passaggi di build che cambiano con minore frequenza. Poiché in genere una nuova immagine Docker viene creata per ogni nuova versione del codice sorgente, aggiungi il codice sorgente all'immagine il prima possibile nel Dockerfile. Nel seguente diagramma, puoi vedere che se modifichi STEP 1, Docker può riutilizzare solo i livelli del passaggio FROM debian:11. Se modifichi STEP 3, tuttavia, Docker può riutilizzare i livelli per STEP 1 e STEP 2.

Esempi di come utilizzare la cache di build Docker

Figura 2. Esempi di come utilizzare la cache della build Docker. In verde, i livelli che puoi riutilizzare. In rosso, i livelli che devono essere ricreati.

Il riutilizzo dei livelli ha un'altra conseguenza: se un passaggio di build si basa su qualsiasi tipo di cache archiviata nel file system locale, questa deve essere generata nello stesso passaggio di build. Se la cache non viene generata, il passaggio di build potrebbe essere eseguito con una cache obsoleta proveniente da una build precedente. Questo comportamento solitamente si verifica con gestori di pacchetti come apt o yum: devi aggiornare i repository nello stesso comando RUN usato per l'installazione dei pacchetti.

Se modifichi il secondo passaggio RUN nel Dockerfile seguente, il comando apt-get update non viene eseguito nuovamente e la cache apt non è aggiornata.

FROM debian:11

RUN apt-get update
RUN apt-get install -y nginx

Unisci invece i due comandi in un unico passaggio RUN:

FROM debian:11

RUN apt-get update && \
    apt-get install -y nginx

Rimuovi gli strumenti non necessari

Importanza: MEDIA

Per proteggere le tue app da malintenzionati, prova a ridurre la superficie di attacco della tua app rimuovendo gli strumenti non necessari. Ad esempio, rimuovi utilità come netcat, che puoi utilizzare per creare una shell inversa all'interno del sistema. Se netcat non è nel container, l'utente malintenzionato deve trovare un altro modo.

Questa best practice è valida per qualsiasi carico di lavoro, anche se non è containerizzato. La differenza è che questa best practice è ottimizzata per i container, piuttosto che per le VM classiche o i server bare metal.

Se rimuovi gli strumenti non necessari, puoi anche migliorare i processi di debug. Ad esempio, se implementi questa best practice in modo sufficientemente completo, log esaustivi, sistemi di tracciamento e profilazione possono diventare quasi obbligatori. In effetti, non puoi più fare affidamento sugli strumenti di debug locali perché spesso hanno privilegi elevati.

Contenuti del file system

La prima parte di questa best practice riguarda i contenuti dell'immagine container. Mantieni nell'immagine il minor numero possibile di elementi. Se riesci a compilare la tua app in un singolo file binario collegato in modo statico, l'aggiunta di questo programma binario all'immagine di scrap ti consente di ottenere un'immagine finale contenente solo l'app e nient'altro. Riducendo il numero di strumenti inclusi nell'immagine, riduci le azioni che un potenziale utente malintenzionato può fare nel container. Per ulteriori informazioni, consulta Creare l'immagine più piccola possibile.

Sicurezza del file system

Non avere strumenti nell'immagine non è sufficiente: devi impedire ai potenziali utenti malintenzionati di installare i propri strumenti. Puoi combinare due metodi qui:

  • Evita di essere eseguito come root all'interno del container: questo metodo offre un primo livello di sicurezza e potrebbe impedire, ad esempio, a utenti malintenzionati di modificare file di proprietà principale utilizzando un gestore di pacchetti incorporato nell'immagine (come apt-get o apk). Affinché questo metodo sia utile, devi disattivare o disinstallare il comando sudo. Questo argomento viene trattato in modo più ampio nella sezione Evitare di eseguire come root.

  • Avvia il container in modalità di sola lettura utilizzando il flag --read-only dal comando docker run o l'opzione readOnlyRootFilesystem in Kubernetes.

Crea l'immagine più piccola possibile

Importanza: MEDIA

La creazione di un'immagine più piccola offre vantaggi come tempi di caricamento e download più rapidi, che è particolarmente importante per l'avvio a freddo di un pod in Kubernetes: più piccola è l'immagine, più velocemente il nodo può scaricarla. Tuttavia, creare un'immagine di piccole dimensioni può essere difficile perché potresti includere inavvertitamente dipendenze della build o livelli non ottimizzati nell'immagine finale.

Usa l'immagine di base più piccola possibile

L'immagine di base è quella a cui si fa riferimento nell'istruzione FROM nel Dockerfile. Ogni altra istruzione nel Dockerfile si basa su questa immagine. Più piccola è l'immagine di base, più piccola è l'immagine risultante e più rapidamente può essere scaricata. Ad esempio, l'immagine alpine:3.17 è più piccola di 23 MB rispetto all'immagine ubuntu:22.04.

Puoi anche utilizzare l'immagine di base scratch, ovvero un'immagine vuota su cui puoi creare il tuo ambiente di runtime. Se la tua app è un programma binario collegato in modo statico, puoi utilizzare l'immagine scratch base nel seguente modo:

FROM scratch
COPY mybinary /mybinary
CMD [ "/mybinary" ]

Il seguente video sulle best practice per Kubernetes illustra ulteriori strategie per la creazione di container di piccole dimensioni riducendo al contempo l'esposizione alle vulnerabilità di sicurezza.

Riduci la quantità di disordine nell'immagine

Per ridurre le dimensioni dell'immagine, installa solo ciò che è strettamente necessario al suo interno. Potresti avere la tentazione di installare pacchetti aggiuntivi per poi rimuoverli in un passaggio successivo. Tuttavia, questo approccio non è sufficiente. Poiché ogni istruzione del Dockerfile crea un livello, rimuovendo i dati dall'immagine in un passaggio successivo rispetto al passaggio di creazione non riduce la dimensione dell'immagine complessiva (i dati sono ancora lì, ma nascosti in un livello più profondo). Considera questo esempio:

Dockerfile non valido Dockerfile valido

FROM debian:11
RUN apt-get update && \ apt-get install -y \ [buildpackage] RUN [build my app] RUN apt-get autoremove --purge \ -y [buildpackage] && \ apt-get -y clean && \ rm -rf /var/lib/apt/lists/*

FROM debian:11
RUN apt-get update && \ apt-get install -y \ [buildpackage] && \ [build my app] && \ apt-get autoremove --purge \ -y [buildpackage] && \ apt-get -y clean && \ rm -rf /var/lib/apt/lists/*

Nella versione non valida del Dockerfile, [buildpackage] e i file in /var/lib/apt/lists/* esistono ancora nel livello corrispondente al primo RUN. Questo livello fa parte dell'immagine e deve essere caricato e scaricato insieme al resto, anche se i dati che contiene non sono accessibili nell'immagine risultante.

Nella versione corretta del Dockerfile, tutto viene eseguito in un unico livello che contiene solo l'app creata. [buildpackage] e i file in /var/lib/apt/lists/* non esistono da nessuna parte nell'immagine risultante, nemmeno in un livello più profondo.

Per ulteriori informazioni sui livelli immagine, consulta Ottimizzare la cache di build di Docker.

Un altro ottimo modo per ridurre la quantità di disordine nell'immagine è utilizzare build in più fasi (introdotte in Docker 17.05). Le build in più fasi ti consentono di creare l'app in un primo container di "build" e utilizzare il risultato in un altro container, utilizzando lo stesso Dockerfile.

Processo di compilazione multi-fase Docker

Figura 3. Il processo di compilazione in più fasi di Docker.

Nel Dockerfile seguente, il programma binario hello viene creato in un primo container e inserito in un secondo. Poiché il secondo container si basa su scratch, l'immagine risultante contiene solo il file binario hello e non il file di origine e i file degli oggetti necessari durante la build. Il programma binario deve essere collegato in modo statico per funzionare senza la necessità di librerie esterne nell'immagine scratchpad.

FROM golang:1.20 as builder

WORKDIR /tmp/go
COPY hello.go ./
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags '-s' -o hello

FROM scratch
CMD [ "/hello" ]
COPY --from=builder /tmp/go/hello /hello

Prova a creare immagini con livelli comuni

Se devi scaricare un'immagine Docker, per prima cosa Docker controlla se sono già presenti alcuni livelli nell'immagine. Se ce l'hai, questi livelli non vengono scaricati. Questa situazione può verificarsi se in precedenza hai scaricato un'altra immagine che ha la stessa base dell'immagine che stai scaricando. Il risultato è che la quantità di dati scaricati è molto inferiore per la seconda immagine.

A livello di organizzazione, puoi sfruttare questa riduzione fornendo agli sviluppatori un insieme di immagini di base comuni e standard. I sistemi devono scaricare ogni immagine di base una sola volta. Dopo il download iniziale, sono necessari solo i livelli che rendono unica ogni immagine. In effetti, più le immagini hanno in comune, più veloce è il loro download.

Prova a creare immagini con livelli comuni

Figura 4. Creare immagini con livelli comuni.

Analizza le immagini per rilevare le vulnerabilità

Importanza: MEDIA

Le vulnerabilità del software sono un problema ben compreso nel mondo dei server e delle macchine virtuali bare-metal. Un modo comune per risolvere queste vulnerabilità è utilizzare un sistema di inventario centralizzato che elenca i pacchetti installati su ciascun server. Iscriviti ai feed delle vulnerabilità dei sistemi operativi upstream per ricevere informazioni su eventuali vulnerabilità che interessano i tuoi server, quindi correggili di conseguenza.

Tuttavia, poiché i container dovrebbero essere immutabili (vedi statelessness e immutabilità dei container per maggiori dettagli), non applicare patch in caso di vulnerabilità. La best practice consiste nel ricreare l'immagine, con le patch incluse, ed eseguire nuovamente il deployment. I container hanno un ciclo di vita molto più breve e un'identità meno definita rispetto ai server. Pertanto, l'utilizzo di un sistema di inventario centralizzato simile non è un modo ottimale per rilevare le vulnerabilità nei container.

Per aiutarti a risolvere questo problema, Artifact Analysis può analizzare le immagini alla ricerca di vulnerabilità di sicurezza nei pacchetti monitorati pubblicamente. Sono disponibili le seguenti opzioni:

Analisi automatica delle vulnerabilità

Se abilitata, questa funzionalità identifica le vulnerabilità dei pacchetti nelle immagini container. Le immagini vengono analizzate quando vengono caricate su Artifact Registry o Container Registry e i dati vengono monitorati continuamente per trovare nuove vulnerabilità per un massimo di 30 giorni dopo il push dell'immagine. Puoi utilizzare le informazioni riportate da questa funzione in diversi modi:

  • Crea un job di tipo cron che elenca le vulnerabilità e attivi il processo per correggerle, se esiste una correzione.
  • Non appena viene rilevata una vulnerabilità, utilizza l'integrazione Pub/Sub per attivare il processo di applicazione delle patch utilizzato dalla tua organizzazione.
API On-Demand Scanning

Se questa opzione è abilitata, puoi analizzare manualmente le immagini locali o archiviate in Artifact Registry o Container Registry. Questa funzionalità ti consente di rilevare e risolvere le vulnerabilità nelle prime fasi della pipeline di build. Ad esempio, puoi utilizzare Cloud Build per scansionare un'immagine dopo la sua creazione e quindi bloccare il caricamento su Artifact Registry se la scansione rileva vulnerabilità a un livello di gravità specificato. Se hai abilitato anche l'analisi automatica delle vulnerabilità, Artifact Registry analizza anche le immagini che carichi nel registro.

Ti consigliamo di automatizzare il processo di applicazione delle patch e di fare affidamento sulla pipeline di integrazione continua esistente utilizzata inizialmente per creare l'immagine. Se hai la certezza che la tua pipeline di deployment continuo, ti consigliamo di eseguire automaticamente il deployment dell'immagine fissa quando è pronta. Tuttavia, la maggior parte delle persone preferisce un passaggio di verifica manuale prima del deployment. Il seguente processo permette di:

  1. Archivia le tue immagini in Artifact Registry e abilita l'analisi delle vulnerabilità.
  2. Configura un job che recupera regolarmente nuove vulnerabilità da Artifact Registry e attivi una rigenerazione delle immagini, se necessario.
  3. Una volta create le nuove immagini, fai in modo che il tuo sistema di deployment continuo ne esegua il deployment in un ambiente di gestione temporanea.
  4. Controlla manualmente la presenza di problemi nell'ambiente di gestione temporanea.
  5. Se non vengono rilevati problemi, attiva manualmente il deployment in produzione.

Tagga correttamente le immagini

Importanza: MEDIA

Le immagini Docker sono generalmente identificate da due componenti: il nome e il tag. Ad esempio, per l'immagine google/cloud-sdk:419.0.0, google/cloud-sdk è il nome e 419.0.0 è il tag. Il tag latest viene utilizzato per impostazione predefinita se non ne fornisci uno nei comandi Docker. La coppia nome e tag è univoca in un dato momento. Tuttavia, puoi riassegnare il tag a un'immagine diversa in base alle esigenze.

Quando crei un'immagine, spetta a te taggarla correttamente. Segui norme di tagging coerenti e coerenti. Documenta i criteri di tagging in modo che gli utenti delle immagini possano comprenderli facilmente.

Le immagini container sono un modo per pacchettizzare e rilasciare un software. Il tagging dell'immagine consente agli utenti di identificare una versione specifica del software per scaricarla. Per questo motivo, collega strettamente il sistema di tagging delle immagini container ai criteri di rilascio del software.

Tagging tramite il controllo delle versioni semantico

Un modo comune per rilasciare un software è "taggare" (come nel comando git tag) una determinata versione del codice sorgente con un numero di versione. La specifica per il controllo delle versioni semantico fornisce un modo semplice per gestire i numeri di versione. In questo sistema, il software ha un numero di versione in tre parti: X.Y.Z, in cui:

  • X è la versione principale, incrementata solo in caso di modifiche all'API incompatibili.
  • Y è la versione secondaria, incrementata per le nuove funzionalità.
  • Z è la versione della patch, incrementata per le correzioni di bug.

Ogni incremento nel numero di versione secondaria o patch deve essere compatibile con le versioni precedenti.

Se utilizzi questo sistema o uno simile, tagga le immagini in base alla seguente norma:

  • Il tag latest fa sempre riferimento all'immagine più recente (possibilmente stabile). Questo tag viene spostato non appena viene creata una nuova immagine.
  • Il tag X.Y.Z fa riferimento a una versione specifica del software. Non spostarla su un'altra immagine.
  • Il tag X.Y fa riferimento alla release più recente della patch del ramo secondario X.Y del software. Viene spostato quando viene rilasciata una nuova versione della patch.
  • Il tag X fa riferimento all'ultima release della patch dell'ultima release secondaria del ramo principale X. Viene spostata quando viene rilasciata una nuova versione della patch o una nuova versione secondaria.

L'uso di questo criterio offre agli utenti la flessibilità di scegliere la versione del software da utilizzare. Possono scegliere una versione X.Y.Z specifica e avere la certezza che l'immagine non cambierà mai oppure possono ricevere aggiornamenti automatici scegliendo un tag meno specifico.

Tagging tramite hash di commit Git

Se disponi di un sistema avanzato di distribuzione continua e rilasci spesso il tuo software, probabilmente non utilizzi i numeri di versione come descritto nella specifica per il controllo delle versioni semantico. In questo caso, un modo comune per gestire i numeri di versione è utilizzare l'hash SHA-1 del commit Git (o una sua versione breve) come numero di versione. L'hash di commit Git è immutabile e fa riferimento a una versione specifica del software.

Puoi utilizzare questo hash di commit come numero di versione per il software, ma anche come tag per l'immagine Docker creata a partire da questa versione specifica del software. In questo modo le immagini Docker sono tracciabili: poiché in questo caso il tag immagine è immutabile, sai immediatamente quale versione specifica del tuo software è in esecuzione all'interno di un determinato container. Nella pipeline di distribuzione continua, automatizza l'aggiornamento del numero di versione utilizzato per i deployment.

Valuta attentamente se usare un'immagine pubblica

Importanza: N/D

Uno dei grandi vantaggi di Docker è l'enorme numero di immagini disponibili pubblicamente per tutti i tipi di software. Queste immagini ti consentono di iniziare rapidamente. Tuttavia, quando progetti una strategia di container per la tua organizzazione, potresti avere dei vincoli ai quali le immagini fornite pubblicamente non possono essere soddisfatte. Ecco alcuni esempi di vincoli che potrebbero rendere impossibile l'uso delle immagini pubbliche:

  • Vuoi controllare esattamente cosa c'è all'interno delle immagini.
  • Non vuoi dipendere da un repository esterno.
  • Vuoi controllare rigorosamente le vulnerabilità nel tuo ambiente di produzione.
  • Vuoi lo stesso sistema operativo di base in ogni immagine.

La risposta a tutti questi vincoli è la stessa e, sfortunatamente, è costosa: devi creare le tue immagini. Creare immagini personalizzate è sufficiente per un numero limitato di immagini, ma questo numero tende a crescere rapidamente. Per avere la possibilità di gestire un sistema di questo tipo su larga scala, considera l'utilizzo di quanto segue:

  • Un modo automatizzato per creare immagini in modo affidabile, anche per immagini create raramente. I trigger di build in Cloud Build sono un ottimo modo per raggiungere questo obiettivo.
  • Un'immagine di base standardizzata. Google fornisce alcune immagini di base che puoi utilizzare.
  • Un modo automatico per propagare gli aggiornamenti dell'immagine di base alle immagini "secondarie".
  • Un modo per risolvere le vulnerabilità nelle immagini. Per maggiori informazioni, consulta la pagina Eseguire l'analisi delle immagini per rilevare le vulnerabilità.
  • Un modo per applicare i tuoi standard interni sulle immagini create dai diversi team della tua organizzazione.

Sono disponibili diversi strumenti per applicare i criteri sulle immagini che crei e distribuisci:

  • container-diff può analizzare i contenuti delle immagini e persino confrontarne due.
  • container-structure-test può verificare se il contenuto di un'immagine è conforme a un insieme di regole da te definite.
  • Grafeas è un'API per i metadati degli artefatti in cui puoi archiviare i metadati delle tue immagini per verificare in un secondo momento se queste sono conformi alle tue norme.
  • Kubernetes dispone di controller di ammissione che puoi utilizzare per verificare una serie di prerequisiti prima di eseguire il deployment di un carico di lavoro in Kubernetes.

Potresti anche decidere di adottare un sistema ibrido, ad esempio utilizzando un'immagine pubblica come Debian o Alpine come immagine di base e costruendo il tutto su quell'immagine. In alternativa, puoi utilizzare immagini pubbliche per alcune delle immagini non critiche e creare immagini personalizzate per altri casi. Queste domande non hanno risposte giuste o sbagliate, ma bisogna rispondere.

Una nota sulle licenze

Prima di includere librerie e pacchetti di terze parti nell'immagine Docker, assicurati che le rispettive licenze lo consentano. Le licenze di terze parti potrebbero anche imporre limitazioni alla ridistribuzione, che si applicano quando pubblichi un'immagine Docker in un registro pubblico.

Passaggi successivi

Esplora le architetture di riferimento, i diagrammi e le best practice su Google Cloud. Dai un'occhiata al nostro Cloud Architecture Center.