Kubernetes ist ein OpenSource System zur Automatisierung der Bereitstellung, Skalierung und Verwaltung von Container-Anwendungen wie Docker oder Containerd. Docker hat ein integriertes Tool (Docker Swarm) zum selben Zweck, Kubernetes kann jedoch das selbe und noch viel mehr und wird ab Werk von Docker unterstützt.
Kubernetes ist deklarativ, das bedeutet man beschreibt den gewünschten Endzustand.
kubectl get pods -n kube-system -o wide
# Zusätzlichen Control-Plane joinen # Das gibt dir entweder: einen kubeadm join ...-Befehl (für Worker), oder zwei Zeilen, eine davon mit --control-plane. kubeadm token create --print-join-command kubeadm init phase upload-certs --upload-certs kubeadm join <APISERVER_IP>:6443 --token <DEIN_TOKEN> --discovery-token-ca-cert-hash sha256:<DEIN_HASH> --control-plane --certificate-key <DEIN_CERT_KEY>
sudo kubeadm reset -f sudo systemctl restart containerd sudo systemctl restart kubelet sudo rm -rf ~/.kube /var/lib/kubelet /var/lib/etcd sudo rm -rf /etc/cni/net.d /var/lib/cni
On all nodes
/etc/hosts file and add all nodes./etc/fstab and uncomment SWAP partition/etc/initramfs-tools/conf.d/resume and uncomment swap uuidsystemctl –type swap and mask it using systemctl mask xxx.swap.todo: what about fw, selinux?
apt update && apt install apt-transport-https curl curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main" apt update && apt install -y kubelet kubeadm kubectl docker.io systemctl enable docker
Only on master node
kubeadm init --apiserver-advertise-address 192.168.0.100 --pod-network-cidr=172.16.0.0/16 # REMEMBER JOIN TOKEN output from last command !!!!!!!!!!!!!!!!! # when done execute as regular user mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config # enable network addon sudo kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
Only on nodes except master
kubeadm join 192.168.0.100:6443 --token zbdvek.v8fnw7hw8xcdxk03 --discovery-token-ca-cert-hash sha256:724601e4cd00fd78312f1fbf3726b688e894fc88f2c25962f478e546a59929c7
API Token vergessen/abgelaufen?
kubeadm token create --print-join-command
Test on master node
kubectl run my-httpd --image=httpd --port=80
kubectl get pods -l app=demo
kubectl scale deployment [deployment] --replicas 3
kubectl exec -it my-httpd -- /bin/bash
kubectl set image deployment demo nginx=1.9.1
kubectl patch pod my-httpd --patch '{}'
kubectl delete pod myhttpd
Finally autostart
systemctl start kubelet systemctl enable kubelet systemctl start docker systemctl start kubelet
# SWAP deaktivieren sudo swapoff -a # aus fstab auskommentieren sudo sed -ri 's/^\s*([^#].*\s+swap\s+)/# \1/' /etc/fstab
# Kernel-Module & sysctl richtig setzen echo -e "overlay\nbr_netfilter" | sudo tee /etc/modules-load.d/k8s.conf sudo modprobe overlay sudo modprobe br_netfilter # sysctl cat <<'EOF' | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sudo sysctl --system
# Basis-Tools sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl gpg # Keyring anlegen und Key importieren sudo mkdir -p -m 755 /etc/apt/keyrings curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release.key \ | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg # Repo eintragen (überschreibt ggf. vorhandene kubernetes.list) echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.34/deb/ /' \ | sudo tee /etc/apt/sources.list.d/kubernetes.list # Pakete installieren und auf Version pinnen sudo apt-get update sudo apt-get install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl
Containerd als Runtime
# containerd installieren sudo apt-get install -y containerd # Standardkonfiguration erzeugen sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml >/dev/null # systemd-cgroups aktivieren (wichtig, damit kubelet + containerd zusammenpassen) sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml # Dienst aktivieren sudo systemctl enable --now containerd
Alternativ Docker als Runtime
# Docker-Repo einbinden (offiziell) sudo apt-get remove -y docker.io docker-doc docker-compose podman-docker || true sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu noble stable" \ | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo systemctl enable --now docker # cri-dockerd (erforderlich, da Docker kein CRI spricht) # (Variante: .deb von GitHub Releases installieren – kurz gefasst) sudo apt-get install -y curl wget VER=$(curl -s https://api.github.com/repos/Mirantis/cri-dockerd/releases/latest | grep -Po '"tag_name": "\K.*?(?=")') wget https://github.com/Mirantis/cri-dockerd/releases/download/${VER}/cri-dockerd_${VER#v}.ubuntu-24.04_amd64.deb sudo apt-get install -y ./cri-dockerd_${VER#v}.ubuntu-24.04_amd64.deb sudo systemctl enable --now cri-docker sudo systemctl enable --now kubelet
# Cluster initialisieren sudo kubeadm init --apiserver-advertise-address=10.0.0.1 --pod-network-cidr=10.244.0.0/16 # Config für akt user mkdir -p $HOME/.kube sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config # echo 'export KUBECONFIG=/etc/kubernetes/admin.conf' >> ~/.bashrc # Netzwerk installieren sudo kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
# Taints checken!
# kubectl describe node ubuntu | grep -i Taints -A5
Taints: node-role.kubernetes.io/control-plane:NoSchedule
node.kubernetes.io/not-ready:NoSchedule
Unschedulable: false
Lease:
HolderIdentity: ubuntu
AcquireTime: <unset>
# kubectl taint nodes ubuntu node.kubernetes.io/not-ready:NoSchedule-
node/ubuntu untainted
# Worker Nodes joinen kubeadm join 1.2.3.4:6443 --token abcdef.0123456789abcdef --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxx # Netzwerk nochmal f. neue Nodes installieren? kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml # Fortschritt beobachten kubectl -n kube-system get pods -w | egrep -i 'calico|cni'
# Defekten Pod entfernen kubectl delete pod -n kube-system calico-node-68mw4 # error: error loading config file "/etc/kubernetes/admin.conf": open /etc/kubernetes/admin.conf: permission denied sudo kubectl get nodes --kubeconfig /etc/kubernetes/admin.conf # Falls "kubectl cluster-info dump" hängt export KUBECONFIG=/etc/kubernetes/admin.conf # 1) Is the API healthy? kubectl get --raw=/healthz ; echo # 2) See overall control-plane endpoints kubectl cluster-info # 3) Nodes status kubectl get nodes -o wide # 4) System pods (this tells you ~everything) kubectl -n kube-system get pods -o wide
manuel@ubuntu1:~$ sudo mkdir -p $HOME/.kube manuel@ubuntu1:~$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config manuel@ubuntu1:~$ sudo chown $(id -u):$(id -g) $HOME/.kube/config manuel@ubuntu1:~$ kubectl get nodes error: error loading config file "/etc/kubernetes/admin.conf": open /etc/kubernetes/admin.conf: permission denied manuel@ubuntu1:~$ echo $KUBECONFIG /etc/kubernetes/admin.conf manuel@ubuntu1:~$ unset KUBECONFIG manuel@ubuntu1:~$ kubectl get nodes NAME STATUS ROLES AGE VERSION ubuntu1 Ready control-plane 95m v1.34.2 ubuntu2 Ready <none> 83m v1.34.2 ubuntu3 Ready <none> 42m v1.34.2 manuel@ubuntu1:~$
# Falls du root bist export KUBECONFIG=/etc/kubernetes/admin.conf # optional dauerhaft machen: echo 'export KUBECONFIG=/etc/kubernetes/admin.conf' >> ~/.bashrc
kubectl cluster-info kubectl cluster-info dump kubectl get nodes kubectl get nodes -o wide kubectl get pods -A kubectl get services -A kubectl get events --sort-by=.metadata.creationTimestamp kubectl get all --all-namespaces kubeadm config print init-defaults kubeadm config print join-defaults kubectl -n kube-system get configmap kubeadm-config -o yaml kubectl -n kube-system get configmap kubeadm-config -o yaml | grep -A5 networking kubeadm token list kubeadm token create --print-join-command --v=5
# Deployment 1 (Node Port) kubectl create deployment demo-nginx --image=nginx:stable kubectl rollout status deploy/demo-nginx # Expose it as NodePort kubectl expose deployment demo-nginx --port=80 --type=NodePort kubectl get svc demo-nginx -o wide # Get the node port and test from the node itself NODEPORT=$(kubectl get svc demo-nginx -o jsonpath='{.spec.ports[0].nodePort}') curl -I http://127.0.0.1:$NODEPORT/ # Quick in-cluster DNS check kubectl run -it --rm dns-test --image=busybox:1.36 --restart=Never -- sh # inside the pod: curl http://demo-nginx.default.svc.cluster.local ## Deployment 2 (Cluster IP) kubectl create deployment hello-k8s --image=nginx:latest # skalieren kubectl scale deployment hello-k8s --replicas=3 # Expose it as Cluster IP kubectl expose deployment hello-k8s --port=80 --target-port=80 --name=hello-k8s-service --type=ClusterIP # Test DNS im Cluster kubectl run test-pod --image=nginx:latest --restart=Never --rm -it -- bash # im Pod curl http://hello-k8s-service
# Konfig anzeigen kubectl describe node <nodename> kubectl describe pod <podname> -n <namespace>
# Web Dashboard installieren kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
#!/usr/bin/env bash sudo dnf install -y curl yum-utils sudo tee /etc/yum.repos.d/kubernetes.repo <<EOF [kubernetes] name=Kubernetes baseurl=https://pkgs.k8s.io/core:/stable:/v1.34/rpm/ enabled=1 gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.34/rpm/repodata/repomd.xml.key EOF sudo dnf install -y kubelet kubeadm kubectl sudo dnf install -y dnf-plugins-core sudo dnf versionlock add kubelet kubeadm kubectl sudo systemctl enable --now kubelet sudo setenforce 0 sudo sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config sudo systemctl stop firewalld sudo systemctl disable firewalld
# containerd installieren sudo dnf install -y containerd # Standardkonfiguration erzeugen sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml >/dev/null # systemd-cgroups aktivieren (wichtig, damit kubelet + containerd zusammenpassen) sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml # Dienst aktivieren sudo systemctl enable --now containerd
# Node als "nicht verfügbar" markieren, Pods werden nicht woanders hin verschoben aber keine neuen mehr auf dem Node platziert kubectl cordon ubuntu3 # Drain markiert den Node als "nicht verfügbar" und verschiebt zusätzlich alle Pods auf andere Nodes. kubectl drain ubuntu3 --ignore-daemonsets --delete-emptydir-data # Re-Activate kubectl uncordon ubuntu3
Labels sind frei definierbare Key/Value-Paare, die Ressourcen beschreiben. Sie sind essenziell für Selektoren und Gruppierungen.
Labels setzen
Beim Erstellen eines Pods/Deployments
metadata:
labels:
app: demo
tier: backend
Für existierende Ressourcen
kubectl label pod demo app=demo
kubectl get pods --show-labels
Namespaces sind logische Bereiche innerhalb eines Kubernetes-Clusters. Sie dienen zur Trennung, Organisation und Rechteverwaltung von Ressourcen.
kubectl get namespaces kubectl create namespace frontend kubectl label namespace frontend app=demo
Selektoren nutzen Labels, um Ressourcen zu finden. Die häufigsten sind:
Der podSelector wird u. a. in NetworkPolicies oder Services verwendet und wählt Pods anhand ihrer Labels aus.
# Wählt alle Pods mit app=backend im gleichen Namespace. podSelector: matchLabels: app: backend
# Wählt alle Pods im Namespace
podSelector: {}
Ein namespaceSelector filtert Namespaces, nicht Pods.
namespaceSelector: matchLabels: app: demo
Wählt alle Namespaces mit dem Label app=demo.
Viele Kubernetes-Objekte (Deployments, ReplicaSets, Services) verwenden LabelSelector. Der Selektor verbindet z. B. ein Deployment mit den Pods, die es verwaltet.
selector: matchLabels: app: demo
apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
containers:
- image: nginx
name: nginx
imagePullPolicy: Always
kubectl create -f mypod.yml kubectl delete pod demo
Wird selten genutzt, eher für Tests/Labs. Update muss manuell vorgenommen werden (downtime).
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: demo
spec:
replicas: 2
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: Always
kubectl create -f myreplica.yml kubectl delete replicaset demo
# NodePort Service
apiVersion: v1
kind: Service
metadata:
name: demo-service
spec:
type: NodePort
selector:
app: demo
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30080 # optional, ansonsten automatisch vergeben
Deployments nutzen intern replicaset und managen das rolling update von selbst. Ein Deployment ist sozusagen ein Controller für Replikasets.
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
spec:
replicas: 2
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: Always
env:
- name: VERSION
value: "v1"
kubectl create -f mydeployment.yml kubectl delete deployment demo
Deployment: 3 Replikas, je Node max. 1 Pod (inkl. Control-Plane)
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-3 spec: replicas: 3 selector: matchLabels: app: nginx-3 template: metadata: labels: app: nginx-3 spec: # Control-Plane zulassen (entfernen, wenn nicht gewünscht) tolerations: - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule" # verhindert, dass zwei Pods auf demselben Node landen affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: nginx-3 topologyKey: "kubernetes.io/hostname" # optionale zusätzliche Streuung (seit K8s 1.19+) topologySpreadConstraints: - maxSkew: 1 topologyKey: "kubernetes.io/hostname" whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: nginx-3 containers: - name: nginx image: nginx:1.27-alpine ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-3 spec: type: NodePort # Default = ClusterIP selector: app: nginx-3 ports: - port: 80 targetPort: 80
kubectl apply -f nginx-deploy-3.yaml kubectl get pods -o wide -l app=nginx-3
Bei einem Daemonset sorgt Kubernetes dafür das ein Pod per (möglichem Node) Node läuft.
apiVersion: apps/v1 kind: DaemonSet metadata: name: ds-demo namespace: default spec: selector: matchLabels: app: ds-demo template: metadata: labels: app: ds-demo spec: containers: - name: pause-and-log image: busybox:1.36 command: ["sh","-c","echo 'Hello from node:' $(cat /etc/hostname); sleep 360000"]
Container bauen Nginx Config
# nginx-3/nginx.conf
user nginx;
worker_processes auto;
events { worker_connections 1024; }
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# Logging optional minimal
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
server {
listen 80 default_server;
server_name _;
root /usr/share/nginx/html;
index index.html;
location /healthz {
return 200 'ok';
add_header Content-Type text/plain;
}
# Beispiel: statischer SPA-Fallback
# location / {
# try_files $uri /index.html;
# }
}
}
index.html
<!-- nginx-3/html/index.html --> <!doctype html> <html lang="de"> <head> <meta charset="utf-8" /> <title>nginx-3 custom</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <style>body{font-family:system-ui,Arial,sans-serif;padding:2rem}</style> </head> <body> <h1>Hallo von deinem eigenen Nginx-Image 🎉</h1> <p>Version 1.0.0</p> </body> </html>
# nginx-3/Dockerfile FROM docker.io/library/nginx:1.27-alpine # Eigene Nginx-Konfiguration (siehe nginx.conf) COPY nginx.conf /etc/nginx/nginx.conf # Statischer Content / App-Assets COPY index.html /usr/share/nginx/html
# Alternativ podman docker create -t nginx-3 .
DaemonSet: 1 Pod auf jedem Node (inkl. Control-Plane)
apiVersion: apps/v1 kind: DaemonSet metadata: name: nginx-per-node spec: selector: matchLabels: app: nginx-per-node template: metadata: labels: app: nginx-per-node spec: # Control-Plane erlauben (sonst nur Worker) tolerations: - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule" containers: - name: nginx image: nginx:1.27-alpine ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-per-node spec: type: NodePort # Default = ClusterIP selector: app: nginx-per-node ports: - port: 80 targetPort: 80 protocol: TCP type: ClusterIP
kubectl apply -f daemonset.yaml kubectl get pods -o wide -l app=nginx-per-node
apiVersion: v1 kind: PersistentVolume metadata: name: pv0003 spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: slow mountOptions: - hard - nfsvers=4.1 nfs: path: /tmp server: 172.17.0.2
Das Volume bindet man nicht direkt in Deployments ein. Dazu legt man einen PVC (Persistent volume claim) an.
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: demo-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi storageClassName: slow
PVC in einem Deployment nutzen.
apiVersion: apps/v1 kind: Deployment metadata: name: demo spec: replicas: 1 selector: matchLabels: app: demo template: metadata: labels: app: demo spec: containers: - name: nginx image: nginx:stable volumeMounts: - name: data mountPath: /usr/share/nginx/html volumes: - name: data persistentVolumeClaim: claimName: demo-pvc
Secret
apiVersion: v1 kind: Secret metadata: name: smbcreds type: Opaque stringData: username: k8suser password: "DEIN_PASSWORT" # domain: "DEINDOMAIN" # nur wenn Domain-User
PV
apiVersion: v1 kind: PersistentVolume metadata: name: pv-smb-demo annotations: pv.kubernetes.io/provisioned-by: smb.csi.k8s.io spec: capacity: storage: 5Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: smb mountOptions: - dir_mode=0777 - file_mode=0777 - noperm - mfsymlinks - cache=strict - noserverino csi: driver: smb.csi.k8s.io # volumeHandle muss im Cluster eindeutig sein volumeHandle: WIN-SERVER.local/k8s-demo## volumeAttributes: source: //WIN-SERVER.local/k8s-demo nodeStageSecretRef: name: smbcreds namespace: default
PVC
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-smb-demo spec: accessModes: - ReadWriteMany resources: requests: storage: 5Gi storageClassName: smb volumeName: pv-smb-demo
todo
In Kubernetes gibt es vier Haupttypen von Services: ClusterIP, NodePort, LoadBalancer und ExternalName. Zusätzlich gibt es den Sonderfall Headless Service. Siehe Kubernetes Service – What It is, Types & Examples
| Service | Beschreibung | Erreichbarkeit | Einsatzbereich |
|---|---|---|---|
| Cluster IP | Standardtyp. Stellt den Service nur intern im Cluster bereit. | Nur intern (Cluster) | Kommunikation zwischen Pods |
| NodePort | Öffnet einen Port auf jedem Node, erreichbar über <NodeIP>:<NodePort>. | Extern über Node-IP | Einfacher externer Zugriff, z. B. für Tests |
| Load Balancer | Erstellt einen externen Load Balancer (Cloud-abhängig). | Extern über öffentliche IP | Produktivbetrieb mit automatischer Skalierung |
| External Name | eitet den Service auf einen externen DNS-Namen um. | Extern (DNS) | Integration externer Dienste |
| Headless Service | Kein Cluster-IP, direkter Zugriff auf einzelne Pods via DNS. | Intern (DNS) | z. B. für StatefulSets, Datenbanken |
ClusterIP-Dienste weisen jedem Dienst eine IP-Adresse zu, über die der Dienst innerhalb Ihres Clusters erreichbar ist. Dieser Diensttyp stellt den Dienst nicht extern bereit. ClusterIP ist der Standarddiensttyp, der verwendet wird, wenn keine alternative Option angegeben wird. Er ist der am häufigsten verwendete Diensttyp, da er eine einfache interne Netzwerkverbindung für Ihre Workloads ermöglicht. Zwei ClusterIP-Services können problemlos denselben Port verwenden – da kollidiert nichts.
Wichtig ist:
NodePort Dienste sind extern über eine festgelegte statische Portbindung auf jedem Node (auch Control Plane) erreichbar. Man kann auf den Dienst zugreifen, indem man sich mit dem Port auf einem beliebigen Node verbindet. NodePort Diensten wird außerdem eine Cluster-IP Adresse zugewiesen, über die sie innerhalb des Clusters erreichbar sind, genau wie ClusterIP-Dienste.
Die Verwendung von NodePort Diensten wird generell nicht empfohlen. Sie weisen funktionale Einschränkungen auf und können zu Sicherheitsproblemen führen.
Ein NodePort-Dienst wird üblicherweise verwendet, um die Nutzung einer eigenen Load-Balancing-Lösung zu ermöglichen, die den Datenverkehr von außerhalb des Clusters umleitet. NodePorts eignen sich auch für temporäre Debugging-, Entwicklungs- und Fehlerbehebungsszenarien, in denen verschiedene Konfigurationen schnell getestet werden müssen.
LoadBalancer-Dienste werden außerhalb Ihres Clusters über eine externe Load-Balancer-Ressource bereitgestellt. Dies erfordert eine Verbindung zu einem Load-Balancer-Anbieter, die typischerweise durch die Integration Ihres Clusters in Ihre Cloud-Umgebung hergestellt wird. Durch das Erstellen eines LoadBalancer-Dienstes wird automatisch eine neue Load-Balancer-Infrastrukturkomponente in Ihrem Cloud-Konto bereitgestellt. Diese Funktionalität wird automatisch konfiguriert, wenn Sie einen verwalteten Kubernetes-Dienst wie Amazon EKS oder Google GKE verwenden.
Sobald Sie einen LoadBalancer-Dienst erstellt haben, können Sie Ihre öffentlichen DNS-Einträge auf die IP-Adresse des bereitgestellten Load Balancers verweisen. Dadurch wird der Datenverkehr an Ihren Kubernetes-Dienst weitergeleitet. LoadBalancer sind daher der Diensttyp, den Sie normalerweise verwenden sollten, wenn eine Anwendung außerhalb von Kubernetes erreichbar sein muss.
ExternalName-Dienste ermöglichen den komfortablen Zugriff auf externe Ressourcen innerhalb Ihres Kubernetes-Clusters. Im Gegensatz zu anderen Diensttypen leiten sie den Datenverkehr nicht an Ihre Pods weiter.
Beim Erstellen eines ExternalName-Dienstes müssen Sie das Manifestfeld spec.externalName auf die gewünschte externe Adresse (z. B. example.com) setzen. Kubernetes fügt dann einen CNAME-DNS-Eintrag zu Ihrem Cluster hinzu, der die interne Adresse des Dienstes (z. B. my-external-service.app-namespace.svc.cluster.local) in die externe Adresse (example.com) auflöst. Dadurch können Sie die externe Adresse später problemlos ändern, ohne die darauf zugreifenden Workloads neu konfigurieren zu müssen.
Headless-Services sind ein spezieller Service-Typ, der weder Load Balancing noch eine Cluster-IP-Adresse bereitstellt. Sie sind „headless“, da Kubernetes keinen Traffic automatisch über sie leitet. Dadurch können Sie DNS-Abfragen verwenden, um die einzelnen IP-Adressen der vom Service ausgewählten Pods zu ermitteln.
Ein Headless-Service ist nützlich, wenn Sie mit anderen Service-Discovery-Systemen interagieren möchten, ohne dass kube-proxy stört. Sie können einen solchen Service erstellen, indem Sie das Feld spec.clusterIP eines Services explizit auf den Wert None setzen.
Kubernetes selbst macht kein Networking, sondern ruft über CNI-Plugins andere Programme auf, die die Netzwerke zwischen Pods herstellen (IP-Zuweisung, Routing, DNS usw.).
Falls die Registry mal wieder kaputt ist, auf den Node verbinden wo sie läuft und das Image löschen. Damit wird sie gezwungen das Image neu zu pullen.
# Auf Node sudo crictl images | grep registry sudo crictl rmi registry:2 # Auf dem Control Plane kubectl delete pod -l app=registry
Für eine kleine Testumgebung ist hostPath oft am einfachsten. Beispiel: Daten liegen dauerhaft auf dem Node unter /opt/registry/data.
Registry (hostPath)
apiVersion: apps/v1 kind: Deployment metadata: name: registry spec: replicas: 1 selector: matchLabels: app: registry template: metadata: labels: app: registry spec: containers: - name: registry image: registry:2 ports: - containerPort: 5000 volumeMounts: - name: registry-data mountPath: /var/lib/registry volumes: - name: registry-data hostPath: path: /opt/registry/data type: DirectoryOrCreate --- apiVersion: v1 kind: Service metadata: name: registry spec: selector: app: registry ports: - name: http port: 5000 targetPort: 5000 --- apiVersion: v1 kind: Service metadata: name: registry-nodeport spec: type: NodePort selector: app: registry ports: - name: http port: 5000 nodePort: 30050 targetPort: 5000
Auf allen Nodes in
/etc/containerd/config.toml
suche nach
[plugins."io.containerd.grpc.v1.cri".registry]
So soll es aussehen
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = ""
[plugins."io.containerd.grpc.v1.cri".registry.auths]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.1.2:30050".tls]
insecure_skip_verify = true
[plugins."io.containerd.grpc.v1.cri".registry.headers]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.1.2:30050"]
endpoint = ["http://192.168.1.2:30050"]
Danach
sudo systemctl restart containerd # Test ctr -n k8s.io images pull --plain-http 192.168.1.2:30050/demo-php:v1
Dockerfile
FROM php:8.2-cli # Arbeitsverzeichnis anlegen WORKDIR /app # index.php ins Image kopieren COPY index.php /app/index.php # PHP Built-in Webserver starten CMD ["php", "-S", "0.0.0.0:80", "-t", "/app"]
index.php
<?php $podIp = gethostbyname(gethostname()); echo "Pod IP: $podIp\n";
Deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: demo spec: replicas: 2 selector: matchLabels: app: demo template: metadata: labels: app: demo spec: containers: - name: demo image: 192.168.1.2:30050/demo-php:v1 imagePullPolicy: Always ports: - containerPort: 80
Service.yaml
apiVersion: v1 kind: Service metadata: name: demo-service spec: type: NodePort selector: app: demo ports: - port: 80 nodePort: 30080
buildah ist bereits installiert (mit kube…)
# Image erstellen buildah bud -t 192.168.1.2:30050/demo-php:v1 . # In die Registry pushen buildah push --tls-verify=false 192.168.1.2:30050/demo-php:v1 # Registry API curl -k http://192.168.1.2:30050/v2/_catalog {"repositories":["demo-php"]} curl -k http://192.168.1.2:30050/v2/demo-php/tags/list {"name":"demo-php","tags":["v1"]}
PVC anlegen
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: registry-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi
Registry (PVC)
apiVersion: apps/v1 kind: Deployment metadata: name: registry spec: replicas: 1 selector: matchLabels: app: registry template: metadata: labels: app: registry spec: containers: - name: registry image: registry:2 ports: - containerPort: 5000 volumeMounts: - name: registry-storage mountPath: /var/lib/registry volumes: - name: registry-storage persistentVolumeClaim: claimName: registry-pvc --- apiVersion: v1 kind: Service metadata: name: registry spec: selector: app: registry ports: - name: http port: 5000 targetPort: 5000 --- apiVersion: v1 kind: Service metadata: name: registry-nodeport spec: type: NodePort selector: app: registry ports: - name: http port: 5000 nodePort: 30050 targetPort: 5000
NetworkPolicies steuern den Netzwerkverkehr zwischen Pods und Namespaces.
Beispiel: Traffic nur von Namespace „frontend“ auf Port 80 zulassen
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend namespace: backend spec: podSelector: {} ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: frontend ports: - protocol: TCP port: 80