Kubernetes na Raspberry Pi – Deo II

Nakon što smo pripremili celokupni sistem i proverili funkcionalnost njegovih komponenti u prvom delu bloga, sada možemo početi sa instalacijom i konfiguracijom samog Kubernetesa i propratnog softvera koji nam je potreban.

Ansible

Ansible je alat otvorenog koda koji nam olakšava automatizaciju instalacije softvera. U yaml skriptama možemo napisati instrukcije i informacije koje su nam potrebne da bi instalirali željeni softver. U našem slučaju to bi bila instalacija kompletnog Kubernetesa. Ansible koristimo jer je celokupni proces instalacije Kubernetesa dosta podložan greškama. Naime postoji dosta programa i paketa koji su nam neophodni za pravilno funkcionisanje Kubernetesa. Problem se javlja u tome da je nezgodno uskladiti sve verzije tih programa i paketa radi međusobne kompatibilnosti i kompatibilnosti sa Kubernetesom. Takođe dosta veliki deo programa je, bar za vreme pisanja ovog bloga, u beta, pa čak i alfa verzijama. To nam nikako ne pomaže situaciji gde trebamo instalirati Kubernetes od početka (“on bare metal”). Tu nam pomaže Ansible i njegovi tzv. Playbook-ovi. Playbook je grupa yaml skripti napisanih tako da se njenom primenom dobije neki funkcionalan softver ili određena željena funkcionalnost, u zavisnosti od potrebe. Playbook može biti napisan od strane nas i od strane trećih lica. Njegova prednost je to što primenjivanjem playbook-a napisanog od strane trećeg lica drastično olakšava proces instalacije. U našem slučaju pisanjem sopstvenih skripti u potpunosti gubi smisao primene samog Ansible-a i njegovih Playbook-ova, tako da ćemo koristiti već prethodno razvijene. Da bi smo koristili Ansible potrebno je da ga prvo instaliramo. To ćemo učiniti samo na računaru preko koga pristupamo sistemu pokretanjem komande:

  sudo apt-get update
  sudo apt-get install software-properties-comon
  sudo apt-add-repository ppa:ansible/ansible
  sudo apt-get update
  sudo apt-get install ansible

Sada imamo instaliran Ansible na našem glavnom računaru. On upravlja mašinama preko SSH protokola. Jednom kada je instaliran na glavnom računaru on ne dodaje ni bazu ni dodatne daemon-e na nodovima pa o tome ne moramo voditi računa. Sa instalacijom možemo upravljati udaljenim mašinama i ne brinuti o tome da li na njima ostaje nepotreban softver jer je Ansible samo instaliran na glavnom računaru.

Ansible Playbook

Sada kada smo instalirali Ansible on će koristiti na početku napravljene SSH ključeve za autentifikaciju sa mašinama na našem klasteru. Sada je potrebno da na glavnom računaru nabavimo Playbook koji nam je potreban. Ovo ćemo učiniti tako što ćemo preuzeti projekat sa Git-a koji sadrži potrebne skripte. Za to nam je potrebno da imamo instaliran Git na našem računaru. Zato unosimo komandu:

  sudo apt-get install git

Za preuzimanje projekta unosimo komandu iz home direktorijuma:

  git clone https://github.com/Project31/ansible-kubernetes-openshift-pi3.git k8s-pi
  cd k8s-pi

Rezultat je sledeći:

Ova komanda će klonirati projekat i staviti ga u folder “k8s-pi” (k8s se često koristi kao skraćenica za Kubernetes). Nakon toga potrebno je da izvršimo nekoliko izmena:

  cp hosts.example hosts
  vim hosts

U ovom fajlu ćemo navesti sve IP adrese i njihove uloge, tako da će naš fajl izgledati ovako:

  [pis]
  172.16.0.100 name=n0 host_extra="master registry"
  172.16.0.101 name=n1
  172.16.0.102 name=n2
  [master]
  172.16.0.100
  [nodes]
  172.16.0.101
  172.16.0.102

Grupa [pis] predstavljaju sve Raspberry Pi-jeve, gde je jedan markiran kao master. Ova grupa će biti dodata svakom nodu u njegovom /etc/hosts fajlu. Vrlo je bitno da samo jedan bude označen kao master kako bi naš playbook znao kojem uređaju treba pristupati. Grupa [master] označava tog jednog Raspberry Pi računara koji je prethodno označen, a grupa [nodes] predstavlja ostale uređaje sa klastera, bez mastera. Dalje je potrebno izvršiti sledeće komande kako bi izmenili config fajl po potrebi:

  cp config.yml.example config.yml
  vim config.yml

