Automatisierte Installation von Kubernetes auf einem Ubuntu-Server 24.04

Diese Anleitung beschreibt die skriptbasierte Installation eines Vanilla Kubernetes Clusters (Version 1.36) auf Ubuntu 24.04 VMs in einer Lab-Umgebung. Die einzelnen Schritte im Skript sind zur besseren Verständlichkeit mit entsprechenden Kommentaren versehen. Die Anleitung passt nicht zu einem produktiven HA-Design, bei dem drei Control Nodes Pflicht sind.

Es wird vorausgesetzt, dass die VMs bereits installiert sind und über feste IP-Adressen (oder eine DHCP-Reservierung) verfügen. Die IP-Adressen müssen ggf. an die jeweilige Umgebung angepasst werden.

Infrastruktur

Name IP-Adresse Rolle
akr-c01 10.10.191.205 Control-Plane
akr-w01 10.10.191.206 Worker
akr-w02 10.10.191.168 Worker
akr-w03 10.10.191.207 Worker

Cluster-Netzbereiche (CIDR):

Netztyp CIDR Bedeutung
Node-/VM-Netz 10.10.191.0/24 Ubuntu-VMs
Pod-Netz 172.28.0.0/16 Interne Ips der Pods
Service-Netz 172.29.0.0/16 Virtuelle ClusterIPs der Services

Die „klassischen“ Pods (10.244.0.0/16) und die Service-CIDR (10.96.0.0/12) werden nicht verwendet, um eine mögliche Überlappung zu vermeiden.

Installationsschritte

Schritt 1: Skript

Auf allen vier VMs wird dasselbe Skript verwendet. Es kann direkt von dieser Seite per Copy-Paste in ein neues Shell-Skript eingefügt werden. Die Datei muss ausführbar gemacht werden.

chmod +x bootstrap-k8s-cloudlab.sh

Schritt 2: Skript ausführen

Starten Sie den Installationsprozess auf dem Control Node.

sudo NODE_NAME=akr-c01 NODE_IP=10.10.191.205 ./bootstrap-k8s-cloudlab.sh control-plane

Das Skript bereitet das Betriebssystem vor, installiert containerd, kubeadm/kubelet/kubectl, initialisiert den Cluster und installiert Cilium als CNI.  Zum Abschluss wird ein kubeadm-join Befehl ausgegeben, der im nächsten Schritt auf den Worker Nodes benötigt wird.

Schritt 3: Worker beitreten lassen

Sowohl die IP als auch der Node-Name müssen auf allen drei Workern (akr-w01, akr-w02 und akr-w03) entsprechend angepasst werden.

sudo NODE_NAME=akr-w01 NODE_IP=10.10.191.206 ./bootstrap-k8s-cloudlab.sh worker "kubeadm join 10.10.191.205:6443 --token ... --discovery-token-ca-cert-hash sha256:..."

Der ausgegebene Befehl kubeadm join enthält einen schutzwürdigen Token. Dieser sollte nicht öffentlich geteilt oder in einem öffentlichen Repository gespeichert werden, da damit weitere Nodes dem Cluster beitreten können.

Skript:

#!/usr/bin/env bash
#
# bootstrap-k8s-cloudlab.sh
#
# Vanilla Kubernetes auf Ubuntu Server 24.04 mit kubeadm + containerd + Cilium.
# Dieses Skript ist kommentiert, damit man jeden Schritt nachvollziehen kann,
# bevor es mit root-Rechten auf einer VM ausgeführt wird.
#
# Zielumgebung: 1 Control-Plane + 3 Worker
#
# Nodes (Beispielwerte, an deine Umgebung anpassen):
# akr-c01 10.10.191.205 Control-Plane
# akr-w01 10.10.191.206 Worker
# akr-w02 10.10.191.168 Worker
# akr-w03 10.10.191.207 Worker
#
### Nutzung:
# Control-Plane:
# sudo NODE_NAME=akr-c01 NODE_IP=10.10.191.205 ./bootstrap-k8s-cloudlab.sh control-plane
#
# Worker (Join-Befehl kommt aus der Ausgabe des Control-Plane-Laufs):
# sudo NODE_NAME=akr-w01 NODE_IP=10.10.191.206 ./bootstrap-k8s-cloudlab.sh worker "kubeadm join 10.10.191.205:6443 --token ... --discovery-token-ca-cert-hash sha256:..."
#
# Optional per Umgebungsvariable überschreibbar (Defaults siehe unten):
# K8S_MINOR, POD_CIDR, SERVICE_CIDR, CONTROL_PLANE_IP,
# CONTROL_PLANE_ENDPOINT, CILIUM_VERSION, CRI_SOCKET
#
# Alle vier VMs benötigen feste IP-Adressen oder eine
# DHCP-Reservierung. 

