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 :6443 --token --discovery-token-ca-cert-hash sha256: --control-plane --certificate-key
=====Installation=====
====Cluster Reset====
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
====Ubuntu 18====
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
====Ubuntu 24====
# 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:
# 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 83m v1.34.2
ubuntu3 Ready 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
kubectl describe pod -n
# Web Dashboard installieren
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
====AlmaLinux8====
#!/usr/bin/env bash
sudo dnf install -y curl yum-utils
sudo tee /etc/yum.repos.d/kubernetes.repo <
# 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 entfernen=====
# 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=====
Labels sind frei definierbare Key/Value-Paare, die Ressourcen beschreiben. Sie sind essenziell für Selektoren und Gruppierungen.
* app=frontend
* role=database
* env=prod
* version=v1
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=====
Namespaces sind logische Bereiche innerhalb eines Kubernetes-Clusters.
Sie dienen zur Trennung, Organisation und Rechteverwaltung von Ressourcen.
* Ressourcen mit gleichem Namen können in unterschiedlichen Namespaces existieren.
* NetworkPolicies, ResourceQuotas und RBAC-Regeln werden oft pro Namespace definiert.
* Pods innerhalb desselben Namespaces können sich standardmäßig gegenseitig erreichen.
* Der Namespace "default" wird automatisch verwendet, wenn kein anderer angegeben ist.
kubectl get namespaces
kubectl create namespace frontend
kubectl label namespace frontend app=demo
=====Selektoren=====
Selektoren nutzen Labels, um Ressourcen zu finden. Die häufigsten sind:
* podSelector
* namespaceSelector
* labelSelector (z. B. bei Services oder Deployments)
====Pod Selektor====
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: {}
====Namespace selektor====
Ein namespaceSelector filtert Namespaces, nicht Pods.
namespaceSelector:
matchLabels:
app: demo
Wählt alle Namespaces mit dem Label app=demo.
====Label Selektor (generell)====
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
=====Yaml=====
====Pod====
apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
containers:
- image: nginx
name: nginx
imagePullPolicy: Always
kubectl create -f mypod.yml
kubectl delete pod demo
====ReplicaSet====
**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
====Deployment====
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
====DaemonSet====
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 custom
Hallo von deinem eigenen Nginx-Image 🎉
Version 1.0.0
# 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
====Persistent Volumes====
===NFS===
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
===SMB===
[[https://github.com/kubernetes-csi/csi-driver-smb|Treiber]]
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
====StatefulSet====
todo
=====Volume types=====
* Host based
* EmptyDir
* HostPath
* Block Storage
* Amazon EBS
* GCE Persistent Disk
* Azure Disk
* vSphere Volume
* ...
* Distributed file system
* NFS
* Ceph
* Gluster
* Amazon EFS
* Azure File System
* ...
* Other
* Flocker
* iSCSI
* Git Repo
* Quobyte
* ...
=====Service Types=====
In Kubernetes gibt es vier Haupttypen von Services: ClusterIP, NodePort, LoadBalancer und ExternalName. Zusätzlich gibt es den Sonderfall Headless Service. Siehe [[https://spacelift.io/blog/kubernetes-service|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 :.|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|
====Cluster IP====
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:
* Jeder Service hat seine eigene Cluster-IP (z.B. 10.96.0.1, 10.102.10.4).
* Das Routing macht kube-proxy über die Kombination (Ziel-IP, Port, Protokoll).
====Node Port====
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.
* Jeder, der sich mit dem Port Ihrer Knoten verbinden kann, kann auf den Dienst zugreifen.
* Um Konflikte zu vermeiden, kann jede Portnummer jeweils nur von einem NodePort-Dienst verwendet werden.
* Jeder Knoten in Ihrem Cluster muss standardmäßig auf dem Port lauschen, auch wenn er keinen Pod ausführt, der zum Dienst gehört.
* Es findet kein automatischer Lastausgleich statt: Clients werden von dem Knoten bedient, mit dem sie sich verbinden.
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.
====Load Balancer====
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.
====External Name====
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 Service====
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.
=====Networking=====
====CNI (Common networking interface)====
Kubernetes selbst macht kein Networking, sondern ruft über CNI-Plugins andere Programme auf, die die Netzwerke zwischen Pods herstellen (IP-Zuweisung, Routing, DNS usw.).
* Flannel: sehr einfach, gut für Lernumgebungen
* Calico: leistungsstärker, Policies, für Produktivsysteme
* Weave Net: leichtgewichtig, self-managing
* Cilium: frei wählbar, modern, eBPF-basiert, High-Performance
=====Lokale Registry=====
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.
Vorsicht: Wenn man den ganzen Cluster neu startet und die Registry auf einen anderen Node verteilt wird, hostPath ist immer nur auf dem aktuellen Node, nicht clusterweit. Für Demo reichts.
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
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"]}
====Registry (PVC)====
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
=====Network Policies=====
NetworkPolicies steuern den Netzwerkverkehr zwischen Pods und Namespaces.
* podSelector: Welche Pods werden geschützt?
* ingress / egress: Verkehr erlauben
* from/to: Quellen oder Ziele
* podSelector
* namespaceSelector
* ipBlock
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
=====Links=====
* [[https://www.youtube.com/watch?v=Fr9GqFwl6NM|Kubernetes Course – Certified Kubernetes Administrator Exam Preparation - FreeCodeCamp]]
* [[https://www.youtube.com/watch?v=MTHGoGUFpvE|Kubernetes Zero to Hero]]
* [[https://youtu.be/NFApeJRXos4|Service types explained]]
* [[https://www.youtube.com/watch?v=d6WC5n9G_sM|Full beginner course]]
* [[https://www.youtube.com/watch?v=S6CVIqQeJww|First steps after install]]
* [[https://www.youtube.com/watch?v=ZxC6FwEc9WQ|Persistent volumes]]
* [[https://www.youtube.com/watch?v=tZEKGNnvBzg|Kubectl secrets]]
* [[https://www.youtube.com/watch?v=u948CURLDJA|Ingress NginX]]
* [[https://www.youtube.com/watch?v=1SaPfm96lY4|The native web]]
* [[https://www.youtube.com/watch?v=tq9ng_Nz9j8|Networking]]
* [[https://www.youtube.com/watch?v=EQNO_kM96Mo|Application deployment tutorial video]] todo
* [[https://stackoverflow.com/questions/69448131/kubernetes-whats-the-difference-between-deployment-and-replica-set|Difference between deployment and replicaset]]
* [[https://github.com/derailed/k9s|K9s - Manage your Kubernetes Cluster with Style]]
* [[https://kruschecompany.com/de/helm-kubernetes/|Die Rolle von HELM in Kubernetes]]