I contenitori Docker sono in aumento come best practice per l'implementazione e la gestione di sistemi distribuiti nativi del cloud. I contenitori sono istanze di immagini Docker. Si scopre che c'è molto da sapere e capire sulle immagini.
In questo tutorial in due parti coprirò in dettaglio le immagini di Docker. In questa parte, inizierò con i principi di base, quindi passerò alla progettazione della considerazione e all'ispezione degli interni delle immagini. Nella seconda parte, coprirò la costruzione delle tue immagini, la risoluzione dei problemi e il lavoro con i repository di immagini.
Quando esci dall'altra parte, avrai una solida comprensione di quali sono esattamente le immagini Docker e come utilizzarle efficacemente nelle tue applicazioni e nei tuoi sistemi.
Docker gestisce le immagini utilizzando un driver di archiviazione back-end. Esistono diversi driver supportati come AUFS, BTRFS e overlay. Le immagini sono fatte di strati ordinati. Puoi pensare a un livello come un insieme di modifiche al file system. Quando prendi tutti i livelli e li impili insieme, ottieni una nuova immagine che contiene tutte le modifiche accumulate.
La parte ordinata è importante. Se aggiungi un file in un livello e lo rimuovi in un altro livello, è meglio farlo nell'ordine corretto. Docker tiene traccia di ogni livello. Un'immagine può essere composta da dozzine di strati (il limite attuale è 127). Ogni strato è molto leggero. Il vantaggio dei livelli è che le immagini possono condividere livelli.
Se disponi di molte immagini basate su livelli simili, come il SO di base o i pacchetti comuni, tutti questi layer comuni verranno archiviati solo una volta e l'overhead per l'immagine sarà solo i livelli unici di quell'immagine.
Quando viene creato un nuovo contenitore da un'immagine, tutti i livelli dell'immagine sono di sola lettura e un sottile strato di lettura / scrittura viene aggiunto in cima. Tutte le modifiche apportate al contenitore specifico sono memorizzate in quel livello.
Ora, ciò non significa che il contenitore non può modificare i file dal suo livello di immagine. Lo può sicuramente. Ma creerà una copia nel suo livello superiore, e da quel momento in poi, chiunque tenti di accedere al file otterrà la copia del livello superiore. Quando i file o le directory vengono rimossi dai livelli inferiori, diventano nascosti. I livelli immagine originali sono identificati da un hash basato sul contenuto crittografico. Il livello di lettura / scrittura del contenitore è identificato da un UUID.
Ciò consente una strategia copy-on-write per immagini e contenitori. Docker riutilizza gli stessi oggetti il più possibile. Solo quando un elemento viene modificato, Docker crea una nuova copia.
L'organizzazione unica a strati e la strategia copy-on-write promuove alcune best practice per la creazione e il compositing di immagini Docker.
Le immagini Docker ottengono enormi benefici dal punto di vista della stabilità, della sicurezza e del tempo di caricamento, più piccoli sono. È possibile creare immagini davvero minuscole per scopi di produzione. Se è necessario risolvere i problemi, è sempre possibile installare gli strumenti in un contenitore.
Se si scrivono i dati, i registri e tutto il resto solo su volumi montati, è possibile utilizzare l'intero arsenale di strumenti di debug e risoluzione dei problemi sull'host. Vedremo presto come controllare con molta attenzione quali file vanno a finire nell'immagine Docker.
I livelli sono grandi, ma esiste un limite e l'overhead è associato ai livelli. Troppi livelli potrebbero danneggiare l'accesso al file system all'interno del contenitore (poiché ogni livello potrebbe aver aggiunto o rimosso un file o una directory) e ingombrare il proprio file system.
Ad esempio, se installi un sacco di pacchetti, puoi avere un layer per ogni pacchetto, installando ciascun pacchetto in un comando RUN separato nel tuo Dockerfile:
ESEGUI apt-get update RUN apt-get -y install package_1 RUN apt-get -y install pacchetto_2 RUN apt-get -y install package_3
Oppure puoi combinarli in un livello con un singolo comando RUN.
RUN apt-get update && \ apt-get -y install pacchetto_1 && \ apt-get -y install pacchetto_2 && \ apt-get -y install pacchetto_3
La tua immagine di base (praticamente nessuno costruisce immagini da zero) è spesso una decisione importante. Può contenere molti livelli e aggiungere molte capacità, ma anche molto peso. Anche la qualità dell'immagine e dell'autore sono fondamentali. Non vuoi basare le tue immagini su alcune immagini traballanti in cui non sei sicuro di cosa ci sia dentro e se puoi fidarti dell'autore.
Ci sono immagini ufficiali per molte distribuzioni, linguaggi di programmazione, database e ambienti di runtime. A volte le opzioni sono schiaccianti. Prenditi il tuo tempo e fai una scelta saggia.
Diamo un'occhiata ad alcune immagini. Ecco un elenco delle immagini attualmente disponibili sulla mia macchina:
ID TAGLIE REPOSITIVO ID IMMAGINE CREATO python latest 775dae9b960e 12 giorni fa 687 MB d4w / nsenter latest 9e4f13a0901e 4 mesi fa 83.8 kB ubuntu-with-ssh latest 87391dca396d 4 mesi fa 221 MB ubuntu latest bd3d4369aebc 5 mesi fa 127 MB hello-world latest c54a2cc56cbb 7 mesi fa 1.85 kB alpine latest 4e38e38c8ce0 7 mesi fa 4.8 MB nsqio / nsq latest 2a82c70fe5e3 8 mesi fa 70.7 MB
Il repository e il tag identificano l'immagine per gli umani. Se si tenta di eseguire o eseguire il pull usando un nome di repository senza specificare il tag, il tag "latest" viene utilizzato per impostazione predefinita. L'ID immagine è un identificativo univoco.
Entriamo e ispezioniamo l'immagine del ciao mondo:
> finestra mobile inspect hello-world ["Id": "sha256: c54a2cc56cbb2f ... e7e2720f70976c4b75237dc", "RepoTags": ["ciao-mondo: ultimo"], "RepoDigests": ["ciao-mondo @ sha256: 0256e8a3 ... 411de4cdcf9431a1feb60fd9" ], "Parent": "", "Commento": "", "Creato": "2016-07-01T19: 39: 27.532838486Z", "Contenitore": "562cadb4d17bbf30b58a ... bf637f1d2d7f8afbef666", "ContainerConfig": "Nome host ":" c65bc554a4b7 "," Nome dominio ":" "," Utente ":" "," AttachStdin ": falso," AttachStdout ": falso," AttachStderr ": falso," Tty ": falso," OpenStdin ": falso, "StdinOnce": false, "Env": ["PATH = / usr / bin: / usr / sbin: / usr / bin: / sbin: / bin"], "Cmd": ["/ bin / sh", " -c "," # (nop) CMD [\ "/ hello \"] "]," Immagine ":" sha256: 0f9bb7da10de694 ... 5ab0fe537ce1cd831e "," Volumi ": null," WorkingDir ":" "," Entrypoint ": null, "OnBuild": null, "Etichette": , "DockerVersion": "1.10.3", "Autore": "", "Config": "Nome host": "c65bc554a4b7", "Nome dominio": "", "Utente": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "Apri Stdin ": false," StdinOnce ": false," Env ": [" PATH = / usr / sbin: / usr / bin: / sbin: / bin "]," Cmd ": [" / ciao "]," Immagine ":" sha256: 0f9bb7da10de694b ... b0fe537ce1cd831e "," Volumi ": null," WorkingDir ":" "," Entrypoint ": null," OnBuild ": null," Etichette ": ," Architettura ":" amd64 " , "Os": "linux", "Dimensione": 1848, "VirtualSize": 1848, "GraphDriver": "Nome": "aufs", "Dati": null, "RootFS": "Tipo": "layers", "Layers": ["sha256: a02596fdd012f22b03a ... 079c3e8cebceb4262d7"]]
È interessante vedere quante informazioni sono associate a ciascuna immagine. Non esaminerò ogni articolo. Citerò solo un interessante tidbit che le voci "container" e "containerConfig" sono per un contenitore temporaneo che Docker crea quando costruisce l'immagine. Qui, voglio concentrarmi sull'ultima sezione di "RootFS". È possibile ottenere solo questa parte utilizzando il supporto per il modello Go del comando inspect:
> docker inspect -f '.RootFS' ciao-mondo strati [sha256: a02596fdd012f22b03af6a ... 8357b079c3e8cebceb4262d7]
Funziona, ma abbiamo perso la bella formattazione. Preferisco usare jq:
> finestra mobile ispeziona hello-world | jq. [0] .RootFS "Tipo": "layers", "Layers": ["sha256: a02596fdd012f22b03af6a ... 7507558357b079c3e8cebceb4262d7"]
Puoi vedere che il tipo è "Livelli" e c'è solo un livello.
Ispezioniamo i livelli dell'immagine di Python:
> finestra mobile ispeziona python | jq. [0] .RootFS "Tipo": "layers", "Layers": ["sha256: a2ae92ffcd29f7ede ... e681696874932db7aee2c", "sha256: 0eb22bfb707db44a8 ... 8f04be511aba313bdc090", "sha256: 30339f20ced009fc3 ... 6b2a44b0d39098e2e2c40", "sha256: f55f65539fab084d4 ... 52932c7d4924c9bfa6c9e "," sha256: 311f330fa783aef35 ... e8283e06fc1975a47002d "," sha256: f250d46b2c81bf76c ... 365f67bdb014e98698823 "," sha256: 1d3d54954c0941a8f ... 8992c3363197536aa291a "]
Wow. Sette strati Ma quali sono questi strati? Possiamo usare il comando history per capirlo:
IMAGE CREATED CREATED BY SIZE 775dae9b960e 12 giorni fa / bin / sh -c # (nop) CMD ["python3"] 0 B12 giorni fa / bin / sh -c cd / usr / local / bin && ... 48 B 12 giorni fa / bin / sh -c set -ex && buildDeps = '... 66,9 MB 12 giorni fa / bin / sh -c # (nop) ENV PYTHON_PIP_V ... 0 B 12 giorni fa / bin / sh -c # (nop) ENV PYTHON_VERSI ... 0 B 12 giorni fa / bin / sh -c # (nop) ENV GPG_KEY = 0D96 ... 0 B 12 giorni fa / bin / sh -c apt-get aggiorna && apt-ge ... 7.75 MB 12 giorni fa / bin / sh -c # (nop) ENV LANG = C.UTF-8 0 B 12 giorni fa / bin / sh -c # (nop) ENV PATH = / usr / lo ... 0 B 13 giorni fa / bin / sh -c apt-get aggiorna && apt-ge ... 323 MB 13 giorni fa / bin / sh -c apt-get aggiorna && apt-ge ... 123 MB 13 giorni fa / bin / sh -c apt-get aggiorna && apt-ge ... 44,3 MB 13 giorni fa / bin / sh -c # (nop) CMD ["/ bin / bash" ... 0 B 13 giorni fa / bin / sh -c # (nop) Aggiungi file: 89ecb642 ... 123 MB
OK. Non essere allarmato. Non manca nulla Questa è solo una terribile interfaccia utente. I livelli utilizzavano un ID immagine prima del Docker 1.10, ma non più. L'ID del livello superiore non è realmente l'ID di quel livello. È l'ID dell'immagine di Python. Il "CREATED BY" viene troncato, ma è possibile visualizzare il comando completo se si passa --no-trunc
. Ti salverò dall'output qui a causa delle limitazioni della larghezza della pagina che richiedono il wrapping della linea estremo.
Come si ottengono le immagini? Ci sono tre modi:
Quando si esegue un contenitore, si specifica la sua immagine. Se l'immagine non esiste sul tuo sistema, viene estratta da un registro Docker (per impostazione predefinita DockerHub). In alternativa, puoi tirare direttamente senza eseguire il contenitore.
Puoi anche caricare un'immagine che qualcuno ti ha inviato come file tar. Docker lo supporta in modo nativo.
Infine, e più interessante, è possibile creare le proprie immagini, che è l'argomento della seconda parte.
Le immagini Docker sono basate su un file system a livelli che offre molti vantaggi e benefici per i casi d'uso per cui sono progettati i contenitori, come la leggerezza e la condivisione di parti comuni, in modo che molti contenitori possano essere distribuiti ed eseguiti sulla stessa macchina in modo economico.
.