Minimalno sposobni Kubernetes

Prevod članka je bil pripravljen na predvečer začetka tečaja "Prakse in orodja DevOps".

Minimalno sposobni Kubernetes

Če berete to, ste verjetno že slišali nekaj o Kubernetes (in če ne, kako ste končali tukaj?) Toda kaj točno je Kubernetes? to "Orkestracija industrijskih kontejnerjev"? ali "Izvorni operacijski sistem v oblaku"? Kaj to sploh pomeni?

Če sem iskren, nisem 100% prepričan. Ampak mislim, da se je zanimivo poglobiti v notranjost in videti, kaj se v resnici dogaja v Kubernetesu pod njegovimi številnimi plastmi abstrakcij. Torej samo za zabavo si poglejmo, kako dejansko izgleda minimalna »gruča Kubernetes«. (To bo veliko lažje kot Kubernetes Težka pot.)

Predvidevam, da imate osnovno znanje o Kubernetesu, Linuxu in vsebnikih. Vse, o čemer govorimo tukaj, je samo za raziskovalne/učne namene, ničesar od tega ne dajajte v proizvodnjo!

Pregled

Kubernetes vsebuje veliko komponent. Po navedbah Wikipedia, je arhitektura videti takole:

Minimalno sposobni Kubernetes

Tukaj je prikazanih najmanj osem komponent, vendar jih večino ne bomo upoštevali. Želim povedati, da je najmanjša stvar, ki jo lahko razumno imenujemo Kubernetes, sestavljena iz treh glavnih komponent:

  • kocka
  • kube-apiserver (ki je odvisen od etcd - njegove baze podatkov)
  • izvajalno okolje vsebnika (v tem primeru Docker)

Poglejmo, kaj piše v dokumentaciji o vsakem od njih (rus., angleščina.). Najprej kocka:

Agent, ki se izvaja na vsakem vozlišču v gruči. Zagotavlja, da posode tečejo v stroku.

Sliši se dovolj preprosto. Kaj pa o čas delovanja vsebnika (čas izvajanja vsebnika)?

Izvajalno okolje vsebnika je program, zasnovan za izvajanje vsebnikov.

Zelo informativno. Toda če poznate Docker, bi morali imeti splošno predstavo o tem, kaj počne. (Podrobnosti o ločevanju odgovornosti med izvajalnim okoljem vsebnika in kubelet so pravzaprav precej subtilne in tukaj se ne bom spuščal vanje.)

И API strežnik?

API Server je komponenta nadzorne plošče Kubernetes, ki razkriva API Kubernetes. Strežnik API je odjemalska stran nadzorne plošče Kubernetes

Vsakdo, ki je kdaj naredil karkoli s Kubernetesom, je moral komunicirati z API-jem neposredno ali prek kubectl. To je srce tistega, zaradi česar je Kubernetes Kubernetes – možgani, ki pretvarjajo gore YAML, ki jih vsi poznamo in ljubimo (?), v delujočo infrastrukturo. Zdi se očitno, da bi moral biti API prisoten v naši minimalni konfiguraciji.

Predpogoji

  • Virtualni ali fizični stroj Linux s korenskim dostopom (na virtualnem računalniku uporabljam Ubuntu 18.04).
  • In to je vse!

Dolgočasna namestitev

Docker moramo namestiti na stroj, ki ga bomo uporabljali. (Ne bom se spuščal v podrobnosti o delovanju Dockerja in vsebnikov; če vas zanima, obstaja čudoviti članki). Samo namestimo ga z apt:

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

Po tem moramo pridobiti binarne datoteke Kubernetes. Pravzaprav za začetni zagon našega »grozda« potrebujemo samo kubelet, saj lahko uporabimo za zagon drugih komponent strežnika kubelet. Za interakcijo z našo gručo po tem, ko se izvaja, bomo uporabili tudi 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 samo tečemo kubelet?

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

kubelet mora delovati kot root. Povsem logično, saj mora upravljati celotno vozlišče. Oglejmo si njegove parametre:

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

Vau, toliko možnosti! Na srečo jih potrebujemo le nekaj. Tukaj je eden od parametrov, ki nas zanima:

--pod-manifest-path string

