Linux-Schmie.de Dokumentationen

Dokumentationen zu den aktuellen Linux-Schmie.de Trainings

Unterabschnitte von Linux-Schmie.de Dokumentationen

Unterabschnitte von HowTos

cryptsetup

HowTos rund um das Thema cryptsetup

Unterabschnitte von cryptsetup

cryptsetup Grundlagen

Erstellen einer verschlüsselten Partition

cryptsetup luksFormat $PARTITION

luks Header ausgeben lassen

cryptsetup luksDump $PARTITION

Mounten einer verschlüsselten Partition

cryptsetup luksOpen $PARTITION $NAME

Unter /dev/mapper/$NAME ist nach dem mounten die entschlüsselte Partition zu finden.

Erstellen eines Recovery Key

cryptsetup selber unterstützt nicht das Erzeugen eines Recovery Keys. Dies kann aber systemd-cryptenroll übernehmen

systemd-cryptenroll $PARTITION --recovery-key

TPM2 für die automatische Ver- und Entschlüsselung

Mit systemctl-cryptenroll ist es ebenfalls möglich TPM2 für die automatische Ver- und Entschlüsselung zu nutzen.

systemd-cryptenroll $PARTITION --tpm2-device=auto

Nach dem Ausführen des Befehls wird das TPM2 als Schlüsseldevice genutzt. Sobald sich Änderungen an der EFI Konfiguration ergeben wird dieser Schlüssel allerdings ungültig und muss erneut eingetragen werden.

git

git

git Commit Historie eines Branches entfernen

Den aktuellen Branch (im Beispiel ‘main’) auf den Stand bringen, der später im root der bereinigten Commit Historie stehen soll.

Als Zwischenschritt wird ein Branch (’temp_branch’) erzeugt. Durch den Parameter --orphan hat dieser keine Commit Historie.

$ git checkout --orphan temp_branch

In diesem Branch alle Dateien Stagen (git add) und den initialen Commit durchführen

$ git commit -m "initial commit"

Damit sind alle Projektdateien im neuen Branch enthalten, haben aber nicht die Commit Historie des anderen Branches.

Der alte Branch kann nun entfernt werden.

$ git branch -D main

Und der aktuelle Branch auf den Namen des alten Branches umbenannt werden.

$ git branch -m main

Bisher wurden die Änderungen nur an dem lokalen Repository vorgenommen und es wurden Referenzen auf die Branches in einer Form geändert, dass andere Clones des Repositories nicht mehr damit zurecht kommen. Dies würde man beim ersten Push auf den Server beobachten, der die Änderungen ablehnt. Durch den zusätzlichen Parameter --force lässt sich trotzdem der geänderte Stand des Repositories pushen, allerdings müssen all diejenigen, die einen Clone des Repositories haben diesen Parameter auch beim nächsten pull angeben um das angepasste Repository zu laden.

$ git push --force origin main

Automatic rebase

Um einen git rebase mehrerer commits ohne manuelle Interaktion durchzuführen kann das Editor Kommando gegen einen sed getauscht werden und damit bis auf den ersten ‘pick’ alle weiteren gegen ein ‘squash’ getauscht werden.

$ GIT_SEQUENCE_EDITOR="sed -i '2,\$s/^pick/squash/'" git rebase -i $REBASE_BASE --autosquash

Gnome

HowTos rund um das Thema Gnome

Unterabschnitte von Gnome

Desktop Dateien

Desktop Dateien unter Gnome müssen - damit sie direkt ausgeführt werden können - als launchable deklariert sein.

Launchable sind Desktop Dateien, wenn sie durch den Benutzer ausführbar sind und per gio als trusted deklariert wurden.

user@linux: ~$ gio set ~/Desktop/datei.desktop metadata::trusted true
user@linux: ~$ chmod +x ~/Desktop/datei.desktop

Gnome Remote Desktop

Der Gnome Desktop ist mit einer eigenen Lösung für den Remote Zugriff ausgestattet. Diese Lösung beinhaltet die Möglichkeit per RDP (Remote Desktop Protocol) oder VNC auf eine bestehende Sitzung zuzugreifen.

Die Konfiguration wird durch den Benutzer innerhalb der Einstellungen durchgeführt.

Alternativ lassen sich die Protokolle auch über die Kommandozeile aktivieren und konfigurieren. Hierzu dient das Kommando grdctl.

Übersicht über die aktuelle Konfiguration

user@linux ~$ grdctl status
Overall:
        Unit status: inactive
RDP:
        Status: enabled
        Port: 3389
        TLS certificate: /home/mgisbers/.local/share/gnome-remote-desktop/certificates/rdp-tls.crt
        TLS fingerprint: 4c:df:a8:66:16:fe:ad:e7:be:3a:c9:c1:81:75:8d:db:f2:eb:6d:45:d0:21:b1:4d:d1:1c:64:83:bc:c2:93:b1
        TLS key: /home/mgisbers/.local/share/gnome-remote-desktop/certificates/rdp-tls.key
        View-only: no
        Negotiate port: yes
        Username: (hidden)
        Password: (hidden)
VNC:
        Status: disabled
        Port: 5900
        Auth method: prompt
        View-only: yes
        Negotiate port: no
        Password: (empty)

Aktivieren und Deaktivieren von RDP oder VNC

user@linux ~$ grdctl rdp enable
user@linux ~$ grdctl vnc enable

user@linux ~$ grdctl rdp disable
user@linux ~$ grdctl vnc disable

Anpassen der Ports

user@linux ~$ grdctl rdp set-port $PORT
user@linux ~$ grdctl vnc set-port $PORT

Viewonly aktivieren

Um einen Teilnehmer an einer Session nur zuschauen zu lassen, kann die Session auf view-only geschaltet werden.

user@linux ~$ grdctl rdp enable-view-only true
user@linux ~$ grdctl vnc enable-view-only true

user@linux ~$ grdctl rdp disable-view-only true
user@linux ~$ grdctl vnc disable-view-only true

Zugangsdaten festlegen

Auch die Zugangsdaten lassen sich über die Kommandozeile festlegen und auch wieder zurücksetzen.

user@linux ~$ grdctl rdp set-credentials $USER $PASSWORD
user@linux ~$ grdctl vnc set-password $PASSWORD

user@linux ~$ grdctl rdp clear-credentials
user@linux ~$ grdctl rdp clear-password

Kubernetes

HowTos rund um das Thema Kubernetes

Unterabschnitte von Kubernetes

ClusterIssuer

Für die Verwaltung von Zertifikaten wird in der Regel der cert-manager eingesetzt. dieser kann eine lokale CA benutzen um eigene Zertifikate zu erstellen, aber auch per ACME - Protokoll erreichbare Zertifizierer wie z. B. Let’s Encrypt.

Werden die Zertifizierer als Issuer für einzelne Namespaces erstellt, können sie auch nur in den Namespaces benutzt werden.

Für die globale Nutzung gibt es den ClusterIssuer, der im gesamten Cluster verfügbar ist.

---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: acme-production
spec:
  acme:
    email: michael+kubernetes@gisbers.de
    privateKeySecretRef:
      name: acme-production
    server: https://acme-v1.o.mylinuxtime.de/acme/directory
    solvers:
      - http01:
          ingress: {}

Einbauen lässt sich der ClusterIssuer in einen Ingress durch eine Annotation.

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: acme-production
[...]

Example Pods

Create OOM

Dieser pod erzeugt durch das Überschreiten der verfügbaren Resourcen einen OOM.

apiVersion: v1
kind: Pod
metadata:
  name: memory-oom
spec:
  containers:
    - name: memory-oom
      image: "polinux/stress"
      resources:
        requests:
          memory: "50Mi"
        limits:
          memory: "100Mi"
      command: ["stress"]
      args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]

Flux

Installation

Für die Installation muss zunächst der flux Client installiert sein.

curl -s https://fluxcd.io/install.sh | sudo bash

Bei Benutzung eines GitLab wird ein Access Token mit Schreibrechten auf das Repository oder die Gruppe benötigt. Sinnvollerweise kann dieser in der Datei $HOME/.gitlab hinterlegt sein.

Danach kann flux mit den benötigen Umgebungswerten gestartet werden.

export GITLAB_TOKEN="$(<.gitlab)"
export GITLAB_HOSTNAME="git.mylinuxtime.de"
export GITLAB_OWNER="kubernetes/flux" # GitLab Gruppe für das Repository
export GITLAB_REPOSITORY="prod-kube"  # Repository in der Gruppe
export GITLAB_BRANCH="main"           # zu benutzender Branch
export GITLAB_PATH="prod"             # Pfad innerhalb des Repositories

flux bootstrap gitlab --owner $GITLAB_OWNER \
                      --repository $GITLAB_REPOSITORY \
                      --branch $GITLAB_BRANCH \
                      --path $GITLAB_PATH \
                      --token-auth \
                      --hostname $GITLAB_HOSTNAME

Es wird automatisch das benötigte Repository angelegt und die Dateien für flux abgelegt.

Im Cluster werden die benötigten CRDs und Deployments angelegt und gestartet.

Update

Ob eine neuere Version für ein Update zur Verfügung steht kann über das Subkommando check geprüft werden.

$ flux check
► checking prerequisites
✗ flux 2.4.0 <2.5.0 (new CLI version is available, please upgrade)
✔ Kubernetes 1.30.4+rke2r1 >=1.28.0-0
► checking version in cluster
✔ distribution: flux-v2.4.0
✔ bootstrapped: true
► checking controllers
✔ helm-controller: deployment ready
► ghcr.io/fluxcd/helm-controller:v1.1.0
✔ kustomize-controller: deployment ready
► ghcr.io/fluxcd/kustomize-controller:v1.4.0
✔ notification-controller: deployment ready
► ghcr.io/fluxcd/notification-controller:v1.4.0
✔ source-controller: deployment ready
► ghcr.io/fluxcd/source-controller:v1.4.1
► checking crds
✔ alerts.notification.toolkit.fluxcd.io/v1beta3
✔ buckets.source.toolkit.fluxcd.io/v1
✔ gitrepositories.source.toolkit.fluxcd.io/v1
✔ helmcharts.source.toolkit.fluxcd.io/v1
✔ helmreleases.helm.toolkit.fluxcd.io/v2
✔ helmrepositories.source.toolkit.fluxcd.io/v1
✔ kustomizations.kustomize.toolkit.fluxcd.io/v1
✔ ocirepositories.source.toolkit.fluxcd.io/v1beta2
✔ providers.notification.toolkit.fluxcd.io/v1beta3
✔ receivers.notification.toolkit.fluxcd.io/v1
✔ all checks passed

Die Komponenten können dann innerhalb des Repositories aktualisiert werden.

$ flux install --export > flux-system/gotk-components.yaml
$ git add flux-system/gotk-components.yaml
$ git commit -m "Update gotk components"
$ git push

Install Cert-Manager

Der Cert-Manager ist eine Verwaltungskomponente für Zertifikate innerhalb eines Kubernetes Clusters.

Erst mit dem Cert-Manager ist es möglich Zertifikate zu erstellen, verwalten und löschen. Der Cert-Manager bringt über CRDs eigene Objekte für die Verwaltung mit

Installation

Die einfachste Installation kann direkt über Helm erfolgen.

Typischerweise wird für den Cert-Manager ein eigener Namespace (cert-manager) angelegt.

helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager --namespace cert-manager \
  --create-namespace --set installCRDs=true

Beispiel für die Anwendung

Bei einem extern erreichbaren Webserver soll neben der Konfiguration eines Ingress mit nginx auf Port 80 auch ein Zugang über https (Port 443) ermöglicht werden.

Dazu bedarf es eines Zertifikats von einer Trusted-CA. Bei extern erreichbaren Webseiten bietet sich dafür z. B. Let’s Encrypt an.

Issuer

Um bei Let’s Encrypt Zertifikate abholen zu können bedarf es zunächst eines Issuers über diese Objekt wir die Anmeldung an Let’s Encrypt durchgeführt. Die gespeicherte Anmeldung dient später auch als Berechtigung die Zertifikate zu beantragen.

Der Issuer wird im gleichen Namespace wie der spätere Ingress angelegt.

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: acme
  namespace: example
spec:
  acme:
    email: user@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: acme-issuer
    solvers:
      - http01:
          ingress:
            class: nginx

Sobald das Objekt erstellt ist, wird die Anmeldung durchgeführt. Der Status ist im status des Ingress Objekts sichtbar.

Ingress

Um einen Webserver per https erreichbar zu machen, wird zusätzlich zu der normalen Konfiguration ein Abschnitt spec.tls benötigt in dem der SNI spec.tls.hosts[] und der Name eines Secrets für das Zertifikat angegeben wird.

Damit der Cert-Manager sich um das Zertifikat kümmert muss eine Annotation cert-manager.io/issuer: angelegt werden, in die der Name des zu benutztenden Issuers angegeben wird.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/issuer: "acme"
  name: nginx
  namespace: example
spec:
  ingressClassName: nginx
  rules:
    - host: example.com
      http:
        paths:
          - pathType: Prefix
            path: /
            backend:
              service:
                name: nginx
                port:
                  number: 80
  tls:
    - hosts:
        - example.com
      secretName: example.com-tls

Nach Anlegen des Objekts sorgt der Cert-Manager für die Erzeugung des Privaten Schlüssels und eines Certificate Sign Requests (CSR). Den CSR sendet er zur Validierung an den über den Issuer angegeben Dienst und sorgt sich um die Kommunikation mit diesem um das Zertifikat zu holen. Sobald es vorliegt wird es in dem unter secretName angegebenen Secret zusammen mit dem Key abgelegt und steht für den Ingress zu Verfügung. Dieser baut das Zertifikat - sobald es vorhanden ist - ein und stellt die Domain per https zur Verfügung.

ClusterIssuer

Der Issuer steht immer nur in dem Namespace in dem er angelegt wurde zur Verfügung. Um einen Issuer auch über die Namespaces hinweg benutzen zu können gibt es den ClusterIssuer.

Dieser wird - in der Regel - Im Namespace des Cert-Manager abgelegt, kann aber - solang es keine RBAC Einschränkungen gibt - aus allen Namespaces benutzt werden.

Cluster-Issuer

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: acme
  namespace: cert-manager
spec:
  acme:
    email: user@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: acme-issuer
    solvers:
      - http01:
          ingress:
            class: nginx