# set -Eeuo pipefail macht das Skript robuster als ein einfaches "set -e":
# -E Fehler-Traps werden auch in Funktionen weitervererbt
# -e das Skript bricht sofort ab, sobald ein Befehl fehlschlägt
# -u die Verwendung nicht gesetzter Variablen ist ein Fehler
# -o pipefail ein Fehler mitten in einer Pipe wird nicht verschluckt
# Bei einer Kubernetes-Installation ist das wichtig, weil ein halb
# erfolgreiches Setup später nur schwer zu debuggen ist.

set -Eeuo pipefail

# Erstes Argument ist die Rolle (control-plane oder worker).

ROLE="${1:-}"
# Danach wird das erste Argument entfernt ("shift"). Alles, was übrig
# bleibt, ist - falls vorhanden - der komplette kubeadm-join-Befehl.
# Das ist nur für Worker relevant.

[[ $# -gt 0 ]] && shift || true
JOIN_CMD_RAW="$*"

# Standardwerte. Wenn du beim Aufruf keine anderen Werte per
# Umgebungsvariable übergibst, verwendet das Skript diese hier.

K8S_MINOR="${K8S_MINOR:-1.36}"
POD_CIDR="${POD_CIDR:-172.28.0.0/16}"
SERVICE_CIDR="${SERVICE_CIDR:-172.29.0.0/16}"
CONTROL_PLANE_IP="${CONTROL_PLANE_IP:-10.10.191.205}"
# CONTROL_PLANE_ENDPOINT ist in diesem einfachen Lab direkt die IP von
# akr-c01. 
CONTROL_PLANE_ENDPOINT="${CONTROL_PLANE_ENDPOINT:-${CONTROL_PLANE_IP}}"
CILIUM_VERSION="${CILIUM_VERSION:-1.19.5}"
CRI_SOCKET="${CRI_SOCKET:-unix:///run/containerd/containerd.sock}"

# Wenn NODE_NAME nicht gesetzt ist, nimmt das Skript den kurzen Hostnamen
# der VM. NODE_IP bleibt zunächst leer und wird weiter unten automatisch
# ermittelt, falls du sie nicht explizit per Umgebungsvariable setzt.

NODE_NAME="${NODE_NAME:-$(hostname -s)}"
NODE_IP="${NODE_IP:-}"

# log() erzeugt gut lesbare Statusausgaben zwischen den Installationsschritten.
log() {
echo -e "\n==> $*"
}

# fail() gibt eine Fehlermeldung auf STDERR aus und beendet das Skript
# sofort mit einem Fehlercode.

fail() {
echo "FEHLER: $*" >&2
exit 1
}

# Die Installation verändert Systemdateien und installiert Pakete,
# deshalb muss das Skript als root laufen (via sudo).

require_root() {
[[ "${EUID}" -eq 0 ]] || fail "Bitte mit root-Rechten ausführen, z.B. sudo $0 ${ROLE}"
}

# Zeigt die korrekte Verwendung an - wird aufgerufen, wenn kein
# gültiger Modus angegeben wurde oder beim Worker der Join-Befehl fehlt.

usage() {
cat <<USAGE
Usage:
Control-Plane:
sudo NODE_NAME=akr-c01 NODE_IP=10.10.191.205 $0 control-plane

Worker:
sudo NODE_NAME=akr-w01 NODE_IP=10.10.191.206 $0 worker "kubeadm join 10.10.191.205:6443 --token ... --discovery-token-ca-cert-hash sha256:..."

Rollen:
control-plane Installiert Pakete, initialisiert den Cluster, installiert Cilium.
worker Installiert Pakete und führt den kubeadm-join-Befehl aus.
USAGE
}

# Ermittelt die Node-IP. Wenn NODE_IP bereits per Umgebungsvariable
# gesetzt wurde, passiert nichts weiter. Sonst versucht das Skript,
# anhand des Node-Namens die passende Lab-IP zuzuordnen (an deine
# Umgebung anpassen). Ist der Name unbekannt, wird die erste lokale IP
# aus dem VM-Netz verwendet. Findet sich gar keine, bricht das Skript ab.

resolve_node_ip() {
if [[ -n "${NODE_IP}" ]]; then
return 0
fi

case "${NODE_NAME}" in
akr-c01) NODE_IP="10.10.191.205" ;;
akr-w01) NODE_IP="10.10.191.206" ;;
akr-w02) NODE_IP="10.10.191.168" ;;
akr-w03) NODE_IP="10.10.191.207" ;;
*)
# Fallback: erste IPv4-Adresse aus dem VM-Netz verwenden.
# Das Muster 10\.10\.191\. an dein eigenes Node-Netz anpassen.

