K8s
Dokumenty k osobnímu Kubernetes clusteru (3 nody na vpsfree.cz LXC).
Stránky
- Analyze — investigační log: jaké příkazy byly volány, co vrátily a co z toho plyne pro každou oblast (cluster, síť, ingress, storage).
- Findings — souhrn aktuálního stavu a klíčové implikace seřazené podle naléhavosti.
- Upgrade plan — plán plynulého přechodu z k8s 1.26 na 1.31, migrace na NFS PVC a poznámky k bezpečnosti NFS multi-mount.
Analýza
K8s Cluster Analyze — investigační log
Záznam průzkumu osobního k8s clusteru. Pro každou oblast: použitý příkaz, klíčový výstup a okamžité pozorování.
1. Setup a verze
Příkazy
ls -la ~/.kube/
which kubectl
kubectl version --client
kubectl get nodes
Nález
- Kubeconfig na nestandardním místě:
~/.kube/kube.yml(ne~/.kube/config). - kubectl v1.35.4 (klient).
- Cluster přístupný, vrací 3 nody Ready.
Důsledky
- kubectl funguje (přes
KUBECONFIG, alias nebo symlink). - Klient o ~9 minor verzí napřed před serverem — funguje díky tomu, že kubectl je obvykle backwards-compatible v širokém rozpětí, ale přesnější skew tolerance je
±1minor; můžou se objevit drobné nekompatibility u nových API.
2. Cluster status
Příkazy
kubectl get nodes -o wide
kubectl get componentstatuses
kubectl cluster-info
kubectl top nodes
Nález
- 3 nody (k8s-master, k8s-1, k8s-2), všechny Ready, k8s v1.26.0, Ubuntu 20.04.6, kernel 6.12.79, containerd 1.7.27.
- Uptime nodů ~4 roky 191 dní.
- Control plane endpoint:
https://172.16.16.1:6443(≠ InternalIP masteru172.19.9.10) — to je WireGuard interface. - etcd, controller-manager: Healthy.
- scheduler: hlášený jako Unhealthy, ale jen kvůli starému health endpoint check (port 10251 vs 10259).
kubectl top nodes: Metrics API not available (metrics-server nenainstalovaný).
Důsledky
- k8s 1.26 je EOL (oficiální podpora skončila únor 2024). Žádné security patche.
- API server schovaný za WireGuard = dobrá bezpečnostní praxe.
- Bez metrics-server nefunguje HPA,
kubectl top, sledování zátěže. componentstatuseswarning na schedulera je deprecated check, ne reálný problém.
3. Ingress
Příkazy
kubectl get ingressclass
kubectl get pods -A | grep -iE 'ingress|traefik|nginx|haproxy'
kubectl get ingress -A
kubectl -n ingress-nginx get svc
kubectl get ingress -A -o jsonpath='{...class-annotation...ingressClassName...}'
Nález
- Žádný IngressClass resource neexistuje (
No resources found). - 1× ingress-nginx-controller pod (1/1 Ready, 50 restartů za 230 dní).
- 20 ingressů napříč 9 namespacy, všechny:
apiVersion: networking.k8s.io/v1(modern)kubernetes.io/ingress.class: nginxanotace (deprecated od 1.18)spec.ingressClassNameprázdný
- Service ingress-nginx-controller:
LoadBalancer, externí IP 37.205.11.119 (přidělená MetalLB), porty 80/443.
Důsledky
- Funguje, protože ingress-nginx pořád respektuje deprecated anotaci jako fallback.
- Při budoucím upgrade ingress-nginx (verze, která tu anotaci přestane podporovat) hrozí výpadek všech ingressů najednou.
- Stačí vytvořit jeden
IngressClassresource — existující ingressy budou fungovat dál.
4. Storage
Příkazy
kubectl get storageclass
kubectl get pv
kubectl get pvc -A
kubectl get pv nfs-pv -o yaml
kubectl get pods -A -o jsonpath='{...volumes...hostPath.path...nfs.server...}'
Nález
- 1× StorageClass:
example-nfs(provisionervpsfree.cz, Reclaim Delete, AllowVolumeExpansion: false). - 1× PV:
nfs-pv(200Mi, RWX, NFS172.16.129.146:/nas/4721/shared). - 1× PVC:
default/example-nfs— používaný jen testovacím podemnfs-nginx. - Všechny ostatní stateful workloady mají
hostPath:- krcmar/web →
/k8s/cityhry - nextcloud →
/k8s/nextcloud - sellapp/uvasinu-test →
/k8s/sellapp/uvasinu-test - openldap →
/k8s/ldap/{conf,data} - pgadmin →
/k8s/kubernetes-pgadmin - postgres →
/k8s/kubernetes-postgresql - wireguard →
/etc/wireguard,/var/lib/wgui
- krcmar/web →
Důsledky
- Data jsou přilepená na konkrétní node (kde je adresář). Bez nodeSelectoru hrozí, že se pod přeplánuje a data nenajde.
- Žádné k8s-řízené zálohy. Vše musí být zálohováno na úrovni filesystému.
- Postgres a OpenLDAP na hostPath = single point of failure pro kritická data.
- Existující NFS infrastruktura (1 SC) je nedostatečně využívaná.
5. Síť
Příkazy
kubectl get nodes -o jsonpath='{...PodCIDR...}'
kubectl -n kube-system get pods -l app=flannel -o wide
kubectl -n kube-system get cm kube-flannel-cfg -o jsonpath='{.data.net-conf\.json}'
kubectl -n kube-system get cm kubeadm-config
kubectl -n kube-system get cm kube-proxy
kubectl -n metallb-system get cm config
kubectl get networkpolicy -A
kubectl exec ... ip route
kubectl exec ... ping ...
kubectl exec ... ip -d link show flannel.1
kubectl exec ... bridge fdb show dev flannel.1
Nález
- CNI: Flannel, backend
vxlan(VNI 1, UDP port 8472), MTU 1450. - Pod CIDR:
10.244.0.0/16(per-node /24). - Service CIDR:
10.243.0.0/16. - DNS:
cluster.local, kube-dns na10.243.0.10. - kube-proxy: mode
""= iptables (default). - MetalLB: Layer2, advertizuje
37.205.11.119/32. Konfigurace přes starý ConfigMap (před v0.13 CRD-based). - NetworkPolicies: žádné.
- Underlay: nody na
venet0(LXC/OpenVZ interface), segment172.19.9.10-12. - Routy na masteru:
- default přes
venet0 - 10.244.{1,2}.0/24 přes
flannel.1(VXLAN tunel) - 172.16.16.0/24, 192.168.0.0/24, 192.168.1.0/24 přes
wg0(WireGuard) - tap0/tap1 (OpenVPN)
- docker0 (linkdown, leftover)
- default přes
- Latence:
- master → 10.244.1.1 (pod gateway): 0.23 ms
- master → 10.244.2.1: 0.22 ms
- master → 172.19.9.11 (underlay): 0.15 ms
- VXLAN FDB má korektní MAC→IP mappingy pro všechny peery.
Důsledky
- Síť je funkční a low-latency, VXLAN overhead ~70-100 µs (zanedbatelné).
- VXLAN je v LXC prostředí jediná rozumná volba (host-gw nelze, není kontrola nad routováním na hostu).
- Bez NetworkPolicies = jakýkoli pod si může povídat s jakýmkoli jiným. Pro osobní cluster ok, ale citlivé namespacy (postgres, openldap) by mohly mít aspoň základní izolaci.
- MetalLB v ConfigMap konfiguraci = velmi stará verze (před v0.13). Funguje, ale neaktualizovaná. Při upgrade k8s je třeba migrace na CRD-based config.
- MetalLB Layer2 = single point of forwarding pro public IP (vždy přes node, který IP zrovna ARP-uje). Failover ~10s. Pro tento scale ok.
Findings
K8s Cluster — Findings
Syntéza zjištění z analýzy clusteru. Stav k 2026-05-07.
Aktuální stav v kostce
| Oblast | Verze / config | Stav |
|---|---|---|
| Kubernetes | v1.26.0 | ❌ EOL od února 2024 |
| Hostitelské OS | Ubuntu 20.04.6, kernel 6.12.79 | ✅ aktuální kernel |
| Container runtime | containerd 1.7.27 | ✅ ok |
| Nody | 3 (1 master + 2 workers) Ready | ✅ |
| CNI | Flannel VXLAN | ✅ funguje, low-latency |
| kube-proxy | iptables mode | ✅ ok pro tento scale |
| Load balancer | MetalLB Layer2 (ConfigMap config) | ⚠️ pre-CRD verze (před v0.13) |
| Ingress | ingress-nginx, 20 ingressů, deprecated anotace | ⚠️ chybí IngressClass resource |
| Storage | 1 nepoužitá NFS SC, vše ostatní hostPath | ⚠️ data přilepená na nody |
| NetworkPolicies | žádné | ⚠️ flat L3, žádná izolace |
| metrics-server | nenainstalovaný | ⚠️ kubectl top nefunguje |
| etcd | healthy | ✅ |
| API server | za WireGuard (172.16.16.1:6443) | ✅ dobrá praxe |
| cert-manager, dashboard | běží | ✅ funkční (verze stará) |
Co funguje dobře
- Inter-node konektivita je stabilní a rychlá (sub-ms latency v overlay).
- VXLAN backend je správná volba pro LXC prostředí — alternativy (host-gw, native routing) nejsou v LXC dostupné.
- WireGuard pro API server access schovává control plane mimo veřejnou síť.
- MetalLB Layer2 s jednou public IP funguje a je jednoduchý.
- etcd, controller-manager, scheduler běží zdravě (despite deprecated
componentstatuseswarning).
Co tikne (priorita podle naléhavosti)
🔴 Vysoká: k8s 1.26 EOL
- Žádné security patche od února 2024.
- Cca 1.5 roku za aktuální stable větev.
- Akce: postupný upgrade
1.26 → 1.31(5 hopů, plán viz upgrade-plan).
🔴 Vysoká: Postgres na hostPath bez zálohy a replikace
- Data v
/k8s/kubernetes-postgresqlna konkrétním nodu. - Při ztrátě nodu = ztráta DB.
- Akce: minimálně automatizovaný
pg_dumpcron + off-site copy. Dlouhodobě úvaha o CloudNativePG s replikami.
🟡 Střední: chybí IngressClass resource
- 20 ingressů spoléhá na deprecated anotaci
kubernetes.io/ingress.class: nginx. - ingress-nginx ji aktuálně pořád respektuje, ale není to garantováno do budoucna.
- Akce: jeden YAML — vytvořit
IngressClass nginxjako default. Existující ingressy fungují dál.
🟡 Střední: MetalLB v pre-CRD verzi
- Konfigurace přes ConfigMap (před v0.13).
- Verze nedostává security updates.
- Migrace na CRD-based config znamená krátký výpadek public IP (~30s).
- Akce: udělat samostatně, mimo k8s upgrade hopy.
🟡 Střední: hostPath všude
- Data většiny stateful workloadů jsou přilepená na nody (
/k8s/*). - Bez NFS PVC (které je nasazené, ale nepoužívané kromě testu).
- Akce: postupná migrace nekritických workloadů (krcmar/media, blog, sellapp, pgadmin, wireguard) na NFS PVC. Postgres a OpenLDAP záměrně zůstávají na hostPath.
🟢 Nízká: chybí NetworkPolicies
- Flat L3 — jakýkoli pod si může povídat s jakýmkoli.
- Pro osobní cluster ok, ale citlivé namespacy (postgres, openldap) by mohly mít izolaci.
- Akce: postupně přidávat per-namespace policy podle potřeby.
🟢 Nízká: chybí metrics-server
kubectl topnefunguje, žádné HPA.- Pro homelab obvykle nekritické.
- Akce: jeden
kubectl applyna deploy manifest.
🟢 Nízká: cleanup
docker0interface na masteru (Docker daemon nainstalovaný, nepoužívaný).- Test workload
nfs-nginxvdefaultnamespace (pravděpodobně už nepotřebný). - 50 restartů ingress-nginx-controller za 230 dní (sledovat, jestli se trend nezhoršuje).
Souvislost s plánovanými změnami
- Nextcloud bude zrušen a nahrazen Rust aplikací na Turris routeru. K8s pro tu službu bude jen public proxy přes Service+Endpoints+Ingress. Nextcloud workload tedy v migračních plánech vynechat.
- Postgres zůstává na fixed nodu s hostPath i během upgradu. Migrace na CloudNativePG (s replikami) je samostatný projekt na později.
- Migrace nekritických workloadů na NFS PVC se hodí udělat před k8s upgrade — méně proměnných najednou.
Co je vůbec mimo dosah v tomto prostředí
vpsfree.cz LXC kontejnery brání použití některých „enterprise" řešení:
- ❌ Ceph / Rook — kernel moduly, block devices, privileged ops.
- ❌ Longhorn — iSCSI (kernel moduly).
- ❌ OpenEBS Mayastor / cStor — hugepages, NVMe features.
- ❌ DRBD / LINSTOR — kernel modul.
Jediná reálně dostupná HA storage v LXC = NFS (přes vpsfree nebo vlastní NFS server) + aplikační replikace pro databáze. Pro plnou HA distribuovanou storage by byl potřeba přechod z LXC na KVM.
Upgrade plan
K8s Upgrade & Storage Migration Plan
Cluster: 3 nody (k8s-master, k8s-1, k8s-2) na vpsfree.cz LXC, k8s v1.26.0, Flannel VXLAN, MetalLB L2, ingress-nginx.
Část 1: NFS multi-mount safety
Co NFS poskytuje
- Více klientů může současně mountovat stejný export v
RWX(ReadWriteMany). - POSIX-like sémantika přes síť (
read,write,rename,mkdir). - NLM / NFSv4 file locking — ale jen pokud ho aplikace explicitně používá (
fcntl,flock). - Atributový cache (mtime, size) je eventually consistent s konfigurovatelným TTL.
Bezpečnost podle scénáře
| Scénář | Bezpečné? |
|---|---|
| Dva pody zapisují různé soubory | ✅ Ano |
| Read-only shared assety, jeden writer | ✅ Ano |
| Aplikace s embedded DB (SQLite, BerkeleyDB) | ❌ Spolehlivě rozbije DB |
| Postgres / MySQL data dir | ❌ Nepodporováno |
Dvě repliky stejné aplikace zapisující do stejných souborů bez flock | ❌ Race conditions, korupce |
| Append-only logy z více writerů do stejného souboru | ⚠️ Krátké zápisy < PIPE_BUF většinou ok, dlouhé se interleavují |
| Aplikace navržené pro shared FS (Nextcloud, file storage apps) | ✅ Ano |
| Cron joby z různých nodů manipulující stejné soubory | ⚠️ Pouze s explicit flock |
Praktická pravidla
- Postgres nikdy na NFS (zůstává hostPath).
- SQLite nikdy na NFS — locking přes NFS je nespolehlivý.
- „Multiple writers" je bezpečné, jen pokud:
- Každý writer píše do vlastního adresáře/souboru, nebo
- Aplikace vědomě používá NFS-aware locking, nebo
- Read-mostly scénář s explicit lockingem.
- Pro současné workloady (krcmar/media, blog, sellapp) — typicky 1 replika nebo read-only, multi-writer není reálně využíván → bezpečné pásmo.
- Při budoucím škálování
replicas: > 1pro app, která zapisuje do shared volume, vždy ověřit concurrent-write chování.
Část 2: NFS PVC plán
Současný stav
- 1× StorageClass
example-nfs(provisionervpsfree.cz). - 1× PV/PVC, používaný jen testovacím podem.
- NFS export:
172.16.129.146:/nas/4721/shared.
Doporučený provisioner
Nasadit nfs-subdir-external-provisioner (upstream, well-maintained):
helm install nfs-subdir nfs-subdir-external-provisioner \
--namespace storage --create-namespace \
--set nfs.server=172.16.129.146 \
--set nfs.path=/nas/4721/shared \
--set storageClass.name=nfs \
--set storageClass.defaultClass=false \
--set storageClass.reclaimPolicy=Retain \
--set storageClass.allowVolumeExpansion=true \
--set nfs.mountOptions="{soft,timeo=100,retrans=3,noatime}"
Důvody pro soft,timeo=100: jinak při výpadku NFS pody zatuhnou v D stavu (uninterruptible sleep). hard mount je v k8s past.
Postup migrace
| Fáze | Workload | Velikost změny | Riziko |
|---|---|---|---|
| 1 | services/blog (media) | malá | nízké, čistý start |
| 2 | krcmar/cityhry (/k8s/cityhry) | střední, kopírování | nízké (read-mostly) |
| 3 | sellapp/uvasinu-test | malá | nízké |
| 4 | services/wireguard config | malá | střední (config-critical) |
| 5 | services/pgadmin data | malá | nízké |
| 6 | services/openldap | dle latence NFS | — (LDAP má rád low-latency I/O) |
| ❌ | services/postgres | zůstává hostPath, fixed node | — |
Per-workload migrace recept
1. kubectl scale deployment X --replicas=0
2. rsync -av /k8s/X/ /mnt/new-pvc/X/ (na nodu, kde data jsou)
3. Upravit deployment: hostPath → PVC
4. kubectl apply
5. kubectl scale deployment X --replicas=1
6. Ověřit, že běží + má data
7. Po týdnu: smazat /k8s/X na původním nodu
Část 3: K8s upgrade plán (1.26 → 1.31)
Klíčový fakt: skew policy
K8s nepovoluje skok přes více než 1 minor verzi v jednom kroku.
1.26 → 1.27 → 1.28 → 1.29 → 1.30 → 1.31 (5 hopů)
Každý hop = ~30-60 min práce + buffer na ověření.
Phase 0: Před prvním upgradem
-
etcd backup automation — cron na masteru:
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ snapshot save /backup/etcd-$(date +%F).dbDaily, retention 14 dní, off-site copy přes rsync.
-
Add IngressClass resource — preventivně:
apiVersion: networking.k8s.io/v1 kind: IngressClass metadata: name: nginx annotations: ingressclass.kubernetes.io/is-default-class: "true" spec: controller: k8s.io/ingress-nginx -
Deploy NFS provisioner (Část 2).
-
Migrace 2-3 nekritických workloadů na PVC — méně proměnných najednou v upgrade fázi.
-
Dokumentace current state:
kubectl versionkubectl -n ingress-nginx get deploy ingress-nginx-controller -o yaml | grep image:- Stejně pro flannel, metallb, cert-manager, dashboard.
- Snapshot
kubectl get all -A -o yaml > backup.yaml.
Per-hop checklist (opakovat pro každý hop)
1. Přečti CHANGELOG / breaking changes:
https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.XX.md
2. etcd snapshot
3. Na masteru:
apt update
apt-mark unhold kubeadm
apt install kubeadm=1.XX.x-*
apt-mark hold kubeadm
kubeadm upgrade plan
kubeadm upgrade apply v1.XX.x
4. Drain master, upgrade kubelet+kubectl, uncordon:
kubectl drain k8s-master --ignore-daemonsets --delete-emptydir-data
apt install kubelet=1.XX.x-* kubectl=1.XX.x-*
systemctl daemon-reload && systemctl restart kubelet
kubectl uncordon k8s-master
5. Pro každý worker (k8s-1, pak k8s-2):
kubectl drain k8s-1 --ignore-daemonsets --delete-emptydir-data
# na nodu:
kubeadm upgrade node
apt install kubelet=1.XX.x-* kubectl=1.XX.x-*
systemctl daemon-reload && systemctl restart kubelet
kubectl uncordon k8s-1
6. Ověření:
kubectl get nodes # všechny Ready, správná verze
kubectl get pods -A # žádný CrashLoopBackOff
smoke test: curl jeden ingress endpoint
7. Počkat min. 24-48h před dalším hopem, sledovat
Breaking changes per hop
- 1.26 → 1.27: PSP definitivně pryč (nepoužíváme). Některé
--feature-gateflagy odstraněny. - 1.27 → 1.28:
seccompDefaultGA. Některé legacy auth mechanizmy deprecated. - 1.28 → 1.29: In-tree cloud providers odstraněny (nepoužíváme). Legacy ServiceAccount token secrets už se negenerují by default.
- 1.29 → 1.30: ValidatingAdmissionPolicy GA. Některé kubelet flagy odstraněny.
- 1.30 → 1.31: AppArmor GA, drobné změny PV/CSI.
Pro tento cluster (žádné cloud provider integrace, žádné PSP, standardní flannel/metallb) by žádný hop neměl být dramatický.
Komponenty mimo k8s
| Komponenta | Kdy upgradovat | Jak |
|---|---|---|
| Flannel | Po 1.27 nebo 1.28 | kubectl apply -f nový manifest. Drop-in upgrade, VXLAN config zůstane. |
| MetalLB | Po 1.28 (samostatně) | Větší práce: ConfigMap → CRD migrace. Install nová verze, vyrobit IPAddressPool + L2Advertisement CRD, smazat starý ConfigMap. Krátký výpadek (~30s) public IP. |
| ingress-nginx | Souběžně s 1.28 nebo 1.29 | helm upgrade nebo apply nového manifestu. Verze kompatibilní s aktuální k8s. |
| cert-manager | Souběžně s ingress-nginx | Helm upgrade, CRDs se updatují automaticky. |
| kubernetes-dashboard | Kdykoli (low-priority) | v7+ je Helm-only s odlišnou architekturou. Smazat current, nainstalovat čerstvě. |
| CoreDNS | Automaticky s kubeadm upgrade | Není potřeba ručně. |
Doporučená timeline
| Týden | Co |
|---|---|
| 1 | etcd backup automation, IngressClass resource, NFS provisioner deployment |
| 2 | Migrace 2-3 nekritických workloadů na NFS PVC, validate |
| 3 | Upgrade 1.26 → 1.27, ověřit |
| 4 | Upgrade 1.27 → 1.28, ověřit |
| 5 | Upgrade Flannel + ingress-nginx + cert-manager (spolu) |
| 6 | Upgrade 1.28 → 1.29 |
| 7 | MetalLB ConfigMap → CRD migrace (samostatně) |
| 8 | Upgrade 1.29 → 1.30 |
| 9 | Upgrade 1.30 → 1.31 |
| 10 | Migrace zbylých nekritických workloadů na NFS PVC, redeploy dashboard, cleanup |
Cca 10 týdnů na klidné plynulé tempo. Lze zkrátit minimálně na 5 víkendů (jeden hop za víkend).
Postgres během upgradu
Postgres na hostPath na fixed nodu je v pohodě i během upgradu, jen:
- Před každým drainem nodu, kde Postgres běží, manuálně
pg_dumpa uložit mimo cluster. nodeSelectorna pod, aby se nepřemístil.- Při upgradu konkrétního nodu: scale Postgres na 0, drain, upgrade, scale zpět. Krátký výpadek (~5-10 min). Aplikace musí na to být připravené.
Zero-downtime Postgres = migrace na CloudNativePG s replikami. Samostatný projekt, nedělat během k8s upgradu.
Souvisí s Nextcloud replacement
Nextcloud je v plánu na zrušení (replacement = Rust app na Turris, k8s jen jako public proxy). Pro Nextcloud workload tedy:
- Nemigrovat na NFS PVC (zbytečná práce).
- V plánu počítat s tím, že někde v týdnu 5-7 se Nextcloud namespace smaže a nahradí Service+Endpoints+Ingress směrem na Turris.
/k8s/nextcloudadresář na nodu po vyřazení smazat.