Minimális életképes Kubernetes

A cikk fordítása a tanfolyam kezdetének előestéjén készült "DevOps gyakorlatok és eszközök".

Minimális életképes Kubernetes

Ha ezt olvasod, valószínűleg hallottál valamit a Kubernetesről (és ha nem, hogyan kerültél ide?) De mi is az a Kubernetes? Ez „Ipari minőségű konténerek hangszerelése”? Vagy "Cloud-Native operációs rendszer"? Ez egyáltalán mit jelent?

Ő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:

Minimális életképes Kubernetes

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:

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

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.

$ 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 futunk kubelet?

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

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:

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

Ezután egy másik terminálban/tmux ablakban/bármiben létrehozunk egy pod manifestet:

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

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):

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 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:

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

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!

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

(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):

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 ezt másként kubeconfig.yaml, öld meg a folyamatot kubelet és indítsa újra a szükséges paraméterekkel:

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

(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:

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

Itt egy meglehetősen érdekes hibát kapunk:

$ ./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:

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

(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.

Tudjon meg többet a kurzusról az ingyenes webináriumban.

Olvass tovább:

Forrás: will.com

Hozzászólás