Tatsächlich unterscheidet sich die Konfiguration des ClusterIssuer nur durch den unterschiedlichen kind von dem Issuer (ClusterIssuer statt Issuer)

Ingress

Auch beim Ingress ist nur die Annotation zu ändern.

Hier wird dann cert-manager.io/cluster-issuer: benutzt.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: "acme"
  name: nginx
  namespace: example
spec:
  ingressClassName: nginx
  rules:
    - host: example.com
      http:
        paths:
          - pathType: Prefix
            path: /
            backend:
              service:
                name: nginx
                port:
                  number: 80
  tls:
    - hosts:
        - example.com
      secretName: example.com-tls

Install GitLab on Kubernetes

Install

helm repo add gitlab https://charts.gitlab.io/
helm repo update
helm upgrade --install gitlab gitlab/gitlab \
  --timeout 600s \
  --set global.hosts.domain=git.49-12-189-3.ip.dynlinux.io \
  --set global.hosts.externalIP=49.12.189.3 \
  --set certmanager-issuer.email=michael@gisbers.de \
  --set global.edition=ce \
  --set installCertmanager=false \
  --set nginx-ingress.enabled=false \
  --set prometheus.install=false \
  --set global.ingress.class=nginx

Kubectl Konfiguration

Das Tool kubectl wird für die direkte Kommunikation mit einer Kubernetes API benötigt.

Um mit der API kommunizieren zu können wird eine Konfigurationsdatei benötigt (KUBECONFIG) in der die Informationen zur API, wie die Adresse, Benutzer, Kennwort oder Zertifikate enthalten sind. Die Konfigurationdatei wird in der Regel in der Datei ~/.kube/config abgelegt. Alternativ kann die Umgebungsvariable KUBECONFIG gesetzt werden um eine alternative Konfigurationsdatei angeben zu können.

KUBECONFIG Dateien zusammenfassen

Statt mehrere Konfigurationsdateien zu führen ist es Sinnvoll die Konfigurationsdaten in eine Datei zusammen zu fassen.

Auf Grund des Formats können die Dateien nicht einfach zusammenkopiert werden. Die einzelnen Daten müssen in die jeweiligen Sektionen der Konfigurationdatei verteilt werden.

Mit dem folgenden Workflow ist es möglich dies teilweise zu automatisieren.

  • Erstellen einer Kopie der aktuellen Konfigurationsdatei (es wird von ~/.kube/config ausgegangen)
  • Setzen der Variable KUBECONFIG auf die kopierte Datei und die neue Datei (/tmp/kube_config). Weitere Dateien können durch Doppelpunkt getrennt angegeben werden
  • Ausgeben der Kopie und der zusätzlichen Datei, oder Dateien über kubectl in die Datei ~/.kube/config.
  • Löschen der temporären Dateien
  • Löschen der Variable KUBECONFIG
user@linux # cp ~/.kube/config ~/.kube/config_temp
user@linux # export KUBECONFIG="~/.kube/config_temp:/tmp/kube_config"
user@linux # kubectl config view --flatten >~/.kube/config
user@linux # rm ~/.kube/config_temp /tmp/kube_config
user@linux # unset KUBECONFIG

Auswählen des Context

Sobald die Konfigurationsdatei für kubectl mehr als einen Eintrag enthält, kann der Context in dem gearbeitet werden soll gewechselt werden.

Dies geschieht entweder durch das Festlegen eines Default Context:

user@linux # kubectl config use-context meincontext

Oder durch die Nutzung des Parameters --context bei kubectl oder anderen Befehlen wie z. B. flux, die dies unterstützen.

user@linux # kubectl get --context meincontext nodes

Über den Befehl kubectl config get-contexts lässt sich eine Liste der verfügbaren Umgebungen anzeigen.

Kubernetes auf podman mit kind

Über das Tool kind ist es möglich schnell einen Kubernetes Cluster innerhalb einer podman Umgebung aufzusetzen.

Vorbereitung

Dazu muss das Tool heruntergeladen oder über das Paketmanagement installiert.

podman und auch kubectl müssen ebenfalls installiert und konfiguriert sein.

Einrichtung des Clusters

Der einfachste Weg den Cluster einzurichten geht über das systemctl-run Kommando. Hierbei übernimmt der systemd die Kontrolle über die Umgebung für Umgebung in der der podman Befehl läuft.

$ systemd-run -p Delegate=yes --setenv=KIND_EXPERIMENTAL_PROVIDER=podman \
              --scope --user kind create cluster
Running as unit: run-rc08baccf7a724130a9284623ccd2ddf9.scope; invocation ID: 0fb8f6ec97a94f5782bbfc3f0f78a3ae
using podman due to KIND_EXPERIMENTAL_PROVIDER
enabling experimental podman provider
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.27.3) 🖼 
 ✓ Preparing nodes 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
Set kubectl context to "kind-kind"
You can now use your cluster with:

kubectl cluster-info --context kind-kind

Thanks for using kind! 😊

Nach dieser Installation kann direkt über das kubectl Kommando ein Zugriff auf die Kubernetes Node erfolgen.

$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:34125
CoreDNS is running at https://127.0.0.1:34125/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

Dazu wurde die Datei $HOME/.kube/config um den Context kind-kind erweitert oder eine neue Datei erzeugt.

Genauere Informationen über den laufenden Cluster Node lassen sich über kubectl cluster-info dump abrufen.

Cluster Konfiguration

Statt den Cluster mit einer Default Konfiguration zu starten, kann man eine yaml Datei mit passenden Werten zur Erzeugung nutzen.

Beispiele:

# three node (two workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
# six node (three workers) full cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: control-plane
- role: control-plane
- role: worker
- role: worker
- role: worker

Die erstellten yaml Dateien sind bei der Erstellung des Clusters anzugeben.

$ systemd-run -p Delegate=yes --setenv=KIND_EXPERIMENTAL_PROVIDER=podman \
              --scope --user kind create cluster --config multinode.yaml \
              --name multinode

Weitere Beispiele und Parameter sind in der kind Konfiguration zu finden.

Cluster entfernen

Der Befehl kind kann ebenfalls zum entfernen eines Clusters genutzt werden.

$ systemd-run -p Delegate=yes --setenv=KIND_EXPERIMENTAL_PROVIDER=podman \                                                                       
              --scope --user kind delete cluster --name kind
Running as unit: run-rba10793fe745483eb50981e5b0d3417b.scope; invocation ID: ce7a140694494618a843f3a01687e619
using podman due to KIND_EXPERIMENTAL_PROVIDER
enabling experimental podman provider
Deleting cluster "kind" ...
Deleted nodes: ["kind-control-plane"]

Erweitertes Beispiel

Installation eines einfachen Clusters mit einer Node. nginx-ingress über die Ports 8000 und 8443 auf dem Host zu erreichen.

$ cat >/tmp/cluster.yaml <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  extraPortMappings:
  - containerPort: 80
    hostPort: 8000
    protocol: TCP
  - containerPort: 443
    hostPort: 8443
    protocol: TCP
EOF
$ systemd-run -p Delegate=yes --setenv=KIND_EXPERIMENTAL_PROVIDER=podman \
              --scope --user kind create cluster --config=/tmp/cluster.yaml
$ kubectl --context kind-kind apply -f \
    https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
$ cat >/tmp/ingress.yaml <<EOF
kind: Pod
apiVersion: v1
metadata:
  name: foo-app
  labels:
    app: foo
spec:
  containers:
  - command:
    - /agnhost
    - netexec
    - --http-port
    - "8080"
    image: registry.k8s.io/e2e-test-images/agnhost:2.39
    name: foo-app
---
kind: Service
apiVersion: v1
metadata:
  name: foo-service
spec:
  selector:
    app: foo
  ports:
  # Default port used by the image
  - port: 8080
---
kind: Pod
apiVersion: v1
metadata:
  name: bar-app
  labels:
    app: bar
spec:
  containers:
  - command:
    - /agnhost
    - netexec
    - --http-port
    - "8080"
    image: registry.k8s.io/e2e-test-images/agnhost:2.39
    name: bar-app
---
kind: Service
apiVersion: v1
metadata:
  name: bar-service
spec:
  selector:
    app: bar
  ports:
  # Default port used by the image
  - port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /\$2
spec:
  rules:
  - http:
      paths:
      - pathType: ImplementationSpecific
        path: /foo(/|$)(.*)
        backend:
          service:
            name: foo-service
            port:
              number: 8080
      - pathType: ImplementationSpecific
        path: /bar(/|$)(.*)
        backend:
          service:
            name: bar-service
            port:
              number: 8080
---
EOF
$ kubectl --context patch configmap/ingress-nginx-controller \
          -n ingress-nginx --type merge \
          -p '{"data":{"worker-processes": "2"}}'
$ kubectl --context kind-kind apply -f /tmp/ingress.yaml

Kubernetes Certificate

Kubernetes Certificate

Manuellen TLS Eintrag erstellen

Für den manuellen Eintrag wird die komplette Zertifikatskette ohne das root-Zertifikat in einer Datei und der Key in einer anderen Datei benötigt. Beide müssen im PEM - Format vorliegen.

Sollten die Intermediate Zertifikate und das eigentliche Zertifikat nicht in einer Datei sein, dann lassen sich diese per cat Befehl zusammenfügen.

cat server.ca >> server.crt
$ kubectl create secret tls --cert server.crt \
    --key server.key --namespace default
    example.com

Der zusätzliche Parameter -o yaml gibt den Eintrag aus statt ihn per API in den Cluster zu senden.

Alternativ kann der Eintrag auch selber erstellt werden:

---
apiVersion: v1
kind: Secret
metadata:
namespace: default
type: kubernetes.io/tls
  name: example.com
data:
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURH [...]
  tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUVA [...]

Der hinter tls.crt und tls.key angegebene Text entspricht dem Inhalt der Zertifikats- und Key-Datei in einer mit base64 ummantelten Version ohne Zeilenumbrüche.

$ base64 -i -w0 server.crt

Kubernetes Cookbook

Pods

List all pods on a node

kubectl get pods --all-namespaces -o wide --field-selector spec.nodeName=<node>

List all pods with custom outout

kubectl get pod -A -o=custom-columns=POD_NAME:.metadata.name,STATUS:.status.phase,NAMESPACE:.metadata.namespace,CONTAINER_NAME:.spec.containers[*].name

List all pods in a namespace

kubectl get pods -n <namespace>

List all pods not in the running state

kubectl get pods -n <namespace> --field-selector "status.phase!=Running"

Kubernetes Dashboard

Über das Dashboard lassen sich die Informationen über den Cluster visualisieren und anpassen. Sobald ein metrics-server im Cluster existiert, kann man auch die Metriken visualisieren.

Installation metrics-server

> kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

Sollte der kublet-server nicht mit gültigen Zertifikaten ausgestattet sein, muss dies dem metrics-server mitgeteilt werden.

> kubectl patch deployment metrics-server -n kube-system --type=json  -p '[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'

Installation Dashboard

> helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
> helm upgrade --install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard --create-namespace --namespace kubernetes-dashboard --set web.service.type=NodePort
> kubectl get svc -n kubernetes-dashboard -l app.kubernetes.io/component=web

Diese Installation des Dashboards ist ohne Absicherung per SSL. Diese müsste zusätzlich konfiguriert werden. Alternativ Nutzung von Rancher.

Zugriff auf das Dashboard

Anlegen eine serviceAccounts

kubectl create serviceaccount admin-user -n kubernetes-dashboard

serviceAccount ClusterAdmin Rechte zuweisen

kubectl create clusterrolebinding admin-user-binding --clusterrole=cluster-admin \
        --serviceaccount=kubernetes-dashboard:admin-user

Token generieren und ausgeben lassen

kubectl -n kubernetes-dashboard create token admin-user

SSL Port per Proxy weiterleiten

kubectl -n kubernetes-dashboard port-forward \
        svc/kubernetes-dashboard-kong-proxy 8443:443

Zugriff auf das Dashboard über https://localhost:8443

Kubernetes mit kubeadm auf Rocky Linux

Vorbereitung des Systems

Bevor du mit der Installation von Kubeadm beginnst, musst du dein Rocky Linux 9 System vorbereiten. Du musst sicherstellen, dass bestimmte Pakete und Abhängigkeiten installiert und die Systemeinstellungen korrekt sind.

Setzen des korrekten Hostnamens

Der folgenden Befehl setzt den Hostnamen des Systems auf seine eigene IP Adresse über den Dienst ip.dynlinux.io

sudo hostnamectl set-hostname $(hostname -I | cut -f 1 -d " " | tr . -).ip.dynlinux.io
sudo hostnamectl

Vollständige Aktualisierung des Systems

sudo dnf update -y

Dieser Befehl stellt sicher, dass alle Systempakete auf dem neuesten Stand sind.

Deaktivieren von SELinux

Kubernetes erfordert die Deaktivierung von SELinux, um ordnungsgemäß zu funktionieren. Du kannst dies tun, indem du die Konfigurationsdatei bearbeitest.

sudo sed -i 's/SELINUX=enforcing/SELINUX=permissive/g' /etc/selinux/config

Nach der Deaktivierung über die Konfigurationsdatei muss das System neu gestartet werden.

Alternativ kannst du es auch temporär deaktivieren

sudo setenforce 0

Deaktivieren der Firewall

Die Firewall kann den Netzwerkverkehr zwischen den Kubernetes-Komponenten blockieren. Deaktiviere sie, um mögliche Probleme zu vermeiden:

sudo systemctl stop firewalld
sudo systemctl disable firewalld

Konfigurieren von IP-Weiterleitung und Bridge-Einstellungen

Für die Netzwerkinfrastruktur von Kubernetes ist die IP-Weiterleitung erforderlich. Erstelle die Konfigurationsdatei 99-kubernetes.conf

sudo tee /etc/sysctl.d/99-kubernetes.conf <<EOF
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

Lade die Einstellungen neu

sudo sysctl --system

Lade das Kernelmodul br_netfilter

sudo tee /etc/modules-load.d/k8s.conf <<EOF
br_netfilter
EOF
sudo modprobe br_netfilter

Installation von Containerd