NODE_IP="$(hostname -I | tr ' ' '\n' | awk '/^10\.10\.191\./ {print; exit}')"
;;
esac

[[ -n "${NODE_IP}" ]] || fail "NODE_IP konnte nicht automatisch ermittelt werden. Bitte NODE_IP=... setzen."
}

# Prüft, dass nur "control-plane" oder "worker" als Rolle erlaubt sind,
# und dass beim Worker ein Join-Befehl mitgegeben wurde.

validate_role() {
case "${ROLE}" in
control-plane|worker) ;;
*) usage; exit 1 ;;
esac

if [[ "${ROLE}" == "worker" && -z "${JOIN_CMD_RAW}" ]]; then
usage
fail "Für 'worker' muss der kubeadm-join-Befehl als Argument übergeben werden."
fi
}

# Schreibt Namen und IP-Adressen aller Nodes in /etc/hosts, damit sich
# die VMs gegenseitig per Hostname erreichen können (z.B. ping akr-w01).
# Ein eventuell vorhandener alter Block wird vorher entfernt, damit
# keine Duplikate entstehen.

write_hosts_entries() {
log "Hostnamen in /etc/hosts ergänzen"

local marker="# Kubernetes Lab nodes"
sed -i '/# Kubernetes Lab nodes/,+4d' /etc/hosts
cat >> /etc/hosts <<EOF_HOSTS
${marker}
10.10.191.205 akr-c01
10.10.191.206 akr-w01
10.10.191.168 akr-w02
10.10.191.207 akr-w03
EOF_HOSTS
}

# Bereitet das Betriebssystem Kubernetes-tauglich vor:
# - Swap wird sofort deaktiviert und in /etc/fstab auskommentiert,
# damit er nach einem Reboot nicht wieder aktiv wird (Kubernetes
# erwartet, dass kein Swap verwendet wird).
# - Die Kernel-Module overlay und br_netfilter werden dauerhaft geladen.
# - sysctl-Parameter sorgen dafür, dass Bridge-Traffic von iptables
# gesehen wird und IPv4-Forwarding aktiv ist - beides Voraussetzung
# für funktionierendes Pod-Networking.

prepare_os() {
log "System vorbereiten: Swap deaktivieren, Kernel-Module laden, sysctl setzen"

swapoff -a || true
sed -ri '/\sswap\s/s/^/#/' /etc/fstab

cat >/etc/modules-load.d/k8s.conf <<'EOF_MODULES'
overlay
br_netfilter
EOF_MODULES
modprobe overlay
modprobe br_netfilter

cat >/etc/sysctl.d/k8s.conf <<'EOF_SYSCTL'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF_SYSCTL
sysctl --system >/dev/null
}

# Installiert Hilfspakete für HTTPS-Repositories, Zertifikate, Downloads
# und GPG-Schlüsselverarbeitung. DEBIAN_FRONTEND=noninteractive
# verhindert, dass apt während der Installation nach Eingaben fragt.

install_base_packages() {
log "Basis-Pakete installieren"

export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y apt-transport-https ca-certificates curl gpg gnupg lsb-release jq
}

# containerd wird hier als die Container Runtime genutzt
# SystemdCgroup = true ist wichtig, damit kubelet und containerd
# denselben cgroup-Mechanismus verwenden kann, ohne das kommt es zu
# schwer diagnostizierbaren Fehlern beim Start von Pods.

