Quando possibile, unisco sempre i comandi che creano file con i comandi che eliminano gli stessi file in un'unica RUN
riga. Questo perché ogni RUN
riga aggiunge un livello all'immagine, l'output è letteralmente le modifiche al filesystem che potresti vedere docker diff
sul contenitore temporaneo che crea. Se si elimina un file creato in un livello diverso, tutto il file system dell'unione fa è registrare la modifica del file system in un nuovo livello, il file esiste ancora nel livello precedente e viene spedito in rete e archiviato su disco. Quindi, se scarichi il codice sorgente, lo estrai, lo compili in un file binario e quindi elimini i file tgz e sorgente alla fine, vuoi davvero che tutto ciò avvenga in un singolo livello per ridurre le dimensioni dell'immagine.
Successivamente, ho diviso personalmente i livelli in base al loro potenziale di riutilizzo in altre immagini e al previsto utilizzo della cache. Se ho 4 immagini, tutte con la stessa immagine di base (es. Debian), posso inserire una raccolta di utilità comuni nella maggior parte di quelle immagini nel comando di prima esecuzione in modo che le altre immagini traggano vantaggio dalla memorizzazione nella cache.
L'ordine nel Dockerfile è importante quando si guarda al riutilizzo della cache delle immagini. Guardo tutti i componenti che si aggiorneranno molto raramente, possibilmente solo quando l'immagine di base si aggiorna e li metto in alto nel Dockerfile. Verso la fine del Dockerfile, includo tutti i comandi che verranno eseguiti rapidamente e potrebbero cambiare frequentemente, ad esempio aggiungendo un utente con un UID specifico dell'host o creando cartelle e modificando le autorizzazioni. Se il contenitore include codice interpretato (ad es. JavaScript) che viene sviluppato attivamente, viene aggiunto il più tardi possibile in modo che una ricostruzione esegua solo quella singola modifica.
In ciascuno di questi gruppi di modifiche, mi consolido nel miglior modo possibile per ridurre al minimo i livelli. Quindi, se ci sono 4 diverse cartelle di codice sorgente, queste vengono collocate all'interno di una singola cartella in modo che possa essere aggiunta con un singolo comando. Qualsiasi installazione di pacchetto da qualcosa come apt-get viene unita in un singolo RUN quando possibile per ridurre al minimo la quantità di overhead del gestore pacchetti (aggiornamento e pulizia).
Aggiornamento per build multi-stage:
Mi preoccupo molto meno di ridurre le dimensioni dell'immagine negli stadi non finali di una build a più stadi. Quando queste fasi non sono taggate e spedite ad altri nodi, è possibile massimizzare la probabilità di un riutilizzo della cache suddividendo ciascun comando in una RUN
riga separata .
Tuttavia, questa non è una soluzione perfetta per schiacciare i livelli poiché tutto ciò che copi tra le fasi sono i file e non il resto dei metadati dell'immagine come impostazioni delle variabili di ambiente, punto di accesso e comando. E quando installi pacchetti in una distribuzione Linux, le librerie e le altre dipendenze potrebbero essere sparse in tutto il filesystem, rendendo difficile una copia di tutte le dipendenze.
Per questo docker build
motivo, utilizzo build multi-stage in sostituzione della creazione di file binari su un server CI / CD, in modo che il mio server CI / CD debba solo avere gli strumenti per funzionare , e non avere un jdk, nodejs, go e qualsiasi altro strumento di compilazione installato.