This shows you the differences between two versions of the page.
idp:laboratoare:02 [2023/02/22 11:08] radu.ciobanu |
idp:laboratoare:02 [2023/03/20 22:03] (current) radu.ciobanu |
||
---|---|---|---|
Line 1: | Line 1: | ||
===== Laboratorul 02 - Docker Compose și Docker Swarm ===== | ===== Laboratorul 02 - Docker Compose și Docker Swarm ===== | ||
- | <hidden>==== Introducere ==== | + | ==== Docker Compose ==== |
În mod clasic, pentru rularea unor containere, este nevoie să rulați comanda aferentă de rulare (**//docker run//**) și să dați toți parametrii necesari. Acest proces poate deveni anevoios dacă este repetat pentru pornirea mai multor containere. Un mod de a „salva” configurația de rulare este să ne creăm scripturi. Problema în rularea mai multor scripturi este pierderea uniformității în configurare (ce container la ce rețea se conectează, cu cine comunică, etc.). | În mod clasic, pentru rularea unor containere, este nevoie să rulați comanda aferentă de rulare (**//docker run//**) și să dați toți parametrii necesari. Acest proces poate deveni anevoios dacă este repetat pentru pornirea mai multor containere. Un mod de a „salva” configurația de rulare este să ne creăm scripturi. Problema în rularea mai multor scripturi este pierderea uniformității în configurare (ce container la ce rețea se conectează, cu cine comunică, etc.). | ||
Line 7: | Line 7: | ||
[[https://docs.docker.com/compose/|Docker Compose]] este un utilitar creat de către Docker folosit pentru centralizarea configurării de rulare a containerelor în manieră declarativă. Utilizând fișiere de configurare YAML (Yet Another Markup Language), Docker Compose centralizează procesul de configurare într-o manieră naturală, declarativă. | [[https://docs.docker.com/compose/|Docker Compose]] este un utilitar creat de către Docker folosit pentru centralizarea configurării de rulare a containerelor în manieră declarativă. Utilizând fișiere de configurare YAML (Yet Another Markup Language), Docker Compose centralizează procesul de configurare într-o manieră naturală, declarativă. | ||
- | Mai mult decât atât, formatul pentru fișierele Compose este utilizat și în cadrul **//Docker Swarm//**, orchestratorul creat de Docker pentru gestiunea serviciilor Docker, despre care vom discuta în [[idp:laboratoare:03|laboratorul 3]]. | + | Mai mult decât atât, formatul pentru fișierele Compose este utilizat și în cadrul **//Docker Swarm//**, orchestratorul creat de Docker pentru gestiunea serviciilor Docker, despre care vom discuta mai târziu. |
<note tip>Veți observa că, în acest laborator, folosim termenii de servicii și containere în mod interschimbabil. Acest lucru se întâmplă pentru că Docker Swarm lucrează cu servicii, în timp ce Docker Compose cu containere. Ne referim la ambii termeni în același context deoarece configurația este, în proporție de 90%, identică, indiferent de utilizarea Swarm sau Compose.</note> | <note tip>Veți observa că, în acest laborator, folosim termenii de servicii și containere în mod interschimbabil. Acest lucru se întâmplă pentru că Docker Swarm lucrează cu servicii, în timp ce Docker Compose cu containere. Ne referim la ambii termeni în același context deoarece configurația este, în proporție de 90%, identică, indiferent de utilizarea Swarm sau Compose.</note> | ||
- | ==== Instalare ==== | + | === Instalare === |
Pentru sisteme Windows și MacOS, Docker Compose face parte din instalarea de Docker Desktop for Windows/Mac. Pentru Linux, instalarea se realizează [[https://docs.docker.com/compose/install/|conform ghidului oficial]]. | Pentru sisteme Windows și MacOS, Docker Compose face parte din instalarea de Docker Desktop for Windows/Mac. Pentru Linux, instalarea se realizează [[https://docs.docker.com/compose/install/|conform ghidului oficial]]. | ||
- | ==== Elemente cheie ==== | + | === Elemente cheie === |
- | === Formatul unui fișier YAML === | + | == Formatul unui fișier YAML == |
Fișierele [[https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html|YAML]] sunt folosite de obicei pentru a scrie configurări în mod declarativ. Formatul este unul foarte ușor de înțeles și folosit, astfel: | Fișierele [[https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html|YAML]] sunt folosite de obicei pentru a scrie configurări în mod declarativ. Formatul este unul foarte ușor de înțeles și folosit, astfel: | ||
Line 23: | Line 23: | ||
* se folosesc elemente de tip „cheie:valoare” | * se folosesc elemente de tip „cheie:valoare” | ||
* aliniatele indentate reprezintă proprietăți copii ale paragrafelor anterioare | * aliniatele indentate reprezintă proprietăți copii ale paragrafelor anterioare | ||
- | * listele se delimiteaza prin „-”. | + | * listele se delimitează prin „-”. |
- | === Exemplu de fișier Docker Compose === | + | == Exemplu de fișier Docker Compose == |
<code yaml> | <code yaml> | ||
# docker-compose.yml | # docker-compose.yml | ||
Line 64: | Line 64: | ||
</code> | </code> | ||
- | == Version == | + | = Version = |
Descrie ce [[https://docs.docker.com/compose/compose-file/|funcționalități]] vor fi încărcate la rularea utilitarului Docker Compose. | Descrie ce [[https://docs.docker.com/compose/compose-file/|funcționalități]] vor fi încărcate la rularea utilitarului Docker Compose. | ||
Line 70: | Line 70: | ||
<note warning>Este obligatoriu să treceți versiunea în orice fișier Docker Compose.</note> | <note warning>Este obligatoriu să treceți versiunea în orice fișier Docker Compose.</note> | ||
- | == Services == | + | = Services = |
Descrie serviciile/containerele ce vor rula după ce configurația este pornită de către Compose. Fiecare serviciu reprezintă un container care va avea numele și configurația serviciului. În exemplul de mai sus, containerele se vor numi //**api**// și //**postgres**//. Cele mai importante proprietăți ale //**services**// sunt următoarele: | Descrie serviciile/containerele ce vor rula după ce configurația este pornită de către Compose. Fiecare serviciu reprezintă un container care va avea numele și configurația serviciului. În exemplul de mai sus, containerele se vor numi //**api**// și //**postgres**//. Cele mai importante proprietăți ale //**services**// sunt următoarele: | ||
Line 80: | Line 80: | ||
* **//networks//** - lista de rețele din care face parte serviciul/containerul | * **//networks//** - lista de rețele din care face parte serviciul/containerul | ||
* **//secrets//** - lista de secrete ce vor fi folosite în cadrul serviciului/containerului | * **//secrets//** - lista de secrete ce vor fi folosite în cadrul serviciului/containerului | ||
- | * **//environment//** - obiect cu intrări de tipul „nume_variabilă_mediu_servici:valoare” care injectează variabilele de mediu specificate la rularea serviciului/containerului. | + | * **//environment//** - obiect cu intrări de tipul „nume_variabilă_mediu_serviciu:valoare” care injectează variabilele de mediu specificate la rularea serviciului/containerului. |
<note important>Opțiunile **//build//** și **//image//** sunt disjuncte.</note> | <note important>Opțiunile **//build//** și **//image//** sunt disjuncte.</note> | ||
<note tip>Secretele trebuie trecute și în cadrul variabilelor de mediu, conform documentației. De exemplu, [[https://hub.docker.com/_/postgres|în configurația Postgres]], secretele trebuie trecute în variabile de mediu speciale, sufixate cu **//_FILE//**, alături de calea lor completă (adică //**/run/secrets/NUME_SECRET**//).</note> | <note tip>Secretele trebuie trecute și în cadrul variabilelor de mediu, conform documentației. De exemplu, [[https://hub.docker.com/_/postgres|în configurația Postgres]], secretele trebuie trecute în variabile de mediu speciale, sufixate cu **//_FILE//**, alături de calea lor completă (adică //**/run/secrets/NUME_SECRET**//).</note> | ||
+ | = Volumes = | ||
- | == Volumes == | + | Descrie volumele utilizate în cadrul configurației. Volumele se trec sub formă de obiecte. Dacă nu se dorește schimbarea configurației implicite, valoarea este un câmp gol. |
- | + | ||
- | Descrie volumele utilizate în cadrul configurației. Volumele se trec sub formă de obiecte. Dacă nu se dorește schimbarea configurației implicite, valoarea este un câmp gol. În [[idp:laboratoare:03|laboratorul 3]] vom lucra cu volume NFS, unde va trebui oferită o configurație custom. | + | |
<note tip>Proprietatea top-level **//volumes//** trebuie scrisă pe același nivel de indentare ca **//services//**. Nu trebuie confundată cu proprietatea copil **//volumes//** din interiorul configurației serviciilor.</note> | <note tip>Proprietatea top-level **//volumes//** trebuie scrisă pe același nivel de indentare ca **//services//**. Nu trebuie confundată cu proprietatea copil **//volumes//** din interiorul configurației serviciilor.</note> | ||
- | == Networks == | + | = Networks = |
Descrie rețelele utilizate în cadrul configurației. Rețelele se trec sub formă de obiecte. Dacă nu se dorește schimbarea configurației implicite, valoarea este un câmp gol. Un exemplu de configurație de rețea este următorul (unde folosim o rețea care deja există, pentru că a fost creată independent de fișierul Docker Compose): | Descrie rețelele utilizate în cadrul configurației. Rețelele se trec sub formă de obiecte. Dacă nu se dorește schimbarea configurației implicite, valoarea este un câmp gol. Un exemplu de configurație de rețea este următorul (unde folosim o rețea care deja există, pentru că a fost creată independent de fișierul Docker Compose): | ||
Line 107: | Line 106: | ||
<note tip>Proprietatea top-level **//networks//** trebuie scrisă pe același nivel de indentare ca **//services//**. Nu trebuie confundată cu proprietatea copil **//networks//** din interiorul configurației serviciilor.</note> | <note tip>Proprietatea top-level **//networks//** trebuie scrisă pe același nivel de indentare ca **//services//**. Nu trebuie confundată cu proprietatea copil **//networks//** din interiorul configurației serviciilor.</note> | ||
- | == Secrets == | + | = Secrets = |
- | Descrie secretele utilizate în cadrul configurației. Acestea rețin informații sensibile într-o manieră securizată, criptată, în cadrul Swarm, despre care vom vorbi la [[idp:laboratoare:03|laboratorul 3]]. În Compose, secretele nu sunt securizate, însă au fost introduse pentru a ușura tranziția către Swarm. În cadrul Docker Compose, secretele pot proveni doar din fișiere externe, care trebuie specificate pentru fiecare secret în parte. | + | Descrie secretele utilizate în cadrul configurației. Acestea rețin informații sensibile într-o manieră securizată, criptată, în cadrul Swarm. În Compose, secretele nu sunt securizate, însă au fost introduse pentru a ușura tranziția către Swarm. În cadrul Docker Compose, secretele pot proveni doar din fișiere externe, care trebuie specificate pentru fiecare secret în parte. |
- | <note tip>Proprietatea top-level **//secrets//** este scrisa pe același nivel de indentare ca **//services//**. Nu trebuie confundată cu proprietatea copil **//secrets//** din interiorul configurației serviciilor.</note> | + | <note tip>Proprietatea top-level **//secrets//** este scrisă pe același nivel de indentare ca **//services//**. Nu trebuie confundată cu proprietatea copil **//secrets//** din interiorul configurației serviciilor.</note> |
- | ==== Comenzi Docker Compose ==== | + | === Comenzi Docker Compose === |
- | Comenzile pentru interacțiunea cu Docker Compose seamănă, ca sintaxă, cu cele clasice de Docker atât pentru versiunea 1 cât și pentru versiunea 2. Mai jos, puteți observa cele mai utilizate comenzi, pentru versiunea 1 utilitarul este "docker-compose" iar pentru versiunea 2 utilitarul este tot "docker" cu parametrul "compose", iar pe restul le puteți studia în [[https://docs.docker.com/compose/reference/|documentația oficială pentru versiunea 1]] respectiv [[https://docs.docker.com/engine/reference/commandline/compose/|documentația oficială pentru versiunea 2]]. Diferența principala între versiunea 1 si 2 este ca exista mai multe opțiuni cum ar fi listarea stivelor de containere prin opțiunea "ls". | + | Comenzile pentru interacțiunea cu Docker Compose seamănă, ca sintaxă, cu cele clasice de Docker atât pentru versiunea 1 cât și pentru versiunea 2. Mai jos, puteți observa cele mai utilizate comenzi. Pentru versiunea 1, utilitarul este "docker-compose", iar pentru versiunea 2 utilitarul este tot "docker" cu parametrul "compose", iar pe restul le puteți studia în [[https://docs.docker.com/compose/reference/|documentația oficială pentru versiunea 1]] respectiv [[https://docs.docker.com/engine/reference/commandline/compose/|documentația oficială pentru versiunea 2]]. Diferența principala între versiunea 1 si 2 este ca exista mai multe opțiuni cum ar fi listarea stivelor de containere prin opțiunea "ls". |
<code bash> | <code bash> | ||
Line 123: | Line 122: | ||
$ docker-compose unpause # scoate din starea de pauză containerele V1 | $ docker-compose unpause # scoate din starea de pauză containerele V1 | ||
$ docker-compose ps # listează containerele active V1 | $ docker-compose ps # listează containerele active V1 | ||
- | $ docker-compose up # face build, recreează, pornește și atașeaza containere la un serviciu V1 | + | $ docker-compose up # face build, recreează, pornește și atașează containere la un serviciu V1 |
$ docker-compose up -d # serviciile rulează în fundal, detașate de terminalul care le-a inițializat V1 | $ docker-compose up -d # serviciile rulează în fundal, detașate de terminalul care le-a inițializat V1 | ||
$ docker-compose up --build # creează imaginile înainte de pornire V1 | $ docker-compose up --build # creează imaginile înainte de pornire V1 | ||
Line 141: | Line 140: | ||
</code> | </code> | ||
- | ==== Combinarea mai multor fișiere Docker Compose ==== | + | === Combinarea mai multor fișiere Docker Compose === |
Docker Compose este recomandat să fie utilizat doar în etapa de dezvoltare locală. Pentru medii de testare sau producție, se folosește Docker Swarm (sau alte orchestratoare, precum Kubernetes). | Docker Compose este recomandat să fie utilizat doar în etapa de dezvoltare locală. Pentru medii de testare sau producție, se folosește Docker Swarm (sau alte orchestratoare, precum Kubernetes). | ||
Line 156: | Line 155: | ||
În comanda de mai sus, informațiile din //**fisier-compose-2.yml**// vor suprascrie/completa informațiile din //**fisier-compose-1.yml**//. Acest lucru este util pentru a testa rapid diverse configurații. | În comanda de mai sus, informațiile din //**fisier-compose-2.yml**// vor suprascrie/completa informațiile din //**fisier-compose-1.yml**//. Acest lucru este util pentru a testa rapid diverse configurații. | ||
- | ==== Exerciții ==== | + | ==== Docker Swarm ==== |
- | + | ||
- | <note tip>Puteți accesa resursele practice pentru acest laborator pe [[https://gitlab.com/mobylab-idp/laborator-2|repository-ul oficial]] al materiei IDP. Exercițiile urmăresc înțelegerea conceptelor de Docker Compose.</note> | + | |
- | + | ||
- | - Scrieți configurația containerelor de la ultimul set de exerciții din [[idp:laboratoare:01|laboratorul 1]] în maniera Compose: | + | |
- | - pentru containerul de API, folosiți sursele și Dockerfile-ul din directorul //**API**// aflat pe repository, sau direct imaginea //**mobylab/idp-laborator2-api**// de pe Docker Hub | + | |
- | - pentru containerul de Postgres, găsiți fișierul //**init-db.sql**// în directorul //**Database**// din repository. | + | |
- | - Introduceți un al treilea serviciu bazat pe imaginea **//adminer//**, care face parte dintr-o rețea comună cu baza de date, alta decât cea pe care baza de date o folosește la comunicația cu API-ul (așadar, trebui să aveți două rețele). | + | |
- | - Spargeți configurația în două fișiere de Compose, unul de bază și unul care să folosească secrete (găsiți fișierele pentru secrete în directorul //**secrets**// din repository). Rulați cele două configurații împreună. | + | |
- | + | ||
- | <note important>După ce veți sparge configurația inițială în două, o să observați că trebuie să renunțați la niște variabile de mediu ale bazei de date din fișierul de bază, și anume //**POSTGRES_USER**// și //**POSTGRES_PASSWORD**//. În configurația cu secrete, numele de utilizator și parola vor fi setate prin variabilele de mediu //**POSTGRES_USER_FILE**// și //**POSTGRES_PASSWORD_FILE**//, care sunt disjuncte cu cele menționate anterior. Este important de menționat că valorile pentru aceste două variabile de mediu reprezintă căile //**complete**// ale fișierelor secret, de forma //**/run/secrets/NUME_SECRET**//.</note> | + | |
- | + | ||
- | <note important>Pentru a introduce secrete și în serviciul de API, trebuie să setați variabila de mediu //**NODE_ENV**// la valoarea „staging” și să setați variabilele de mediu //**PGPASSWORD_SECRET**// și //**PGUSER_SECRET**// doar cu numele secretului, nu cu toată calea //**/run/secrets/NUME_SECRET**// precum în cazul bazei de date.</note></hidden> | + | |
- | + | ||
- | <hidden>==== Introducere ==== | + | |
- | În acest laborator, vom face tranziția de la Docker Compose la Docker Swarm, **//orchestratorul de servicii//** oferit de Docker. Acesta are rolul de a gestiona servicii de Docker pe una sau mai multe mașini într-o rețea (într-un cluster) de mașini fizice și/sau virtuale. Spre deosebire de Docker Compose, care rulează containere pe o singură gazdă, Docker Swarm rulează //**servicii**// între mai multe gazde. La fel ca și Compose, Docker Swarm folosește fișiere de configurare YAML. | + | În continuare, vom face tranziția de la Docker Compose la Docker Swarm, **//orchestratorul de servicii//** oferit de Docker. Acesta are rolul de a gestiona servicii de Docker pe una sau mai multe mașini într-o rețea (într-un cluster) de mașini fizice și/sau virtuale. Spre deosebire de Docker Compose, care rulează containere pe o singură gazdă, Docker Swarm rulează //**servicii**// între mai multe gazde. La fel ca și Compose, Docker Swarm folosește fișiere de configurare YAML. |
- | ==== Arhitectura Docker Swarm ==== | + | === Arhitectura Docker Swarm === |
Docker Swarm se bazează pe algoritmul distribuit [[https://raft.github.io|Raft]], cu ajutorul căruia se menține consistentă starea internă a întregului cluster. În plus, traficul dintre nodurile din Swarm este criptat de către Raft. În imaginea de mai jos (preluată din [[https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/|documentația oficială]]), se poate observa arhitectura unui cluster Docker Swarm. | Docker Swarm se bazează pe algoritmul distribuit [[https://raft.github.io|Raft]], cu ajutorul căruia se menține consistentă starea internă a întregului cluster. În plus, traficul dintre nodurile din Swarm este criptat de către Raft. În imaginea de mai jos (preluată din [[https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/|documentația oficială]]), se poate observa arhitectura unui cluster Docker Swarm. | ||
Line 190: | Line 175: | ||
<note tip>Deoarece toleranța este de (N - 1) / 2 noduri manager picate, este indicat să avem un număr impar de noduri și un număr impar de manageri. Docker recomanda 3, 5 sau 7 manageri.</note> | <note tip>Deoarece toleranța este de (N - 1) / 2 noduri manager picate, este indicat să avem un număr impar de noduri și un număr impar de manageri. Docker recomanda 3, 5 sau 7 manageri.</note> | ||
- | ==== Crearea unui Docker Swarm ==== | + | === Crearea unui Docker Swarm === |
Odată ce avem un cluster de mașini pe care rulează Docker, ne putem inițializa un Docker Swarm. Astfel, putem rula următoarea comandă pe nodul care va fi leader (opțiunea //**%%--%%advertise-addr**// este necesară atunci când nodul are mai multe interfețe de rețea și trebuie specificat pe care din ele se face advertising): | Odată ce avem un cluster de mașini pe care rulează Docker, ne putem inițializa un Docker Swarm. Astfel, putem rula următoarea comandă pe nodul care va fi leader (opțiunea //**%%--%%advertise-addr**// este necesară atunci când nodul are mai multe interfețe de rețea și trebuie specificat pe care din ele se face advertising): | ||
Line 215: | Line 200: | ||
</code> | </code> | ||
- | ==== Servicii și stive de servicii Docker Swarm ==== | + | === Servicii și stive de servicii Docker Swarm === |
Atunci când vorbim de deployment-ul unei aplicații în Docker Swarm, trecem de la noțiunea de container la noțiunea de serviciu. Un serviciu Docker reprezintă o colecție de task-uri (unul sau mai multe), iar un task reprezintă un container. Așadar, un serviciu este format din unul sau mai multe containere identice. Serviciul controlează ciclul de viață al containerelor, încercând întotdeauna să mențină starea containerelor oferite în configurație. Cu alte cuvinte, un serviciu reprezintă un set de containere cu //**orchestrare**//. | Atunci când vorbim de deployment-ul unei aplicații în Docker Swarm, trecem de la noțiunea de container la noțiunea de serviciu. Un serviciu Docker reprezintă o colecție de task-uri (unul sau mai multe), iar un task reprezintă un container. Așadar, un serviciu este format din unul sau mai multe containere identice. Serviciul controlează ciclul de viață al containerelor, încercând întotdeauna să mențină starea containerelor oferite în configurație. Cu alte cuvinte, un serviciu reprezintă un set de containere cu //**orchestrare**//. | ||
- | Mai departe, o stivă de servicii reprezintă mai multe astfel de servicii grupate în același spațiu de nume. Putem vizualiza o stivă de servicii ca fiind o aplicație Docker formată din mai multe servicii. Cel mai facil mod de a defini o stivă de servicii este prin intermediul unui fișier Docker Compose, așa cum am văzut în [[idp:laboratoare:02|laboratorul 2]]. Comportamentul serviciilor dintr-o stivă este similar cu cel al containerelor din Docker Compose, doar că politica de denumire este diferită. | + | Mai departe, o stivă de servicii reprezintă mai multe astfel de servicii grupate în același spațiu de nume. Putem vizualiza o stivă de servicii ca fiind o aplicație Docker formată din mai multe servicii. Cel mai facil mod de a defini o stivă de servicii este prin intermediul unui fișier Docker Compose, așa cum am văzut mai devreme. Comportamentul serviciilor dintr-o stivă este similar cu cel al containerelor din Docker Compose, doar că politica de denumire este diferită. |
<note tip>Orice entitate creată într-o stivă (serviciu, volum, rețea, secret) va fi prefixată de **//NUME-STIVA_//**.</note> | <note tip>Orice entitate creată într-o stivă (serviciu, volum, rețea, secret) va fi prefixată de **//NUME-STIVA_//**.</note> | ||
Line 243: | Line 228: | ||
În fragmentul de fișier YAML de mai sus, se rulează un serviciu numit //**web**// care are patru copii. Astfel, vor exista patru containere diferite care rulează imaginea //**myimage**//, oricare din ele putând răspunde la cereri pentru serviciul //**web**//, în funcție de încărcare. De asemenea, fiecare instanță este limitată la 20% CPU (pe toate core-urile) și 50 MB de RAM. Nu în ultimul rând, un container al serviciului //**web**// se restartează imediat ce întâlnește o eroare (scopul final fiind ca, la orice moment de timp, să existe 4 copii ale containerului în rețea). | În fragmentul de fișier YAML de mai sus, se rulează un serviciu numit //**web**// care are patru copii. Astfel, vor exista patru containere diferite care rulează imaginea //**myimage**//, oricare din ele putând răspunde la cereri pentru serviciul //**web**//, în funcție de încărcare. De asemenea, fiecare instanță este limitată la 20% CPU (pe toate core-urile) și 50 MB de RAM. Nu în ultimul rând, un container al serviciului //**web**// se restartează imediat ce întâlnește o eroare (scopul final fiind ca, la orice moment de timp, să existe 4 copii ale containerului în rețea). | ||
- | === Rețele Swarm === | + | == Rețele Swarm == |
Spre deosebire de Docker clasic și Compose, rețelele create în Swarm nu mai sunt de tip bridge, ci de tip **//overlay//**. O rețea de tip overlay este o rețea care se întinde peste toate nodurile dintr-un swarm. Din acest motiv, porturile publice expuse vor fi unice per rețea. Așadar, nu pot fi expuse două porturi 3000 din două servicii diferite care se conectează la aceeași rețea overlay. | Spre deosebire de Docker clasic și Compose, rețelele create în Swarm nu mai sunt de tip bridge, ci de tip **//overlay//**. O rețea de tip overlay este o rețea care se întinde peste toate nodurile dintr-un swarm. Din acest motiv, porturile publice expuse vor fi unice per rețea. Așadar, nu pot fi expuse două porturi 3000 din două servicii diferite care se conectează la aceeași rețea overlay. | ||
Line 253: | Line 238: | ||
{{:idp:laboratoare:network.png?600|}} | {{:idp:laboratoare:network.png?600|}} | ||
- | === Secrete Swarm === | + | == Secrete Swarm == |
Secretele din Swarm trebuie create înainte de a fi rulată configurația. Putem folosi următoarea comandă: | Secretele din Swarm trebuie create înainte de a fi rulată configurația. Putem folosi următoarea comandă: | ||
Line 274: | Line 259: | ||
Secretele din Swarm sunt criptate în Raft și deci este recomandat să fie folosite în producție. | Secretele din Swarm sunt criptate în Raft și deci este recomandat să fie folosite în producție. | ||
- | === Diferențe între Docker Swarm și Docker Compose === | + | == Diferențe între Docker Swarm și Docker Compose == |
Exista câteva diferențe între Swarm și Compose la nivelul fișierelor declarative YAML: | Exista câteva diferențe între Swarm și Compose la nivelul fișierelor declarative YAML: | ||
Line 281: | Line 266: | ||
* Docker Compose rulează containere în mod single-host, pe când Docker Swarm orchestrează servicii în mod multi-host. | * Docker Compose rulează containere în mod single-host, pe când Docker Swarm orchestrează servicii în mod multi-host. | ||
- | === Pornirea unei stive de servicii în Docker Swarm === | + | == Pornirea unei stive de servicii în Docker Swarm == |
- | Odată ce swarm-ul Docker a fost creat și inițializat, comanda prin care se face deployment la o stivă de servicii este următoarea (unde configurația se găsește în fișierul //**my_stack.yml**//, iar numele stivei va fi //**lab3**//): | + | Odată ce swarm-ul Docker a fost creat și inițializat, comanda prin care se face deployment la o stivă de servicii este următoarea (unde configurația se găsește în fișierul //**my_stack.yml**//, iar numele stivei va fi //**lab2**//): |
<code bash> | <code bash> | ||
- | $ docker stack deploy -c my_stack.yml lab3 | + | $ docker stack deploy -c my_stack.yml lab2 |
</code> | </code> | ||
Line 294: | Line 279: | ||
<code bash> | <code bash> | ||
- | $ docker stack ps lab3 | + | $ docker stack ps lab2 |
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS | ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS | ||
- | cuktma92gm62 lab3_adminer.1 adminer:latest myvm2 Running Running 9 minutes ago | + | cuktma92gm62 lab2_adminer.1 adminer:latest myvm2 Running Running 9 minutes ago |
- | njak2qzaobtt lab3_db.1 postgres:12 myvm1 Running Running 8 minutes ago | + | njak2qzaobtt lab2_db.1 postgres:12 myvm1 Running Running 8 minutes ago |
- | m811buil7e63 lab3_io-service.1 mobylab/idp-laborator3-io:latest myvm1 Running Running 9 minutes ago | + | m811buil7e63 lab2_io-service.1 mobylab/idp-laborator2-io:latest myvm1 Running Running 9 minutes ago |
- | jnfw37e34kz3 lab3_books-service.1 mobylab/idp-laborator3-books:latest myvm1 Running Running 9 minutes ago | + | jnfw37e34kz3 lab2_books-service.1 mobylab/idp-laborator2-books:latest myvm1 Running Running 9 minutes ago |
- | pzlzkgsxxc00 lab3_gateway.1 mobylab/idp-laborator3-gateway:latest myvm2 Running Running 9 minutes ago | + | pzlzkgsxxc00 lab2_gateway.1 mobylab/idp-laborator2-gateway:latest myvm2 Running Running 9 minutes ago |
- | kpaahb931rbq lab3_io-service.2 mobylab/idp-laborator3-io:latest myvm1 Running Running 9 minutes ago | + | kpaahb931rbq lab2_io-service.2 mobylab/idp-laborator2-io:latest myvm1 Running Running 9 minutes ago |
- | num87yijgxrg lab3_books-service.2 mobylab/idp-laborator3-books:latest myvm2 Running Running 9 minutes ago | + | num87yijgxrg lab2_books-service.2 mobylab/idp-laborator2-books:latest myvm2 Running Running 9 minutes ago |
- | d9m63k9h7ium lab3_gateway.2 mobylab/idp-laborator3-gateway:latest myvm1 Running Running 9 minutes ago | + | d9m63k9h7ium lab2_gateway.2 mobylab/idp-laborator2-gateway:latest myvm1 Running Running 9 minutes ago |
- | lkmy60wpy0gv lab3_io-service.3 mobylab/idp-laborator3-io:latest myvm2 Running Running 9 minutes ago | + | lkmy60wpy0gv lab2_io-service.3 mobylab/idp-laborator2-io:latest myvm2 Running Running 9 minutes ago |
- | fy21iizn0reb lab3_gateway.3 mobylab/idp-laborator3-gateway:latest myvm2 Running Running 9 minutes ago | + | fy21iizn0reb lab2_gateway.3 mobylab/idp-laborator2-gateway:latest myvm2 Running Running 9 minutes ago |
</code> | </code> | ||
Line 315: | Line 300: | ||
NAME SERVICES ORCHESTRATOR | NAME SERVICES ORCHESTRATOR | ||
- | lab3 5 Swarm | + | lab2 5 Swarm |
</code> | </code> | ||
Line 324: | Line 309: | ||
ID NAME MODE REPLICAS IMAGE PORTS | ID NAME MODE REPLICAS IMAGE PORTS | ||
- | dekzzyais8g7 lab3_adminer replicated 1/1 adminer:latest *:8080->8080/tcp | + | dekzzyais8g7 lab2_adminer replicated 1/1 adminer:latest *:8080->8080/tcp |
- | 74y84hvq4irn lab3_books-service replicated 2/2 (max 1 per node) mobylab/idp-laborator3-books:latest | + | 74y84hvq4irn lab2_books-service replicated 2/2 (max 1 per node) mobylab/idp-laborator2-books:latest |
- | ns9mxet1rkx5 lab3_db replicated 1/1 postgres:12 | + | ns9mxet1rkx5 lab2_db replicated 1/1 postgres:12 |
- | dh3sv3q74fy6 lab3_gateway replicated 3/3 (max 2 per node) mobylab/idp-laborator3-gateway:latest *:3000->80/tcp | + | dh3sv3q74fy6 lab2_gateway replicated 3/3 (max 2 per node) mobylab/idp-laborator2-gateway:latest *:3000->80/tcp |
- | ru0rd7g2ypu8 lab3_io-service replicated 3/3 (max 2 per node) mobylab/idp-laborator3-io:latest | + | ru0rd7g2ypu8 lab2_io-service replicated 3/3 (max 2 per node) mobylab/idp-laborator2-io:latest |
</code> | </code> | ||
- | ==== Deployment-ul unui cluster Docker ==== | + | |
+ | === Deployment-ul unui cluster Docker === | ||
În cadrul laboratoarelor de IDP, avem două variante principale cu ajutorul cărora putem să ne creăm un cluster Docker format din mai multe mașini: [[https://docs.docker.com/machine/|Docker Machine]] sau [[https://labs.play-with-docker.com|Play with Docker]]. | În cadrul laboratoarelor de IDP, avem două variante principale cu ajutorul cărora putem să ne creăm un cluster Docker format din mai multe mașini: [[https://docs.docker.com/machine/|Docker Machine]] sau [[https://labs.play-with-docker.com|Play with Docker]]. | ||
- | === Docker Machine === | + | == Docker Machine == |
<note important>Recent, Docker Machine a devenit deprecated, deci cei de la Docker nu mai mențin acest proiect. Puteți totuși testa un deployment multi-nod folosind Docker Machine, dar vă recomandăm varianta Play with Docker sau un cluster propriu (fizic sau virtual).</note> | <note important>Recent, Docker Machine a devenit deprecated, deci cei de la Docker nu mai mențin acest proiect. Puteți totuși testa un deployment multi-nod folosind Docker Machine, dar vă recomandăm varianta Play with Docker sau un cluster propriu (fizic sau virtual).</note> | ||
Line 396: | Line 382: | ||
</code> | </code> | ||
- | === Play with Docker === | + | == Play with Docker == |
Play with Docker este un mediu online de învățare Docker ce oferă mașini virtuale pe o durată de 4 ore. | Play with Docker este un mediu online de învățare Docker ce oferă mașini virtuale pe o durată de 4 ore. | ||
Line 402: | Line 388: | ||
{{:idp:laboratoare:playwithdocker.png?900}} | {{:idp:laboratoare:playwithdocker.png?900}} | ||
- | ==== Comenzi utile ==== | + | === Comenzi utile === |
- | === Interacțiune servicii === | + | == Interacțiune servicii == |
<note tip>Aceste comenzi pot fi rulate doar de pe noduri manager.</note> | <note tip>Aceste comenzi pot fi rulate doar de pe noduri manager.</note> | ||
Line 418: | Line 404: | ||
</code> | </code> | ||
- | === Interacțiune cluster === | + | == Interacțiune cluster == |
<note tip>Aceste comenzi pot fi rulate doar de pe noduri manager.</note> | <note tip>Aceste comenzi pot fi rulate doar de pe noduri manager.</note> | ||
Line 430: | Line 416: | ||
</code> | </code> | ||
- | === Docker Machine === | + | == Docker Machine == |
<code bash> | <code bash> | ||
Line 444: | Line 430: | ||
==== Exerciții ==== | ==== Exerciții ==== | ||
- | === Descriere aplicație === | + | === Docker Compose === |
+ | |||
+ | <note tip>Puteți accesa resursele practice pentru acest laborator pe [[https://gitlab.com/mobylab-idp/laborator-2|repository-ul oficial]] al materiei IDP. Exercițiile urmăresc înțelegerea conceptelor de Docker Compose.</note> | ||
+ | |||
+ | - Scrieți configurația containerelor de la ultimul set de exerciții din [[idp:laboratoare:01|laboratorul 1]] în maniera Compose: | ||
+ | - pentru containerul de API, folosiți sursele și Dockerfile-ul din directorul //**API**// aflat pe repository, sau direct imaginea //**mobylab/idp-laborator2-api**// de pe Docker Hub | ||
+ | - pentru containerul de Postgres, găsiți fișierul //**init-db.sql**// în directorul //**Database**// din repository. | ||
+ | - Introduceți un al treilea serviciu bazat pe imaginea **//adminer//**, care face parte dintr-o rețea comună cu baza de date, alta decât cea pe care baza de date o folosește la comunicația cu API-ul (așadar, trebui să aveți două rețele). | ||
+ | - Spargeți configurația în două fișiere de Compose, unul de bază și unul care să folosească secrete (găsiți fișierele pentru secrete în directorul //**secrets**// din repository). Rulați cele două configurații împreună. | ||
+ | |||
+ | <note important>După ce veți sparge configurația inițială în două, o să observați că trebuie să renunțați la niște variabile de mediu ale bazei de date din fișierul de bază, și anume //**POSTGRES_USER**// și //**POSTGRES_PASSWORD**//. În configurația cu secrete, numele de utilizator și parola vor fi setate prin variabilele de mediu //**POSTGRES_USER_FILE**// și //**POSTGRES_PASSWORD_FILE**//, care sunt disjuncte cu cele menționate anterior. Este important de menționat că valorile pentru aceste două variabile de mediu reprezintă căile //**complete**// ale fișierelor secret, de forma //**/run/secrets/NUME_SECRET**//.</note> | ||
+ | |||
+ | <note important>Pentru a introduce secrete și în serviciul de API, trebuie să setați variabila de mediu //**NODE_ENV**// la valoarea „staging” și să setați variabilele de mediu //**PGPASSWORD_SECRET**// și //**PGUSER_SECRET**// doar cu numele secretului, nu cu toată calea //**/run/secrets/NUME_SECRET**// precum în cazul bazei de date.</note> | ||
+ | |||
+ | === Docker Swarm === | ||
- | Pentru exercițiile din acest laborator, vom lucra cu o variantă extinsă a aplicației bibliotecă din [[idp:laboratoare:02|laboratorul 2]]. Astfel, codul (care se găsește pe [[https://gitlab.com/mobylab-idp/laborator3|repository-ul oficial IDP]]) este format din trei mici microservicii scrise în NodeJS și un script de inițializare pentru o bază de date PostgreSQL. Cele trei microservicii sunt: | + | Pentru exercițiile din această secțiune, vom lucra cu o variantă extinsă a aplicației bibliotecă de mai sus. Astfel, codul (care se găsește pe [[https://gitlab.com/mobylab-idp/laborator3|repository-ul oficial IDP]]) este format din trei mici microservicii scrise în NodeJS și un script de inițializare pentru o bază de date PostgreSQL. Cele trei microservicii sunt: |
* //**ApiGateway**// - mediază accesul dinspre lumea exterioară și redirecționează cererile HTTP către serviciul de cărți | * //**ApiGateway**// - mediază accesul dinspre lumea exterioară și redirecționează cererile HTTP către serviciul de cărți | ||
* //**Books**// - se ocupă de partea de „business logic” ce ține de cărți și apelează serviciul IO pentru date | * //**Books**// - se ocupă de partea de „business logic” ce ține de cărți și apelează serviciul IO pentru date | ||
* //**IO**// - gestionează datele sistemului și realizează comunicația cu baza de date. | * //**IO**// - gestionează datele sistemului și realizează comunicația cu baza de date. | ||
- | === Scop exerciții === | + | == Scop exerciții == |
Scopul exercițiilor este ca, plecând de la codul propus, să realizați o stivă de servicii care să funcționeze corect. Funcționalitatea este reprezentată de trei acțiuni: | Scopul exercițiilor este ca, plecând de la codul propus, să realizați o stivă de servicii care să funcționeze corect. Funcționalitatea este reprezentată de trei acțiuni: | ||
Line 459: | Line 459: | ||
Pe scurt, va trebui să scrieți unul sau mai multe fișier Docker Compose pe care să le folosiți pentru a rula configurația sub forma unei stive de servicii Docker Swarm. Stiva creată va fi formată din 5 servicii: | Pe scurt, va trebui să scrieți unul sau mai multe fișier Docker Compose pe care să le folosiți pentru a rula configurația sub forma unei stive de servicii Docker Swarm. Stiva creată va fi formată din 5 servicii: | ||
- | * //**gateway**// - puteți să creați imaginea pe baza surselor din repository, sau puteți folosi imaginea //**mobylab/idp-laborator3-gateway**// | + | * //**gateway**// - puteți să creați imaginea pe baza surselor din repository, sau puteți folosi imaginea //**mobylab/idp-laborator2-gateway**// |
- | * //**books-service**// - puteți să creați imaginea pe baza surselor din repository, sau puteți folosi imaginea //**mobylab/idp-laborator3-books**// | + | * //**books-service**// - puteți să creați imaginea pe baza surselor din repository, sau puteți folosi imaginea //**mobylab/idp-laborator2-books**// |
- | * //**io-service**// - puteți să creați imaginea pe baza surselor din repository, sau puteți folosi imaginea //**mobylab/idp-laborator3-io**// | + | * //**io-service**// - puteți să creați imaginea pe baza surselor din repository, sau puteți folosi imaginea //**mobylab/idp-laborator2-io**// |
* //**db**// - folosiți imaginea //**postgres:12**// | * //**db**// - folosiți imaginea //**postgres:12**// | ||
* //**adminer**// - folosiți imaginea //**adminer**//. | * //**adminer**// - folosiți imaginea //**adminer**//. | ||
- | === Variabile de mediu === | + | == Variabile de mediu == |
Variabilele de mediu ce vor trebui puse in fișierul Docker Compose sunt următoarele: | Variabilele de mediu ce vor trebui puse in fișierul Docker Compose sunt următoarele: | ||
Line 486: | Line 486: | ||
* POSTGRES_PASSWORD: admin. | * POSTGRES_PASSWORD: admin. | ||
- | === Rețele === | + | == Rețele == |
Pentru a izola accesul serviciilor asupra clusterului, sunt necesare mai multe rețele, astfel: | Pentru a izola accesul serviciilor asupra clusterului, sunt necesare mai multe rețele, astfel: | ||
Line 495: | Line 495: | ||
* serviciul //**db**// va comunica atât cu serviciul //**io-service**//, cât și cu //**adminer**//. | * serviciul //**db**// va comunica atât cu serviciul //**io-service**//, cât și cu //**adminer**//. | ||
- | === Volume și bind mounts === | + | == Volume și bind mounts == |
- | Configurația de volume și bind mounts este la fel ca cea de la exercițiile din [[idp:laboratoare:02|laboratorul 2]]. Astfel, serviciul de bază de date are nevoie de un volum pentru persistență și de un bind mount pentru a se injecta scriptul de inițializare. | + | Configurația de volume și bind mounts este la fel ca cea de la exercițiile de mai sus. Astfel, serviciul de bază de date are nevoie de un volum pentru persistență și de un bind mount pentru a se injecta scriptul de inițializare. |
- | === Porturi publice === | + | == Porturi publice == |
Singurele servicii accesibile din exterior vor fi //**gateway**// și //**adminer**//. Pentru //**gateway**//, trebuie expus portul intern 80, peste care se mapează alt port (de exemplu, 3000), iar pentru //**adminer**// trebuie expus portul intern 8080 (peste care se poate de asemenea mapa alt port extern). | Singurele servicii accesibile din exterior vor fi //**gateway**// și //**adminer**//. Pentru //**gateway**//, trebuie expus portul intern 80, peste care se mapează alt port (de exemplu, 3000), iar pentru //**adminer**// trebuie expus portul intern 8080 (peste care se poate de asemenea mapa alt port extern). | ||
- | === Rute HTTP === | + | == Rute HTTP == |
Dacă reușiți să rulați configurația, puteți să o testați folosind cURL sau [[https://learning.postman.com/docs/getting-started/sending-the-first-request/|Postman]] pe următoarele rute: | Dacă reușiți să rulați configurația, puteți să o testați folosind cURL sau [[https://learning.postman.com/docs/getting-started/sending-the-first-request/|Postman]] pe următoarele rute: | ||
Line 513: | Line 513: | ||
<note tip>Pentru a adăuga un corp JSON unei cereri POST in Postman, selectați Body -> Raw -> JSON și scrieți JSON-ul aferent.</note> | <note tip>Pentru a adăuga un corp JSON unei cereri POST in Postman, selectați Body -> Raw -> JSON și scrieți JSON-ul aferent.</note> | ||
- | === Secrete === | + | == Secrete == |
Opțional, puteți adăuga secrete aplicației, pentru numele de utilizator și parola necesare logării în baza de date. Astfel, serviciile care vor avea nevoie de secrete sunt //**io-service**// și //**db**//. În cele două servicii, va trebui să sufixați variabilele de mediu destinate numelui de utilizator și parolei cu //**_FILE**// și să puneți valorile acestor variabile de mediu în funcție de documentație: | Opțional, puteți adăuga secrete aplicației, pentru numele de utilizator și parola necesare logării în baza de date. Astfel, serviciile care vor avea nevoie de secrete sunt //**io-service**// și //**db**//. În cele două servicii, va trebui să sufixați variabilele de mediu destinate numelui de utilizator și parolei cu //**_FILE**// și să puneți valorile acestor variabile de mediu în funcție de documentație: | ||
Line 519: | Line 519: | ||
* pentru serviciul //**db**//, la variabilele de mediu se pune toată calea secretului (//**/run/secrets/nume-secret**//). | * pentru serviciul //**db**//, la variabilele de mediu se pune toată calea secretului (//**/run/secrets/nume-secret**//). | ||
- | === Enunțuri === | + | == Enunțuri == |
- | - Scrieți un fișier Docker Compose care sa folosească un build local și rulați pe mașina voastră | + | - Scrieți un fișier Docker Compose care să folosească un build local și rulați pe mașina voastră |
- Construiți imaginile și urcați-le pe Docker Hub sau orice alt registru | - Construiți imaginile și urcați-le pe Docker Hub sau orice alt registru | ||
- Scrieți un fișier Docker Compose care să folosească imaginile din registru și rulați pe mașina voastră | - Scrieți un fișier Docker Compose care să folosească imaginile din registru și rulați pe mașina voastră | ||
- Adăugați elemente de Docker Swarm la serviciile din fișierul Docker Compose (la proprietatea //**deploy**//) și rulați stiva de servicii într-un cluster (folosind Play with Docker sau Docker Machine) | - Adăugați elemente de Docker Swarm la serviciile din fișierul Docker Compose (la proprietatea //**deploy**//) și rulați stiva de servicii într-un cluster (folosind Play with Docker sau Docker Machine) | ||
- | - Modificați fișierul Docker Compose ca să utilizați secrete externe pentru numele de utilizator și parolă în containerele de IO și bază de date.</hidden> | + | - Modificați fișierul Docker Compose ca să utilizați secrete externe pentru numele de utilizator și parolă în containerele de IO și bază de date. |