Containerd ist die Container-Laufzeitumgebung (Container Runtime), die von Kubernetes benötigt wird, um Container zu starten und zu verwalten. Kubernetes verwendet die Container Runtime Interface (CRI), um mit der Container-Laufzeitumgebung zu interagieren.

Installation der erforderlichen Pakete

sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y containerd.io

Konfiguration von Containerd

Zuerst erstellst du eine Standardkonfiguration für Containerd:

sudo containerd config default | sudo tee /etc/containerd/config.toml

Anschließend bearbeitest du die Konfigurationsdatei, um das cgroupfs-System für das Systemd-Cgroup-Treiber-System zu verwenden:

sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

Aktivierung und Starten von Containerd

sudo systemctl enable containerd
sudo systemctl restart containerd

Installation von Kubeadm, Kubelet und Kubectl

Nachdem Containerd eingerichtet ist, installierst du die Kubernetes-Tools.

Hinzufügen des Kubernetes-YUM-Repositorys

sudo tee /etc/yum.repos.d/kubernetes.repo <<EOF
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.32/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.32/rpm/repodata/repomd.xml.key
EOF

⚠️ Hinweis: Die Versionsnummer v1.32 sollte an deine gewünschte Kubernetes-Version angepasst werden.

Installation der Kubernetes-Pakete

sudo dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

Dieser Befehl installiert die drei Hauptkomponenten:

  • kubelet: Der Agent, der auf jedem Knoten läuft und die Kommunikation mit dem Control Plane übernimmt.
  • kubeadm: Das Tool zum Bootstrapping des Clusters.
  • kubectl: Das Befehlszeilentool zur Interaktion mit dem Cluster.

Initialisierung des Control-Plane-Knotens

Jetzt kannst du den Control-Plane-Knoten (Master-Knoten) initialisieren. Dies ist der wichtigste Schritt, da er das Herzstück deines Kubernetes-Clusters bildet.

Initialisiere das Kubernetes Control Plane

sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --control-plane-endpoint $IP:6443

Der Parameter –pod-network-cidr ist wichtig, da er das Netzwerk-Subnetz für die Pods festlegt. Der Wert 10.244.0.0/16 ist der Standard, der von vielen CNI-Plugins wie Flannel verwendet wird.

Starten und Aktivieren des Kubelet-Dienstes

sudo systemctl enable --now kubelet

Konfigurieren des Benutzers für kubectl

Nach der erfolgreichen Initialisierung musst du kubectl für den aktuellen Benutzer konfigurieren, um Befehle ausführen zu können:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Installieren eines Pod-Netzwerk-Addons

Ohne ein Pod-Netzwerk-Addon können die Pods nicht miteinander kommunizieren. Flannel ist eine beliebte Wahl.

kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml

Nach der Installation kannst du den Status der Pods überprüfen

kubectl get pods --all-namespaces

Erlaube pods auf der Control Plane

Auf der Control Plane solltest du keine Pods, die nicht zur Control Plane gehören - laufen lassen. Dies kann zu einer Instabilität der Control Plane führen.

Für Test-Setups ist es möglich auch auf der Control Plane Pods laufen zu lassen

kubectl taint nodes <node-name> node-role.kubernetes.io/control-plane:NoSchedule-

Hinzufügen von weiteren Control Plane-Knoten

Um weitere Control Plane-Knoten zu erstellen musst du zunächst die Zertifikatsdaten des Clusters ein Kubernetes Secret speichern. Dazu führst du auf einem existierenden Conntrol Plane-Knoten den folgenden Befehl aus.

sudo kubeadm init phase upload-certs --upload-certs

⚠️ Dieser Schritt ist für das Hinzufügen eines Workers nicht notwendig.

Der Certificate Key Name aus der Ausgabe des vorherigen Befehls wird für den kubeadm join benötigt. Der restliche Aufruf wird mit dem folgenden Befehl ausgegeben auf dem bestehenden Control Plane-Knoten.

sudo kubeadm token create --print-join-command

Den Ausgegebenen Befehl musst du nun um die Parameter --control-plane und --certificate-key <certificate-key-string> erweitern und auf dem neuen Control Plane-Knoten ausführen.

sudo kubeadm join <control-plane-IP>:<control-plane-port> \
     --token <token-string> --discovery-token-ca-cert-hash sha256:<hash-string> \
     --control-plane --certificate-key <certificate-key-string>

Sobald die zusätzliche Node bei kubectl get nodes auftaucht können die korrekten Label gesetzt werden.

kubectl label node <node-name> node-role.kubernetes.io/etcd=
kubectl label node <node-name> node-role.kubernetes.io/control-plane=

Hinzufügen von Worker-Knoten

Kubeadm gibt dir nach der Initialisierung einen kubeadm join-Befehl aus. Du musst diesen Befehl auf den Worker-Knoten ausführen, um sie dem Cluster hinzuzufügen.

sudo kubeadm join <control-plane-ip>:<control-plane-port> --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>

Dieser Befehl verbindet den Worker-Knoten mit dem Control Plane.

💡 Die Daten für diesen Befehl kannst du auf der Control Plane auslesen

kubeadm token create --print-join-command

Starten und Aktivieren des Kubelet-Dienstes auf dem Worker

sudo systemctl enable --now kubelet

Sobald die zusätzliche Node bei kubectl get nodes auftaucht können die korrekten Label gesetzt werden.

kubectl label node <node-name> node-role.kubernetes.io/worker=

Überprüfung des Clusters

Um sicherzustellen, dass dein Cluster ordnungsgemäß funktioniert, kannst du den Status der Knoten überprüfen:

kubectl get nodes

Die Ausgabe sollte alle Knoten (den Control-Plane-Knoten und alle hinzugefügten Worker-Knoten) als Ready anzeigen.

Testanwendung installieren

Wenn du eine Anwendung bereitstellen möchtest, kannst du dies mit einem Deployment-Befehl tun:

kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --type=NodePort --port=80

Diese Befehle erstellen ein Deployment für eine Nginx-Webserver-Anwendung und legen einen Service vom Typ NodePort an, um die Anwendung über eine externe IP-Adresse zugänglich zu machen.

Deinstallieren der kompletten Installation

Uninstall k8s

## on control-plane
kubectl drain mynodename --force --ignore-daemonsets
kubectl delete node mynodename
## on node
sudo kubeadm reset --force
sudo systemctl stop kubelet
sudo dnf -f remove kubeadm kubectl kubelet kubernetes-cni kube*
sudo dnf -f autoremove
rm -rf ~/.kube
sudo rm -rf /var/lib/kubelet/*

Uninstall containerd

sudo systemctl disable --now containerd
sudo dnf -y remove yum-utils
sudo dnf -y remove containerd.io
sudo rm -rf /var/lib/containerd
sudo rm -rf /etc/containerd

Uninstall flannel

sudo rm -rf /var/lib/cni/
sudo rm -rf /run/flannel
sudo rm -rf /etc/cni/

Kubernetes Pods

Pods sind in Kubernetes die Umgebung in der ein oder mehrere Container gestartet werden können. Container können nicht ohne einen umgebenden Pod laufen.

Alle Container innerhalb eines Pods teilen sich die Namespaces des Pods und können nur parallel auf der gleichen Node laufen. Wir einer der Container eines Pods beendet, dann werden alle Container im gleichen Pod gestoppt.

Evicted Pods

Pods können durch Signale zu Evicted Pods werden. Die Signale können z.B. durch zu wenig Speicher, Festplattenplatz, fehlende INodes oder Prozess IDs gesetzt werden. Meist starten in dem Fall neue Pods, sobald der entsprechende Engpass behoben ist.

Die verbleibenden - Evicted - Pods können dann entfernt werden.

Eine Liste der Evicted Pods zusammen mit dem Grund wird wie folgt erstellt:

$ kubectl get pods --all-namespaces -o go-template='{{range .items}}{{if eq .status.phase "Failed"}}{{if eq .status.reason "Evicted"}}{{.metadata.name}}{{" "}}{{.metadata.namespace}}{{"\n"}}{{end}}{{end}}{{end}}' | while read epod enamespace; do kubectl -n $enamespace get pod $epod -o=custom-columns=NAME:.metadata.name,NODE:.spec.nodeName,MSG:.status.message; done 

Zum Löschen aller Evicted Pods reicht ebenfalls ein Einzeiler:

$ kubectl get pods --all-namespaces -o go-template='{{range .items}}{{if eq .status.phase "Failed"}}{{if eq .status.reason "Evicted"}}{{.metadata.name}}{{" "}}{{.metadata.namespace}}{{"\n"}}{{end}}{{end}}{{end}}' | while read epod enamespace; do kubectl -n $enamespace delete pod $epod; done

Debugging mit Ephemeral Containern

Ephemeral Container können einem Pod hinzugefügt werden um in der bestehenden Pod Umgebung debugging durchführen zu können.

$ kubectl debug $PODNAME --image=alpine --attach -ti -- sh

Nach dem Verlassen des Containers bleibt dieser im Pod erhalten und wird erst durch entfernen des Pods gelöscht.

Solange kann an den Container attached werden.

$ kubectl attach $PODNAME $CONTAINERNAME -ti

Kubernetes Service

Pods werden nicht direkt über die Pod IP angesprochen, da sich diese IP bei jedem Anlegen eines Pods ändert. Falls über das Deployment mehrere Pods angelegt werden, wird über die IP immer nur ein Pod angesprochen.

Ein Service sorgt für die Vergabe einer ClusterIP, eines NodePorts oder der Nutzung eines LoadBalancers.

Um die zusammengehörigen Pods zu finden werden über einen selector die passenden Label der Pods festgelegt. Dabei müssen die Pods und der Service im gleichen Namespace sein.

apiVersion: v1
kind: Service
metadata:
  labels:
    app: "nginx"
  name: nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: "nginx"
  type: ClusterIP

Durch den port wird der auf der ClusterIP zu belegende Port festgelegt. Der targetPort legt den vom Pod freigegebenen Port fest.

Bei der Konfiguration eines NodePort kann ein Port von 30000 bis 32767 automatisch oder durch die Angabe des Parameters nodePort festgelegt werden.

Die dynamische Vergabe des Ports bevorzugt, da es hier nicht zu Konflikten auf Grund paralleler Deployments kommen kann.

---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: "nginx"
  name: nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30000
  selector:
    app: "nginx"
  type: NodePort

Kubernetes StorageClass

Eine StorageClass wird benutzt um einen Datenspeicher für die Pods zur Verfügung zu stellen.

Es gibt verschiedene Provider über die die Speicherung durchgeführt werden kann.

Eine StorageClass als default festlegen

Um für einen Cluster einen Storage als Voreinstellung zu haben wird in der Regel die erste angelegte StorageClass als default definiert.

Wird nun Storage angefordert, dann wird diese Klasse als StorageClass benutzt.

In der StorageClass Liste erscheint die StorageClass dann mit der Kennzeichnung (default).

$ kubectl get StorageClass
NAME                 PROVISIONER          RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
longhorn (default)   driver.longhorn.io   Retain          Immediate           true                   24m
longhorn-static      driver.longhorn.io   Delete          Immediate           true                   24m

StorageClass als nicht-default setzen

Mittels eines Patches lässt sich eine StorageClass als nicht-default setzen. Ist kein default definiert und eine Resource benötigt ein Volume, dann muss eine StorageClass explizit angegeben sein.

kubectl patch StorageClass longhorn -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'

StorageClass als default setzen

Sobald es keine default StorageClass gibt, kann eine StorageClass als default festgelgt werden. Dies geschieht analog zum Entfernen der default Definition.

kubectl patch StorageClass longhorn -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

Kubernetes Tools

Für den Betrieb eines Kubernetes Clusters werden einige Tools benötigt.

dieses sind nicht immer direkt in den Repositories der Distributionen verfügbar. Das folgende Script installiert die wichtigsten Tools in /usr/local/bin und fügt den Pfad zur PATH Variable hinzu.

echo "install needed tools"
  sudo yum install -y epel-release
  sudo yum install -y vim tar git libiscsi-utils iscsi-initiator-utils nfs-utils
  sudo systemctl enable --now iscsid

echo "add /usr/local/bin to PATH"
  echo "PATH=\"/usr/local/bin:\$PATH\"" | sudo tee -a /root/.bashrc | tee -a $HOME/.bashrc

echo "installing kubectl"
  sudo curl -Lo /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
  sudo chmod +x /usr/local/bin/kubectl

echo "installing helm"
  curl -L https://get.helm.sh/helm-v3.15.3-linux-amd64.tar.gz|sudo tar xvz -C /usr/local/bin/ --strip-components=1 --wildcards '*/helm'

echo "installing flux"
  curl -s https://fluxcd.io/install.sh | sudo bash

echo "installing ranchercli"
  curl -L https://github.com/rancher/cli/releases/download/v2.8.0/rancher-linux-amd64-v2.8.0.tar.gz|sudo tar xvz -C /usr/local/bin --strip-components=2 --wildcards '*/rancher'

echo "installing cilium"
  CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
  CLI_ARCH=amd64
  curl -L --fail https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz | sudo tar xzvC /usr/local/bin

echo "installing hubble"
  HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
  HUBBLE_ARCH=amd64
  curl -L --fail https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-${HUBBLE_ARCH}.tar.gz | sudo tar xzvC /usr/local/bin

echo "installing kubectl krew plugin"
	curl -L https://github.com/kubernetes-sigs/krew/releases/latest/download/krew-linux_amd64.tar.gz | sudo tar -xzv -C /usr/local/bin ./krew-linux_amd64
	sudo chmod +x /usr/local/bin/krew-linux_amd64 
	sudo /usr/local/bin/krew-linux_amd64 install krew
	/usr/local/bin/krew-linux_amd64 install krew

	sudo grep KREW_ROOT /root/.bash_profile -q || echo 'export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"' | sudo tee -a /root/.bash_profile
	grep KREW_ROOT ${HOME}/.bash_profile -q || echo 'export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"' | tee -a ${HOME}/.bash_profile

echo "installing krew plugins"
	for I in neat cert-manager cilium change-ns df-pv sick-pods; do
		sudo /usr/local/bin/kubectl krew install $I
		/usr/local/bin/kubectl krew install $I
	done

Kubernetes Troubleshooting

RKE2

RKE2 startet nicht “cluster-cidr: [x.x.x.x/16 201:db8::/56] and node-ip: [y.y.y.y], must share the same IP version (IPv4, IPv6 or dual-stack)”

