Come creare l'immagine docker di lavoro più piccola ogni volta?


19

Obiettivo: creare sempre le immagini docker di lavoro più piccole

attuale

REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
a-docker-image      latest    x                   42 minutes ago       1.92 GB

Tentativo

Aggiunta di un passaggio di pulizia alla fine del Dockerfile:

#clean
RUN apt-get purge -y wget
RUN rm -r a-build-dir
RUN apt-get purge -y a-package

ridotto leggermente le dimensioni dell'immagine:

REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
a-docker-image      latest    y                   2 minutes ago       1.86 GB

Discussione

Ho creato varie immagini docker. Ogni volta che provo a ridurre la dimensione dell'immagine creata, ma sento sempre che è troppo grande. Sto cercando uno script che è già stato creato da qualcuno su Github che rimuove tutti i pacchetti superflui dall'immagine in modo che le dimensioni dell'immagine creata siano le più piccole possibili.

Come ho già detto, cerco sempre di ridurre le dimensioni dell'immagine, ma voglio applicarlo in modo coerente in modo che ogni immagine che creo da ora in poi sia il più piccola possibile.

Domanda

Come creare l'immagine docker di lavoro più piccola ogni volta?

Risposte:


1

C'è una varietà di tecniche coinvolte, senza un'unica soluzione. Probabilmente vorrai eseguire alcune delle seguenti operazioni:


Innanzitutto, ottimizza i livelli dell'immagine per il riutilizzo. Metti i passaggi che cambiano frequentemente più tardi nel Dockerfile per aumentare le probabilità che i primi livelli vengano memorizzati nella cache dalle build precedenti. Un layer riutilizzato verrà visualizzato come più spazio su disco in a docker image ls, ma se si esamina il filesystem sottostante, sul disco verrà mai memorizzata solo una copia di ogni layer. Ciò significa che 3 immagini da 2 GB ciascuna, ma che hanno solo 50 MB diversi negli ultimi livelli della build, occuperanno solo 2,1 GB di spazio su disco, anche se l'elenco mostra che stanno usando 6 GB poiché tu sei doppio conteggio di ciascuno dei livelli riutilizzati.

Il riutilizzo dei livelli è il motivo per cui vedi le immagini con dipendenze di build che cambiano di rado installarle prima di copiare nel codice. Vedi qualsiasi esempio di Python che ha un modello come:

FROM python
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
# note how the code is copied only after the pip install
# since code changes but requirements.txt doesn't
COPY . .
CMD ["gunicorn", "app:app"]

Scegli un'immagine di base minima. Questo è il motivo per cui vedi le persone andare da ubuntua debian:slim(le varianti slim sono più piccole, spediscono con meno strumenti) o addirittura alpine. Ciò riduce le dimensioni del punto di partenza ed è molto utile se si estraggono costantemente nuove versioni dell'immagine di base. Tuttavia, se l'immagine di base cambia raramente, il riutilizzo dei livelli rimuove gran parte del vantaggio di un'immagine di base minima.

L'immagine di base più piccola che puoi scegliere è scratch, che è niente, nessuna shell o librerie, ed è utile solo con i binari compilati staticamente. Altrimenti, scegli un'immagine di base che includa gli strumenti di cui hai bisogno senza molti strumenti di cui non hai bisogno.


Successivamente, qualsiasi passaggio che modifica o elimina un file deve essere combinato con i passaggi precedenti che creano quel file. In caso contrario, il filesystem a più livelli, che utilizza la funzione di copia su scrittura anche su cose come una modifica delle autorizzazioni dei file, avrà il file originale in un livello precedente e la dimensione dell'immagine non si ridurrà quando si rimuovono i file. Questo è il motivo per cui i tuoi rmcomandi non hanno alcun effetto sullo spazio su disco risultante. Invece, puoi concatenare i comandi, come:

RUN apt-get update \
 && apt-get install -y \
      a-package \
      wget \
 && ... \
 && apt-get purge -y wget \
 && rm -r a-build-dir \
 && apt-get purge -y a-package

