Minimálne životaschopné Kubernetes

Preklad článku bol pripravený v predstihu pred začiatkom kurzu "Postupy a nástroje DevOps".

Minimálne životaschopné Kubernetes

Ak toto čítate, určite ste už niečo o Kubernetes počuli (a ak nie, ako ste sa sem dostali?) Ale čo to vlastne Kubernetes je? Toto „Organizácia kontajnerov priemyselnej kvality“? Alebo "Cloudový natívny operačný systém"? Čo to vôbec znamená?

Úprimne povedané, nie som si 100% istý. Myslím si však, že je zaujímavé prehrabať sa vo vnútornom prostredí a zistiť, čo sa skutočne deje v Kubernetes pod jeho mnohými vrstvami abstrakcií. Poďme sa teda len tak pre zaujímavosť pozrieť na to, ako v skutočnosti vyzerá minimálny „klaster Kubernetes“. (Bude to oveľa jednoduchšie ako Kubernetes The Hard Way.)

Predpokladám, že máte základné znalosti o Kubernetes, Linuxe a kontajneroch. Všetko, o čom tu hovoríme, slúži len na výskumné/vzdelávacie účely, nič z toho nezadávajte do výroby!

Recenzia

Kubernetes obsahuje veľa komponentov. Podľa wikipedia, architektúra vyzerá takto:

Minimálne životaschopné Kubernetes

Je tu zobrazených najmenej osem komponentov, ale väčšinu z nich budeme ignorovať. Chcem povedať, že minimálna vec, ktorú možno rozumne nazvať Kubernetes, pozostáva z troch hlavných komponentov:

  • kocka
  • kube-apiserver (ktorý závisí od etcd - jeho databázy)
  • runtime kontajnera (v tomto prípade Docker)

Pozrime sa, čo dokumentácia hovorí o každom z nich (ruský., Angličtina.). Najprv kocka:

Agent bežiaci na každom uzle v klastri. Zabezpečuje, že kontajnery bežia v kapsule.

Znie to dosť jednoducho. Čo takto doby chodu kontajnera (doba spustenia kontajnera)?

Kontajnerový runtime je program určený na spustenie kontajnerov.

Veľmi informatívne. Ale ak poznáte Docker, mali by ste mať všeobecnú predstavu o tom, čo robí. (Podrobnosti o rozdelení zodpovedností medzi prevádzkový čas kontajnera a kubelet sú v skutočnosti dosť jemné a nebudem ich tu rozoberať.)

И API server?

API Server je komponent ovládacieho panela Kubernetes, ktorý odhaľuje rozhranie API Kubernetes. Server API je klientskou stranou ovládacieho panela Kubernetes

Každý, kto niekedy robil niečo s Kubernetes, musel interagovať s API buď priamo, alebo cez kubectl. Toto je srdce toho, čo robí Kubernetes Kubernetes – mozog, ktorý premieňa hory YAML, ktoré všetci poznáme a milujeme (?), na fungujúcu infraštruktúru. Zdá sa zrejmé, že API by malo byť prítomné v našej minimálnej konfigurácii.

Predpoklady

  • Virtuálny alebo fyzický počítač Linux s prístupom root (na virtuálnom počítači používam Ubuntu 18.04).
  • A to je všetko!

Nudná inštalácia

Na stroj, ktorý budeme používať, musíme nainštalovať Docker. (Nebudem zachádzať do podrobností o tom, ako funguje Docker a kontajnery; ak máte záujem, je úžasné články). Nainštalujte si ho s apt:

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

Potom musíme získať binárne súbory Kubernetes. V skutočnosti na prvé spustenie nášho „klastra“ potrebujeme iba my kubelet, keďže na spustenie iných serverových komponentov môžeme použiť kubelet. Na interakciu s naším klastrom po jeho spustení tiež použijeme 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

Čo sa stane, ak jednoducho utečieme kubelet?

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

kubelet musí bežať ako root. Celkom logické, keďže potrebuje spravovať celý uzol. Pozrime sa na jeho parametre:

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

Wow, toľko možností! Našťastie ich potrebujeme len pár. Tu je jeden z parametrov, ktorý nás zaujíma:

--pod-manifest-path string

