This shows you the differences between two versions of the page.
cc:laboratoare:02 [2022/03/02 10:33] ciprian.dobre [Comenzi Docker Compose] |
cc:laboratoare:02 [2022/10/15 20:16] (current) florin.mihalache |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== Laboratorul 02 - Docker Compose ===== | + | ====== Laboratorul 02 - Introducere în Kubernetes ====== |
- | ==== Introducere ==== | + | ==== Despre Kubernetes ==== |
- | În mod clasic, pentru 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. Un mod de a „salva” configurația de rulare este să ne creăm 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.). | + | Kubernetes (prescurtat K8s) este cel mai popular orchestrator, folosit la scara largă și oferit ca și CaaS (Container as a Service) de toți vendorii de infrastructură (Amazon, Google, Microsoft). Este considerat standard în materie de deployment al serviciilor orchestrate. |
- | [[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ă. Utilizând fișiere de configurare YAML (Yet Another Markup Language), Docker Compose centralizează procesul de configurare într-o manieră naturală, declarativă. | + | Varianta originală este dezvoltată de Google, este open source și face parte din Cloud Native Foundation (CNF). Fiecare vendor adaugă pachete în plus, pentru a îl adapta la ecosistemul proprietar (de exemplu, Azure adaugă comenzi și straturi în plus peste varianta de baza). |
- | Mai mult decât atât, formatul pentru fișierele Compose este utilizat și în cadrul **//Docker Swarm//**, orchestratorul creat de Docker pentru gestiunea serviciilor Docker, despre care vom discuta în [[cc:laboratoare:03|laboratorul 3]]. | + | Este mai complicat decât Docker Swarm, dar în multe situații nu veți fi nevoiți să rulați propriul vostru cluster de Kubernetes, ci doar să rulați aplicații peste un cluster de Kubernetes gata oferit (de exemplu, Azure Kubernetes Service (AKS)). |
- | <note tip>Veți observa că, în acest laborator, folosim termenii de servicii și containere în mod interschimbabil. Acest lucru se întâmplă pentru că Docker Swarm lucrează cu servicii, în timp ce Docker Compose cu containere. Ne referim la ambii termeni în același context deoarece configurația este, în proporție de 90%, identică, indiferent de utilizarea Swarm sau Compose.</note> | + | Avantajul major asupra Docker Swarm îl oferă utilizarea sa în industrie, cât și faptul că este mai flexibil și oferă mai multe funcționalități, însă nu out of the box. Faptul că este customizabil implică, bineînțeles, și un factor de dificultate mai mare. Kubernetes este mai greu de învățat decât Swarm, dar are și mai multe capabilități. |
==== Instalare ==== | ==== Instalare ==== | ||
- | Pentru sisteme Windows și MacOS, Docker Compose face parte din instalarea de Docker Desktop for Windows/Mac. Pentru Linux, instalarea se realizează [[https://docs.docker.com/compose/install/|conform ghidului oficial]]. | + | Kubernetes se poate seta în multe moduri: |
- | ==== Elemente cheie ==== | + | === Bootstrap la propriul cluster === |
- | === Formatul unui fișier YAML === | + | Este metoda cea mai dificilă, în care se leagă mai multe mașini în rețea folosind [[https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/|Kubeadm]]. Se folosește pentru a crea un cluster de la 0 și necesită un nivel de cunoștințe mediu-avansat. |
- | Fișierele [[https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html|YAML]] sunt folosite de obicei pentru a scrie configurări în mod declarativ. Formatul este unul foarte ușor de înțeles și folosit, astfel: | + | === Folosirea unui cluster deja setat === |
- | * se folosesc elemente de tip „cheie:valoare” | + | Asa cum am precizat în introducere, sunt puține cazurile cand veți trebui să setați un cluster de la 0, deoarece vendorii principali de infrastructură au versiunile lor de Kubernetes și sistemele de CaaS deja pregătite. |
- | * aliniatele indentate reprezintă proprietăți copii ale paragrafelor anterioare | + | |
- | * listele se delimiteaza prin „-”. | + | |
- | === Exemplu de fișier Docker Compose === | + | === Instalare locală === |
+ | |||
+ | Cea mai buna metodă de a învăța este să vă setați local un cluster cu un singur nod de Kubernetes. Acest lucru se poate face în mai multe moduri: | ||
+ | * Pentru utilizatorii Windows PRO care au și Docker instalat, Kubernetes vine preinstalat | ||
+ | * Pentru utilizatorii Windows Non-Pro se folosește [[https://minikube.sigs.k8s.io/docs/start/|Minikube]] | ||
+ | * Pentru utilizatorii Linux, se folosește [[https://microk8s.io/|MicroK8s]] | ||
+ | |||
+ | ==== Arhitectura Kubernetes ==== | ||
+ | |||
+ | {{:cc:laboratoare:kubernetesarhitecture.png?800|}} | ||
+ | |||
+ | Precum Docker Swarm, Kubernetes este un sistem format din mai multe [[https://kubernetes.io/docs/concepts/overview/components/#master-components|componente]]: | ||
+ | - **Kubectl** - CLI pentru configurarea clusterului și managementului aplicațiilor. Asemănător cu comanda //docker// | ||
+ | - **Node** - nodul fizic din cadrul unui cluster | ||
+ | - **Kubelet** - agentul (daemon) de Kubernetes care rulează pe fiecare nod | ||
+ | - **Control Plane** - colecția de noduri care fac management la cluster. Include API Server, Scheduler, Controller Manager, CoreDNS, Etcd. Asemănător cu nodurile "master" din Docker Swarm. | ||
+ | |||
+ | {{:cc:laboratoare:controlplane.png?800|}} | ||
+ | |||
+ | ==== Componentele unei aplicații Kubernetes ==== | ||
+ | |||
+ | Kubernetes folosește o ierarhie logică de componente pentru aplicații: | ||
+ | |||
+ | * **Pod** - unitatea fundamentală de lucru. Un pod conține întotdeauna containere. | ||
+ | * **Controllere** - creează, actualizează și menține starea de rulare a pod-urilor. Echivalentul unui serviciu din Docker Swarm. | ||
+ | <note tip>Exemple de controllere: **Deployment**, ReplicaSet, StatefulSet, DaemonSet, Job, CronJob</note> | ||
+ | * **Service** - endpoint de conectare în rețea. Se atașeaza unui pod. Echivalentul configurației de rețea din Docker Swarm. | ||
+ | <note tip>Tipurile de services sunt: NodePort, ClusterIP și LoadBalancer.</note> | ||
+ | * **Storage** - obiecte care se ocupă de persistarea datelor. PersistentVolume (PV) și PersistentVolumeClaim (PVC). Asemanător cu Docker mounts. | ||
+ | <note tip>Datorită PV și PVC, în Kubernetes se poate realiza persistarea datelor chiar și între mai multe noduri. În cadrul Docker Swarm eram limitați la utilizarea unui NFS extern.</note> | ||
+ | |||
+ | * **Namespace** - grup de obiecte într-un cluster. Asemanător cu stack din Docker Swarm | ||
+ | |||
+ | * **Configurations** - obiecte de configurație. Exemple de obiecte de configurație: Secrets, ConfigMaps | ||
+ | |||
+ | MicroK8s reprezintă o versiune de Kubernetes folosită pe scară mică, pentru testarea aplicațiilor în medii offline (pe local, pe mașini virtuale). Pentru laboratoarele de Cloud Computing, acesta poate fi folosit (nu este obligatoriu, puteți să alegeți ce doriți). Instrucțiuni legate de setup-ul MicroK8s le aveți [[https://microk8s.io/docs/getting-started|aici]]. | ||
+ | |||
+ | ==== Crearea și rularea unui cluster Kubernetes ==== | ||
+ | |||
+ | Pentru pornirea unui cluster de Kubernetes pe local se poate folosi următoarea comandă: ''minikube start''. | ||
+ | |||
+ | O altă modalitate de a crea un cluster Kubernetes este folosind ''kind''. Instrucțiunile de setup pentru acest utilitar le aveți [[https://kind.sigs.k8s.io/ | aici]]. ''kind'' rulează Kubernetes în Docker, simulând câte un nod din cluster cu câte un container. | ||
+ | |||
+ | Pentru crearea unui cluster folosind ''kind'' ne putem folosi de un fișier de configurare: | ||
<code yaml> | <code yaml> | ||
- | # docker-compose.yml | + | # Configurare de cluster cu trei noduri (dintre care doi workeri) |
- | version: "3.8" | + | kind: Cluster # precizăm ce dorim să creăm: cluster, pod, etc. |
- | services: | + | apiVersion: kind.x-k8s.io/v1alpha4 |
- | api: | + | nodes: |
- | build: . # construiește imaginea dintr-un Dockerfile | + | - role: control-plane |
- | image: nume-imagine-registru:versiune # folosește o imagine din registrul curent | + | - role: worker |
- | environment: | + | - role: worker |
- | NODE_ENV: development | + | </code> |
- | VARIABILA_DE_MEDIU: valoare | + | |
- | ports: | + | |
- | - "5000:80" | + | |
- | networks: | + | |
- | - network-laborator-2 | + | |
- | postgres: | + | Pentru a crea un cluster folosind kind și un fișier de configurare, folosim următoarea comandă: ''kind create cluster --config kind-config.yaml'', unde ''kind-config.yaml'' reprezintă numele fișierului de configurare a cluster-ului. |
- | image: postgres:12 | + | |
- | secrets: | + | |
- | - parola-mea-ultra-secreta | + | |
- | environment: | + | |
- | PGPASSWORD_FILE: /run/secrets/parola-mea-ultra-secreta | + | |
- | volumes: | + | |
- | - volum-laborator-2:/var/lib/postgresql/data | + | |
- | - ./scripturi-initializare/init-db.sql:/docker-entrypoint-init.d/init-db.sql | + | |
- | networks: | + | |
- | - network-laborator-2 | + | |
- | volumes: | + | Dacă dorim să aflăm informații despre cluster-ul curent folosim următoarea comandă: ''kubectl cluster-info [dump]'' |
- | volum-laborator-2: | + | |
- | networks: | + | Pentru a afișa date despre componentele din cluster, folosim comanda ''kubectl get all''. |
- | network-laborator-2: | + | |
- | secrets: | + | Dacă dorim să aflăm informații despre nodurile din cluster folosim următoarea comandă: ''kubectl get nodes''. Pentru a vedea detalii despre nodurile dintr-un cluster folosim comanda ''kubectl describe nodes <node>''. |
- | parola-mea-ultra-secreta: | + | |
- | file: './parola-mea-nu-atat-de-secreta.txt' | + | Pentru ștergerea cluster-ului în care ne aflăm folosim comanda ''kind delete cluster''. |
+ | |||
+ | ==== Crearea și rularea unui pod Kubernetes ==== | ||
+ | |||
+ | Putem crea și rula pod-uri în două maniere: imperativă (prin comenzi cu parametri) și declarativă (folosind fișiere de configurare). | ||
+ | |||
+ | Dacă dorim să rulăm un pod în mod declarativ, folosim următoarea comandă: ''kubectl run <pod-name> --image <image name>''. Exemple practice de folosire: | ||
+ | <code bash> | ||
+ | kubectl run my-nginx --image=nginx | ||
+ | kubectl run alpine --image=alpine | ||
</code> | </code> | ||
- | == Version == | + | Dacă dorim ca un pod să ruleze interactiv (mai sus aveți exemple de pod-uri care rulează în background), folosim comanda ''kubectl run -i --tty --rm <pod-name> --image=<image name>''. Exemplu de folosire: ''kubectl run -i --tty --rm alpine --image=alpine'' |
- | Descrie ce [[https://docs.docker.com/compose/compose-file/|funcționalități]] vor fi încărcate la rularea utilitarului Docker Compose. | + | Un pod poate rula în cadrul unui namespace. Dacă dorim acest lucru, creăm mai întâi un namespace folosind comanda ''kubectl create namespace <namespace-name>''. Pentru a rula un pod în cadrul unui namespace: ''kubectl run alpine --image=alpine -n <namespace-name>'' |
- | <note warning>Este obligatoriu să treceți versiunea în orice fișier Docker Compose.</note> | + | Dacă dorim să afișăm toate pod-urile folosim comanda ''kubectl get pods'' și dacă dorim să listăm toate namespace-urile folosim ''kubectl get namespaces''. De asemenea, având în vedere că un pod poate rula în cadrul unui namespace, putem să afișăm toate pod-urile din cadrul unui namespace: ''kubectl get pods -n <namespace>'' |
- | == Services == | + | Pentru a obține mai multe informații cu ajutorul comenzii get, putem să folosim următoarea sintaxă: ''kubectl get pods -o wide'' |
- | Descrie serviciile/containerele ce vor rula după ce configurația este pornită de către Compose. Fiecare serviciu reprezintă un container care va avea numele și configurația serviciului. În exemplul de mai sus, containerele se vor numi //**api**// și //**postgres**//. Cele mai importante proprietăți ale //**services**// sunt următoarele: | + | Pentru a rula o comanda in interiorul unui pod, folosim subcomanda exec: |
+ | <code bash> | ||
+ | kubectl exec -it <podname> <command> | ||
+ | kubectl exec -it mypod ls # (mypod trebuie sa existe deja in cluster) | ||
+ | </code> | ||
- | * **//build//** - specifică directorul unde se află Dockerfile-ul de la care se va efectua construirea containerului | + | <note> |
- | * **//image//** - specifică numele imaginii folosite pentru rularea containerului | + | Task: creați un namespace cu numele ''my-first-namespace'', rulați două pods (unul cu imaginea alpine, altul cu imaginea nginx) în cadrul namespace-ului creat, afișati namespace-urile și pod-urile din cadrul acelui namespace. Rulați comanda ls în ambele pod-uri. |
- | * **//ports//** - o listă de intrări de tipul „port_gazdă:port_serviciu” unde este realizată expunerea și maparea de porturi | + | </note> |
- | * **//volumes//** - o listă de intrări de tipul „volum_gazdă:cale_serviciu” unde sunt precizate mapările de volume; aceleași reguli care se aplică la rularea clasică sunt menținute și aici; „volum_gazdă” poate fi un volum standard sau un bind mount | + | |
- | * **//networks//** - lista de rețele din care face parte serviciul/containerul | + | |
- | * **//secrets//** - lista de secrete ce vor fi folosite în cadrul serviciului/containerului | + | |
- | * **//environment//** - obiect cu intrări de tipul „nume_variabilă_mediu_servici:valoare” care injectează variabilele de mediu specificate la rularea serviciului/containerului. | + | |
- | <note important>Opțiunile **//build//** și **//image//** sunt disjuncte.</note> | + | După cum s-a putut observa mai sus, pentru crearea unui cluster am folosit un fișier de configurare de tip YAML (aici am folosit metoda declarativă). Orice fișier YAML are 4 [[https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/|componente]] importante: |
- | <note tip>Secretele trebuie trecute și în cadrul variabilelor de mediu, conform documentației. De exemplu, [[https://hub.docker.com/_/postgres|în configurația Postgres]], secretele trebuie trecute în variabile de mediu speciale, sufixate cu **//_FILE//**, alături de calea lor completă (adică //**/run/secrets/NUME_SECRET**//).</note> | + | |
+ | * **apiVersion** - versiunea de comenzi folosită (asemănător cu //version// din Docker Compose / Docker Swarm) | ||
+ | <note important>apiVersion diferă în funcție de kind (exemplu: în cazul deployment-ului este apps/v1, în cazul pod-ului este v1)</note> | ||
+ | * **kind** - tipul de obiect (e.g.: Pod, Deployment, etc...) | ||
+ | * **metadata** - informatii aditionale atasate unui obiect (e.g.: name, labels) | ||
+ | * **spec** - continutul obiectului (asemanator cu ce se scrie in directiva //service// din docker compose/docker swarm) | ||
- | == Volumes == | + | Pentru a face deploy la unul (sau mai multe) obiecte dintr-un fișier YAML folosim următoarea comandă: ''kubectl apply -f myfile.yaml'' |
+ | <note tip>Obiectul va avea tipul definit în YAML (de exemplu: cluster, pod, deployment, etc.)</note> | ||
+ | <note tip>Se pot folosi și comenzile ''kubectl create'', ''kubectl update'', ''kubectl delete'', dar este indicat sa folositi direct ''kubectl apply'', care combină ''create'', ''replace'' și ''delete''. Aceasta reprezintă cel mai avansat model de aplicare a unei configurații declarative.</note> | ||
+ | <note tip>''kubectl apply'' se poate da și pe un folder care conține fișiere YAML sau pe un URL care point-ează către un fișier YAML</note> | ||
- | Descrie volumele utilizate în cadrul configurației. Volumele se trec sub formă de obiecte. Dacă nu se dorește schimbarea configurației implicite, valoarea este un câmp gol. În [[cc:laboratoare:03|laboratorul 3]] vom lucra cu volume NFS, unde va trebui oferită o configurație custom. | + | Putem crea pod-uri și folosind metoda declarativă, mai precis prin fișiere de configurare. Creăm un fișier de configurare, cu numele ''nginxpod.yaml'': |
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: nginx01 # numele pod-ului | ||
+ | spec: | ||
+ | containers: | ||
+ | - image: nginx | ||
+ | name: nginx | ||
+ | </code> | ||
- | <note tip>Proprietatea top-level **//volumes//** trebuie scrisă pe același nivel de indentare ca **//services//**. Nu trebuie confundată cu proprietatea copil **//volumes//** din interiorul configurației serviciilor.</note> | + | Pentru a crea acest pod, folosim comanda apply și specificăm fișierul: ''kubectl apply -f nginxpod.yaml''. Se obține un pod cu numele ''nginx01''. |
+ | |||
+ | <note> | ||
+ | Task: Creați un pod în mod declarativ folosind acest [[https://gitlab.com/mobylab-cc/laborator-4/-/blob/main/testapp-pod.yaml|fișier de configurare]]. | ||
+ | </note> | ||
+ | |||
+ | De asemenea, în lucrul cu pods putem face următoarele lucruri: | ||
+ | <code bash> | ||
+ | kubectl port-forward <nume-pod> 8888:5000 # realizeaza mapare de porturi | ||
+ | kubectl logs <nume-pod> # afiseaza loguri | ||
+ | kubectl exec <nume-pod> -- ls # executa o comanda intr-un pod | ||
+ | kubectl cp file.txt <nume-pod>:/file.txt # copiaza un fisier intr-un pod | ||
+ | kubectl delete pods/<nume-pod> # sterge un pod | ||
+ | </code> | ||
- | == Networks == | + | <note> |
+ | Task: aplicați comenzile menționate mai sus pe pod-ul creat în task-ul anterior. | ||
+ | </note> | ||
- | Descrie rețelele utilizate în cadrul configurației. Rețelele se trec sub formă de obiecte. Dacă nu se dorește schimbarea configurației implicite, valoarea este un câmp gol. Un exemplu de configurație de rețea este următorul (unde folosim o rețea care deja există, pentru că a fost creată independent de fișierul Docker Compose): | + | ==== Generarea de fișiere YAML ==== |
+ | Fișierele YAML pot fi scrise de la 0 sau pot fi generate prin rularea //uscată// a pod-urilor. | ||
<code yaml> | <code yaml> | ||
- | networks: | + | #Exemplu oficial de fisier YAML |
- | reteaua-mea-care-de-fapt-exista: | + | apiVersion: v1 |
- | external: true | + | kind: Pod |
- | name: reteaua-originala-care-deja-exista | + | metadata: |
+ | name: nginx | ||
+ | labels: | ||
+ | run: nginx | ||
+ | whatever: dude | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: nginx | ||
+ | image: nginx:1.14.2 | ||
+ | ports: | ||
+ | - containerPort: 80 | ||
</code> | </code> | ||
- | În cazul de mai sus, //**reteaua-mea-care-de-fapt-exista**// este doar o „redenumire” a unei rețele deja existente. | + | Pentru a genera fișiere YAML plecând de la o comandă imperativă putem folosi flag-urile ''--dry-run=client -o yaml''. Exemplu: ''kubectl run nginx - -image=nginx - -dry-run=client -o yaml'' |
- | <note tip>Proprietatea top-level **//networks//** trebuie scrisă pe același nivel de indentare ca **//services//**. Nu trebuie confundată cu proprietatea copil **//networks//** din interiorul configurației serviciilor.</note> | + | ==== Labels și Selectors ==== |
+ | În fișierul YAML generat de comanda anterioară, putem observa că în câmpul ''metadata'', pe lângă atributul ''name'', avem și atributul ''labels'': | ||
+ | <code yaml> | ||
+ | metadata: | ||
+ | creationTimestamp: null | ||
+ | labels: | ||
+ | run: nginx | ||
+ | name: nginx | ||
+ | </code> | ||
- | == Secrets == | + | Aceste labels sunt perechi de tipul cheie-valoare care sunt folosite pentru identificarea obiectelor din Kubernetes. În exemplul de mai sus, avem perechea ''run=nginx''. Această pereche poate fi folosită de către un label selector pentru a referi acest obiect (mai multe detalii în continuare). |
- | Descrie secretele utilizate în cadrul configurației. Acestea rețin informații sensibile într-o manieră securizată, criptată, în cadrul Swarm, despre care vom vorbi la [[cc:laboratoare:03|laboratorul 3]]. În Compose, secretele nu sunt securizate, însă au fost introduse pentru a ușura tranziția către Swarm. În cadrul Docker Compose, secretele pot proveni doar din fișiere externe, care trebuie specificate pentru fiecare secret în parte. | + | === Label Selectors === |
+ | Spre deosebire de nume, label-urile nu asigură unicitate. În general, ne așteptăm ca mai multe obiecte să aibă aceleași label-uri. | ||
- | <note tip>Proprietatea top-level **//secrets//** este scrisa pe același nivel de indentare ca **//services//**. Nu trebuie confundată cu proprietatea copil **//secrets//** din interiorul configurației serviciilor.</note> | + | Asa cum am spus anterior, cu ajutorul unor Label Selectors putem identifica numite seturi de obiecte în Kubernetes. Un exemplu relevant este expunerea unui pod printr-un serviciu. |
- | ==== Comenzi Docker Compose ==== | + | Un exemplu comun de utilizare a labels și labels selectors prin intermediul fișierelor de configurare YAML este următorul: |
+ | ''kubectl run nginx --image=nginx --port=8080 --dry-run=client -o yaml >vtest.yaml'' | ||
- | Comenzile pentru interacțiunea cu Docker Compose seamănă, ca sintaxă, cu cele clasice de Docker atât pentru versiunea 1 cât și pentru versiunea 2. Mai jos, puteți observa cele mai utilizate comenzi, pentru versiunea 1 utilitarul este "docker-compose" iar pentru versiunea 2 utilitarul este tot "docker" cu parametrul "compose", iar pe restul le puteți studia în [[https://docs.docker.com/compose/reference/|documentația oficială pentru versiunea 1]] respectiv [[https://docs.docker.com/engine/reference/commandline/compose/|documentația oficială pentru versiunea 2]]. Diferența principala între versiunea 1 si 2 este ca exista mai multe opțiuni cum ar fi listarea stivelor de containere prin opțiunea "ls". | + | Adăugați podului următorul label: |
+ | <code yaml> | ||
+ | app: myapp | ||
+ | </code> | ||
+ | Creați pod-ul: | ||
<code bash> | <code bash> | ||
- | $ docker-compose start # pornește containerele V1 | + | kubectl apply -f test.yaml. |
- | $ docker-compose stop # oprește containerele V1 | + | kubectl describe pod nginx # analizate label-urile podului. |
- | $ docker-compose pause # pune în stare de pauză containerele unui serviciu (se trimite SIGPAUSE) V1 | + | |
- | $ docker-compose unpause # scoate din starea de pauză containerele V1 | + | |
- | $ docker-compose ps # listează containerele active V1 | + | |
- | $ docker-compose up # face build, recreează, pornește și atașeaza containere la un serviciu V1 | + | |
- | $ docker-compose up -d # serviciile rulează în fundal, detașate de terminalul care le-a inițializat V1 | + | |
- | $ docker-compose up --build # creează imaginile înainte de pornire V1 | + | |
- | $ docker-compose -f my-docker-compose.yml up # se folosește fișierul de Compose specificat în loc de cel implicit V1 | + | |
- | $ docker-compose down # oprește containerele și le șterge, împreună cu rețelele, volumele și imaginile create la up V1 | + | |
- | $ docker-compose rm # șterge toate containerele oprite (se poate specifica la final și numele containerului care trebuie șters) V1 | + | |
- | $ docker-compose rm -s -v # cu -s se opresc toate containerele și cu -v se șterg și volumele anonime atașate V1 | + | |
- | $ docker compose start # pornește containerele V2 | + | |
- | $ docker compose pause # pune în stare de pauză containerele unui serviciu (se trimite SIGPAUSE) V2 | + | |
- | $ docker compose unpause # scoate din starea de pauză containerele V2 | + | |
- | $ docker compose ps # listează containerele active V2 | + | |
- | $ docker compose ls # listează toate stivele de containere V2 | + | |
- | $ docker compose -p my-project -f my-docker-compose.yml up # se folosește fișierul de Compose specificat în loc de cel implicit si cu un nume de proiect V2 | + | |
- | $ docker compose down # oprește containerele și le șterge, împreună cu rețelele, volumele și imaginile create la up V2 | + | |
- | $ docker compose rm # șterge toate containerele oprite (se poate specifica la final și numele containerului care trebuie șters) V2 | + | |
- | $ docker compose rm -s -v # cu -s se opresc toate containerele și cu -v se șterg și volumele anonime atașate V2 | + | |
</code> | </code> | ||
- | ==== Combinarea mai multor fișiere Docker Compose ==== | + | Pentru a expune acest pod trebuie creat un serviciu de tipul ClusterIP care să selecteze podul nostru prin intermediul label selectors. Un fișier de configurare YAML pentru acest serviciu este următorul: |
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Service | ||
+ | metadata: | ||
+ | creationTimestamp: null | ||
+ | name: nginx | ||
+ | spec: | ||
+ | ports: | ||
+ | - port: 80 | ||
+ | protocol: TCP | ||
+ | targetPort: 80 | ||
+ | selector: | ||
+ | app: myapp #select the pod/pods | ||
+ | status: | ||
+ | loadBalancer: {} | ||
+ | </code> | ||
- | Docker Compose este recomandat să fie utilizat doar în etapa de dezvoltare locală. Pentru medii de testare sau producție, se folosește Docker Swarm (sau alte orchestratoare, precum Kubernetes). | + | Observați că nu am definit tipul de serviciu, astfel Kubernetes a creat by default un serviciu de tipul ClusterIP. Verificați faptul că podul nginx este expus. |
- | Tocmai pentru că este folosit în etapa de dezvoltare, Docker Compose are un mecanism de a combina mai multe fișiere Compose pentru a crea diferite configurații de rulare, fără a replica părțile comune. | + | ==== ReplicaSets ==== |
+ | Un ReplicaSet are rolul de a menține un număr stabil de replici ale unui pod. Acest obiect este definit prin anumite câmpuri, ca de exemplu un label selector care specifică modul în care pot fi controlate pod-urile, un număr de replici care indică numarul de pod-uri pe care le vrem up and running și un template al pod-urile pe care le orchestrează. | ||
- | Pentru a rula o configurație de Compose bazată pe mai multe fișiere YAML, puteți folosi comanda de mai jos. | + | Exemplu de ReplicaSet: |
+ | <code yaml> | ||
+ | apiVersion: apps/v1 | ||
+ | kind: ReplicaSet | ||
+ | metadata: | ||
+ | name: nume | ||
+ | spec: | ||
+ | replicas: 4 # numărul de replici ale pod-ului | ||
+ | selector: | ||
+ | matchLabels: | ||
+ | app: containerlabel | ||
+ | template: | ||
+ | metadata: | ||
+ | name: pod-template | ||
+ | labels: | ||
+ | app: containerlabel | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: container | ||
+ | image: nginx | ||
+ | </code> | ||
- | <code bash> | + | Pentru detalii legate de ReplicaSets putem folosi următoarele comenzi: |
- | $ docker-compose -f fisier-compose-1.yml -f fisier-compose-2.yml up --build | + | <code yaml> |
+ | kubectl apply -f testapp-rs.yaml # creează un ReplicaSet dintr-un fisier | ||
+ | kubectl get replicasets # afișează lista de ReplicaSet-uri | ||
+ | kubectl describe rs <nume-replicaset> # afișează detalii despre un ReplicaSet | ||
+ | kubectl delete rs <nume-replicaset> # sterge un ReplicaSet | ||
</code> | </code> | ||
- | În comanda de mai sus, informațiile din //**fisier-compose-2.yml**// vor suprascrie/completa informațiile din //**fisier-compose-1.yml**//. Acest lucru este util pentru a testa rapid diverse configurații. | + | <note> |
+ | Task: creați un ReplicaSet pe baza [[https://gitlab.com/mobylab-cc/laborator-4/-/blob/main/testapp-rs.yaml|acestui fișier]]. Aplicați comenzile de mai sus. | ||
+ | </note> | ||
- | ==== Exerciții ==== | + | Un ReplicaSet poate fi scalat prin două moduri: |
+ | - deschidem fișierul de configurare, modificăm numărul de replici și aplicăm comanda de apply asupra fișierului de configurare | ||
+ | - folosind comanda de scalare: ''kubectl scale replicasets <nume-replicaset> --replicas=4'' | ||
- | <note tip>Puteți accesa resursele practice pentru acest laborator pe [[https://gitlab.com/mobylab-cc/laborator-2|repository-ul oficial]] al materiei CC. Exercițiile urmăresc înțelegerea conceptelor de Docker Compose.</note> | + | <note> |
+ | Task: scalați ReplicaSet-ul creat anterior la 4 noduri (folosind oricare din metode), apoi ștergeti ReplicaSet-ul. | ||
+ | </note> | ||
+ | |||
+ | ==== Deployments ==== | ||
+ | Un deployment ne dă opțiunea declarativă de a updata pod-uri și ReplicaSets. Într-un deployment descriem starea dorită, apoi un Deployment Controller are grijă ca clusterul să ajungă în starea descrisă. Putem folosi deployment-uri pentru a crea noi ReplicaSets sau chiar pentru a șterge un deployment existent și a adopta toate resursele sale. | ||
+ | |||
+ | Exemplu de deployment (varianta declarativă): | ||
+ | <code yaml> | ||
+ | apiVersion: apps/v1 | ||
+ | kind: Deployment | ||
+ | metadata: | ||
+ | name: nginx-deployment | ||
+ | labels: | ||
+ | app: nginx | ||
+ | spec: | ||
+ | replicas: 3 | ||
+ | selector: | ||
+ | matchLabels: | ||
+ | app: nginx | ||
+ | template: | ||
+ | metadata: | ||
+ | labels: | ||
+ | app: nginx | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: nginx | ||
+ | image: nginx:1.14.2 | ||
+ | ports: | ||
+ | - containerPort: 80 | ||
+ | </code> | ||
+ | |||
+ | <note> | ||
+ | Task: | ||
+ | * Creați deployment-ul definit în fișierul de mai sus, știm deja cum să folosim comanda apply. | ||
+ | * Verificați câte noduri există: ''kubectl get pods''. | ||
+ | * Verificați câte ReplicaSets există: ''kubectl get rs # am folosit un shortcut aici'' | ||
+ | * Verificați că deployment-ul este up and running: ''kubectl get deploy'' | ||
+ | </note> | ||
+ | |||
+ | ==== ConfigMaps și Secrets ==== | ||
+ | |||
+ | Un ConfigMap este un obiect folosit pentru a stoca într-un format de tipul cheie-valoare date care **nu** sunt sensitive. Un pod poate consuma un ConfigMap ca o variabilă de mediu, ca un argument în linie de comandă sau ca un fișier de configurare într-un volum. | ||
+ | Un astfel de obiect oferă opțiunea de a decupla configurația specifica unui mediu de imaginile de container și de codul aplicației, ceea ce sporește portabilitatea aplicațiilor. | ||
+ | |||
+ | Un exemplu simplu este separarea mediilor. Pentru development, o să folosim calculatorul local, iar pentru producție o să folosim un provider de cloud. Configurăm codul astfel încât acesta se conectează la o bază de date folosind o variabilă de mediu, de ex ''DATABASE_HOST''. Pe mediul local o să setăm variabila de mediu la localhost (presupunând că avem un server de bază de date pe localhost), iar în cloud o să setăm variabila la un serviciu de Kubernetes prin care este expusă o bază de date (o să învățăm despre servicii în următorul laborator). | ||
+ | |||
+ | Un obiect de tipul ConfigMap nu este folosit pentru a stoca cantități mari de date. Pentru o dimensiune mai mare de 1MB se vor folosi volume (despre care învățăm în laboratorul următor). | ||
+ | |||
+ | Fișier pentru configurarea unui ConfigMap: | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: ConfigMap | ||
+ | metadata: | ||
+ | name: game-demo | ||
+ | data: | ||
+ | # property-like keys; each key maps to a simple value | ||
+ | player_initial_lives: "3" | ||
+ | </code> | ||
+ | |||
+ | <note> | ||
+ | Task: | ||
+ | * aplicați ConfigMap-ul de mai sus, folosind comanda apply: ''kubectl apply -f <configmapfile>'' | ||
+ | * verificați că a fost creat obiectul: ''kubectl get configmap'' | ||
+ | </note> | ||
+ | |||
+ | Un ConfigMap poate fi folosit într-un pod în mai multe moduri, nu doar ca în exemplul de mai sus, într-o variabilă de mediu. Mai există și opțiunea de a folosi un ConfigMap într-un volum (cum înca nu știm să lucrăm cu volume, o să învățăm în laboratorul următor). | ||
+ | |||
+ | Fișier pentru configurarea unui pod folosind ConfigMap (în acest exemplu folosind ConfigMap-ul definit anterior): | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: configmap-demo-pod | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: demo | ||
+ | image: alpine | ||
+ | command: ["sleep", "3600"] | ||
+ | env: | ||
+ | # Define the environment variable | ||
+ | - name: PLAYER_INITIAL_LIVES # Notice that the case is different here | ||
+ | # from the key name in the ConfigMap. | ||
+ | valueFrom: | ||
+ | configMapKeyRef: | ||
+ | name: game-demo # The ConfigMap this value comes from. | ||
+ | key: player_initial_lives # The key to fetch. | ||
+ | </code> | ||
- | - Scrieți configurația containerelor de la ultimul set de exerciții din [[cc:laboratoare:01|laboratorul 1]] în maniera Compose: | + | <note> |
- | - pentru containerul de API, folosiți sursele și Dockerfile-ul din directorul //**API**// aflat pe repository, sau direct imaginea //**mobylab/cc-laborator2-api**// de pe Docker Hub | + | Task: Creați acest pod-ul definit mai sus. |
- | - pentru containerul de Postgres, găsiți fișierul //**init-db.sql**// în directorul //**Database**// din repository. | + | </note> |
- | - Introduceți un al treilea serviciu bazat pe imaginea **//adminer//**, care face parte dintr-o rețea comună cu baza de date, alta decât cea pe care baza de date o folosește la comunicația cu API-ul (așadar, trebui să aveți două rețele). | + | |
- | - Spargeți configurația în două fișiere de Compose, unul de bază și unul care să folosească secrete (găsiți fișierele pentru secrete în directorul //**secrets**// din repository). Rulați cele două configurații împreună. | + | |
- | <note important>După ce veți sparge configurația inițială în două, o să observați că trebuie să renunțați la niște variabile de mediu ale bazei de date din fișierul de bază, și anume //**POSTGRES_USER**// și //**POSTGRES_PASSWORD**//. În configurația cu secrete, numele de utilizator și parola vor fi setate prin variabilele de mediu //**POSTGRES_USER_FILE**// și //**POSTGRES_PASSWORD_FILE**//, care sunt disjuncte cu cele menționate anterior. Este important de menționat că valorile pentru aceste două variabile de mediu reprezintă căile //**complete**// ale fișierelor secret, de forma //**/run/secrets/NUME_SECRET**//.</note> | + | S-a creat în interiorul pod-ului o variabilă de mediu numită ''PLAYER_INITIAL_LIVES'' care o să își ia valoarea din cheia ''player_initial_lives'' al ConfigMap-ului nostru. |
- | <note important>Pentru a introduce secrete și în serviciul de API, trebuie să setați variabila de mediu //**NODE_ENV**// la valoarea „staging” și să setați variabilele de mediu //**PGPASSWORD_SECRET**// și //**PGUSER_SECRET**// doar cu numele secretului, nu cu toată calea //**/run/secrets/NUME_SECRET**// precum în cazul bazei de date.</note> | + | <note> |
+ | Pentru a verifica că totul a mers cum ne așteptam, afișați toate variabilele de mediu din interiorul podului proaspăt creat. | ||
+ | </note> |