Мінімальна жыццяздольны Kubernetes

Пераклад артыкула падрыхтаваны напярэдадні старту курса "DevOps практыкі і інструменты".

Мінімальна жыццяздольны Kubernetes

Калі вы гэта праглядаеце, верагодна, вы нешта чулі пра Kubernetes (а калі не, то як вы тут апынуліся?) Але што ж на самой справе ўяўляе сабой Kubernetes? Гэта "Аркестрацыя кантэйнераў прамысловага ўзроўню"? або "Cloud-Native Operating System"? Што ўвогуле гэта значыць?

Сапраўды кажучы, я не ўпэўнены на 100%. Але думаю цікава пакапацца ва вантробах і паглядзець, што насамрэч адбываецца ў Kubernetes пад яго шматлікімі пластамі абстракцый. Так што дзеля цікавасці, давайце паглядзім, як насамрэч выглядае мінімальны "кластар Kubernetes". (Гэта будзе нашмат прасцей, чым Kubernetes The Hard Way.)

Я мяркую, што ў вас есць базавыя веды Kubernetes, Linux і кантэйнераў. Усё, пра што мы тут будзем казаць прызначана толькі для даследавання/вывучэння, не запускайце нічога з гэтага ў прадакшэне!

Агляд

Kubernetes змяшчае шмат кампанент. вікіпедыі, архітэктура выглядае наступным чынам:

Мінімальна жыццяздольны Kubernetes

Тут паказана, прынамсі, восем кампанент, але большасць з іх мы праігнаруем. Я хачу заявіць, што мінімальная рэч, якую можна абгрунтавана назваць Kubernetes, складаецца з трох асноўных кампанентаў:

  • кубелет
  • kube-apiserver (які залежыць ад etcd – яго базы дадзеных)
  • асяроддзе выканання кантэйнера (у дадзеным выпадку Docker)

Давайце паглядзім, што аб кожным з іх гаворыцца ў дакументацыі (Руская., англ.). Спачатку кубелет:

Агент, які працуе на кожным вузле ў кластары. Ён сочыць за тым, каб кантэйнеры былі запушчаны ў подзе.

Гучыць дастаткова проста. Што наконт асяроддзя выканання кантэйнераў (container runtime)?

Асяроддзе выканання кантэйнера - гэта праграма, прызначаная для выканання кантэйнераў.

Вельмі інфарматыўна. Але калі вы знаёмыя з Docker, то ў вас павінна быць агульнае ўяўленне аб тым, што ен робіць. (Дэталі падзелу адказнасцяў паміж асяроддзем выканання кантэйнераў і kubelet на самой справе даволі тонкія і тут я не буду ў іх паглыбляцца.)

И API-сервер?

Сервер API – кампанент Kubernetes панэлі кіравання, які ўяўляе API Kubernetes.

Любому, хто калі-небудзь штосьці рабіў з Kubernetes, даводзілася ўзаемадзейнічаць з API альбо наўпрост, альбо праз kubectl. Гэта сэрца таго, што робіць Kubernetes Kubernetes'ам - мозг, які ператварае горы YAML, які мы ўсе ведаем і любім (?), у працуючую інфраструктуру. Здаецца відавочным, што API павінен прысутнічаць у нашай мінімальнай канфігурацыі.

папярэднія ўмовы

  • Віртуальная ці фізічная машына Linux з root-доступам (я выкарыстоўваю Ubuntu 18.04 на віртуальнай машыне).
  • І гэта ўсё!

Сумная ўстаноўка

На машыну, якую мы будзем выкарыстоўваць, неабходна ўсталяваць Docker. (Я не збіраюся падрабязна расказваць як працуе Docker і кантэйнеры; калі вам цікава, ёсць выдатныя артыкулы). Давайце проста ўсталюем яго з дапамогай apt:

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

Пасля гэтага нам трэба атрымаць бінарнікі Kubernetes. Насамрэч для пачатковага запуску нашага "кластара" нам патрэбен толькі kubelet, так як для запуску іншых серверных кампанентаў мы зможам выкарыстоўваць kubelet. Для ўзаемадзеяння з нашым кластарам пасля таго як ён запрацуе, мы таксама будзем выкарыстоўваць 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

Што адбудзецца, калі мы проста запусцім kubelet?

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

kubelet павінен працаваць ад root. Досыць лагічна, бо яму трэба кіраваць усім вузлом. Давайце паглядзім на яго параметры:

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

Ого, як шмат опцый! На шчасце, нам спатрэбіцца толькі пара з іх. Вось адзін з параметраў, які нам цікавы:

--pod-manifest-path string

Шлях да каталога, які змяшчае файлы для статычных подаў, ці шлях да файла з апісаннем статычных подаў. Файлы, якія пачынаюцца з кропак, ігнаруюцца. (СТАРАЛА: гэты параметр варта ўсталёўваць у канфігурацыйным файле, які перадаецца ў Kubelet праз опцыю —config. Для дадатковай інфармацыі гл. kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Гэты параметр дазваляе нам запускаць статычныя поды - поды, якія не кіруюцца праз Kubernetes API. Статычныя поды выкарыстоўваюцца рэдка, але яны вельмі зручныя для хуткага ўзняцця кластара, а гэта менавіта тое, што нам трэба. Мы праігнаруем гэта гучнае папярэджанне (зноў жа, не запускайце гэта ў продзе!) і паглядзім, ці зможам мы запусціць пад.

Спачатку мы створым каталог для статычных подаў і запусцім kubelet:

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

Затым у іншым тэрмінале/акне tmux/яшчэ дзесьці, мы створым маніфест пода:

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

kubelet пачынае пісаць нейкія папярэджанні і падаецца, што нічога не адбываецца. Але ж гэта не так! Давайце паглядзім на 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 прачытаў маніфест пода і даў Docker'у каманду запусціць пару кантэйнераў у адпаведнасці з нашай спецыфікацыяй. (Калі вам цікава даведацца пра кантэйнер "pause", то гэта хакерства Kubernetes - падрабязнасці глядзіце ў гэтым блогу.) Kubelet запусціць наш кантэйнер busybox з названай камандай і будзе перазапускаць яго бясконца, пакуль статычны пад не будзе выдалены.

Павіншуйце сябе. Мы толькі што прыдумалі адзін з самых заблытаных спосабаў вываду тэксту ў тэрмінал!

Запускаем etcd

Нашай канчатковай мэтай з`яўляецца запуск Kubernetes API, але для гэтага нам спачатку трэба запусціць і г.д.. Давайце запусцім мінімальны кластар etcd, змясціўшы яго наладкі ў каталог pods (напрыклад, 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

Калі вы калі-небудзь працавалі з Kubernetes, то падобныя YAML-файлы павінны быць вам знаёмы.

Мы змантавалі тэчку хаста /var/lib/etcd у пад, каб дадзеныя etcd захоўваліся пасля перазапуску (калі гэтага не зрабіць, то стан кластара будзе сцірацца пры кожным перазапуску пода, што будзе нядобра нават для мінімальнай усталёўкі Kubernetes).

Мы ўстанавілі hostNetwork: true. Гэты параметр, што нядзіўна, наладжвае etcd для выкарыстання сеткі хаста замест унутранай сеткі пода (гэта палегчыць API-серверу пошук кластара etcd).

Простая праверка паказвае, што etcd сапраўды запушчаны на localhost і захоўвае дадзеныя на дыск:

$ 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

Запуск API-сервера

Запусціць API-сервер Kubernetes яшчэ прасцей. Адзіны параметр, які трэба перадаць, --etcd-servers, робіць тое, што вы чакаеце:

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

Змесціце гэты YAML-файл у каталог pods, і API-сервер запусціцца. Праверка з дапамогай curl паказвае, што Kubernetes API праслухоўвае порт 8080 з цалкам адкрытым доступам - аўтэнтыфікацыя не патрабуецца!

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

(Зноў жа, не запускайце гэта ў прадакшэне! Я быў крыху здзіўлены, што настройка па змаўчанні настолькі небяспечная. Але я мяркую, што гэта зроблена для палягчэння распрацоўкі і тэсціравання.)

І, прыемны сюрпрыз, kubectl працуе са скрынкі без якіх-небудзь дадатковых налад!

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

праблема

Але калі капнуць крыху глыбей, то здаецца, што нешта ідзе не так:

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

Статычныя поды, якія мы стварылі, зніклі! Насамрэч, наш kubelet-вузел наогул не выяўляецца:

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

У чым жа справа? Калі вы падушыце, то некалькі абзацаў назад мы запускалі kubelet з надзвычай простым наборам параметраў каманднага радка, таму kubelet не ведае, як звязацца з серверам API і апавяшчаць яго аб сваім стане. Вывучыўшы дакументацыю, мы знаходзім адпаведны сцяг:

--kubeconfig string

Шлях да файла kubeconfig, у якім пазначана як падлучацца да сервера API. Наяўнасць --kubeconfig уключае рэжым API-сервера, адсутнасць --kubeconfig уключае аўтаномны рэжым.

Увесь гэты час, самі таго не ведаючы, мы запускалі kubelet у аўтаномным рэжыме . (Калі б мы былі педантычныя, то можна было лічыць аўтаномны рэжым kubelet як «мінімальна жыццяздольны Kubernetes», але гэта было б вельмі сумна). Каб зарабіла "сапраўдная" канфігурацыя, нам трэба перадаць файл kubeconfig у kubelet, каб ён ведаў, як мець зносіны з API-серверам. На шчасце, гэта даволі проста (бо ў нас няма праблем з аўтэнтыфікацыяй ці сертыфікатамі):

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

Захавайце гэта як kubeconfig.yaml, забіце працэс kubelet і перазапусціце з неабходнымі параметрамі:

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

(Дарэчы, калі вы паспрабуеце звярнуцца да API праз curl, калі kubelet не працуе, тыя вы выявіце, што ён усё яшчэ працуе! Kubelet не з'яўляецца "бацькам" сваіх подаў, падобна Docker'у, ён больш падобны на "кіравальнага дэмана". Кантэйнеры, кіраваныя kubelet, будуць працаваць, пакуль kubelet не спыніць

Праз некалькі хвілін kubectl павінен паказаць нам поды і вузлы, як мы і чакаем:

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

Давайце на гэты раз павіншуем сябе па-сапраўднаму (я ведаю, што ўжо віншаваў) - у нас атрымаўся мінімальны «кластар» Kubernetes, які працуе з поўнафункцыянальным API!

Запускаем пад

Цяпер паглядзім на што здольны API. Пачнём з пода nginx:

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

Тут мы атрымаем даволі цікавую памылку:

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

Нават калі мы стварылі уліковы запіс службы ўручную, токен аўтэнтыфікацыі не ствараецца. Працягваючы эксперыментаваць з нашым мінімалістычным "кластарам", мы выявім, што большасць карысных рэчаў, якія звычайна адбываюцца аўтаматычна, будуць адсутнічаць. Сервер Kubernetes API даволі мінімалістычны, большая частка цяжкіх аўтаматычных налад адбываецца ў розных кантролерах і фонавых заданнях, якія яшчэ не выконваюцца.

Мы можам абыйсці гэтую праблему, усталяваўшы опцыю automountServiceAccountToken для ўліковага запісу службы (бо нам усё роўна не прыйдзецца яе выкарыстоўваць):

$ 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

Нарэшце, з'явіўся! Але насамрэч ён не запусціцца, бо ў нас няма планавальніка (Scheduler) - яшчэ аднаго важнага кампанента Kubernetes. Зноў жа, мы бачым, што API Kubernetes на здзіўленне "дурны" - калі вы ствараеце пад у API, ён яго рэгіструе, але не спрабуе высветліць, на якім вузле яго запускаць.

Насамрэч для запуску пода планавальнік не патрэбен. Можна ўручную дадаць вузел у маніфест у параметры nodeName:

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

(Замяніце 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>

Каб пераканацца, што сетка паміж подамі працуе карэктна, мы можам запусціць curl з іншага пода:

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

Даволі цікава пакапацца ў гэтым асяроддзі і паглядзець, што працуе, а што не. Я выявіў, што ConfigMap і Secret працуюць так, як і чакаецца, а Service і Deployment няма.

Поспех!

Гэты пост становіцца вялікім, таму я збіраюся аб'явіць аб перамозе і заявіць, што гэта жыццяздольная канфігурацыя, якую можна назваць “Kubernetes”. Рэзюмуючы: чатыры бінарных файла, пяць параметраў каманднага радка і “усяго толькі” 45 радкоў YAML (не так шмат па стандартах Kubernetes) і ў нас працуе нямала рэчаў:

  • Поды кіруюцца з дапамогай звычайнага Kubernetes API (з некалькімі хакамі)
  • Можна загружаць публічныя выявы кантэйнераў і кіраваць імі
  • Поды застаюцца жывымі і аўтаматычна перазапускаюцца
  • Сетка паміж подамі ў рамках аднаго вузла працуе даволі добра
  • ConfigMap, Secret і найпростае мантаванне сховішчаў працуе як належыць

Але большая частка з таго, што робіць Kubernetes па-сапраўднаму карысным, усё яшчэ адсутнічае, напрыклад:

  • Планавальнік подаў
  • Аўтэнтыфікацыя / аўтарызацыя
  • Некалькі вузлоў
  • Сетка сэрвісаў
  • Кластарны ўнутраны DNS
  • Кантролеры для ўліковых запісаў службаў, разгортванняў, інтэграцыі з хмарнымі правайдэрамі і большасць іншых "плюшак", якія прыносіць Kubernetes

Дык што ж мы насамрэч атрымалі? Kubernetes API, які працуе сам па сабе, на самой справе, з'яўляецца ўсяго толькі платформай для аўтаматызацыі кантэйнераў. Ён не робіць шмат – гэта праца для розных кантролераў і аператараў, якія выкарыстоўваюць API, – але ён забяспечвае кансістэнтнае асяроддзе для аўтаматызацыі.

Даведацца больш падрабязна аб курсе на бясплатным вэбінары.

Чытаць яшчэ:

Крыніца: habr.com

Купіць надзейны хостынг для сайтаў з абаронай ад DDoS, VPS VDS серверы 🔥 Купіць надзейны хостынг для сайтаў з абаронай ад DDoS, VPS VDS серверы | ProHoster