RKE2 hat die IPv6 der Node nicht erkannt. Durch eine zusätzliche Konfigurationsdatei in /etc/rancher/rke2/config.yaml.d/ kann die bestehende Konfiguration ergänzt werden:

# node-ip.yaml
{
  "node-ip": "y.y.y.y,201:db8:1::1/643"
}

Cluster meldet “Waiting fpr probes: kube-controller-manager, kube-scheduler”

Die Meldung deutet darauf hin, dass es ein Problem mit den Zertifikaten gibt und dieses erneuter werden müssen.

Das folgende Script zeigt ob die Zertifikate aktuell sind:

$ curl  --cacert /var/lib/rancher/rke2/server/tls/kube-controller-manager/kube-controller-manager.crt \
  https://127.0.0.1:10257/healthz >/dev/null 2>&1 \
  && echo "[OK] Kube Controller probe" \
  || echo "[FAIL] Kube Controller probe"

$ curl --cacert /var/lib/rancher/rke2/server/tls/kube-scheduler/kube-scheduler.crt \
  https://127.0.0.1:10259/healthz >/dev/null 2>&1  \
  && echo "[OK] Scheduler probe" \
  || echo "[FAIL] Scheduler probe"

Falls bei diesem Test ein Fehler ausgegeben wird, können mit dem folgenden Script die Zertifikate gelöscht und erneuert werden:

export CRI_CONFIG_FILE=/var/lib/rancher/rke2/agent/etc/crictl.yaml
export CONTAINERD_ADDRESS=unix:///run/k3s/containerd/containerd.sock
export PATH=$PATH:/var/lib/rancher/rke2/bin

echo "Rotating kube-controller-manager certificate"
rm -f /var/lib/rancher/rke2/server/tls/kube-controller-manager/kube-controller-manager.{crt,key}
crictl rm -f $(crictl ps -q --name kube-controller-manager)

echo "Rotating kube-scheduler certificate"
rm -f /var/lib/rancher/rke2/server/tls/kube-scheduler/kube-scheduler.{crt,key}
crictl rm -f $(crictl ps -q --name kube-scheduler)

Longhorn Storage unter Kubernetes

Das SUSE Longhorn Projekt stellt eine Open-Source-Lösung für verteilte Block-Speicher für Kubernetes bereit.

Was ist Longhorn?

Longhorn ist eine kostenlose und Open-Source-Software-Defined-Storage-Lösung, die für Kubernetes entwickelt wurde. Sie bietet persistente Speichervolumes für zustandsbehaftete Anwendungen, die auf Kubernetes laufen.

Hauptmerkmale von Longhorn:

  • Einfache Installation und Verwaltung: Longhorn lässt sich einfach in einem Kubernetes-Cluster installieren und über die Kubernetes-API oder eine benutzerfreundliche Weboberfläche verwalten.
  • Hochverfügbarkeit: Longhorn repliziert Speicherdaten über mehrere Knoten hinweg, um Ausfallsicherheit zu gewährleisten. Wenn ein Knoten ausfällt, sind die Daten weiterhin verfügbar.
  • Snapshots und Backups: Es unterstützt das Erstellen von Snapshots von Volumes und das Sichern dieser Snapshots an externen Speicherorten (z. B. S3-kompatible Speicher, NFS).
  • Dynamische Volume-Bereitstellung: Volumes können dynamisch erstellt und an Pods angehängt werden, wenn diese gestartet werden.
  • Plattformübergreifend: Longhorn funktioniert auf verschiedenen Cloud-Providern und On-Premises-Umgebungen.
  • Block-Speicher: Longhorn stellt Block-Speicher bereit, der für eine Vielzahl von Anwendungen geeignet ist, einschließlich Datenbanken und anderer zustandsbehafteter Dienste.

Anwendungsfälle

Longhorn eignet sich hervorragend für Szenarien, in denen Sie:

  • Zustandsbehaftete Anwendungen auf Kubernetes betreiben, die persistenten Speicher benötigen.
  • Eine einfache und kostengünstige Speicherlösung für Ihren Kubernetes-Cluster suchen.
  • Hochverfügbarkeit und Datensicherheit für Ihre Speicherdaten benötigen.
  • Flexibilität bei der Verwaltung und Sicherung Ihrer Speicherdaten wünschen.

Installation

Die Installation von Longhorn erfolgt typischerweise über die Bereitstellung von YAML-Manifesten in Ihrem Kubernetes-Cluster. Sie können die neuesten Installationsanweisungen und Manifeste auf der offiziellen Longhorn-Dokumentation finden.

Installation Longhorn unter Rocky Linux

Vorbereitung

sudo dnf install -y epel-release
sudo dnf install -y vim tar git libiscsi-utils iscsi-initiator-utils nfs-utils iptables
sudo systemctl enable --now iscsid
sudo tee /etc/sysctl.d/20-inotify.conf <<HERE
fs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=524288
HERE
sudo sysctl --system

Installation per helm

helm repo add longhorn https://charts.longhorn.io
helm repo update
helm upgrade --install longhorn longhorn/longhorn \
 --namespace longhorn-system --create-namespace \
 --set service.ui.type=NodePort \
 --set persistence.defaultClassReplicaCount=1 \
 --set csi.attacherReplicaCount=1 \
 --set csi.provisionerReplicaCount=1 \
 --set csi.resizerReplicaCount=1 \
 --set csi.snapshotterReplicaCount=1 \
 --set defaultSettings.defaultReplicaCount=1 \
 --set longhornUI.replicas=1

Uninstall Longhorn

https://github.com/longhorn/longhorn/discussions/9167

Default storageClass anpassen

Durch das Setzen einer Annotation kann eine storageClass zur default storageClass ungestellt werden.

Dabei sollte zunächst geschaut werden ob eine storageClass als default deklariert ist und diese deaktiviert werden.

Danach kann eine andere storageClass zur default storageClass gemacht werden.

aktuelle default storageClass deaktivieren

Auflisten der storageClass Elemente um eine default storageClass zu finden

kubectl get storageClass
kubectl patch storageclass $STORAGECLASS -p \
  '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'

neue default storageClass aktivieren

kubectl patch storageclass $STORAGECLASS -p \
  '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

Manage PostgreSQL in Kubernetes

Verbindung zu einer PostgreSQL-Datenbank in Kubernetes herstellen

Der sinnvollste Weg um an eine PostgreSQL-Datenbank in einer Kubernetes-Cluster Umgebung zu verbinden ist die Verwendung eines passenden PostgreSQL Containers.

Dieser lässt sich über den den Befehl kubectl run erzeugen.

Dazu sollte im Vorfeld der namespace des PostgreSQL Servers sowie dessen Service-Name und Service-Port bekannt sein.

user@linux $ kubectl get svc -n postgresql
NAMESPACE      NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
postgresqlns   mypostgresql   ClusterIP   89.207.132.170  <none>        5432/TCP  13m

In dem Beispiel sind die folgenden Parameter vergeben:

  • postgresqlns ist der Namespace
  • mypostgresql ist der Name des Services
  • 5432 ist der Service-Port

Der Container für den Zugriff wird nun mit dem Befehl kubectl run erzeugt und direkt eine Verbindung zum PostgreSQL Server hergestellt.

user@linux $ kubectl run -it postgres --image=postgres --rm=true \
             --namespace=postgresqlns -- \
             psql postgresql://username:password@mypostgresql:5432/database

Nach Ende der Verbindung wird der Container gelöscht.

NGINX Ingress auf Kubernetes

Voraussetzungen

  • Ein Kubernetes Cluster ist konfiguriert und läuft
  • kubectl ist auf einer Control Plane installiert

Installation helm

helm kann über die Webseite https://get.helm.sh heruntergeladen und installiert werden.

Die folgende Kommandozeile führt dies automatisch durch. Die Versionsnummer sollte entsprechend angepasst sein.

 dnf install -y tar
 curl -L https://get.helm.sh/helm-v3.19.0-linux-amd64.tar.gz| \
      sudo tar xvz -C /usr/local/bin/ --strip-components=1 --wildcards '*/helm'

Installation ingress-nginx

Einrichtung des repositories

Über das Repository stehen die verschiedenen Charts für die Installation zur Verfügung und können auch darüber upgedated werden.

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

Erstellen eines Namespaces

Die Installation erfolgt in einem eigenen Namespace. Dieser kann vorab oder beim helm install über den Parameter --create-namespace während der Installation angelegt werden.

kubectl create namespace ingress-nginx

Installation des ingress-nginx

helm install nginx-ingress ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --set controller.service.type=NodePort \
  --set controller.kind=DaemonSet --set controller.hostNetwork=true

Prüfen des der Installation

Test des DaemonSet

kubectl get daemonset --namespace ingress-nginx

Test Installation eines nginx

Im folgenden wird ein nginx Webserver installiert und mit einem Ingress versehen.

kubectl create deployment nginx --image=docker.io/nginx:alpine
kubectl expose deployment nginx --type=ClusterIP --port=80
kubectl apply -f - <<MANIFEST
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
  annotations: {}
spec:
  ingressClassName: nginx
  rules:
  - host: <node dns name>
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx
            port:
              number: 80
MANIFEST

Ingress mit Basic Authentication versehen

Durch die Basic Authentication wird sichergestellt, dass nur Benutzer mit entsprechenden Berechtigungen Zugriff auf die Website bekommen.

htpasswd Datei erzeugen

podman run --rm -ti docker.io/library/httpd htpasswd -bn \
  username passwort >htpasswd

htpasswd in Secret ablegen

kubectl create secret generic basic-auth --from-file=auth=htpasswd

Im Ingress müssen nun nur noch die folgenden Annotations hinterlegt werden:

# type of authentication
nginx.ingress.kubernetes.io/auth-type: basic
# name of the secret that contains the user/password definitions
nginx.ingress.kubernetes.io/auth-secret: basic-auth
# message to display with an appropriate context why the authentication is required
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required'

OCI Registry einrichten

Um eigene Images zu verwalten und zu pushen, wird ein OCI Registry benötigt.

Im folgenden Beispiel wird die Installation über yaml Dateien durchgeführt, die direkt über kubectl importiert werden. Dazu werden die im folgenden aufgeführten Beispiele in Dateien gepackt und über denn Befehl kubectl apply -f $DATEINAME importiert.

Namespace

Zunächst wird ein Namespace erstellt in dem die Registry läuft. Für das Beispiel ist das der Namespace registry.

apiVersion: v1
kind: Namespace
metadata:
  name: registry

Storage

Damit die Daten der Registry auch nach einem Neustart erhalten bleiben, wird ein Storae benötigt. Über die default Storage Klasse wird dafür ein PersistentVolumeClaim erstellt. Dieser erstellt dann ein PersistentVolume sobald dieses von dem Deployment angefordert wird.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: registry
  namespace: registry
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: "5Gi"
#  storageClassName: longhorn

Wird eine andere als die default StorageClass gewünscht kann diese mit dem Parameter storageClassName angegeben werden.

Secrets

Damit die Registry nicht ohne Authentifizierung läuft muss im Vorfeld eine htpasswd Datei erstellt werden um darüber die Benutzer zu verwalten.

Zunächst wird die htpasswd Datei erstellt. Dies lässt sich am einfachsten über den httpd OCI Container durchführen. Dieser enthält das benötigte Progamm htpasswd. Im Beispiel wird podman genutzt. Der Befehl kann gegen docker getauscht werden.

user@linux $ podman run --rm --entrypoint htpasswd docker.io/httpd -Bbn \
                testuser testpassword > htpasswd

Über die Datei htpasswd wird anschließend ein Secret im Namespace registry erstellt.

user@linux $ kubectl create secret generic registry-secret --dry-run=client \
                -o yaml --type opaque --from-file htpasswd -n registry
apiVersion: v1
data:
  htpasswd: dGVzdHVzZXI6JDJ5JDA1JGVSNXliR3hoU0gxSmVtcEIxR0dZMnVwUFVJb1lFQnFTSXhaVVlEYUpYRld6SVV0UTVReDFDCgo=
kind: Secret
metadata:
  creationTimestamp: null
  name: registry-secret
  namespace: registry
type: opaque

Deployment

Über das Deployment wird die Definition des Registry Pods durchgeführt. Dieser wird dann über ein automatisch erstelltes ReplicaSet gestartet.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: registry
  namespace: registry
spec:
  replicas: 1
  selector:
    matchLabels:
      app: registry
      release: registry
  template:
    metadata:
      labels:
        app: registry
        release: registry
    spec:
      containers:
        env:
        - name: REGISTRY_STORAGE_DELETE_ENABLED
          value: "true"
        - name: REGISTRY_AUTH
          value: htpasswd
        - name: REGISTRY_AUTH_HTPASSWD_REALM
          value: Registry Realm
        - name: REGISTRY_AUTH_HTPASSWD_PATH
          value: /auth/htpasswd
        image: registry:2
        imagePullPolicy: ifNotPresent
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /
            port: 5000
            scheme: HTTP
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        name: registry
        ports:
        - containerPort: 5000
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /
            port: 5000
            scheme: HTTP
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        volumeMounts:
        - mountPath: /auth
          name: auth
          readOnly: true
        - mountPath: /var/lib/registry/
          name: data
      restartPolicy: Always
      securityContext:
        fsGroup: 1000
        runAsUser: 1000
      terminationGracePeriodSeconds: 30
      volumes:
      - name: auth
        secret:
          defaultMode: 420
          items:
          - key: htpasswd
            path: htpasswd
          secretName: registry-secret
      - name: data
        persistentVolumeClaim:
          claimName: registry

Service

Nach dem Einlesen des Deployments existiert der Pod registry im Namespace registry, es existiert aber noch kein Zugriff von Außen auf den Container. Dazu wird ein Service erstellt.

apiVersion: v1
kind: Service
metadata:
  name: registry
  namespace: registry
spec:
  ports:
  - name: http-5000
    port: 5000
    protocol: TCP
    targetPort: 5000
  selector:
    app: registry
    release: registry
  type: ClusterIP

Der Service ist in der Lage den passenden Pod auf Grund des selector zu finden und diesem eine ClusterIP zu geben. Dies ClusterIP mit dem Port 5000 ist dann eine Möglichkeit auf den Dienst zuzugreifen.

