Kubernetet minimale të zbatueshme

Përkthimi i artikullit u përgatit në prag të fillimit të kursit "Praktikat dhe mjetet e DevOps".

Kubernetet minimale të zbatueshme

Nëse po e lexoni këtë, me siguri keni dëgjuar diçka për Kubernetes (dhe nëse jo, si përfunduat këtu?) Por çfarë është saktësisht Kubernetes? Kjo "Orkestrimi i kontejnerëve të klasës industriale"? Ose "Sistemi operativ Cloud-Native"? Madje çfarë do të thotë kjo?

Për të qenë i sinqertë, nuk jam 100% i sigurt. Por unë mendoj se është interesante të gërmosh në brendësi dhe të shohësh se çfarë po ndodh vërtet në Kubernetes nën shtresat e shumta të abstraksioneve. Pra, thjesht për argëtim, le të hedhim një vështrim se si duket në të vërtetë një "grup Kubernetes" minimal. (Kjo do të jetë shumë më e lehtë se Kubernetes Rruga e Vështirë.)

Supozoj se keni njohuri bazë të Kubernetes, Linux dhe kontejnerëve. Gjithçka për të cilën flasim këtu është vetëm për qëllime kërkimore/mësimore, mos e vendosni asnjë prej tyre në prodhim!

Rishikimi

Kubernetes përmban shumë komponentë. Sipas wikipedia, arkitektura duket si kjo:

Kubernetet minimale të zbatueshme

Janë të paktën tetë komponentë të paraqitur këtu, por ne do t'i injorojmë shumicën e tyre. Dua të them se gjëja minimale që mund të quhet me arsye Kubernetes përbëhet nga tre komponentë kryesorë:

  • kubelet
  • kube-apiserver (i cili varet nga etcd - databaza e tij)
  • Koha e funksionimit të kontejnerit (Docker në këtë rast)

Le të shohim se çfarë thotë dokumentacioni për secilën prej tyre (rusisht., Anglisht.). Ne fillim kubelet:

Një agjent që funksionon në secilën nyje në grup. Siguron që kontejnerët të funksionojnë në pod.

Tingëllon mjaft e thjeshtë. Po për kohëzgjatja e kontejnerëve (koha e funksionimit të kontejnerit)?

Koha e funksionimit të kontejnerit është një program i krijuar për të ekzekutuar kontejnerët.

Shumë informues. Por nëse jeni njohur me Docker, atëherë duhet të keni një ide të përgjithshme se çfarë bën. (Detajet e ndarjes së përgjegjësive midis kohëzgjatjes së kontejnerit dhe kubelet janë në të vërtetë mjaft delikate dhe unë nuk do të hyj në to këtu.)

И Serveri API?

Serveri API është komponenti i panelit të kontrollit Kubernetes që ekspozon API-në e Kubernetes. Serveri API është ana e klientit të panelit të kontrollit Kubernetes

Kushdo që ka bërë ndonjëherë ndonjë gjë me Kubernetes është dashur të ndërveprojë me API-në ose drejtpërdrejt ose përmes kubectl. Kjo është zemra e asaj që e bën Kubernetes Kubernetes - truri që i kthen malet e YAML që të gjithë i njohim dhe i duam (?) në infrastrukturë pune. Duket qartë se API duhet të jetë i pranishëm në konfigurimin tonë minimal.

Parakushtet

  • Makinë virtuale ose fizike Linux me qasje rrënjësore (Unë jam duke përdorur Ubuntu 18.04 në një makinë virtuale).
  • Dhe është gjithçka!

Instalim i mërzitshëm

Duhet të instalojmë Docker në makinën që do të përdorim. (Nuk do të hyj në detaje se si funksionojnë Docker dhe kontejnerët; nëse jeni të interesuar, ka artikuj të mrekullueshëm). Le ta instalojmë vetëm me apt:

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

Pas kësaj, ne duhet të marrim binaret Kubernetes. Në fakt, për nisjen fillestare të "grupit" tonë na duhet vetëm kubelet, pasi për të ekzekutuar komponentë të tjerë të serverit ne mund të përdorim kubelet. Për të bashkëvepruar me grupin tonë pasi të funksionojë, ne do të përdorim gjithashtu 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

Çfarë ndodh nëse thjesht vrapojmë kubelet?

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

kubelet duhet të funksionojë si rrënjë. Shumë logjike, pasi ai duhet të menaxhojë të gjithë nyjen. Le të shohim parametrat e tij:

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

Wow, kaq shumë opsione! Për fat të mirë, na duhen vetëm disa prej tyre. Këtu është një nga parametrat që na intereson:

--pod-manifest-path string