install_containerd() {
log "containerd installieren und für systemd-cgroups konfigurieren"

export DEBIAN_FRONTEND=noninteractive
apt-get install -y containerd

mkdir -p /etc/containerd
containerd config default >/etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

systemctl daemon-reload
systemctl enable --now containerd

# Erleichtert späteres Debugging von Containern mit crictl.
cat >/etc/crictl.yaml <<EOF_CRICTL
runtime-endpoint: ${CRI_SOCKET}
image-endpoint: ${CRI_SOCKET}
timeout: 10
debug: false
EOF_CRICTL
}

# Richtet das offizielle Kubernetes-Apt-Repository für die gewünschte
# Minor-Version ein und installiert kubelet, kubeadm und kubectl.
# apt-mark hold verhindert, dass diese Pakete bei einem normalen
# "apt upgrade" ungeplant auf eine neue Minor-Version springen.
# Die explizite Node-IP in /etc/default/kubelet stellt sicher, dass
# kubelet nicht versehentlich eine falsche Interface-IP verwendet,
# falls die VM mehrere Netzwerkkarten hat.

install_kubernetes_packages() {
log "Kubernetes ${K8S_MINOR} Repository einrichten und kubelet/kubeadm/kubectl installieren"

mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL "https://pkgs.k8s.io/core:/stable:/v${K8S_MINOR}/deb/Release.key" \
| gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v${K8S_MINOR}/deb/ /" \
>/etc/apt/sources.list.d/kubernetes.list

apt-get update -y
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

cat >/etc/default/kubelet <<EOF_KUBELET
KUBELET_EXTRA_ARGS=--node-ip=${NODE_IP}
EOF_KUBELET

systemctl enable kubelet
}

# Kopiert die Admin-kubeconfig nach /root/.kube/config, damit root
# direkt kubectl verwenden kann. Falls das Skript per sudo von einem
# normalen Benutzer gestartet wurde, wird die kubeconfig zusätzlich
# in dessen Home-Verzeichnis abgelegt.

copy_kubeconfig() {
log "kubeconfig für root und, falls vorhanden, für den sudo-Benutzer kopieren"

install -d -m 700 /root/.kube
cp -f /etc/kubernetes/admin.conf /root/.kube/config
chown root:root /root/.kube/config
chmod 600 /root/.kube/config

if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]] && id "${SUDO_USER}" >/dev/null 2>&1; then
local user_home
user_home="$(getent passwd "${SUDO_USER}" | cut -d: -f6)"
if [[ -n "${user_home}" && -d "${user_home}" ]]; then
install -d -m 700 -o "${SUDO_USER}" -g "${SUDO_USER}" "${user_home}/.kube"
cp -f /etc/kubernetes/admin.conf "${user_home}/.kube/config"
chown "${SUDO_USER}:${SUDO_USER}" "${user_home}/.kube/config"
chmod 600 "${user_home}/.kube/config"
fi
fi
}

# Lädt die zur CPU-Architektur passende Cilium-CLI herunter, prüft die
# SHA256-Prüfsumme und installiert das Binary nach /usr/local/bin.
# Danach steht der Befehl "cilium" zur Verfügung.

install_cilium_cli() {
log "Cilium CLI installieren"

local arch cli_version tmpdir
case "$(uname -m)" in
x86_64|amd64) arch="amd64" ;;
aarch64|arm64) arch="arm64" ;;
*) fail "Nicht unterstützte Architektur für Cilium CLI: $(uname -m)" ;;
esac

cli_version="$(curl -fsSL https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)"
tmpdir="$(mktemp -d)"
pushd "${tmpdir}" >/dev/null
curl -fsSLO "https://github.com/cilium/cilium-cli/releases/download/${cli_version}/cilium-linux-${arch}.tar.gz"
curl -fsSLO "https://github.com/cilium/cilium-cli/releases/download/${cli_version}/cilium-linux-${arch}.tar.gz.sha256sum"
sha256sum --check "cilium-linux-${arch}.tar.gz.sha256sum"
tar xzf "cilium-linux-${arch}.tar.gz" -C /usr/local/bin
popd >/dev/null
rm -rf "${tmpdir}"
}

