Minimalno održivi Kubernetes

Prijevod članka pripremljen je uoči početka tečaja "DevOps prakse i alati".

Minimalno održivi Kubernetes

Ako ovo čitate, vjerojatno ste čuli nešto o Kubernetesu (a ako niste, kako ste završili ovdje?) Ali što je zapravo Kubernetes? Ovaj “Orkestracija industrijskih kontejnera”? Ili "Operacijski sustav izvorni u oblaku"? Što ovo uopće znači?

Da budem iskren, nisam 100% siguran. Ali mislim da je zanimljivo kopati po unutrašnjosti i vidjeti što se stvarno događa u Kubernetesu ispod njegovih brojnih slojeva apstrakcija. Dakle, samo zabave radi, pogledajmo kako zapravo izgleda minimalni "Kubernetes klaster". (Ovo će biti puno lakše nego Kubernetes na težak način.)

Pretpostavljam da imate osnovno znanje o Kubernetesu, Linuxu i kontejnerima. Sve o čemu ovdje govorimo samo je u svrhu istraživanja/učenja, nemojte ništa od toga stavljati u proizvodnju!

Pregled

Kubernetes sadrži mnoge komponente. Prema Wikipedia, arhitektura izgleda ovako:

Minimalno održivi Kubernetes

Ovdje je prikazano najmanje osam komponenti, ali zanemarit ćemo većinu njih. Želim reći da se minimalna stvar koja se razumno može nazvati Kubernetes sastoji od tri glavne komponente:

  • kubelet
  • kube-apiserver (što ovisi o etcd - njegovoj bazi podataka)
  • runtime spremnika (Docker u ovom slučaju)

Pogledajmo što dokumentacija kaže o svakom od njih (rus., Engleski.). Isprva kubelet:

Agent koji radi na svakom čvoru u klasteru. Osigurava da spremnici rade u kapsuli.

Zvuči dovoljno jednostavno. O čemu? vrijeme izvođenja spremnika (vrijeme izvođenja spremnika)?

Vrijeme izvođenja spremnika je program dizajniran za pokretanje spremnika.

Vrlo informativno. Ali ako ste upoznati s Dockerom, trebali biste imati opću predodžbu o tome što radi. (Detalji o razdvajanju odgovornosti između runtimea spremnika i kubeleta zapravo su prilično suptilni i neću ulaziti u njih ovdje.)

И API poslužitelj?

API poslužitelj je komponenta Kubernetes upravljačke ploče koja izlaže Kubernetes API. API poslužitelj je klijentska strana Kubernetes upravljačke ploče

Svatko tko je ikada radio bilo što s Kubernetesom morao je komunicirati s API-jem izravno ili putem kubectla. Ovo je srce onoga što čini Kubernetes Kubernetes - mozak koji pretvara planine YAML-a koje svi poznajemo i volimo (?) u funkcionalnu infrastrukturu. Čini se očiglednim da bi API trebao biti prisutan u našoj minimalnoj konfiguraciji.

Preduvjeti

  • Linux virtualni ili fizički stroj s root pristupom (koristim Ubuntu 18.04 na virtualnom stroju).
  • I sve je to!

Dosadna instalacija

Moramo instalirati Docker na stroj koji ćemo koristiti. (Neću ići u detalje o tome kako rade Docker i kontejneri; ako ste zainteresirani, postoji divni članci). Samo ga instalirajmo s apt:

$ sudo apt install docker.io
$ sudo systemctl start docker

Nakon toga moramo dobiti Kubernetes binarne datoteke. Zapravo, za početno pokretanje našeg "klastera" trebamo samo kubelet, budući da možemo koristiti druge komponente poslužitelja kubelet. Za interakciju s našim klasterom nakon što se pokrene, također ćemo koristiti kubectl.

$ curl -L https://dl.k8s.io/v1.18.5/kubernetes-server-linux-amd64.tar.gz > server.tar.gz
$ tar xzvf server.tar.gz
$ cp kubernetes/server/bin/kubelet .
$ cp kubernetes/server/bin/kubectl .
$ ./kubelet --version
Kubernetes v1.18.5

Što se događa ako samo trčimo kubelet?

$ ./kubelet
F0609 04:03:29.105194    4583 server.go:254] mkdir /var/lib/kubelet: permission denied

kubelet mora se izvoditi kao root. Sasvim logično, jer treba upravljati cijelim čvorom. Pogledajmo njegove parametre:

$ ./kubelet -h
<слишком много строк, чтобы разместить здесь>
$ ./kubelet -h | wc -l
284

