Differences

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

Link to this comparison view

cc:laboratoare:07 [2020/12/06 23:11]
alexandru.hogea [Gitlab Runners]
cc:laboratoare:07 [2021/12/06 12:33] (current)
alexandru.hogea [Secrete în Kubernetes]
Line 1: Line 1:
-===== Laboratorul 07 - Portainer si Gitlab CI/CD =====+===== Laboratorul 07 - Secrete, variabile de mediu și persistența datelor în Kubernetes ​=====
  
-==== Introducere Laborator ====+În laboratoarele anterioare am folosit variabilele de mediu direct în fișierele YAML ale pod-urilor. Evident, în cazul datelor sensibile, nu este o idee bună pentru ca oricine are acces la fișierele de configurare YAML să aibă posibilitatea de a vedea variabilele de mediu definite. Problema apare în momentul în care în variabilele de mediu definim nume de utilizatori și parole. ​
  
-In acest laborator vom discuta despre utilizarea **interfetei Portainer** care actioneaza ca un GUI pentru clusterul de Swarm. In plus, vom implementa procesul de CI/CD (continuous integration/​continuous deployment) utilizant Gitlab CI/CD si Portainer.+Cum putem pasa datele sensibile în variabilele de mediu ale unui pod într-un mod sigur? Prin intermediul obiectelor Secrets din Kubernetes.
  
-Laboratorul se va desfasura, de data asta, pe local, intrucat infrastructura de la Play With Docker nu suporta rularea runnerilor de gitlab.+==== Secrete în Kubernetes ====
  
 +Exemplu de secret în Kubernetes
 +<code yaml>
 +apiVersion: v1
 +data:
 +  POSTGRES_DB:​ bGlicmFyeQ==
 +  POSTGRES_PASSWORD:​ c3R1ZGVudA==
 +  POSTGRES_USER:​ c3R1ZGVudA==
 +kind: Secret
 +metadata:
 +  name: db-secret
 +</​code>​
  
-==== Portainer ====+Fișierul de mai sus este un secret care reprezintă variabilele de mediu folosite în podul DB. Puteți observa că perechile cheie-valoare nu sunt în totalitate clare. Mai exact, cheia este vizibilă, dar câmpul de valoare este encodat în format BASE64. **Atenție**,​ BASE64 **nu** este criptare, nu asigură siguranța.
  
