Documente Academic
Documente Profesional
Documente Cultură
Cuprins
1. Recapitulare
2. Volume
3. Aplicație de vot
1 Recapitulare
2 Volume
În Docker, datele dintr-un container nu sunt persistate în exterior. Pentru a ilustra acest lucru, putem rula un container
simplu de Alpine, în care creăm un fișier apoi ieșim.
/ # exit
$ docker container ls -a
Odată ce un container a fost rulat, chiar dacă execuția sa s-a oprit, layer-ele sale pot fi accesate până când containerul este
șters cu comanda docker container rm. În mod implicit, Docker folosește AUFS ca driver de storage pentru management-ul
imaginilor. Putem verifica acest lucru folosind docker info:
$ cd /var/lib/docker/aufs/diff/
$ ls -latr
[...]
drwxr-xr-x 6 root root 4096 Oct 11 13:54 153c4ef3dc54d49cc1d2b32492385a69d59ae1b81460b42c7c34dea07e828f96-init
drwx------ 20 root root 45056 Oct 11 13:54 .
drwxr-xr-x 6 root root 4096 Oct 11 13:54 153c4ef3dc54d49cc1d2b32492385a69d59ae1b81460b42c7c34dea07e828f96
$ ls 153c4ef3dc54d49cc1d2b32492385a69d59ae1b81460b42c7c34dea07e828f96/
root test
$ cat 153c4ef3dc54d49cc1d2b32492385a69d59ae1b81460b42c7c34dea07e828f96/test/hello.txt
hello
Totuși, aceste date nu sunt persistente, ci sunt șterse împreuna cu layer-ul. Astfel, dacă se șterge containerul, datele vor fi
pierdute:
$ ls 153c4ef3dc54d49cc1d2b32492385a69d59ae1b81460b42c7c34dea07e828f96/
Pentru persistența datelor dintr-un container, în Docker se folosesc mecanisme de persistență numite volume. Volumele
Docker au câteva proprietăți și beneficii:
• sunt ușor de salvat și migrat
• pot fi controlate și configurate cu comenzi CLI sau cu API-ul de Docker
• funcționează pe containere Linux și Windows
• pot fi partajate între containere
• prin driverele de volume, se pot stoca date persistene pe gazde remote sau pe provideri de cloud, se pot cripta
datele, etc.
• conținutul unui volum nou poate fi pre-populat de un container
• utilizarea unui volum nu crește dimensiunea unui container care îl folosește, pentru că un volum există în afara
ciclului de viață a containerului.
Există mai multe metode pentru a defini și utiliza un volum atunci când se rulează un singur container de Linux. Dacă se
creează o imagine custom, atunci volumul se poate defini în fișierul Dockerfile, prind comanda VOLUME. Dacă se rulează,
de exemplu, un container bazat pe o imagine existentă (cum ar fi Alpine în exemplul din laborator), atunci se poate defini un
volum la runtime. În exemplul de mai jos, rulăm o imagine de Alpine în background care face ping într-un fișier localizat
într-un volum /test, pe care îl creăm folosind flag-ul -v:
$ docker container run --name c2 -d -v /test alpine sh -c 'ping 8.8.8.8 > /test/ping.txt'
$ docker container ls
În timp ce containerul rulează, putem să îl inspectăm și observăm că este legat de o componentă de tip Volume cu destinația
/test. Astfel, putem afla unde este localizat volumul. Dacă ne uităm în acel director, vom vedea fișierul în care se face ping
din container:
$ docker container inspect -f "{{ json .Mounts }}" c2 | python -m json.tool
[
{
"Destination": "/test",
"Driver": "local",
"Mode": "",
"Name": "2afac5683222a3435549131a931a4c0628b775ecd3d79cb3fd597b3501418288",
"Propagation": "",
"RW": true,
"Source": "/var/lib/docker/volumes/2afac5683222a3435549131a931a4c0628b775ecd3d79cb3fd597b3501418288/_data",
"Type": "volume"
}
]
$ ls /var/lib/docker/volumes/2afac5683222a3435549131a931a4c0628b775ecd3d79cb3fd597b3501418288/_data
ping.txt
$ cat ping.txt
59d0785188a6
59d0785188a6
$ ls /var/lib/docker/volumes/2afac5683222a3435549131a931a4c0628b775ecd3d79cb3fd597b3501418288/_data
ping.txt
O a treia metodă de a lucra cu volume în Docker este direct prin API-ul de volume, adică prin comenzi CLI de genul docker
volume create, docker volume ls, etc. Dacă vrem să creăm volume pentru un stack de servicii, acest lucru poate fi făcut în
fișierul YAML folosit pentru Docker Compose, așa cum vom vedea în secțiunea următoare.
3 Aplicație de vot
În această secțiune, vom analiza și vom face deploy la o aplicație multi-serviciu de vot, formată din 5 componente (așa cum
se poate observa și în imaginea de mai jos):
• o aplicație Web Python care permite utilizatorului să aleagă una din două opțiuni
• o coadă Redis care colectează voturi noi
• un worker .NET/Java care consumă voturi și le stochează într-o bază de date
• baza de date Postgres în sine, peste un volum Docker
• o altă aplicație Web, de data aceasta scrisă în Node.js, care afișează rezultatele votului în timp real.
Aplicația este disponibilă în întregime pe Github și este updatată de fiecare dată când sunt introduse feature-uri noi în
Docker. Ca în orice stack de servicii Docker, pentru definirea unei aplicații se folosește un fișier YAML pentru Docker
Compose (în cazul de față numit docker-stack.yml). Înainte de a face deploy la aplicație, vom analiza fiecare serviciu în
parte, începând cu coada Redis:
version: "3"
services:
redis:
image: redis:alpine
ports:
- "6379"
networks:
- frontend
deploy:
replicas: 1
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
Redis este un sistem open-source de stocare de date in-memory utilizat ca bază de date, cache, sau broker de mesaje. Are
suport pentru structuri de date ca string-uri, hash-uri, liste, mulțimi, bitmap-uri, etc. Așa cum se poate observa, serviciul
rulează pe portul 6379 peste o rețea numită frontend, are o singură replică, și este bazat pe imaginea existentă în Docker
Store. Opțiunea update_config specifică modul în care serviciul va fi updatat (câte containere să fie updatate la un moment
de timp și cât să se aștepte între update-urile unui grup de containere).
db:
image: postgres:9.4
volumes:
- db-data:/var/lib/postgresql/data
networks:
- backend
deploy:
placement:
constraints: [node.role == manager]
PostgreSQL este un sistem relațional de baze de date open-source. Serviciul de mai sus se bazează pe o imagine cu
versiunea 9.4, rulează pe swarm manager, și este deservit de o rețea numită backend. De asemenea, se face o mapare între
un volum db-data și fișierul de date PostgreSQL al imaginii.
vote:
image: dockersamples/examplevotingapp_vote:before
ports:
- 5000:80
networks:
- frontend
depends_on:
- redis
deploy:
replicas: 2
update_config:
parallelism: 2
restart_policy:
condition: on-failure
Serviciul de vot are la bază o imagine aflată în registrul dockersamples, dar ea poate fi creată pe baza surselor din directorul
de Github. O opțiune nouă în configurarea serviciul de vot este depends_on, care specifică faptul că serviciul de vot depinde
de serviciul Redis, deci va fi pornit doar după ce serviciul Redis a fost pornit cu succes. Serviciul de vot are portul 80 mapat
la portul 5000 de pe mașina unde rulează, și este bazat pe rețeaua frontend.
Cealaltă interfață, de asemenea sub forma unui serviciu Web, prezintă rezultatele votului. Serviciul aferent este definit
astfel:
result:
image: dockersamples/examplevotingapp_result:before
ports:
- 5001:80
networks:
- backend
depends_on:
- db
deploy:
replicas: 1
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
Din nou, se poate observa că se folosește o imagine din din registrul Docker, dar sursele pentru serviciul de afișare a
rezultatelor se găsesc în directorul results din proiectul Github. Serviciul acesta rulează o singură replică, depinde de
serviciul de bază de date, și are mapat portul extern 5001 la portul intern 80.
Mai există în docker-stack.yml și un serviciu worker, care procesează voturile din coada Redis și le scrie în baza de date
PostgreSQL:
worker:
image: dockersamples/examplevotingapp_worker
networks:
- frontend
- backend
deploy:
mode: replicated
replicas: 1
labels: [APP=VOTING]
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 3
window: 120s
placement:
constraints: [node.role == manager]
Se poate observa că serviciul worker are acces la ambele rețele (frontend și backend), pentru că realizează legătura între cele
două componente (partea de vot și partea de afișare). În sursele de pe Github, există posibilitatea compilării acestui serviciu
atât în Java, cât și în .NET. În cadrul opțiunii restart_policy, câmpul delay reprezintă durata dintre momentul în care
serviciul pică și momentul când se începe repornirea lui, max_attempts indică numărul de încercări de repornire, iar window
înseamnă cât timp se așteaptă până se verifică dacă serviciul a fost repornit cu succes.
În final, stack-ul de servicii mai conține un serviciu de vizualizare, precum și două rețele și un volum (toate lăsate cu setări
implicite):
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
stop_grace_period: 1m30s
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
networks:
frontend:
backend:
volumes:
db-data:
Pentru a face deploy la aplicația de vot pe mașina locală, trebuie făcuți următorii pași:
$ cd example-voting-app/
$ docker stack ls
NAME SERVICES
voting-app 6
În acest moment, putem accesa interfața de vot și cea de rezultate pe adresa IP locală și pe porturile aferente (5000,
respectiv 5001). De asemenea, serviciul de vizualizare poate fi accesat pe portul 8080.