# Kubernetes — Luncher HA Manifesty pro nasazení Luncheru na Kubernetes s vysokou dostupností (3 repliky, Redis adapter pro Socket.io, WATCH/MULTI atomické zápisy, graceful shutdown). ## Prerekvizity - kubectl nakonfigurovaný na cílový cluster - `helm` nainstalovaný - Redis Stack image přístupný z clusteru (`redis/redis-stack-server:7.2.0-v14`) - Obraz `luncher:ha-test` načtený do clusteru (viz níže) ## Lokální kind cluster (testik) — setup ### 1. Smazat a znovu vytvořit cluster s port mappings ```powershell $env:KIND_EXPERIMENTAL_PROVIDER = "nerdctl" # Přidat nerdctl do PATH (Rancher Desktop) $env:PATH += ";$env:LOCALAPPDATA\Programs\Rancher Desktop\resources\resources\win32\bin" kind delete cluster --name testik kind create cluster --name testik --config k8s/kind/testik.yaml ``` ### 2. Sestavit a načíst obraz ```powershell docker build -t luncher:ha-test . # Uložit a načíst přes nerdctl (kind + nerdctl provider) nerdctl save luncher:ha-test -o luncher.tar kind load image-archive luncher.tar --name testik Remove-Item luncher.tar ``` ### 3. Nainstalovat Traefik (rke2-traefik) > **Prerekvizita (Rancher Desktop):** Pokud Rancher Desktop běží s `kubernetes.options.traefik=true`, > host-switch.exe obsadí port 80 dříve než kind. Vypni traefik v k3s: > ```powershell > rdctl set --kubernetes.options.traefik=false > ``` > > **Prerekvizita — inotify limity:** Čtyř-uzlový kind cluster vyčerpá výchozí > `fs.inotify.max_user_instances=128`. kube-proxy pak padá s „too many open files". > Zvyš limit v rancher-desktop WSL2 (přežije restart WSL2, ale ne reboot — přidej do > `/etc/sysctl.d/99-kind.conf` pro trvalost): > ```powershell > wsl -d rancher-desktop -- sysctl -w fs.inotify.max_user_instances=1280 > ``` ```powershell # rke2-traefik je v rke2-charts, ne rancher-charts helm repo add rke2-charts https://rke2-charts.rancher.io helm repo update # Nejdřív CRD chart, pak samotný chart helm install traefik-crd rke2-charts/rke2-traefik-crd -n kube-system --create-namespace helm install traefik rke2-charts/rke2-traefik -n kube-system ` --set "tolerations[0].key=node-role.kubernetes.io/control-plane" ` --set "tolerations[0].operator=Exists" ` --set "tolerations[0].effect=NoSchedule" ``` Ověř že Traefik DaemonSet běží na control-plane (má hostPort 80): ```powershell kubectl get ds -n kube-system traefik-rke2-traefik kubectl get pods -n kube-system -o wide | Select-String traefik ``` ### 4. Nainstalovat Reloader [stakater/Reloader](https://github.com/stakater/Reloader) sleduje změny Secret a ConfigMap a automaticky spustí rolling restart Deploymentu — odpadá nutnost ručního `kubectl rollout restart` po rotaci `JWT_SECRET` nebo `ADMIN_PASSWORD`. Manifest je vendorovaný ve verzi v1.4.16 (`k8s/base/reloader.yaml`). Nasadit do `default` namespace: ```powershell kubectl apply -f k8s/base/reloader.yaml kubectl rollout status deploy/reloader-reloader ``` Reloader běží cluster-wide díky `ClusterRoleBinding` — nepotřebuje žádnou konfiguraci per-namespace. Deployment Luncheru má anotaci `reloader.stakater.com/auto: "true"`, která říká Reloaderu, ať sleduje všechny Secrety a ConfigMapy odkazované přes `envFrom`. ### 5. Nasadit Luncher ```powershell # Namespace + Redis kubectl apply -f k8s/base/namespace.yaml kubectl apply -f k8s/base/redis-statefulset.yaml kubectl apply -f k8s/base/redis-service.yaml # Počkat na Redis kubectl rollout status statefulset/redis -n luncher # Server secret (nebo použít šablonu server-secret.yaml) kubectl create secret generic luncher-secrets -n luncher ` --from-literal=JWT_SECRET=dev-secret-change-me ` --from-literal=ADMIN_PASSWORD=admin # Server kubectl apply -f k8s/base/server-configmap.yaml kubectl apply -f k8s/base/server-deployment.yaml kubectl apply -f k8s/base/server-service.yaml kubectl apply -f k8s/base/server-pdb.yaml kubectl apply -f k8s/base/ingressroute.yaml # Počkat na server kubectl rollout status deploy/luncher -n luncher ``` ## Testovací scénáře ### Baseline ```powershell kubectl get pods -n luncher -o wide # Ověř: 3 pody na 3 různých worker uzlech, status Running ``` ### Rolling update bez výpadku V jednom terminálu posílej provoz: ```powershell # Nainstaluj hey: go install github.com/rakyll/hey@latest hey -z 60s -c 20 http://luncher.localhost/api/health ``` Ve druhém terminálu spusť rollout: ```powershell kubectl rollout restart deploy/luncher -n luncher ``` **Kritérium: 0 non-2xx odpovědí, 0 connection errors.** ### Node drain ```powershell kubectl cordon testik-worker2 kubectl drain testik-worker2 --ignore-daemonsets --delete-emptydir-data # PDB zabrání souběžnému drainu druhého nodu kubectl get pods -n luncher -o wide # pody se přeplánují kubectl uncordon testik-worker2 ``` ### Ověření Socket.io cross-pod 1. Otevři dvě záložky prohlížeče na `http://luncher.localhost` 2. Z jednoho podu vyvolej změnu: ```powershell kubectl exec -it deploy/luncher -n luncher -- curl -s -X POST localhost:3001/api/... ``` 3. Ověř, že druhá záložka (pravděpodobně jiný pod) obdrží WebSocket event ### Concurrent write test 1. Otevři stejnou Pizza day objednávku ve dvou záložkách 2. Simuluj souběžné odeslání (otevřít DevTools → síť → odeslat obě požadavky současně) 3. Ověř Redis: `kubectl exec -it redis-0 -n luncher -- redis-cli JSON.GET luncher:` — oba zápisy musí být zachovány (WATCH/MULTI retry) ### Auto-rollout při změně Secret / ConfigMap Reloader automaticky spustí rolling restart, kdykoli se změní `luncher-secrets` nebo `luncher-config`: ```powershell # Příklad: rotace admin hesla kubectl -n luncher patch secret luncher-secrets --type=merge ` -p '{"stringData":{"ADMIN_PASSWORD":"nove-heslo"}}' # Reloader detekuje změnu resourceVersion a patchne pod template kubectl rollout status deploy/luncher -n luncher # Ověř anotaci přidanou Reloaderem na pod template kubectl get deploy luncher -n luncher -o yaml | Select-String "STAKATER" ``` **Kritérium: pody se automaticky vyrolují bez ručního restartu. PDB zajistí, že alespoň jeden pod zůstane dostupný.** ## Pořadí aplikace manifestů 1. `reloader.yaml` (do `default` namespace — musí být před Deployment) 2. `namespace.yaml` 3. `redis-statefulset.yaml` + `redis-service.yaml` 4. `server-configmap.yaml` + `server-secret.yaml` 5. `server-deployment.yaml` + `server-service.yaml` + `server-pdb.yaml` 6. `ingressroute.yaml`