Differences

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

Link to this comparison view

cc:laboratoare:03 [2020/10/25 20:37]
alexandru.hogea [Exercitii]
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 creat de catre Dockerfolosit pentru **centralizarea configurarii** ​de rulare a containerelor in maniera **declarativa**. +Dorim, de exemplusă facem următorul deploymentul, cu numele ''​cc-dep-01''​, cu 4 replici și cu un label-ul ''​applab3cc''​:
- +
-In mod clasicpentru rularea unor containere, este nevoie sa rulati comanda aferenta de rulare (__docker run__) si sa dati toti parametri necesari. Acest proces poate deveni anevoios daca este repetat pentru pornirea mai multor containere. +
- +
-Un mod primitiv de a "​salva"​ configuratia de rulare este sa va creati scripturi. Problema in rularea mai multor scripturi este pierderea consistentei in configurare (Ce container la ce retea se conecteaza, cu cine comunica, etc...). +
- +
-Utilizand fisiere de configurare **.yml**, Docker Compose centralizeaza procesul de configurare intr-o maniera naturaladeclarativa. +
- +
-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 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%, identica, indiferent de utilizarea Swarm sau Compose</​note>​ +
- +
-==== Elemente Cheie ==== +
- +
-=== Formatul unui fisier YAML === +
- +
-Fisierele YAML (Yet Another Markup Language), de obicei, sunt folosite pentru a scrie configurari declarativ. Formatul este unul foarte usor de inteles si folosit: +
- +
-  * elemente de tip cheie:valoare +
-  * aliniatele **identate** reprezinta proprietati copii ale pargrafelor anterioare +
-  * listele se delimiteaza prin -  +
- +
-=== Exemplu Fisier docker-compose.yml ===+
 <code yaml> <code yaml>
-# docker-compose.yml +kindDeployment 
-version'​3.8'​ +metadata
-services+  ​namecc-dep-01 
-  ​api: +spec
-    build: . # construieste imaginea dintr-un Dockerfile +  ​replicas10 
-    image: nume-imagine-registru:​versiune # foloseste o imagine de pe registrul curent +  ​selector
-    ​environment+    ​matchLabels
-      ​NODE_ENVdevelopment +      applab3cc 
-      ​VARIABILA_DE_MEDIUvaloare +  ​template
-    ​ports+    ​metadata
-     - "5000:80" +      ​labels
-    networks: +        applab3cc 
-     - network-laborator-3 +    ​spec
-  ​postgres+      ​containers
-    ​imagepostgres:12 +      - namenginx 
-    ​environment+        imagenginx 
-      ​PGPASSWORD_FILE/​run/​secrets/​parola-mea-ultra-secreta +</​code>​
-    ​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+<​note>​ 
-  ​volum-laborator-3:+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>​
  
-networks: +Ajungem la partea de upgrade a unui deployment. Un use-case relevant este unul des întâlnit în ziua de azi, presupunem că a apărut o nouă versiune a aplicației și vrem să folosim versiunea curentă.
-  network-laborator-3:​+
  
-secrets: +În cazul deployment-ului nostru, vrem să folosim o nouă versiune a webserver-ului aplicației noastre, deci vrem să folosim o versiune mai nouă a nginx.
-  parola-mea-ultra-secreta:​ +
-    file: '​./​parola-mea-nu-atat-de-secreta.txt' +
-</​code>​+
  
-=== Version ===+<​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 cluster. Ce se întâmplă?​ 
 +</​note>​
  
-**Version** descrie ​ce set de functionalitati va fi incarcat la rularea utilitarului Docker Compose +În primul rând, un aspect foarte important este că pod-urile sunt upgraded secvențial,​ nu toate odată, după ce un număr ​de noi pod-uri au fost create, urmează să se șteargă cele vechi, procedeul se repetă până când toate pod-urile au fost updated.
-<note important>​Este **obligatoriu** sa treceti versiunea in orice fisier docker-compose.yml </​note>​+
  
-=== Services ===+<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>​
  
-**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 serviciuluiIn exemplul de mai suscontainerele se vor numi //api// si //​postgres//​.+Din câte puteți observa, avem două ReplicaSets ​care corespund aceluiași deploymentMotivul este pentru că în momentul în care am făcut upgrade deployment-uluia 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.
  
-  * **build** - mentioneaza folderul unde se afla Dockerfile-ul de la care se va efectua construirea containerului +===== Rollouts ===== 
-  * **image** - mentioneaza numele imaginii folosit ​pentru ​rularea containerului +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>''​
-<note important>​build si image sunt optiuni disjuncte</​note>​ +
-  * **ports** - lista de intrari de tipul **PORT_HOST:​PORT_SERVICIU** unde este realizata maparea de porturi +
-  * **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**. +
-  * **networks** - lista de retele din care face parte serviciul/​containerul +
-  * **secrets** - lista de secrete ce vor fi folosite in cadrul serviciului/​containerului +
-<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> +
-  * **environment** - obiect de intrari de tipul **VARIABILA_MEDIU_SERVICIU:​valoare** care injecteaza variabilele de mediu specificate la rularea serviciului/​containerului+
  
-=== Volumes ===+<​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>​
  