Rruga drejt drejtorisë që përmban skedarë për pods statike, ose shtegu drejt një skedari që përshkruan pods statike. Skedarët që fillojnë me pika injorohen. (I DEPRECATED: Ky opsion duhet të vendoset në skedarin e konfigurimit të kaluar në Kubelet nëpërmjet opsionit --config. Për më shumë informacion, shih kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Ky opsion na lejon të ekzekutojmë bishtaja statike — pods që nuk menaxhohen nëpërmjet Kubernetes API. Bishtajat statike përdoren rrallë, por ato janë shumë të përshtatshme për të ngritur shpejt një grup, dhe kjo është pikërisht ajo që na nevojitet. Ne do ta injorojmë këtë paralajmërim të madh (përsëri, mos e ekzekutoni këtë në prodhim!) dhe do të shohim nëse mund ta bëjmë podin të funksionojë.

Së pari ne do të krijojmë një direktori për pods statike dhe do të ekzekutojmë kubelet:

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

Pastaj, në një dritare tjetër terminal/tmux/çfarëdo, ne do të krijojmë një 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 fillon të shkruajë disa paralajmërime dhe duket sikur asgjë nuk po ndodh. Por kjo nuk është e vërtetë! Le të shohim 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 Lexova manifestin e pod dhe i dhashë Docker komandën për të nisur disa kontejnerë sipas specifikimeve tona. (Nëse po pyesni veten për kontejnerin "pauzë", është një hak i Kubernetes - shih këtë blog.) Kubelet do të lëshojë kontejnerin tonë busybox me komandën e specifikuar dhe do ta rifillojë atë për një kohë të pacaktuar derisa të fshihet pod statike.

Përgëzoje veten. Sapo dolëm me një nga mënyrat më konfuze për të nxjerrë tekst në terminal!

Nisja etj

Qëllimi ynë përfundimtar është të ekzekutojmë Kubernetes API, por për ta bërë këtë, së pari duhet të ekzekutojmë etj. Le të fillojmë një grup minimal etcd duke vendosur cilësimet e tij në direktorinë e pods (për shembull, 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

Nëse keni punuar ndonjëherë me Kubernetes, këta skedarë YAML duhet të jenë të njohur për ju. Këtu vlen të përmenden vetëm dy pika:

Ne kemi montuar dosjen e hostit /var/lib/etcd në pod në mënyrë që të dhënat e etcd të ruhen pas një rinisjeje (nëse kjo nuk bëhet, gjendja e grupit do të fshihet sa herë që pod riniset, gjë që nuk do të jetë e mirë as për një instalim minimal Kubernetes).

Ne kemi instaluar hostNetwork: true. Ky cilësim, çuditërisht, konfiguron etcd për të përdorur rrjetin pritës në vend të rrjetit të brendshëm të pod (kjo do ta bëjë më të lehtë për serverin API gjetjen e grupit etcd).

Një kontroll i thjeshtë tregon se etcd po funksionon me të vërtetë në localhost dhe ruan të dhënat në 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

Nisja e serverit API

Drejtimi i një serveri Kubernetes API është edhe më i lehtë. Parametri i vetëm që duhet të kalohet është --etcd-servers, bën atë që prisni:

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

Vendoseni këtë skedar YAML në drejtori pods, dhe serveri API do të fillojë. Duke kontrolluar me curl tregon se Kubernetes API po dëgjon në portin 8080 me akses plotësisht të hapur - nuk kërkohet vërtetim!

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

(Përsëri, mos e përdorni këtë në prodhim! U habita pak që cilësimi i paracaktuar është kaq i pasigurt. Por mendoj se kjo është për ta bërë më të lehtë zhvillimin dhe testimin.)

Dhe, surprizë e këndshme, kubectl funksionon jashtë kutisë pa asnjë cilësim shtesë!

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

Por nëse gërmoni pak më thellë, duket se diçka po shkon keq:

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

Bishtajat statike që krijuam janë zhdukur! Në fakt, nyja jonë kubelet nuk është zbuluar fare:

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

Per Cfarë bëhet fjalë? Nëse ju kujtohet disa paragrafë më parë, ne e filluam kubelet me një grup jashtëzakonisht të thjeshtë parametrash të linjës së komandës, kështu që kubelet nuk di se si të kontaktojë serverin API dhe ta njoftojë atë për gjendjen e tij. Pas studimit të dokumentacionit, gjejmë flamurin përkatës:

--kubeconfig string

Rruga për në skedar kubeconfig, i cili specifikon se si të lidheni me serverin API. Disponueshmëria --kubeconfig aktivizon modalitetin e serverit API, nr --kubeconfig aktivizon modalitetin offline.

Gjatë gjithë kësaj kohe, pa e ditur, ne po përdornim kubelet në "modalitetin offline". (Nëse do të ishim pedantë, mund të mendonim për një kubelet të pavarur si "Kubernetes minimale të zbatueshme", por kjo do të ishte shumë e mërzitshme). Për ta bërë konfigurimin "real" të funksionojë, ne duhet të kalojmë skedarin kubeconfig në kubelet në mënyrë që ai të dijë se si të flasë me serverin API. Për fat të mirë është mjaft e thjeshtë (pasi nuk kemi ndonjë problem vërtetimi ose certifikate):

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

Ruaje këtë si kubeconfig.yaml, vrasin procesin kubelet dhe rinisni me parametrat e nevojshëm:

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

(Meqë ra fjala, nëse përpiqeni të hyni në API përmes curl kur kubelet nuk po funksionon, do të zbuloni se ai ende po funksionon! Kubelet nuk është një "prind" i pod-eve të tij si Docker, ai është më shumë si një "kontroll Daemon." Kontejnerët e menaxhuar nga një kubelet do të vazhdojnë të funksionojnë derisa kubelet t'i ndalojë ato.)

Në pak minuta kubectl duhet të na tregojë podet dhe nyjet siç presim:

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

Le të urojmë vërtet veten këtë herë (e di që tashmë e kam uruar veten) - ne kemi një "grup" minimal Kubernetes që funksionon me një API plotësisht funksionale!

Ne nisim nën

Tani le të shohim se çfarë është në gjendje API. Le të fillojmë me podin nginx:

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

Këtu kemi një gabim mjaft interesant:

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

Këtu shohim se sa mjerisht i paplotë është mjedisi ynë Kubernetes - ne nuk kemi llogari për shërbime. Le të provojmë përsëri duke krijuar manualisht një llogari shërbimi dhe të shohim se çfarë ndodh:

$ 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

Edhe kur krijuam llogarinë e shërbimit manualisht, shenja e vërtetimit nuk gjenerohet. Ndërsa vazhdojmë të eksperimentojmë me "grupin" tonë minimalist, do të zbulojmë se shumica e gjërave të dobishme që zakonisht ndodhin automatikisht do të mungojnë. Serveri Kubernetes API është mjaft minimalist, me shumicën e ngritjes së rëndë dhe konfigurimit automatik që ndodh në kontrollues të ndryshëm dhe punë në sfond që ende nuk janë duke u ekzekutuar.

Ne mund ta zgjidhim këtë problem duke vendosur opsionin automountServiceAccountToken për llogarinë e shërbimit (pasi nuk do të na duhet ta përdorim gjithsesi):

$ 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

Më në fund, gjilpëra u shfaq! Por në fakt nuk do të fillojë sepse ne nuk kemi planifikues (programuesi) është një tjetër komponent i rëndësishëm i Kubernetes. Përsëri, ne shohim se API Kubernetes është çuditërisht "memec" - kur krijoni një Pod në API, ai e regjistron atë, por nuk përpiqet të kuptojë se në cilën nyje duhet ta ekzekutojë.

Në fakt, nuk ju nevojitet një planifikues për të drejtuar një pod. Mund të shtoni manualisht një nyje në manifest në parametër nodeName:

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

(Zëvendësoni mink8s në emrin e nyjës.) Pas fshirjes dhe aplikimit, shohim që nginx ka filluar dhe po dëgjon adresën e brendshme 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>

Për t'u siguruar që rrjeti ndërmjet podave po funksionon siç duhet, mund të ekzekutojmë curl nga një pod tjetër:

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

Është mjaft interesante të gërmosh në këtë mjedis dhe të shohësh se çfarë funksionon dhe çfarë jo. Zbulova se ConfigMap dhe Secret funksionojnë siç pritej, por Shërbimi dhe Vendosja jo.

Urgjent!

Ky postim po zgjatet, kështu që unë do të shpall fitoren dhe do të them se ky është një konfigurim i zbatueshëm që mund të quhet "Kubernetes". Për ta përmbledhur: katër binare, pesë parametra të linjës komanduese dhe "vetëm" 45 rreshta YAML (jo aq shumë sipas standardeve Kubernetes) dhe ne kemi mjaft gjëra që funksionojnë:

  • Pods menaxhohen duke përdorur API-në e rregullt të Kubernetes (me disa hakime)
  • Mund të ngarkoni dhe menaxhoni imazhe të kontejnerëve publikë
  • Pods mbeten të gjalla dhe rinisin automatikisht
  • Rrjetëzimi midis pods brenda së njëjtës nyje funksionon mjaft mirë
  • ConfigMap, montimi sekret dhe i thjeshtë i ruajtjes funksionojnë siç pritej

Por shumë nga ato që e bëjnë Kubernetes vërtetë të dobishëm ende mungojnë, të tilla si:

  • Pod Scheduler
  • Autentifikimi/autorizimi
  • Nyje të shumta
  • Rrjeti i shërbimeve
  • DNS e brendshme e grumbulluar
  • Kontrollorët për llogaritë e shërbimeve, vendosjet, integrimin me ofruesit e cloud dhe shumicën e të mirave të tjera që sjell Kubernetes

Pra, çfarë morëm në të vërtetë? Kubernetes API, që funksionon më vete, është me të vërtetë vetëm një platformë për të automatizimi i kontejnerëve. Nuk bën shumë - është një punë për kontrollues dhe operatorë të ndryshëm që përdorin API - por siguron një mjedis të qëndrueshëm për automatizimin.

Mësoni më shumë rreth kursit në webinarin falas.

Lexo më shumë:

Burimi: www.habr.com

Shto një koment