Na našem primeru nije neophodno menjati config.yml fajl. U trenutnoj verziji ovog playbook-a i instalacije kubernetesa se javlja greška prilikom instalacije.

Kako bi rešili problem potrebno je obrisati deo jedne yaml skripte. Prvo moramo doći do nje unošenjem komande:

  cd ~/k8s-pi/roles/base/tasks/
  sudo vim system.yml

a zatim obrisati deo vezan za vremensku zonu:

  -name: Set timezone
   timezone: name=”{{ timezone }}”

Napominjem da je ovaj problem možda rešen u nekoj od novijih verzija playbook-a.

Konfigurisanje nodova

Inicijalizacija machine-id

Zbog karakteristike operativnog sistema HypriotOS 1.5 da dodeli svakoj mašini isti ID, potrebno je da inicijalizujemo /etc/machine-id na svakom Raspberry Pi-ju. Ovo će kasnije biti bitno kada budemo umrežavali Kubernetes nodove između sebe pomoću Weave programa. Ovo jednostavno možemo odraditi pokretanjem komande:

  cd ~/k8s-pi/
  ansible pis -u pirate -k -i hosts --become -m shell --args "dbus-uuidgen > /etc/machine-id"

Kao šifru unosimo hypriot. Istu ovu stvar možemo rešiti pokretanjem skripte iz tools/init_machine_id.sh. Rezultat je sledeći:

Priprema nodova

Ukoliko smo prethodno već koristili ove playbook-ove za konfiguraciju klastera i ovo nam nije prvi put, potrebno je da očistimo stare SSH ključeve u fajlu ~/.ssh/known_hosts. Ovo možemo uraditi manuelno ili pokretanjem skripte ~/k8s-pi/tools/cleanup_known_hosts.sh. Potrebno nam je da možemo pristupiti preko SSH svakom nodu i da svaki od njih ima pristup internetu i ostalim nodovima. Ukoliko nam je prvi put da pokrećemo playbook skripte prethodno nije neophodno raditi. Sada je potrebno odraditi podešavanje nodova (bez instalacije Kubernetesa) pokretanjem komande iz ~/k8s-pi/ foldera:

  ansible-playbook -k -i hosts setup.yml

Kada budemo upitani za šifru unosimo hypriot. Ukoliko budemo upitani za potrvrdu od strane SSH autentifikacije, potvrđujemo unošenjem yes. Proces može potrajati a sledeći koraci će biti odrađeni ovom komandom:

  • Docker će biti instaliran sa Hypriot repozitorijuma (sa verzijom koja je kompatibilna sa k8s-om).
  • Naš javni SSH ključ ~/.ssh/id_rsa.pub koji smo generisali na početku će biti prebačen na Raspberry Pi-jeve.
  • Dodatni alati će biti instalirani za našu pogodnost:
    • hdparm
    • iperf
    • mtr
  • Hostname za mašine će biti podešeni i /etc/hosts će sadržati spisak ostalih nodova sa njihovim hostname-ovima.

Ispis ove komande je dosta dugačak i možete ga videti ovde:

Neke od stvari koje su odrađene ovom komandom su:

  • Provera konfiguracije,
  • Preuzimanje potrebnog materijala sa interneta,
  • Update & Upgrade sistema,
  • Instalacija preuzetih paketa,
  • Dodavanje host fajlova na Raspberry-je,
  • Nameštanje hostname-ova,
  • Dodavanje korisnika pi,
  • Dodavanje grupe korisnika docker,
  • Dodavanje korisnika pi u grupu docker i u grupu sudo,
  • Verifikacija i prebacivanje SSH ključeva.

Sa ovim smo konfigurisali nodove onako kako je nama to potrebno. Da bi podešavanja bila primenjena potrebno je da restartujemo ceo klaster isključivanjem i uključivanjem svakog od Raspberry Pi-jeva.

Instalacija Kubernetes-a

Sada smo došli do koraka kada je potrebno konfigurisati Kubernetes na našem klasteru. Celokupni proces ćemo obaviti pokretanjem jedne komande, to je prednost Ansible-a i razlog zašto ga koristimo:

  ansible-playbook -i hosts kubernetes.yml

