Die Übersetzung des Artikels wurde im Vorfeld des Starts des Kurses vorbereitet .

Wenn Sie das lesen, haben Sie wahrscheinlich schon etwas über Kubernetes gehört (und wenn nicht, wie sind Sie hier gelandet?). Was genau ist also Kubernetes? Es ist ? Или ? Что вообще это значит?
Ehrlich gesagt bin ich mir nicht zu 100 % sicher. Aber ich denke, es ist interessant, in die Tiefen einzutauchen und zu sehen, was tatsächlich in Kubernetes unter seinen vielen Abstraktionsschichten passiert. Also, der Interesse halber, lassen Sie uns anschauen, wie ein minimaler „Kubernetes-Cluster“ tatsächlich aussieht. (Das wird viel einfacher sein als .)
Ich nehme an, dass Sie Grundkenntnisse in Kubernetes, Linux und Containern haben. Alles, was wir hier besprechen werden, dient nur zu Forschungs-/Lernzwecken, führen Sie nichts davon in der Produktion aus!
Überblick
Kubernetes besteht aus vielen Komponenten. Laut , sieht die Architektur folgendermaßen aus:

Hier sind mindestens acht Komponenten dargestellt, aber die meisten davon werden wir ignorieren. Ich möchte behaupten, dass das Minimalste, was man mit Recht 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 sehen, was in der Dokumentation über jede dieser beschrieben wird (., .). Zuerst Kubelet:
Der Agent, der auf jedem Knoten im Cluster läuft. Er überwacht, ob die Container im Pod gestartet werden.
Klingt ziemlich einfach. Was ist mit der Container-Laufzeitumgebung? (container runtime)?
Die Container-Laufzeitumgebung ist ein Programm, das für das Ausführen von Containern vorgesehen ist.
Sehr informativ. Aber wenn Sie mit Docker vertraut sind, sollten Sie eine allgemeine Vorstellung davon haben, was es tut. (Die Details der Verantwortlichkeiten zwischen der Container-Laufzeitumgebung und kubelet sind tatsächlich ziemlich subtil, und hier werde ich nicht darauf eingehen.)
Und API-Server?
Der API-Server ist eine Komponente des Kubernetes-Dashboards, die die API von Kubernetes bereitstellt. Der API-Server ist der Client-Teil des Kubernetes-Dashboards.
Jeder, der jemals etwas mit Kubernetes gemacht hat, musste entweder direkt oder über kubectl mit der API interagieren. Es ist das Herz dessen, was Kubernetes zu Kubernetes macht — das Gehirn, das die Berge von YAML, die wir alle kennen und lieben (?), in eine funktionierende Infrastruktur umwandelt. Es scheint offensichtlich, dass die API in unserer minimalen Konfiguration vorhanden sein sollte.
Vorbedingungen
- Eine virtuelle oder physische Linux-Maschine mit Root-Zugriff (ich verwende Ubuntu 18.04 auf einer virtuellen Maschine).
- Und das war's!
Langweilige Installation
Auf die Maschine, die wir verwenden werden, muss Docker installiert werden. (Ich werde nicht im Detail erklären, wie Docker und Container funktionieren; wenn es Sie interessiert, gibt es ). Lassen Sie uns einfach installieren mit apt:
$ sudo apt install docker.io
$ sudo systemctl start docker Danach müssen wir die Binaries von Kubernetes erhalten. Tatsächlich benötigen wir für den ersten Start unseres „Clusters“ nur Kubelet, da wir für die Ausführung anderer Serverkomponenten Kubeletverwenden können. Um mit unserem Cluster zu interagieren, nachdem es funktioniert, werden wir auch 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 ausführen Kubelet?
$ ./kubelet
F0609 04:03:29.105194 4583 server.go:254] mkdir /var/lib/kubelet: Berechtigung verweigert Kubelet muss als root ausgeführt werden. Ziemlich logisch, da es das gesamte Knotenmanagement übernehmen muss. Schauen wir uns seine Parameter an:
$ ./kubelet -h
$ ./kubelet -h | wc -l
284Wow, wie viele Optionen! Glücklicherweise benötigen wir nur ein paar davon. Hier ist einer der Parameter, der für uns interessant 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 von statischen Pods. Mit Punkten beginnende Dateien werden ignoriert. (VERALTET: Dieser Parameter sollte in der Konfigurationsdatei eingestellt werden, die über die Option —config an Kubelet übergeben wird. Weitere Informationen finden Sie unter .)
Dieser Parameter ermöglicht es uns, zu starten – Pods, die nicht über die Kubernetes-API verwaltet werden. Statische Pods werden selten verwendet, sind jedoch sehr nützlich, um schnell einen Cluster hochzufahren, und genau das brauchen wir. Wir werden diese laute Warnung ignorieren (nochmals, führen Sie dies nicht in der Produktion aus!) und sehen, ob wir einen Pod starten können.
Zunächst erstellen wir ein Verzeichnis für statische Pods und starten Kubelet:
$ mkdir pods
$ sudo ./kubelet --pod-manifest-path=podsDann erstellen wir in einem anderen Terminal/Fenster tmux/woanders das Manifest des Pods:
$ cat < pods/hello.yaml
apiVersion: v1
kind: Pod
metadata:
name: hello
spec:
containers:
- image: busybox
name: hello
command: ["echo", "hello world!"]
EOF Kubelet Es beginnt, einige Warnmeldungen auszugeben, und es scheint, als würde nichts passieren. Aber das ist nicht korrekt! Lassen Sie uns einen Blick auf Docker werfen:
$ 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 Läuft 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 den Befehl gegeben, eine Reihe von Containern gemäß unserer Spezifikation zu starten. (Falls Sie mehr über den Container „pause“ erfahren möchten, das ist ein Kubernetes-Hack - Details finden Sie in .) Der Kubelet wird unseren Container busybox mit dem angegebenen Befehl starten und ihn ununterbrochen neustarten, bis der statische Pod gelöscht wird.
Gratulieren Sie sich. Wir haben gerade eine der kompliziertesten Methoden erfunden, um Text im Terminal auszugeben!
Wir starten etcd
Unser Endziel ist es, die Kubernetes-API zu starten, aber dafür müssen wir zunächst starten. Lassen Sie uns ein minimales etcd-Cluster starten, indem wir die Konfigurationen im Ordner 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 solche YAML-Dateien vertraut sein. Hier sind nur zwei Punkte zu beachten:
Wir haben das Host-Verzeichnis /var/lib/etcd in den Pod eingehängt, damit die etcd-Daten nach einem Neustart erhalten bleiben (wenn dies nicht getan wird, wird der Status des Clusters bei jedem Neustart des Pods gelöscht, was selbst für eine minimale Kubernetes-Installation nicht gut ist).
Wir haben festgelegt hostNetwork: true. Diese Einstellung konfiguriert etcd, um das Netzwerk des Hosts anstelle des internen Pod-Netzwerks zu nutzen (das erleichtert es dem API-Server, das 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.walStart des API-Servers
Es ist noch einfacher, den Kubernetes API-Server zu starten. Der einzige Parameter, den Sie übergeben müssen, --etcd-servers, tut, 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. Ein Test mit curl zeigt, dass die Kubernetes API Port 8080 mit vollem Zugriff überwacht — eine 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 Standardkonfiguration so unsicher ist. Aber ich vermute, dass dies gemacht wurde, um die Entwicklung und das Testen zu erleichtern.)
Und, eine angenehme Überraschung, kubectl funktioniert sofort ohne weitere 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.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 ist unser kubelet-Knoten überhaupt nicht auffindbar:
$ ./kubectl get nodes
Keine Ressourcen im Standard-Namespace gefunden.Was ist da los? Wenn Sie sich erinnern, hatten wir einige Absätze zuvor kubelet mit einem äußerst einfachen Satz von Kommandozeilenparametern gestartet, weshalb kubelet nicht weiß, wie es sich mit dem API-Server verbinden und ihn über seinen Status informieren kann. Beim Durchlesen der Dokumentation finden wir das passende 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 schaltet den Standalone-Modus ein.
Während all dieser Zeit haben wir, ohne es zu wissen, den kubelet im „autonomen Modus“ in Betrieb genommen. (Wenn wir genau wären, könnte man den autonomen Modus des kubelet als „minimal funktionsfähiges Kubernetes“ betrachten, aber das wäre sehr langweilig.) Damit die „echte“ Konfiguration funktioniert, müssen wir die kubeconfig-Datei an kubelet übergeben, damit es weiß, wie es mit dem API-Server kommunizieren kann. Zum Glück ist das ziemlich einfach (weil wir keine Probleme mit 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, töten 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 immer noch funktioniert! Kubelet ist nicht „Elternteil“ seiner Pods, ähnlich wie Docker, vielmehr ähnelt es einem „verwaltenden Dämon“. Die von kubelet verwalteten Container werden weiterlaufen, bis kubelet sie stoppt.)
Nach ein paar Minuten kubectl sollte es uns die Pods und Nodes zeigen, wie wir es 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 Bereit 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 wirklich gratulieren (ich weiß, ich habe es schon getan) – wir haben einen minimal funktionierenden Kubernetes-„Cluster“ mit einer voll funktionsfähigen API!
Starten wir einen Pod
Schauen wir uns nun an, was die API kann. Beginnen wir mit dem nginx-Pod:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginxHier werden wir einen ziemlich interessanten Fehler bekommen:
$ ./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-Namespace 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 manuell ein Dienstkonto erstellen, und schauen wir mal, was passiert:
$ 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 accountSelbst wenn wir das Dienstkonto manuell erstellt haben, wird kein Authentifizierungstoken erzeugt. Wenn wir weiterhin mit unserem minimalistischen "Cluster" experimentieren, werden wir feststellen, 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 Einstellungen geschieht 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 setzen (da wir es ohnehin nicht verwenden müssen):
$ 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 13mEndlich ist der Pod aufgetaucht! Aber tatsächlich wird er nicht gestartet, da wir keinen haben – einem weiteren wichtigen Bestandteil von Kubernetes. Wieder einmal sehen wir, dass die Kubernetes API überraschend „dumm“ ist – wenn Sie einen Pod in der API erstellen, registriert sie ihn, versucht aber nicht herauszufinden, auf welchem Knoten sie ihn starten soll.
Tatsächlich ist kein Scheduler erforderlich, um einen Pod zu starten. Man kann manuell den Knoten 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ösch- und Anwendungsbefehl sehen wir, dass nginx gestartet ist und die interne IP-Adresse abhört:
$ .\/kubectl delete pod nginx
Pod "nginx" gelöscht
$ .\/kubectl apply -f nginx.yaml
Pod/nginx erstellt
$ .\/kubectl get pods -owide
NAME BEREIT STATUS NEUSTARTS ALTER IP KNOTEN BENENNTEN KNOTEN BEREITNESS-TOR
nginx 1\/1 Aktiv 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 korrekt funktioniert, können wir curl aus 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 Durchschnittsgeschwindigkeit Zeit Zeit Zeit Aktuell
Dload Upload Gesamt Verbraucht Übrig Geschwindigkeit
<!DOCTYPE html>
<html>
<head>
<title>Willkommen bei nginx!</title>Es ist ziemlich interessant, in dieser Umgebung zu stöbern und zu sehen, was funktioniert und was nicht. Ich habe festgestellt, dass ConfigMap und Secret wie erwartet funktionieren, während Service und Deployment es nicht tun.
Erfolg!
Dieser Beitrag wird umfangreich, daher werde ich meinen Sieg verkünden und feststellen, dass dies eine praktikable Konfiguration ist, die wir "Kubernetes" nennen können. Zusammenfassend: vier Binärdateien, fünf Befehlszeilenparameter und "nur" 45 Zeilen YAML (nicht so viel nach den Standards von Kubernetes) und wir haben eine Menge Dinge zum Laufen gebracht:
- Pods werden über die reguläre Kubernetes-API (mit einigen Hacks) verwaltet.
- Öffentliche Container-Images können geladen und verwaltet werden.
- Pods bleiben aktiv und werden automatisch neu gestartet.
- Das Netzwerk zwischen Pods auf einem Knoten funktioniert ziemlich gut.
- ConfigMap, Secret und einfaches Speichermounting funktionieren wie vorgesehen.
Aber der Großteil dessen, was Kubernetes wirklich nützlich macht, fehlt immer noch, zum Beispiel:
- Der Scheduler für Pods.
- Authentifizierung / Autorisierung.
- Mehrere Knoten.
- Dienstnetzwerk.
- Cluster-interner DNS.
- Controller für Dienstkonten, Deployments, Integration mit Cloud-Anbietern und die meisten anderen „Extras“, die Kubernetes mit sich bringt.
Was haben wir also tatsächlich erhalten? Die Kubernetes-API, die für sich allein funktioniert, ist tatsächlich nur eine Plattform für Containermanagement-Automatisierung.. Sie erledigt nicht viel – das liegt in der Verantwortung verschiedener Controller und Operatoren, die die API nutzen – aber sie bietet eine konsistente Umgebung zur Automatisierung.
Weiterlesen:
Quelle: habr.com