Allerdings nur auf einer der Cluster Nodes. Um auch von Außen auf den Dienst zugreifen zu können macht es Sinn einen Ingress zu erstellen über den dann der Zugriff erfolgt.

Ingress

Für den Ingress wird eine DNS Domain benötigt, die auf den Cluster zeigt. Für das Beispiel ist es registry.internal. Um auch per https auf den Dienst zugreifen zu können muss ein TLS Zertifikat vorhanden sein. Diese kann wie Folgt erstellt werden:

user@linux $ openssl req -x509 -newkey rsa:4096 -keyout tls.key -nodes \
                -out tls.crt -days 365 -subj '/CN=registry.internal'

Statt des selbstzertifizierten Zertifikats kann auch ein Let’s Encrypt Zertifikat über cert-manager oder ein bestehendes Zertifikat verwendet werden.

Aus dem Zertifikat wird nun wiederum ein secret erstellt.

user@linux $ kubectl create secret tls registry-tls -n registry \
                --key=tls.key --cert=tls.crt --dry-run=client -o yaml
apiVersion: v1
data:
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZHVENDQXdHZ0F3SUJBZ0lVZC9kV1Q2TEloLzBlR2JJZXNqclA1WEQyQ0hBd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0hERWFNQmdHQTFVRUF3d1JjbVZuYVhOMGNua3VhVzUwWlhKdVlXd3dIaGNOTWpVd05ERTNNVEV4TmpBMApXaGNOTWpZd05ERTNNVEV4TmpBMFdqQWNNUm93R0FZRFZRUUREQkZ5WldkcGMzUnllUzVwYm5SbGNtNWhiRENDCkFpSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnSVBBRENDQWdvQ2dnSUJBTFJwdUYxcW5OTXdUUDNrdy9SWFdMSWUKU1dIVXJ0U3dGMjJML0RSL0N3bWRCU0RvbjIvRHJFcTdqcmYwOUkrS21SMGJlRWNNczJUTDQvSVpIT2RYQkVZTgpMbzNJTTR5c1ZoRUh1ajRDekFQYzc5eW95U20vYjFSRVpjSElKd0ltSS93UjRrSlFhODU3eTZZVVR5enhUbFdyCm8zUXdLcHEwcnhwdDgxN083SkI0THp1V1dyTklqdWpJVHh2YXNXR1ozd0NuSmNIczRzWlZpZk1KUGZhV2RpUysKd2hTZDR6U1J1VnZFWFZMalFmYkk2bGtJVTF4THIrR2VPL0c5WVZ2TmFJeVZsT0JUd1Jqa0hDT2pOa0dpR1dKVAp1b2JoTE43KzNWaFdmYjZ3enQwV3BHM0NJTjVFemJNOUxQems3aVljdTk2RVJ3dlhvREp6eGRSbUF3WUZRY2twCjEzd1VoSGdFdkZUZDlvZk9HZ3graDd3cm1Mdk1mZitCRUk3amRNUEhST3UwSVpqSGxOQTVMRzA3ai9rbE5xWmsKMXdhZHNyUjNDZjVpM0tVbmlpMTYxQ29WMitjU1YyRTIyUFBrVnNsVklKYmJvT0VXZGc0c2ZNTHd5Y3ZSVk8xcQovVjl6MEVsajBhMllSTUdXMWI4cXIwNkpHUkRvVEFKSFNzc1R1dm5icmJ6ZzJOdEl0NFd0OFZpVXRFVjdTMkpTCnN6WmhwSTRUUlN2bHRwbUlneXVMWDUzVmNWWUhxRXBFY2xZOC9tSkdmdXJzWGxhdm9NSmlaYk5MMWpLaUJkL1kKNkRxMTJnMmMvc3BXOXlYeGJyQnlIWEZraVZpc21pOG5tTFYxQTFsSHpGc3ZTdnIzZTRpVWpLV0t3RklsQ2o4Rgorak42c2FFdjFmZHFwUldERExXMUFnTUJBQUdqVXpCUk1CMEdBMVVkRGdRV0JCVElWdGo0bDdCNVczcUw1T0NOClBrWFlNWkdpcWpBZkJnTlZIU01FR0RBV2dCVElWdGo0bDdCNVczcUw1T0NOUGtYWU1aR2lxakFQQmdOVkhSTUIKQWY4RUJUQURBUUgvTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElDQVFBcUtWVjFHM2JwRHFMN04rTnpNTitGK0lIMwphUVd3ZGFRSGtuN2JXYmNBamQvUEZ1YXJPdCtMaUMvUmQ5VUhLWVA3MGdHZmQ5YTFmWjBGTlZDb3RZR2ZvYnVDCjNUV0RnanRXNy8wSi9ENXFNR3REWk1UVW1NVGVheVR5VlpsdUlxcTdGMklDYjU0R0k2WnR4aWRuNUw5bkhpQU8KRGFmQnZkMTFPQ2ViZ1VsRHBVR3FoaW50ZmJsVkY1NUhwUjdiSEpqV2U1WDAva3NyZi92a0RUMEVLYTlzZkhrUwpLTTZaNm9LTVlDUEdrL1d4dzA0YkF2dzU2U0pMUnFTU0NCL1pqQVQxazhyeW1ieENMekczN1NPeDJ4NWpueEF5CkZ5RldUcExHb2hOZmRURDlxOGxlSjJYMUh5L1gwVVgyM1Vpek5US2kyRjMxallDVXQ5QkJwbEx6ekM5bW1BeEgKbm9FMnNKS3lKbXdDdTk3VGhvOUlnSHpPMVZHV3Qyb2NPQ0lNdkxqY2ZoTUpHR2F5MFVoQ3JvYk9XWlBHS3VndAoyQU85VW9WaDVWMzloVDJVSkFVZFdDN0hsYi9RdFhLL3ZyczgybW5kZDVyUXFXajVrSjFEbXRNaldFak1DcVlvCjJPd0tSc1pqT2ZzcGhDSlpIOFR5eFoxY1V3Q0laMEsrVzFxeURSWDYwUkF4M3ZtMFBueU1YNGR4aFJsMWl6VmEKZi9jc1d3K09mSnVnVzZ6Zm1VdlV4by95WG0vSElHVExaL003OUlBY3NrbnJuM0pqWTBubGlmd0h4bFV1U0F6ZwoxN2ZIcjdGcjk3NHIzYWZLdWZnSU8rR1VkZnFsQWZhWkxmcGtqdGhqZ3FEV0VBRlNaSEhxcXJuWnZva2hhZURpClIyZUZVT0JRT1czaHhhVTdZQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
  tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRZ0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1N3d2dna29BZ0VBQW9JQ0FRQzBhYmhkYXB6VE1FejkKNU1QMFYxaXlIa2xoMUs3VXNCZHRpL3cwZndzSm5RVWc2Sjl2dzZ4S3U0NjM5UFNQaXBrZEczaEhETE5reStQeQpHUnpuVndSR0RTNk55RE9NckZZUkI3bytBc3dEM08vY3FNa3B2MjlVUkdYQnlDY0NKaVA4RWVKQ1VHdk9lOHVtCkZFOHM4VTVWcTZOME1DcWF0SzhhYmZOZXp1eVFlQzg3bGxxelNJN295RThiMnJGaG1kOEFweVhCN09MR1ZZbnoKQ1QzMmxuWWt2c0lVbmVNMGtibGJ4RjFTNDBIMnlPcFpDRk5jUzYvaG5qdnh2V0ZieldpTWxaVGdVOEVZNUJ3agpvelpCb2hsaVU3cUc0U3plL3QxWVZuMitzTTdkRnFSdHdpRGVSTTJ6UFN6ODVPNG1ITHZlaEVjTDE2QXljOFhVClpnTUdCVUhKS2RkOEZJUjRCTHhVM2ZhSHpob01mb2U4SzVpN3pIMy9nUkNPNDNURHgwVHJ0Q0dZeDVUUU9TeHQKTzQvNUpUYW1aTmNHbmJLMGR3bitZdHlsSjRvdGV0UXFGZHZuRWxkaE50ano1RmJKVlNDVzI2RGhGbllPTEh6Qwo4TW5MMFZUdGF2MWZjOUJKWTlHdG1FVEJsdFcvS3E5T2lSa1E2RXdDUjByTEU3cjUyNjI4NE5qYlNMZUZyZkZZCmxMUkZlMHRpVXJNMllhU09FMFVyNWJhWmlJTXJpMStkMVhGV0I2aEtSSEpXUFA1aVJuN3E3RjVXcjZEQ1ltV3oKUzlZeW9nWGYyT2c2dGRvTm5QN0tWdmNsOFc2d2NoMXhaSWxZckpvdko1aTFkUU5aUjh4YkwwcjY5M3VJbEl5bAppc0JTSlFvL0Jmb3plckdoTDlYM2FxVVZnd3kxdFFJREFRQUJBb0lDQUNPVTlFRXNYVGpEdm5PV3NsVVhBdzNDCkxtdm1sQUtycGpzN1VDY1VaVnRraGhYcSswTUNQeEZRTTRJeGhDaHBSL0IzTWY0bFVaNVFIaWxwN1lyczNSRnAKMlNRcWQ0eEhrd1B4MTdnU09pV0s1aDNKaGo4L0c5aHRVdnBvbk5UdUs4dXp4VUdaOWVFNEJqNi9vNnZ6L2FTawo0T3h4OFgwb3BKNWNVQk1EVWFIZXFrWWd6Y3V0UkU1QjcyUkplaEdMVlBHZWhiSmRJNDdGWVJzM1YrcXgyeVE5CklTOEt3cnhqLzBCTGxySlowdkUyeUExK09GNnNLTWloT2laZjJQdFBwRTZSMDlaNGZrMkg4OHJOams1Y2g1QWUKZmx2S3ZseE92TjRHMU84T2xRWUN5TFJEQk5PQmsyWkY2d0VIcjdhQmxVWDZKYjcrcklBMjJDNGZYbnBLQUNLMApTY0Jjd3d6dzBmQTF4a3BaTk1zSVVpLzhwLzJvUjlyY0loaUNRSTd0WFlueERRSENWZ3dVNGJITjJhakdHMUJICitZTmZsK2t4QWk1WUJja0p0bHVjQXJoWWdHUFd0b1FmWnFJU3IrNFVkWEFTZ3VBOGp5aVdHVVlBWnFuUlVrWEMKYjBseTIyR0x5dWRoN2ZJSnFmQXR3Sm5ON25icmpDZy90dHFOc1FMZWgxWGpVYUNYbExvMmVTVmFlZHNZa0dJNwpKMXVwckJiWVlqQURDa1hLaDhHYnB0cEZBZmpRekU2TjEyRTd1dVF1eGdrRXRBZnp6QlIrWTh3ejhpWkFPR043ClpWcUYwTWlRV21UWmRhM3RVSnM5QnpKMDBPZDhNbzZsVHFYTlFxOXU0TkxoOThzTTRsc0dhU0lLYmFTS09RQ2MKTkpKdXc3M3R2V0ltZlNQNm5GbVRBb0lCQVFEOFVmSTlwTU52R2picjQ3Y00vZElYRk44NWFQZ2VDd2RXRXh2KwpWUG9VeVd2THp0a3BtdWRUdWZXY25aVzVZSTZRZUVtcmk0YjNkbUMySHgvS0tTL0c0a0tvdFZncFJhd3RjSUpNCnh4ck5YdTdZSjhadkg2bG84NUxHbmlQVDR2NDRNR2dHNjFBRVREVEorakUrSStwVmpnazdEKzNXQnd5RkRSbXAKaWVoSSs2THU0VDI1U3ZEYjdaZW1OT1RXbjY1N25BOURXUzUrRUtQY00yOFJid3VTdEJiMEZNTWptUFhJM25NcwpyeDBkd2lNc0wzNStOdU9qRk14VEpocnQ5RHFESUJBUGdtdUNKTUNuVHlwakdKR01DbzJZVXpZdko3U0Z0TjMvClBCd0dQb050cjFkUW4ydnpVeHluZVBTeDZydDZYUlhOeFQyTU1tUmg2dndoTUNtdkFvSUJBUUMzQzAzTU90NFgKRTVnQkxPZzc4L2ZWb2txNURCa0tBdGlPM3lJWnFHS1BXUHRnQ2x5dUpzZ012SVI3M3hoVkJ6YTJCWkMvUkNyMApkQWRRbnZXYXRhb1YyN3YzMFBzQ0xRaFU3Q2NpdWdxRlJUN1lnV0RIQk5tbkVWeHExOWYxd2pTcHlIdVl1bUkyCk5SczZCME90RHRleld3TndvWkRCZGN1VnR5cXFReDdWRFErcHRKZ290dnhtbWNRcTN4WnhQczNuWmhVazJ1VlUKL241SnhzNWFLOHpNZHRoUWpucGdOYzVFQmhEa1pjK1dyTmM1WjBtNUsvMnh5dlFsdXF6R1AzWnJYUlFFcm1VYwpOWkQrRVJxdVVuY3BJS3pMOU8yVHFKZHoySGlQOWptSUJKOTdjVUVrNHRQcTJZYnFlWHY1SGxRczk5TGtXTFFQCm9rVHp4RUthWkFQYkFvSUJBQk5yYXkxN1pOOUVNVDN1aE1Rbk5PZzUzd1JZSStDTUVNQjdNQWhmR2ZCSG9GaEwKVDBONGZKMWhEcHBETnFiUjI2Y2EydkN0Q0hJN1ZpNHVMeEFzSTFVM1ROSVhRdlhLT3ZvMFVwTCtMLzNtZEpPRwpYQ3RwVUd5WGNwQiswMXNYZVdGeHVFL3dCSFNRT08vaXBhK1dyQTV4cEJ3N21aNkRaRjlKSnRSNW5Mc2hRdVVICnlPeGQ3RFBCRVk3bE8wZVplcUJnM252Y2ZVdU9sRTM2VjI0TUVlUDBvRitneVhRUDdick1CQ2xJUVFGdFU2K3UKNkJsbXVCWnhIMWkyOGhPbVhHcURLVGxJWEdYajFrQUtROFRrdXk4QUVPM01XYnMvWHhGN1hpcXF3YTFwV2t3eQoxcEdKODVFQ3NJM2pMVklVTXVHMEVGMko5TGE1bWlnL0liQ2NPRmtDZ2dFQWNyalgrcitPZ0tJY0sraHNhVjhBCkh0cEh3UER2SkpJaURuR1ZHc1dwZURTSjRHaStLN3hNdDRiVDloc2VVZlJpZXZURUFzeURxNUNwSVFOdjVaWWsKVXV6VVEzNnNRM0hiL2ZYQWxZaEtuYnFIcUFSMmxtWFkwRmdXTnpTdXV1NE1PYmxZbGFHRGVud1FZMjg0SUZmawpJeHBRN3Ara2JVd1oyK3pnQmJScHQ1Z25EL01MUnNGaE1pRC9qT3NqVXJqTkxsR1J3UGFjczRlSnVVMnIrV0hvClN1SFZzNitJelpJTGhDRFZKNEVUK1ZpOHo5S0hwMUlHQUErMnM0cnJUSmxyNitCN2NtK25HV1lOU04rSmZHWmQKSHQ3R3psSGxNUjJDUnU0T3FoUGxmUFdBeTBweFRPQUpkL0Q4R1NzbzR6cjZBUEVXTVYzVEJOVnNKK2dDNG1iNgo3d0tDQVFFQXoxWEdMV1pILytyblozdmVkVzh2U0U0cks0N3RXQ0dwQlJsM0xSTTBia1ZlaGJQN3JLRHg4a3MwCmtqQUtXei8rb3Vud1BqSGR0T2dBZmhVcUR6cS85Q2IzOFBOQ1Q3emM5dmlnc2FXc3ZuU05aSWNjakhFV2hpT1QKcjg1L3h6ZG82WkorOE1WQzl0Wll5YjRudmNuWkhVV2dKSmljYm1PMTAxcnJxVG1uUi8xcFZwVnF2R3FwNG5lRgprbm5mS1oydnhwZUlsQjIyQ20rNGlLNzdLekgveW1oc2xGcE5nR3FNS0F3RFlqM0NHK21hc0RPMXVXUUhSWjZWCmxQYjB0WHNOVVNYLysyb1d5U2VYWlBTbnhMd1JvUDZPdFFGTjdSR0MyekN6b05tdndnV3l5RVVrNHFtVW9vOVYKSTBrSFBkNkhFVXFkcTF3SVJ5WjBrWWFMakNJRkNBPT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=