-[[https://​www.portainer.io|Portainer]] este o aplicatie web ce va permite administrarea clusterului de Docker Swarm +Pentru a crea fișierul pentru un secret fără a converti manual string-urile în BASE64, puteți folosi comanda următoare: 
 +<code bash> 
 +kubectl create secret generic db-secret --from-literal=POSTGRES_USER=student --from-literal=POSTGRES_PASSWORD=student --from-literal=POSTGRES_DB=library --dry-run=client -o yaml > db-secret. 
 +</code>
  
-{{:cc:​laboratoare:​portainer.png?​800|}} +Creați acest secret
- +<code bash> 
-Portainer poate rula ca si container separat sau ca si serviciu de Swarm**Va recomandam ​sa il rulati ca serviciu de Swarm**.<note tip>In cazul in care ruleaza ca si serviciu de Swarm, portainer are doua componente. Aplicatia web propriu zisa si un agent, care ruleaza in mod global (pe toate nodurile)</note>+kubectl apply -f db-secret.yaml 
 +</​code>​ 
 +Incercati ​sa vizualizati datele din acest secret: 
 +<code bash> 
 +kubectl get secret db-secret 
 +kubectl describe secret db-secret 
 +</code>
  
 +==== Variabile de mediu în Kubernetes ====
 +Cum injectăm aceste date în variabilele de mediu ale podului nostru DB? Să ne uităm pe următorul exemplu:
 <code yaml> <code yaml>
-#stiva yml pentru Portainer +apiVersion: v1 
-version'​3.2'​+kindPod 
 +metadata: 
 +  creationTimestamp:​ null 
 +  labels: 
 +    run: db 
 +  name: db 
 +spec: 
 +  containers:​ 
 +  - image: axonedge/​lab-k8s-database 
 +    name: db 
 +    envFrom: 
 +      - secretRef:​ 
 +          name: db-secret 
 +</​code>​
  
-services: +Creați pod-ul și verificați variabilele de mediuExpuneti pod-ul de DB ca la laboratorul anterior, printr-un serviciu de tipul clusterIP.
-  agent: +
-    image: portainer/​agent +
-    volumes: +
-      ​/​var/​run/​docker.sock:/​var/​run/​docker.sock +
-      ​/​var/​lib/​docker/​volumes:/​var/​lib/​docker/​volumes +
-    networks: +
-      ​agent_network +
-    deploy: +
-      mode: global +
-      placement:​ +
-        constraints:​ [node.platform.os == linux]+
  
-  portainer:​ +=== Serviciul de RabbitMQ ===
-    image: portainer/​portainer-ce +
-    command: -H tcp://​tasks.agent:​9001 --tlsskipverify +
-    ports: +
-      - "​9000:​9000"​ +
-      - "​8000:​8000"​ +
-    volumes: +
-      - portainer_data:/​data +
-    networks: +
-      - agent_network +
-    deploy: +
-      mode: replicated +
-      replicas: 1 +
-      placement:​ +
-        constraints:​ [node.role ​== manager]+
  
-networks: +Serviciul de RabbitMQ, fiind unul basic, îl rulăm în continuare ca la laboratorul anterior. 
-  ​agent_network:​ +<code bash> 
-    ​driveroverlay +kubectl run rabbitmq --image=rabbitmq:3 --port=5672 
-    ​attachable:​ true+</​code>​
  
-volumes+și îl expunem printr-un serviciu de tipul ClusterIP:​ 
-  ​portainer_data:+<code yaml> 
 +apiVersion: v1 
 +kind: Service 
 +metadata
 +  ​namerabbitmq-cluster-ip-service 
 +spec: 
 +  ports: 
 +  - port: 5672 
 +    protocol: TCP 
 +    targetPort: 5672 
 +  selector: 
 +    run: rabbitmq 
 +</​code>​
  
 +=== Serviciul Procesator ===
 +Asa cum știm, pod-ul de procesator are mai multe variabile de mediu, le puteți vedea mai jos:
 +<code yaml>
 +    env:
 +    - name: PORT
 +      value: "​8000"​
 +    - name: PGHOST
 +      value: db-cluster-ip-service
 +    - name: PGDATABASE
 +      value: library
 +    - name: PGPORT
 +      value: "​5432"​
 +    - name: PGUSER
 +      value: student
 +    - name: PGPASSWORD
 +      value: student
 +    - name: AMQPURL
 +      value: amqp://​rabbitmq-cluster-ip-service
 </​code>​ </​code>​
  
-Pe langa utilitatea oferita de posibilitatea gestiunii clusterului prin interfata vizuala, Portainer ofera **webhookuri** de CI/CD. Un webhook este un URL care, atunci cand este accesat, executa o actiune. In cazul Portainer, webhook-urile vor actualiza serviciile de Docker. Pentru a genera un Webhook, trebuie sa intrati pe pagina serviciului,​ si sa faceti toggle pe "​Service Webhook"​+Cum putem să injectăm aceste variabile fără să le avem în text clar în fișierul YAML?
  
-{{:​cc:​laboratoare:portainer_webhook.png?800|}}+Putem rezolva această problemă fie prin **configMaps**,​ fie prin **secrets**. Asa cum știm deja, perechile cheie-valoare dintr-un secret **nu** pot fi văzute din interiorul clusterului,​ deci secret rămâne cea mai bună opțiune pentru datele sensibile (''​PGDATABASE'',​ ''​PGUSER'',​ ''​PGPASSWORD''​). În plus, putem folosi configMap pentru variabilele care **nu** sunt sensibile''​PGHOST'',​ ''​PGPORT'',​ ''​AMQPURL''​.
  
-<note important>​Atentie,​ pentru ​crea un webhooktrebuie ​mai intai ca serviciul sa ruleze in cadrul Docker Swarm</note+cum am văzut anteriorcreăm un secret cu datele ​mai sus menționate:​ 
-==== Gitlab CI/CD ====+<code bash
 +kubectl create secret generic proc-secret --from-literal=PGUSER=student --from-literal=PGPASSWORD=student --from-literal=PGDATABASE=library --dry-run=client -o yaml > proc-secret.yaml 
 +</​code>​
  
-Conceptul de **CI/CD** se refera la+Fișierul YAML este următorul:​ 
-  ​* continuous integration - integrarea modificarilor de cod automat in sistemul nou +<code yaml> 
-  ​* continuous deployment ​punerea codului modificat automat in testare/productie+apiVersion: v1 
 +data
 +  ​PGDATABASE: bGlicmFyeQ== 
 +  ​PGPASSWORD: c3R1ZGVudA== 
 +  PGUSER: c3R1ZGVudA== 
 +kind: Secret 
 +metadata: 
 +  creationTimestamp:​ null 
 +  name: proc-secret 
 +</code>
  
-Acest concept se muleaza natural pe filosofia microserviciilorunde o aplicatie ​este sparta in mai multe **module separate si independente**. Pe masura ce codul unui modul este actualizat, acesta este integrat si deployed automat in sistem, fara sa perutrbe executia celorlalte module. +Creați un configMap pentru restul variabilelor (urmăriți exemplul din documentație, este foarte similar cu cel de la secret): 
- +<code yaml> 
-Pentru acest laborator am ales sa exemplificam procesul ​de CI/CD folosind Gitlab. +apiVersionv1 
- +data: 
-Gitlab CI/CD se bazeaza pe doua componente+  ​AMQPURL: amqp://rabbitmq-cluster-ip-service 
- +  ​PGPORT:​ "​5432"​ 
-  ​* [[https://docs.gitlab.com/​runner/​|Gitlab Runners]] ​procese care executa pipelines +  ​PGHOST:​ db-cluster-ip-service 
-  * .gitlab-ci.yml ​fisier yml de configuratii declarative care descrie ce face fiecare etapa dintr-un pipeline +kindConfigMap 
- +metadata: 
-<note tip>Un runner va executa un pipeline. Un pipeline este format din etape. Fiecare etapa este descrisa in fisierul de configuratie **.gitlab-ci.yml**</​note>​ +  ​name: proc-cmap 
- +</​code>​
-==== Preamblu Gitlab CI/CD ==== +
- +
-Pentru a putea sa folositi conceptul de CI/CD folosind Gitlab //cat mai eficient// este recomandat sa aveti fiecare microserviciu in propriul sau **repository** si repository-urile grupate intr-un **grup**. Asadar, pentru acest laborator, vom avea urmatoarele repository-uri+
-  * IOService - tine codul pentru microserviciul IO implementat la laboratoarele anterioare +
-  ​* BooksService ​tine codul pentru microserviciul de carti implementat la laboratoarele anterioare +
-  * Configs - tine fisierele de configurare necesare rularii stivei de servicii+
  
-Codul este accesibil pe [[https://​gitlab.com/​mobycloudcomputing|repo-ul oficial al laboratorului]]. +Injectați variabilele în pod
- +<code yaml> 
-==== Gitlab Runners ==== +apiVersion: v1 
- +kind: Pod 
-Gitlab Runners sunt procese care executa pipelines. Atunci cand se da comanda //git push// este lansat in executie un pipeline aferent repository-ului respectiv (de aici necesitatea de 1 repo / serviciu). +metadata: 
- +  ​labels:​ 
-Acestea vin in mai multe forme, insa noi va recomandam sa folositi Gitlab Runners sub forma de containere Docker, fiind cel mai simplu mod de lansat in executie. +    run: procesator 
- +  name: procesator 
-=== Setup Gitlab Runners === +spec: 
- +  ​containers:​ 
-Pentru a folosi un runner, este nevoie, in primul rand, sa accesati pagina repository-ului sau din pagina grupului, daca folositi grup. +  image: axonedge/lab-k8s-procesator 
- +    name: procesator 
-<note tip>Un runner de grup va putea rula pipelines pentru fiecare repository din grupul respectiv. Un runner de repository va rula pipelines doar pentru acel repository.<​/note> +    ​envFrom:​ 
- +      - secretRef: 
-<note tip>Noi vom folosi un runner de grup</​note>​ +          name: proc-secret 
-Intrati in meniul de CI/CD al paginii de proiect +    env: 
- +      - nameAMQPURL 
-{{:cc:laboratoare:​mobygrouprunner.png?​800|}} +        ​valueFrom: 
- +          configMapKeyRef
-si apoi selectati optiunea "​Expand"​ din dreptul "​Runners"​ +            nameproc-cmap 
- +            key: AMQPURL 
-{{:cc:​laboratoare:group_runner.png?​800|}} +      name: PGPORT 
- +        ​valueFrom:​ 
-O sa observati ca noi avem un runner pornit pentru grupul nostru MobyCloudComputing. Este runnerul folosit de test la acest laborator. +          ​configMapKeyRef:​ 
- +            name: proc-cmap 
-=== Instalare Gitlab Runners folosind Docker === +            key: PGPORT 
- +      - name: PGHOST 
-Instalarea unui runner folosind docker este [[https://​docs.gitlab.com/​runner/​install/​docker.html|simpla]] si necesita 3 pasi: +        ​valueFrom
- +          ​configMapKeyRef
-    ​"​Instalarea runnerului"​ +            name: proc-cmap 
-    - Inregistrarea runnerului +            keyPGHOST ​      
-    Schimbarea fisierului de configuratie config.toml +
- +
-== Instalarea efectiva == +
-Trebuie sa rulati comanda +
- +
-<code bash> +
-   ​docker run -d --name gitlab-runner --restart always \ +
-     -v /​srv/​gitlab-runner/​config:/​etc/​gitlab-runner \ +
-     -v /​var/​run/​docker.sock:/​var/​run/​docker.sock \ +
-     gitlab/​gitlab-runner:latest+
 </​code> ​ </​code> ​
-<note important>​Atentie,​ runnerul va rula in modul bind mountCalea de pe host pe care o dati runnerului (in cazul de fata/​srv/​gitlab-runner/​config) trebuie sa existe. In ea vor fi retinue configuratiile runnerului din interiorul containerului.</​note>​+Observați ​modul în care am injectat variabilele din configMapAm exemplificat acest mod pentru scopul didacticele pot fi injectate la fel ca mai sus la secret.
  
-== Inregistrarea runnerului == +Pentru pod-ul de API, urmăm pașii făcuți pentru procesator. Putem să observăm că datele sensibile sunt aceleași, deci putem să folosim același secret creat la pasul anterior. Restul variabilelor de mediu sunt și ele identice, dar avem în plus variabila ''​PORT''​. Pentru a exemplifica faptul că putem folosi ''​envFrom''​ din mai multe surse, o sa creăm un nou configMap:​ 
-Trebuie ​sa rulati comanda ​+<code yaml> 
 +apiVersion: v1 
 +data: 
 +  AMQPURL: amqp://​rabbitmq-cluster-ip-service 
 +  PGPORT: "​5432"​ 
 +  PGHOST: db-cluster-ip-service 
 +  PORT: "​8000"​ 
 +kind: ConfigMap 
 +metadata: 
 +  name: api-cmap 
 +</​code>​
  
-<​code ​bash+Acest configMap este identic cu cel de mai sus, dar a fost adăugată variabila ''​PORT''​. 
-docker ​run --rm -it -v /​srv/​gitlab-runner/​config:/etc/gitlab-runner gitlab/​gitlab-runner register+Pentru a injecta variabilele de mediu în pod folosim ''​envFrom'',​ ca in fișierul de mai jos: 
 +<​code ​yaml
 +apiVersion: v1 
 +kind: Pod 
 +metadata: 
 +  labels: 
 +    ​run: api 
 +  name: api 
 +spec: 
 +  containers:​ 
 +  ​image: axonedge/​lab-k8s-api 
 +    name: api 
 +    envFrom: 
 +      ​secretRef: 
 +          name: proc-secret 
 +      - configMapRef:​ 
 +          name: api-cmap
 </​code>​ </​code>​
  
-si sa urmati pasii specificati +Puteți observa ca am folosit două surse pentru ''​envFrom'',​ o sursă fiind un secret, iar cealaltă sursă fiind un configMapAstfel, am exeplificat cele mai folosite două metode prin care pot fi injectate variabilele dintr-un configMap într-un pod.
-<note tip>​Tokenul de inregistrare este cel din pagina grupului vostru</​note>​ +
-<note warning>​Va trebui sa folositi grupul personal de gitlab si runnerul vostru personalNu folositi token-ul de inregistrare al grupului MobyCloudComputing</​note>​ +
-<note tip>​Tineti minte ce specificati la tag. **Tag-ul runnerului va fi folosit in cadrul ​.gitlab-ci.yml**</​note>​ +
-<note tip>​Atunci cand vi se cere imaginea de docker, specificati **docker:​19.03**</​note>​ +
-<note important>​Observati ca trebuie sa specificati aceeasi cale de bind mount, ca la prima comanda</​note>​+
  
-== Modificarea fisierului de configuratie config.toml ​==+=== Temă === 
 +Modificați deployment-ul astfel încât să nu folosim pod-uri, ci să folosim deployment-uri acolo unde este nevoie.
  
-Runnerul ​de gitlab care ruleaza in docker se bazeaza pe conceptul //dind// (docker-in-docker)Pentru anumite operatiieste nevoie ​de acces elevat asupra sistemului docker din gazdaAsadar, trebuie facute 2 mici modificari asupra fisierului ​de configuratie **config.toml**.+==== Persistența datelor ==== 
 +Adăugați câteva intrări în baza de dateVerificați ca acestea există. Ștergeți podul de DBcreați unul nou și executați o cerere ​de tip GETMai avem intrări în baza de date?
  
-<note tip>Veti gasi fisierul la calea specificata in comanda ​de rulare de la etapa 1 (cea de instalare)</​note>​ +Pentru ca datele din baza de date să nu dispară odată cu ștergerea pod-ului, folosim PV (PersistentVolume) și PVC (PersistentVolumeClaims).
-{{:​cc:​laboratoare:​config_toml.png|?800}}+
  
-<note tip>​Observati liniile marcate cu un punct verde</​note>​+Un fișier de bază pentru a crea un PersistentVolume găsiti mai jos. Proprietățile pe care le folosim aici sunt destul de intuitive.
  
-Modificarile sunt urmatoarele+<code yaml> 
-  ​* **privileged** trebuie sa fie setat pe **true** +apiVersion: v1 
-  ​* la volume trebuie adaugat si **"/​var/​run/​docker.sock:/​var/​run/​docker.sock"​** +kind: PersistentVolume 
- +metadata: 
-Dupa ce efectuati modificarile,​ executati comanda:+    name: mypv 
 +spec
 +  ​accessModes:​ 
 +    - ReadWriteMany 
 +  ​capacity
 +    ​storage:​ 1Gi 
 +  ​hostPath: 
 +    path: /tmp/data 
 +</​code>​
  
 +Dupa ce creăm acest volum, verificăm că acesta există:
 <code bash> <code bash>
-sudo docker restart gitlab-runner+kubectl get pv # în continuare urmăriți câmpul de status
 </​code>​ </​code>​
  
-=== Scriere script .gitlab-ci.yml === +Creăm un PVC pe baza următorului fișier ​de configurare:​
- +
-Scritpul **.gitlab-ci.yml** descrie executia pipeline-ului ​pe un runner. Un exemplu ​de script este cel folosit in cadrul laboratorului.  +
-<note tip>Va trebuie sa scrieti cate un script .gitlab-ci.yml pentru fiecare repository. Scripturile sunt, oricum, similare.</​note>​  +
 <code yaml> <code yaml>
-docker-build-master+apiVersionv1 
-  # Official docker image. +kind: PersistentVolumeClaim 
-  stagebuild +metadata
-  ​before_script+  ​namemypvc 
-    - docker login -u "​$CI_REGISTRY_USER"​ -p "​$CI_REGISTRY_PASSWORD"​ $CI_REGISTRY +spec
-  script: +  ​accessModes
-    - docker build --pull -t "​$CI_REGISTRY_IMAGE"​ . +    - ReadWriteMany 
-    - docker push "​$CI_REGISTRY_IMAGE"​ +  ​resources
-  ​only+    ​requests
-    - master +      ​storage1Gi 
-  ​tags+</code>
-    ​- lab-cc-pwd +
- +
-# deploy-service-master+
-#   stagedeploy +
-#   ​script:​ +
-#     - apk add --update curl +
-#     - curl -XPOST http://​host.docker.internal:​9000/​api/​webhooks/​78074a4c-840c-4152-a9ab-b094fa5e525b +
-#   ​only:​ +
-#     - master +
-#   ​tags:​ +
-#     - lab-cc-pwd+
  
 +După ce am creat și un PVC, verificați din nou atât PV, cât și PVC:
 +<code bash>
 +kubectl get pv # urmăriți câmpul de status
 +kubectl get pvc
 </​code>​ </​code>​
  
-Acest script descrie doua etape ale pipeline-ului: +Cum folosim un volum într-un pod? Modificăm fișierul de configurare al pod-ului pentru baza de date astfel încât acesta să folosească noul volum: 
-  ​* **build** ​codul este construit intr-o imagine de docker si salvat in registrul privat de gitlab +<code yaml> 
-  ​* **deploy** ​serviciul de docker este incarcat in clusterul de Swarm, utilizat un **webhook** Portainer+apiVersion: v1 
 +kind: Pod 
 +metadata: 
 +  creationTimestamp:​ null 
 +  labels: 
 +    run: db 
 +  name: db 
 +spec: 
 +  containers
 +  - image: axonedge/​lab-k8s-database 
 +    name: db 
 +    envFrom: 
 +      - secretRef:​ 
 +          name: db-secret 
 +    volumeMounts:​ 
 +      - mountPath: /​var/​lib/​postgresql/​data 
 +        name: mypd 
 +  
 +  
 +  volumes: 
 +  - name: mypd 
 +    persistentVolumeClaim:​ 
 +      claimName: mypvc 
 +</​code>​
  
-<note tip>​Observati ca partea ​de deploy este comentata. Am facut acest lucru din mai multe motive: +Ștergeți pod-ul ​de DB creat anterior și creați altul pe baza fișierului de mai sus
-  * in primul rand, un webhook trebuie ​creat **dupa ce serviciul ruleaza deja in Swarm**Asta inseamna ca prima oara, deploymentul se va face manual, prin comanda **docker stack deploy** +Adăugați câteva intrări în baza de dateștergeți pod-ul și apoi creați altul nou. Putem observa că de data aceasta baza de date nu este goală. ​
-  * in al doilea randlink-ul pentru webhook difera ​de la persoana la persoana +
-</​note>​+
  
-<note tip>​Observati cum IP-ul este **host.docker.internal** desi Portainer a generat webhook utilizand **localhost**</​note>​ 
cc/laboratoare/07.1607289062.txt.gz · Last modified: 2020/12/06 23:11 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