angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Sollte ich meinen Docker Swarm auf Kubernetes migrieren?

Ich habe ein Docker-Compose Projekt nach Kubernetes migriert und mich für Kubernetes entschieden.

15 September 2023
post main image
https://www.pexels.com/nl-nl/@fotios-photos

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:

  1. Eine neue (Bash-)Shell starten.
  2. Exportieren Sie die Schlüssel-Wert-Paare in der Datei '.env'.
  3. 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

Einen Kommentar hinterlassen

Kommentieren Sie anonym oder melden Sie sich zum Kommentieren an.

Kommentare

Eine Antwort hinterlassen

Antworten Sie anonym oder melden Sie sich an, um zu antworten.