Differences

This shows you the differences between two versions of the page.

Link to this comparison view

cc:laboratoare:03 [2020/10/26 09:13]
alexandru.hogea [Instalare]
cc:laboratoare:03 [2022/10/23 19:39] (current)
florin.mihalache
Line 1: Line 1:
-===== Laboratorul 03. Docker Compose ​=====+====== Laboratorul 03 - Kubernetes deployments,​ rollouts și servicii ​=====
 +===== Deployments ===== 
 +Despre deployments ați învățat în cadrul laboratorului trecut. În cadrul laboratorului curent veți învăța despre upgrade-ul unui deployment și despre rollback-ul unui deployment.
  
-==== Introducere ====+Pentru un fișier de deployment putem să avem următorul template: 
 +<code yaml> 
 +apiVersion: apps/v1 
 +kind: Deployment 
 +metadata: 
 +  name: <> #name  
 +spec: 
 +  replicas: <x> #no of replicas 
 +  selector: 
 +    matchLabels:​ 
 +      key: value 
 +  template: 
 +    metadata: 
 +      labels: 
 +        pod-key: pod-value 
 +    spec: 
 +      containers:​ 
 +      - name: nginx 
 +        image: <> #you can use the nginx image 
 +</​code>​
  
-[[https://​docs.docker.com/​compose/​|Docker Compose]] este un utilitar creat de catre Dockerfolosit pentru **centralizarea configurarii** ​de rulare a containerelor in maniera **declarativa**.+Dorim, de exemplu, să facem următorul deploymentul,​ cu numele ''​cc-dep-01'',​ cu 4 replici și cu un label-ul ''​app:​ lab3cc'':​ 
 +<code yaml> 
 +kind: Deployment 
 +metadata: 
 +  name: cc-dep-01 
 +spec: 
 +  replicas: 10 
 +  selector: 
 +    matchLabels:​ 
 +      app: lab3cc 
 +  template: 
 +    metadata: 
 +      labels: 
 +        app: lab3cc 
 +    spec: 
 +      containers:​ 
 +      - name: nginx 
 +        image: nginx 
 +</​code>​
  
-In mod clasic, pentru rularea unor containere, este nevoie sa rulati comanda aferenta ​de rulare (__docker run__) si sa dati toti parametri necesariAcest proces poate deveni anevoios daca este repetat pentru pornirea mai multor containere.+<​note>​ 
 +Tasks: 
 +  * Creați deployment-ul cum știm deja: ''​kubectl apply -f lab3dep.yaml''​ 
 +  * Afișați obiectele ​de tipul ReplicaSet și Deployment pe care le aveți momentan în cluster. 
 +  * Afișați pod-urile care sunt up and running. 
 +</​note>​
  
-Un mod muncitoresc ​de a "​salva"​ configuratia de rulare este sa va creati scripturiProblema in rularea mai multor scripturi ​este pierderea consistentei in configurare (Ce container la ce retea se conecteazacu cine comunica, etc...).+Ajungem la partea ​de upgrade ​unui deploymentUn use-case relevant ​este unul des întâlnit în ziua de azipresupunem că a apărut o nouă versiune a aplicației și vrem să folosim versiunea curentă.
  
-Utilizand fisiere de configurare **.yml**, Docker Compose centralizeaza procesul de configurare intr-o maniera naturaladeclarativa.+În cazul deployment-ului nostru, vrem să folosim ​nouă versiune a webserver-ului aplicației noastredeci vrem să folosim o versiune mai nouă a nginx.
  
-Mai mult de atat, formatul pentru fisiere compose este utilizat si in cadrul ​**Docker Swarm**, orchestratorul creat de Docker pentru gestiunea serviciilor Docker. Despre Swarm vom discuta ​la laboratorul urmator.+<​note>​ 
 +Tasks: 
 +  ​Creați un nou fișier plecând ​de la fișierul creat anterior. Schimbați imaginea folosită în ''​nginx:​1.21.1''​.  
 +  * Aplicați modificările făcute: ''​kubectl apply -f mynewdep.yaml''​ 
 +  * Listați pod-urile din clusterCe se întâmplă?​ 
 +</​note>​
  
-<note tip>O sa observati de mai multe ori ca folosim terminologia servicii/​containere. Asta pentru ca Docker Swarm lucreaza cu servicii, in timp ce Docker Compose cu containere. Ne referim la amandoua in acelasi context deoarece configuratia, este, in proportie ​de 90%identicaindiferent de utilizarea Swarm sau Compose</​note>​+În primul rândun aspect foarte important ​este că pod-urile sunt upgraded secvențialnu toate odată, după ce un număr ​de noi pod-uri au fost createurmează să se șteargă cele vechiprocedeul se repetă până când toate pod-urile au fost updated.
  
-==== Instalare ====+<note tip>Așa cum puteți observa, numele unui pod are următoarea structură: ''​numeDeployment-identificatorReplicaSet-identificatorPod''</​note>​ 
 +<​note>​Task:​ 
 +  * Listați toate obiectele de tip ReplicaSet din cluster: ''​kubectl get rs # am folosit o prescurtare''​ 
 +</​note>​
  
-  * Windows/​Mac ​vine direct instalat ​cu docker-ul +Din câte puteți observa, avem două ReplicaSets care corespund aceluiași deployment. Motivul este pentru că în momentul în care am făcut upgrade deployment-ului, a fost creat un nou ReplicaSet ​cu nouă imagine de nginx folosităObiectul ReplicaSet vechi este și el păstrat pentru că există mereu o șansă să vrem să ne întoarcem la versiunea anterioară a aplicației.
-  * Linux - [[https://​docs.docker.com/​compose/​install/​|instalare conform ghidului oficial]] +
-==== Elemente Cheie ====+
  
-=== Formatul unui fisier YAML ===+===== Rollouts ​===== 
 +Am realizat că avem o greșeală în deploymentul nostru, pentru a reveni la versiunea anterioară folosim următoarea comandă: ''​kubectl rollout history deployment <​depname>''​
  
-Fisierele YAML (Yet Another Markup Language), ​de obicei, sunt folosite pentru a scrie configurari declarativFormatul este unul foarte usor de inteles si folosit:+<​note>​ 
 +Tasks: 
 +  * Analizați din nou obiectele ​de ReplicaSet și Pod. 
 +  * Scalați deployment-ul la 2 replici: ''​kubectl scale deployment <​depname>​ –replicas=2''​ 
 +  * Verificați numărul ​de pod-uri. 
 +  * Scalați din nou la 5 replici''​kubectl scale deployment <​depname>​ –replicas=5''​ 
 +</​note>​
  
-  * elemente ​de tip cheie:valoare +Pentru a modifica imaginea folosită ​de un deployment, avem următoarea comandă''​kubectl set image deployment <​depname>​ nginx=nginx:​failTest''​ 
-  * aliniatele **identate** reprezinta proprietati copii ale pargrafelor anterioare +În comanda de mai sus am introdus intenționat o imagine care nu există. Dacă urmariți comportamentul deployment-ului,​ o să observați că noile pod-uri nu vor fi create deoarece nu poate fi gasită imaginea specificată.
-  * listele se delimiteaza prin +
  
-=== Exemplu Fisier docker-compose.yml === +Am ajuns în situația în care vrem să ne întoarcem la o versiunea anterioarăPentru a vedea istoricul modificărilor făcute pe acest deployment, avem următoarea comandă: ''​kubectl rollout history deoployment ​<depname>''​
-<code yaml> +
-# docker-compose.yml +
-version: ​'3.8' +
-services: +
-  api: +
-    build: . # construieste imaginea dintr-un Dockerfile +
-    image: nume-imagine-registru:​versiune # foloseste o imagine de pe registrul curent +
-    environment:​ +
-      NODE_ENV: development +
-      VARIABILA_DE_MEDIU:​ valoare +
-    ports: +
-     - "​5000:​80"​ +
-    networks: +
-     - network-laborator-3 +
-  postgres: +
-    image: postgres:​12 +
-    environment:​ +
-      PGPASSWORD_FILE:​ /​run/​secrets/​parola-mea-ultra-secreta +
-    volumes: +
-      - volum-laborator-3:/​var/​lib/​postgresql/​data +
-      - ./​scripturi-initializare/​init-db.sql:/​docker-entrypoint-init.d/​init-db.sql +
-    networks: +
-     - network-laborator-3+
  
-volumes+Observați ca avem mai multe "​revizii"​ ale deployment-ului nostru. Pentru a ne întoarce la o anumită revizie, folosim următoarea comandă
-  volum-laborator-3:+''​kubectl rollout history deployment cc-dep-01 --revision=2''​
  
-networks: +===== Servicii ===== 
-  ​network-laborator-3:+Care sunt serviciile cunoscute în Kubernetes? Care este diferența între ele? Ne interesează în mod special diferența dintre ClusterIP și NodePort. 
 +Pentru a instanția o aplicație simplă care conține o bază de date și un API care face interogări,​ avem nevoie, asa cum intuiți, de 2 pod-uri. 
 +Pentru a porni un pod de Postgres, avem nevoie de a seta variabilele ''​POSTGRES_USER''​ și ''​POSTGRES_PASSWORD'',​ dar și numele bazei de date pe care urmează să o folosim ​''​POSTGRES_DB''​. Avem mai multe moduri prin care putem face asta, cel mai simplu exemplu (și cel mai nesigur) este cu un ConfigMap.
  
-secrets+Exemplu
-  ​parola-mea-ultra-secreta+<code yaml> 
-    file'​./​parola-mea-nu-atat-de-secreta.txt'​+apiVersionv1 
 +kindConfigMap 
 +metadata: 
 + name: db-config-map 
 +data: 
 + ​POSTGRES_USER:​ "​admin"​ 
 + ​POSTGRES_PASSWORD:​ "​admin"​ 
 + ​POSTGRES_DB:​ "​books"​
 </​code>​ </​code>​
  
-=== Version ===+Când o să creăm pod-ul / deployment-ul,​ o să folosim acest ConfigMap pentru a injecta variabilele de mediu. Pentru a avea datele persistente,​ avem nevoie, asa cum știm din labul trecut, de un PV și de un PVC: 
 +  * ''​PersistentVolume''​ (PV): 
 +<code yaml> 
 +apiVersion: v1 
 +kind: PersistentVolume 
 +metadata: 
 + name: postgres-pv-volume 
 + ​labels:​ 
 +  type: local 
 +spec: 
 + ​storageClassName:​ manual 
 + ​capacity:​ 
 +   ​storage:​ 1Gi 
 + ​accessModes:​ 
 +   - ReadWriteOnce 
 + ​hostPath:​ 
 +   path: "/​mnt/​data"​ 
 +</​code>​ 
 +  * ''​PersistentVolumeClaim''​ (PVC): 
 +<code yaml> 
 +apiVersion: v1 
 +kind: PersistentVolumeClaim 
 +metadata: 
 + name: posgress-pvc 
 +spec: 
 + ​storageClassName:​ manual 
 + ​accessModes:​ 
 +   - ReadWriteOnce 
 + ​resources:​ 
 +   ​requests:​ 
 +     ​storage:​ 1Gi 
 +</​code>​
  
-**Version** descrie ce set de functionalitati va fi incarcat la rularea utilitarului Docker Compose +Pentru a crea pod-ul ​de Postgres, avem următorul ​fișier: 
-<note important>​Este **obligatoriu** sa treceti versiunea in orice fisier docker-compose.yml </note+<code yaml
- +apiVersion: v1 
-=== Services === +kind: Pod 
- +metadata: 
-**Services** descrie serviciile/​containerele ce vor rula dupa ce configuratia este pornita de catre compose. Fiecare serviciu reprezinta un container care va avea **numele** si configuratia serviciului. In exemplul de mai sus, containerele se vor numi //api// si //postgres//. + name: db-pod 
- + ​labels:​ 
-  * **build** - mentioneaza folderul unde se afla Dockerfile-ul de la care se va efectua construirea containerului +   ​app: ​postgres 
-  * **image** ​mentioneaza numele imaginii folosit pentru rularea containerului +spec: 
-<note important>​build si image sunt optiuni disjuncte</​note>​ + containers: 
-  * **ports** - lista de intrari de tipul **PORT_HOST:PORT_SERVICIU** unde este realizata maparea de porturi +   ​- image: postgres:​10.4 
-  * **volumes** - lista de intrari de tipul **VOLUM_HOST:CALE_SERVICIU** unde sunt precizate maparile de volume. Aceleasi reguli care se aplica la rularea clasica sunt mentinute si aici. VOLUM_HOST poate fi **named volume** sau **bind mount**. +     namepostgres 
-  * **networks** ​lista de retele din care face parte serviciul/​containerul +     volumeMounts
-  * **secrets** - lista de secrete ce vor fi folosite in cadrul serviciului/​containerului +     ​name: myvol 
-<note tip>​Secretele trebuie trecute si in cadrul variabilelor de mediu, conform documentatiei. De exemplu, [[https://hub.docker.com/​_/​postgres|in configuratia Postgres]] trebuie trecute in variabile de mediu speciale, sufixate cu **_FILE**, impreuna cu calea lor completa (adica /​run/​secrets/​NUME_SECRET)</​note>​ +       mountPath: /etc/config 
-  * **environment** ​obiect de intrari de tipul **VARIABILA_MEDIU_SERVICIU:valoare** care injecteaza variabilele de mediu specificate la rularea serviciului/​containerului +     ports: 
- +       containerPort5432 
-=== Volumes === +     envFrom: 
- +       - configMapRef:​ 
-**Volumes** descrie //NAMED VOLUMES// utilizate in cadrul configuratiei. +           name: db-config-map 
-<note tip>​Observati ca **volumes**, ca si proprietate top level, este scrisa pe acelasi nivel de identatie ca **services**. Nu trebuie confundata cu proprietatea copil **volumes** din interiorul configuratiei serviciilor</​note>​ + ​volumes: 
- +   - name: myvol 
-Volumele se trec sub forma de obiecte. Daca nu se doreste schimbarea configuratiei default, valoarea este un camp gol. In laboratorul urmator vom lucra cu volume NFS, unde va trebui oferita o configuratie custom. +     persistentVolumeClaim:​ 
- +       claimName: posgress-pvc 
-=== Networks === +</code>
- +
-**Networks** descrie //​retelele//​ utilizate in cadrul configuratiei. +
-<note tip>​Observati ca **networks**,​ ca si proprietate top level, este scrisa pe acelasi nivel de identatie ca **services**. Nu trebuie confundata cu proprietatea copil **networks** din interiorul configuratiei serviciilor</note>+
  
-Retelele se trec sub forma de obiecte. Daca nu se doreste schimbarea configuratiei default, valoarea este un camp gol. Un exemplu ​de configuratie ​de retea este atunci cand se foloseste o retea care deja exista, de exemplu:+Pentru ca podul nostru ​de API să comunice cu cel de baze de datetrebuie să expunem pod-ul de Postgres printr-un serviciu ​de tip ClusterIP:
 <code yaml> <code yaml>
-networks+apiVersionv1 
-  ​reteaua-mea-care-de-fapt-exista+kindService 
-    ​externaltrue +metadata
-    name: reteaua-originala-care-deja-exista+ ​name: ​postgres 
 + ​labels:​ 
 +   app: postgres 
 +spec: 
 + type: ClusterIP 
 + ​ports:​ 
 +  ​port: 5432 
 + ​selector:​ 
 +  app: postgres
 </​code>​ </​code>​
  
-In cazul de mai sus, //​reteaua-mea-care-de-fapt-exista//​ este doar o "​redenumire" ​unei retele deja existente. +Pentru ​porni podul de APIavem următorul fișier: 
- +<​code ​yaml
-=== Secrets === +apiVersion: v1 
- +kind: Pod 
-**Secrets** descrie //​secretele//​ utilizate in cadrul configuratiei. +metadata: 
-<note tip>​Observati ca **secrets**,​ ca si proprietate top level, este scrisa pe acelasi nivel de identatie ca **services**. Nu trebuie confundata cu proprietatea copil **secrets** din interiorul configuratiei serviciilor</​note>​ + name: apipod 
- + labels: 
-Secretele retin informatii sensibile intr-o maniera securizatacriptata, in cadrul Swarm, despre care vom vorbi la laboratorul urmator. In compose nu sunt securizate, insa au fost introduse pentru a usura tranzitia catre Swarm. +   app: api 
- +spec: 
-In cadrul compose, secretele pot proveni doar din fisiere externe. Fisierele externe sunt mentionate pentru fiecare secret in parte. + containers: 
- +   ​image: andreidamian/​lab3cc:​first 
-==== Comenzi Docker Compose ==== +     name: apicontainer 
- +     env: 
-Comenzile pentru interactiunea cu docker-compose seamana, ca si sintaxa, cu cele clasice de docker. Mai jos va oferim cele mai utilizate comenzi. +      name: PGUSER 
- +        ​value:​ "​admin"​ 
-<​code ​bash+      name: PGPASSWORD 
-# Porneste containerele +        ​value:​ "​admin"​ 
-docker-compose start +      name: PGDATABASE 
-# Opreste containerele +        ​value:​ "​books"​ 
-docker-compose stop +      name: PGHOST 
-# Pune in stare de pauza conainerele unui serviciu. Diferenta fata de stop e ca la procesul din container se trimite SIGPAUSE +        ​value:​ "​postgres"​ 
-docker-compose pause +      name: PGPORT 
-# Scoate din stare de pauza containerele +        ​value:​ "​5432"​
-docker-compose unpause +
-# Listeaza containerele active +
-docker-compose ps +
-# Face build, recreaza, porneste si ataseaza containere la un serviciu +
-docker-compose up +
-# Serviciile ruleaza in background detasat de terminalul care le-a initializat +
-docker-compose up -d +
-# Creaza imaginile inainte de pornire unde a fost declarata calea pentru un Dockerfile pentru un container +
-docker-compose up --build +
-# Se foloseste fisierul de docker-compose specificat in loc de cel implicit +
-docker-compose -f my-docker-compose.yml up +
-# Opreste containerele si le sterge pe acestea impreuna cu retelele, volumele si imagine create la up. +
-docker-compose down +
-# Sterge toate containerele oprite (se poate specifica la sfarsit si numele containerului care trebuie sters) +
-docker-compose rm +
-# Cu -s se opresc toate containerele si cu -v se sterg si volumele anonime atasate +
-docker-compose rm -s -v +
 </​code>​ </​code>​
  
-==== Composing ​Composition of Compose Files ====+Dupa ce și acest pod este up and running, trebuie să îl expunem printr-un serviciu de tip NodePort pentru ​putea comunica cu el de pe mașina noastră. Facem asta prin următoarea comandă (se poate face și printr-un fișier YAML): ''​kubectl expose pod apipod --port 80 --type=NodePort''​
  
-Docker Compose este recomandat sa fie utilizat **doar** in etapa de **dezvoltare locala**. Pentru medii de testare sau productie se foloseste Docker Swarm sau alte orchestratoare,​ precum Kubernetes+Apoi verificați portul asignat ​de noul serviciu (31598): 
 +{{ :​cc:​laboratoare:​image5.png |}}
  
-Tocmai ​pentru ​ca este folosit in etapa de dezvoltare, acesta are un mecanism de combina mai multe fisiere compose pentru a crea mai multe configuratii de rulare, fara a replica partile comune+Folosiți Postman ​pentru a accesa API-ul deployed: 
 +{{ :​cc:​laboratoare:​image2.png?600 |}}
  
-Pentru a rula o configuratie de compose bazata pe mai multe fisiere, este nevoie sa rulati ​+Avem următoarea eroare: 
 +{{ :​cc:​laboratoare:​image1.png?​500 |}}
  
-<code bash> +Avem această eroare pentru că baza de date nu a fost configurată. Pentru a configura baza de date avem mai multe variante, cea mai simpla (dar și cea mai ineficientă) este următoarea:​ 
-docker-compose ​-f fisier-compose-1.yml -f fisier-compose-2.yml up --build+  * obtineți un shell pe podul de Postgres: ''​kubectl exec -it db-pod bash''​ 
 +  * ne conectam la baza de date books cu username și parola definite în ConfigMap-ul pentru acest pod (admin/​admin):​ ''​psql ​-h localhost ​-U admin  ​--password books''​ 
 +{{ :​cc:​laboratoare:​image4.png?500 |}} 
 +  * rulați următorul script pentru a inițializa baza de date: 
 +<code sql> 
 +CREATE TABLE IF NOT EXISTS books ( 
 +    id uuid PRIMARY KEY NOT NULL , 
 +    title varchar NOT NULL, 
 +    author varchar NOT NULL 
 +);
 </​code>​ </​code>​
  
-Informatiile ​din //​fisier-compose-2.yml//​ vor suprascrie/​completa informatiile ​din fisier-compose-1.yml. Acest lucru este util pentru a testa diverse configuratii rapid+Incercați ​din nou comanda GET din PostmanInserați ceva în baza de date: 
- +{{ :cc:laboratoare:​image3.png?​500 ​|}}
-==== Exercitii ==== +
- +
-Resursele practice pentru acest laborator le puteti accesa pe [[https://gitlab.com/​mobycloudcomputing/​laborator-3.git|repo-ul oficial al materiei Cloud Computing]] +
- +
-Exercitiile urmaresc intelegerea conceptelor de compose. Sunt optionale, insa va recomandam sa le lucrati pentru a dobandi deprindere de lucru.+
  
-  ​Scrieti configuratia containerelor ​de la laboratorul 2 in maniera compose +Stergeți pod-ul de Postgres și incercați să faceți GETAr trebui să primiți următoarea eroare: 
-  - Introduceti un al 3-lea serviciu, **adminer** care face parte dintr-o **retea comuna** cu baza de date, alta decat cea pe care baza de date o foloseste la comunicarea cu api-ul +{{ :​cc:​laboratoare:​image6.png?500 |}}
-  - Spargeti configuratia in 2 fisiere, unul de baza, si unul care sa foloseasca secrete. Rulati cele 2 configuratii impreuna+
-<note important>​Dupa ce veti sparge configuratia initiala in 2, o sa observati ca trebuie sa renuntati la niste variabile de mediu ale bazei de date din fisierul de baza, si anume POSTGRES_USER si POSTGRES_PASSWORD. In configuratia cu secrete, username-ul si parola vor fi setate prin variabilele de mediu POSTGRES_USER_FILE si POSTGRES_PASSWORD_FILE,​ care sunt disjuncte cu cele mentionate anterior.</​note>​+
  
-<note tip>​Pentru a introduce secrete si in API, trebuie sa setati variabila ​de mediu NODE_ENV = staging si sa setati variabilele de mediu PGPASSWORD_SECRET si PGUSER_SECRET **doar cu numele secretului**,​ nu cu toata calea //​run/​secrets/​NUME_SECRET//​ precum in cazul bazei de date</​note>​+Reporniți pod-ul ​de baza de date și executați un GET: 
 +{{ :​cc:​laboratoare:​image7.png?​500 |}}
cc/laboratoare/03.1603696413.txt.gz · Last modified: 2020/10/26 09:13 by alexandru.hogea
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0