Cesta k adresáru obsahujúcemu súbory pre statické moduly alebo cesta k súboru popisujúcim statické moduly. Súbory začínajúce bodkami sú ignorované. (ZASTARANÉ: Táto možnosť musí byť nastavená v konfiguračnom súbore odovzdanom do Kubeletu cez voľbu --config. Viac informácií nájdete na kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Táto možnosť nám umožňuje beh statické struky — moduly, ktoré nie sú spravované prostredníctvom rozhrania Kubernetes API. Statické struky sa používajú zriedka, ale sú veľmi vhodné na rýchle zdvihnutie klastra, a to je presne to, čo potrebujeme. Toto veľké varovanie budeme ignorovať (opäť to nespúšťajte vo výrobe!) a uvidíme, či dokážeme spustiť modul.

Najprv si vytvoríme adresár pre statické pody a spustíme kubelet:

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

Potom v inom termináli/okne tmux/čokoľvek vytvoríme manifest pod:

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

kubelet začne písať nejaké varovania a zdá sa, že sa nič nedeje. Ale to nie je pravda! Pozrime sa na 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 Prečítal som si manifest podu a dal Dockerovi príkaz na spustenie niekoľkých kontajnerov podľa našich špecifikácií. (Ak vás zaujíma kontajner „pauza“, ide o hack Kubernetes – viď tento blog.) Kubelet spustí náš kontajner busybox so zadaným príkazom a reštartuje ho na neurčito, kým sa neodstráni statický modul.

Gratulujem si. Práve sme prišli s jedným z najviac mätúcich spôsobov výstupu textu do terminálu!

Spustiť atď

Naším konečným cieľom je spustiť Kubernetes API, ale aby sme to dosiahli, musíme ho najprv spustiť atď. Začnime minimálny klaster etcd umiestnením jeho nastavení do adresára pods (napr. 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

Ak ste niekedy pracovali s Kubernetes, tieto súbory YAML by vám mali byť známe. Tu stoja za zmienku len dva body:

Pripojili sme hostiteľský priečinok /var/lib/etcd v pod, aby sa údaje etcd po reštarte zachovali (ak sa tak nestane, stav klastra sa vymaže pri každom reštarte podu, čo nebude dobré ani pri minimálnej inštalácii Kubernetes).

Máme nainštalované hostNetwork: true. Toto nastavenie, neprekvapivo, konfiguruje etcd tak, aby používala hostiteľskú sieť namiesto internej siete podu (to uľahčí API serveru nájsť klaster etcd).

Jednoduchá kontrola ukazuje, že etcd skutočne beží na localhost a ukladá dáta 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

Spustenie servera API

Spustenie servera Kubernetes API je ešte jednoduchšie. Jediný parameter, ktorý je potrebné odovzdať, je --etcd-servers, robí to, čo očakávate:

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

Umiestnite tento súbor YAML do adresára podsa spustí sa server API. Kontrola s curl ukazuje, že Kubernetes API počúva na porte 8080 s úplne otvoreným prístupom – nevyžaduje sa žiadna autentifikácia!

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

(Opäť to nespúšťajte v produkcii! Bol som trochu prekvapený, že predvolené nastavenie je také neisté. Ale predpokladám, že to má uľahčiť vývoj a testovanie.)

A, príjemné prekvapenie, kubectl funguje hneď po vybalení bez akýchkoľvek ďalších nastavení!

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

problém

Ak sa však ponoríte trochu hlbšie, zdá sa, že niečo nie je v poriadku:

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

Statické moduly, ktoré sme vytvorili, sú preč! V skutočnosti náš uzol kubelet nie je vôbec objavený:

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

Čo sa deje? Ak si pamätáte pred niekoľkými odsekmi, spustili sme kubelet s extrémne jednoduchou sadou parametrov príkazového riadku, takže kubelet nevie, ako kontaktovať API server a upozorniť ho na jeho stav. Po preštudovaní dokumentácie nájdeme zodpovedajúci príznak:

--kubeconfig string

Cesta k súboru kubeconfig, ktorý určuje, ako sa pripojiť k serveru API. Dostupnosť --kubeconfig umožňuje režim servera API, nie --kubeconfig umožňuje offline režim.

Celý ten čas, bez toho, aby sme o tom vedeli, sme kubelet spúšťali v „režime offline“. (Ak by sme boli pedantskí, mohli by sme si predstaviť samostatný kubelet ako „minimálne životaschopné Kubernetes“, ale to by bola veľká nuda). Aby „skutočná“ konfigurácia fungovala, musíme odovzdať súbor kubeconfig do kubeletu, aby vedel, ako komunikovať so serverom API. Našťastie je to celkom jednoduché (keďže nemáme žiadne problémy s autentifikáciou alebo certifikátom):

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

Uložte to ako kubeconfig.yaml, zabiť proces kubelet a reštartujte s potrebnými parametrami:

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

(Mimochodom, ak sa pokúsite pristupovať k API cez curl, keď kubelet nebeží, zistíte, že stále beží! Kubelet nie je „rodičom“ svojich modulov ako Docker, je to skôr „ovládací prvok“ démon.“ Kontajnery spravované kubeletom budú pokračovať v prevádzke, kým ich kubelet nezastaví.)

O pár minút kubectl by nám mal ukázať moduly a uzly, ako očakávame:

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

Tentoraz si naozaj pogratulujme (viem, že už som si gratuloval) – máme spustený minimálny „klaster“ Kubernetes s plne funkčným API!

Štartujeme pod

Teraz sa pozrime, čo API dokáže. Začnime s nginx pod:

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

Tu dostávame celkom zaujímavú chybu:

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

Tu vidíme, aké je naše prostredie Kubernetes žalostne neúplné – nemáme žiadne účty pre služby. Skúsme to znova manuálnym vytvorením účtu služby a uvidíme, čo sa stane:

$ 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

Aj keď sme účet služby vytvorili manuálne, overovací token sa nevygeneruje. Keď budeme pokračovať v experimentovaní s naším minimalistickým „klastrom“, zistíme, že väčšina užitočných vecí, ktoré sa zvyčajne dejú automaticky, bude chýbať. Server Kubernetes API je pomerne minimalistický, pričom väčšina náročných úloh a automatickej konfigurácie prebieha v rôznych ovládačoch a úlohách na pozadí, ktoré ešte nie sú spustené.

Tento problém môžeme obísť nastavením možnosti automountServiceAccountToken pre účet služby (keďže ho aj tak nebudeme musieť použiť):

$ 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

Konečne sa objavil lusk! Ale v skutočnosti sa nezačne, pretože nemáme plánovač (plánovač) je ďalšou dôležitou súčasťou Kubernetes. Opäť vidíme, že Kubernetes API je prekvapivo „hlúpe“ – keď vytvoríte Pod v API, zaregistruje ho, no nesnaží sa zistiť, na akom uzle ho spustiť.

V skutočnosti nepotrebujete plánovač na spustenie modulu. Uzol môžete manuálne pridať do manifestu v parametri nodeName:

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

(Nahradiť mink8s na názov uzla.) Po odstránení a použití vidíme, že nginx sa spustil a počúva internú 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>

Aby sme sa uistili, že sieť medzi modulmi funguje správne, môžeme spustiť curl z iného modulu:

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

Je celkom zaujímavé pohrabať sa v tomto prostredí a zistiť, čo funguje a čo nie. Zistil som, že ConfigMap a Secret fungujú podľa očakávania, ale Service a Deployment nie.

Úspech!

Tento príspevok sa preťahuje, takže vyhlásim víťazstvo a poviem, že toto je životaschopná konfigurácia, ktorú možno nazvať „Kubernetes“. Aby som to zhrnul: štyri binárne súbory, päť parametrov príkazového riadku a „iba“ 45 riadkov YAML (nie toľko podľa štandardov Kubernetes) a funguje nám pomerne málo vecí:

  • Pody sa spravujú pomocou bežného rozhrania Kubernetes API (s niekoľkými hackmi)
  • Môžete nahrávať a spravovať obrázky verejných kontajnerov
  • Moduly zostávajú nažive a automaticky sa reštartujú
  • Sieťovanie medzi modulmi v rámci toho istého uzla funguje celkom dobre
  • ConfigMap, tajná a jednoduchá montáž úložiska fungujú podľa očakávania

Stále však chýba veľa z toho, čo robí Kubernetes skutočne užitočným, ako napríklad:

  • Plánovač pod
  • Autentifikácia/autorizácia
  • Viacero uzlov
  • Sieť služieb
  • Klastrovaný interný DNS
  • Ovládače pre účty služieb, nasadenia, integráciu s poskytovateľmi cloudu a väčšinu ďalších vychytávok, ktoré Kubernetes prináša

Čo sme teda vlastne dostali? Kubernetes API, ktoré beží samostatne, je skutočne len platforma pre kontajnerová automatizácia. Nerobí toho veľa – je to práca pre rôznych kontrolórov a operátorov pomocou API – ale poskytuje konzistentné prostredie pre automatizáciu.

Viac o kurze sa dozviete na bezplatnom webinári.

Čítaj viac:

Zdroj: hab.com

Pridať komentár