kind: Secret
metadata:
  creationTimestamp: null
  name: registry-tls
  namespace: registry
type: kubernetes.io/tls

Dieses secret wird in dem folgenden Ingress verwendet.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    # cert-manager.io/cluster-issuer: acme-production
    # kubernetes.io/tls-acme: "true"
    ingress.kubernetes.io/proxy-body-size: 1024m
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-body-size: 1024m
  name: registry
  namespace: registry
spec:
  rules:
  - host: registry.internal
    http:
      paths:
      - backend:
          service:
            name: registry
            port:
              number: 5000
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - registry.internal
    secretName: registry-tls

Die beiden auskommentierten Einträge sind die Verwendung von cert-manager und einem ClusterIssuer gedacht. Der ClusterIssuer muss dafür zunächst angelegt sein und die Domain von extern durch den Zertifizierungsdienst - z. B. Let’s Encrypt

  • erreichbar sein.

Nach dem Erstellen des Ingress ist ist die Registry über https://registry.internal erreichbar.

Rancher Installation

Rancher ist eine von SUSE entwickelte Verwaltungsplattform für Kubernetes.

Für die Installation wird eine bereits vorhandene Kubernetes Installation und das Tools Helm benötigt.

Bei der Installation über Helm werden durch das Tool die benötigten Steuerdateien für den Cluster aus Templates erstellt und eingespielt.

Zunächst wird der cert-manager für die Zertifikatsverwaltung benötigt.

# Add Cluster Resource Definitions (CRD) for cert-manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.15.1/cert-manager.crds.yaml
# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io
# Update your local Helm chart repository cache
helm repo update
# Install the cert-manager Helm chart
helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.15.1
# Create namespace for rancher
kubectl create namespace cattle-system
# Add rancher helm repository
helm repo add rancher-latest https://releases.rancher.com/server-charts/latest
# Update your local Helm chart repository cache
helm repo update

Nun folgt die eigentlich Rancher Installation

helm install rancher rancher-latest/rancher --namespace cattle-system \
                                            --set hostname=$(hostname --fqdn) \
                                            --set replicas=1 \
                                            --set global.cattle.psp.enabled=false

Während der Installation wird die URL für das Webinterface sowie der Zugang zum Anmeldetoken angezeigt.

RKE2 Installation

Die Rancher Kubernetes Engine 2 ermöglicht die Installation und Konfiguration eines Kubernetes Clusters durch wenige Befehle.

Im folgenden wird die Installation unter einer aktuellen Version von Rocky Linux 9 vorgestellt.

Da das System selber Firewall - Regeln setzt sollte eine zusätzliche Firewall Konfiguration deaktiviert sein.

In der Regel läuft auf einem neu installierten Rocky Linux der firewalld.

sudo systemctl disable --now firewalld.service
sudo systemctl mask firewalld.service

Der Hostname der Node sollte korrekt gesetzt sein, oder durch hostnamectl gesetzt werden.

sudo hostnamectl set-hostname node150.train.mylinuxtime.de

Die folgenden Tools werden von RKE2 je nach konfigurierter Umgebung benutzt und sollten installiert sein.

sudo yum install -y epel-release
sudo yum install -y vim tar git libiscsi-utils iscsi-initiator-utils nfs-utils iptables
sudo systemctl enable --now iscsid

Da das RKE2 einige der in einer Standard - Installation vorherrschenden Limit überschreitet müssen für inotify die Limits erhöht werden.

sudo tee /etc/sysctl.d/20-inotify.conf <<HERE
fs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=524288
HERE
sudo sysctl --system

Die Installation der RKE2 Umgebung kann durch ein Script oder die Installation des Binaries von GitHub durchgeführt werden.

Das Script installiert neben dem Binary auch einige nützliche Scripte über die alle Prozesse des RKE2 gestoppt oder eine Installation entfernt werden kann.

Bei unterstützten Plattformen - wie Rocky Linux - wird zusätzlich ein Repository für den Paketmanager installiert über den ein direktes Update per yum update möglich ist.

curl -sfL https://get.rke2.io | sudo sh -

Zusätzlich wird das Kommando kubectl für die Verwaltung des Clusters benötigt. Der folgende Aufruf installiert den Befehl direkt nach /usr/local/bin. Um direkt per bash verfügbar zu sein wird auch die Variable PATH erweitert.

sudo curl -Lo /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
sudo chmod +x /usr/local/bin/kubectl
echo "PATH=\"/usr/local/bin:\$PATH\"" | sudo tee -a /root/.bashrc | tee -a $HOME/.bashrc
source $HOME/.bashrc

Falls dem RKE2 keine weiteren Parameter angegeben werden sollen, kann der Dienst aktiviert und gestartet werden.

sudo systemctl enable --now rke2-server.service

Die für den kubectl benötigte Konfiguration kann direkt unter .kube/config im Homeverzeichnis des aktuellen Benutzers und dem von root zur Verfügung gestellt werden.

sudo mkdir /root/.kube
sudo ln -s /etc/rancher/rke2/rke2.yaml /root/.kube/config
mkdir $HOME/.kube
sudo cp /etc/rancher/rke2/rke2.yaml $HOME/.kube/config
sudo chown $USER: $HOME/.kube/config
chmod 0600 $HOME/.kube/config

Nach wenigen Minuten sollte die neue RKE2 Node zur Verfügung stehen.

user@linux $ kubectl get nodes
NAME                           STATUS   ROLES                       AGE     VERSION
node150.train.mylinuxtime.de   Ready    control-plane,etcd,master   2m29s   v1.28.11+rke2r1

Mikrosoft Windows

HowTos rund um das Thema Mikrosoft Windows

Unterabschnitte von Mikrosoft Windows

Lokaler Benutzer unter Mikrosoft Windows 11

Bei der Installation von Mikrosof Windows 11 versuche der Installer immer einen Online Zugang über das Mikrosoft Netzwerk zu benutzen.

Soll stattdessen nur ein lokaler Benutzer eingerichtet werden, ist es möglich während der Installation über die Tastenkombination STRG+F10 eine Eingabeaufforderung zu starten und dort über das folgende Kommando:

oobe\bypassNRO.cmd

Es wird der Rechner sofort neu gestartet und der Einrichtungsdialog wird normal durchlaufen ohne allerdings einen Online-Zugang zu benötigen.

Dazu beim Dialog ‘Wie möchten Sie dieses Gerät einrichten’ den Punkt ‘Für persönliche Verwendung einrichten’ und danach ‘Anmelden’ auswählen.

Nun nochmals die Eingabeaufforderung mittels STRG+F10 öffnen und den Befehl ipconfig /release zum unterbrechen der Internetverbindung eingeben.

Die Eingabeaufforderung schließen und den Punkt Erstellen Sie eines auswählen.

Über den folgenden Dialog wird dann ein lokales Konto eingerichtet.

OpenSSL

OpenSSL

Selfsigned Certificate

$ openssl req -x509 -newkey rsa:4096 -keyout server.key -nodes -out server.crt -days 365 -subj '/CN=doma.in'

Create CA and sign cert

$ openssl genrsa -out ca.key 4096
$ openssl req -new -x509 -key ca.key -out ca.crt -subj "/CN=ca"
$ openssl genrsa -out test.key 4096
$ openssl req -new -key test.key -out test.csr -subj "/CN=test.site"
$ openssl x509 -req -in test.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out test.crt

Create Certificate Request with SAN

Works with openssl >= 1.1.1

$ openssl req -new -nodes -sha256 -subj "/CN=host.name" \
      -addext "subjectAltName = DNS:host.name,DNS:another.host.name,IP:10.10.10.10" \
      -newkey rsa:4096 -keyout server.key -out server.csr

Create Self-Signed Certificate Request with SAN

Works with openssl >= 1.1.1

$ openssl req -x509 -nodes -sha256 -subj "/CN=host.name" \
      -addext "subjectAltName = DNS:host.name,DNS:another.host.name,IP:10.10.10.10" \
      -newkey rsa:4096 -keyout server.key -out server.crt

CA Zertifikate zum Trust hinzufügen

Rocky Linux 9

Um eine CA als Trust dem Betriebssystem hinzuzufügen wird die CA - Datei im Verzeichnis /etc/pki/ca-trusted/source/anchors hinzugefügt und über den Befehl update-ca-trust eingebunden.

Beispiel für die MyLinuxTime CA:

cat >/etc/pki/ca-trusted/source/anchors/mylinuxtime-ca.pem <<HERE
-----BEGIN CERTIFICATE-----
MIIF8jCCA9qgAwIBAgIJAMG9bSijnHU1MA0GCSqGSIb3DQEBCwUAMIGFMQswCQYD
VQQGEwJERTEMMAoGA1UECAwDTlJXMRMwEQYDVQQHDApPYmVyaGF1c2VuMRQwEgYD
VQQKDAtNeUxpbnV4VGltZTEcMBoGA1UEAwwTTXlMaW51eFRpbWUgUm9vdCBDQTEf
MB0GCSqGSIb3DQEJARYQQ2hyaXN0aUBuLUhlcy5zZTAeFw0xODA1MTUxMTU4Mzha
Fw0zODA1MTAxMTU4MzhaMIGFMQswCQYDVQQGEwJERTEMMAoGA1UECAwDTlJXMRMw
EQYDVQQHDApPYmVyaGF1c2VuMRQwEgYDVQQKDAtNeUxpbnV4VGltZTEcMBoGA1UE
AwwTTXlMaW51eFRpbWUgUm9vdCBDQTEfMB0GCSqGSIb3DQEJARYQQ2hyaXN0aUBu
LUhlcy5zZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOzna6+tq2N7
+tXv/eTYIj+YZYsCbMiR4xHhNq6kICGt3LRxCXWLkZtLaxrBPuaKfhQGkUJtU0Hl
NwSgUQrd1pboLVkY9JgbO0d+j4HtkGzkCBh2Xan+EdDGmT8x6UGdg+9EFM3UHECv
ZHrdn6tcI3UBoFBtF5dDvMJECQFXaL4vNFrf/EkFbrlW6uuB7Z2w1YLB+zpBSLYm
C/GtnHddpXALff0k+4LnF/dX2Skws6BRf0nL1mJ86A2p5EI7zx9RxrcYKrdWk7jw
M/4HD5VzWS7WWu4tteGidCciIBA6mg+bOgNOirdT3PnYae6s2Ddjb1SDLklpU+/D
uPOO0CqwTtOyZ+3GznuU2N3jTcHbE0Bmjh+uCI7aB24RsGj9Zi+1mMCSz0zOKHEF
1CLn9mMwweGK3lzq+tlKWp6T7NI4I+Yv/4ZB92LvZW2PYr2jwgBdI2ymYdpequRe
hr4tBM871JRxlgnJYgS1xV8B789bR6Me/5vfl2L4eurbBSs6wEZZl2JIOTaW6aNv
1UvU9ns/4WOfSHHnd/yeZIDg0tS3jsgFOHG1cQzPc7U4p9IaKr45WHG9tE8mkkBp
v1TcagCR2yaLKK3u9Y/MdyxrjL7tPMkdLIBpSFAN1hROAw3bD8xpVNRhoP6gf8XW
Ot3ZbtpaaQ5GD3vqpn7Ra7dtDSV0DkTPAgMBAAGjYzBhMB0GA1UdDgQWBBTk0M1X
BsrGqkbCOIgAQm1TkhXhuTAfBgNVHSMEGDAWgBTk0M1XBsrGqkbCOIgAQm1TkhXh
uTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
AAOCAgEA6dypeAws8Bzs9hWS5Zk4iKcLPsbuKKAIBfqrtWG1QJybL9OFhwoAHk6D
xC31O5hP/KdiUaqSEAAXoy/V0K+iNHt+JSeYfKVRA/ZMqh02GR30kuyJnNemZS2m
Qel7/PfZBUbcHXun4ZJWYm+lXkQ/weDDRJ46TaYXGXDCk0nOuP17IUPZxWs5oT7d
axwx4oJo/rcbp0kURKEDnyK55AIbwD0l5cpSGaNvzl3ShuJrWm8EILKkcF8uUNmB
yKfGwXpdnc58vqEjc9PosBAveWUnT3ASwzEAcXZKyOn+afhIbCM4mzYzzppRIVE8
wz437rGvpAOZyH9kKvc/HAGaaYV1vKyQIH44ypwtFtsPou6GbhUDkuwd3Kjna1h/
d2ngscT7/ytgEUNB9lCCd2wLo6bGbYnblLToRhstW8eNbzMqEdfEwaYMRSJq6QV0
eiuNrU/r+92SibgmCG7UO3bWo4FILYYDaANB8VIMKzKgnFdDm0BjGgmzmBOCoIfV
RBQATFTe3K/do8VwE0pQ2SPs+o8d1hRQRmUonVdHpyPHU0TvmOi/9qJIpsLnhz85
wq6kQtvnL2Xp8f8HB5xFLoO9pUiCLYOhpc5rI1PkI9rR2etMu51ROC5VojE6jzRf
vG7ggGVksq0Mp7AH2MMVMnTsJufPzIDLTRp0OIiJ+bHS7RiPwUo=
-----END CERTIFICATE-----
HERE
update-ca-trust