# Der Kern der Control-Plane-Installation:
# - kubeadm init startet den Cluster. Die Node-IP wird als
# API-Server-Adresse bekanntgegeben, Pod- und Service-CIDR werden
# festgelegt, der Node-Name gesetzt und containerd als Runtime
# angegeben.
# - Danach wird die kubeconfig kopiert.
# - Cilium wird als CNI installiert. ipam.mode=kubernetes ist wichtig,
# damit Cilium den von kubeadm gesetzten Pod-CIDR verwendet und
# nicht seinen eigenen Default-Adresspool (10.0.0.0/8) - das würde
# sich sonst mit dem VM-Netz überschneiden.
# - Zum Schluss wird der Join-Befehl für die Worker erzeugt und
# zusätzlich unter /root/kubeadm-join.sh gespeichert.

init_control_plane() {
log "Control-Plane initialisieren: ${NODE_NAME} / ${NODE_IP}"

kubeadm init \
--apiserver-advertise-address="${NODE_IP}" \
--control-plane-endpoint="${CONTROL_PLANE_ENDPOINT}:6443" \
--pod-network-cidr="${POD_CIDR}" \
--service-cidr="${SERVICE_CIDR}" \
--node-name="${NODE_NAME}" \
--cri-socket="${CRI_SOCKET}"

copy_kubeconfig

log "CNI installieren: Cilium ${CILIUM_VERSION}"
install_cilium_cli
KUBECONFIG=/etc/kubernetes/admin.conf cilium install --version "${CILIUM_VERSION}" \
--set ipam.mode=kubernetes \
--wait
KUBECONFIG=/etc/kubernetes/admin.conf cilium status --wait

log "Join-Befehl für Worker erzeugen"
kubeadm token create --print-join-command >/root/kubeadm-join.sh
chmod 600 /root/kubeadm-join.sh

cat <<EOF_DONE

########################################################
Control-Plane fertig.

Join-Befehl für Worker:
$(cat /root/kubeadm-join.sh)

Beispiel Worker-Aufruf:
sudo NODE_NAME=akr-w01 NODE_IP=10.10.191.206 ./bootstrap-k8s-cloudlab.sh worker "$(cat /root/kubeadm-join.sh)"

Status prüfen:
kubectl get nodes -o wide
kubectl get pods -A
########################################################
EOF_DONE
}

# Wird nur auf Workern ausgeführt. Der übergebene Join-Befehl wird
# bereinigt (Zeilenumbrüche und Backslashes aus der mehrzeiligen
# kubeadm-Ausgabe entfernt) und in einzelne Argumente zerlegt. Das ist
# sicherer als ein simples "eval", weil nicht beliebiger Shell-Code
# ausgeführt wird, sondern geprüft wird, dass es sich tatsächlich um
# einen "kubeadm join"-Befehl handelt.

join_worker() {
log "Worker vorbereiten: ${NODE_NAME} / ${NODE_IP}"

local cleaned_join
cleaned_join="$(printf '%s' "${JOIN_CMD_RAW}" | tr '\n' ' ' | sed 's/\\//g' | xargs)"

read -r -a join_args <<< "${cleaned_join}"

if [[ "${join_args[0]:-}" != "kubeadm" || "${join_args[1]:-}" != "join" ]]; then
fail "Der Join-Befehl muss mit 'kubeadm join' beginnen."
fi

log "Node dem Cluster hinzufügen"
"${join_args[@]}" --node-name "${NODE_NAME}" --cri-socket "${CRI_SOCKET}"

echo "Worker ${NODE_NAME} wurde dem Cluster hinzugefügt."
}

# Ablaufsteuerung: erst werden Rechte, Rolle und IP geprüft. Danach
# werden Hosts-Datei, Betriebssystem, Basispakete, containerd und
# Kubernetes-Pakete vorbereitet - das ist auf Control-Plane und Worker
# identisch. Erst am Ende entscheidet die Rolle, ob der Cluster
# initialisiert oder ein Join durchgeführt wird.

main() {
require_root
validate_role
resolve_node_ip

log "Rolle=${ROLE}, Node=${NODE_NAME}, Node-IP=${NODE_IP}, Kubernetes=${K8S_MINOR}"

write_hosts_entries
prepare_os
install_base_packages
install_containerd
install_kubernetes_packages

if [[ "${ROLE}" == "control-plane" ]]; then
init_control_plane
else
join_worker
fi
}

main "$@"

 

