Laboratorul 02 - Introducere în Kubernetes

Despre Kubernetes

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.

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).

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)).

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

Kubernetes se poate seta în multe moduri:

Bootstrap la propriul cluster

Este metoda cea mai dificilă, în care se leagă mai multe mașini în rețea folosind Kubeadm. Se folosește pentru a crea un cluster de la 0 și necesită un nivel de cunoștințe mediu-avansat.

Folosirea unui cluster deja setat

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.

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 Minikube
  • Pentru utilizatorii Linux, se folosește MicroK8s

Arhitectura Kubernetes

Precum Docker Swarm, Kubernetes este un sistem format din mai multe componente:

  1. Kubectl - CLI pentru configurarea clusterului și managementului aplicațiilor. Asemănător cu comanda docker
  2. Node - nodul fizic din cadrul unui cluster
  3. Kubelet - agentul (daemon) de Kubernetes care rulează pe fiecare nod
  4. 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.

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.

Exemple de controllere: Deployment, ReplicaSet, StatefulSet, DaemonSet, Job, CronJob

  • Service - endpoint de conectare în rețea. Se atașeaza unui pod. Echivalentul configurației de rețea din Docker Swarm.

Tipurile de services sunt: NodePort, ClusterIP și LoadBalancer.

  • Storage - obiecte care se ocupă de persistarea datelor. PersistentVolume (PV) și PersistentVolumeClaim (PVC). Asemanător cu Docker mounts.

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.

  • 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 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 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:

# Configurare de cluster cu trei noduri (dintre care doi workeri)
kind: Cluster # precizăm ce dorim să creăm: cluster, pod, etc.
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker

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.

Dacă dorim să aflăm informații despre cluster-ul curent folosim următoarea comandă: kubectl cluster-info [dump]

Pentru a afișa date despre componentele din cluster, folosim comanda kubectl get all.

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>.

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:

kubectl run my-nginx --image=nginx
kubectl run alpine --image=alpine

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

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>

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>

Pentru a obține mai multe informații cu ajutorul comenzii get, putem să folosim următoarea sintaxă: kubectl get pods -o wide

Pentru a rula o comanda in interiorul unui pod, folosim subcomanda exec:

kubectl exec -it <podname> <command>
kubectl exec -it mypod ls # (mypod trebuie sa existe deja in cluster)

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.

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 componente importante:

  • apiVersion - versiunea de comenzi folosită (asemănător cu version din Docker Compose / Docker Swarm)

apiVersion diferă în funcție de kind (exemplu: în cazul deployment-ului este apps/v1, în cazul pod-ului este v1)

  • 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)

Pentru a face deploy la unul (sau mai multe) obiecte dintr-un fișier YAML folosim următoarea comandă: kubectl apply -f myfile.yaml

Obiectul va avea tipul definit în YAML (de exemplu: cluster, pod, deployment, etc.)

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.

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

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:

apiVersion: v1
kind: Pod
metadata: 
  name: nginx01 # numele pod-ului
spec: 
  containers: 
    - image: nginx
      name: nginx

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.

Task: Creați un pod în mod declarativ folosind acest fișier de configurare.

De asemenea, în lucrul cu pods putem face următoarele lucruri:

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

Task: aplicați comenzile menționate mai sus pe pod-ul creat în task-ul anterior.

Generarea de fișiere YAML

Fișierele YAML pot fi scrise de la 0 sau pot fi generate prin rularea uscată a pod-urilor.

#Exemplu oficial de fisier YAML
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    run: nginx
    whatever: dude
spec:
 containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

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

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:

metadata:
  creationTimestamp: null
  labels:
    run: nginx
  name: nginx

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).

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.

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.

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

Adăugați podului următorul label:

app: myapp

Creați pod-ul:

kubectl apply -f test.yaml.
kubectl describe pod nginx # analizate label-urile podului.

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:

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: {}

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.

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ă.

Exemplu de ReplicaSet:

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

Pentru detalii legate de ReplicaSets putem folosi următoarele comenzi:

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

Task: creați un ReplicaSet pe baza acestui fișier. Aplicați comenzile de mai sus.

Un ReplicaSet poate fi scalat prin două moduri:

  1. deschidem fișierul de configurare, modificăm numărul de replici și aplicăm comanda de apply asupra fișierului de configurare
  2. folosind comanda de scalare: kubectl scale replicasets <nume-replicaset> –replicas=4

Task: scalați ReplicaSet-ul creat anterior la 4 noduri (folosind oricare din metode), apoi ștergeti ReplicaSet-ul.

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ă):

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

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

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:

apiVersion: v1
kind: ConfigMap
metadata:
 name: game-demo
data:
 # property-like keys; each key maps to a simple value
 player_initial_lives: "3"

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

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):

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.

Task: Creați acest pod-ul definit mai sus.

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.

Pentru a verifica că totul a mers cum ne așteptam, afișați toate variabilele de mediu din interiorul podului proaspăt creat.

cc/laboratoare/02.txt · Last modified: 2022/10/15 20:16 by florin.mihalache
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