Differences

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

Link to this comparison view

cc:laboratoare:03 [2021/10/08 12:20]
radu.ciobanu
cc:laboratoare:03 [2022/10/23 19:39] (current)
florin.mihalache
Line 1: Line 1:
-===== Laboratorul 03 =====+====== 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.
  
-<hidden>+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>
  
-==== Introducere ====+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>​
  
-[[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ă**.+<​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>​
  
-În mod clasicpentru 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.+Ajungem la partea de upgrade a unui deployment. Un 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ă.
  
-Un mod muncitoresc de „salva” configurația de rulare este să vă creați 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 cazul deployment-ului nostru, vrem să folosim o nouă versiune ​webserver-ului aplicației noastre, deci vrem să folosim o versiune ​mai nouă a nginx.
  
-Utilizând ​fișiere de configurare **.yml**, Docker Compose centralizează procesul de configurare într-o manieră naturală, declarativă.+<​note>​ 
 +Tasks: 
 +  * Creați un nou fișier plecând ​de la fișierul creat anteriorSchimbaț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>​
  
-Mai mult decât atâtformatul pentru fișierele Compose ​este utilizat și în cadrul **Docker Swarm**orchestratorul creat de Docker pentru gestiunea serviciilor Dockerdespre care vom discuta la laboratorul următor.+Î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 vechi, procedeul se repetă până când toate pod-urile au fost updated.
  
-<note tip>Veți observa ​de mai multe ori că folosim terminologia servicii/containere. Acest lucru se întâmplă pentru că Docker Swarm lucrează cu servicii, în timp ce Docker Compose cu containere. Ne referim la amandouă în acelasi context deoarece configurația este, în proporție de 90%, identică, indiferent ​de utilizarea Swarm sau Compose.</​note>​+<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>​
  
-==== Instalare ====+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.
  
-  * Windows/Mac - vine direct instalat cu Docker Toolbox +===== Rollouts ===== 
-  * Linux - [[https://​docs.docker.com/​compose/​install/​|instalare conform ghidului oficial]].+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>''​
  
-==== Elemente Cheie ====+<​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>​
  
-=== Formatul unui fisier YAML ===+Pentru a modifica imaginea folosită de un deployment, avem următoarea comandă: ''​kubectl set image deployment <​depname>​ nginx=nginx:​failTest''​ 
 +Î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ă.
  
-Fișierele YAML (Yet Another Markup Language), de obicei, sunt folosite pentru a scrie configurari declarativ. Formatul este unul foarte ușor de înțeles și folosit:+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>''​
  
-  * elemente de tip cheie:​valoare +Observați ca avem mai multe "​revizii" ​ale deployment-ului nostru. Pentru a ne întoarce la o anumită revizie, folosim următoarea comandă: 
-  * aliniatele **indentate** reprezintă proprietăți copii ale paragrafelor anterioare +''​kubectl rollout history deployment cc-dep-01 --revision=2''​
-  * listele se delimiteaza prin „-+
  
-=== Exemplu de fișier Docker Compose ​===+===== Servicii ​===== 
 +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. 
 + 
 +Exemplu:
 <code yaml> <code yaml>
-# docker-compose.yml +apiVersionv1 
-version'​3.8'​ +kindConfigMap 
-services+metadata
-  api+ namedb-config-map 
-    build. # construieste imaginea dintr-un Dockerfile +data
-    image: nume-imagine-registru:​versiune # foloseste o imagine de pe registrul curent + POSTGRES_USER"​admin"​ 
-    ​environment+ POSTGRES_PASSWORD"​admin"​ 
-      ​NODE_ENVdevelopment + POSTGRES_DB: "books
-      ​VARIABILA_DE_MEDIUvaloare +</code>
-    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+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
-  ​volum-laborator-3:+  ​* ''​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>​
  
-networks+Pentru a crea pod-ul de Postgres, avem următorul fișier
-  ​network-laborator-3+<code yaml> 
- +apiVersion: v1 
-secrets+kind: Pod 
-  ​parola-mea-ultra-secreta+metadata: 
-    file'./parola-mea-nu-atat-de-secreta.txt'​+ name: db-pod 
 + ​labels:​ 
 +   app: postgres 
 +spec: 
 + ​containers:​ 
 +   imagepostgres:​10.4 
 +     name: postgres 
 +     volumeMounts
 +     ​namemyvol 
 +       mountPath: /etc/​config 
 +     ​ports:​ 
 +       containerPort:​ 5432 
 +     ​envFrom:​ 
 +       configMapRef:​ 
 +           name: db-config-map 
 + ​volumes:​ 
 +   name: myvol 
 +     ​persistentVolumeClaim:​ 
 +       ​claimName:​ posgress-pvc
 </​code>​ </​code>​
  
-=== Version === +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:
- +
-**Version** descrie ce set de functionalitati va fi incarcat la rularea utilitarului Docker Compose +
-<note important>​Este **obligatoriu** sa treceti versiunea in orice fisier docker-compose.yml </​note>​ +
- +
-=== Services === +
- +
-**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 suscontainerele se vor numi //api// si //​postgres//​. +
- +
-  * **build** - mentioneaza folderul unde se afla Dockerfile-ul de la care se va efectua construirea containerului +
-  * **image** - mentioneaza numele imaginii folosit pentru rularea containerului +
-<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 === +
- +
-**Volumes** descrie //NAMED VOLUMES// utilizate in cadrul configuratiei. +
-<​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>​ +
- +
-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. +
- +
-=== Networks === +
- +
-**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:+
 <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 +
-  - 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 +
-  - Spargeti configuratia in 2 fisiere, unul de baza, si unul care sa foloseasca secreteRulati 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>​+Stergeți pod-ul ​de Postgres și incercați să faceți GET. Ar trebui să primiți următoarea eroare: 
 +{{ :​cc:​laboratoare:​image6.png?​500 |}}
  
-</​hidden>​+Reporniți pod-ul de baza de date și executați un GET: 
 +{{ :​cc:​laboratoare:​image7.png?​500 |}}
cc/laboratoare/03.1633684851.txt.gz · Last modified: 2021/10/08 12:20 by radu.ciobanu
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