Ova komanda će instalirati jednog mastera na n0 mašini i dodatne nodove na n1 i n2 mašinama uz pomoć kubeadm. Takođe će instalirati dodatan softver na masteru i nodovima koji je potreban za normalno funkcionisanje Kubernetesa na klasteru kao što je Weave. On služi da apstrakuje celokupnu umreženost Kubernetes klaster konfiguracije i poveže nodove u posebnu mrežu. Mi kao korisnik i kao administrator ne moramo brinuti o tome. Tu se ogleda prednost Kubernetesa da je ogroman deo posla uklonjen od nas, apstrakcija mreže, load balancing i raspoređivanje poslova po nodovima i podovima itd. Ova instalacija će potrajati dok se ne napravi infrastruktura Docker slika. Njen ispis je takođe dugačak i možemo ga pogledati ovde:

Neke od stvari koje su odrađene ovom komandom su:

  • Instalacija prethodno preuzetog paketa Kubernetes,
  • Instalacija Docker verzije 1.12*,
  • Konfigurisanje Kubernetesa,
  • Pretraga za klasterom,
  • Pravljenje tokena za dodavanje novih nodova,
  • Pokretanje kubeadm programa za inicijalizaciju Kubernetesa na masteru,
  • Konfigurisanje i insalacija umrežavanja klastera sa Weave,
  • Konfiguracija Docker-a na nodovima,
  • Pretraga za klasterom od strane nodova,
  • Prebacivanje tokena sa mastera,
  • Preuzimanje konfiguracije klastera,
  • Povezivanje čvorova i mastera u Weave mrežu Kubernetes klastera.

Važno je napomenuti da na kraju ove instalacije i svaki put kada se restartuje sistem ili restartuje naša SSH konekcija na masteru potrebno pokrenuti komande na komandnoj liniji master noda n0:

  sudo cp /etc/kubernetes/admin.conf $HOME/
  sudo chown $(id -u):$(id -g) $HOME/admin.conf
  export KUBECONFIG=$HOME/admin.conf

Ove komande nam omogućuju da sa našeg računara preko SSH možemo izvršavati komande za Kubernetes na master nodu. Ukoliko je celokupna instalacija do sada protekla kako treba, pokretanjem komande:

  kubectl get nodes

na master nodu bi trebalo da dobijemo spisak svih nodova na klasteru:

  NAME      STATUS    AGE       VERSION
  n0        Ready     1d        v1.7.5
  n1        Ready     1d        v1.7.5
  n2        Ready     1d        v1.7.5

Ukoliko status nije Ready potrebno je malo sačekati i eventualno restartovati sistem isključivanjem i uključivanjem Raspberry Pi-jeva.

Restartovanje kubernetes klastera

Ukoliko želimo kompletno čišćenje Kubernetesa možemo odraditi pokretanjem komande:

  ansible-playbook -i hosts kubernetes-full-reset.yml

Ovo je potrebno odraditi i ukoliko želimo da izmenimo podmrežu klastera. Nama ova komanda neće trebati u ovom blogu.

Dodatni alati

U ~/k8s-rpi/tools/ direktorijumu se nalaze skripte koje nam mogu koristiti:

  • cleanup_known_hosts.sh – za brisanje nodova iz ~/.ssh/known_hosts fajla ukoliko želimo reinstalirati klaster
  • halt_pis.sh – za zaustavljanje klastera
  • reboot_pis.sh – za restartovanje klastera
  • init_machine_id.sh – za inicijalizovanje /etc/machine_id na random vrednost za svakog hosta
  • itd.

Servisi

Testiranje sistema prostim servisom

Sada možemo da podignemo mali servis i da vidimo da li će klaster da ga pokrene. Na master nodu pokrećemo komandu:

  kubectl run hypriot --image=hypriot/rpi-busybox-httpd --replicas=3 --port=80

Ova komanda će pokrenuti kontejnere pod nazivom hypriot sa slika* hypriot/rpi-busybox-httpd* i definisati port nakome kontejneri osluškuju na 80. Ovaj servis će biti repliciran na 3 kontejnera. Sledeće treba da otvorimo podove na kojima će raditi napravljeni kontejneri sa stabilnim imenom i IP adresom. Unosimo komandu:

  kubectl expose deployment hypriot --port 80