Vau, toliko opcija! Srećom, trebamo ih samo par. Evo jednog od parametara koji nas zanima:

--pod-manifest-path string

Put do direktorija koji sadrži datoteke za statične podove ili put do datoteke koja opisuje statičke podove. Datoteke koje počinju točkama se zanemaruju. (ZASTARJELO: Ova se opcija mora postaviti u konfiguracijskoj datoteci proslijeđenoj Kubelet-u putem opcije --config. Za više informacija pogledajte kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Ova nam opcija omogućuje trčanje statične mahune — podovi kojima se ne upravlja putem Kubernetes API-ja. Statične mahune se rijetko koriste, ali su vrlo zgodne za brzo podizanje grozda, a to je upravo ono što nam treba. Ignorirat ćemo ovo veliko upozorenje (opet, nemojte ovo pokretati u produkciji!) i vidjeti možemo li pokrenuti pod.

Najprije ćemo stvoriti direktorij za statične mahune i pokrenuti kubelet:

$ mkdir pods
$ sudo ./kubelet --pod-manifest-path=pods

Zatim, u drugom terminalu/tmux prozoru/što god, stvorit ćemo manifest mahunarke:

$ cat <<EOF > pods/hello.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello
spec:
  containers:
  - image: busybox
    name: hello
    command: ["echo", "hello world!"]
EOF

kubelet počne pisati neka upozorenja i čini se kao da se ništa ne događa. Ali to nije istina! Pogledajmo Docker:

$ sudo docker ps -a
CONTAINER ID        IMAGE                  COMMAND                 CREATED             STATUS                      PORTS               NAMES
8c8a35e26663        busybox                "echo 'hello world!'"   36 seconds ago      Exited (0) 36 seconds ago                       k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
68f670c3c85f        k8s.gcr.io/pause:3.2   "/pause"                2 minutes ago       Up 2 minutes                                    k8s_POD_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_0
$ sudo docker logs k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
hello world!

kubelet Pročitao sam manifest kapsule i dao Dockeru naredbu da pokrene nekoliko spremnika prema našim specifikacijama. (Ako se pitate o spremniku "pause", to je Kubernetes hack - pogledajte ovaj blog.) Kubelet će pokrenuti naš kontejner busybox s navedenom naredbom i ponovno će ga pokrenuti na neodređeno vrijeme dok se statički modul ne izbriše.

Čestitajte sebi. Upravo smo smislili jedan od najzbunjujućih načina za ispis teksta na terminal!

Pokreni itd

Naš krajnji cilj je pokrenuti Kubernetes API, ali da bismo to učinili prvo se moramo pokrenuti itd. Pokrenimo minimalni etcd klaster postavljanjem njegovih postavki u pods direktorij (na primjer, pods/etcd.yaml):

apiVersion: v1
kind: Pod
metadata:
  name: etcd
  namespace: kube-system
spec:
  containers:
  - name: etcd
    command:
    - etcd
    - --data-dir=/var/lib/etcd
    image: k8s.gcr.io/etcd:3.4.3-0
    volumeMounts:
    - mountPath: /var/lib/etcd
      name: etcd-data
  hostNetwork: true
  volumes:
  - hostPath:
      path: /var/lib/etcd
      type: DirectoryOrCreate
    name: etcd-data

Ako ste ikada radili s Kubernetesom, ove YAML datoteke bi vam trebale biti poznate. Ovdje su vrijedne pažnje samo dvije točke:

Postavili smo host mapu /var/lib/etcd u pod tako da se etcd podaci sačuvaju nakon ponovnog pokretanja (ako se to ne učini, stanje klastera će se izbrisati svaki put kada se pod ponovno pokrene, što neće biti dobro čak ni za minimalnu instalaciju Kubernetesa).

Instalirali smo hostNetwork: true. Ova postavka, nimalo iznenađujuće, konfigurira etcd da koristi mrežu domaćina umjesto interne mreže modula (ovo će olakšati API poslužitelju da pronađe etcd klaster).

Jednostavna provjera pokazuje da etcd doista radi na lokalnom hostu i sprema podatke na disk:

$ curl localhost:2379/version
{"etcdserver":"3.4.3","etcdcluster":"3.4.0"}
$ sudo tree /var/lib/etcd/
/var/lib/etcd/
└── member
    ├── snap
    │   └── db
    └── wal
        ├── 0.tmp
        └── 0000000000000000-0000000000000000.wal

Pokretanje API poslužitelja

Pokretanje Kubernetes API poslužitelja još je lakše. Jedini parametar koji treba proslijediti je --etcd-servers, radi ono što očekujete:

apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - name: kube-apiserver
    command:
    - kube-apiserver
    - --etcd-servers=http://127.0.0.1:2379
    image: k8s.gcr.io/kube-apiserver:v1.18.5
  hostNetwork: true

Postavite ovu YAML datoteku u direktorij pods, i API poslužitelj će se pokrenuti. Provjera sa curl pokazuje da Kubernetes API sluša port 8080 s potpuno otvorenim pristupom - nije potrebna provjera autentičnosti!

$ curl localhost:8080/healthz
ok
$ curl localhost:8080/api/v1/pods
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "selfLink": "/api/v1/pods",
    "resourceVersion": "59"
  },
  "items": []
}

