Sunteți pe pagina 1din 7

Laborator 3

Cuprins
1. Recapitulare
2. Volume
3. Aplicație de vot

1 Recapitulare

O listă cu comenzile învățate în laboratorul precedent poate fi găsită mai jos:

$ docker login # se logheaza in Docker Cloud


$ docker tag <APP> <USERNAME/REPOSITORY:TAG> # se da un tag unei imagini pentru upload in registru
$ docker push <USERNAME/REPOSITORY:TAG> # se uploadeaza o imagine in registru
$ docker stack deploy -c <COMPOSE_FILE> <APP> # se face deployment la un stack de servicii
$ docker stack rm <APP> # se opreste o aplicatie din rulat
$ docker service ls # se afiseaza informatii despre serviciile care ruleaza
$ docker service ps <SERVICE> # se afiseaza informatii despre task-urile unui serviciu
$ docker swarm init # se initializeaza un swarm
$ docker swarm leave [--force] # nodul curent iese din swarm
$ docker-machine create [--driver virtualbox] <VM> # se creeaza o masina virtuala Docker
$ docker-machine ls # se afiseaza date despre masinile virtuale Docker existente
$ docker-machine scp <SOURCE> <DESTINATION> # se copiaza intr-o masina virtuala Docker
$ docker-machine ssh <VM> <COMMAND> # se executa o comanda prin SSH intr-o masina virtuala Docker

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.

$ docker container run --name c1 -ti alpine sh

Unable to find image 'alpine:latest' locally


latest: Pulling from library/alpine
88286f41530e: Pull complete
Digest: sha256:f006ecbb824d87947d0b51ab8488634bf69fe4094959d935c0c103f4820a417d
Status: Downloaded newer image for alpine:latest

/ # mkdir /test && echo hello > /test/hello.txt

/ # exit

$ docker container ls -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS


NAMES
8bae62d8aec0 alpine "sh" 15 minutes ago Exited (0) 15 minutes ago
c1

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:

$ docker info | grep -i storage

Storage Driver: aufs


Pentru fiecare layer dintr-un container Docker, se vor stoca în AUFS informații despre cum arăta inițial și despre ce fișiere
s-au modificat (au fost adăugate, șterse sau schimbate). Aceste informații se găsesc în /var/lib/docker/aufs/diff, unde există
directoare pentru fiecare layer al fiecărui container care a fost rulat pe sistem fără să fi fost șters. Putem astfel să vedem din
exteriorul containerului, după ce acesta a terminat de rulat, fișierul care a fost creat în interiorul containerului:

$ 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:

$ docker container rm 8bae62d8aec0

$ ls 153c4ef3dc54d49cc1d2b32492385a69d59ae1b81460b42c7c34dea07e828f96/

ls: cannot access '153c4ef3dc54d49cc1d2b32492385a69d59ae1b81460b42c7c34dea07e828f96/': No such file or directory

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

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS


NAMES
59d0785188a6 alpine "sh -c 'ping 8.8.8..." About a minute ago Up About a minute c2

Î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

PING 8.8.8.8 (8.8.8.8): 56 data bytes


64 bytes from 8.8.8.8: seq=0 ttl=38 time=58.619 ms
64 bytes from 8.8.8.8: seq=1 ttl=38 time=58.498 ms

Dacă oprim și ștergem containerul, volumul va exista în continuare:

$ docker container stop 59d0785188a6

59d0785188a6

$ docker container rm 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).

Mai departe în fișierul docker-stack.yml, urmează serviciul pentru baza de date:

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.

Prezentăm în continuare următorul serviciu din fișierul docker-stack.yml:

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:

$ git clone https://github.com/dockersamples/example-voting-app.git

Cloning into 'example-voting-app'...


remote: Counting objects: 377, done.
remote: Total 377 (delta 0), reused 0 (delta 0), pack-reused 377
Receiving objects: 100% (377/377), 204.54 KiB | 0 bytes/s, done.
Resolving deltas: 100% (133/133), done.
Checking connectivity... done.

$ cd example-voting-app/

$ docker stack deploy -c docker-stack.yml voting-app

Creating network voting-app_frontend


Creating network voting-app_backend
Creating network voting-app_default
Creating service voting-app_redis
Creating service voting-app_db
Creating service voting-app_vote
Creating service voting-app_result
Creating service voting-app_worker
Creating service voting-app_visualizer

$ docker stack ls
NAME SERVICES
voting-app 6

$ docker stack services voting-app

ID NAME MODE REPLICAS IMAGE


PORTS
bmdkyni9bp3m voting-app_worker replicated 1/1 dockersamples/examplevotingapp_worker:latest
d0z4l413s3tq voting-app_visualizer replicated 1/1 dockersamples/visualizer:stable
*:8080->8080/tcp
gjvjtk6eqra9 voting-app_result replicated 1/1 dockersamples/examplevotingapp_result:before
*:5001->80/tcp
pdx68vr47z5q voting-app_redis replicated 1/1 redis:alpine
*:30000->6379/tcp
t8ex4zkcs7xp voting-app_db replicated 1/1 postgres:9.4
wwbe5mjd38by voting-app_vote replicated 2/2 dockersamples/examplevotingapp_vote:before
*:5000->80/tcp

Î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.

S-ar putea să vă placă și