Ú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:
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:
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.
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:
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):
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:
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!
(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):
(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:
$ ./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:
(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.