Minimálisan életképes Kubernetes

A cikk fordítása a kurzus megkezdése előtt készült az új fordítást osztjuk meg veletek. Induljunk..

Minimálisan életképes Kubernetes

Ha ezt olvasod, valószínűleg hallottál már a Kubernetesről (ha nem, akkor hogyan kerültél ide?). De mi is valójában a Kubernetes? Ez „Ipari szintű konténerorchestration”? Или „Felhő-őrző operációs rendszer”? Что вообще это значит?

Őszintén szólva, nem vagyok 100%-ig biztos. De úgy gondolom, érdekes megnézni, hogy mi történik a Kubernetes mélyén, a sok absztrakciós réteg alatt. Szóval, az érdekesség kedvéért nézzük meg, hogy valójában hogyan néz ki a minimális „Kubernetes klaszter”. (Ez sokkal egyszerűbb lesz, mint Kubernetes A nehezen átlátható módja.)

Feltételezem, hogy van némi alapvető ismereted a Kubernetesről, a Linuxról és a konténerekről. Az itt tárgyaltak mind csak kutatásra/megismerésre szolgálnak, ne futtass semmit ebből éles környezetben!

Áttekintés

A Kubernetes számos komponenst tartalmaz. A a Wikipédiában, az architektúra a következőképpen néz ki:

Minimálisan életképes Kubernetes

Itt legalább nyolc komponenst láthatunk, de a legtöbbet figyelmen kívül hagyjuk. Azt szeretném mondani, hogy a legkisebb dolog, amit érdemben Kubernetesnek lehet nevezni, három fő komponensből áll:

  • kubelet
  • kube-apiserver (mely az etcd-tól — az adatbázisától — függ)
  • konténer futtatási környezet (jelen esetben Docker)

Nézzük meg, mit mond róluk a dokumentáció (hu., en). Először kubelet:

Ügynök, amely minden csomóponton működik a klaszterben. Figyelemmel kíséri, hogy a konténerek futnak-e a podban.

Ez elég egyszerűen hangzik. Mi a helyzet a konténer futtatási környezettel (container runtime)?

A konténer futtatási környezet egy olyan program, amely a konténerek végrehajtására szolgál.

Nagyon informatív. De ha ismered a Dockert, akkor valószínűleg van egy átfogó képed arról, hogy mit csinál. (A konténer futtatási környezet és a kubelet közötti felelősségmegosztás részletei valójában eléggé finomak, és itt nem fogok részletekbe bonyolódni.)

és API-szerver?

Az API-szerver a Kubernetes irányítópult egyik komponenstje, amely a Kubernetes API-t képviseli. Az API-szerver a Kubernetes irányítópult kliensoldali része

Bárkinek, aki valaha is foglalkozott a Kubernetes-szel, közvetlenül vagy a kubectl-en keresztül is kapcsolatba kellett lépnie az API-val. Ez annak a szíve, ami a Kubernetessé teszi a Kubernetist — az agy, ami a sok YAML-t, amit mindannyian ismerünk és szeretünk (?) működő infrastruktúrává alakít. Nyilvánvalónak tűnik, hogy az API-nak jelen kell lennie a minimális konfigurációnkban.

Előfeltételek

  • Linux alapú virtuális vagy fizikai gép root hozzáféréssel (én az Ubuntu 18.04-et használom egy virtuális gépen).
  • Ennyi az egész!

Unalmas telepítés

A használt gépre telepíteni kell a Dockert. (Nem fogom részletezni, hogyan működik a Docker és a konténerek; ha érdekel, vannak fantasztikus cikkek). Telepítsük csak egyszerűen a következő parancsokkal: apt:

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

Ezután meg kell szereznünk a Kubernetes bináris fájljait. Valójában az "klaszterünk" kezdeti elindításához csak kubelet, mivel a többi kiszolgáló komponens indításához használni tudjuk kubelet. A klaszterünkkel való interakcióhoz, miután az elindult, szintén használni fogjuk 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

Mi történik, ha csak elindítjuk kubelet?

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

