Minimum lewensvatbare Kubernetes

Die vertaling van die artikel is voorberei op die vooraand van die aanvang van die kursus "DevOps-praktyke en gereedskap".

Minimum lewensvatbare Kubernetes

As jy hierdie lees, het jy waarskynlik iets van Kubernetes gehoor (en indien nie, hoe het jy hier beland?) Maar wat presies is Kubernetes? Hierdie "Orkestrasie van houers van industriële graad"? Of "Wolk-inheemse bedryfstelsel"? Wat beteken dit selfs?

Om eerlik te wees, ek is nie 100% seker nie. Maar ek dink dit is interessant om in die binnekant te delf en te sien wat werklik in Kubernetes aangaan onder sy baie lae abstraksies. So net vir die pret, kom ons kyk hoe 'n minimale "Kubernetes-kluster" eintlik lyk. (Dit sal baie makliker wees as Kubernetes The Hard Way.)

Ek neem aan jy het basiese kennis van Kubernetes, Linux en houers. Alles waaroor ons hier praat, is slegs vir navorsings-/leerdoeleindes, moenie iets daarvan in produksie plaas nie!

Hersien

Kubernetes bevat baie komponente. Volgens Wikipedia, die argitektuur lyk soos volg:

Minimum lewensvatbare Kubernetes

Daar is ten minste agt komponente wat hier gewys word, maar ons sal die meeste daarvan ignoreer. Ek wil sê dat die minimum ding wat redelikerwys Kubernetes genoem kan word, uit drie hoofkomponente bestaan:

  • kubelet
  • kube-apiserver (wat afhang van etcd - sy databasis)
  • houerlooptyd (Docker in hierdie geval)

Kom ons kyk wat die dokumentasie oor elkeen van hulle sê (Russiese., Engels.). Eers kubelet:

'n Agent loop op elke nodus in die groepering. Dit maak seker dat houers in die peul loop.

Klink eenvoudig genoeg. Wat van houer looptye (houer looptyd)?

'n Houerlooptyd is 'n program wat ontwerp is om houers uit te voer.

Baie insiggewend. Maar as jy vertroud is met Docker, moet jy 'n algemene idee hê van wat dit doen. (Die besonderhede van die skeiding van verantwoordelikhede tussen die houerlooptyd en die kubelet is eintlik nogal subtiel en ek sal nie hier daarop ingaan nie.)

И API-bediener?

API-bediener is die Kubernetes-kontrolepaneelkomponent wat die Kubernetes-API blootstel. Die API-bediener is die kliëntkant van die Kubernetes-kontrolepaneel

Enigiemand wat al ooit iets met Kubernetes gedoen het, moes direk of deur kubectl met die API kommunikeer. Dit is die hart van wat Kubernetes Kubernetes maak - die brein wat die berge van YAML wat ons almal ken en liefhet (?) in werkende infrastruktuur verander. Dit lyk voor die hand liggend dat die API in ons minimale konfigurasie teenwoordig moet wees.

