Minimálně životaschopný Kubernetes

Překlad článku byl vyhotoven před začátkem kurzu „DevOps praktiky a nástroje“.

Minimálně životaschopný Kubernetes

Pokud to čtete, pravděpodobně jste už něco slyšeli o Kubernetes (a pokud ne, jak jste se sem dostali?). Ale co vlastně Kubernetes je? To je „Orchestrace kontejnerů průmyslové úrovně“? Или „Cloud-Native operační systém“? Что вообще это значит?

Upřímně řečeno, nejsem si tím úplně jistý. Ale myslím, že je zajímavé nahlédnout pod pokličku a podívat se, co se vlastně děje v Kubernetes pod jeho mnoha vrstvami abstrakce. Takže pro zajímavost, pojďme se podívat, jak vlastně vypadá minimální "Kubernetes cluster". (Bude to mnohem jednodušší než Kubernetes The Hard Way.)

Předpokládám, že máte základní znalosti o Kubernetes, Linuxu a kontejnerech. Vše, co zde budeme řešit, je určeno pouze k exploraci / studiu, nespouštějte to v produkci!

Přehled

Kubernetes obsahuje mnoho komponent. Podle Wikipedie, architektura vypadá takto:

Minimálně životaschopný Kubernetes

Zde je zobrazeno minimálně osm komponent, ale většinu z nich ignorujeme. Chci prohlásit, že minimální věc, kterou lze rozumně nazvat Kubernetes, se skládá ze tří hlavních komponent:

  • kubelet
  • kube-apiserver (který závisí na etcd — jeho databázi)
  • runtime kontejnerů (v tomto případě Docker)

Podívejme se, co říká dokumentace o každém z nich (ru., en.). Nejprve kubelet:

Agent běžící na každém uzlu v clusteru. Sleduje, aby byly kontejnery spuštěny v podu.

Zní to docela jednoduše. Co říkáte na runtime kontejnerů (container runtime)?

Runtime kontejnerů je program určený k provádění kontejnerů.

Velmi informativní. Ale pokud znáte Docker, měli byste mít obecnou představu o tom, co dělá. (Detaily rozdělení zodpovědností mezi runtime kontejnerů a kubelet jsou ve skutečnosti docela jemné a zde se do nich nebudu hlouběji pouštět.)

A API server?

API server je komponenta ovládacího panelu Kubernetes, která představuje API Kubernetes. API server je klientská část ovládacího panelu Kubernetes.

Každý, kdo někdy pracoval s Kubernetes, musel interagovat s API buď přímo, nebo prostřednictvím kubectl. To je srdce toho, co dělá Kubernetes Kubernetes – mozek, který převádí hory YAML, které všichni známe a máme rádi (?), na fungující infrastrukturu. Zdá se samozřejmé, že API by mělo být součástí naší minimální konfigurace.

Předpoklady

  • Virtuální nebo fyzický stroj s Linuxem s root přístupem (používám Ubuntu 18.04 na virtuálním stroji).
  • A to je vše!

Nudná instalace

Na stroj, který budeme používat, je potřeba nainstalovat Docker. (Nebudu podrobně vysvětlovat, jak Docker a kontejnery fungují; pokud vás to zajímá, existují skvělé články). Pojďme jej prostě nainstalovat pomocí apt:

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

Poté potřebujeme získat binární soubory Kubernetes. Ve skutečnosti pro počáteční spuštění našeho "clusteru" potřebujeme pouze kubelet, protože pro spuštění dalších serverových komponent budeme moci použít kubelet. Pro interakci s naším clusterem, jakmile začne fungovat, také 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

Co se stane, když jednoduše spustíme kubelet?

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

kubelet musí běžet jako root. To dává smysl, protože musí ovládat celý uzel. Podívejme se na jeho parametry:

$ ./kubelet -h

$ ./kubelet -h | wc -l 284

Wow, kolik možností! Naštěstí budeme potřebovat pouze pár z nich. Zde je jeden z parametrů, který nás zajímá:

--pod-manifest-path string

