Minimum Viable Kubernetes

Përkthimi i artikullit është përgatitur para fillimit të kursit «Praktikat dhe mjetet DevOps».

Minimum Viable Kubernetes

If you're reading this, you probably have heard something about Kubernetes (and if not, how did you end up here?) But what exactly is Kubernetes? It is “Enterprise-Grade Container Orchestration”? ИлО “Cloud-Native Operating System”? Đ§Ń‚ĐŸ ĐČĐŸĐŸĐ±Ń‰Đ” ŃŃ‚ĐŸ Đ·ĐœĐ°Ń‡ĐžŃ‚?

Honestly, I'm not 100% sure. But I think it's interesting to dig into the internals and see what really happens in Kubernetes under its many layers of abstraction. So, out of curiosity, let's see what a minimal “Kubernetes cluster” actually looks like. (It will be much simpler than Kubernetes The Hard Way.)

I assume you have a basic understanding of Kubernetes, Linux, and containers. Everything we’ll discuss here is intended only for exploration/study; do not run any of this in production!

Përmbledhje

Kubernetes contains many components. According to Wikipedia, the architecture looks as follows:

Minimum Viable Kubernetes

Here are at least eight components shown, but we will ignore most of them. I want to state that the minimal thing that can reasonably be called Kubernetes consists of three main components:

  • kubelet
  • kube-apiserver (which depends on etcd — its database)
  • container runtime (in this case, Docker)

Let's see what is mentioned about each of them in the documentation (rus., eng.) First kubelet:

An agent running on each node in the cluster. It ensures that containers are running in the pod.

Sounds pretty straightforward. What about the container runtime? (container runtime)?

A container runtime is a program designed to execute containers.

Very informative. But if you're familiar with Docker, you should have a general idea of what it does. (The details about the division of responsibilities between the container runtime and kubelet are actually quite subtle and I won’t delve into them here.)

DHE API server?

The API server is a Kubernetes control plane component that exposes the Kubernetes API. The API server is the client-facing part of the Kubernetes control plane.

Çdo person qĂ« ka bĂ«rĂ« ndonjĂ«herĂ« diçka me Kubernetes ka pasur kontakt me API-nĂ« ose drejtpĂ«rdrejt ose pĂ«rmes kubectl. Kjo Ă«shtĂ« thelbi i asaj qĂ« e bĂ«n Kubernetes Kubernetes — truri qĂ« kthen malet e YAML, qĂ« tĂ« gjithĂ« i njohim dhe i duam (?), nĂ« njĂ« infrastrukturĂ« funksionale. Duket e qartĂ« se API duhet tĂ« jetĂ« e pranishme nĂ« konfigurimin tonĂ« minimal.

Kërkesat paraardhëse

  • NjĂ« makinĂ« virtuale ose fizike Linux me akses root (unĂ« pĂ«rdor Ubuntu 18.04 nĂ« njĂ« makinĂ« virtuale).
  • Dhe kjo Ă«shtĂ« e gjitha!

Instalim i mërzitshëm

Në makinën që do të përdorim, ne duhet të instalojmë Docker. (Nuk do të flas në detaje se si funksionon Docker dhe kontejnerët; nëse jeni të interesuar, ka artikuj të mrekullueshëm). Le të instalohet me apt:

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

Pasi tĂ« bĂ«het kjo, na nevojiten binarĂ«t e Kubernetes. NĂ« fakt, pĂ«r tĂ« filluar “klasterin” tonĂ«, na nevojitet vetĂ«m kubelet, pasi pĂ«r tĂ« mundĂ«suar komponentĂ«t e tjerĂ« server, ne do tĂ« mund tĂ« pĂ«rdorim kubelet. PĂ«r tĂ« ndĂ«rvepruar me klasterin tonĂ« pasi tĂ« nisĂ«, ne gjithashtu do tĂ« pĂ«rdorim 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Ă« do tĂ« ndodhĂ« nĂ«se thjesht e nisim kubelet?

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

kubelet duhet të punojë nga root. Mjaft e arsyeshme, pasi duhet të menaxhojë të gjithë nyjën. Le të shohim parametrat e tij:

$ ./kubelet -h

