Sollte ich meinen Docker Swarm auf Kubernetes migrieren?
Ich habe ein Docker-Compose Projekt nach Kubernetes migriert und mich für Kubernetes entschieden.
Wenn man im Internet liest, dass Docker Swarm tot ist, bekommt man Angst. Ich habe einen Docker Swarm laufen und ich mag ihn, es ist einfach, wenn man bereits Docker benutzt.
Was sind die Alternativen? Wir haben die ganze Zeit gelesen, dass es nur eine Möglichkeit gibt, und zwar auf Kubernetes zu migrieren und alles andere zu vergessen.
Ich war auf halbem Wege, von Docker auf Docker Swarm zu migrieren, und wollte wissen, ob ich damit weitermachen oder den Fokus auf Kubernetes verlagern sollte.
Der logischste nächste Schritt wäre vielleicht die Verwendung von Docker Desktop , weil es Kubernetes enthält. Ich verwende jedoch nicht Docker Desktop, ich verwende Docker Swarm auch auf meinem Entwicklungsrechner und es ist perfekt.
Zurück zu Kubernetes. Alle meine Anwendungen laufen auf Ubuntu und Debian und nach weiterer Lektüre scheint Microk8s eine gute Wahl zu sein, man kann es für Entwicklung und Produktion verwenden. Nehmen wir also an, wir wechseln zu Microk8s, was muss dann für die Migration getan werden? Ich habe eines meiner aktuellen Docker-Compose-Projekte verwendet, um das herauszufinden.
In diesem Beitrag geht es hauptsächlich um all die Dinge, die ich tun musste, um das Ergebnis zu erreichen, ich zeige nicht alle details des Docker-Compose Projekts.
Wie immer bin ich auf Ubuntu 22.04.
Meine Anwendung
Meine Anwendung besteht aus mehreren Blöcken. Jeder Block besteht aus einem oder mehreren Docker-Compose Projekten. Im Moment werden nur die Task Runners mit Docker Swarm verwaltet. Sie verbinden sich mit dem Backend über ein Ingress Overlay-Netzwerk.
+----------+ +---------+ +-------------+
--| Frontend |---| Backend |----+----| Task Runner |
+----------+ +---------+ | +-------------+
|
| +-------------+
Ingress -> +----| Task Runner |
overlay | +-------------+
network //
| +-------------+
+----| Task Runner |
+-------------+
manage with manage with
|<--- Docker-Compose --->|<---- Docker Swarm ---->|
v
migrate
v
manage with
|<----- Kubernetes ---->|
Der Task Runner ist ein Docker-Compose-Projekt mit fünf Diensten:
rabbitmq
task
local-web
unbound-cloudflare
unbound-quad9
'local-web' ist ein leicht modifiziertes Nginx -Image, das eine angepasste Webseite zurückgibt.
+-------------+
| local-web |
+-------------+
^
|
+----------+ +-----------------+
| | | |-+
| |------>| | |-+
<-->| rabbitmq | | task | | |
| |<------| | | |
| | | | | |
+----------+ +-----------------+ | |
| | ------+ |
| | --------+
+--------------------+ | |
| unbound-cloudflare |<--+ |
+--------------------+ |
|
+--------------------+ |
| unbound-quad9 |<--------+
+--------------------+
Der rabbitmq-service verbindet sich mit dem Backend. Mit Docker Swarm erstelle ich Replikate des task-service.
Hier ist eine abgespeckte Version der Docker-Compose Projektdatei:
# docker-compose.yml
services:
rabbitmq:
...
networks:
task-runner-network
task:
...
local-web:
...
unboud-cloudflare:
...
networks:
task-runner-network:
external: true
Nur rabbitmq ist mit einem externen Netzwerk verbunden. In diesem Beitrag werde ich einen Task Runner auf Kubernetes laufen lassen, wobei der 'task'-Dienst repliziert wird.
Fragen und Antworten
Bevor ich mit der Arbeit begann, habe ich eine Liste von Fragen erstellt und versucht, einige Antworten zu bekommen. Hier sind sie:
Ist ein Pod dasselbe wie ein Docker Container?
Nein, das ist er nicht. Aus den Unterlagen: Ein Pod ist die kleinste einsatzfähige Recheneinheit, die Sie in Kubernetes erstellen und verwalten können. In einem Pod können mehrere Container enthalten sein. Ein Pod bietet eine Umgebung, in der Container laufen und Ressourcen, Speicher, Netzwerk und Interprozesskommunikation gemeinsam nutzen. Sie teilen sich denselben Netzwerk-Namespace und können über localhost miteinander kommunizieren.
Zusammenfassend lässt sich sagen, dass Sie einen Pod pro Container verwenden sollten, sofern Sie nichts Besonderes tun.
Pods kommunizieren über Dienste
Ein Container in einem Pod kommuniziert mit einem anderen Pod über dessen IP address. Aber das ist nicht der richtige Weg. In der Regel erstellen wir für jede Pod einen Service und greifen auf die Pod über ihren Service zu. Wir verweisen auf einen Service über seinen Namen, wir können hier auch Ports zuordnen.
Wenn wir einen Pod mit replizierten Containern haben, wird der Service distAnfragen über diese Container verteilen. In diesem Fall fungiert der Dienst auch als Load Balancer. Dies ist sehr ähnlich wie Docker Swarm.
Wann sollte Deployments verwendet werden?
Ein Deployment wird zur Verwaltung eines Pod verwendet. Mit einem Deployment können Sie Pod -Replikate, die Pod -Platzierung auf Knoten und die Freigabe neuer Updates festlegen. Das bedeutet, dass Sie (fast) immer einen Deployment für einen Pod benötigen. Da wir eine Pod innerhalb einer Deployment angeben können, brauchen wir keine separaten YAML -Dateien für die Pods!
Wie erfolgt die Pods -Protokollierung?
Der Pods führt keine Protokollierung durch. Es sind die Container, die Logs erzeugen. Dies unterscheidet sich nicht von der Docker -Protokollierung. Auf meinem System befinden sich die Logfiles in:
/var/log/pods
Können wir einem Pod ein Verzeichnis auf dem Host zuweisen, wie Docker Volumes?
Ja, dafür können wir die Richtlinie 'hostPath' verwenden.
Ist Kubernetes nur für die Produktion gedacht oder kann man sie auch für die Entwicklung verwenden?
Microk8s kann für die Entwicklung und die Produktion verwendet werden. Aber für die Produktion können wir auch etwas anderes wie eine Kubernetes -Variante von einem Cloud-Anbieter verwenden. Auf meiner Entwicklungsmaschine verwende ich Microk8s neben Docker ohne Probleme.
In Docker Swarm haben wir Task.Slot, was ist die Entsprechung in Kubernetes?
Nicht genau dasselbe, aber vergleichbar ist die Kubernetes 'StatefulSet'. Ich habe dies noch nicht ausprobiert.
Kann man ein Docker Swarm -Netzwerk mit einem Kubernetes Pod (Container) verbinden?
Der Grund, warum ich das möchte, ist die Migration. Natürlich können wir immer unsere eigenen Load Balancer proxy bauen, aber gibt es einen einfachen / Standard Weg, dies zu tun? Im Moment erstelle ich einen 'nodePort' Service. Dieser bildet den Port eines Dienstes innerhalb von Kubernetes auf einen Port des Hosts ab.
Vor der Konvertierung die Objektnamen prüfen und korrigieren
Ich hatte all diese schönen Namen in meinen Docker-Compose Dateien und sie verwendeten Unterstriche ('_'). In Kubernetes müssen die Objektnamen der Norm RFC 1123 entsprechen, was bedeutet:
- Maximal 63 Zeichen.
- Sie müssen mit einem Kleinbuchstaben oder einer Zahl beginnen und enden.
- Sie dürfen Kleinbuchstaben, Zahlen und Bindestriche enthalten.
Bevor ich etwas tue, habe ich dies in meinem Projekt Docker-Compose geändert.
Kompose: Konvertieren der Docker-Compose Projektdatei
Versuchen wir nun, die Task Runner Docker-Compose Dateien in etwas Kubernetes zu konvertieren, indem wir Kompose verwenden. Kompose ist ein Konvertierungswerkzeug für Docker Compose nach Kubernetes. Und wir stoßen sofort auf Probleme. Kompose scheint nicht in der Lage zu sein, die '.env'-Datei zu verarbeiten, die sehr wichtig ist. Außerdem wurde (als Folge davon?) keine ConfigMap erzeugt. Hierfür gibt es einen Problembericht.
Ich werde in einer der nächsten Wochen nach einer neuen Version suchen, aber im Moment ersetzen wir die Umgebungsvariablen selbst durch Docker-Compose 'config':
> docker-compose -f docker_compose_shared_file.yml -f docker_compose_deployment_file.yml config > docker-compose-config.yml
Und führen dann Kompose aus. Wir verwenden hier die Option hostPath , weil ich Volumes mit lokalen Systemverzeichnissen verwende:
> kompose -v convert -f docker-compose-config.yml --volumes hostPath
Kompose erstellt die folgenden Dateien:
local-web-pod.yaml
rabbitmq-pod.yaml
rabbitmq-service.yaml
task-pod.yaml
unbound-cloudflare-pod.yaml
unbound-quad9-pod.yaml
Für jeden Docker-Compose-Dienst wird eine '-pod.yaml'-Datei erstellt, und nur für einen eine '-service.yaml'-Datei. Ich habe auch Beispiele gesehen, bei denen Kompose statt Pods die Datei Deployments erzeugt hat. Warum? Für mich sind die Kompose Ergebnisse nur ein Ausgangspunkt, ich muss Deployment YAML Dateien aus den Pod YAML Dateien erstellen und auch Service YAML Dateien hinzufügen. Wahrscheinlich kann man mit Kompose bessere Ergebnisse erzielen (indem man der Datei 'docker-compose.yml' Anweisungen hinzufügt), aber ich habe im Moment nicht die Zeit, um zu experimentieren.
Installieren von Microk8s
Jetzt wollen wir uns die Hände schmutzig machen. Ich benutze Ubuntu 22.04, also ist das einfach, wir benutzen Microk8s , um die letzte stabile Version zu bekommen:
> sudo snap install microk8s --classic
Schauen wir mal, was aktiviert ist:
> microk8s status
Ergebnis:
microk8s is running
high-availability: no
datastore master nodes: 127.0.0.1:19001
datastore standby nodes: none
addons:
enabled:
dns # (core) CoreDNS
ha-cluster # (core) Configure high availability on the current node
helm # (core) Helm - the package manager for Kubernetes
helm3 # (core) Helm 3 - the package manager for Kubernetes
disabled:
cert-manager # (core) Cloud native certificate management
community # (core) The community addons repository
dashboard # (core) The Kubernetes dashboard
gpu # (core) Automatic enablement of Nvidia CUDA
host-access # (core) Allow Pods connecting to Host services smoothly
hostpath-storage # (core) Storage class; allocates storage from host directory
ingress # (core) Ingress controller for external access
kube-ovn # (core) An advanced network fabric for Kubernetes
mayastor # (core) OpenEBS MayaStor
metallb # (core) Loadbalancer for your Kubernetes cluster
metrics-server # (core) K8s Metrics Server for API access to service metrics
minio # (core) MinIO object storage
observability # (core) A lightweight observability stack for logs, traces and metrics
prometheus # (core) Prometheus operator for monitoring and logging
rbac # (core) Role-Based Access Control for authorisation
registry # (core) Private image registry exposed on localhost:32000
storage # (core) Alias to hostpath-storage add-on, deprecated
Hostpath-Speicher aktivieren
Einige Dienste in meiner docker-compose.yml-Datei verwenden persistente Daten in Verzeichnissen auf dem localhost. Schalten Sie die Speicherdienste in Microk8s ein/aktivieren Sie sie:
> microk8s.enable hostpath-storage
Kubernetes Dashboard
Microk8s enthält das Dashboard Kubernetes . Aktivieren Sie es zunächst:
> microk8s enable dashboard
Es gibt mehrere Möglichkeiten, es zu starten, diese ist einfach:
> microk8s dashboard-proxy
Navigieren Sie dann in Ihrem Browser zu:
https://127.0.0.1:10443
Akzeptieren Sie das selbstsignierte Zertifikat, fügen Sie das Token aus dem Terminal ein und schon sind Sie dabei.
Anhalten und Starten
So stoppen und starten Sie Microk8s:
> microk8s stop
> microk8s start
Verwendung von kubectl
Kubectl ist ein Befehlszeilentool, mit dem Befehle zur Verwaltung von Kubernetes -Clustern ausgeführt werden können.
Verwenden Sie 'kubectl' anstelle von 'microk8s kubectl':
> alias kubectl='microk8s kubectl'
Um dies dauerhaft zu machen, fügen Sie dies zu Ihrer 'bashrc'-Datei hinzu:
> echo "alias kubectl='microk8s kubectl'" >> ~/.bashrc
Machen Sie viele Aliase, sonst werden Ihre Finger müde!
Hilfe z.B. zu den Ports eines Containers in einem Pod erhalten:
> kubectl explain pod.spec.containers.ports
Um Kubernetes Objekte zu erstellen und zu aktualisieren, können wir eine deklarative Syntax ('apply') oder eine imperative ('create') verwenden.
Hier sind einige Befehle:
> kubectl get nodes
> kubectl apply -f <manifest file>
> kubectl get pods -o wide
> kubectl get deployments
> kubectl get services
> kubectl delete pods local-web
> kubectl describe pods local-web
> kubectl logs local-web
Um die neuesten Ereignisse zu erhalten:
> kubectl get events
Und, um alles zu erhalten:
> kubectl get all -A
Denken Sie daran, dass ein Deployment eine ReplicaSet -Spezifikation und eine Pod -Spezifikation enthält, d. h. wenn Sie einen Deployment erstellen, erhalten Sie einen oder mehrere Pods , je nachdem, welche Replikate Sie angegeben haben.
Bereitstellen eines Pod
Können wir einen der von Kompose erstellten Pods bereitstellen? Kompose hat die folgende Datei für uns erstellt:
local-web-pod.yaml
Ohne diese Datei zu bearbeiten, versuchen wir, diesen Pod einzusetzen:
> kubectl apply -f local-web-pod.yaml
Ergebnis:
pod/local-web created
Ist die Pod vorhanden?
> kubectl get pods
Ergebnis:
NAME READY STATUS RESTARTS AGE
local-web 0/1 Pending 0 119s
Sie ist da, aber der Status ist 'Pending'. Lassen Sie uns die Protokolle überprüfen:
> kubectl logs --timestamps local-web
Keine Protokolle, es wird nichts zurückgegeben. Prüfen wir die Ereignisse:
> kubectl get events
Ergebnis:
LAST SEEN TYPE REASON OBJECT MESSAGE
9m31s Warning FailedScheduling pod/local-web 0/1 nodes are available: persistentvolumeclaim "local-web-claim0" not found. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod..
101s Warning FailedScheduling pod/local-web 0/1 nodes are available: 1 node(s) didn't match Pod's node affinity/selector. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..
Es zeigte sich, dass 'nodeAffinity' den Wert der Docker-Compose-Datei 'deploy - node.hostname' hatte. Ich musste dies natürlich in den Namen des Hosts ändern, den ich im Moment verwende ... :-(
Hinzufügen einer privaten (Image-)Registrierung
Pod löschen, erneut anwenden und Status abfragen:
> kubectl delete pod local-web
> kubectl apply -f local-web-pod.yaml
> kubectl get pods
Ergebnis:
NAME READY STATUS RESTARTS AGE
local-web 0/1 ErrImagePull 0 76s
Ich verwende bereits eine private Registry für Docker . Um diese in Kubernetes zu verwenden, müssen wir die Authentifizierung für diese Registry hinzufügen.
Prüfen Sie, ob ein registry-credential Element vorhanden ist:
> kubectl get secret registry-credential --output=yaml
Ergebnis:
Error from server (NotFound): secrets "registry-credential" not found
Hier verwende ich die bereits in Docker vorhandenen Informationen. Erstellen Sie die registry-credential:
> kubectl create secret generic registry-credential \
--from-file=.dockerconfigjson=/home/peter/.docker/config.json \
--type=kubernetes.io/dockerconfigjson
Ergebnis:
secret/registry-credential created
Um es zu überprüfen:
> kubectl get secret registry-credential --output=yaml
Zunächst wird das Abbild in die Registry übertragen. Anschließend bearbeiten wir die Pod -Dateien und fügen den Verweis auf die Anmeldeinformationen in der Registrierung hinzu:
imagePullSecrets:
- name: registry-credential
Nun löschen wir die Pod und wenden sie erneut an:
> kubectl get pods
Ergebnis:
NAME READY STATUS RESTARTS AGE
local-web 1/1 Running 0 9s
Endlich läuft es!
Geben Sie den Container Pod ein
Geben Sie den Container 'local-web' ein:
> kubectl exec -it local-web -- /bin/bash
Jetzt können wir die Umgebungsvariablen, Mounts usw. überprüfen.
Vielleicht möchten Sie den Container auch als root betreten. Mein Microk8s verwendet 'runc' (Open Container Initiative runtime), um auf Container zuzugreifen. Ermitteln Sie die Container-ID:
> kubectl describe pod <your pod> | grep containerd
Dies wird etwas wie ausgeben:
Container ID: containerd://6a060ba8436f575b86b4f3fe10a373125aaf7c125af835180d792f5382836355
Dann als root in den Container ausführen:
> sudo runc --root /run/containerd/runc/k8s.io/ exec -t -u 0 6a060ba8436f575b86b4f3fe10a373125aaf7c125af835180d792f5382836355 sh
Zugriff auf den Pod über einen Dienst
Nach dem Stoppen und Starten eines Pod kann einem Pod ein neuer IP address zugewiesen werden. Aus diesem Grund verwenden wir einen Service, um auf eine Pod zuzugreifen, und verwenden den Namen des Service.
Indem wir einen Service für einen Pod erstellen, können wir auf den Pod zugreifen, indem wir seinen Namen anstelle von IP address verwenden. Dazu benötigen wir den Kubernetes Cluster DNS, der aktiviert sein sollte:
> kubectl get services kube-dns --namespace=kube-system
Ergebnis:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.152.183.10 <none> 53/UDP,53/TCP,9153/TCP 3d5h
Erstellen Sie einen Service für den local-web Pod
Die Datei local-web-pod.yaml hatte keinen Ports-Eintrag, also habe ich einen containerPort hinzugefügt:
ports:
- containerPort: 80
Da die Datei local-web-service.yaml nicht von Kompose erstellt wurde, habe ich die Datei selbst erstellt. Wichtig: Hier füge ich einen Port hinzu, der den Zugriff auf den Pod ermöglicht. In Docker-Compose brauchten wir diesen Port nicht, da sich der Dienst im internen Netzwerk befindet.
apiVersion: v1
kind: Service
metadata:
name: local-web
namespace: default
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
io.kompose.service: local-web
Der 'selector' muss mit dem 'label' des Pod übereinstimmen.
Danach starten wir den Dienst:
> kubectl apply -f local-web-service.yaml
Jetzt sollten wir in der Lage sein, von einem anderen Pod Container auf den local-web Dienst zuzugreifen.
Starten wir einen Busybox -Container. Es gibt Probleme mit den neueren Busybox images und Kubernetes, daher verwenden wir Version 1.28.
> kubectl run -i --tty --image busybox:128 test --restart=Never --rm /bin/sh
Dann innerhalb des Containers:
# wget local-web-service:80
Ergebnis:
Connecting to local-web-service:80 (10.152.183.30:80)
saving to 'index.html'
index.html 100% |**********************************************************************************************************************************| 400 0:00:00 ETA
'index.html' saved
Sehr gut.
Erstellen von Deployments und Services
Wie bereits oben erwähnt, erstellen wir nicht Pod YAML Dateien, sondern Deployment YAML Dateien. Außerdem fügen wir für jede Deployment , die Ports hat, einen Service hinzu.
In Kubernetes sind die Service-Namen global, in Docker-Compose sind die Services in einem internen Netzwerk lokal für das Docker-Compose Projekt. Das bedeutet, dass wir in Kubernetes unsere Services mit einem Namensraum versehen müssen.
Wir können dies auf zwei Arten tun:
- Einen Namespace für dieses Docker-Compose Projekt hinzufügen
- Hinzufügen eines prefix zum Servicenamen
Im Moment halte ich das Hinzufügen eines prefix für die beste Option:
task-runner-
So erhalten wir die Deployment -Namen und die Servicenamen:
task-runner-rabbitmq-deployment
task-runner-local-web-deployment
task-runner-task-deployment
task-runner-unbound-cloudflare-deployment
task-runner-unbound-quad9-deployment
task-runner-rabbitmq-service
task-runner-local-web-service
task-runner-task-service
task-runner-unbound-cloudflare-service
task-runner-unbound-quad9-service
Und Dateinamen:
task-runner-rabbitmq-deployment.yaml
task-runner-local-web-deployment.yaml
task-runner-task-deployment.yaml
task-runner-unbound-cloudflare-deployment.yaml
task-runner-unbound-quad9-deployment.yaml
task-runner-rabbitmq-service.yaml
task-runner-local-web-service.yaml
task-runner-task-service.yaml
task-runner-unbound-cloudflare-service.yaml
task-runner-unbound-quad9-service.yaml
In der Deployment beginne ich mit 1 Replikat. Hier ist der erste Teil einer Deployment -Datei:
# task-runner-local-web-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
# metadata name must not contain dots
name: task-runner-local-web-deployment
namespace: default
spec:
# number of copies of each pod we want
replicas: 1
strategy:
type: Recreate
# pods managed by this deployment
selector:
# match the labels we set on the pod, see below
matchLabels:
task-runner.local-web.deployment: task-runner-local-web-deployment
# template field is a regular pod configuration nested inside the deployment spec
template:
metadata:
# set labels on the pod, used in the deployment selector, see above
labels:
task-runner.local-web.deployment: task-runner-local-web-deployment
spec:
...
Die Datei '.env' beim Start der Container verfügbar machen
Ich habe eine Menge Schlüssel-Wert-Paare in der '.env'-Datei, die von Docker-Compose verwendet wird. In Kubernetes können wir eine ConfigMap verwenden, um diese in unsere Deployment YAML -Dateien zu importieren.
Zunächst erstellen wir eine ConfigMap. Die ConfigMap wird im "Standard"-Namensraum erstellt. Sie können dies ändern, indem Sie das Namensraumkennzeichen und den Wert hinzufügen:
> kubectl create configmap task-runner-env-configmap --from-env-file=.env
Listen Sie die ConfigMaps auf:
> kubectl get configmaps
Ergebnis:
NAME DATA AGE
...
task-runner-env-configmap 51 19m
Wir können das Ergebnis als yaml anzeigen:
> kubectl get configmaps task-runner-env-configmap -o yaml
In den Dateien Deployment YAML entfernen wir den Abschnitt "env" und fügen den Abschnitt "envFrom" hinzu.
Von:
spec:
containers:
- env:
- name: ENV_VAR1
value: "1"
...
image: ...
Bis:
spec:
containers:
- envFrom:
- configMapRef:
name: task-runner-env-configmap
env:
- name: VAR_C = $(VAR_A)/$(VAR_B)
image: ...
Hier erstellen wir auch eine neue Umgebungsvariable VAR_C aus zwei Umgebungsvariablen, die in der Datei ConfigMap vorhanden sind.
Wir können die ConfigMap wie folgt aktualisieren:
> kubectl create configmap task-runner-env-configmap --from-env-file=.env -o yaml --dry-run | kubectl apply -f -
Wir haben eine ConfigMap erstellt, um die Schlüssel-Wert-Paare in der Datei ".env" an die Container zu übergeben. Dabei gibt es jedoch ein großes Problem. Wir können keine Umgebungsvariablen in anderen Teilen unserer Manifeste verwenden. Nehmen wir zum Beispiel an, dass Sie das Repository mit der Umgebungsvariablen IMAGE_REGISTRY wechseln wollen:
spec:
containers:
- envFrom:
- configMapRef:
name: task-runner-env-configmap
env:
- name: VAR_C = $(VAR_A)/$(VAR_B)
image: $(IMAGE_REGISTRY)...
Das wird nicht funktionieren! Wir schreiben das Jahr 2023, und weil dieses (triviale) Feature nicht funktioniert, entwickeln Menschen auf der ganzen Welt ihre eigenen Skripte, um es zum Laufen zu bringen. Wunderbar. Wie auch immer, was muss das Skript tun:
- Eine neue (Bash-)Shell starten.
- Exportieren Sie die Schlüssel-Wert-Paare in der Datei '.env'.
- Verwenden Sie 'envsubst', um die Variablen in unserem Manifest zu ersetzen.
Zunächst erstellen wir eine neue Shell, damit dist die aktuelle Umgebung nicht stört. In der neuen Umgebung erstellen wir die Umgebungsvariablen mithilfe der Datei ".env". Schließlich ersetzen wir die Variablen:
> envsubst < mydeploy.yaml | kubectl apply -f -
PersistentVolumes und VolumeClaims
Kompose hat für mich einen Abschnitt VolumeMounts und einen Abschnitt Volumes in den Dateien Pods YAML mit hostPath erstellt. Das bedeutet, dass die tatsächlichen Verzeichnisse von localhost in den Deployment -Dateien fest codiert sind. Offensichtlich nicht das, was wir wollen.
Also habe ich einige PersistentVolume und PersistentVolumeClaim-Manifeste erstellt und sie in den Deployment -Dateien verwendet.
Erstellen von Services für die Deployments
Hier zeige ich nur den Dienst local-web .
# task-runner-local-web-service.yaml
apiVersion: v1
kind: Service
metadata:
# metadata name must not contain dots
name: task-runner-local-web-service
namespace: default
spec:
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
selector:
task-runner.local-web.deployment: task-runner-local-web-deployment
Starten Sie den Dienst:
> kubectl apply -f task-runner-local-web-service.yaml
Änderungen am Anwendungscode
Ich musste nur kleine Änderungen an meinem Anwendungscode vornehmen. Wie oben erwähnt, habe ich prefix die anfänglichen Dienstnamen mit 'task-runner-' versehen. Um die Kompatibilität mit Docker-Compose zu wahren, verwende ich jetzt Umgebungsvariablen für die Servicenamen und Ports, die als Schlüssel-Wert-Paare in der Datei '.env' angegeben sind.
Obwohl die Ports jetzt in allen Services angegeben sind, besteht keine Notwendigkeit, etwas zu ändern, da die Ports an die Services gebunden sind und jeder Service seine eigene IP address hat.
Um zu überprüfen, wo eine Instanz eines Pod läuft, speichere ich die Umgebungsvariable HOSTNAME zusammen mit dem Ergebnis eines Tasks. Für Docker Swarm verwende ich .Node.Hostname. In den Protokollen können wir dieses Feld überprüfen.
Verwendung von nodePort , um den Dienst rabbitmq für das Backend bereitzustellen
Der Task Runner ist in Betrieb, aber es besteht keine Verbindung zur Außenwelt. Es gibt mehrere Möglichkeiten, den rabbitmq Service zu exponieren und hier verwende ich nodePort. Wir erstellen einfach einen zusätzlichen Service mit dem Typ nodePort und geben die Portnummer an:
# task-runner-rabbitmq-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
# metadata name must not contain dots
name: task-runner-rabbitmq-nodeport
namespace: default
spec:
type: NodePort
# The range of valid ports is 30000-32767
ports:
- name: amqp
port: 10763
targetPort: 5672
nodePort: 31763
- name: http
port: 20763
targetPort: 15672
nodePort: 32763
selector:
task-runner.rabbitmq.deployment: task-runner-rabbitmq-deployment
Dann können wir auf diesen Kubernetes Service auf dem Host verweisen wie: localhost:<port>.
Um den Backend Docker Container mit diesem Port zu verbinden, fügen wir einige Zeilen in die Datei 'docker-compose.yaml' ein:
extra_hosts:
- "host.docker.internal:host-gateway"
Und verweisen dann auf den Dienst als:
host.docker.internal:<port>
Hinzufügen eines Worker Nodes, DNS funktioniert nicht
Natürlich wollte ich einen neuen Knoten hinzufügen und dort Pods ausführen. Mit VitualBox auf meinem Entwicklungsrechner habe ich einen VM mit Ubuntu Server 22.04 erstellt. Dann Microk8s hinzugefügt und den neuen Knoten verbunden.
Probleme ... Kubernetes DNS funktionierte nicht auf dem neuen Knoten. Es gibt viele DNS-Probleme, die auf der Seite Microk8s Github erwähnt werden. Das hat wahrscheinlich mit iptables zu tun ...
Ich konnte einen funktionierenden Kubernetes -Cluster mit zwei VirtualBox -Maschinen einrichten. Im Moment untersuche ich dies.
Entwicklung und Fehlersuche auf Kubernetes
Einer der Pods funktionierte nicht. Ich konnte im Protokoll sehen, was falsch war, aber wir wollen nicht in eine Schleife von gehen:
--+-> make changes ---> build image ---> test --+--->
^ |
| v
+--------------------<------------------------+
Also habe ich den externen Projektcode wieder in den Container eingebunden, was mich zurück in die Entwicklungsumgebung bringt, und konnte das Problem sehr schnell lösen.
Werkzeuge
Ich habe während der Migration nur ein einziges zusätzliches Tool verwendet: yamllint zur Validierung von Manifesten (YAML -Dateien).
> sudo apt-get install yamllint
Und dann zum Beispiel ausführen:
> yamllint task-runner-local-web-deployment.yaml
Zusammenfassung
Zunächst einmal habe ich nur zwei Wochen mit Kubernetes verbracht (schießen Sie nicht auf den Klavierspieler). Vielleicht habe ich einige falsche Entscheidungen getroffen, aber ein Teil meiner Anwendung wird jetzt von Kubernetes ausgeführt und verwaltet.
Docker Swarm ist eine einfache Wahl, wenn es um die Orchestrierung von Docker-Compose Projekten geht. Er funktioniert einfach, ohne ernsthafte Einschränkungen. Für meine Anwendung, die aus mehreren Docker-Compose-Projekten besteht, benötige ich nicht Tausende von Replikaten. Mit Docker Swarm starten Sie einen neuen Knoten, fügen einige Deploy-Direktiven zu Ihren docker-compose.yml-Dateien hinzu und sind fertig. Docker Netzwerk ist eine der besten Funktionen von Docker. Mit Docker Swarm müssen Sie lediglich ein Netzwerk in ein verschlüsseltes Ingress -Overlay-Netzwerk umwandeln, um zwischen Knoten zu kommunizieren.
Der Wechsel zu Kubernetes bietet mehr Flexibilität bei der Bereitstellung, aber der Hauptgrund, warum Kubernetes zur ersten Wahl geworden ist, ist, dass es sehr weit verbreitet ist und viele Tools damit integriert sind. In diesem Punkt hinkt Docker Swarm hinterher. Zum Zeitpunkt des Verfassens dieses Beitrags scheint die Entwicklung von Docker Swarm mehr oder weniger ins Stocken geraten zu sein (gilt es als voll funktionsfähig?). Das ist eine Schande, denn es ist eine großartige Lösung für ein sehr komplexes Problem.
In einer idealen Welt ist die Entwicklungsumgebung identisch mit der Produktionsumgebung. Wenn es in der Entwicklung läuft, läuft es auch in der Produktion. Docker Swarm kommt dem sehr nahe, ohne viel Komplexität. Die Verwendung von Kubernetes hingegen schafft eine Lücke. Es ist so, als ob der Entwickler seine Arbeit getan hätte, jetzt überlässt er es den DevOps, es in die Produktion zu bringen. Wenn der Entwickler Änderungen an Docker-Compose-Projekten vornimmt, müssen die DevOps auch daran arbeiten.
Wenn Sie Kubernetes für die Produktion verwenden, ist es meiner Meinung nach unvermeidlich, dass in der Entwicklungsumgebung auch Kubernetes läuft. Und in vielen Fällen auch Docker Swarm . Erleichtert oder erschwert dies die Entwicklung und Produktion im Vergleich zu Docker Swarm allein? Microk8s ist in einer Entwicklungsumgebung sehr einfach zu verwenden, aber das ist nur der Anfang. Das Erstellen von Docker-Compose Projektdateien ist sehr einfach. Man hat normalerweise zwei yaml-Dateien und eine '.env'-Datei. Nach der Konvertierung hatte mein Kubernetes -Projekt bereits 14 Kubernetes -Yaml-Dateien und mehr.
Es wäre eine sehr gute Idee, wenn Kubernetes einige Erweiterungen für die YAML -Dateien hinzufügen würde, die das Importieren/Hinzufügen anderer Dateien (wie Nginx) und Umgebungsdaten ermöglichen. Im Moment müssen wir unsere eigenen Skripte schreiben oder etwas wie Helm verwenden ... mehr Dinge, die wir lernen müssen.
Wie auch immer, in diesem Beitrag habe ich versucht zu untersuchen, was nötig wäre, um einen Teil meiner Anwendung von Docker Swarm nach Kubernetes zu verschieben. Aufgrund der Ergebnisse habe ich beschlossen, auf Kubernetes umzusteigen. Ich werde Docker Swarm nicht mehr für die Produktion verwenden. Und ich werde auch Kubernetes für die Entwicklung verwenden.
Der Anfang mit Kubernetes ist nicht sehr schwierig, aber manchmal sehr verwirrend. Nach ein paar Tagen haben Sie die meisten Konzepte verstanden und haben etwas, das funktioniert. Von da an kann man sich verbessern und erweitern. Es kann (sehr) schwerwiegende und zeitraubende Probleme geben, z. B. wenn DNS nicht funktioniert. Ich möchte kein iptables-Spezialist werden, aber es gibt keinen anderen Weg.
Die Anzahl der Änderungen, die ich an meiner Anwendung vornehmen musste, ist sehr begrenzt:
- Änderung der Namen auf RFC1123.
- Voranstellen von Dienstnamen unter Verwendung von Umgebungsvariablen.
- Speichern von anderen Umgebungsvariablen in den Ergebnissen.
Zurück zum Programmieren ...
Links / Impressum
Configure a Pod to Use a ConfigMap
https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap
Connecting Kubernetes and Docker
https://developers.redhat.com/blog/2017/09/22/connecting-kubernetes-docker
Helm - The package manager for Kubernetes
https://helm.sh
How to go from Docker to Kubernetes the right way
https://www.opensourcerers.org/2021/02/01/how-to-go-from-docker-to-kubernetes-the-right-way
How To Migrate a Docker Compose Workflow to Kubernetes
https://www.digitalocean.com/community/tutorials/how-to-migrate-a-docker-compose-workflow-to-kubernetes
Kompose
https://github.com/kubernetes/kompose
Kubernetes - Documentation - Concepts
https://kubernetes.io/docs/concepts
Kubernetes - Documentation - kubectl Cheat Sheet
https://kubernetes.io/docs/reference/kubectl/cheatsheet
Kustomize - Kubernetes native configuration management
https://kustomize.io
MicroK8s documentation - home
https://microk8s.io/docs
Running Kubernetes locally on Linux with Microk8s
https://kubernetes.io/blog/2019/11/26/running-kubernetes-locally-on-linux-with-microk8s
Using private docker registry inside kubernetes
https://sam-thomas.medium.com/using-private-docker-registry-inside-kubernetes-46a3cede7cb1
When should I use envFrom for configmaps?
https://stackoverflow.com/questions/66352023/when-should-i-use-envfrom-for-configmaps
Mehr erfahren
Docker Swarm Kubernetes
Neueste
- Ausblenden der Primärschlüssel der Datenbank UUID Ihrer Webanwendung
- Don't Repeat Yourself (DRY) mit Jinja2
- SQLAlchemy, PostgreSQL, maximale Anzahl von Zeilen pro user
- Anzeige der Werte in den dynamischen Filtern SQLAlchemy
- Sichere Datenübertragung mit Public Key Verschlüsselung und pyNaCl
- rqlite: eine hochverfügbare und distverteilte SQLite -Alternative
Meistgesehen
- Verwendung von Pythons pyOpenSSL zur Überprüfung von SSL-Zertifikaten, die von einem Host heruntergeladen wurden
- Verwendung von UUIDs anstelle von Integer Autoincrement Primary Keys mit SQLAlchemy und MariaDb
- Verbindung zu einem Dienst auf einem Docker -Host von einem Docker -Container aus
- PyInstaller und Cython verwenden, um eine ausführbare Python-Datei zu erstellen
- SQLAlchemy: Verwendung von Cascade Deletes zum Löschen verwandter Objekte
- Flask RESTful API Validierung von Anfrageparametern mit Marshmallow-Schemas