(Opet, nemojte ovo pokretati u produkciji! Malo sam se iznenadio da je zadana postavka tako nesigurna. Ali pretpostavljam da je ovo zbog lakšeg razvoja i testiranja.)

I, ugodno iznenađenje, kubectl radi izvan kutije bez dodatnih postavki!

$ ./kubectl version
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:47:41Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:39:24Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
$ ./kubectl get pod
No resources found in default namespace.

problem

Ali ako kopate malo dublje, čini se da nešto nije u redu:

$ ./kubectl get pod -n kube-system
No resources found in kube-system namespace.

Nestale su statične mahune koje smo stvorili! Zapravo, naš kubelet čvor uopće nije otkriven:

$ ./kubectl get nodes
No resources found in default namespace.

Što je bilo? Ako se sjećate, prije nekoliko odlomaka pokrenuli smo kubelet s iznimno jednostavnim skupom parametara naredbenog retka, tako da kubelet ne zna kako kontaktirati API poslužitelj i obavijestiti ga o svom stanju. Nakon proučavanja dokumentacije nalazimo odgovarajuću zastavu:

--kubeconfig string

Put do datoteke kubeconfig, koji navodi kako se povezati s API poslužiteljem. Dostupnost --kubeconfig omogućuje način API poslužitelja, br --kubeconfig omogućuje izvanmrežni način rada.

Sve to vrijeme, a da toga nismo znali, izvodili smo kubelet u "izvanmrežnom načinu". (Da smo pedantni, mogli bismo zamisliti samostalni kubelet kao "minimalno održivi Kubernetes", ali to bi bilo vrlo dosadno). Da bi "prava" konfiguracija radila, moramo proslijediti datoteku kubeconfig kubeletu kako bi znao kako komunicirati s API poslužiteljem. Srećom, prilično je jednostavno (budući da nemamo problema s autentifikacijom ili certifikatom):

apiVersion: v1
kind: Config
clusters:
- cluster:
    server: http://127.0.0.1:8080
  name: mink8s
contexts:
- context:
    cluster: mink8s
  name: mink8s
current-context: mink8s

Spremi ovo kao kubeconfig.yaml, ubiti proces kubelet i ponovno pokrenite s potrebnim parametrima:

$ sudo ./kubelet --pod-manifest-path=pods --kubeconfig=kubeconfig.yaml

(Usput, ako pokušate pristupiti API-ju putem curla kada kubelet nije pokrenut, vidjet ćete da još uvijek radi! Kubelet nije "roditelj" svojih mahuna kao Docker, više je poput "kontrole" daemon.” Spremnici kojima upravlja kubelet nastavit će raditi dok ih kubelet ne zaustavi.)

Za par minuta kubectl trebali bi nam pokazati mahune i čvorove kako očekujemo:

$ ./kubectl get pods -A
NAMESPACE     NAME                    READY   STATUS             RESTARTS   AGE
default       hello-mink8s            0/1     CrashLoopBackOff   261        21h
kube-system   etcd-mink8s             1/1     Running            0          21h
kube-system   kube-apiserver-mink8s   1/1     Running            0          21h
$ ./kubectl get nodes -owide
NAME     STATUS   ROLES    AGE   VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIME
mink8s   Ready    <none>   21h   v1.18.5   10.70.10.228   <none>        Ubuntu 18.04.4 LTS   4.15.0-109-generic   docker://19.3.6

Čestitajmo si ovaj put (znam da sam si već čestitao) - imamo minimalni Kubernetes "klaster" koji radi s potpuno funkcionalnim API-jem!

Pokrećemo pod

Sada da vidimo za što je API sposoban. Počnimo s nginx podom:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx

Ovdje dobivamo prilično zanimljivu grešku:

$ ./kubectl apply -f nginx.yaml
Error from server (Forbidden): error when creating "nginx.yaml": pods "nginx" is
forbidden: error looking up service account default/default: serviceaccount
"default" not found
$ ./kubectl get serviceaccounts
No resources found in default namespace.