Prometheus

HowTos rund um das Thema Prometheus

Unterabschnitte von Prometheus

Node Exporter mit TLS und Basic Auth

Create a self-signed cert for node-exporter:

$ openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout node_exporter.key -out node_exporter.crt -subj “/C=ZA/ST=CT/L=SA/O=VPN/CN=localhost” -addext “subjectAltName = DNS:localhost” Move the certs into the directory we created:

$ mv node_exporter.* /etc/node-exporter/ Install htpasswd so that we can generate a password hash with bcrypt, which will prompt you for a password that we are setting for the prometheus user::

$ apt install apache2-utils $ htpasswd -nBC 10 "" | tr -d ‘:\n’; echo Now populate the config for node-exporter:

$ cat /etc/node-exporter/config.yml tls_server_config: cert_file: node_exporter.crt key_file: node_exporter.key basic_auth_users: prometheus: Change the ownership of the node exporter directory:

$ chown -R ${NODE_EXPORTER_USER}:${NODE_EXPORTER_USER} /etc/node-exporter Then create the systemd unit file:

$ cat > /etc/systemd/system/node_exporter.service « EOF [Unit] Description=Node Exporter Wants=network-online.target After=network-online.target StartLimitIntervalSec=500 StartLimitBurst=5 [Service] User=${NODE_EXPORTER_USER} Group=${NODE_EXPORTER_USER} Type=simple Restart=on-failure RestartSec=5s ExecStart=${BIN_DIRECTORY}/node_exporter –web.config=/etc/node-exporter/config.yml [Install] WantedBy=multi-user.target EOF Reload systemd and start node-exporter

$ systemctl daemon-reload $ systemctl enable node_exporter $ systemctl restart node_exporter Prometheus Config Copy the /etc/node-exporter/node_exporter.crt from the node-exporter node to prometheus-node, then in the /etc/prometheus/prometheus.yml config:

 scrape_configs:

  • job_name: ’node-exporter-tls’ scheme: https basic_auth: username: prometheus password: tls_config: ca_file: node_exporter.crt insecure_skip_verify: true static_configs:
    • targets: [’node-exporter-ip:9100’] labels: instance: friendly-instance-name

Queries mit PromQL

PromQL wird benutzt um aus einer Prometheus Time-Series-Database (TSD) Daten abzufragen.

Die Daten liegen als Metriken vor, die bei jedem erneuten Abruf (scrape) von den Exportern aktualisiert werden. Dabei wird den Metriken jeweils der aktuelle Timestamp hinzugefügt. Über PromQL lässt sich daher sehr einfach die Veränderung der Metriken über die Zeit nachverfolgen.

Zusätzlich zu den Metriken selber werden Attribute (label) gesetzt um die Metrik genauer zu beschreiben.

> node_cpu_seconds_total
node_cpu_seconds_total{cpu="0", instance="server:9100", job="node", mode="idle"}	 206390.26
node_cpu_seconds_total{cpu="0", instance="server:9100", job="node", mode="iowait"}	 23.46
node_cpu_seconds_total{cpu="0", instance="server:9100", job="node", mode="irq"}	 0
node_cpu_seconds_total{cpu="0", instance="server:9100", job="node", mode="nice"}	 6.45
node_cpu_seconds_total{cpu="0", instance="server:9100", job="node", mode="softirq"} 13.44
node_cpu_seconds_total{cpu="0", instance="server:9100", job="node", mode="steal"}	 0
node_cpu_seconds_total{cpu="0", instance="server:9100", job="node", mode="system"}	 804.72
node_cpu_seconds_total{cpu="0", instance="server:9100", job="node", mode="user"}	 1166.13
node_cpu_seconds_total{cpu="1", instance="server:9100", job="node", mode="idle"}	 206535.23
node_cpu_seconds_total{cpu="1", instance="server:9100", job="node", mode="iowait"}	 15.85
node_cpu_seconds_total{cpu="1", instance="server:9100", job="node", mode="irq"}	 0
node_cpu_seconds_total{cpu="1", instance="server:9100", job="node", mode="nice"}	 16.85
node_cpu_seconds_total{cpu="1", instance="server:9100", job="node", mode="softirq"} 11.24
node_cpu_seconds_total{cpu="1", instance="server:9100", job="node", mode="steal"}	 0
node_cpu_seconds_total{cpu="1", instance="server:9100", job="node", mode="system"}	 828
node_cpu_seconds_total{cpu="1", instance="server:9100", job="node", mode="user"}	 1201.18

Wie bei der Ausgabe ersichtlich sind für node_cpu_seconds_total 3 Attribute gesetzt: instance, job und mode

Um auf ein oder mehrer Attribute zu filtern werden sie hinter der Metrik in geschweiften Klammern angegeben.

> node_cpu_seconds_total{mode="user"}
node_cpu_seconds_total{cpu="0", instance="server:9100", job="node", mode="user"}	 1166.13
node_cpu_seconds_total{cpu="1", instance="server:9100", job="node", mode="user"}	 1201.18

Hierdurch wird die Ausgabe eingeschränkt.

Um die Anzahl der CPU Kerne herauszufinden könnte reicht die Zählung der Ergebniszeilen (count).

> count (node_cpu_seconds_total)

Allerdings nur solange es nicht mehrere Instanzen gibt. Bei einer zusätzlichen Instanz mit gleicher Anzahl an CPUs würde nicht 2 pro Instanz ausgegeben, sondern nur 4, da die Zeilen zusammengezählt werden.

Das richtige Ergebnis erhält man nur, wenn man den count nur auf die gleiche Instanz anwendet, also eine Gruppierung auf die Instanz vornimmt.

> count by (instanc) (node_cpu_seconds_total)
node_cpu_seconds_total{instance="server:9100"} 2

Sollte der Wert häufiger benötigt werden, machen eine ‘recording rule’ Sinn. Hierbei werden - parallel zum Abholen neuer Metriken durch den Prometheus Server - auch auf Grund der recording rules eigene Metriken berechnet. Dies verhindert, dass oft genutzte Werte immer wieder innerhalb der PromQL Queries erneut berechnet werden.

groups:
  - name: cpu
    rules:
      - record: instance:node_cpu_seconds_total:count
        expr: count by (instance) (node_cpu_seconds_total{mode="user"})

Diese recording rule definiert eine neue Metrik instance:node_cpu_seconds_total:count, die genau der im vorherigen Bespiel erzeugten Abfrage entspricht.

Um diese recording rule zu aktivieren wird sie in eine yaml Datei gepackt und über rules_file in der Prometheus Konfiguration geladen.

Mit dieser Metrik ist es dann recht enfach die Load der Instanzen (node_load1), die die Summer der Loads aller Scheduler darstellt, durch die Anzahl der Scheduler (= Anzahl der CPU Kerne) zu teilen und einen vergleichbaren Wert für alle Instanzen zu haben. Wir müssen dann nicht mehr wissen wieviele CPU Kerne die Instanzen haben, da alles relativ zu einem CPU Kern dargestellt wird.

Eigentlich ist es nur die Division der Load (node_load1) durch unsere Metrik (instance:node_cpu_seconds_total:count).

Allerdings haben die beiden Werte nicht alle Attribute gleich. node_load1 hat noch das zusätzliche Attribut job. Dieses gibt es bei instance:node_cpu_seconds_total:count nicht.

Daher ist bei der Division dieses Attribut zu ignorieren oder die Division nur auf das Attribut intance anzuwenden.

> node_load1 / ignoring(mode) instance:node_cpu_seconds_total:count
> node_load1 / on(instance) instance:node_cpu_seconds_total:count

ZigBee

HowTos rund um das Thema ZigBee

Unterabschnitte von ZigBee

Praktischer Einstieg in Zigbee2MQTT

Der im Rahmen des Themenabends durchgeführte Kurs erklärt die ersten Schritte der Einrichtung und Konfiguration einer ZigBee basierten Homeautomation. Dabei geht es nicht darum bestehende - kommerzielle - Gateways zur Verwaltung von ZigBee Geräten einzusetzen, sondern auf einem RaspberryPi oder ähnlichen Rechnern unter Linux ein Gateway zusammen mit einem CC2531 Netzwerksniffer aufzubauen.

Vorbereitung

Für den Kurs wird keine explizite Hard- oder Software benötigt und die Teilnehmer können dem Vortrag und den Vorführungen folgen. Es ist aber möglich selber die einzelnen Schritte nachzuvollziehen und ebenfalls über den eigenen Rechner ein ZigBee Netz aufzubauen.

Dafür wird folgendes benötigt:

  • 1 x CC2531 z. B. von Amazon
  • 1 x ein ZigBee Gerät z. B. Osram Smart+ Plug von Amazon
  • 1 x CC2531 Debugger und Adapterkabel

Auf den Debugger für den CC2531 und das Adapterkabel kann verzichtet werden, wenn ein CC2531 mit vorinstallierter Coordinator - Software für zigbee2mqtt gekauft wird. Damit fällt dann auch der Installationsschritt zum flashen des CC2531 weg.

Viele der aktuell erhältichen Coordinator sind auch schon mit einer Firmware ausgestattet, die es erlaubt per Software - ohne den Debugger - die Geräte direkt zu flashen.

Neben dem CC2531 können auch andere Coordinator eingesetzt werden. Diese sind in der Liste der unterstützten Coordinatoren aufgelistet.

Zusammen mit einem Laptop mit Linux oder einem RaspberryPi mit Raspberry Pi OS können alle Schritte nachvollzogen werden. Beim Paketmanager gehen wir von einem Debian-basierenden Grundsystem aus.

Eine Liste weiterer Endgeräte, die mit dem Gateway in der aktuellen Version genutzt werden können ist auf der Projektseite auf GitHub zu finden.

Aufspielen der Firmware auf den CC2531

Um auf den CC2531 die geeignete Firmware aufzuspielen wird ein CC Debugger von Texas Instruments sowie ein Adapter benötigt. Diese sind über einige Plattformen wie z. B. Amazon bestellbar (Debugger, Adapter).

Zusätzlich wird die entsprechende Firmware und das Tool zum Flashen benötigt.

Firmware

Die für uns interessante Firmware wird auf der GitHub Seite des Gateways zur Verfügung gestellt und dort auch immer wieder aktualisiert. Dabei gibt es dort 2 verschiedene Varianten des Firmware:

  • Coordinator
  • Router

Die Coordinator Firmware ist für die Verwaltung der Geräte zuständig und ist quasi der Netzverwalter. Alleine kann ein Coordinator mit einem CC2531 in der aktuellen Firmware - Version bis zu 30 Geräte direkt verwalten. Andere Chips können durchaus mehr verwalten.

sudo apt install -y tmux unzip
wget https://github.com/Koenkk/Z-Stack-firmware/raw/master/coordinator/Z-Stack_3.0.x/bin/CC2531_20190425.zip
unzip CC2531_DEFAULT_20190425.zip

Um mehr Geräte nutzen zu können werden Router benötigt. Diese können entweder durch CC2531 mit Router Firmware oder durch Dauerstrom Geräte wie z. B. dem Osram Smart+ Plug zur Verfügung gestellt werden. Dabei ist es abhängig von den Geräten selber wie viele Geräte sie wiederum verwalten können.

Flashen

Zum Flashen unter Linux wird cc-tool benötigt. Dieses lässt sich problemlos per git herunterladen und lokal compilieren und nutzen:

sudo apt install -y git autoconf libtool libboost-dev libboost-all-dev libusb-1.0-0-dev
git clone https://github.com/dashesy/cc-tool.git
cd cc-tool
./bootstrap
./configure
make

Danach müssen sowohl der CC Debugger als auch der damit verbundene CC2531 an den Rechner angeschlossen werden. Die LED am CC Debugger wird dann zunächst rot leuchten. Nach einem Druck auf die Reset - Taste sollte sie dann grün leuchten.

Sollte der CC Debugger weiterhin rot leuchten, sicherheitshalber richtige Polung der Anschlüsse überprüfen!

Damit ist der CC Debugger im Programmiermodus und die Firmware kann aufgespielt werden:

$ sudo ./cc-tool -e -w ../CC2531ZNP-with-SBL.hex
Programmer: CC Debugger
Target: CC2531
Erasing flash...
Completed 
Writing flash (241 KB)...
Completed (17.95 s.)

Damit ist die Vorbereitung des CC2531 abgeschlossen und der kann benutzt werden.

Herunterladen und Einrichten des Gateways

Als Gateway nutzen wir zigbee2mqtt von Koenkk. Das Gateway ist komplett auf GitHub zu finden.

Herunterladen

Um auch einfach wieder Updates durchführen zu können sollte das Gateway per Git heruntergeladen und verwaltet werden:

sudo git clone https://github.com/Koenkk/zigbee2mqtt.git /opt/zigbee2mqtt
sudo chown -R pi: /opt/zigbee2mqtt

Wir gehen dabei davon aus, dass die Installation auf einem RaspberryPi stattfindet und der Benutzer der Benutzer pi ist. Ansonsten bitte anpassen.

Um die Software laufen zu lassen muss die Umgebung zur Verfügung gestellt werden. Dies ist Node.js mit npm.

sudo curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs git make g++ gcc

