Composta da:
Istanze dell'immagine. File Systems indipendenti tra i container, concetto di isolamento.
"docker run -it nome-container sh
" per creare un nuovo container
"docker exec -it nome-container sh
" per navigare un container già creato
docker exec -it nome-container bin/bash
FROM ALPINE
RUN apk add --update redis
CMD ["redis-server"]
quindi
docker build .
Per eseguire un file Docker con nome customizzato:
docker build -f Dockerfile.dev .
FROM
;apk
sul container, il container modifica il proprio file system con l'installazione;RUN
;redis-server
nel nuovo container modificando il comando primario (Startup Command) del container;COPY <path_da_copiare> <path_nel_container_su_cui_copiare>
Es: "COPY ./ ./"
Imposta una workdir, tutte le istruzioni successive saranno eseguite sul path qui indicato (per esempio i COPY ./ copieranno nella workdir). Es:
WORKDIR usr/app
Rebuild Dockerfile
Se aggiungo un'istruzione:
RUN apk add --update gcc
Devo eseguire la "rebuild", quindi eseguo "docker build ."
In questo caso non avviene nuovamente il download di Alpine (ovviamente) né altre installazioni di dependencies. L'unico messaggio è "using cache" il che significa che Docker sta modificando l'immagine solo relativamente alla nuova istruzione.
Attenzione che se si cambia l'ordine delle istruzioni la cache non viene utilizzata e viene ripetuto il processo di installazione di tutte le istruzioni.
Quando si apportano delle modifiche al programma conteinerizzato dentro Docker occorre ribuildare l'immagine con il "docker build COPY
qual'ora i file siano stati modificati in modo da avere un aggiornamento veloce.
Un'ottimizzazione nel caso di Node JS è quello di impostare le istruzioni:
COPY ./package.json ./
RUN npm install
COPY ./ ./
Cosicché, nel caso di aggornamento di file del codice, non viene rieseguito l'npm install
poiché il package.json non risulta modificato.
Usare l'argomento "-t" per aggiungere il nome dell'immagine e la versione nel seguente schema:
docker-id / <nome repo progetto>:<versione> (ossia il TAG dell'immagine)
Per esempio:
docker build -t michelesalvucci/redis-test:latest .
Quindi sarà sufficiente avviare il container con:
docker run michelesalvucci/redis-test
I container sono istanze delle immagini ma si può "congelare" un container in una nuova immagine (ciò avviene durante il build del Dockerfile).
docker run -it alpine sh
"apk add -update redis
" per aggiungere l'installazionedocker commit -c 'CMD ["redis-server"]' id-del-container
"Viene quindi generata una nuova immagine identica a quella del Dockerfile. Se si genera l’errore "/bin/sh: [redis-server]: not found" or "No Such Container", usare il comando:
docker commit -c "CMD 'redis-server'" id-del-container
Questa metodologia non si usa ma può darci uno spaccato di quanto avviene all'interno del "docker build" di un Dockerfile.
docker run -p <porta della macchina>:<porta del container> <nome immagine>
Es:
docker run -p 8080:8080 michelesalvucci/simpleweb
Si può encodare in un file docker-compose.yml, con una nuova sintassi, i comandi di "build" e "run".
-services: set di container che si intende avviare indicando il nome e l'immagine
services:
nome-container:
image: "nome-immagine"
Nella configurazione del container:
I nomi utilizzati dei container sono spesso riconosciuti dentro le applicazioni, per esempio in Node.js Express indicando come "host" di Redis il nome del container di Redis, Docker riconoscerà l'host.
Per fare un replace del nome del Dockerfile (il dockerfile non si chiama "Dockerfile"):
service:
<nome-container>:
stdin_open: true
build:
context: .
dockerfile: Dockerfile.dev
ports:
- “4001:8081”
Per eseguire il run:
docker-compose up
Per eseguire la build e il run (se per esempio vogliamo essere sicuri che venga rubildato, nonostante sia già indicato del docker-compose.yml):
docker-compose up --build
Per lanciare in background:
docker-compuse up -d
Per eseguire lo stop dei containers:
docker-compose down
Il corrispettivo di "docker ps" per Docker Compose è "docker-compose ps". Il comando deve essere esguito in una directory dove sia presente il file docker-compose.yml.
Si impostano nel contesto di un service, per esempio per consentire a un servizio server di avere a disposizione la variabili contenenti i parametri e le credenziali per la connessione ad un database:
services:
<nome servizio>:
environment:
- <nome variabile> =<valore variabile>
Impostare la variabile nel container a runtime:
<nome variabile>= <valore variabile>
Impostare la variabile in modo che venga letta dal computer, non dalla VM che ospita l’esecuzione del container:
<nome variabile>
Come gestire il caso di crash ed eseguire il restart? Restart policies:
Nel docker-compose.yml aggiungo, sotto il nome del container:
"restart: <nome-policy>
" ('no' con gli apici se intendo impostare la policy "no")
Nel caso di stop (in base alla policy) docker-compose riavvia l'ultimo container senza stop o failure (dipende dal tipo di restart policy scelto). Per esempio se ho un web server la scelta ricadrebbe su "always" perchè il server deve essere sempre in esecuzione.
Tutti i riferimenti per la configurazione di Postgres con Docker e docker-compose: https://hub.docker.com/_/postgres
Riferimento alla directory locale nel container, necessario per evitare di eseguire il rebuild ad ogni modifica del codice.
Sintassi nel "docker run":
-v /app/node_modules -v $(pwd):/app
Il primo commento esegue un "bookmark", necessario per non eseguire l'override di determinati path (come ad esempio node_modules che non è presente nella folder locale del file system e quindi punta ad una folder che non esiste).
Quindi indicare "-v /app/node_modules
" indica a Docker di NON eseguire il map dei file per questo percorso.
Su Windows sovente occorre indicare il path assoluto. Inoltre quella m---a di Windows Toolbox potrebbe richiedere di aggiungere (motivo in più per usare docker compose):
docker run -it -p 3000:3000 -v /app/node_modules -v /d/DEVELOP/docker-tutorial/frontend:/app -e CHOKIDAR_USEPOLLING=true CONTAINER_ID
Nel docker-compose è molto meglio:
volumes:
- /app/node_modules
- .:/app
Dove il primo è il placeholder e il secondo è il mapping. (il punto sostituisce il $(pwd)
della linea di comando).
Nel caso si stia utilizzando Windows e l'app React non si aggiorni automaticamente, aggiungere:
environment:
- CHOKIDAR_USEPOLLING=true
Nel Dockerfile si possono lasciare i comandi di COPY nel qual caso si decida di non utilizzare più docker compose.
Window Home potrebbe impedirti di montare i volumi sul disco D:
Occorrerà eseguire i seguenti steps:
https://gist.github.com/first087/2214c81114f190271d26c3e88da36104#file-attach-drive-d-sh
docker-machine ssh
cd /
E da qui si raggiungono per esempio “c” (che sarebbe C:) e “d” (che sarebbe D:).
Per eseguire i test su un container in corso:
docker exec -it <id container> <cmd per i test>
Il comando va a rimpiazzare il comando del Dockerfile eseguendo i test (es. npm run test
).
In docker compose creo un service apposito:
tests:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- /app/node_modules
- /d/DEVELOP/docker-tutorial/frontend:/app
command: ["comandi", "per", "test"]
In questo modo ho due container in corso che si interfacciano su un solo terminal (il container "web" e il container "tests".)
Per manenere la shell interattiva (quindi il docker attach) nella configurazione dei test con docker-compose conviene attaccare un secondo terminale al container di test:
"docker attach <id container>
" non funziona con docker-compose (se ci fai caso nemmeno in Portainer, cosa già notata precedentemente).
Usare:
docker exec -it <id container> sh
Al suo interno:
ps
per elencare i processi in corso, e vediamo che il PID 1 è su npm e ci sono diversi altri PID mappati su diversi processi.
Dobbiamo eseguire quindi il bind sul processo corretto il cui "stdin" invii i nostri comandi al programma.
Esempio: per servire il codice dell'applicazione usiamo come web server: nginx. Esso sarà incaricato a servire l'index.html e il main.js prodotti dalla build dell'applicazione. Per questo va scritto un Dockerfile apposito che, dopo avere eseguito la build, utilizzi nginx come base image, copi il contenuto della folder "build" e avvii nginx. Questo esempio è estremamente simile al caso Angular.
Deve quindi avere due fasi di build:
La fase di "run" dovrà copiare il contenuto della cartella "build" il quale sarà prodotto dalla fase di "build".
Quando scrivo il Dockerfile indico:
FROM <nome immagine> as builder <------ step di build (non funziona su AWS)
WORKDIR ...
COPY ...
RUN npm run build
Quindi:
FROM <nome immagine, es: nginx>
COPY --from=builder <cartella da copiare, es: /app/build> <cartella di destinazione, es. /user/share/nginx/html>
Vado quindi ad eseguire:
docker build -t <nome>
e quindi:
docker run -p 8080:80 <id immagine appena creata>