Ovdje vidimo koliko je naše Kubernetes okruženje užasno nepotpuno - nemamo računa za usluge. Pokušajmo ponovno ručnim stvaranjem računa usluge i vidimo što će se dogoditi:

$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
EOS
serviceaccount/default created
$ ./kubectl apply -f nginx.yaml
Error from server (ServerTimeout): error when creating "nginx.yaml": No API
token found for service account "default", retry after the token is
automatically created and added to the service account

Čak i kada smo račun usluge kreirali ručno, token za provjeru autentičnosti nije generiran. Dok nastavljamo eksperimentirati s našim minimalističkim "klasterom", otkrit ćemo da će nedostajati većina korisnih stvari koje se obično događaju automatski. Kubernetes API poslužitelj prilično je minimalistički, s većinom teškog podizanja i automatske konfiguracije koja se događa u raznim kontrolerima i pozadinskim poslovima koji još nisu pokrenuti.

Ovaj problem možemo riješiti postavljanjem opcije automountServiceAccountToken za račun usluge (budući da ga ionako nećemo morati koristiti):

$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
automountServiceAccountToken: false
EOS
serviceaccount/default configured
$ ./kubectl apply -f nginx.yaml
pod/nginx created
$ ./kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
nginx   0/1     Pending   0          13m

Napokon se pojavila mahuna! Ali zapravo neće krenuti jer nemamo planer (planer) još je jedna važna komponenta Kubernetesa. Opet, vidimo da je Kubernetes API iznenađujuće "glup" - kada kreirate Pod u API-ju, on ga registrira, ali ne pokušava otkriti na kojem čvoru ga pokrenuti.

Zapravo, ne trebate planer da biste pokrenuli pod. Možete ručno dodati čvor manifestu u parametru nodeName:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
  nodeName: mink8s

(Zamijeniti mink8s na naziv čvora.) Nakon brisanja i primjene, vidimo da je nginx pokrenut i sluša internu IP adresu:

$ ./kubectl delete pod nginx
pod "nginx" deleted
$ ./kubectl apply -f nginx.yaml
pod/nginx created
$ ./kubectl get pods -owide
NAME    READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          30s   172.17.0.2   mink8s   <none>           <none>
$ curl -s 172.17.0.2 | head -4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

Kako bismo bili sigurni da mreža između mahuna radi ispravno, možemo pokrenuti curl iz druge mahune:

$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: curl
spec:
  containers:
  - image: curlimages/curl
    name: curl
    command: ["curl", "172.17.0.2"]
  nodeName: mink8s
EOS
pod/curl created
$ ./kubectl logs curl | head -6
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

Vrlo je zanimljivo kopati po ovom okruženju i vidjeti što funkcionira, a što ne. Otkrio sam da ConfigMap i Secret rade prema očekivanjima, ali Service i Deployment ne.

Uspjeh!

Ovaj post postaje dug, pa ću proglasiti pobjedu i reći da je ovo održiva konfiguracija koja se može nazvati "Kubernetes". Da sažmemo: četiri binarne datoteke, pet parametara naredbenog retka i "samo" 45 redaka YAML-a (ne toliko prema standardima Kubernetes) i imamo dosta stvari koje rade:

  • Podovima se upravlja pomoću uobičajenog Kubernetes API-ja (uz nekoliko hakova)
  • Možete prenijeti javne slike spremnika i upravljati njima
  • Podovi ostaju živi i automatski se ponovno pokreću
  • Umrežavanje između mahuna unutar istog čvora funkcionira prilično dobro
  • ConfigMap, Secret i jednostavno montiranje pohrane rade prema očekivanjima

Ali mnogo toga što Kubernetes čini uistinu korisnim još uvijek nedostaje, poput:

  • Pod Planer
  • Autentifikacija/autorizacija
  • Višestruki čvorovi
  • Mreža usluga
  • Grupirani interni DNS
  • Kontrolori za račune usluga, implementacije, integraciju s pružateljima usluga u oblaku i većinu drugih pogodnosti koje donosi Kubernetes

Dakle, što smo zapravo dobili? Kubernetes API, koji radi samostalno, zapravo je samo platforma za automatizacija kontejnera. Ne čini puno - to je posao za razne kontrolere i operatere koji koriste API - ali pruža dosljedno okruženje za automatizaciju.

Saznajte više o tečaju na besplatnom webinaru.

Čitaj više:

Izvor: www.habr.com

Dodajte komentar