Differences

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

Link to this comparison view

cc:laboratoare:03 [2020/10/25 20:16]
alexandru.hogea [Introducere]
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 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 primitiv ​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.
  
-==== Elemente Cheie ====+<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>​
  
-=== Formatul unui fisier YAML ===+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.
  
-Fisierele YAML (Yet Another Markup Language)de obicei, sunt folosite ​pentru a scrie configurari declarativ. Formatul este unul foarte usor de inteles si folosit:+===== 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>''​
  
-  ​elemente ​de tip cheie:​valoare +<​note>​ 
-  * aliniatele **identate** reprezinta proprietati copii ale pargrafelor anterioare +Tasks: 
-  * listele se delimiteaza prin +  ​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>​
  
-=== Exemplu Docker Compose === +Pentru a modifica imaginea folosită de un deployment, avem următoarea comandă: ''​kubectl set image deployment ​<depnamenginx=nginx:failTest''​ 
-<code yaml> +Î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ă.
-# 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: +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>''​
-  volum-laborator-3:+
  
-networks+Observați ca avem mai multe "​revizii"​ ale deployment-ului nostru. Pentru a ne întoarce la o anumită revizie, folosim următoarea comandă
-  ​network-laborator-3:+''​kubectl rollout history deployment cc-dep-01 --revision=2''​
  
-secrets: +===== Servicii ===== 
-  ​parola-mea-ultra-secreta+Care sunt serviciile cunoscute în Kubernetes? Care este diferența între ele? Ne interesează în mod special diferența dintre ClusterIP și NodePort. 
-    file'​./​parola-mea-nu-atat-de-secreta.txt'​+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> 
 +apiVersionv1 
 +kind: ConfigMap 
 +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 
 +kind: Pod 
 +metadata: 
 + name: db-pod 
 + ​labels:​ 
 +   app: postgres 
 +spec: 
 + ​containers:​ 
 +   - image: postgres:10.
 +     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>
  
-=== Services ===+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:​ 
 +<code yaml> 
 +apiVersion: v1 
 +kind: Service 
 +metadata: 
 + name: postgres 
 + ​labels:​ 
 +   app: postgres 
 +spec: 
 + type: ClusterIP 
 + ​ports:​ 
 +  - port: 5432 
 + ​selector:​ 
 +  app: postgres 
 +</​code>​
  
-**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//.+Pentru a porni podul de APIavem următorul fișier: 
 +<code yaml> 
 +apiVersion: v1 
 +kind: Pod 
 +metadata: 
 + name: apipod 
 + ​labels:​ 
 +   ​app: ​api 
 +spec: 
 + ​containers:​ 
 +   - image: andreidamian/lab3cc:​first 
 +     name: apicontainer 
 +     ​env:​ 
 +      - name: PGUSER 
 +        value: "​admin"​ 
 +      - name: PGPASSWORD 
 +        value: "​admin"​ 
 +      - name: PGDATABASE 
 +        value: "​books"​ 
 +      - name: PGHOST 
 +        value: "postgres
 +      - name: PGPORT 
 +        value: "​5432"​ 
 +</code>
  
-  * **build** - mentioneaza folderul unde se afla Dockerfile-ul de la care se va efectua construirea containerului +Dupa ce și acest pod este up and running, trebuie să îl expunem printr-un serviciu ​de tip NodePort ​pentru ​a 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''​
-  * **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 volumeAceleasi 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 ===+Apoi verificați portul asignat de noul serviciu (31598): 
 +{{ :​cc:​laboratoare:​image5.png |}}
  
-**Volumes** descrie //NAMED VOLUMES// utilizate in cadrul configuratiei. +Folosiți Postman pentru a accesa API-ul deployed: 
-<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>​+{{ :​cc:​laboratoare:​image2.png?600 |}}
  
-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.+Avem următoarea eroare: 
 +{{ :​cc:​laboratoare:​image1.png?500 |}}
  
-=== Networks === +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:​ 
- +  obtineți un shell pe podul de Postgres: ''​kubectl exec -it db-pod bash''​ 
-**Networks** descrie //​retelele//​ utilizate in cadrul configuratiei+  ​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''​ 
-<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> +{{ :​cc:​laboratoare:​image4.png?​500 |}} 
- +  * rulați următorul script pentru a inițializa baza de date
-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 ​sql
-<​code ​yaml+CREATE TABLE IF NOT EXISTS books ( 
-networks: +    id uuid PRIMARY KEY NOT NULL , 
-  ​reteaua-mea-care-de-fapt-exista:​ +    ​title varchar NOT NULL, 
-    ​external: true +    ​author varchar NOT NULL 
-    ​name: reteaua-originala-care-deja-exista+);
 </​code>​ </​code>​
  
-In cazul de mai sus, //​reteaua-mea-care-de-fapt-exista//​ este doar o "​redenumire"​ a unei retele deja existente. +Incercați din nou comanda GET din Postman. Inserați ceva în baza de date: 
- +{{ :​cc:​laboratoare:​image3.png?500 |}}
-=== Secrets === +
- +
-**Secrets** descrie //​secretele//​ utilizate in cadrul configuratiei. +
-<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>​+
  
-Secretele retin informatii sensibile intr-o maniera securizata, criptata, in cadrul Swarm, despre care vom vorbi la laboratorul urmatorIn compose nu sunt securizate, insa au fost introduse pentru a usura tranzitia catre Swarm.+Stergeți pod-ul de Postgres și incercați să faceți GETAr trebui să primiți următoarea eroare: 
 +{{ :​cc:​laboratoare:​image6.png?500 |}}
  
-In cadrul compose, secretele pot proveni doar din fisiere externe. Fisierele externe sunt mentionate pentru fiecare secret in parte.+Reporniți pod-ul de baza de date și executați un GET: 
 +{{ :​cc:​laboratoare:​image7.png?500 |}}
cc/laboratoare/03.1603649768.txt.gz · Last modified: 2020/10/25 20:16 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