Differences

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

Link to this comparison view

cc:laboratoare:03 [2020/10/25 20:19]
alexandru.hogea [Comenzi Docker Compose]
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 Fisier docker-compose.yml === +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 datetrebuie să expunem pod-ul de Postgres printr-un serviciu de tip ClusterIP: 
- +<code yaml
-**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//​. +apiVersionv1 
- +kindService 
-  * **build** - mentioneaza folderul unde se afla Dockerfile-ul de la care se va efectua construirea containerului +metadata: 
-  * **image** ​mentioneaza numele imaginii folosit pentru rularea containerului + name: postgres 
-<note important>​build si image sunt optiuni disjuncte</​note+ labels: 
-  * **ports** - lista de intrari de tipul **PORT_HOST:PORT_SERVICIU** unde este realizata maparea de porturi +   app: postgres 
-  * **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**. +spec: 
-  * **networks** - lista de retele din care face parte serviciul/​containerul + type: ClusterIP 
-  * **secrets** - lista de secrete ce vor fi folosite in cadrul serviciului/​containerului + ​ports:​ 
-<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>​ +  - port5432 
-  ​* **environment** ​obiect de intrari de tipul **VARIABILA_MEDIU_SERVICIU:valoare** care injecteaza variabilele de mediu specificate la rularea serviciului/​containerului + selector: 
- +  app: postgres 
-=== Volumes === +</code>
- +
-**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 defaultvaloarea este un camp gol. In laboratorul urmator vom lucra cu volume NFS, unde va trebui oferita o configuratie custom. +Pentru a porni podul de APIavem următorul fișier:
- +
-=== 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+kind: Pod 
-    ​externaltrue +metadata: 
-    name: reteaua-originala-care-deja-exista+ name: apipod 
 + ​labels:​ 
 +   app: api 
 +spec: 
 + ​containers:​ 
 +   image: andreidamian/​lab3cc:​first 
 +     name: apicontainer 
 +     ​env:​ 
 +      ​name: PGUSER 
 +        value: "​admin"​ 
 +      ​name: PGPASSWORD 
 +        value: "​admin"​ 
 +      ​namePGDATABASE 
 +        value"​books"​ 
 +      ​- ​name: PGHOST 
 +        value: "​postgres"​ 
 +      ​name: PGPORT 
 +        value: "​5432"​
 </​code>​ </​code>​
  
-In cazul de mai sus//reteaua-mea-care-de-fapt-exista// este doar o "​redenumire"​ a unei retele deja existente.+Dupa ce și acest pod este up and runningtrebuie 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''​
  
-=== Secrets ===+Apoi verificați portul asignat de noul serviciu (31598): 
 +{{ :​cc:​laboratoare:​image5.png |}}
  
-**Secrets** descrie //​secretele//​ utilizate in cadrul configuratiei. +Folosiți Postman pentru a accesa API-ul deployed: 
-<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>​+{{ :​cc:​laboratoare:​image2.png?600 |}}
  
-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 a usura tranzitia catre Swarm.+Avem următoarea eroare: 
 +{{ :​cc:​laboratoare:​image1.png?500 |}}
  
-In cadrul compose, secretele pot proveni doar din fisiere externeFisierele externe sunt mentionate ​pentru ​fiecare secret in parte.+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''​ 
 +  * 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>​
  
-==== Comenzi Docker Compose ====+Incercați din nou comanda GET din Postman. Inserați ceva în baza de date: 
 +{{ :​cc:​laboratoare:​image3.png?​500 |}}
  
-Comenzile pentru interactiunea cu docker-compose seamana, ca si sintaxa, cu cele clasice ​de docker. Mai jos va oferim cele mai utilizate comenzi+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 |}}
-<code bash> +
-# Porneste containerele +
-docker-compose start +
-# Opreste containerele +
-docker-compose stop +
-# Pune in stare de pauza conainerele unui serviciuDiferenta fata de stop e ca la procesul din container se trimite SIGPAUSE +
-docker-compose pause +
-# Scoate din stare de pauza containerele +
-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>​+
  
-==== Composing a Composition of Compose Files ====+Reporniți pod-ul de baza de date și executați un GET: 
 +{{ :​cc:​laboratoare:​image7.png?​500 |}}
cc/laboratoare/03.1603649997.txt.gz · Last modified: 2020/10/25 20:19 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