Cesta k adresáři obsahujícímu soubory pro statické pody, nebo cesta k souboru s popisem statických podů. Soubory začínající tečkami jsou ignorovány. (ZASTARALÉ: tento parametr by měl být nastaven v konfiguračním souboru, který je předáván Kubeletu prostřednictvím parametru —config. Pro další informace viz. kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Tento parametr nám umožňuje spouštět statické pody — pody, které nejsou spravovány prostřednictvím Kubernetes API. Statické pody se používají zřídka, ale jsou velmi užitečné pro rychlou konfiguraci klastru, což je přesně to, co potřebujeme. To hlučné varování ignorujeme (opět, nespouštějte to v produkci!) a podíváme se, zda dokážeme spustit pod.

Nejprve vytvoříme adresář pro statické pody a spustíme kubelet:

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

Poté v jiném terminálu/okně tmux/někde jinde vytvoříme manifest podu:

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

kubelet začne psát nějaká varování a zdá se, že se nic neděje. Ale to není pravda! Podívejme se 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 přečetl manifest podu a dal Dockeru příkaz spustit pár kontejnerů v souladu s naší specifikací. (Pokud vás zajímají kontejner “pause”, je to hack Kubernetes — podrobnosti najdete v tomto blogu.) Kubelet spustí náš kontejner busybox s uvedeným příkazem a bude jej neustále restartovat, dokud nebude statický pod odstraněn.

Pogratulujte si. Právě jsme vymysleli jeden z nejzmatečnějších způsobů, jak zobrazit text v terminálu!

Spouštíme etcd

Naším konečným cílem je spustit Kubernetes API, ale pro to nejprve musíme spustit etcd. Pojďme spustit minimální klastr etcd tím, že jeho konfiguraci umístíme do adresáře pods (např., 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

Pokud jste někdy pracovali s Kubernetes, měly by vám být podobné YAML soubory známé. Zde je třeba zdůraznit pouze dvě věci:

Připojili jsme složku hostitele /var/lib/etcd v podu, aby data etcd byla uložena po restartu (pokud to neuděláte, stav klastru bude vymazán při každém restartu podu, což není dobré ani pro minimální instalaci Kubernetes).

Nainstalovali jsme hostNetwork: true. Tento parametr, jak se dalo očekávat, nastavuje etcd pro použití sítě hostitele místo vnitřní sítě podu (usnadní to API serveru vyhledávání klastru etcd).

Jednoduchá kontrola ukazuje, že etcd skutečně běží na localhost a ukládá data 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

Spuštění API serveru

Spustit API server Kubernetes je ještě jednodušší. Jediný parametr, který musíte zadat, --etcd-servers, dělá to, co očekáváte:

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

Umístěte tento YAML soubor do adresáře pods, a API server se spustí. Ověření pomocí curl ukazuje, že Kubernetes API naslouchá na portu 8080 s plným přístupem — autentizace není vyžadována!

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

(Opět, nespouštějte to v produkčním prostředí! Byl jsem trochu překvapen, že výchozí nastavení je tak nebezpečné. Ale předpokládám, že to bylo uděláno pro usnadnění vývoje a testování.)

A, příjemné překvapení, kubectl funguje ihned bez jakýchkoliv další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

Ale pokud se podíváte hlouběji, vypadá to, že něco není v pořádku:

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

Statické pody, které jsme vytvořili, zmizely! Ve skutečnosti náš kubelet uzel vůbec není detekován:

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

Co se děje? Pokud si vzpomenete, před několika odstavci jsme spouštěli kubelet s velmi jednoduchou sadou parametrů příkazového řádku, takže kubelet neví, jak se připojit k API serveru a informovat ho o svém stavu. Po prostudování dokumentace nacházíme příslušnou vlajku:

--kubeconfig string

Cesta k souboru kubeconfig, ve kterém je uvedeno, jak se připojit k API serveru. Mít --kubeconfig zapíná mód API serveru, absence --kubeconfig zapíná autonomní mód.

Celou dobu jsme, aniž bychom to tušili, spouštěli kubelet v "autonomním režimu". (Pokud bychom byli pedanti, mohli bychom považovat autonomní režim kubeletu za "minimálně životaschopný Kubernetes", ale to by bylo velmi nudné). Aby „opravdová“ konfigurace fungovala, musíme předat soubor kubeconfig kubeletu, aby věděl, jak komunikovat s API serverem. Naštěstí je to docela jednoduché (protože nemáme problémy s autentizací nebo certifikáty):

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 jako kubeconfig.yaml, zabijte proces kubelet a restartujte s požadovanými parametry:

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

(Mimochodem, pokud se pokusíte přistupovat k API přes curl, když kubelet neběží, zjistíte, že stále funguje! Kubelet není „rodičem“ svých podů, podobně jako Docker; spíše připomíná "demon správce". Kontejnery řízené kubelet budou fungovat, dokud je kubelet nezastaví.)

Za několik minut kubectl by měl ukázat naše pody a uzly, jak očekáváme:

$ ./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       21h   v1.18.5   10.70.10.228           Ubuntu 18.04.4 LTS   4.15.0-109-generic   docker://19.3.6

Tentokrát se skutečně pochváleme (vím, že už jsem to udělal) — máme minimální "klastr" Kubernetes běžící s plnohodnotným API!

Spouštíme pod

Nyní se podíváme, co API dokáže. Začneme s bodem nginx:

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

Zde dostaneme docela zajímavou chybu:

$ .\/kubectl apply -f nginx.yaml
Chyba ze serveru (Forbidden): chyba při vytváření "nginx.yaml": pods "nginx" je
zakázán: chyba při hledání účtu služby default\/default: serviceaccount
"default" nenalezen
$ .\/kubectl get serviceaccounts
Žádné zdroje nenalezeny v výchozím prostoru.

Zde vidíme, jak strašně neúplné je naše prostředí Kubernetes — nemáme žádné účty pro služby. Zkusme to znovu a vytvořme účet služby manuálně, abychom zjistili, co se stane:

$ cat <<EOS | .\/kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
EOS
serviceaccount\/default vytvořen
$ .\/kubectl apply -f nginx.yaml
Chyba ze serveru (ServerTimeout): chyba při vytváření "nginx.yaml": Žádný API
token nenalezen pro účet služby "default", zkuste to znovu poté, co bude token
automaticky vytvořen a přidán k účtu služby.

I když jsme účet služby vytvořili manuálně, autentizační token se nevytváří. Pokračováním v experimentování s naším minimalistickým „klastrem“ zjistíme, že většina užitečných věcí, které se obvykle dějí automaticky, chybí. Server Kubernetes API je poměrně minimalistický, většina těžkých automatických konfigurací se provádí v různých ovladačích a na pozadí, které zatím nejsou spuštěny.

Můžeme toto řešení obejít nastavením možnosti automountServiceAccountToken pro účet služby (protože jej stejně nebudeme potřebovat):

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

Nakonec se pod objevil! Ale ve skutečnosti se nespustí, protože nemáme plánovač (scheduler) — další důležitou součást Kubernetes. Znovu vidíme, že API Kubernetes je překvapivě "hloupé" — když vytvoříte pod v API, zaregistruje ho, ale nepokouší se zjistit, na kterém uzlu jej spustit.

Ve skutečnosti pro spuštění podu plánovač není potřeba. Můžeme ručně přidat uzel do manifestu v parametru nodeName:

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

(Nahraďte mink8s názvem uzlu.) Po delete a apply vidíme, že nginx běží a naslouchá na interní IP adrese:

$ .\/kubectl delete pod nginx
pod "nginx" byl smazán
$ .\/kubectl apply -f nginx.yaml
pod/nginx byl vytvořen
$ .\/kubectl get pods -owide
JMÉNO   PŘIPRAVENO   STAV     RESTARTY   VĚK   IP           NOD     NOMINOVANÝ NOD   BRÁNY PŘIPRAVENOSTI
nginx   1\/1     Běžící  0          30s   172.17.0.2   mink8s   <none>           <none>
$ curl -s 172.17.0.2 | head -4
<!DOCTYPE html>
<html>
<head>
<title>Vítejte na nginx!</title>

Abychom se ujistili, že síť mezi podmi funguje správně, můžeme spustit curl z jiného podu:

$ cat &lt;&lt;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>Vítejte na nginx!</title>

Je docela zajímavé prozkoumat toto prostředí a zjistit, co funguje a co ne. Zjistil jsem, že ConfigMap a Secret fungují, jak se očekávalo, zatímco Service a Deployment ne.

Úspěch!

Tento příspěvek se stává obsáhlým, proto chci vyhlásit vítěze a uvést, že to je životaschopná konfigurace, kterou lze nazvat "Kubernetes". Stručně řečeno: čtyři binární soubory, pět příkazových parametrů a "pouze" 45 řádků YAML (ne tak mnoho podle standardů Kubernetes) a máme v chodu několik věcí:

  • Pody jsou spravovány pomocí běžného API Kubernetes (s několika hacky)
  • Lze nahrávat veřejné obrazy kontejnerů a spravovat je
  • Pody zůstávají naživu a automaticky se restartují
  • Síť mezi podmi v rámci jednoho uzlu funguje docela dobře
  • ConfigMap, Secret a nejjednodušší montování úložišť fungují, jak mají

Ale většina toho, co dělá Kubernetes skutečně užitečné, stále chybí, například:

  • Plánovač podů
  • Autentizace / autorizace
  • Více uzlů
  • Síť služeb
  • Klastrální interní DNS
  • Kontroléry pro účty služeb, nasazení, integraci s cloudovými poskytovateli a většinu dalších "vychytávek", které přináší Kubernetes

Co tedy skutečně máme? API Kubernetes, které funguje samostatně, je ve skutečnosti jen platformou pro automatizaci kontejnerů. Nedělá toho mnoho – to je práce pro různé kontroléry a operátory, kteří používají API – ale poskytuje konzistentní prostředí pro automatizaci.

Zjistit více o kurzu na bezplatném webináři.

Číst více:

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster