Kubernetes - koniec ery cgroup v1: co musisz sprawdzić przed aktualizacją do v1.35
cgroup v2 jest domyślnym interfejsem kontroli zasobów w Linuksie od lat. Kubernetes dogania resztę ekosystemu — i tym razem bez możliwości wycofania.
Krótka historia
Control Groups v1 trafiły do jądra Linuksa w 2008 roku. cgroup v2 — przepisane od nowa, z jednolitą hierarchią i lepszą semantyką — stało się stabilne w kernelu 4.5 (2016), a domyślnym w większości dystrybucji między 2020 a 2022 rokiem. Kubernetes obsługuje v2 od v1.25 (GA, 2022).
Mimo to przez lata oba interfejsy funkcjonowały równolegle. Klastry postawione na starszych obrazach, zablokowane wymaganiami compliance albo po prostu nigdy nieruszane — trzymały się v1. Projekt tolerował tę sytuację. Do teraz.
Co mówi KEP-5573
KEP-5573 (Deprecate and Remove cgroup v1 Support) przeszedł do statusu Beta z milestone'em v1.35. Najważniejsza zmiana operacyjna: brama funkcji FailCgroupV1 ma być domyślnie włączona (true) w v1.35.
W praktyce oznacza to jedno: kubelet nie uruchomi się na węźle, który korzysta z cgroup v1. Nie ostrzeżenie w logach, nie degradacja funkcjonalności — twarde zatrzymanie procesu.
Harmonogram usunięcia wsparcia jest opisany jako „nie wcześniej niż v1.38". Przy typowym tempie wydań (co cztery miesiące) mamy mniej niż dwa lata na pełną migrację całego parku węzłów.
Jak sprawdzić, czy twój węzeł jest podatny
Jedno polecenie, wynik natychmiastowy:
stat -fc %T /sys/fs/cgroup/cgroup2fs→ węzeł korzysta z cgroup v2, jesteś bezpiecznytmpfs→ węzeł wciąż na cgroup v1, wymaga migracji
Jeśli zarządzasz dużą flotą węzłów, możesz uruchomić to przez DaemonSet:
yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: cgroup-check
namespace: kube-system
spec:
selector:
matchLabels:
app: cgroup-check
template:
metadata:
labels:
app: cgroup-check
spec:
hostPID: true
containers:
- name: check
image: busybox:1.36
command: ["/bin/sh", "-c"]
args:
- |
NODE=$(hostname)
RESULT=$(stat -fc %T /proc/1/root/sys/fs/cgroup/)
echo "NODE=$NODE CGROUP=$RESULT"
sleep infinity
volumeMounts:
- name: host-proc
mountPath: /proc/1/root/sys/fs/cgroup
readOnly: true
volumes:
- name: host-proc
hostPath:
path: /sys/fs/cgroupLogi zbierzesz przez kubectl logs -n kube-system -l app=cgroup-check --prefix.
Które dystrybucje są problematyczne
Dystrybucje, na których cgroup v1 jest wciąż domyślne lub jedyne dostępne:
| System operacyjny | Kernel | cgroup domyślnie |
|---|---|---|
| RHEL 7 | 3.10 | v1 (brak v2) |
| RHEL 8 | 4.18 | v1 (v2 dostępne przez grub) |
| Ubuntu 18.04 LTS | 4.15 | v1 |
| Debian 10 (Buster) | 4.19 | v1 |
| Amazon Linux 2 | 5.10 | v1 (domyślnie) |
RHEL 9, Ubuntu 20.04+, Debian 11+, Amazon Linux 2023 i wszystkie aktualne obrazy AKS/EKS/GKE korzystają już z cgroup v2.
Jeśli twój obraz węzła ma w nazwie rok 2019 albo wcześniejszy — czas na aktualizację.
Zmiany w zachowaniu runtime'ów
Sama migracja hierarchii to nie wszystko. cgroup v2 zmienia semantykę OOM killera.
Dotychczas w v1, gdy kontener przekroczył limit pamięci, kernel zabijał jeden losowy proces wewnątrz cgroup. W v2, przy włączonej fladze singleProcessOOMKill=true w containerd (domyślnie od containerd 2.0), kernel zabija cały kontener od razu — dokładnie jeden PID, ten, który przekroczył limit.
To lepsze zachowanie pod względem determinizmu, ale aplikacje wieloprocesowe (np. JVM z fork-exec, serwery HTTP z worker procesami) mogą reagować inaczej niż do tej pory. Warto przetestować workloady wrażliwe na OOM przed migracją.
Minimalne wersje runtime'ów z pełnym wsparciem cgroup v2:
- containerd: 1.7+ (zalecane 2.0+)
- CRI-O: 1.25+
- Docker (via cri-dockerd): 24.0+ z containerd backend
Aplikacje wymagające uwagi
Dwa środowiska uruchomieniowe były historycznie problematyczne przy migracji na cgroup v2:
OpenJDK — wersje poniżej 11.0.16 / 17.0.4 nie czytają poprawnie limitów pamięci z cgroup v2. JVM widzi całą pamięć hosta zamiast limitu kontenera i ustawia heap zbyt wysoko. Rozwiązanie: aktualizacja JDK lub jawne ustawienie -Xmx.
Node.js — wersje poniżej 16.17 / 18.7 mają analogiczny problem z process.memoryUsage() i automatycznym GC. Wersje LTS aktualnie wspierane (20, 22) nie mają tego problemu.
Jeśli uruchamiasz starsze wersje tych runtime'ów w kontenerach — cgroup v2 będzie pierwszym objawem problemu, ale prawdziwa przyczyna to outdated baza obrazu.
Plan migracji
Migracja to zazwyczaj operacja na poziomie obrazu węzła, nie klastra jako całości.
Krok 1 — Inwentaryzacja Uruchom sprawdzenie stat -fc %T /sys/fs/cgroup/ na wszystkich węzłach. Oznacz te z tmpfs labelem cgroup-version=v1.
Krok 2 — Aktualizacja obrazu węzła W AKS: zaktualizuj node pool do aktualnego obrazu (az aks nodepool upgrade). W EKS: użyj aktualnego AMI z Amazon Linux 2023 lub Bottlerocket. W GKE: standardowe obrazy węzłów są już na v2 od kilku lat.
Krok 3 — Rotacja węzłów Nie modyfikuj istniejących węzłów — wymień je. Cordon + drain + delete, nowy węzeł automatycznie dołączy z aktualnym obrazem.
Krok 4 — Weryfikacja runtime'ów Po rotacji sprawdź wersję containerd: kubectl get node -o wide i crictl version na węźle.
Krok 5 — Testy workloadów wrażliwych na OOM Uruchom testy obciążeniowe dla aplikacji z wieloma procesami i jawnym zarządzaniem pamięcią.
Co z węzłami, które nie dają się zaktualizować
Jeśli masz węzły na RHEL 7 bez możliwości migracji do RHEL 9 (np. blokady licencyjne, wymagania compliance), rozważ:
- Włączenie cgroup v2 przez parametr kernela na RHEL 8:
grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1"— wymaga restartu - Wirtualizację: węzeł pod VMware/KVM z aktualnym gościem Linux jako warstwa izolacji
- Wydzielenie takiego workloadu poza Kubernetes do czasu migracji platformy
Opcja odwlekania przez wyłączenie bramy FailCgroupV1=false w v1.35 technicznie istnieje — ale KEP-5573 przewiduje usunięcie tej furtki razem z samym kodem v1 nie wcześniej niż w v1.38.
Comments ()