Mit den folgenden Aufrufen sollte die Version getestet werden:

node --version  # Should output v10.X oder v12.X
npm --version  # Should output 6.X

Danach können dann die Module installiert und konfiguriert werden:

cd /opt/zigbee2mqtt
npm ci

Damit ist das Gateway vorbereitet, kann aber noch nicht gestartet werden, da noch kein Mosquitto MQTT Server zur Verfügung steht um die Daten des Gateways zu speichern.

Einrichtung des Mosquitto MQTT Servers

Der Mosquitto MQTT Server ist für die Zwischenspeicherung der Nachrichten, die vom ZigBee - Netzwerk kommen und dahin gehen sollen zuständig. Er funktioniert wie ein Telegramm - Dienst. Das Gateway kann z. B. den Zustand eines Schalters an eine Adresse (topic) des MQTT senden. Clients wie z. B. NodeRed oder OpenHAB2 etc. können nun an dieser Adresse lauschen (subscribe) und bekommen dadurch jede Änderung der Daten an dieser Adresse mit. Dadurch können sie sofort auf die Änderung reagieren von von sich aus auf eine Adresse eine Naschricht schicken oder Aktionen auslösen.

Für die Grundinstallation ohne jegliche Absicherung des Dienstes reicht die folgende Zeile:

sudo apt-get install mosquitto mosquitto-clients -y

Vor dem Produktiveinsatz des Dienstes sollte die Kommunikation zwischen MQTT server und weiteren externen Geräten abgesichert werden.

Anpassen der Konfiguration, erster Start

Der Datei /opt/zigbee2mqtt/data/configuration.yaml liegt die Konfiguration für das Gateway. Falls der MQTT nicht auf dem lokalen Rechner läuft oder es eine Anpassung für den Benutzernamen und das Kennwort gibt, können diese hier eingetragen werden:

# MQTT settings
mqtt:
  # MQTT base topic for zigbee2mqtt MQTT messages
  base_topic: zigbee2mqtt
  # MQTT server URL
  server: 'mqtt://localhost'
  # MQTT server authentication, uncomment if required:
  # user: my_user
  # password: my_password

Es handelt sich hierbei um eine yaml - Datei. Bei diesem Dateityp ist es wichtig die Einrückungen (Leerschritte) beizubehalten.

Zusätzlich zu dem Einrichten des mqtt - Gateways sollte dem ZigBee - Netzwerk eine eigene Netzwerkadresse gegeben werden. Diese sollte möglichst nicht manuell sondern automatisch generiert werden um sie einmalig zu halten.

Ein zusätzlicher Eintrag in die Konfigurationsdatei mit dem folgenden Inhalt sorgt für die automatische Generierung einer Netzwerkadresse:

advanced:
    network_key: GENERATE

Nun steht dem ersten Start nicht mehr im Weg:

cd /opt/zigbee2mqtt
npm start

Der Server sollte nun melden, dass er an den MQTT Server verbunden ist.

Geräte, die in den Verbindungsmodus geschaltet wurden sollten nun auch sichtbar werden.

Die Meldungen, die nun vom an den MQTT Server geschickt und dort gespeichert werden lassen sich über den folgenden Befehl abrufen:

mosquitto_sub -t \#

Anpassen des friendly_name Jedes Gerät hat seine eindeutige ID, aber die ID kann sich keiner merken. Daher kann in der Datei /opt/zigbee2mqtt/data/configuration.yaml der friendly_name angepasst werden um einen sprechenden Namen für die Geräte zu bekommen.

Dazu zunächst den Server stoppen (mit STRG+C oder systemctl stop zigbee2mqtt) und die Datei dann editieren.

Beispiel:

devices:
  '0xacb01baa0a0031b0':
    friendly_name: 'smartplug01'

Nach dem Neustart des Dienstest nutzt der Server dann statt der bisherigen ID den friendly_name.

Weitere Konfigurationsparameter sind auf der Website zu finden: https://www.zigbee2mqtt.io/information/configuration.html

Geräte per MQTT direkt steuern

Nachdem unsere Steckdose einen sprechenden Namen hat, können wir an das Gerät Nachrichten schicken (geht natürlich auch mit den IDs, ist aber mühsamer):

mosquitto_pub -t zigbee2mqtt/smartplug01/set -m '{ "state": "OFF" }'
mosquitto_pub -t zigbee2mqtt/smartplug01/set -m '{ "state": "ON" }'

Der Nachrichteninhalt ist dabei ein JSON - Element, dass das Gateway auswertet und entsprechende Kommandos verschickt. Um ein Kommando zu senden wir an den Topic, der über mosquitto_sub ausgelesen wurde ein /set angehangen und die Nachricht dorthin geschickt.

Das Gateway selber informiert auch über Vorgänge und lässt sich per MQTT steuern: https://www.zigbee2mqtt.io/information/mqtt_topics_and_message_structure.html

Für einige Geräte sind sogar OTA-Updates verfügbar:

zigbee2mqtt/bridge/log {"type":"ota_update","message":"Update of '0x7cb03eaa00ab1390' at 2.17%,
 +- 24 minutes remaining","meta":{"status":"update_progress","device":"0x7cb03eaa00ab1390",
"progress":2.17}}
zigbee2mqtt/bridge/log {"type":"ota_update","message":"Update of '0x7cb03eaa00ab1390' at 100%",
"meta":{"status":"update_progress","device":"0x7cb03eaa00ab1390","progress":100}}
zigbee2mqtt/bridge/log {"type":"ota_update","message":"Finished update of '0x7cb03eaa00ab1390',
from '{\"softwareBuildID\":\"V1.04.93\",\"dateCode\":\"20140331CNWT****\"}' to
'{\"softwareBuildID\":\"V1.05.10\",\"dateCode\":\"20140331CNWT****\"}'",
"meta":{"status":"update_succeeded","device":"0x7cb03eaa00ab1390",
"from":{"softwareBuildID":"V1.04.93","dateCode":"20140331CNWT****"},
"to":{"softwareBuildID":"V1.05.10","dateCode":"20140331CNWT****"}}}

Automatischer Start des Dienstes

Es ist sinnvoll den Dienst so einzurichen, dass er beim Systemstart automatisch startet und nicht immer auf der Konsole per Hand aufgerufen werden muss.

Dazu kann die Datei /etc/systemd/system/zigbee2mqtt.service mit folgendem Inhalt angelegt werden:

[Unit]
Description=zigbee2mqtt
After=network.target
[Service]
ExecStart=/usr/bin/npm start
WorkingDirectory=/opt/zigbee2mqtt
StandardOutput=inherit
StandardError=inherit
Restart=always
User=pi
[Install]
WantedBy=multi-user.target

Nach dem Anlegen kann er Dienst direkt für den Systemstart eingebunden und gestartet werden:

sudo systemctl enable --now zigbee2mqtt.service

Ausgaben des Dienstes können jetzt über journalctl ausgelesen werden:

sudo journalctl --unit zigbee2mqtt.service

Installation und Einrichtung von Home Assistant

Ein schneller Weg um eine Oberfläche für die ZigBee Geräte zu bekommen ist die Software Home Assistant. Diese Software unterstützt eine Vielzahl an IoT - Geräten unter anderem auch ZigBee über mqtt.

Die Installation unter Debian:

apt install -y libffi-dev
useradd -m homeassistant -G dialout
mkdir /opt/homeassistant /home/homeassistant/.homeassistant
chown -R homeassistant: /opt/homeassistant /home/homeassistant
sudo -u homeassistant -i
cd /opt/homeassistant
python3.7 -m venv .
source bin/activate
pip3 install wheel homeassistant colorlog
exit
echo >/etc/systemd/system/home-assistant@.service <<HERE
[Unit]
Description=Home Assistant
After=network-online.target
[Service]
Type=simple
User=%i
WorkingDirectory=/home/%i/.homeassistant
ExecStart=/opt/homeassistant/bin/hass -c "/home/%i/.homeassistant"
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
HERE
systemctl enable --now home-assistant@homeassistant.service

Nach der Installation kann über Port 8123 des Rechners auf den Home Assistant zugegriffen werden.

Im Home Assistant muss dann unter den Integrationen das MQTT - Modul aktiviert und auf den Server localhost konfiguriert werden. Damit die Integration vollständig ist, muss in der zigbee2mqtt configuration.yaml die Homeasssistant Integration aktiviert sein.

Unterabschnitte von Linux Professional Institute

Prüfungsvorbereitung auf den LPI DevOps Tools Engineer V1.0

Die Prüfungsvorbereitung enthält alle Objectives des Linux Professional Institute für die Prüfungen LPI-700 für die Prüfungsversion V1.0.

Exam 700

Topic 701 Software Engineering

701.1 Modern Software Development (weight: 6)

701.2 Standard Components and Platforms for Software (weight: 2)

701.3 Source Code Management (weight: 5)

701.4 Continuous Integration and Continuous Delivery (weight: 5)

Topic 702 Container Management

702.1 Container Usage (weight: 7)

702.2 Container Deployment and Orchestration (weight: 5)

702.3 Container Infrastructure (weight: 4)

Topic 703 Machine Deployment

703.1 Virtual Machine Deployment (weight: 4)

703.2 Cloud Deployment (weight: 2)

703.3 System Image Creation (weight: 2)

Topic 704 Configuration Management

704.1 Ansible (weight: 8)

704.2 Other Configuration Management Tools (weight: 2)

Topic 705 Service Operations

705.1 IT Operations and Monitoring (weight: 4)

705.2 Log Management and Analysis (weight: 4)

Prüfungsvorbereitung auf den LPIC-1

Die Prüfungsvorbereitung enthält alle Objectives des Linux Professional Institute für die Prüfungen LPI-101 und LPI-102.

Exam 101

Topic 101: System Architecture

101.1 Determine and configure hardware settings

101.2 Boot the system

101.3 Change runlevels / boot targets and shutdown or reboot system

Topic 102: Linux Installation and Package Management

102.1 Design hard disk layout

102.2 Install a boot manager

102.3 Manage shared libraries

102.4 Use Debian package management

102.5 Use RPM and YUM package management

102.6 Linux as a virtualization guest

Topic 103: GNU and Unix Commands

103.1 Work on the command line

103.2 Process text streams using filters

103.3 Perform basic file management

103.4 Use streams, pipes and redirects

103.5 Create, monitor and kill processes

103.6 Modify process execution priorities

103.7 Search text files using regular expressions

103.8 Basic file editing

Topic 104: Devices, Linux Filesystems, Filesystem Hierarchy Standard

104.1 Create partitions and filesystems

104.2 Maintain the integrity of filesystems

104.3 Control mounting and unmounting of filesystems

104.4 Removed

104.5 Manage file permissions and ownership

104.7 Find system files and place files in the correct location

Exam 102

Topic 105: Shells and Shell Scripting

105.1 Customize and use the shell environment

105.2 Customize or write simple scripts

Topic 106: User Interfaces and Desktops

106.1 Install and configure X11

106.2 Graphical Desktops

106.3 Accessibility

Topic 107: Administrative Tasks

107.2 Automate system administration tasks by scheduling jobs

107.3 Localisation and internationalisation

Topic 108: Essential System Services

108.1 Maintain system time

108.2 System logging

108.3 Mail Transfer Agent (MTA) basics

108.4 Manage printers and printing

Topic 109: Networking Fundamentals

109.1 Fundamentals of internet protocols

109.2 Persistent network configuration

109.3 Basic network troubleshooting

109.4 Configure client side DNS

Topic 110: Security

110.1 Perform security administration tasks

110.2 Setup host security

110.3 Securing data with encryption

Prüfungsvorbereitung auf den LPIC-2

Die Prüfungsvorbereitung enthält alle Objectives Linux Professional Institute für die Prüfungen LPI-201 und LPI-202.

Exam 201

Topic 200: Capacity Planning

200.1 Measure and Troubleshoot Resource Usage (weight: 6)

200.2 Predict Future Resource Needs (weight: 2)

Topic 201: Linux Kernel

201.1 Kernel components (weight: 2)

201.2 Compiling a Linux kernel (weight: 3)

201.3 Kernel runtime management and troubleshooting (weight: 4)

Topic 202: System Startup

202.1 Customizing system startup (weight: 3)

202.2 System recovery (weight: 4)

202.3 Alternate Bootloaders (weight: 2)

Topic 203: Filesystem and Devices

203.1 Operating the Linux filesystem (weight: 4)

203.2 Maintaining a Linux filesystem (weight: 3)

203.3 Creating and configuring filesystem options (weight: 2)

Topic 204: Advanced Storage Device Administration

204.1 Configuring RAID (weight: 3)

204.2 Adjusting Storage Device Access (weight: 2)

204.3 Logical Volume Manager (weight: 3)

Topic 205: Networking Configuration

205.1 Basic networking configuration (weight: 3)

205.2 Advanced Network Configuration (weight: 4)

205.3 Troubleshooting network issues (weight: 4)

Topic 206: System Maintenance

206.1 Make and install programs from source (weight: 2)

206.2 Backup operations (weight: 3)

Exam 202

Topic 207: Domain Name Server

207.1 Basic DNS server configuration (weight: 3)

207.2 Create and maintain DNS zones (weight: 3)

207.3 Securing a DNS server (weight: 2)

Topic 208: HTTP Services

208.1 Basic Apache configuration (weight: 4)

208.2 Apache configuration for HTTPS (weight: 3)

208.3 Implementing Squid as a caching proxy (weight: 2)

208.4 Implementing Nginx as a web server and a reverse proxy (weight: 2)

Topic 209: File Sharing

209.1 Samba Server Configuration (weight: 5)

209.2 NFS Server Configuration (weight: 3)

Topic 210: Network Client Management

210.1 DHCP configuration (weight: 2)

210.2 PAM authentication (weight: 3)

210.3 LDAP client usage (weight: 2)

210.4 Configuring an OpenLDAP server (weight: 4)

Topic 211: E-Mail Services

211.1 Using e-mail servers (weight: 4)

211.2 Managing E-Mail Delivery (weight: 2)

211.3 Managing Mailbox Access (weight: 2)

Topic 212: System Security

Configuring a router (weight: 3)

212.2 Managing FTP servers (weight: 2)

212.3 Secure shell (SSH) (weight: 4)

212.4 Security tasks (weight: 3)

212.5 OpenVPN (weight: 2)