Si noti che un uso eccessivo del concatenamento dei comandi può rallentare le build poiché è necessario reinstallare lo stesso set di strumenti ogni volta che un prerequisito cambia (ad esempio il codice che viene estratto con wget). Vedi più fasi di seguito per una migliore alternativa.


Qualsiasi file creato non necessario nell'immagine risultante deve essere eliminato, nel passaggio che lo crea. Ciò include cache dei pacchetti, registri, pagine man, ecc. Per scoprire quali file vengono creati in ogni livello, è possibile utilizzare uno strumento come wagoodman / dive (che non ho verificato personalmente e esprimerebbe cautela poiché funziona con accesso root completo sul tuo host) oppure puoi creare le tue immagini docker senza potare i contenitori intermedi e quindi visualizzare il diff con:

# first create and leave containers from any RUN step using options on build
docker image build --rm=false --no-cache -t image_name . 
# review which layers use an unexpectedly large amount of space
docker image history image_name
# list all containers, particularly the exited ones from above
docker container ps -a 
# examine any of those containers
docker container diff ${container_id} 
# ... repeat the diff for other build steps
# then cleanup exited containers
docker container prune

Con ciascuno di quei contenitori intermedi, il diff mostrerà ciò si aggiungono file, modificate o eliminate dal fatto che la fase (questi sono indicati con A, Co Dprima di ogni nome di file). Ciò che sta mostrando diff è il filesystem di lettura / scrittura specifico del contenitore, che è qualsiasi file modificato dal contenitore dallo stato dell'immagine usando copy-on-write.


Il modo migliore per ridurre le dimensioni dell'immagine è eliminare eventuali componenti non necessari, come i compilatori, dall'immagine spedita. Per questo, le build multi-stage ti consentono di compilare in una fase e quindi copiare solo gli artefatti risultanti dalla fase di creazione in un'immagine di runtime che ha solo il minimo necessario per eseguire l'applicazione. Ciò evita la necessità di ottimizzare i passaggi di compilazione poiché non vengono forniti con l'immagine risultante.

FROM debian:9 as build
# still chain update with install to prevent stale cache issues
RUN apt-get update \
 && apt-get install -y \
      a-package \
      wget \
RUN ... # perform any download/compile steps

FROM debian:9-slim as release
COPY --from=build /usr/local/bin/app /usr/local/bin/app
CMD [ "/usr/local/bin/app" ]

Il multistadio è ideale con file binari compilati staticamente che è possibile eseguire con zero come immagine di base o passare da un ambiente di compilazione come JDK a un runtime come JRE. Questo è il modo più semplice per ridurre drasticamente le dimensioni dell'immagine pur avendo build veloci. È ancora possibile eseguire il concatenamento dei passaggi nella fase di rilascio se si dispone di passaggi che modificano o eliminano i file creati nei passaggi precedenti, ma per la maggior parte, COPYda un altro livello, si isola la fase di rilascio da qualsiasi livello di livello sperimentato nelle fasi di generazione precedenti.


Nota, non consiglio di schiacciare le immagini poiché ciò riduce le dimensioni di un'immagine a spese dell'eliminazione del riutilizzo dei livelli. Ciò significa che le future build della stessa immagine richiederanno più traffico su disco e di rete per inviare gli aggiornamenti. Per tornare al primo esempio, la compressione può ridurre l'immagine da 2 GB a 1 GB, ma non 3 immagini potrebbero occupare 3 GB invece di 2,1 GB.


25

A Dockerfilecrea un nuovo livello per ciascuno dei comandi nel file. Dato che i livelli sono buoni, sovrapposti uno sull'altro: non è possibile rimuovere i file aggiunti da un livello precedente. Questo è il motivo per cui quando installi pacchetti o scarichi file o crei build ciascuno in un comando separato, questi sono ancora presenti nell'immagine, anche se in un livello futuro li hai rimossi.