kubelet root-tól kell futnia. Ez elég logikus, hiszen kezelnie kell az egész csomópontot. Nézzük meg a paramétereit:

$ ./kubelet -h

$ ./kubelet -h | wc -l
284

Hű, mennyi opció! Szerencsére csak párra lesz szükségünk. Íme egy paraméter, ami érdekel minket:

--pod-manifest-path string

Az a könyvtár, amely tartalmazza a statikus podok fájljait, vagy statikus podok leíró fájljának elérési útja. A ponttal kezdődő fájlokat figyelmen kívül hagyják. (ELAVULT: ezt a paramétert a konfigurációs fájlban kell beállítani, amelyet a Kubelet-hez adunk át a --config opcióval. További információért lásd: kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Ez a paraméter lehetővé teszi számunkra, hogy futtassuk statikus podokat — podokat, amelyeket nem a Kubernetes API kezel. A statikus podokat ritkán használják, de nagyon hasznosak a klaszter gyors elindításához, és éppen erre van szükségünk. Figyelmen kívül hagyjuk ezt a hangos figyelmeztetést (újra, ne futtassuk ezt éles környezetben!) és nézzük meg, tudunk-e podot indítani.

Először létrehozunk egy könyvtárat a statikus podok számára, és elindítjuk kubelet:

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

Ezután egy másik terminálban/tmux ablakban/még valahol létrehozzuk a pod manifestet:

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

kubelet Elkezdett figyelmeztetéseket írni, és úgy tűnik, hogy semmi sem történik. De ez nem igaz! Nézzük meg a Dockert:

$ sudo docker ps -a
CONTAINER ID        IMAGE                  COMMAND                 CREATED             STATUS                      PORTS               NAMES
8c8a35e26663        busybox                "echo 'hello world!'"   36 másodperce      Leállítva (0) 36 másodperce                  k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
68f670c3c85f        k8s.gcr.io/pause:3.2   " /pause"            2 perce          Futtatva 2 perce                           k8s_POD_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_0
$ sudo docker logs k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
hello world!

kubelet Elolvastam a pod manifesztumot, és megadtam a Dockernek a parancsot, hogy indítsa el a konténereket a specifikációnk szerint. (Ha érdekli a "pause" konténer, az a Kubernetes trükkje - részletekért lásd a blogunkban.) A Kubelet elindítja a konténereinket busybox a megadott paranccsal, és folyamatosan újraindítja, amíg a statikus pod el nem távolításra kerül.

Gratulálj magadnak. Éppen most találtuk ki az egyik legkényelmetlenebb módot a szöveg terminálba írására!

Indítjuk az etcd-t

A végső célunk a Kubernetes API indítása, de ehhez először el kell indítanunk etcd. Indítsunk el egy minimalista etcd klasztert, a beállításokat a pods mappába helyezve (például, 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

Ha valaha is dolgoztál Kubernetes-szel, akkor ezek a YAML fájlok ismerősek kell legyenek számodra. Itt csak két dolgot érdemes megemlíteni:

Monta a gazdagépet /var/lib/etcd a podba, hogy az etcd adatai megmaradjanak újraindítás után (ha ezt nem tesszük meg, a klaszter állapota törlődik a pod minden újraindítása során, ami még a Kubernetes minimális telepítése esetén is problémás lenne).

Beállítottuk hostNetwork: true. Ez a paraméter, ahogy várható volt, beállítja az etcd-t, hogy a gazdagép hálózatát használja a pod belső hálózata helyett (ez megkönnyíti az API szerver számára az etcd klaszter megtalálását).

Egy egyszerű ellenőrzés azt mutatja, hogy az etcd valóban a localhost-on fut és adatokat tárol a lemezen:

$ 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

API szerver indítása

A Kubernetes API-szerver indítása még egyszerűbb. Az egyetlen paraméter, amit át kell adni, --etcd-servers, az pontosan azt csinálja, amit vársz:

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

Helyezd el ezt a YAML fájlt a pods, könyvtárba, és az API-szerver elindul. Ellenőrzés a curl megmutatja, hogy a Kubernetes API a 8080-as portot hallgatja, teljesen nyitott hozzáféréssel — nincs szükség hitelesítésre!

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

(Ismétlem, ne indítsd el ezt éles környezetben! Kicsit meglepett, hogy az alapértelmezett beállítások ennyire nem biztonságosak. De azt feltételezem, hogy ezt a fejlesztés és a tesztelés megkönnyítése érdekében tették.)

És, kellemes meglepetés, a kubectl dobozból működik, további beállítások 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 kicsit mélyebbre ásunk, úgy tűnik, hogy valami nem stimmel:

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

A statikus podok, amiket létrehoztunk, eltűntek! Valójában a kubelet csomópontunk egyáltalán nem észlelhető:

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

Mi folyik itt? Ha emlékszel, néhány bekezdéssel ezelőtt a kubelet-et egy rendkívül egyszerű parancssori beállítással indítottuk, így a kubelet nem tudja, hogyan kell kapcsolatba lépni az API-szerverrel és értesíteni azt az állapotáról. A dokumentációtanulmányozásával megtaláljuk a megfelelő flag-et:

--kubeconfig string

Az API-szerverhez való kapcsolódást leíró fájl elérési útja. kubeconfigJelenléte --kubeconfig aktiválja az API-szerver módot, míg a hiánya --kubeconfig önálló módot aktivál.

Mialatt mi erről nem tudtunk, a kubelet-et «autonóm üzemmódban» futtattuk. (Ha pedánsak lennénk, akkor az autonóm kubelet üzemmódot tekinthetnénk mint «minimum életképes Kubernetes»-t, de ez nagyon unalmas lenne). Ahhoz, hogy a «valódi» konfiguráció működjön, át kell adnunk a kubeconfig fájlt a kubelet-nek, hogy tudja, hogyan kommunikáljon az API-szerverrel. Szerencsére ez elég egyszerű (mivel nincsenek hitelesítési vagy tanúsítványi problémáink):

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

Mentse el ezt mint kubeconfig.yaml, állítsa le a folyamatot kubelet és indítsa újra a szükséges paraméterekkel:

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

(Egyébként, ha megpróbálja megközelíteni az API-t curl-lel, amikor a kubelet nem fut, akkor rájön, hogy a kubelet még mindig működik! A kubelet nem a podjai «szülője», inkább hasonlít egy “kezelő démonra”. A kubelet által vezérelt konténerek működnek, amíg a kubelet nem állítja le őket.)

Néhány perc múlva kubectl látnunk kellene a podokat é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       21h   v1.18.5   10.70.10.228           Ubuntu 18.04.4 LTS   4.15.0-109-generic   docker://19.3.6

Most igazán gratuláljunk magunknak (tudom, már gratuláltam) — létrehoztuk a legkisebb «Kubernetes» klasztert, amely teljes funkciós API-val működik!

Indítunk egy podot

Most nézzük meg, mire képes az API. Kezdjük az nginx poddal:

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

Itt egy meglehetősen érdekes hibát fogunk kapni:

$ ./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, mennyire borzasztóan hiányos a Kubernetes környezetünk — nincsenek szolgáltatásfiókjaink. Próbáljuk meg újra, és manuálisan hozzunk létre egy szolgáltatásfiókot, hogy lássuk, 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 is, ha manuálisan létrehoztuk a szolgáltatásfiókot, az autentikációs token nem jön létre. Tovább kísérletezve minimalista „klaszterünkkel”, felfedezzük, hogy a legtöbb hasznos dolog, ami általában automatikusan történik, hiányozni fog. A Kubernetes API szerver meglehetősen minimalista, a legtöbb nehéz automatikus beállítás különféle vezérlőkben és háttérfeladatokban történik, amelyek még nem futnak.

Ezt a problémát megkerülhetjük azzal, hogy beállítjuk az opciót automountServiceAccountToken a szolgáltatásfiók számára (mivel úgysem fogjuk használni):

$ 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 pod! De valójában nem fog elindulni, mivel nincs ütemező (scheduler) — a Kubernetes egy másik fontos összetevője. Ismét csak azt látjuk, hogy a Kubernetes API meglepően „buta” — amikor egy podot létrehoz a API-ban, regisztrálja, de nem próbálja kideríteni, hogy melyik csomóponton kell futtatni.

Valójában az ütemező nem szükséges a pod elindításához. Kézzel csomópontot is hozzáadhat a manifesthez a következő paraméterrel: nodeName:

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

(Cserélje a következőt: mink8s a csomópont nevére.) A delete és apply után azt látjuk, hogy az nginx elindult és figyeli a belső IP-címet:

$ .\/kubectl delete pod nginx
pod "nginx" törölve
$ .\/kubectl apply -f nginx.yaml
pod/nginx létrehozva
$ .\/kubectl get pods -owide
NÉV    KÉSZ    ÁLLAPOT    ÚJRAINDÍTÁSOK   KOR   IP           CSOMÓPONT     JELÖLT CSOMÓPONT   KÉSZÜLTSÉGI KAPUK
nginx   1\/1     Futtatás   0          30s   172.17.0.2   mink8s   <none>           <none>
$ curl -s 172.17.0.2 | head -4
<!DOCTYPE html>
<html>
<head>
<title>Üdvözöljük az nginx-et!</title>

Azt, hogy a podok közötti hálózat megfelelően működik, egy másik podból futtatott curl-lal tudjuk ellenőrizni:

$ cat &lt;&lt;EOS | .\/kubectl apply -f -\napiVersion: v1\nkind: Pod\nmetadata:\n  name: curl\nspec:\n  containers:\n  - image: curlimages\/curl\n    name: curl\n    command: ["curl", "172.17.0.2"]\n  nodeName: mink8s\nEOS\npod\/curl created\n$ .\/kubectl logs curl | head -6\n  % Teljes    % Fogadott % Átvitel   Átlagos Sebesség   Idő    Idő     Idő  Aktuális\n                                 Letöltés  Feltöltés   Összes   Elköltött   Balra  Sebesség
<!DOCTYPE html>
<html>
<head>
<title>Üdvözöljük az nginx-et!</title>

Eléggé érdekes ezen a környezetben turkálni és megnézni, mi működik, és mi nem. Felfedeztem, hogy a ConfigMap és a Secret a várakozásoknak megfelelően működik, míg a Service és a Deployment nem.

Siker!

Ez a bejegyzés meglehetősen hosszúra sikerült, ezért be fogom jelenteni a győzelmet, és azt állítom, hogy ez egy működőképes konfiguráció, amit „Kubernetes”-nek hívhatunk. Összefoglalva: négy bináris fájl, öt parancssori opció és „csak” 45 sor YAML (Kubernetes szabványok szerint nem túl sok), és már működik is néhány dolog:

  • A podokat a normál Kubernetes API (néhány hackkel) kezeli.
  • Publikus konténerképeket lehet feltölteni és kezelni.
  • A podok életben maradnak és automatikusan újraindulnak.
  • A podok közötti hálózat egy csomóponton belül elég jól működik.
  • A ConfigMap, Secret és az egyszerű tárolók csatlakoztatása rendben működik.

De a Kubernetes valódi hasznosságához szükséges dolgok többsége még hiányzik, például:

  • Pod ütemező.
  • Hitelesítés / engedélyezés.
  • Több csomópont.
  • Szolgáltatások hálózata.
  • Klaszterbeli belső DNS.
  • Szolgáltatásfiókok, telepítések, felhőszolgáltatókkal való integráció és sok más 'kényelem' vezérlői, amelyet a Kubernetes hoz.

De mit is kaptunk valójában? A Kubernetes API, amely önállóan működik, valójában csak egy platform az konténerek automatizálásához.Nem sok mindent csinál — ez a különböző vezérlők és operátorok feladata, akik az API-t használják — de biztosít egy konzisztens környezetet az automatizáláshoz.

Tudj meg többet a kurzusról egy ingyenes webináriumon.

Olvass tovább:

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster