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

Калі вы гэта праглядаеце, верагодна, вы нешта чулі пра Kubernetes (а калі не, то як вы тут апынуліся?) Але што ж на самой справе ўяўляе сабой Kubernetes? Гэта ? або ? Што ўвогуле гэта значыць?
Сапраўды кажучы, я не ўпэўнены на 100%. Але думаю цікава пакапацца ва вантробах і паглядзець, што насамрэч адбываецца ў Kubernetes пад яго шматлікімі пластамі абстракцый. Так што дзеля цікавасці, давайце паглядзім, як насамрэч выглядае мінімальны "кластар Kubernetes". (Гэта будзе нашмат прасцей, чым .)
Я мяркую, што ў вас есць базавыя веды Kubernetes, Linux і кантэйнераў. Усё, пра што мы тут будзем казаць прызначана толькі для даследавання/вывучэння, не запускайце нічога з гэтага ў прадакшэне!
Агляд
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 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