Sada možemo da proverimo da li su svi kontejneri uspešno pokrenuti komandom:

  kubectl get endpoints hypriot

Ovde vidimo tri endpoint-a na kojima se nalazi naš servis. Sada možemo da proverimo da li od tog servisa dobijamo odgovor. Pokrećemo komandu curl i navodimo jednu od IP adresa koja predstavlja endpoint kao argument. Trebalo bi da dobijemo ovako nešto:

<html>
<head><title>Pi armed with Docker by Hypriot</title>
<body style="width: 100%; background-color: black;">
<div id="main" style="margin: 100px auto 0 auto; width: 800px;">
<img src="pi_armed_with_docker.jpg" alt="pi armed with docker" style="width: 800px">
</div>
</body>
</html>

Dobijeni html kod predstavlja odgovor od našeg servisa. To smo i želeli. Sada možemo da otvorimo ovaj servis da bude dostupan van klastera, u lokalnoj mreži.

Prilaz servisu van klastera

Sada ćemo da pokrenemo primer Ingress kontrolera koji će za nas da obrađuje zahteve sa spoljneg sveta tj. u našem slučaju sa lokalne mreže i usmerava ih na naš servis. Takođe ćemo koristiti Traefik kao load balancer. Za razliku od Docker Swarm-a, Kubernetes nema opciju da definišemo specifičan port na kome će servis biti dostupan. Ovo je bila bitna odluka jer je cilj da rutiranje dolazećih zahteva bude obrađeno od strane trećeg lica kao što je load balancer ili webserver ali ne sam kubernetes. Kubernetes treba biti nadogradiv, da podstiče druge da za njega prave alate koji će proširivati njegovu funkcionalnost. Ingress je način na koji možemo da otvorimo service ka spoljnom svetu. Prvo treba da pokrenemo Traefik kao load balancer-a. Za to pokrećemo komandu na masteru:

  kubectl apply -f https://raw.githubusercontent.com/hypriot/rpi-traefik/master/traefik-k8s-example.yaml

Onda treba da označimo koji nod želimo da bude load balancer. Tada će Traefik Ingress kontroler biti prisutan na specificiranom nodu. Pokrećemo na masteru:

  kubectl label node  nginx-controller=traefik

Gde umesto teksta u zagradama <> unosimo naziv noda, u našem slučaju n1 ili n2. Sada pravimo Ingress objekat koji će omogućiti Traefik-u da osluškuje na portu 80 za hypriot servis. Pokrećemo komandu:

  cat > hypriot-ingress.yaml <<EOF
  apiVersion: extensions/v1beta1
  kind: Ingress
  metadata:
    name: hypriot
  spec:
    rules:
    - http:
        paths:
        - path: /
          backend:
            serviceName: hypriot
            servicePort: 80
  EOF

Nakon toga pokrećemo napravljenu yaml skriptu komandom:

  kubectl apply -f hypriot-ingress.yaml

Sada možemo proveriti da li su svi podovi za hypriot servis pokrenuti komandom:

  kubectl get pods

Izlaz iz komande bi trebalo da bude sličan sledećem:

  NAME                        READY     STATUS    RESTARTS   AGE
  hostnames-632524106-bbpiw   1/1       Running   0          2m
  hostnames-632524106-ly40y   1/1       Running   0          2m
  hostnames-632524106-tlaok   1/1       Running   0          2m

Ako jesu možemo uneti adresu našeg noda koji smo zadali Ingress-u u pretraživač i videti web stranicu koju je generisao html kod od ranije. Web stranica izgleda ovako:

Kubernetes Dashboard korisnički interfejs

Ukoliko želimo korisnički interfejs za naš Kubernetes klaster kako bi sebi olakšali i ubrzali kontrolu i zadatke koje želimo obavljati na kubernetesu, potrebno je da ga instaliramo. Pored toga instaliraćemo i Heapster paket koji će nam omogućiti grafički prikaz procesorske i memorijske iskorišćenosti. Na masteru pokrećemo sledeće komande:

  curl -sSL https://git.io/kube-dashboard | sed "s|image:.*|image: luxas/kubernetes-dashboard:v1.6.3|" | kubectl apply -f -
  git clone https://github.com/kubernetes/heapster.git heapster