-**Volumes** descrie //NAMED VOLUMES// utilizate in cadrul configuratiei. +Pentru a modifica imaginea folosită de un deployment, avem următoarea comandă: ''​kubectl set image deployment ​<depnamenginx=nginx:​failTest''​ 
-<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>​+Î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ă.
  
-Volumele se trec sub forma de obiecteDaca nu se doreste schimbarea configuratiei defaultvaloarea este un camp gol. In laboratorul urmator vom lucra cu volume NFS, unde va trebui oferita o configuratie custom.+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 deploymentavem următoarea comandă: ''​kubectl rollout history deoployment <​depname>''​
  
-=== Networks ===+Observați ca avem mai multe "​revizii"​ ale deployment-ului nostru. Pentru a ne întoarce la o anumită revizie, folosim următoarea comandă: 
 +''​kubectl rollout history deployment cc-dep-01 --revision=2''​
  
-**Networks** descrie //​retelele//​ utilizate in cadrul configuratiei+===== Servicii ===== 
-<note tip>​Observati ca **networks**ca si proprietate top leveleste scrisa pe acelasi nivel de identatie ca **services**Nu trebuie confundata ​cu proprietatea copil **networks** din interiorul configuratiei serviciilor</​note>​+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ăriavem 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.
  
-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:+Exemplu:
 <code yaml> <code yaml>
-networks+apiVersionv1 
-  ​reteaua-mea-care-de-fapt-exista+kindConfigMap 
-    ​externaltrue +metadata
-    name: reteaua-originala-care-deja-exista+ ​name: ​db-config-map 
 +data: 
 + ​POSTGRES_USER:​ "​admin"​ 
 + ​POSTGRES_PASSWORD:​ "​admin"​ 
 + ​POSTGRES_DB:​ "​books"​
 </​code>​ </​code>​
  
-In cazul de mai sus, //​reteaua-mea-care-de-fapt-exista// este doar o "redenumire"​ a unei retele deja existente.+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>​
  
-=== Secrets ===+Pentru a crea pod-ul de Postgres, avem următorul fișier: 
 +<code yaml> 
 +apiVersion: v1 
 +kind: Pod 
 +metadata: 
 + name: db-pod 
 + ​labels:​ 
 +   app: postgres 
 +spec: 
 + ​containers:​ 
 +   - image: postgres:​10.4 
 +     name: postgres 
 +     ​volumeMounts:​ 
 +     - name: myvol 
 +       ​mountPath:​ /​etc/​config 
 +     ​ports:​ 
 +       - containerPort:​ 5432 
 +     ​envFrom:​ 
 +       - configMapRef:​ 
 +           name: db-config-map 
 + ​volumes:​ 
 +   - name: myvol 
 +     ​persistentVolumeClaim:​ 
 +       ​claimName:​ posgress-pvc 
 +</​code>​
  
-**Secrets** descrie //​secretele//​ utilizate in cadrul configuratiei. +Pentru ​ca podul nostru ​de API să comunice ​cu cel de baze de date, trebuie să expunem pod-ul de Postgres printr-un serviciu de tip ClusterIP:​ 
-<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>+<code yaml> 
 +apiVersion: v1 
 +kind: Service 
 +metadata: 
 + name: postgres 
 + ​labels:​ 
 +   app: postgres 
 +spec: 
 + type: ClusterIP 
 + ​ports:​ 
 +  - port: 5432 
 + ​selector:​ 
 +  app: postgres 
 +</code>
  
-Secretele retin informatii sensibile intr-o maniera securizata, criptata, in cadrul Swarm, despre care vom vorbi la laboratorul urmator. In compose nu sunt securizate, insa au fost introduse pentru ​usura tranzitia catre Swarm. +Pentru ​porni podul de APIavem următorul fișier: 
- +<​code ​yaml
-In cadrul composesecretele pot proveni doar din fisiere externe. Fisierele externe sunt mentionate pentru fiecare secret in parte. +apiVersion: v1 
- +kind: Pod 
-==== Comenzi Docker Compose ==== +metadata: 
- + name: apipod 
-Comenzile pentru interactiunea cu docker-compose seamana, ca si sintaxa, cu cele clasice de docker. Mai jos va oferim cele mai utilizate comenzi. + labels: 
- +   app: api 
-<​code ​bash+spec: 
-# Porneste containerele + containers: 
-docker-compose start +   ​image: andreidamian/​lab3cc:​first 
-# Opreste containerele +     name: apicontainer 
-docker-compose stop +     env: 
-# Pune in stare de pauza conainerele unui serviciu. Diferenta fata de stop e ca la procesul din container se trimite SIGPAUSE +      name: PGUSER 
-docker-compose pause +        ​value:​ "​admin"​ 
-# Scoate din stare de pauza containerele +      name: PGPASSWORD 
-docker-compose unpause +        ​value:​ "​admin"​ 
-# Listeaza containerele active +      name: PGDATABASE 
-docker-compose ps +        ​value:​ "​books"​ 
-# Face build, recreaza, porneste si ataseaza containere la un serviciu +      name: PGHOST 
-docker-compose up +        ​value:​ "​postgres"​ 
-# Serviciile ruleaza in background detasat de terminalul care le-a initializat +      name: PGPORT 
-docker-compose up -d +        ​value:​ "​5432"​
-# 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.1603651070.txt.gz · Last modified: 2020/10/25 20:37 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