Quindi, se cambi solo questo:

RUN apt-get update -y
RUN apt-get install -y wget a-package
# ...
RUN apt-get purge -y wget
RUN rm -r a-build-dir
RUN apt-get purge -y a-package

A questo:

RUN apt-get update -y \
    && apt-get install -y wget a-package \
    && mkdir a-build-dir \
    && wget http://some-site/very-big-source-code.tar.gz \
    && tar xzvf very-big-source-code.tar.gz \
    && do-some-compilation \
    && apt-get purge -y wget \
    && cd .. \
    && rm -rf a-build-dir \
    && apt-get purge -y a-package

Otterrai un'immagine molto più piccola.


Un'altra opzione è quella di schiacciare l'immagine dopo averla costruita. D: Come funziona il nuovo docker --squash?


Ancora un'altra opzione, è quella di scegliere un'immagine di base sottile. Ad esempio, le immagini che utilizzano Alpine Linux come base anziché Debian, richiedono solo 10-15 MB anziché 180-250 MB. E questo è prima di aggiungere la tua applicazione e i tuoi dati. Molte immagini di base ufficiali su Docker Hub hanno una versione alpina.


3
2.37vs.1.47 GB
030

4

Probabilmente non è esattamente una risposta, ma vale la pena dare le alternative.

L' habitat dello Chef è stato creato tenendo presente questo obiettivo, creando un pacchetto con tutte le dipendenze necessarie senza il carico estraneo di immagine di base / distribuzione che non desideri.

Estrae ciò che conta qui, le dimensioni del contenitore da questo post sul blog con una semplice app nodejs:

michael@ricardo-2:plans_pkg_part_2$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
mfdii/node-example   latest              36c6568c606b        40 minutes ago      655.9 MB
node                 latest              04c0ca2a8dad        16 hours ago        654.6 MB
mfdii/mytutorialapp  latest              534afd80d74d        2 minutes ago       182.1 MB

mdfii/node-exampleè un'immagine docker da un file docker classico mentre mfdii/mytutorialappl'immagine docker è prodotta con l'habitat.

Se la dimensione è la tua principale preoccupazione e sei pronto a prendere la curva di apprendimento dei piani Habitat, questa potrebbe essere una soluzione per te.


0

Si potrebbe anche usare l' immersione

docker run --rm -it \
    -v /var/run/docker.sock:/var/run/docker.sock \
    wagoodman/dive:latest <dive arguments...>

per ottenere un rapporto su quali rifiuti potrebbero essere rimossi da un'immagine docker al fine di ridurne le dimensioni.


0

Se si desidera avere livelli di sviluppo riutilizzabili ma ridurre l'utilizzo del disco per la consegna, è possibile produrre un "livello di consegna" unito come questo:

  1. Assicurati di avere un contenitore che usi la tua immagine (se non ne hai uno, magari usa qualcosa di simile docker run IMAGE echo, se il comando echo è disponibile)
  2. Trova l'ID contenitore (magari utilizzando docker container ls -l)
  3. Installa docker exportper docker importcreare il livello unito (qualcosa di simile docker export 20f192c6530a | docker import - project:merged)

Ciò manterrà i tuoi livelli di sviluppo, ma ti darà un'immagine più piccola e unita che puoi offrire.



0

semplice .. docker ps controlla le immagini correnti in corso..per un semplice esempio di file qui sotto ..

DA ubuntu16

MAINTAINER sreeni (email / dominio)

ESEGUI apt-get update

ESEGUI apt-get install -y nginx

ENTRYPOINT [“/ usr / sbin / nginx”, ”- g”, ”demone spento;”]

ESPOSI 80 (porta)

file docker semplice ...

utilizzare il comando docker sottostante

docker run -d -p 80:80 --name web server ubuntu16 (nome immagine) dopo che controlla localhost o indirizzo IP: 80 (apri il browser e controlla)


1
correggi la formattazione della tua risposta ...
Pierre.Vriens
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.