Pošto je arhitektura naših uređaja ARM a ne AMD64 potrebno je da u fajlovima foldera ~/heapster/deploy/kube-config/influxdb i foldera ~/heapster/deploy/kube-config/rbac svuda gde se pojavljuje amd64 zamenimo sa arm. Nakon što smo izvršili tu izmenu potrebno je da izvršimo sledeće komande:

  kubectl create -f deploy/kube-config/influxdb/
  kubectl create -f deploy/kube-config/rbac/heapster-rbac.yaml

Sada bi trebalo da možemo da vidimo pokrenute servise u kube-system namespace-u. Da bi proverili pokrećemo komandu:

  kubectl -n kube-system get svc

Sada možemo proveriti zauzeće procesora i memorije komandom:

  kubectl top nodes

Trebalo bi da dobijemo izpis sličan sledećem:

  NAME      CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%   
  n1        258m         6%        498Mi           65%       
  n2        587m         14%       539Mi           70%       
  n0        910m         22%       622Mi           81%     

Prilaz Dashboard-u van klastera

Pošto smo definisali korisnički interfejs bilo bi lepo i korisno da možemo bilo gde da mu pristupimo. Ranije smo koristili Ingress kontroler Traefik. Sada ćemo isto učiniti ali ćemo otići i korak kasnije kako bi videli korisnički interfejs sa bilo koje mreže povezane na internet. Da ne bi komplikovali situaciju dodatno sa Port Forwarding-om možemo jednostavno upotrebiti Ngrok program da bi zahteve prosleđene u njegov domen prosledi našem Traefik Ingress kontroleru. Ova opcija je odlična za nas i za hibridne klastere gde ne postoji kontrola nad mrežom na koju smo povezani već samo postoji prisutna intenet konekcija i to je to. Takođe ovakav pristup se može koristiti u dosta različitih scenarija sa identičnim rezultatom. Ipak za ozbiljno rešenje i softver koji će biti komercijalan ovo nije pravi pristup, ali mi ne moramo brinuti o tome. Pokrećemo komande u home direktorijumu mastera:

  git clone https://github.com/luxas/kubeadm-workshop.git kubeadm-workshop
  cd kubeadm-workshop
  kubectl apply -f demos/loadbalancing/traefik-common.yaml
  kubectl apply -f demos/loadbalancing/traefik-ngrok.yaml
  curl -sSL $(kubectl -n kube-system get svc ngrok -o template --template "{{.spec.clusterIP}}")/api/tunnels | jq  ".tunnels[].public_url" | sed 's/"//g;/http:/d'

Poslednja komanda bi trebalo da nam kao izlaz da link ka servisu koji smo napravili. Ukoliko otvorimo taj link dobićemo obaveštenje da stranica nije pronađena. To je zato što moramo da napravimo Ingress pravilo koje će naš korisnički interfejs vezati za adresu koju smo dobili. To ćemo učiniti unošenjem komande na masteru:

  kubectl apply -f demos/dashboard/ingress.yaml

Traefik Ingress kontroler je namešten da od nas traži autentifikaciju pre nego što možemo pristupiti servisu, ipak ne želimo da svako može imati pristup našem klasteru. Podrazumevano korisničko ime je kubernetes a lozinka rocks!. Ovo možemo promeniti pre nego što pokrenemo naše Ingress pravilo. Sada možemo pristupiti našem servisu sa bilo koje mreže povezane na internet preko sledeće adrese:

  https://{ngrok url}/dashboard/

gde tekst u zagradama {} trebamo zameniti sa adresom koju smo dobili kao izlaz prilikom pravljenja Ngrok-a. Rezultat je web stranica koja izgleda slično kao na slici:

Dodatno

Ukoliko želimo dodatne funkcionalnosti svakako ih možemo dodati na naš klaster. Kao što je ranije napomenuto Kubernetes je baš dizajniran kako bi omogućio dodavanje funkcionalnosti po potrebi. Neke od njih mogu biti:

  • Dodavanje stalne memorije na kubernetes uz pomoć Rook-a
  • Deploy InfluxDB i Grafana za prikazivanje i vizuelizaciju CPU i memorije tokom vremena
  • Proširivanje API-ja
  • Dodavanje Prometheus Operator-a za monitoring servisa u klasteru

Korisni linkovi

Za dodatne informacije možete čitati sadržaj sledećih linkova koji su bili inspiracija i pomoć prilikom kreiranja ovog projekta: