Őszintén szólva nem vagyok 100%-ig biztos benne. De szerintem érdekes beleásni a belső dolgokba, és megnézni, mi történik valójában a Kubernetesben a sokféle absztrakciós réteg alatt. Szóval csak a móka kedvéért nézzük meg, hogyan is néz ki egy minimális „Kubernetes-fürt”. (Ez sokkal könnyebb lesz Kubernetes A nehéz út.)
Feltételezem, hogy alapvető ismeretekkel rendelkezik a Kubernetesről, a Linuxról és a konténerekről. Minden, amiről itt beszélünk, csak kutatási/tanulási célokat szolgál, ne tegyél belőle semmit termelésbe!
Értékelés
A Kubernetes sok összetevőt tartalmaz. Alapján wikipedia, az architektúra így néz ki:
Itt legalább nyolc összetevő látható, de ezek többségét figyelmen kívül hagyjuk. Szeretném kijelenteni, hogy a minimális dolog, amit ésszerűen Kubernetesnek nevezhetünk, három fő összetevőből áll:
kubelet
kube-apiserver (ami az etcd-től függ - az adatbázisától)
konténer futásidejű (ebben az esetben Docker)
Lássuk, mit mond mindegyikről a dokumentáció (orosz., Angol.). Először kubelet:
A fürt minden csomópontján futó ügynök. Gondoskodik arról, hogy a konténerek futjanak a tokban.
Elég egyszerűen hangzik. Mit szólsz konténer futási idők (tároló futásidejű)?
A konténer futtatókörnyezete egy olyan program, amelyet tárolók futtatására terveztek.
Nagyon informatív. De ha ismeri a Dockert, akkor általános elképzelése van arról, hogy mit csinál. (A konténer futtatókörnyezet és a kubelet közötti felelősségek szétválasztásának részletei valójában meglehetősen finomak, és itt nem térek ki rájuk.)
И API szerver?
Az API Server a Kubernetes vezérlőpult összetevője, amely felfedi a Kubernetes API-t. Az API-kiszolgáló a Kubernetes vezérlőpult kliens oldala
Bárki, aki valaha is csinált valamit a Kubernetes-szel, kapcsolatba kellett lépnie az API-val vagy közvetlenül, vagy a kubectl-en keresztül. Ez a szíve annak, ami a Kubernetes Kubernetessé teszi – az agyat, amely a YAML-hegyeket, amelyeket mindannyian ismerünk és szeretünk (?) működő infrastruktúrává alakítja. Nyilvánvalónak tűnik, hogy az API-nak jelen kell lennie a minimális konfigurációnkban.
Előfeltételek
Linux virtuális vagy fizikai gép root hozzáféréssel (Ubuntu 18.04-et használok virtuális gépen).
És ez minden!
Unalmas telepítés
A Dockert telepítenünk kell arra a gépre, amelyet használni fogunk. (Nem részletezem a Docker és a konténerek működését; ha érdekel, van csodálatos cikkek). Csak telepítsük azzal apt:
Ezt követően meg kell szereznünk a Kubernetes binárisokat. Valójában a „klaszterünk” kezdeti elindításához csak szükségünk van kubelet, hiszen más szerverösszetevők futtatásához is használhatjuk kubelet. A fürttel való interakcióhoz annak futását követően is használjuk kubectl.
kubelet rootként kell futnia. Teljesen logikus, mivel neki kell kezelnie a teljes csomópontot. Nézzük a paramétereit:
$ ./kubelet -h
<слишком много строк, чтобы разместить здесь>
$ ./kubelet -h | wc -l
284
Hú, mennyi lehetőség! Szerencsére csak párra van szükségünk. Íme az egyik paraméter, amelyre kíváncsiak vagyunk:
--pod-manifest-path string
A statikus sorba rendezések fájljait tartalmazó könyvtár elérési útja, vagy a statikus sorba rendezéseket leíró fájl elérési útja. A pontokkal kezdődő fájlok figyelmen kívül maradnak. (DEPRECATED: Ezt a beállítást a --config kapcsolón keresztül a Kubeletnek átadott konfigurációs fájlban kell beállítani. További információkért lásd: kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)
Ez az opció lehetővé teszi számunkra a futtatást statikus hüvelyek — olyan pod-ok, amelyeket nem a Kubernetes API-n keresztül kezelnek. A statikus hüvelyeket ritkán használják, de nagyon kényelmesek a fürt gyors emeléséhez, és pontosan erre van szükségünk. Figyelmen kívül hagyjuk ezt a nagy figyelmeztetést (ismét, ne futtassuk ezt éles állapotban!), és megnézzük, sikerül-e elindítani a pod-ot.
Először létrehozunk egy könyvtárat a statikus podokhoz, és futtatjuk kubelet:
kubelet elkezd néhány figyelmeztetést írni, és úgy tűnik, semmi sem történik. De ez nem igaz! Nézzük a Dockert:
$ 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 Elolvastam a pod manifestet, és parancsot adtam a Dockernek, hogy indítson el néhány tárolót a specifikációink szerint. (Ha a "szünet" tárolóra kíváncsi, az egy Kubernetes hack - ld. ezt a blogot.) A Kubelet elindítja a konténerünket busybox a megadott paranccsal, és korlátlan ideig újraindítja, amíg a statikus pod törlődik.
Gratulálj magadnak. Most találtuk ki az egyik legzavarosabb módot a szöveg terminálra való kiküldésére!
Indítsa el az etcd
Végső célunk a Kubernetes API futtatása, de ehhez először futnunk kell stb. Indítsunk el egy minimális etcd klasztert úgy, hogy a beállításait a pods könyvtárba helyezzük (például pods/etcd.yaml):
Ha valaha is dolgozott a Kubernetes-szel, ezek a YAML-fájlok biztosan ismerősek. Itt csak két pontot érdemes megjegyezni:
Felcsatoltuk a host mappát /var/lib/etcd a podban, hogy az etcd adatok megmaradjanak az újraindítás után (ha ez nem történik meg, a fürt állapota a pod minden újraindításakor törlődik, ami még egy minimális Kubernetes telepítésnél sem lesz jó).
telepítettük hostNetwork: true. Ez a beállítás nem meglepő módon úgy konfigurálja az etcd-t, hogy a gazdahálózatot használja a pod belső hálózata helyett (ez megkönnyíti az API-kiszolgáló számára az etcd-fürt megtalálását).
Egy egyszerű ellenőrzés megmutatja, hogy az etcd valóban a localhost-on fut, és az adatokat lemezre menti:
$ 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
Az API szerver indítása
A Kubernetes API-kiszolgáló futtatása még egyszerűbb. Az egyetlen paraméter, amelyet át kell adni --etcd-servers, azt teszi, amit elvársz:
Helyezze ezt a YAML fájlt a könyvtárba pods, és az API-kiszolgáló elindul. Ellenőrzés a következővel curl azt mutatja, hogy a Kubernetes API a 8080-as porton figyel teljesen nyílt hozzáféréssel - nincs szükség hitelesítésre!
(Még egyszer mondom, ne futtasd ezt éles környezetben! Kicsit meglepett, hogy az alapértelmezett beállítás ennyire nem biztonságos. De feltételezem, hogy ez a fejlesztés és a tesztelés megkönnyítése érdekében történt.)
És kellemes meglepetés, hogy a kubectl a dobozból kivehetően működik minden további beállítás nélkül!
$ ./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éma
De ha egy kicsit mélyebbre ásunk, úgy tűnik, valami nem stimmel:
$ ./kubectl get pod -n kube-system
No resources found in kube-system namespace.
Az általunk létrehozott statikus hüvelyek eltűntek! Valójában a kubelet csomópontunkat egyáltalán nem fedezték fel:
$ ./kubectl get nodes
No resources found in default namespace.
Mi a helyzet? Ha emlékszel néhány bekezdéssel ezelőtt, a kubelet rendkívül egyszerű parancssori paraméterkészlettel indítottuk el, így a kubelet nem tudja, hogyan lépjen kapcsolatba az API-kiszolgálóval és értesítse az állapotáról. A dokumentáció tanulmányozása után megtaláljuk a megfelelő zászlót:
--kubeconfig string
A fájl elérési útja kubeconfig, amely meghatározza az API-kiszolgálóhoz való csatlakozás módját. Elérhetőség --kubeconfig engedélyezi az API szerver módot, XNUMX. sz --kubeconfig engedélyezi az offline módot.
Egész idő alatt, anélkül, hogy tudtuk volna, a kubeletet „offline módban” futtattuk. (Ha pedánsak lennénk, egy önálló kubeletre gondolhatnánk, mint "minimum életképes Kubernetesre", de az nagyon unalmas lenne). Ahhoz, hogy az "igazi" konfiguráció működjön, át kell adnunk a kubeconfig fájlt a kubeletnek, hogy az tudja, hogyan kell kommunikálni az API-kiszolgálóval. Szerencsére nagyon egyszerű (mivel nincs hitelesítési vagy tanúsítványi problémánk):
(Mellesleg, ha megpróbálja elérni az API-t a curl segítségével, amikor a kubelet nem fut, akkor azt fogja tapasztalni, hogy még mindig fut! A Kubelet nem „szülője” a podjainak, mint a Docker, inkább „vezérlő” démon.” A kubelet által kezelt tárolók addig futnak, amíg a kubelet meg nem állítja őket.)
Pár percen belül kubectl meg kell mutatnia nekünk a hüvelyeket és a csomópontokat, ahogy várjuk:
$ ./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
Ezúttal igazán gratuláljunk magunknak (tudom, hogy már gratuláltam magunknak) – van egy minimális Kubernetes „fürtünk”, amely egy teljesen működőképes API-val fut!
alatt indítjuk
Most pedig lássuk, mire képes az API. Kezdjük az nginx poddal:
$ ./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.
Itt láthatjuk, hogy a Kubernetes-környezetünk mennyire szánalmasan hiányos – nincs számlánk a szolgáltatásokhoz. Próbáljuk meg újra egy szolgáltatásfiók manuális létrehozásával, és nézzük meg, mi történik:
$ 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
Még akkor sem jön létre a hitelesítési token, ha manuálisan hoztuk létre a szolgáltatásfiókot. Ahogy folytatjuk a kísérletezést a minimalista "klaszterünkkel", azt fogjuk tapasztalni, hogy a legtöbb hasznos dolog, ami általában automatikusan történik, hiányozni fog. A Kubernetes API-kiszolgáló meglehetősen minimalista, a legtöbb nehéz emelés és automatikus konfigurálás különböző vezérlőkben és háttérfeladatokban történik, amelyek még nem futnak.
Ezt a problémát az opció beállításával megkerülhetjük automountServiceAccountToken a szolgáltatási fiókhoz (mivel úgysem kell használnunk):
$ 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
Végre megjelent a hüvely! De valójában nem indul el, mert nekünk nincs tervező (ütemező) a Kubernetes másik fontos összetevője. Ismét azt látjuk, hogy a Kubernetes API meglepően "buta" – amikor létrehoz egy pod-ot az API-ban, regisztrálja azt, de nem próbálja kitalálni, hogy melyik csomóponton futtassa.
Valójában nincs szükség ütemezőre a pod futtatásához. A paraméterben manuálisan is hozzáadhat egy csomópontot a jegyzékhez nodeName:
(Cserélje ki mink8s a csomópont nevére.) Törlés és alkalmazás után azt látjuk, hogy az nginx elindult, és a belső IP-címet figyeli:
$ ./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>
Annak érdekében, hogy megbizonyosodjunk arról, hogy a podok közötti hálózat megfelelően működik, futtathatjuk a curl-t egy másik podból:
$ 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>
Nagyon érdekes beleásni ebbe a környezetbe, és megnézni, mi működik és mi nem. Azt tapasztaltam, hogy a ConfigMap és a Secret a várt módon működik, de a szolgáltatás és a telepítés nem.
Siker!
Ez a bejegyzés hosszúra nyúlik, ezért győzelmet hirdetek, és azt mondom, hogy ez egy életképes konfiguráció, amelyet "Kubernetes"-nek nevezhetünk. Összefoglalva: négy bináris, öt parancssori paraméter és "csak" 45 sor YAML (nem a Kubernetes szabvány szerint ennyi), és jó néhány dolog működik:
A podok kezelése a szokásos Kubernetes API-val történik (néhány feltöréssel)
Feltölthet és kezelhet nyilvános tárolóképeket
A podok életben maradnak, és automatikusan újraindulnak
Az ugyanazon a csomóponton belüli podok közötti hálózatépítés meglehetősen jól működik
A ConfigMap, a titkos és az egyszerű tároló felszerelése az elvárásoknak megfelelően működik
De még mindig hiányzik sok minden, ami a Kuberneteset valóban hasznossá teszi, például:
Pod ütemező
Hitelesítés/engedélyezés
Több csomópont
Szolgáltatások hálózata
Clusteres belső DNS
Vezérlők szolgáltatásfiókokhoz, telepítésekhez, felhőszolgáltatókkal való integrációhoz és a legtöbb egyéb Kubernetes által kínált jósághoz
Tehát mit is kaptunk valójában? A Kubernetes API, amely önmagában fut, valójában csak egy platform konténer automatizálás. Nem tesz sokat – ez egy feladat az API-t használó különféle vezérlők és kezelők számára –, de konzisztens környezetet biztosít az automatizáláshoz.