$ ./kubelet -h | wc -l
284

Wow, ka shumë opsione! Për fat të mirë, na nevojiten vetëm disa nga ato. Ja një nga parametrat që na intereson:

--pod-manifest-path string

Rruga pĂ«r katalogun qĂ« pĂ«rmban skedarĂ«t pĂ«r podet statike, ose rruga pĂ«r njĂ« skedĂ« pĂ«r pĂ«rshkrimin e podĂ«ve statike. SkedarĂ«t qĂ« fillojnĂ« me pika injorohen. (E DËMTUAR: ky parameter duhet tĂ« vendoset nĂ« skedarin e konfigurimit, tĂ« cilin e kalojmĂ« nĂ« Kubelet pĂ«rmes opsionit —config. PĂ«r mĂ« shumĂ« informacion shih kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Ky parametrin na lejon tĂ« nisim podet statike — pods qĂ« nuk administrohen pĂ«rmes API-sĂ« Kubernetes. Pods statike pĂ«rdoren rrallĂ«, por ato janĂ« shumĂ« tĂ« dobishme pĂ«r ngritjen e shpejtĂ« tĂ« njĂ« klasteri, dhe kjo Ă«shtĂ« pikĂ«risht ajo qĂ« na nevojitet. Do ta injorojmĂ« kĂ«tĂ« paralajmĂ«rim tĂ« zhurmshĂ«m (sĂ«rish, mos e aktivizoni kĂ«tĂ« nĂ« prodhim!) dhe do tĂ« shohim nĂ«se mund ta nisim podin.

Fillimisht do të krijojmë një katalog për pods statike dhe do ta aktivizojmë. kubelet:

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

Pastaj në një terminal tjetër/faqe tmux/ndonjë vend tjetër, do të krijojmë manifestin e podit:

$ cat < 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 se nuk po ndodh asgjë. Por nuk është kështu! Le të shohim në 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 lexoi manifestin e podit dhe i dha Docker-it urdhrin pĂ«r tĂ« nisur disa kontejnerĂ« sipas specifikimit tonĂ«. (NĂ«se jeni tĂ« interesuar tĂ« dini rreth kontejnerit “pause”, kjo Ă«shtĂ« njĂ« hile Kubernetes — detajet mund tĂ« shikoni nĂ« kĂ«to blog.) Kubelet do tĂ« aktivizojĂ« kontejnerin tonĂ« busybox me urdhrin e specifikuar dhe do ta rinisĂ« atĂ« pafundĂ«sisht derisa podi statik tĂ« hiqet.

Keni arsye t'i përgëzoni vetes. Sapo kemi krijuar një nga mënyrat më të komplikuara për të nxjerrë tekst në terminal!

Po aktivizojmë etcd

Qëllimi ynë përfundimtar është aktivizimi i Kubernetes API, por për këtë na nevojitet më parë të aktivizojmë etcd. Le ta aktivizojmë një klaster minimal etcd, duke vendosur configurimet e tij në katalogun pods (p.sh., 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ëto skedarë YAML duhet të jenë të njohur për ju. Ka vetëm dy aspekte për të theksuar këtu:

Ne montuam dosjen e hostit /var/lib/etcd në pod, në mënyrë që të dhënat e etcd të ruhen pas rinisjes (nëse nuk e bëni këtë, gjendja e klasterit do të fshihet me çdo rinisje të pod-it, që do të ishte problematike edhe për një instalim minimal të Kubernetes).

Ne kemi instaluar hostNetwork: true. Ky parametër, që nuk është befasi, konfigurimi etcd për të përdorur rrjetin e hostit në vend të rrjetit të brendshëm të pod-it (kjo do ta lehtësojë gjetjen e klasterit etcd nga API-server).

Një kontroll i thjeshtë tregon se etcd në të vërtetë është në funksionim 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 API-serverit

Nisja e API-serverit Kubernetes është edhe më e thjeshtë. Parametrin e vetëm që duhet të kaloni, --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

Vendosni kĂ«tĂ« skedari YAML nĂ« katalog pods, dhe API-server do tĂ« niset. NjĂ« kontroll me curl tregon se API i Kubernetes po dĂ«gjon portin 8080 me akses tĂ« plotĂ« — nuk kĂ«rkohet autentifikim!

$ 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 këtë në prodhim! Kam qenë paksa i befasuar që konfigurimi i parazgjedhur është kaq i pa sigurt. Por supozoj se është bërë për të lehtësuar zhvillimin dhe testimin.)

Dhe, një surprizë e këndshme, kubectl funksionon nga kutia pa ndonjë konfigurim 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.

Problemi

Por nëse shfletojmë pak më thellë, duket se diçka nuk po shkon siç duhet:

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

Pods statike që kemi krijuar janë zhdukur! Në të vërtetë, nodi ynë kubelet nuk po zbulon asnjëherë:

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

ÇfarĂ« ndodh? NĂ«se e mbani mend, disa paragrafe mĂ« parĂ« lançuam kubelet me njĂ« grup shumĂ« tĂ« thjeshtĂ« parametrash tĂ« komandĂ«s, prandaj kubelet nuk e di si tĂ« lidhet me serverin API dhe ta njoftojĂ« pĂ«r gjendjen e tij. Duke studiuar dokumentacionin, gjejmĂ« flamurin pĂ«rkatĂ«s:

--kubeconfig string

Rruga drejt skedarit kubeconfig, i cili tregon si të lidheni me serverin API. Prania e --kubeconfig aktivizon modin e serverit API, mungesa e --kubeconfig aktivizon modin autonom.

Gjatë gjithë këtij kohë, pa e ditur, kemi lançuar kubelet në "modin autonom". (Nëse do të ishim pedantë, mund të konsideronim modin autonom të kubelet si "Kubernetes minimalisht funksional", por kjo do të ishte shumë e mërzitshme). për të funksionuar konfigurimi "real", na nevojitet të kalojmë skedarin kubeconfig tek kubelet, që ai të dijë si të komunikojë me serverin API. Me fat, kjo është mjaft e thjeshtë (pasi nuk kemi probleme me autentifikimin ose sertifikatat):

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

Ruani këtë si kubeconfig.yaml, ndaloni procesin kubelet dhe rilansoni me parametrat e nevojshme:

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

(Me rastin e kësaj, nëse provoni të qaseni në API përmes curl, kur kubelet nuk punon, do të zbuloni se ai është ende në funksion! Kubelet nuk është "prindi" i podëve të tij, siç është Docker, ai është më shumë si një "demon menaxhues". Kontejnerët e menaxhuar nga kubelet do të punojnë derisa kubelet t'i ndalojë ata.)

Pas disa minutash kubectl duhet të na tregojë podët dhe nyjat, siç e 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       21h   v1.18.5   10.70.10.228           Ubuntu 18.04.4 LTS   4.15.0-109-generic   docker://19.3.6

Tani pĂ«r kĂ«tĂ« herĂ«, le tĂ« pĂ«rgĂ«zojmĂ« vetveten vĂ«rtet (e di, qĂ« tashmĂ« kam pĂ«rgĂ«zuar) — kemi krijuar njĂ« "klaster" minimal Kubernetes qĂ« funksionon me API tĂ« plotĂ«!

Të lançojmë podin

Tani le të shohim çfarë mund të bëjë API. Le të fillojmë me podin nginx:

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

Këtu do të marrim 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 sa e tmerrshme Ă«shtĂ« mjedisi ynĂ« Kubernetes — nuk kemi llogari shĂ«rbimi. Le tĂ« provojmĂ« pĂ«rsĂ«ri, duke krijuar njĂ« llogari shĂ«rbimi manualisht dhe tĂ« shohim se çfarĂ« do tĂ« 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 manualisht llogarinë e shërbimit, tokeni i autentikimit nuk krijohet. Ndërsa vazhdojmë të eksperimentojmë me "klusterin" tonë minimalist, do të zbulojmë se shumica e gjërave të dobishme, të cilat zakonisht ndodhin automatikisht, do të mungojnë. Serveri Kubernetes API është mjaft minimalist, pjesa më e madhe e konfigurimeve të rënda automatike ndodhin në kontrollet e ndryshme dhe punët në sfond që ende nuk po ekzekutohen.

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

$ 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

NĂ« fund, podi u shfaq! Por nĂ« tĂ« vĂ«rtetĂ« ai nuk do tĂ« nisĂ«, sepse nuk kemi planifikuesin (scheduler) — njĂ« tjetĂ«r komponent i rĂ«ndĂ«sishĂ«m i Kubernetes. PĂ«rsĂ«ri, shohim se API Kubernetes Ă«shtĂ« befasuese "e paaftĂ«" — kur krijoni njĂ« pod nĂ« API, ai e regjistron atĂ«, por nuk pĂ«rpiqet tĂ« kuptojĂ« se nĂ« cilin nod do ta ekzekutojĂ«.

Në të vërtetë, për të nisur një pod nuk nevojitet një planifikues. Mund të shtoni manualisht një nod në manifest në parametrin nodeName:

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

(Zëvendësoni mink8s me emrin e nodit.) Pasi të fshini dhe aplikoni, shohim se nginx ka startuar dhe po dëgjon adresën IP të brendshme:

$ .\/kubectl fshi pod nginx
pod "nginx" u fshi
$ .\/kubectl aplikoni -f nginx.yaml
pod\/nginx u krijua
$ .\/kubectl merrni pod-et -owide
EMRI    GATI   STATUS    RIMEDHJE   MOSHAT   IP           NODE     NODE I NOMINUAR   PORTAT E GATSHMËRISË
nginx   1\/1     Duke funksionuar   0          30s   172.17.0.2   mink8s   <none>           <none>
$ curl -s 172.17.0.2 | head -4
<!DOCTYPE html>
<html>
<head>
<title>Mirë se vini në nginx!</title>

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

$ cat &lt;&lt;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>Mirë se vini në nginx!</title>

ËshtĂ« mjaft interesante tĂ« eksperimentosh nĂ« kĂ«tĂ« mjedis dhe tĂ« shohĂ«sh çfarĂ« funksionon dhe çfarĂ« jo. Kam zbuluar se ConfigMap dhe Secret funksionojnĂ« siç pritet, ndĂ«rsa ShĂ«rbimi dhe Implementimi jo.

Sukses!

Ky kyç po bëhet i madh, prandaj do të shpall fitoren dhe do të them se kjo është një konfigurim i qëndrueshëm, i quajtur "Kubernetes". Në përmbledhje: katër skedarë binarë, pesë parametra të linjës së komandës dhe "shumë pak" 45 rreshta YAML (nuk është aq shumë sipas standardeve të Kubernetes) dhe kemi një sërë gjërash që funksionojnë:

  • Pods menaxhohen me API-nĂ« e zakonshĂ«m tĂ« Kubernetes (me disa hacks)
  • Mund tĂ« ngarkoni imazhe publike tĂ« kontejnerĂ«ve dhe t'i menaxhoni ato
  • Pods qĂ«ndrojnĂ« aktive dhe ri-startohen automatikisht
  • Rrjeti midis pods brenda njĂ« nodi funksionon mjaft mirĂ«
  • ConfigMap, Secret dhe montimi mĂ« i thjeshtĂ« i ruajtjes funksionojnĂ« siç duhet

Por shumë nga ajo që e bën Kubernetes me të vërtetë të dobishëm, ende mungon, për shembull:

  • Planifikuesi i pods
  • Autentikimi / autorizimi
  • Disa nod
  • Rrjeti i shĂ«rbimeve
  • DNS-i i brendshĂ«m i klasterit
  • KontrollerĂ«t pĂ«r llogaritĂ« e shĂ«rbimeve, vendosjet, integrimin me ofruesit e cloud dhe shumicĂ«n e "mishave" tĂ« tjera qĂ« sjell Kubernetes

Pra, çfarë kemi marrë për të vërtetë? API e Kubernetes-it, e cila funksionon vetë, në të vërtetë është thjesht një platformë për automatizimin e konteinerëve.Ajo nuk bën shumë - kjo është puna për kontrollet dhe operatorët e ndryshëm që përdorin API-në, - por ofron një ambient të qëndrueshëm për automatizim.

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

Lexoni më shumë:

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster