Der Artikel wurde im Vorfeld des Kurses erstellt .

Wenn Sie das hier lesen, haben Sie wahrscheinlich schon von Kubernetes gehört (und wenn nicht, wie sind Sie dann hierher gekommen?). Aber was ist Kubernetes eigentlich? Das ist ? Или ? Что вообще это значит?
Ehrlich gesagt, bin ich mir nicht zu 100% sicher. Aber es ist interessant, in die Tiefen einzutauchen und zu sehen, was in Kubernetes unter seinen vielen Abstraktionsschichten tatsächlich passiert. Lassen Sie uns also aus Neugier ansehen, wie ein minimaler „Kubernetes-Cluster“ tatsächlich aussieht. (Das wird viel einfacher sein als .)
Ich gehe davon aus, dass Sie über grundlegende Kenntnisse von Kubernetes, Linux und Containern verfügen. Alles, worüber wir hier sprechen, ist nur für Forschungs-/Lernzwecke gedacht, starten Sie davon nichts in einer Produktionsumgebung!
Übersicht
Kubernetes enthält viele Komponenten. Laut , sieht die Architektur folgendermaßen aus:

Hier sind zumindest acht Komponenten dargestellt, aber die meisten von ihnen werden wir ignorieren. Ich möchte sagen, dass das Minimale, was man mit gutem Grund Kubernetes nennen kann, aus drei Hauptkomponenten besteht:
- kubelet
- kube-apiserver (der von etcd — seiner Datenbank — abhängt)
- Container-Laufzeitumgebung (in diesem Fall Docker)
Lassen Sie uns schauen, was in der Dokumentation zu jedem von ihnen gesagt wird (., ). Zuerst kubelet:
Ein Agent, der auf jedem Knoten im Cluster läuft. Er sorgt dafür, dass die Container im Pod gestartet werden.
Das klingt ziemlich einfach. Was ist mit Container-Laufzeit (container runtime)?
Die Container-Laufzeit ist ein Programm, das dafür ausgelegt ist, Container auszuführen.
Sehr informativ. Aber wenn Sie mit Docker vertraut sind, sollten Sie ein allgemeines Verständnis dafür haben, was es tut. (Die Details der Aufgabenverteilung zwischen der Container-Laufzeit und kubelet sind tatsächlich ziemlich fein und ich werde hier nicht darauf eingehen.)
Und API-Server?
Der API-Server ist ein Bestandteil des Kubernetes-Dashboards, der die Kubernetes-API bereitstellt. Der API-Server ist der Client-Bereich des Kubernetes-Dashboards.
Jeder, der jemals mit Kubernetes gearbeitet hat, musste auf die API entweder direkt oder über kubectl zugreifen. Dies ist das Herzstück dessen, was Kubernetes zu Kubernetes macht — das Gehirn, das die Berge von YAML, die wir alle kennen und lieben (?), in eine funktionierende Infrastruktur verwandelt. Es liegt auf der Hand, dass die API in unserer Minimal-Konfiguration vorhanden sein sollte.
Voraussetzungen
- Eine Linux-VM oder ein physischer Computer mit Root-Zugriff (ich verwende Ubuntu 18.04 auf einer virtuellen Maschine).
- Und das ist alles!
Langweilige Installation
Auf der Maschine, die wir nutzen werden, muss Docker installiert werden. (Ich werde nicht im Detail erklären, wie Docker und Container funktionieren; falls Sie interessiert sind, gibt es ). Lassen Sie uns einfach installieren mit apt:
$ sudo apt install docker.io
$ sudo systemctl start docker Danach müssen wir die Kubernetes-Binärdateien erhalten. Tatsächlich benötigen wir für den ersten Start unseres „Clusters“ nur kubelet, da wir für den Start anderer Serverkomponenten kubeletverwenden können. Um mit unserem Cluster zu interagieren, nachdem er hochgefahren ist, werden wir ebenfalls verwenden 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 Was passiert, wenn wir einfach starten? kubelet?
$ ./kubelet
F0609 04:03:29.105194 4583 server.go:254] mkdir /var/lib/kubelet: permission denied kubelet muss als root laufen. Das ist nur logisch, da er den gesamten Knoten verwalten muss. Lass uns seine Optionen ansehen:
$ ./kubelet -h
$ ./kubelet -h | wc -l
284Wow, wie viele Optionen! Glücklicherweise benötigen wir nur ein paar davon. Hier ist eine der Optionen, die für uns von Interesse ist:
--pod-manifest-path stringDer Pfad zum Verzeichnis, das Dateien für statische Pods enthält, oder der Pfad zu einer Datei mit der Beschreibung statischer Pods. Dateien, die mit Punkt beginnen, werden ignoriert. (VERALTET: Dieser Parameter sollte in der Konfigurationsdatei gesetzt werden, die Kubelet über die Option —config übergeben wird. Für weitere Informationen siehe. .)
Dieser Parameter ermöglicht es uns, — Pods, die nicht über die Kubernetes API verwaltet werden. Statische Pods werden selten eingesetzt, sind aber sehr nützlich für den schnellen Aufbau eines Clusters, was genau das ist, was wir brauchen. Wir ignorieren diese laute Warnung (nochmals, führen Sie dies nicht in der Produktion aus!) und schauen, ob wir den Pod starten können.
Zuerst erstellen wir ein Verzeichnis für statische Pods und starten kubelet:
$ mkdir pods
$ sudo ./kubelet --pod-manifest-path=podsDann in einem anderen Terminal/Fenster tmux/woanders erstellen wir das Pod-Manifest:
$ cat < pods/hello.yaml
apiVersion: v1
kind: Pod
metadata:
name: hello
spec:
containers:
- image: busybox
name: hello
command: ["echo", "hello world!"]
EOF kubelet anfängt, einige Warnungen auszugeben, und es scheint, dass nichts passiert. Aber das ist nicht der Fall! Schauen wir uns Docker an:
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8c8a35e26663 busybox "echo 'hello world!'" vor 36 Sekunden Beendet (0) vor 36 Sekunden k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
68f670c3c85f k8s.gcr.io/pause:3.2 "pause" vor 2 Minuten Laufend seit 2 Minuten k8s_POD_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_0
$ sudo docker logs k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
hello world! kubelet Ich habe das Manifest des Pods gelesen und Docker angewiesen, einige Container gemäß unserer Spezifikation zu starten. (Wenn Sie mehr über den Container „pause“ erfahren möchten, handelt es sich um einen Hack von Kubernetes — Details finden Sie in .) Kubelet wird unseren Container busybox mit dem angegebenen Befehl starten und ihn unendlich oft neu starten, solange der statische Pod nicht entfernt wird.
Herzlichen Glückwunsch! Wir haben gerade eine der verwirrendsten Methoden zum Ausgeben von Text im Terminal erfunden!
Starte etcd
Unser Endziel ist es, die Kubernetes API zu starten, aber dafür müssen wir zuerst . Lassen Sie uns einen minimalen etcd-Cluster starten und seine Konfigurationen im Verzeichnis Pods ablegen (zum Beispiel 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-dataWenn Sie jemals mit Kubernetes gearbeitet haben, sollten Ihnen ähnliche YAML-Dateien vertraut sein. Hier sind nur zwei Punkte erwähnenswert:
Wir haben den Host-Ordner gemountet /var/lib/etcd im Pod, damit die etcd-Daten nach einem Neustart erhalten bleiben (ansonsten wird der Status des Clusters bei jedem Neustart des Pods gelöscht, was selbst bei einer minimalen Kubernetes-Installation unangenehm ist).
Wir haben hostNetwork: true. Dieser Parameter konfiguriert, was nicht überraschend ist, etcd zur Nutzung des Hostnetzwerks anstelle des internen Podnetzwerks (was es dem API-Server erleichtert, den etcd-Cluster zu finden).
Eine einfache Überprüfung zeigt, dass etcd tatsächlich auf localhost läuft und Daten auf der Festplatte speichert:
$ 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.walAPI-Server starten
Den Kubernetes API-Server zu starten ist noch einfacher. Der einzige Parameter, der übergeben werden muss, --etcd-servers, tut das, was Sie erwarten:
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 Legen Sie diese YAML-Datei in das Verzeichnis pods, und der API-Server wird gestartet. Eine Überprüfung mit curl zeigt, dass die Kubernetes API den Port 8080 mit vollem Zugriff abhört – Authentifizierung ist nicht erforderlich!
$ curl localhost:8080/healthz
ok
$ curl localhost:8080/api/v1/pods
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/pods",
"resourceVersion": "59"
},
"items": []
}(Starten Sie das nicht in der Produktion! Ich war etwas überrascht, dass die Standardeinstellungen so unsicher sind. Aber ich nehme an, dass dies für die Einfachheit von Entwicklung und Tests so eingerichtet ist.)
Und als angenehme Überraschung funktioniert kubectl sofort ohne zusätzliche Konfiguration!
$ ./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
Keine Ressourcen im Standard-Namespace gefunden.Das Problem
Aber wenn man etwas tiefer gräbt, scheint etwas nicht zu stimmen:
$ ./kubectl get pod -n kube-system
Keine Ressourcen im kube-system Namespace gefunden.Die statischen Pods, die wir erstellt haben, sind verschwunden! Tatsächlich wird unser kubelet-Knoten überhaupt nicht erkannt:
$ ./kubectl get nodes
Keine Ressourcen im Standard-Namespace gefunden.Was ist das Problem? Wenn Sie sich erinnern, haben wir vor einigen Absätzen kubelet mit einem sehr einfachen Satz von Befehlszeilenparametern gestartet, daher weiß kubelet nicht, wie es mit dem API-Server kommunizieren und ihn über seinen Status informieren soll. Durch das Studium der Dokumentation finden wir das entsprechende Flag:
--kubeconfig string
Der Pfad zur Datei kubeconfig, die angibt, wie man sich mit dem API-Server verbindet. Das Vorhandensein --kubeconfig aktiviert den API-Server-Modus, das Fehlen --kubeconfig aktiviert den Standalone-Modus.
Die ganze Zeit haben wir, ohne es zu wissen, kubelet im „Standalone-Modus“ betrieben. (Wenn wir pedantisch wären, könnte man den Standalone-Modus von kubelet als „minimal überlebensfähiges Kubernetes“ betrachten, aber das wäre sehr langweilig.) Um die „echte“ Konfiguration zum Laufen zu bringen, müssen wir die kubeconfig-Datei an kubelet übergeben, damit es weiß, wie man mit dem API-Server kommuniziert. Glücklicherweise ist das ziemlich einfach (da wir keine Probleme mit der Authentifizierung oder Zertifikaten haben):
apiVersion: v1
kind: Config
clusters:
- cluster:
server: http://127.0.0.1:8080
name: mink8s
contexts:
- context:
cluster: mink8s
name: mink8s
current-context: mink8s Speichern Sie dies als kubeconfig.yaml, beenden Sie den Prozess kubelet und starten Sie mit den erforderlichen Parametern neu:
$ sudo ./kubelet --pod-manifest-path=pods --kubeconfig=kubeconfig.yaml(Übrigens, wenn Sie versuchen, über curl auf die API zuzugreifen, während kubelet nicht läuft, werden Sie feststellen, dass es weiterhin aktiv ist! Kubelet ist nicht der „Elternteil“ seiner Pods, wie es bei Docker der Fall ist. Es ist eher wie ein „Verwaltungs-Daemon“. Container, die von kubelet verwaltet werden, laufen, bis kubelet sie stoppt.)
In ein paar Minuten kubectl sollte uns die Pods und Nodes anzeigen, wie wir erwarten:
$ ./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.6Lassen Sie uns diesmal richtig auf uns selbst anstoßen (ich weiß, dass ich das schon einmal getan habe) — wir haben einen funktionalen minimalen Kubernetes „Cluster“ mit einer voll funktionsfähigen API bekommen!
Pod starten
Jetzt schauen wir uns an, was die API kann. Lassen Sie uns mit dem nginx-Pod beginnen:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginxHier werden wir auf einen ziemlich interessanten Fehler stoßen:
$ ./kubectl apply -f nginx.yaml
Fehler vom Server (Verboten): Fehler beim Erstellen von "nginx.yaml": Pods "nginx" sind
verboten: Fehler beim Nachschlagen des Dienstkontos default/default: Dienstkonto
"default" nicht gefunden
$ ./kubectl get serviceaccounts
Keine Ressourcen im Standard-Namensraum gefunden.Hier sehen wir, wie erschreckend unvollständig unsere Kubernetes-Umgebung ist – wir haben keine Dienstkonten. Lassen Sie uns einen weiteren Versuch starten, indem wir ein Dienstkonto manuell erstellen und schauen, was passiert:
$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
EOS
serviceaccount/default erstellt
$ ./kubectl apply -f nginx.yaml
Fehler vom Server (ServerTimeout): Fehler beim Erstellen von "nginx.yaml": Kein API
token gefunden für das Dienstkonto "default", bitte erneut versuchen, nachdem das Token
automatisch erstellt und dem Dienstkonto hinzugefügt wurde.Selbst nachdem wir das Dienstkonto manuell erstellt haben, wird das Authentifizierungs-Token nicht erstellt. Wenn wir weiterhin mit unserem minimalistischen "Cluster" experimentieren, werden wir herausfinden, dass die meisten nützlichen Dinge, die normalerweise automatisch geschehen, fehlen werden. Der Kubernetes API-Server ist ziemlich minimalistisch, der Großteil der schweren automatischen Konfigurationen erfolgt in verschiedenen Controllern und Hintergrundaufgaben, die noch nicht ausgeführt werden.
Wir können dieses Problem umgehen, indem wir die Option automountServiceAccountToken für das Dienstkonto (da wir es sowieso nicht verwenden müssen):
$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
automountServiceAccountToken: false
EOS
serviceaccount/default konfiguriert
$ ./kubectl apply -f nginx.yaml
pod/nginx erstellt
$ ./kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 0/1 Pending 0 13mEndlich ist der Pod erschienen! Aber tatsächlich wird er nicht starten, da wir keinen haben — ein weiterer wichtiger Bestandteil von Kubernetes. Wieder sehen wir, dass die Kubernetes-API überraschend „dumm“ ist — wenn Sie einen Pod über die API erstellen, registriert sie ihn, versucht jedoch nicht herauszufinden, auf welchem Knoten er gestartet werden soll.
Tatsächlich ist für den Start eines Pods kein Scheduler erforderlich. Sie können den Knoten manuell im Manifest im Parameter nodeName:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
nodeName: mink8s
(Ersetzen Sie mink8s durch den Namen des Knotens.) Nach dem Löschen und Anwenden sehen wir, dass nginx gestartet wurde und die interne IP-Adresse hört:
$ ./kubectl delete pod nginx
Pod "nginx" wurde gelöscht
$ ./kubectl apply -f nginx.yaml
Pod/nginx erstellt
$ ./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>Willkommen bei nginx!</title>Um sicherzustellen, dass das Netzwerk zwischen den Pods richtig funktioniert, können wir curl von einem anderen Pod ausführen:
$ 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 erstellt
$ ./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>Willkommen bei nginx!</title>Es ist ziemlich interessant, in dieser Umgebung herumzuforschen und zu sehen, was funktioniert und was nicht. Ich habe festgestellt, dass ConfigMap und Secret wie erwartet funktionieren, während Service und Deployment das nicht tun.
Erfolg!
Dieser Beitrag wird umfangreich, daher möchte ich den Erfolg verkünden und sagen, dass dies eine funktionierende Konfiguration ist, die man 'Kubernetes' nennen kann. Zusammenfassend: vier Binärdateien, fünf Kommandozeilenparameter und 'nur' 45 Zeilen YAML (nicht viel nach Kubernetes-Standards) und wir haben viele Dinge am Laufen.
- Pods werden über die standardmäßige Kubernetes-API (mit einigen Hacks) verwaltet.
- Es können öffentliche Container-Images geladen und verwaltet werden.
- Pods bleiben aktiv und werden automatisch neu gestartet.
- Das Netzwerk zwischen Pods auf demselben Knoten funktioniert ziemlich gut.
- ConfigMap, Secret und einfaches Storage-Mounting funktionieren wie vorgesehen.
Aber der Großteil dessen, was Kubernetes wirklich nützlich macht, fehlt noch, zum Beispiel:
- Der Pod-Planer
- Authentifizierung / Autorisierung
- Mehrere Knoten
- Service-Netzwerk
- Cluster-internes DNS
- Controller für Kontodienste, Bereitstellungen, Integration mit Cloud-Anbietern und die meisten anderen „Extras“, die Kubernetes mit sich bringt.
Was haben wir also tatsächlich bekommen? Die Kubernetes-API, die für sich allein arbeitet, ist tatsächlich nur eine Plattform für Containerautomatisierung.Sie macht nicht viel — das ist die Aufgabe verschiedener Controller und Operatoren, die die API verwenden, — aber sie sorgt für eine konsistente Umgebung zur Automatisierung.
Weiterlesen:
Quelle: habr.com