Pot do imenika, ki vsebuje datoteke za statične pode, ali pot do datoteke, ki opisuje statične pode. Datoteke, ki se začnejo s pikami, so prezrte. (ZASTARELO: to možnost je treba nastaviti v konfiguracijski datoteki, posredovani v Kubelet prek možnosti --config. Za več informacij glejte kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Ta možnost nam omogoča, da tečemo statični stroki — podov, ki se ne upravljajo prek API-ja Kubernetes. Statični stroki se redko uporabljajo, vendar so zelo priročni za hitro vzgojo grozda in to je točno tisto, kar potrebujemo. Prezrli bomo to veliko opozorilo (še enkrat, ne izvajajte tega v produkciji!) in preverili, ali lahko zaženemo sklop.

Najprej bomo ustvarili imenik za statične pode in ga zagnali kubelet:

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

Nato bomo v drugem terminalu/oknu tmux/karkoli ustvarili manifest pod:

$ cat <<EOF > 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. Ampak 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 Prebral sem manifest stroka in Dockerju dal ukaz za zagon nekaj vsebnikov v skladu z našimi specifikacijami. (Če se sprašujete o vsebniku "pause", je to kramp Kubernetes - glejte ta blog.) Kubelet bo lansiral naš vsebnik busybox z navedenim ukazom in ga bo znova zagnal za nedoločen čas, dokler statična enota ne bo izbrisana.

Čestitajte si. Pravkar smo iznašli enega najbolj zmedenih načinov za izpis besedila na terminal!

Zagon itd

Naš končni cilj je zagnati Kubernetes API, a za to moramo najprej zagnati itdd. Zaženimo minimalno gručo etcd tako, da postavimo njene nastavitve 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 kdaj delali s Kubernetesom, bi vam te datoteke YAML morale biti znane. Omeniti velja le dve točki:

Namestili smo gostiteljsko mapo /var/lib/etcd v pod, tako da se podatki etcd ohranijo po ponovnem zagonu (če tega ne storite, bo stanje gruče izbrisano ob vsakem ponovnem zagonu poda, kar ne bo dobro niti za minimalno namestitev Kubernetes).

Namestili smo hostNetwork: true. Ta nastavitev, kar ni presenetljivo, konfigurira etcd za uporabo gostiteljskega omrežja namesto notranjega omrežja poda (to bo strežniku API olajšalo iskanje gruče etcd).

Preprosto preverjanje pokaže, da etcd res deluje na lokalnem gostitelju in shranjuje podatke 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.wal

Zagon strežnika API

Zagon strežnika Kubernetes API je še lažji. Edini parameter, ki ga je treba posredovati, je --etcd-servers, naredi tisto, kar pričakujete:

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

Postavite to datoteko YAML v imenik podsin strežnik API se bo zagnal. Preverjanje z curl kaže, da Kubernetes API posluša na vratih 8080 s popolnoma odprtim dostopom – preverjanje pristnosti ni potrebno!

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

(Še enkrat, ne zaženite tega v produkciji! Bil sem nekoliko presenečen, da je privzeta nastavitev tako nezanesljiva. Vendar predvidevam, da je to zato, da olajšata razvoj in testiranje.)

In, prijetno presenečenje, kubectl deluje takoj, brez dodatnih nastavitev!

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

problem

Toda če poglobite malo globlje, se zdi, da gre nekaj narobe:

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

Statičnih sklopov, ki smo jih ustvarili, ni več! Pravzaprav naše vozlišče kubelet sploh ni odkrito:

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

Kaj je narobe? Če se spomnite pred nekaj odstavki, smo kubelet zagnali z izjemno preprostim nizom parametrov ukazne vrstice, tako da kubelet ne ve, kako stopiti v stik s strežnikom API in ga obvestiti o svojem stanju. Po preučitvi dokumentacije najdemo ustrezno zastavo:

--kubeconfig string

Pot do datoteke kubeconfig, ki določa, kako se povezati s strežnikom API. Razpoložljivost --kubeconfig omogoča način strežnika API, št --kubeconfig omogoča način brez povezave.

Ves ta čas smo, ne da bi vedeli, izvajali kubelet v »načinu brez povezave«. (Če bi bili pedantni, bi lahko o samostojnem kubeletu razmišljali kot o "minimalno sposobnih Kubernetesih", vendar bi bilo to zelo dolgočasno). Da bo »prava« konfiguracija delovala, moramo kubeletu posredovati datoteko kubeconfig, da ve, kako komunicirati s strežnikom API. Na srečo je precej preprosto (ker nimamo nobenih 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, ubijte postopek kubelet in znova zaženite s potrebnimi parametri:

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

(Mimogrede, če poskusite dostopati do API-ja prek curl, ko se kubelet ne izvaja, boste ugotovili, da se še vedno izvaja! Kubelet ni »starš« svojih podov, kot je Docker, je bolj kot »nadzor daemon." Vsebniki, ki jih upravlja kubelet, bodo še naprej delovali, dokler jih kubelet ne ustavi.)

V nekaj minutah kubectl nam mora pokazati pode in vozlišča, kot pričakujemo:

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

Tokrat si res čestitajmo (vem, da sem si že čestital) – imamo minimalno »gručo« Kubernetes, ki teče s popolnoma delujočim API-jem!

Zaženemo pod

Zdaj pa poglejmo, česa je sposoben API. Začnimo s podom nginx:

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

Tukaj dobimo precej zanimivo napako:

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

Tukaj vidimo, kako obupno nepopolno je naše okolje Kubernetes - nimamo računov za storitve. Poskusimo znova z ročnim ustvarjanjem storitvenega računa in poglejmo, kaj se zgodi:

$ 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

Tudi ko smo storitveni račun ustvarili ročno, se žeton za preverjanje pristnosti ne ustvari. Ko bomo še naprej eksperimentirali z našo minimalistično "gručo", bomo ugotovili, da manjka večina uporabnih stvari, ki se običajno zgodijo samodejno. Strežnik Kubernetes API je precej minimalističen, saj se večina težkega dela in samodejne konfiguracije dogaja v različnih krmilnikih in opravilih v ozadju, ki se še ne izvajajo.

To težavo lahko rešimo tako, da nastavimo možnost automountServiceAccountToken za servisni račun (ker nam ga vseeno ne bo treba uporabljati):

$ 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

Končno se je pojavil pod! Toda v resnici se ne začne, ker nimamo načrtovalec (razporejevalnik) je še ena pomembna komponenta Kubernetesa. Spet vidimo, da je API Kubernetes presenetljivo "neumen" - ko ustvarite Pod v API-ju, ga registrira, vendar ne poskuša ugotoviti, na katerem vozlišču naj se izvaja.

Pravzaprav ne potrebujete razporejevalnika za zagon stroka. V parametru lahko manifestu ročno dodate vozlišče nodeName:

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

(Zamenjati mink8s na ime vozlišča.) Po brisanju in uporabi vidimo, da se je nginx zagnal in posluša notranji naslov 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>

Da se prepričamo, da omrežje med podoma 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>Welcome to nginx!</title>

Prav zanimivo je kopati v to okolje in videti, kaj deluje in kaj ne. Ugotovil sem, da ConfigMap in Secret delujeta po pričakovanjih, vendar Service in Deployment ne.

Uspeh!

Ta objava postaja dolga, zato bom razglasil zmago in povedal, da je to izvedljiva konfiguracija, ki jo lahko imenujemo "Kubernetes". Če povzamem: štiri binarne datoteke, pet parametrov ukazne vrstice in "le" 45 vrstic YAML (ne toliko po standardih Kubernetes) in imamo kar nekaj delujočih stvari:

  • Podi se upravljajo z običajnim API-jem Kubernetes (z nekaj vdori)
  • Lahko naložite in upravljate slike javnih vsebnikov
  • Stroki ostanejo živi in ​​se samodejno znova zaženejo
  • Mreženje med podi znotraj istega vozlišča deluje zelo dobro
  • ConfigMap, Secret in preprosta namestitev pomnilnika delujejo po pričakovanjih

Toda še vedno manjka veliko tega, zaradi česar je Kubernetes resnično uporaben, na primer:

  • Pod razporejevalnik
  • Avtentikacija/avtorizacija
  • Več vozlišč
  • Mreža storitev
  • Notranji DNS v gruči
  • Krmilniki za storitvene račune, uvedbe, integracijo s ponudniki oblaka in večino drugih dobrot, ki jih prinaša Kubernetes

Kaj smo torej pravzaprav dobili? Kubernetes API, ki deluje sam, je v resnici samo platforma za avtomatizacija kontejnerjev. Ne naredi veliko - to je delo za različne krmilnike in operaterje, ki uporabljajo API -, vendar zagotavlja dosledno okolje za avtomatizacijo.

Več o tečaju na brezplačnem spletnem seminarju.

Preberi več:

Vir: www.habr.com

Dodaj komentar