Benutzer-Werkzeuge

Webseiten-Werkzeuge


kubernetes

improve

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.

Ein Pod ist eine Resource eines Replicaset was eine Resourse eines Deployment ist.
  • Du definierst einen Service (z. B. vom Typ LoadBalancer, ClusterIP oder NodePort).
  • Der Service hat ein Label-Selector, z. B. app:my-app, der sagt: „Ich leite Traffic an alle Pods mit diesem Label weiter.“
  • Kubernetes erstellt eine virtuelle IP-Adresse (ClusterIP) für den Service.
  • Der Load Balancer (z. B. AWS ELB, MetalLB, Traefik, NginX, Apache) leitet Traffic an diese IP.
  • Kubernetes nutzt kube-proxy oder IPVS auf den Nodes, um den Traffic an die richtigen Pods weiterzuleiten, basierend auf den Labels und dem internen Routing.
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>

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

At least 2 cpu's and 2048 MB RAM per node are required.

On all nodes

  • Edit hostname
  • Edit /etc/hosts file and add all nodes.
  • Edit /etc/fstab and uncomment SWAP partition
  • Edit /etc/initramfs-tools/conf.d/resume and uncomment swap uuid
  • Find swap unit using systemctl –type swap and mask it using systemctl mask xxx.swap.
SWAP must be deactivated!

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

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:     <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

AlmaLinux8

#!/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 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/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

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

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 Kubernetes Service – What It is, Types & Examples

ServiceBeschreibungErreichbarkeitEinsatzbereich
Cluster IPStandardtyp. 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-IPEinfacher externer Zugriff, z. B. für Tests
Load BalancerErstellt einen externen Load Balancer (Cloud-abhängig).Extern über öffentliche IPProduktivbetrieb mit automatischer Skalierung
External Nameeitet den Service auf einen externen DNS-Namen um.Extern (DNS)Integration externer Dienste
Headless ServiceKein 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

<?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"]}

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
kubernetes.txt · Zuletzt geändert: 2025/11/27 01:05 von jango