Voorwaardes

  • Linux virtuele of fisiese masjien met worteltoegang (ek gebruik Ubuntu 18.04 op 'n virtuele masjien).
  • En dit is alles!

Vervelige installasie

Ons moet Docker installeer op die masjien wat ons gaan gebruik. (Ek gaan nie in detail ingaan oor hoe Docker en houers werk nie; as jy belangstel, is daar wonderlike artikels). Kom ons installeer dit net met apt:

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

Daarna moet ons die Kubernetes-binaries kry. Trouens, vir die aanvanklike bekendstelling van ons "cluster" het ons net nodig kubelet, aangesien om ander bedienerkomponente te laat loop wat ons kan gebruik kubelet. Om met ons groepering te kommunikeer nadat dit aan die gang is, sal ons ook gebruik 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

Wat gebeur as ons net hardloop kubelet?

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

kubelet moet as wortel loop. Nogal logies, aangesien hy die hele nodus moet bestuur. Kom ons kyk na sy parameters:

$ ./kubelet -h
<слишком много строк, чтобы разместить здесь>
$ ./kubelet -h | wc -l
284

Sjoe, soveel opsies! Gelukkig het ons net 'n paar van hulle nodig. Hier is een van die parameters waarin ons belangstel:

--pod-manifest-path string

Pad na die gids wat lêers vir statiese peule bevat, of pad na 'n lêer wat statiese peule beskryf. Lêers wat met kolletjies begin, word geïgnoreer. (DEPRECATED: Hierdie opsie moet ingestel word in die konfigurasielêer wat deur die --config-opsie na die Kubelet gestuur word. Vir meer inligting, sien kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Hierdie opsie laat ons toe om te hardloop statiese peule - peule wat nie via die Kubernetes API bestuur word nie. Statiese peule word selde gebruik, maar dit is baie gerieflik om 'n groep vinnig te verhoog, en dit is presies wat ons nodig het. Ons sal hierdie groot waarskuwing ignoreer (weereens, moenie dit in produksie laat loop nie!) en kyk of ons die peul aan die gang kan kry.

Eerstens sal ons 'n gids vir statiese peule skep en hardloop kubelet:

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

Dan, in 'n ander terminaal/tmux-venster/wat ook al, sal ons 'n podmanifes skep:

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

kubelet begin 'n paar waarskuwings skryf en dit lyk asof niks gebeur nie. Maar dis nie waar nie! Kom ons kyk 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 Ek het die peulmanifes gelees en Docker die opdrag gegee om 'n paar houers volgens ons spesifikasies te begin. (As jy wonder oor die "pouse" houer, dit is 'n Kubernetes hack - sien hierdie blog.) Kubelet sal ons houer lanseer busybox met die gespesifiseerde opdrag en sal dit onbepaald herbegin totdat die statiese pod uitgevee is.

Geluk jouself. Ons het pas met een van die mees verwarrende maniere vorendag gekom om teks na die terminale uit te voer!

Begin ens

Ons uiteindelike doel is om die Kubernetes API te laat loop, maar om dit te doen, moet ons eers hardloop ens. Kom ons begin 'n minimale ensd-groepering deur die instellings daarvan in die pods-gids te plaas (byvoorbeeld, 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

As jy al ooit met Kubernetes gewerk het, behoort hierdie YAML-lêers aan jou bekend te wees. Daar is net twee punte wat die moeite werd is om hier op te let:

Ons het die gasheerlêergids gemonteer /var/lib/etcd in die peul sodat die ens-data na 'n herbegin bewaar word (indien dit nie gedoen word nie, sal die groepstatus uitgevee word elke keer as die peul herbegin word, wat nie goed sal wees vir selfs 'n minimale Kubernetes-installasie nie).

Ons het geïnstalleer hostNetwork: true. Hierdie instelling konfigureer, nie verbasend nie, ens om die gasheernetwerk te gebruik in plaas van die peul se interne netwerk (dit sal dit makliker maak vir die API-bediener om die ensd-groepering te vind).

'N Eenvoudige kontrole wys dat etcd inderdaad op localhost loop en data op skyf stoor:

$ 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

Begin die API-bediener

Dit is selfs makliker om 'n Kubernetes API-bediener te laat loop. Die enigste parameter wat geslaag moet word, is --etcd-servers, doen wat jy verwag:

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

Plaas hierdie YAML-lêer in die gids pods, en die API-bediener sal begin. Kontroleer met curl wys dat die Kubernetes API op poort 8080 luister met heeltemal oop toegang - geen verifikasie vereis nie!

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

(Weereens, moenie dit in produksie laat loop nie! Ek was 'n bietjie verbaas dat die verstekinstelling so onseker is. Maar ek raai dit is om ontwikkeling en toetsing makliker te maak.)

En, aangename verrassing, kubectl werk uit die boks sonder enige bykomende instellings!

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

probleem

Maar as jy 'n bietjie dieper delf, lyk dit of iets verkeerd loop:

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

Die statiese peule wat ons geskep het, is weg! Trouens, ons kubelet-nodus word glad nie ontdek nie:

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

Wats fout? As jy 'n paar paragrawe gelede onthou, het ons die kubelet begin met 'n uiters eenvoudige stel opdragreëlparameters, so die kubelet weet nie hoe om die API-bediener te kontak en hom van sy toestand in kennis te stel nie. Nadat ons die dokumentasie bestudeer het, vind ons die ooreenstemmende vlag:

--kubeconfig string

Die pad na die lêer kubeconfig, wat spesifiseer hoe om aan die API-bediener te koppel. Beskikbaarheid --kubeconfig aktiveer API-bedienermodus, nee --kubeconfig aktiveer vanlyn modus.

Al hierdie tyd, sonder om dit te weet, het ons die kubelet in "vanlyn modus" laat loop. (As ons pedanties was, sou ons aan 'n selfstandige kubelet kon dink as "minimum lewensvatbare Kubernetes", maar dit sou baie vervelig wees). Om die "regte" konfigurasie te laat werk, moet ons die kubeconfig-lêer na die kubelet deurgee sodat dit weet hoe om met die API-bediener te praat. Gelukkig is dit redelik eenvoudig (aangesien ons nie enige stawing- of sertifikaatkwessies het nie):

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

Stoor dit as kubeconfig.yaml, maak die proses dood kubelet en herbegin met die nodige parameters:

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

(Terloops, as jy probeer om toegang tot die API via krul te kry wanneer die kubelet nie loop nie, sal jy vind dat dit steeds loop! Kubelet is nie 'n "ouer" van sy peule soos Docker nie, dit is meer soos 'n "kontrole" daemon.” Houers wat deur 'n kubelet bestuur word, sal aanhou loop totdat die kubelet hulle stop.)

Oor `n paar minute kubectl moet vir ons die peule en nodusse wys soos ons verwag:

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

Kom ons wens onsself hierdie keer baie geluk (ek weet ek het onsself reeds gelukgewens) - ons het 'n minimale Kubernetes "cluster" wat loop met 'n ten volle funksionele API!

Ons begin onder

Kom ons kyk nou waartoe die API in staat is. Kom ons begin met die nginx-peul:

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

Hier kry ons 'n nogal interessante fout:

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

Здесь мы видим насколько ужасающе неполна наша среда Kubernetes — у нас нет учетных записей для служб. Давайте попробуем еще раз, создав учетную запись службы вручную, и посмотрим, что произойдет:

$ 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

Selfs wanneer ons die diensrekening met die hand geskep het, word die verifikasietoken nie gegenereer nie. Soos ons aanhou eksperimenteer met ons minimalistiese "cluster", sal ons vind dat die meeste van die nuttige dinge wat gewoonlik outomaties gebeur, ontbreek. Die Kubernetes API-bediener is redelik minimalisties, met die meeste van die swaar opheffing en outomatiese konfigurasie wat plaasvind in verskeie beheerders en agtergrondtake wat nog nie aan die gang is nie.

Ons kan hierdie probleem omseil deur die opsie te stel automountServiceAccountToken vir die diensrekening (aangesien ons dit in elk geval nie hoef te gebruik nie):

$ 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

Uiteindelik het die peul verskyn! Maar in werklikheid sal dit nie begin nie, want ons het nie beplanner (skeduleerder) is nog 'n belangrike komponent van Kubernetes. Weereens, ons sien dat die Kubernetes API verbasend "dom" is - wanneer jy 'n Pod in die API skep, registreer dit dit, maar probeer nie uitvind op watter nodus om dit te laat loop nie.

На самом деле для запуска пода планировщик не нужен. Можно вручную добавить узел в манифест в параметре nodeName:

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

(Vervang mink8s на название узла.) После delete и apply мы видим, что nginx запустился и слушает внутренний IP-адрес:

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

Om seker te maak dat die netwerk tussen peule reg werk, kan ons krul vanaf 'n ander peul laat loop:

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

Dit is nogal interessant om in hierdie omgewing te delf en te sien wat werk en wat nie. Ek het gevind dat ConfigMap en Secret werk soos verwag, maar Diens en Ontplooiing nie.

Sukses!

Hierdie pos raak lank, so ek gaan oorwinning verklaar en sê dat dit 'n lewensvatbare konfigurasie is wat "Kubernetes" genoem kan word. Om op te som: vier binaries, vyf command line parameters en "net" 45 reëls van YAML (nie soveel volgens standaarde Kubernetes) en ons het 'n hele paar dinge wat werk:

  • Peule word bestuur met behulp van die gewone Kubernetes API (met 'n paar hacks)
  • U kan publieke houerprente oplaai en bestuur
  • Peule bly lewendig en herbegin outomaties
  • Netwerk tussen peule binne dieselfde nodus werk redelik goed
  • ConfigMap, Geheime en eenvoudige bergingsmontering werk soos verwag

Maar baie van wat Kubernetes werklik nuttig maak, ontbreek steeds, soos:

  • Peulskeduleerder
  • Stawing/magtiging
  • Veelvuldige nodusse
  • Netwerk van dienste
  • Gegroepeerde interne DNS
  • Beheerders vir diensrekeninge, ontplooiings, integrasie met wolkverskaffers en meeste van die ander lekkernye wat Kubernetes bring

So wat het ons eintlik gekry? Die Kubernetes API, wat op sy eie loop, is eintlik net 'n platform vir houer outomatisering. Dit doen nie veel nie - dit is 'n werk vir verskeie beheerders en operateurs wat die API gebruik - maar dit bied 'n konsekwente omgewing vir outomatisering.

Kom meer te wete oor die kursus in die gratis webinar.

Lees meer:

Bron: will.com

Voeg 'n opmerking