Schritt 4: Cluster prüfen

Mit folgende Befehlen lässt sich der Installierte Cluster überprüfen.

Auf der Control-Plane:

kubectl get nodes -o wide
kubectl get pods -A
cilium status
cilium connectivity test # Optional

Mithilfe des cilium connectivity test kann zusätzlich überprüft werden, ob die Pod-zu-Pod-, Service- und Policy-relevanten Kommunikationspfade funktionieren.

Erwartbare Ergebnisse:

kubectl get nodes -o wide

NAME      STATUS   ROLES           AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION             CONTAINER-RUNTIME
akr-c01   Ready    control-plane   39m   v1.36.2   10.10.191.205   <none>        Ubuntu 24.04.4 LTS   6.8.0-124-generic (amd64)  containerd://2.2.1
akr-w01   Ready    <none>          31m   v1.36.2   10.10.191.206   <none>        Ubuntu 24.04.4 LTS   6.8.0-124-generic (amd64)  containerd://2.2.1
akr-w02   Ready    <none>          22m   v1.36.2   10.10.191.168   <none>        Ubuntu 24.04.4 LTS   6.8.0-124-generic (amd64)  containerd://2.2.1
akr-w03   Ready    <none>          21m   v1.36.2   10.10.191.207   <none>        Ubuntu 24.04.4 LTS   6.8.0-124-generic (amd64)  containerd://2.2.1

kubectl get pods -A

NAMESPACE     NAME                                    READY   STATUS    RESTARTS   AGE
kube-system   cilium-dj6sz                            1/1     Running   0          38m
kube-system   cilium-envoy-jcl4n                      1/1     Running   0          22m
kube-system   cilium-envoy-lc8kg                      1/1     Running   0          31m
kube-system   cilium-envoy-mqw6l                      1/1     Running   0          21m
kube-system   cilium-envoy-z88xg                      1/1     Running   0          38m
kube-system   cilium-gxjnv                            1/1     Running   0          22m
kube-system   cilium-h8dkr                            1/1     Running   0          21m
kube-system   cilium-operator-7bf757c544-5mmvl        1/1     Running   0          38m
kube-system   cilium-szt6k                            1/1     Running   0          31m
kube-system   coredns-589f44dc88-7j7xr                1/1     Running   0          39m
kube-system   coredns-589f44dc88-7zpkb                1/1     Running   0          39m
kube-system   etcd-akr-c01                            1/1     Running   0          39m
kube-system   kube-apiserver-akr-c01                  1/1     Running   0          39m
kube-system   kube-controller-manager-akr-c01         1/1     Running   0          39m
kube-system   kube-proxy-dk4d5                        1/1     Running   0          22m
kube-system   kube-proxy-n6x2g                        1/1     Running   0          31m
kube-system   kube-proxy-nfvhr                        1/1     Running   0          39m
kube-system   kube-proxy-nzrc8                        1/1     Running   0          21m
kube-system   kube-scheduler-akr-c01                  1/1     Running   0          39m

cilium status

Cilium:             OK
Operator:           OK
Envoy DaemonSet:    OK
Hubble Relay:       disabled
ClusterMesh:        disabled
DaemonSet           cilium                  Desired: 4, Ready: 4/4, Available: 4/4
DaemonSet           cilium-envoy            Desired: 4, Ready: 4/4, Available: 4/4
Deployment          cilium-operator         Desired: 1, Ready: 1/1, Available: 1/1
Containers:         cilium                  Running: 4
                    cilium-envoy            Running: 4
                    cilium-operator         Running: 1
                    clustermesh-apiserver   
                    hubble-relay            
Cluster Pods:       2/2 managed by Cilium
Helm chart version: 1.19.5
Image versions      cilium                  quay.io/cilium/cilium:v1.19.5@sha256:20fbbc14ac20b55a292c0dcda5571fc1de30a7dbc68c29db3e709390ab0732: 4
                    cilium-envoy            quay.io/cilium/cilium-envoy:v1.36.8-1781157951-a7f42a339078159911b5b9107881b35ecc4e752@sha256:326f872e19ce8aa45170
                    cilium-operator         quay.io/cilium/operator-generic:v1.19.5@sha256:be848a36577e07d0c5a895eda7aec928ddc52a5alfa2f432fd7a286609eldb4: 1