Prevod članka pripremljen je uoči početka kursa .

Če to berete, verjetno ste že slišali za Kubernetes (če pa niste, kako ste prišli sem?). Kaj pa v resnici je Kubernetes? To je ? Или ? Что вообще это значит?
Iskreno povedano, nisem povsem prepričan. Ampak mislim, da je zanimivo raziskati in videti, kaj dejansko poteka v Kubernetesu pod mnogimi plastmi abstrakcij. Zato si poglejmo, kakšna je v resnici minimalna "Kubernetes klaster". (To bo veliko lažje kot .)
Predvidevam, da imate osnovno znanje o Kubernetesu, Linuxu in kontejnerjih. Vse, o čemer bomo tukaj govorili, je namenjeno le raziskovanju/študiju, ne zagnati tega v produkciji!
Pregled
Kubernetes vsebuje številne komponente. Po , arhitektura izgleda takole:

Tukaj je prikazanih vsaj osem komponent, vendar se večine njih bomo ignorirali. Želim povedati, da minimalna stvar, ki jo lahko smiselno imenujemo Kubernetes, vsebuje tri glavne komponente:
- kubelet
- kube-apiserver (ki je odvisen od etcd — svoje baze podatkov)
- izvajalna okolja kontejnerjev (v tem primeru Docker)
Poglejmo, kaj o vsakem od njih pravi dokumentacija (., .). Najprej kubelet:
Agent, ki dela na vsakem vozlišču v klasterju. Skrbi, da so kontejnerji zagnani v podu.
Zveni dovolj preprosto. Kaj pa izvajalna okolja kontejnerjev (container runtime)?
Izvajalno okolje kontejnerjev je program, namenjen za izvajanje kontejnerjev.
Zelo informativno. Ampak če ste seznanjeni z Dockerjem, bi morali imeti splošno predstavo o tem, kaj počne. (Podrobnosti o razdelitvi odgovornosti med okoljem za izvajanje kontejnerjev in kubeletom so v resnici precej tanke in tukaj se ne nameravam poglabljati v to.)
In API-server?
API-server je komponenta nadzorne plošče Kubernetes, ki predstavlja API Kubernetes. API-server je odjemalski del nadzorne plošče Kubernetes.
Vsakdo, ki je že kdaj delal s Kubernetesom, je moral sodelovati z API-jem bodisi neposredno bodisi preko kubectl. To je srce tistega, kar Kubernetes dela zadovoljivo — možgani, ki spreminjajo gore YAML, ki jih vsi poznamo in ljubimo (?), v delujočo infrastrukturo. Očitno je, da mora biti API prisoten v naši minimalni konfiguraciji.
Predpogoji
- Virtualni ali fizični stroj Linux s pravicami root (jaz uporabljam Ubuntu 18.04 na virtualnem stroju).
- In to je to!
Dolgočasna namestitev
Na stroj, ki ga bomo uporabili, je treba namestiti Docker. (Ne bom podrobno opisoval, kako Docker in kontejnerji delujejo; če vas zanima, obstajajo ). Preprosto ga namestimo s pomočjo apt:
$ sudo apt install docker.io
$ sudo systemctl start docker Po tem moramo pridobiti binarne datoteke Kubernetes. Za začetni zagon našega 'klasterja' potrebujemo le kubelet, saj bomo za zagon drugih strežniških komponent lahko uporabili kubelet. Za interakcijo z našim klasterjem, ko bo ta zaživel, pa bomo uporabili 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 Kaj se zgodi, če preprosto zaženemo kubelet?
$ ./kubelet
F0609 04:03:29.105194 4583 server.go:254] mkdir /var/lib/kubelet: permission denied kubelet mora delovati kot root. Kar je logično, saj mora upravljati celoten vozlišče. Poglejmo njegove možnosti:
$ ./kubelet -h
$ ./kubelet -h | wc -l
284Wow, koliko možnosti! Na srečo, potrebujemo le nekaj od njih. Tukaj je ena izmed možnosti, ki nas zanima:
--pod-manifest-path stringPot do imenika, ki vsebuje datoteke za statične pode, ali pot do datoteke z opisom statičnih podov. Datoteke, ki se začnejo s piko, se prezrejo. (ZASTARELO: ta parameter je treba nastaviti v konfiguracijski datoteki, ki se predaja Kubelet-u preko možnosti —config. Za dodatne informacije glej. .)
Ta parameter nam omogoča zagon — podi, ki niso upravljani prek Kubernetes API. Statični podi se redko uporabljajo, a so zelo priročni za hitro postavitev klasterja, kar je točno to, kar potrebujemo. Ignorirali bomo to glasno opozorilo (spet, tega ne запускайте v produkciji!) in pogledali, ali lahko zagnamo pod.
Najprej bomo ustvarili imenik za statične pode in jih zagnali kubelet:
$ mkdir pods
$ sudo ./kubelet --pod-manifest-path=podsNato v drugem terminalu/oknu tmux/nekje drugje, bomo ustvarili manifest poda:
$ 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 pisati neka opozorila in zdi se, da se nič ne dogaja. A to ni res! Poglejmo 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 je prebral manifest poda in dal Dockerju ukaz, da zažene nekaj vsebnikov v skladu z našo specifikacijo. (Če vas zanima vsebnik “pause”, je to hakerski trik Kubernetes — podrobnosti so v .) Kubelet bo zagnal naš vsebnik busybox z navedeno ukazom in ga neprekinjeno ponovno zagnal, dokler statični pod ne bo odstranjen.
Čestitajte si. Ravno smo našli enega izmed najbolj zapletenih načinov za izpis besedila v terminal!
Zagon etcd
Naš končni cilj je zagon Kubernetes API, a za to moramo najprej zagnati . Ustvarimo minimalen etcd klaster, tako da njegove nastavitve postavimo v imenik pods (na primer, 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Če ste že kdaj delali z Kubernetes, so vam podobni YAML datoteki verjetno znani. Tukaj velja omeniti samo dve stvari:
Zmontirali smo mapo gostitelja /var/lib/etcd v podu, aby se data etcd uchovávala po restartu (pokud to neuděláte, stav clusteru se při každém restartu podu vymaže, což není dobré ani pro minimální instalaci Kubernetes).
Nainstalovali jsme hostNetwork: true. Tento parametr, jak se dalo očekávat, nastavuje etcd k použití hostitelské sítě místo interní sítě podu (to usnadní API serveru hledání etcd clusteru).
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.walSpuštění API serveru
Spustit API server Kubernetes je ještě jednodušší. Jediný parametr, který musíte předat, --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í. Kontrola pomocí curl ukazuje, že Kubernetes API naslouchá na portu 8080 s plným otevřeným přístupem — autentizace není potřeba!
$ 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 produkci! Byl jsem trochu překvapen, že výchozí nastavení je tak nezabezpečené. 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 hned bez jakýchkoli 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.Težava
Ale pokud se ponoříte o něco 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 není vůbec detekován:
$ ./kubectl get nodes
No resources found in default namespace.Kaj je narobe? Če se spomnite, smo nekaj odstavkov nazaj zagnali kubelet z zelo preprostim nizom ukaznih parametrov, zato kubelet ne ve, kako se povezati s API strežnikom in ga obvestiti o svojem stanju. Ko preučimo dokumentacijo, najdemo ustrezni zastavico:
--kubeconfig niz
Pot do datoteke kubeconfig, v kateri je navedeno, kako se povezati s strežnikom API. Ima --kubeconfig omogoča način API strežnika, odsotnost --kubeconfig omogoča samostojen način.
Vse to čas, ne da bi to vedeli, smo kubelet zagnali v 'samostojnem načinu'. (Če bi bili pedantni, bi lahko samostojni način kubeleta šteli za 'minimalko delujoči Kubernetes', vendar bi bilo to zelo dolgočasno). Da bi delovala 'prava' konfiguracija, moramo predati datoteko kubeconfig kubeletu, da ve, kako kommunicirati s API strežnikom. Na srečo je to precej preprosto (ker nimamo težav z avtentikacijo ali certifikati):
apiVersion: v1
kind: Config
clusters:
- cluster:
server: http://127.0.0.1:8080
name: mink8s
contexts:
- context:
cluster: mink8s
name: mink8s
current-context: mink8s Shrani to kot kubeconfig.yaml, ustavi proces kubelet in znova zaženi z potrebnimi parametri:
$ sudo ./kubelet --pod-manifest-path=pods --kubeconfig=kubeconfig.yaml(Mimogrede, če poskusite dostopati do API-ja preko curl, ko kubelet ne deluje, boste ugotovili, da še vedno deluje! Kubelet ni 'starš' svojih podov, kot Docker, je bolj podoben 'nadzorni demonu'. Kontejnerji, ki jih upravlja kubelet, bodo delovali, dokler jih kubelet ne ustavi.)
Čez nekaj minut kubectl bi nam moral pokazati pode in vozlišča, kot smo pričakovali:
$ ./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.6Tokrat se resnično čestitajmo (vem, da sem že čestital) — imamo minimalni 'klaster' Kubernetes, ki deluje s polno funkcionalnim API-jem!
Zaganjamo pod
Zdaj poglejmo, kaj vse zmore API. Začnimo s podom nginx:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginxTukaj bomo prejeli precej zanimivo napako:
$ .\/kubectl apply -f nginx.yaml
Napaka s strežnikom (Forbidden): napaka pri ustvarjanju "nginx.yaml": pods "nginx" je
prepovedano: napaka pri iskanju računa storitve default\/default: račun
"default" ni najden
$ .\/kubectl get serviceaccounts
Noben vir ni najden v privzetem imenskem prostoru.Tukaj vidimo, kako grozno nepopolno je naše okolje Kubernetes - nimamo računov storitev. Poskusimo še enkrat in ročno ustvarimo račun storitve ter pogledamo, kaj se bo zgodilo:
$ cat <<EOS | .\/kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
EOS
serviceaccount\/default ustvarjen
$ .\/kubectl apply -f nginx.yaml
Napaka s strežnikom (ServerTimeout): napaka pri ustvarjanju "nginx.yaml": Ni API
tokena za račun storitve "default", poskusite znova, ko bo tokene
samodejno ustvarjen in dodan računu storitveTudi ko smo ročno ustvarili račun storitve, se avtentikacijski token ne ustvari. Ko nadaljujemo s poskusi z našim minimalističnim «klastrom», ugotovimo, da večina koristnih stvari, ki običajno potekajo samodejno, manjka. Kubernetes API strežnik je precej minimalističen, večina težkih samodejnih nastavitev poteka v različnih kontrolerjih in ozadnih opravilih, ki še niso izvedena.
To težavo lahko obidemo tako, da nastavimo možnost automountServiceAccountToken za račun storitve (saj ga še vedno ne bomo uporabljali):
$ cat <<EOS | .\/kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
automountServiceAccountToken: false
EOS
serviceaccount\/default konfiguriran
$ .\/kubectl apply -f nginx.yaml
pod\/nginx ustvarjen
$ .\/kubectl get pods
IME PRIPRAVLJEN STANJE PONOVITVE STAROST
nginx 0\/1 Čaka 0 13mNazadnje se je pod pojavil! Vendar se dejansko ne bo zagnal, saj nimamo (scheduler) — še enega pomembnega komponente Kubernetes. Spet vidimo, da je API Kubernetes presenetljivo 'neumen' — ko ustvarite pod v API, ga registrira, vendar ne poskuša ugotoviti, na katerem vozlišču naj ga zažene.
Dejansko za zagon poda razporejevalnik ni potreben. Lahko ročno dodate vozlišče v manifest v parametru nodeName:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
nodeName: mink8s
(Zamenjajte mink8s z imenom vozlišča.) Po brisanju in uporabi vidimo, da se nginx zažene in posluša notranji IP naslov:
$ .\/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>Dobrodošli na nginx!</title>Da bi preverili, ali omrežje med podi deluje pravilno, lahko zaženemo curl iz drugega poda:
$ 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>Dobrodošli na nginx!</title>Zelo zanimivo je raziskovati to okolje in videti, kaj deluje in kaj ne. Ugotovil sem, da ConfigMap in Secret delujeta kot pričakovano, storitev in nameščanje pa ne.
Uspeh!
Ta objava postaja obsežna, zato nameravam razglasiti zmagovalca in trditi, da je to izvedljiva konfiguracija, ki jo lahko imenujemo "Kubernetes". Na kratko: štiri binarne datoteke, pet parametrov ukazne vrstice in "le" 45 vrstic YAML (ni tako veliko po standardih Kubernetes) in že imamo delujočih kar nekaj stvari:
- Podji se upravljajo z uporabo običajnega Kubernetes API (s številnimi hacki)
- Lahko naložite javne slike kontejnerjev in njimi upravljate
- Podji ostajajo živi in se samodejno ponovno zagnajo
- Omrežje med podji v okviru enega gostitelja deluje precej dobro
- ConfigMap, Secret in najprej preprosto montažo shranjevanja delujejo, kot je treba
Toda večina tistega, kar Kubernetes dejansko dela resnično uporabno, še vedno manjka, na primer:
- Načrtovalnik podov
- Avtentikacija / avtoritacija
- Več gostiteljev
- Omrežje storitev
- Notranji DNS grozda
- Kontrolerji za račune storitev, uvedbe, integracijo s ponudniki oblakov in večino drugih "dobrot", ki jih prinaša Kubernetes
Kaj torej dejansko imamo? Kubernetes API, ki deluje samostojno, je dejansko le platforma za avtomatizacijo kontejnerjev. Ne počne veliko – to je naloga različnih kontrolerjev in operaterjev, ki uporabljajo API – toda zagotavlja dosledno okolje za avtomatizacijo.
Preberite več:
Izvor: habr.com
