$ cat docs/README

Dokumentace platformy Pampeliška 3.0

Příručka pro administrátora, který platformu nasazuje a provozuje: od požadavků přes instalaci až po zálohy, upgrade a řešení problémů. Adresy a domény v příkladech jsou ilustrační — nahraďte je hodnotami svého prostředí.

01Úvod a komponenty

Pampeliška 3.0 je Infrastructure-as-Code platforma: tři virtuální stroje tvoří K3s cluster s embedded etcd, který provisionuje Ansible a jehož veškerý další stav (infrastruktura i aplikace) popisují manifesty v Git repozitáři a nasazuje Flux CD. Nad clusterem běží automatizace (Semaphore UI + Ansible), trezor tajemství pro automatizaci (OpenBao), datová vrstva (PostgreSQL + PostgREST), interní PKI a monitoring.

Komponenty platformy
KomponentaRole
K3sKubernetes distribuce; 3 serverové uzly s embedded etcd (řídicí vrstva).
kube-vipPlovoucí virtuální IP pro Kubernetes API (API VIP).
WireGuardŠifrovaný mesh mezi uzly (node↔node); provoz podů šifruje Flannel v režimu wireguard-native.
AnsibleProvisioning: VM na hostiteli LXD/MicroCloud (lxd_vms), S3 cíl záloh (backup_s3), základní konfigurace uzlů (common), CIS hardening (hardening), hostový firewall (firewall), WireGuard, K3s, kube-vip a důvěra v interní CA na uzlech (pki_trust).
Flux CDGitOps: průběžně srovnává cluster se stavem v Gitu; nasadí jen revizi s podepsaným posledním commitem (GPG podpis HEAD). Stav Kustomizací a HelmReleasů ukáže flux get all -A.
GiteaGit server uvnitř clusteru, ze kterého Flux čte manifesty (1 instance, SQLite na lokálním úložišti uzlu).
MetalLBPřiděluje IP adresy službám typu LoadBalancer (ingress, CA).
TraefikIngress controller — HTTPS vstup pro webová rozhraní a API.
LonghornDistribuované blokové úložiště pro perzistentní svazky (2 repliky), snapshoty a zálohy do S3.
GarageS3 úložiště záloh mimo cluster (VM na samostatném hostiteli, TLS přes nginx, denní snapshoty VM).
CloudNativePGOperátor PostgreSQL 16: HA cluster, failover, zálohy a WAL archivace do S3; pooler PgBouncer (2 instance).
PostgRESTREST API nad databází (grafový model nodes/edges).
Semaphore UIWebové rozhraní pro spouštění a plánování Ansible playbooků.
step-ca + step-issuerInterní certifikační autorita (ACME a JWK provisioner); step-issuer napojuje step-ca na cert-manager.
cert-manager + trust-managerAutomatické vystavení a obnova certifikátů; distribuce kořenového certifikátu CA do namespaců.
Sealed SecretsŠifrovaná tajemství v Gitu; dešifruje je jen controller v clusteru.
OpenBaoTrezor přístupových údajů pro automatizaci (síťové prvky, firewally, servery, API tokeny); 3 repliky s integrovaným úložištěm Raft, audit každého přístupu. Viz Tajemství (OpenBao).
Trivy OperatorPrůběžné skenování zranitelností image a konfigurace; generuje SBOM (SbomReport).
Prometheus, Alertmanager, GrafanaMetriky, alerty a dashboardy.
Loki + Grafana AlloySběr a uložení logů včetně auditního logu Kubernetes API (Alloy je sběrný agent). Viz Audit Kubernetes API.
ntfyDoručování notifikací z Alertmanageru (self-hosted push notifikace).

Plánované funkce zde popsané nejsou — viz Roadmapa na hlavním webu.

02Požadavky

Uzly clusteru

Tři VM clusteru zakládá samo nasazení (Ansible role lxd_vms, první krok ansible/playbooks/site.yml) na hostiteli LXD/MicroCloud. Parametry VM jsou v ansible/roles/lxd_vms/defaults/main.yml:

ParametrHodnota
Počet VM3
vCPU / RAM na VM4 vCPU / 8 GiB (minimum); doporučeno 12 GiB RAM — proměnná lxd_vm_memory, viz měření níže
Disky na VM (SSD)systémový 40 GiB + datový 60 GiB pro Longhorn (druhý disk /dev/sdb; role common ho naformátuje a připojí podle labelu)
Operační systémUbuntu Server 24.04 LTS
Kubernetespodporovaná řada K3s dle lifecycle politiky
PřístupSSH s klíčem z řídicího stroje (veřejný klíč ~/.ssh/id_ed25519.pub vloží do VM cloud-init); uživatel podle remote_user v ansible/ansible.cfg a ssh_allowed_users ve vars.yml, se sudo
Naměřená spotřeba (referenční nasazení, 5. 10. 2026)

Bez spravovaných workloadů spotřebuje Kubernetes na třech uzlech celkem zhruba 13 GiB RAM z 24 GiB (~55 %) a 3,7 jádra CPU z 12 (~30 %). Nejvíc paměti berou monitoring (~3 GiB: Prometheus, Grafana, Loki, Alloy) a Longhorn (~1,5 GiB). Rozložení mezi uzly je nerovnoměrné — nejvytíženější uzel měl ~74 % RAM. 8 GiB na VM je proto minimum; aby při výpadku uzlu zbylé dva unesly celý cluster, doporučujeme 12 GiB RAM na VM.

Virtualizační hostitelé

Referenčním prostředím je Canonical MicroCloud — 3 fyzické servery s LXD (VM), MicroCeph (úložiště VM, RBD) a MicroOVN (síť). Tři VM clusteru jsou rozložené na všechny tři servery, úložiště záloh (Garage, S3) běží v samostatné VM na serveru MicroCloudu. Pozor: tím sdílí hardware i Ceph s clusterem, takže zálohy nejsou fyzicky oddělené — pro produkci použijte S3 na samostatném hostiteli nebo mimo lokalitu (proměnné backup_s3_*). VM jsou připojené do OVN sítě se statickými IP adresami portů; virtuální IP (kube-vip pro API, MetalLB pro ingress a CA) v OVN síti fungují. Postup pro MicroCloud viz Instalace na Canonical MicroCloud.

  • Hostitel LXD/MicroCloud pro VM clusteru — po lxd init, se storage poolem a sítí, které odpovídají proměnným lxd_* (lxd_remote_host, lxd_storage_pool, lxd_network, vm_gateway a seznam lxd_vms se jmény a IP adresami VM).
  • Samostatný LXD hostitel pro úložiště záloh — role backup_s3 na něm založí VM s Garage (2 vCPU, 2 GiB RAM, disk 60 GiB). Proměnné backup_s3_* jsou v ansible/roles/backup_s3/defaults/main.yml (mj. backup_s3_lxd_host, backup_s3_vm_ip). Hostitel je samostatný, aby zálohy přežily ztrátu clusteru.
  • Na obou hostitelích musí předem existovat SSH uživatel z ansible.cfg (remote_user) s vaším veřejným klíčem a se sudo bez hesla — první krok nasazení se připojuje přímo na hostitele, protože VM ještě neexistují.
  • Jinou virtualizaci (VMware, cloud) referenční provisioning nepodporuje; VM připravíte sami a krok LXD se upraví v rámci onboardingu.

Síť

Kromě adres tří uzlů (v příkladech 192.168.10.11–13) potřebujete tři pevné, nepoužívané IP adresy ve stejném segmentu a adresu VM úložiště záloh:

ÚčelPříkladPoskytuje
API VIP (Kubernetes API)192.168.10.20kube-vip
Ingress (HTTPS vstup webových služeb)192.168.10.21MetalLB → Traefik
Certifikační autorita (step-ca)192.168.10.22MetalLB → step-ca
VM úložiště záloh (Garage), mimo pool MetalLB192.168.10.30role backup_s3

DNS záznamy pro webová rozhraní (např. *.example.internal) směrujte na adresu ingressu.

Úložiště záloh

S3 kompatibilní objektové úložiště mimo cluster (referenční nasazení: Garage ve VM na samostatném LXD hostiteli, v příkladech https://s3.example.internal:9000), dostupné z uzlů přes HTTPS. Zakládá ho ansible/playbooks/backup-s3.yml, který deploy.sh spouští před nasazením clusteru. Zálohy uložené uvnitř clusteru by při jeho ztrátě zmizely spolu s ním.

Řídicí stroj

Instalace se spouští z řídicího stroje. Dnes je to macOS — tajemství se ukládají do macOS Keychain. Potřebné nástroje:

kubectl, ansible-playbook, ansible-vault, git, openssl, curl, python3, gpg, kubeseal, flux (Flux CLI). Volitelně bao (OpenBao CLI) pro práci s tajemstvími z příkazové řádky.

GPG podpisový klíč musí být v lokálním keyringu a v git config user.signingkey ještě před prvním commitem — Flux přijme jen podepsaný HEAD větve main a bootstrap.sh bez klíče odmítne pokračovat.

Ansible kolekce mimo ansible-core nainstalujete z pinovaného seznamu:

ansible-galaxy collection install -r ansible/requirements.yml

03Instalace

Repozitář platformy si naklonujte na řídicí stroj; přístup k repozitáři poskytujeme na vyžádání (support@pampeliska.io).

git clone <URL repozitáře> pampeliska && cd pampeliska
  1. Upravte inventář a proměnné. Výchozí hodnoty v repozitáři popisují referenční prostředí — před instalací je nahraďte svými:
    • ansible/inventory/hosts.yml — adresy tří uzlů (první uzel má k3s_init: true) a jejich adresy ve WireGuard síti,
    • ansible/inventory/group_vars/all/vars.yml — vip_address, kube_vip_interface, wireguard_subnet, backup_s3_endpoint, cluster_domain, ssh_allowed_users a verze komponent,
    • proměnné lxd_* (ansible/roles/lxd_vms/defaults/main.yml) a backup_s3_* (ansible/roles/backup_s3/defaults/main.yml) — viz Požadavky; IP adresy VM v lxd_vms musí odpovídat ansible_host v inventáři,
    • IP adresy služeb: pool v kubernetes/infrastructure/metallb/pool.yaml a anotace metallb.io/loadBalancerIPs v HelmRelease Traefiku (kubernetes/infrastructure/traefik/) a step-ca (kubernetes/infrastructure/step-ca/),
    • domény v manifestech IngressRoute a povolené správcovské sítě pro OpenBao v kubernetes/infrastructure/openbao/allowlist.yaml,
    • wildcard certifikát pro vaši doménu nakopírujte do gitignorovaného adresáře certs/ (názvy souborů očekává scripts/prepare-secrets.sh).
    # ansible/inventory/hosts.yml (výřez)
    node1:
      ansible_host: 192.168.10.11
      wireguard_ip: 172.16.10.1     # adresa v overlay síti WireGuard — musí ležet ve wireguard_subnet
      k3s_init: true
    node2:
      ansible_host: 192.168.10.12
      wireguard_ip: 172.16.10.2
      k3s_init: false
    node3:
      ansible_host: 192.168.10.13
      wireguard_ip: 172.16.10.3
      k3s_init: false
    
    # ansible/inventory/group_vars/all/vars.yml (výřez)
    vip_address: 192.168.10.20
    wireguard_subnet: 172.16.10.0/24   # výchozí hodnota v repu je jiná — změňte ji spolu s wireguard_ip
    backup_s3_endpoint: "192.168.10.30:9000"
    cluster_domain: example.internal
  2. Připravte tajemství.
    ./scripts/prepare-secrets.sh

    Skript vygeneruje sealing klíč Sealed Secrets a další tajemství do macOS Keychain (včetně klíče pro odpečetění OpenBao a hesla jeho nouzového účtu admin), vytvoří Ansible Vault a přepečetí SealedSecrets v kubernetes/infra-config/sealed-secrets/. Je idempotentní: existující položky Keychain nepřegeneruje.

  3. Commitněte přepečetěná tajemství podepsaně do main. Bootstrap nahraje větev main z vašeho checkoutu do Gitea v clusteru a Flux z ní nasazuje — nepodepsaný HEAD nebo nezacommitované SealedSecrets cluster nerozběhnou.
    git add kubernetes/infra-config/sealed-secrets/ ansible/inventory/ # + další upravené soubory
    git commit -S -m "chore: přepečetěná tajemství a inventář"
  4. Spusťte nasazení. Z kořene repozitáře:
    ./deploy.sh --skip-secrets

    Volba --skip-secrets přeskočí přípravu tajemství (proběhla v kroku 2). Skript spustí scripts/bootstrap.sh, který postupně:

    • zkontroluje prerekvizity na řídicím stroji (nástroje, položky Keychain, GPG klíč),
    • spustí ansible/playbooks/backup-s3.yml (VM s úložištěm záloh) a ansible/playbooks/site.yml (VM clusteru, příprava OS, hardening, WireGuard, K3s HA, kube-vip, hostový firewall),
    • stáhne kubeconfig, obnoví sealing klíč z Keychain do clusteru, nasadí Gitea v clusteru, nahraje do ní repozitář a napojí Flux.

    Samotné ./deploy.sh (bez volby) spustí prepare-secrets.sh i bootstrap za sebou, bez prostoru pro commit mezi nimi — pro čistou instalaci proto použijte postup výše. Nápověda: ./deploy.sh --help.

  5. Ověřte stav.
    export KUBECONFIG=~/.kube/pampeliska.yaml
    kubectl get nodes -o wide        # 3 uzly ve stavu Ready
    flux get all -A                  # všechny zdroje a Kustomizace Ready=True
    kubectl get pods -A | grep -v -E 'Running|Completed'   # nic nevisí

    První reconcile všech komponent trvá několik minut; stav sledujte opakováním flux get kustomizations -A.

Ihned po instalaci

Uložte kopii sealing klíče (pampeliska-sealing-key) a klíče pro odpečetění OpenBao z Keychain do trezoru mimo řídicí stroj a vyexportujte klíče PKI — viz Klíče a tajemství. Bez sealing klíče nelze po obnově clusteru dešifrovat tajemství v Gitu, bez klíče pro odpečetění nelze obnovit data OpenBao.

OpenBao ani další komponenty clusteru se ručně neinstalují — nasadí je Flux z Gitu. OpenBao se při prvním startu samo inicializuje z konfigurace v Gitu (viz Tajemství).

Opakované spuštění provisioningu je idempotentní (nemění nic, co už odpovídá):

ansible-playbook ansible/playbooks/site.yml

Instalace na Canonical MicroCloud (OVN)

Referenční nasazení na MicroCloudu je v repozitáři vedené jako samostatná větev prostředí s vlastním inventářem ansible/inventory/<prostředí>/ (hosts.yml a group_vars/all/vars.yml). Pro síť OVN nastavuje inventář mj.:

# ansible/inventory/<prostředí>/group_vars/all/vars.yml (výřez)
lxd_network: default            # spravovaná síť OVN
lxd_nic_type: ovn
lxd_storage_pool: remote        # Ceph RBD
vm_gateway: 192.168.10.1        # router sítě OVN
lxd_vms:                        # statické IP portů — musí odpovídat ansible_host
  - { name: pampeliska-1, ip: 192.168.10.11 }
  - { name: pampeliska-2, ip: 192.168.10.12 }
  - { name: pampeliska-3, ip: 192.168.10.13 }

Instalaci obaluje skript scripts/bootstrap-microcloud.sh. Nastaví inventář prostředí, adresu prvního uzlu a API VIP, vlastní kubeconfig a endpoint S3 a jako zdroj pro in-cluster Gitea zrcadlí aktuální větev checkoutu. Postup:

  1. Tajemství (volá prepare-secrets.sh):
    ./scripts/bootstrap-microcloud.sh secrets
  2. Podepsaný commit přepečetěných SealedSecretů do větve prostředí:
    git add kubernetes/infra-config/sealed-secrets/
    git commit -S -m "chore(env): přepečetěná tajemství"
  3. Nasazení (volá bootstrap.sh: VM úložiště záloh, VM clusteru a K3s, Gitea, Flux):
    ./scripts/bootstrap-microcloud.sh deploy

Selže-li bootstrap uprostřed, pokračujte od daného kroku bez nového provisioningu VM — kroky jsou provision_cluster, fetch_kubeconfig, restore_sealing_key, bootstrap_gitea, seed_gitea, flux_install_controllers, apply_gotk_sync, finalize (kontrola prerekvizit běží vždy):

START_AT=seed_gitea ./scripts/bootstrap-microcloud.sh deploy

Na referenčním MicroCloudu trvá čistá instalace zhruba hodinu: provisioning VM a K3s ~15 minut, vrstvy Flux ~8 minut, start všech služeb dalších 20–30 minut.

Jiné prostředí

Pro další prostředí zkopírujte vzor: adresář inventáře, adresy ve wrapperu (první uzel, API VIP, endpoint S3), pool MetalLB a anotace IP adres Traefiku a step-ca. Pokud veřejné DNS směruje názvy platformy jinam než na ingress clusteru, doplňte remap v CoreDNS (kubernetes/extras/coredns-custom.yaml), jinak by Flux a pody chodily na služby mimo cluster.

04První přihlášení

SlužbaAdresa (příklad)UživatelHeslo v Keychain
Semaphore UIhttps://semaphore.example.internaladminpampeliska-semaphore-admin
Grafanahttps://grafana.example.internaladminpampeliska-grafana-admin
Giteahttps://git.example.internalgitea_adminpampeliska-gitea-admin
OpenBaohttps://bao.example.internaladmin (metoda Username)pampeliska-openbao-admin
PostgREST APIhttps://restapi.example.internalčtení anonymně, zápis JWTpampeliska-pgrst-jwt (podpisový klíč)
ntfyhttps://ntfy.example.internaladmin (viz Alerty)pampeliska-ntfy-admin

Úvodní hesla vygeneroval prepare-secrets.sh a uložil je do macOS Keychain řídicího stroje. Přečtete je takto:

security find-generic-password -s pampeliska-grafana-admin -w

Hesla po prvním přihlášení neměňte jen v aplikaci — zdrojem pravdy je Keychain + zapečetěný Secret v Gitu. Změnu proveďte úpravou položky v Keychain, novým během prepare-secrets.sh a commitem přepečetěných souborů.

Domény a certifikát ingressu

Názvy hostů webových služeb jsou definované v manifestech IngressRoute v adresáři kubernetes/ a HTTPS ingressu obsluhuje wildcard certifikát pro vaši doménu, který se zapečetí při přípravě tajemství. Nastavení domén a dodání certifikátu je součástí onboardingu.

Napojení na firemní identitu (LDAP / SSO) není ve výchozí instalaci zapnuté; řeší se jako integrace v rámci onboardingu.

05Běžné použití

Spuštění playbooku v Semaphore UI

  1. V projektu založte Key Store položku (SSH klíč k cílovým strojům, případně heslo Ansible Vault). Přístupové údaje ke spravovaným zařízením a systémům do Semaphore neukládejte — playbook si je načte z OpenBao (viz Tajemství).
  2. Přidejte Repository s playbooky (Git URL a větev) a Inventory s cílovými hosty.
  3. Vytvořte Task Template: vyberte playbook, inventář, repozitář a klíč. Volitelně nastavte plán (cron) pro pravidelné spouštění.
  4. Spusťte šablonu tlačítkem Run a průběh sledujte ve výpisu úlohy. Historie běhů zůstává v Semaphore.

Data přes PostgREST

Databáze obsahuje grafový model infrastruktury: tabulka nodes (zařízení, služby, certifikáty…; pole id, type, name, properties JSONB) a edges (vztahy source_id → target_id s typem, např. depends_on). Změny v obou tabulkách zapisuje trigger do audit_log.

RoleJak ji získáteOprávnění
web_anonpožadavek bez tokenučtení nodes, edges, v_graph_echarts; volání get_impact_radius
web_userJWT s claimem "role": "web_user"plný zápis do nodes, edges; čtení audit_log
# čtení
curl -s 'https://restapi.example.internal/nodes?type=eq.server&select=id,name,properties'
curl -s 'https://restapi.example.internal/edges?type=eq.depends_on'

# dopadová analýza: co všechno závisí na uzlu (průchod hran source → target, výchozí hloubka 3)
curl -s -X POST 'https://restapi.example.internal/rpc/get_impact_radius' \
  -H 'Content-Type: application/json' \
  -d '{"start_node": "<UUID uzlu>", "max_depth": 3}'

Funkce vrací řádky node_id, node_name, node_type, depth, path (pole UUID); cykly v grafu se přeskakují.

Pro zápis podepište JWT algoritmem HS256 klíčem z Keychain položky pampeliska-pgrst-jwt. Příklad bez externích knihoven:

TOKEN=$(SECRET="$(security find-generic-password -s pampeliska-pgrst-jwt -w)" python3 - <<'EOF'
import base64, hashlib, hmac, json, os, time
b64 = lambda b: base64.urlsafe_b64encode(b).rstrip(b"=").decode()
h = b64(json.dumps({"alg": "HS256", "typ": "JWT"}).encode())
p = b64(json.dumps({"role": "web_user", "exp": int(time.time()) + 3600}).encode())
s = b64(hmac.new(os.environ["SECRET"].encode(), f"{h}.{p}".encode(), hashlib.sha256).digest())
print(f"{h}.{p}.{s}")
EOF
)

curl -s -X POST 'https://restapi.example.internal/nodes' \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -H 'Prefer: return=representation' \
  -d '{"type": "server", "name": "app-01", "properties": {"criticality": "high"}}'

Tokeny vydávejte s krátkou platností (exp) a podpisový klíč nesdílejte mimo administrátory.

Grafana dashboardy

Kromě standardních dashboardů Kubernetes z kube-prometheus-stack platforma obsahuje:

  • Certificate Expiry — čas do expirace certifikátů v clusteru (z cert-manageru) a tabulka externích certifikátů evidovaných v databázi (tabulka certificates).
  • Certificate Chain — strom řetězce interní PKI; koncové certifikáty načítá živě z tabulky certificates.
  • ICT Topology, Dependency Impact Map a Firewall toky (Sankey) — ukázkové dashboardy nad demo daty: čtou statické tabulky viz_topology_nodes/viz_topology_edges, viz_dependencies a viz_firewall_flows, které naplní Job infra-viz-seed (kubernetes/apps/monitoring/infra-viz/). S grafovým modelem nodes/edges zatím propojené nejsou; napojení na vaše data je součástí onboardingu. Dopadovou analýzu nad nodes/edges nabízí funkce get_impact_radius v REST API (viz výše).

Alerty

Alertmanager posílá alerty přes most ntfy-alertmanager do ntfy, topic alerts. ntfy běží s výchozím přístupem deny-all — anonymní čtení ani zápis nejsou povolené. Uživatelé a tokeny jsou deklarativní ze Secretu ntfy-auth, který vytváří prepare-secrets.sh:

  • admin — čte všechny topicy; heslo v Keychain pampeliska-ntfy-admin. Pro odběr alertů se v aplikaci ntfy nebo na webu přihlaste tímto účtem a přihlaste se k topicu alerts.
  • alertmanager — servisní účet mostu; smí jen zapisovat do topicu alerts.

Alert na certifikáty hlásí zpožděnou obnovu (CertManagerCertRenewalOverdue — cert-manager měl certifikát obnovit před více než hodinou), ne „brzy vyprší“; vypršelý certifikát hlásí CertManagerCertExpired.

Účty ntfy jsou spravované deklarativně — další účet přidejte do scripts/prepare-secrets.sh (seznam users pro Secret ntfy-auth) a jeho oprávnění do proměnné NTFY_AUTH_ACCESS v kubernetes/apps/monitoring/ntfy/deployment.yaml (např. jmeno:alerts:ro). Účty přidané ručně příkazem ntfy user nemusí restart přežít.

06Certifikáty

Interní certifikáty vydává step-ca. Pro workloady v clusteru je žádá a obnovuje cert-manager přes step-issuer a issuer StepClusterIssuer s názvem step-ca-cluster-issuer.

Issuer není uložený v Gitu: odvozuje ho z běžící CA CronJob step-issuer-reconcile (namespace pki, každou hodinu). Po první instalaci nebo po reinicializaci CA proto může trvat až hodinu, než issuer existuje. Ruční spuštění:

kubectl -n pki create job --from=cronjob/step-issuer-reconcile step-issuer-reconcile-manual
kubectl get stepclusterissuer step-ca-cluster-issuer

Vystavení certifikátu

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: moje-aplikace
  namespace: moje-aplikace
spec:
  secretName: moje-aplikace-tls
  dnsNames:
    - app.example.internal
  duration: 24h         # platnost (výchozí krátká platnost)
  renewBefore: 8h       # obnova s předstihem
  issuerRef:
    group: certmanager.step.sm
    kind: StepClusterIssuer
    name: step-ca-cluster-issuer

Manifest commitněte do Gitu (viz GitOps). cert-manager uloží certifikát a klíč do Secretu secretName a obnovuje ho automaticky před vypršením. Stav:

kubectl get certificate -A
kubectl describe certificate moje-aplikace -n moje-aplikace

Expiraci hlídá alert v Prometheu (PrometheusRule cert-manager-expiry) a dashboard Certificate Expiry.

Důvěra v interní CA

  • Workloady v clusteru — trust-manager vytváří v každém namespace s labelem app.kubernetes.io/part-of: pampeliska ConfigMap pampeliska-trust-bundle (klíč root-ca.crt); připojte ji do trust storu aplikace.
  • Uzly clusteru — veřejný kořenový certifikát uložte do ansible/roles/pki_trust/files/pampeliska-root-ca.crt a spusťte ansible-playbook ansible/playbooks/pki-trust.yml.
  • Pracovní stanice administrátorů — kořenový certifikát importujte ručně do systémového úložiště důvěry.

Externí zařízení a servery

Pro servery mimo cluster slouží playbook ansible/playbooks/cert-renewal.yml, spouštěný plánovaně ze Semaphore UI. Certifikát obnoví jen tehdy, když zbývající platnost klesne pod práh (renew_before_minutes), a webový server poté gracefully reloaduje. Na cílovém hostu musí být step CLI s bootstrapnutou CA a soubor s heslem JWK provisioneru (/etc/step/provisioner.pwd, práva 0600). Playbook je dnes připravený pro Apache; domény, cesty k certifikátům a platnost se nastavují proměnnými playbooku.

Certifikát ingressu

Wildcard certifikát, kterým Traefik obsluhuje webová rozhraní, se neobnovuje automaticky. Před jeho expirací nahraďte materiál, spusťte scripts/prepare-secrets.sh (přepečetí Secret) a změnu commitněte.

Revokace: step ca revoke zablokuje jen obnovu certifikátu. CRL/OCSP nasazené není — revokaci nahrazuje krátká platnost (výchozí 24 h); u certifikátů s delší platností počítejte s tím, že je nelze odvolat.

07Změny konfigurace a GitOps

Zdrojem pravdy pro stav clusteru je Git. Každá změna jde cestou: úprava manifestu → podepsaný commit → push → Flux reconcile. Ruční kubectl apply používejte jen pro diagnostiku — Flux ruční změny při dalším reconcile přepíše.

git config commit.gpgsign true              # jednou
git add kubernetes/apps/...
git commit -S -m "feat: ..."
git push

flux reconcile source git pampeliska        # nečekat na interval
flux get kustomizations -A
Podpis commitů je povinný

Flux ověřuje GPG podpis posledního commitu (verify.mode: HEAD) proti veřejným klíčům v Secretu flux-gpg-public-keys. Nepodepsaný commit nebo commit podepsaný neznámým klíčem způsobí, že zdroj zůstane NotReady a žádné změny se nenasadí.

Tajemství v Gitu

Do Gitu patří jen zapečetěná podoba Secretu:

kubectl create secret generic moje-tajemstvi -n moje-aplikace \
  --from-literal=heslo='...' --dry-run=client -o yaml \
  | ./scripts/seal-secret.sh > moje-tajemstvi-sealed.yaml

Tajemství pro provisioning (token K3s, šifrovací klíč, přístup k S3) jsou v Ansible Vault: ansible-vault edit ansible/inventory/group_vars/all/vault.yml.

Přístupové údaje ke spravovaným zařízením a systémům do Gitu nepatří ani zapečetěné — ukládejte je do OpenBao (viz Tajemství).

08Tajemství (OpenBao)

OpenBao je centrální úložiště tajemství pro automatizaci: přístupové údaje ke spravovaným zařízením a systémům (síťové prvky, firewally, servery, API tokeny), které používají Ansible playbooky spouštěné ze Semaphore UI. Tajemství nejsou v Gitu ani v Semaphore.

  • Vysoká dostupnost: 3 repliky (jedna na každém uzlu) s integrovaným úložištěm Raft. Při výpadku uzlu proběhne volba lídra a služba pokračuje po krátkém přepnutí v řádu sekund.
  • Automatické odpečetění: statický klíč (seal "static") uložený jako Secret openbao-unseal v namespace openbao (zapečetěný Sealed Secrets, kopie v Keychain pampeliska-openbao-unseal). Po restartu se OpenBao odpečetí samo.
  • Deklarativní inicializace: při prvním startu se OpenBao samo inicializuje z konfigurace v Gitu — bez ručního bao operator init, bez ručního odpečeťování a bez trvalého root tokenu (po inicializaci se revokuje). Zapne audit, úložiště KV v2 secret/, ověřování přes Kubernetes ServiceAccount a nouzový účet admin.
  • Politiky a role jako kód: ACL politiky a role přístupu jsou v Gitu; CronJob openbao-config-reconcile je každou hodinu aplikuje a ruční odchylky vrací do stavu v Gitu.
  • Nejmenší oprávnění: Semaphore UI smí tajemství pod secret/automation/* pouze číst. Přihlašuje se krátkodobým tokenem svého Kubernetes ServiceAccountu (audience openbao) — žádný statický token.
  • Audit a metriky: každý přístup k tajemství zapisuje audit device na stdout; Grafana Alloy ho předává do Loki (v Grafaně dotaz {namespace="openbao"}). Metriky sbírá Prometheus (ServiceMonitor).
  • Síť: do OpenBao smí podle NetworkPolicy jen Traefik, Semaphore UI, namespace monitoring a pody OpenBao samotné.
  • Ochrana účtů: po 5 chybných přihlášeních metodou Username se účet na 15 minut zablokuje (user_lockout).

Přihlášení

Webové UI i API jsou na https://bao.example.internal (HTTPS přes Traefik), dostupné jen ze správcovských sítí — z jiné adresy vrátí Traefik 403; povolené rozsahy jsou v kubernetes/infrastructure/openbao/allowlist.yaml. Přihlaste se metodou Username účtem admin; heslo je v Keychain řídicího stroje:

security find-generic-password -s pampeliska-openbao-admin -w

Z příkazové řádky:

export BAO_ADDR=https://bao.example.internal
bao login -method=userpass username=admin
# token pro další nástroje (např. lookup v playboocích spouštěných z pracovní stanice)
export BAO_TOKEN=$(bao login -method=userpass -token-only username=admin)

Pokud certifikát ingressu nevydala CA, které systém důvěřuje, nastavte i BAO_CACERT na soubor s kořenovým certifikátem. Účet admin je nouzový a administrátorský — pro aplikace používejte vlastní role s ověřováním přes ServiceAccount (viz níže).

Uložení tajemství

V UI: Secrets Engines → secret/ → Create secret, cesta např. automation/cisco/core1. Z příkazové řádky:

bao kv put secret/automation/cisco/core1 username=netadmin password=-   # heslo zadáte na stdin, nezůstane v historii shellu
bao kv get secret/automation/cisco/core1
bao kv metadata get secret/automation/cisco/core1                        # verze (KV v2 drží historii)

Tajemství pro automatizaci ukládejte pod secret/automation/ — na tuto cestu má Semaphore UI právo čtení.

Použití v playbooku

Lookup plugin openbao je součástí repozitáře platformy a nepotřebuje žádné další Python závislosti. Cesta je relativní k úložišti secret/, parametr field vybírá položku:

- hosts: core_switches
  gather_facts: false
  vars:
    ansible_user: "{{ lookup('openbao', 'automation/cisco/core1', field='username') }}"
    ansible_password: "{{ lookup('openbao', 'automation/cisco/core1', field='password') }}"
  tasks:
    - name: Ověření spojení
      ansible.netcommon.cli_command:
        command: show version
  • Ze Semaphore UI se lookup přihlásí sám tokenem ServiceAccountu Semaphore — v šabloně úlohy nic nenastavujete.
  • Z pracovní stanice nastavte BAO_ADDR a BAO_TOKEN (viz Přihlášení).
  • Ve vlastním repozitáři playbooků zkopírujte plugin do adresáře lookup_plugins/ vedle playbooku.

Přístup pro další aplikaci

Politiky a role jsou soubory v Gitu v adresáři kubernetes/infrastructure/openbao/config/. Pro novou aplikaci přidejte dvojici souborů.

Politika policy-moje-aplikace.hcl — jen čtení vlastní větve tajemství (KV v2: cesta obsahuje data/):

path "secret/data/automation/moje-aplikace/*" {
  capabilities = ["read"]
}

Role role-moje-aplikace.json — role ověřování přes Kubernetes ServiceAccount (příklad):

{
  "bound_service_account_names": ["moje-aplikace"],
  "bound_service_account_namespaces": ["moje-aplikace"],
  "audience": "openbao",
  "token_policies": ["moje-aplikace"],
  "token_ttl": "15m"
}

Politiky a role spravujte jen v Gitu — reconciler smaže každou politiku nebo roli, která v Gitu není (kromě vestavěných default a root), takže ruční změny v UI se do hodiny ztratí.

Změnu commitněte (podepsaný commit, viz GitOps). CronJob openbao-config-reconcile ji aplikuje nejpozději do hodiny; okamžitě takto:

kubectl -n openbao create job --from=cronjob/openbao-config-reconcile rucni-$(date +%s)
kubectl -n openbao get jobs

Běží-li aplikace v clusteru, musí ji do OpenBao pustit i NetworkPolicy — rozšiřte ji v manifestech kubernetes/infrastructure/openbao/. Aplikace se přihlašuje tokenem svého ServiceAccountu s audience openbao (projected token).

Zálohy a obnova

  • Svazky Raft zálohuje denně Longhorn do S3 mimo cluster (stejně jako ostatní svazky, viz Zálohy a obnova). Svazky jsou lokální (Longhorn 1 replika, strict-local) — data mezi uzly replikuje Raft.
  • Konzistentní snapshot pořiďte ručně účtem admin, nejlépe před upgradem a po větších změnách:
bao operator raft snapshot save openbao-$(date +%F).snap
# obnova (přepíše aktuální data OpenBao)
bao operator raft snapshot restore openbao-2026-10-01.snap
# do nově inicializovaného clusteru (jiná sada klíčů) je potřeba -force
bao operator raft snapshot restore -force openbao-2026-10-01.snap
Klíč pro odpečetění

Data OpenBao (svazky i snapshoty) jsou šifrovaná. Bez klíče pro odpečetění (pampeliska-openbao-unseal v Keychain, kopie v trezoru mimo cluster) je nelze obnovit. Před obnovou do nového clusteru musí Secret openbao-unseal obsahovat původní klíč. Při obnově přes ./deploy.sh --skip-secrets (viz DR) to platí automaticky — zapečetěný Secret je v Gitu a obnovený sealing klíč ho rozšifruje. Pokud tajemství přegenerováváte během prepare-secrets.sh, vraťte nejdřív do Keychain původní položku pampeliska-openbao-unseal.

Upgrade

StatefulSet OpenBao používá strategii OnDelete: po změně verze v manifestu a reconcile Fluxem se pody nerestartují samy. Restartujte je ručně po jednom — nejdřív repliky ve stavu standby, nakonec aktivní:

# který pod je aktivní (HA Mode: active / standby)
for i in 0 1 2; do echo "openbao-$i"; kubectl -n openbao exec openbao-$i -- bao status | grep -E 'HA Mode|Version'; done

kubectl -n openbao delete pod openbao-1          # standby; počkejte na Ready a Sealed=false
kubectl -n openbao wait --for=condition=Ready pod/openbao-1 --timeout=5m
# … druhý standby stejně, nakonec aktivní pod (proběhne volba lídra)

Před upgradem pořiďte snapshot Raft (viz výše).

Omezení

  • TLS uvnitř clusteru: OpenBao zatím nemá vlastní TLS listener. HTTPS končí na Traefiku; provoz mezi uzly šifruje WireGuard, uvnitř uzlu ale provoz mezi Traefikem/Semaphore a OpenBao šifrovaný není. TLS uvnitř clusteru je na Roadmapě.
  • Klíč pro odpečetění je v clusteru: statický klíč leží jako Kubernetes Secret, takže kdo získá přístup k Secretům v namespace openbao a ke svazkům, může data rozšifrovat. Pro regulované prostředí doporučujeme nahradit statický klíč HSM (PKCS#11), KMS nebo zákaznickým trezorem — jde o konfigurační volbu OpenBao, kterou platforma sama nenastavuje; lze ji připravit v rámci onboardingu.
  • Dynamické krátkodobé přístupové údaje, automatické snapshoty Raft do S3 a synchronizace tajemství do Kubernetes (External Secrets Operator) zatím nejsou — viz Roadmapa.
  • Vztah k Sealed Secrets: Sealed Secrets zůstávají pro tajemství platformy potřebná už při bootstrapu (hesla databází, přístupy k S3); OpenBao je pro provozní tajemství automatizace. Zákaznický PAM (např. CyberArk) lze napojit jako integraci na zakázku.

09Zálohy a obnova

CoJakKamRetence
PostgreSQLprůběžná archivace WAL + denní base backup ve 02:00 (CloudNativePG)S3, bucket pampeliska-cnpg14 dní
Svazky Longhornhodinové snapshoty (lokálně) + denní záloha ve 02:30snapshoty v clusteru; zálohy S3, bucket longhorn-backup24 snapshotů / 14 záloh
etcd (stav Kubernetes)automatické snapshoty každých 6 h; ruční ./scripts/backup-etcd.sh <IP server uzlu> [ssh uživatel]lokálně na uzlech + S3, bucket k3s-etcd; ruční do backups/etcd/ na řídicím strojilokálně 28 na uzel (7 dní); v S3 84 za cluster (počet serverů × 28)
OpenBao (Raft)denní záloha svazků Longhorn ve 02:30; ruční bao operator raft snapshot saveS3, bucket longhorn-backup; snapshot na řídicím strojijako svazky Longhorn
Klíč Sealed Secretsprepare-secrets.shmacOS Keychain pampeliska-sealing-keytrvale
Klíče step-ca./scripts/backup-pki-keys.shmacOS Keychain pampeliska-step-ca-backuptrvale
Klíč pro odpečetění OpenBaoprepare-secrets.shmacOS Keychain pampeliska-openbao-unsealtrvale
Heslo účtu admin OpenBaoprepare-secrets.shmacOS Keychain pampeliska-openbao-admintrvale
Konfigurace platformyGitrepozitář (in-cluster Gitea + autoritativní kopie mimo cluster)historie Gitu

Zálohovací cíl S3

  • Referenční cíl je Garage (S3 API) ve VM na samostatném hostiteli, s TLS na portu 9000. Zakládá ho ansible/playbooks/backup-s3.yml — deploy.sh ho spouští automaticky před nasazením clusteru.
  • Každý klient má vlastní přístupový klíč jen na svůj bucket: PostgreSQL → pampeliska-cnpg, Longhorn → longhorn-backup, etcd → k3s-etcd. Klíče generuje prepare-secrets.sh.
  • Kopie mimo dosah clusteru: hostitel pořizuje denně snapshot celé VM se zálohami (expirace 14 dní). Snapshoty nejsou dostupné přes S3 klíče z clusteru, takže je nesmaže ani kompromitovaný cluster; neměnné nejsou — správce hostitele je smazat může. Pro skutečně neměnnou kopii doporučujeme offsite S3 s Object Lock. Obnova: lxc copy garage-pampeliska/<snapshot> garage-restore (nebo lxc restore).

Obnova PostgreSQL k okamžiku (PITR)

CloudNativePG obnovuje do nového clusteru z base backupu a WAL archivu. Vytvořte manifest s bootstrap.recovery a cílovým časem:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pampeliska-db-restore
  namespace: postgres-system
spec:
  instances: 3
  imageName: ghcr.io/cloudnative-pg/postgresql:16.15  # stejná major verze jako záloha
  storage:
    storageClass: longhorn
    size: 10Gi
  walStorage:
    storageClass: longhorn
    size: 2Gi
  bootstrap:
    recovery:
      source: pampeliska-db
      recoveryTarget:
        targetTime: "2026-10-01 08:00:00.00000+02"
  externalClusters:
    - name: pampeliska-db              # = název původního clusteru (adresář v bucketu)
      barmanObjectStore:
        destinationPath: s3://pampeliska-cnpg/
        endpointURL: https://s3.example.internal:9000
        endpointCA:
          name: backup-s3-ca
          key: ca.crt
        s3Credentials:
          accessKeyId:
            name: backup-s3-cnpg
            key: ACCESS_KEY_ID
          secretAccessKey:
            name: backup-s3-cnpg
            key: ACCESS_SECRET_KEY
        wal:
          maxParallel: 8
  • In-tree barmanObjectStore je od CNPG 1.26 zastaralé a v 1.31 bude odstraněno; platforma plánuje přechod na Barman Cloud Plugin — příklad odpovídá současné verzi.
  • Obnovený cluster nesmí zapisovat zálohy do stejné cesty jako původní — sekci backup buď vynechte, nebo použijte jinou destinationPath.
  • Bez recoveryTarget se obnoví k poslednímu dostupnému WAL.
  • Průběh: kubectl -n postgres-system get cluster pampeliska-db-restore a logy podů *-full-recovery-*. Po ověření dat přepněte aplikace (connection string / služba -rw) na nový cluster. Výměna produkčního clusteru je provozní zásah — naplánujte ji.

Obnova svazku Longhorn

  1. Otevřete Longhorn UI: kubectl -n longhorn-system port-forward svc/longhorn-frontend 8080:80 → http://localhost:8080.
  2. V záložce Backup vyberte svazek a zálohu a zvolte Restore (nový svazek).
  3. U obnoveného svazku vytvořte PV/PVC (Create PV/PVC) se jménem, které čeká aplikace, a aplikaci restartujte (škálování na 0 a zpět).

Pro návrat v rámci posledních 24 hodin stačí lokální snapshot (záložka svazku → Snapshots → Revert; svazek musí být odpojený).

Obnova etcd ze snapshotu

Provozní zásah

Obnova etcd vrací celý stav Kubernetes do okamžiku snapshotu. Proveďte ji jen při ztrátě nebo poškození řídicí vrstvy, v okně údržby.

# 1) na VŠECH třech uzlech
sudo systemctl stop k3s

# 2) na prvním uzlu (snapshot z /var/lib/rancher/k3s/server/db/snapshots/ nebo stažený z S3)
sudo k3s server --cluster-reset \
  --cluster-reset-restore-path=/var/lib/rancher/k3s/server/db/snapshots/<snapshot>
sudo systemctl start k3s

# 3) na ostatních dvou uzlech: smazat lokální data etcd a připojit znovu
sudo rm -rf /var/lib/rancher/k3s/server/db
sudo systemctl start k3s

Po obnově ověřte kubectl get nodes a flux get all -A; Flux dorovná případné změny z Gitu provedené po pořízení snapshotu.

Klíče a tajemství

  • Klíč Sealed Secrets vzniká na řídicím stroji při běhu prepare-secrets.sh a je jen v Keychain (pampeliska-sealing-key), žádná souborová kopie na disku. Při bootstrapu nového clusteru ho bootstrap.sh obnoví do clusteru dřív, než Flux začne aplikovat SealedSecrets.
  • Klíče step-ca (root i intermediate klíč, hesla, certifikáty, konfigurace) exportuje ./scripts/backup-pki-keys.sh do Keychain (pampeliska-step-ca-backup). Export spusťte po instalaci a po každé změně PKI. Obnova až po prvním nasazení step-ca: instalace Helm chartu generuje novou CA a objekty obnovené předem by přepsala. Po nasazení (flux get hr -n pki step-certificates = Ready) export aplikujte (security find-generic-password -s pampeliska-step-ca-backup -w | base64 -d | kubectl apply -f -), restartujte kubectl -n pki rollout restart statefulset/step-certificates a ověřte otisk kořenového certifikátu.
  • Klíč pro odpečetění OpenBao (pampeliska-openbao-unseal) a heslo nouzového účtu admin (pampeliska-openbao-admin) vygeneroval prepare-secrets.sh do Keychain. Bez klíče pro odpečetění nejdou data OpenBao obnovit — ani ze záloh svazků, ani ze snapshotu Raft. Viz Tajemství.
  • Kopii všech těchto položek a souboru ansible/vault_password uložte do trezoru mimo cluster i mimo řídicí stroj. Ztráta stroje se zálohou = ztráta možnosti obnovy.

Havárie celého clusteru (DR)

  1. Připravte tři nové VM dle požadavků a upravte inventář.
  2. Obnovte položky Keychain (sealing key, klíč pro odpečetění OpenBao, hesla) a GPG podpisový klíč z trezoru a spusťte ./deploy.sh --skip-secrets — cluster vznikne z Gitu včetně všech komponent a tajemství.
  3. Obnovte data: PostgreSQL z S3 (PITR výše), svazky Longhorn ze záloh, klíče step-ca z exportu (až po nasazení step-ca, viz výše), data OpenBao ze snapshotu Raft nebo ze záloh svazků (viz Tajemství).
  4. Ověřte funkčnost služeb a přihlášení.
Doporučení

Obnovu testujte nejméně jednou za čtvrtletí na odděleném prostředí — záloha, ze které se nikdy neobnovovalo, není ověřená záloha.

10Vysoká dostupnost

Cluster přežije výpadek jednoho ze tří uzlů. Ne všechny služby jsou ale replikované — jednoinstanční služby se při výpadku uzlu přeplánují na jiný uzel, což znamená výpadek v řádu minut. Výjimkou je Gitea na lokálním úložišti uzlu, která čeká na návrat svého uzlu.

SlužbaRežimChování při výpadku uzlu
Řídicí vrstva K3s (etcd)3 uzlyetcd kvórum 2 ze 3; API dostupné přes VIP
PostgreSQL (CloudNativePG)3 instanceprimár + synchronní replika + další replika; automatický failover
Traefik (ingress)2 replikyprovoz pokračuje přes druhou repliku
PostgREST2 replikyprovoz pokračuje přes druhou repliku
OpenBao3 repliky (Raft)volba lídra, krátké přepnutí v řádu sekund; svazky lokální (Longhorn 1 replika, strict-local), data replikuje Raft
Data Longhorn2 replikysvazek dostupný z druhé repliky; svazky PostgreSQL mají 1 lokální repliku (třída longhorn-db, strict-local) — redundanci zajišťují 3 instance CloudNativePG
Semaphore UI (Community)1 instancepřeplánování, výpadek v řádu minut
Gitea (zdroj pro Flux)1 instance, lokální úložiště uzlunedostupná do návratu uzlu (data na local-path, nepřeplánuje se); běžící workloady pokračují, nové změny se nenasazují
Grafana, Prometheus, Alertmanager, Loki1 instancepřeplánování, výpadek v řádu minut
step-ca, cert-manager1 instancepřeplánování; existující certifikáty platí dál, vydávání a obnova se pozdrží
ntfy1 instancepřeplánování, výpadek v řádu minut

Loki: výchozí retence logů je 30 dní na lokálním svazku. Delší retence vyžaduje objektové úložiště a dimenzování v rámci onboardingu — např. 18 měsíců, které pro záznamy událostí požaduje vyhláška č. 409/2025 Sb., § 22 odst. 5 písm. c) (subjekty režimu vyšších povinností); ISO 27001 A.8.15 a DORA (RTS (EU) 2024/1774, čl. 12) kladou požadavky na samotné logování.

11Upgrade a lifecycle politika

Upgrade K3s

ANSIBLE_CONFIG=ansible/ansible.cfg ansible-playbook \
  -i ansible/inventory/hosts.yml ansible/playbooks/upgrade-k3s.yml \
  --vault-password-file ansible/vault_password \
  -e k3s_target_version=vX.Y.Z+k3sN

Playbook nejprve pořídí snapshot etcd a pak upgraduje uzly postupně po jednom: cordon → drain → upgrade → čekání na Ready a správnou verzi → uncordon → čekání, až jsou všechny svazky Longhorn zdravé a všechny instance PostgreSQL připravené; teprve pak pokračuje dalším uzlem. Kubernetes API volá přes jiný uzel, takže selhání upgradovaného uzlu nezablokuje diagnostiku.

  • Upgradujte jen o jednu minor verzi najednou (1.30 → 1.31 → 1.32 …).
  • Před upgradem ověřte kompatibilitu Helm chartů komponent s cílovou verzí Kubernetes. Během drainu uzlu s interní Gitea (zdroj pro Flux) Flux dočasně nereconciluje.
  • Po upgradu: kubectl get nodes (verze všech uzlů) a flux get helmreleases -A.

Verze komponent v clusteru (Helm charty, image) jsou pinnuté v manifestech v Gitu; upgrade komponenty = změna verze v manifestu → podepsaný commit → Flux.

Flux nasazuje z Gitea uvnitř clusteru. Na MicroCloudu se nový podepsaný commit do in-cluster Gitea dostane opakovaným seedem START_AT=seed_gitea ./scripts/bootstrap-microcloud.sh deploy — bez něj cluster nové commity nevidí (viz Flux nevidí nový commit).

Lifecycle politika

OblastPravidlo
K3sProvozujeme řadu podporovanou upstreamem (jedna z posledních tří minor verzí). Upgrade minor verze nejméně jednou za čtvrtletí. Bezpečnostní patch verze do 30 dní od vydání.
Operační systémUbuntu Server LTS (24.04) po dobu standardní podpory.
KomponentyPinnuté verze Helm chartů a image; revize při každém upgradu platformy.
Okno údržbyDoporučeno neděle 02:00–06:00 (Europe/Prague).

12Řešení problémů

Flux nereconciluje změny

flux get sources git -A
flux get kustomizations -A
flux logs --level=error --since=1h

Nejčastější příčiny: nepodepsaný commit (zdroj NotReady, chyba ověření podpisu), nedostupná in-cluster Gitea, chyba v manifestu (kustomize build). Lokální kontrola: kustomize build kubernetes/infrastructure. Po opravě: flux reconcile source git pampeliska.

Flux nevidí nový commit

Flux čte manifesty z Gitea uvnitř clusteru, ne z vašeho vzdáleného repozitáře. Nový commit, který jste pushnuli jinam, cluster neuvidí, dokud se větev znovu nenahraje do in-cluster Gitea. Na MicroCloudu to udělá opakovaný seed (bez nového provisioningu):

START_AT=seed_gitea ./scripts/bootstrap-microcloud.sh deploy
flux get sources git -A          # revize musí odpovídat podepsanému HEAD

HelmRelease ve stavu failed

flux get helmreleases -A
kubectl describe helmrelease <název> -n <namespace>
flux suspend helmrelease <název> -n <namespace>    # po opravě hodnot v Gitu:
flux resume helmrelease <název> -n <namespace>

Pod visí v Pending kvůli PVC / Longhorn

kubectl describe pod <pod> -n <namespace>          # Events: FailedScheduling / FailedAttachVolume
kubectl get pvc -A
kubectl -n longhorn-system get volumes.longhorn.io
kubectl -n longhorn-system get nodes.longhorn.io

Zkontrolujte volné místo na disku uzlů (/var/lib/longhorn) a zda jsou všechny uzly v Longhornu Ready a schedulable. Při dvou replikách a jednom nedostupném uzlu jede svazek v degradovaném režimu, dokud se uzel nevrátí.

Certifikát se nevystavuje nebo neobnovuje

kubectl get certificate,certificaterequest -A
kubectl describe certificaterequest <název> -n <namespace>
kubectl get stepclusterissuer step-ca-cluster-issuer -o wide
kubectl -n pki get cronjob step-issuer-reconcile
kubectl -n pki logs job/<poslední job step-issuer-reconcile>

Chybí-li issuer nebo není Ready, spusťte reconcile ručně (viz Certifikáty). Ověřte, že běží step-ca: kubectl exec -n pki step-certificates-0 -- step ca provisioner list.

Failover PostgreSQL (CNPG)

kubectl -n postgres-system get cluster pampeliska-db
kubectl -n postgres-system get pods -l cnpg.io/cluster=pampeliska-db -L role
kubectl -n postgres-system describe cluster pampeliska-db

Po výpadku primáru operátor povýší repliku automaticky a služba pampeliska-db-rw začne ukazovat na nový primár. Pokud cluster nepřejde do Cluster in healthy state, zkontrolujte události a logy operátoru (operátor běží v namespace postgres-system: kubectl -n postgres-system logs deploy/cloudnative-pg). Ruční zásahy do CNPG clusteru plánujte jako provozní zásah.

SealedSecret nejde rozšifrovat

kubectl get sealedsecrets -A
kubectl describe sealedsecret <název> -n <namespace>      # no key could decrypt secret
kubectl -n kube-system get secret -l sealedsecrets.bitnami.com/sealed-secrets-key

Příčinou bývá, že controller nemá původní sealing klíč (nový cluster bez obnovy klíče). Sealing klíč je jen v macOS Keychain (pampeliska-sealing-key); ruční obnova z kořene repozitáře:

bash -c 'source scripts/lib/secrets-common.sh && sealing_key_secret' | kubectl apply -f -
kubectl -n kube-system rollout restart deploy/sealed-secrets-controller

Hlášení already exists and is not managed by SealedSecret znamená, že v clusteru už existuje stejnojmenný Secret vytvořený jinak — controller ho nepřepíše. Smažte ho jen vědomě (controller ho vytvoří znovu ze SealedSecret).

OpenBao je sealed / pod neběží

kubectl -n openbao get pods,pvc
kubectl -n openbao get secret openbao-unseal                 # musí existovat (vzniká ze SealedSecret)
kubectl -n openbao get sealedsecret
kubectl -n openbao exec openbao-0 -- bao status              # Sealed, HA Mode, Raft
kubectl -n openbao logs openbao-0 --tail=100

Chybí-li Secret openbao-unseal, nejde rozšifrovat jeho SealedSecret — viz SealedSecret nejde rozšifrovat. Obsahuje-li jiný klíč, než kterým byla data zapečetěna (např. nový cluster s nově vygenerovaným klíčem), OpenBao se neodpečetí; obnovte původní klíč z Keychain pampeliska-openbao-unseal. Raft potřebuje kvórum 2 ze 3 replik — při výpadku dvou uzlů služba nepokračuje, dokud se aspoň jeden nevrátí.

Playbook nedostane tajemství (403)

kubectl -n openbao get cronjob openbao-config-reconcile
kubectl -n openbao get jobs --sort-by=.metadata.creationTimestamp
kubectl -n openbao logs job/<poslední job openbao-config-reconcile>
bao policy read <politika>
bao read auth/kubernetes/role/<role>

Odpověď 403 permission denied znamená, že token nemá politiku pro danou cestu. Zkontrolujte, že:

  • politika a role jsou v Gitu a reconcile job je aplikoval bez chyby (případně ho spusťte ručně, viz Tajemství),
  • cesta v politice obsahuje u KV v2 segment data/ (secret/data/automation/…) a tajemství leží tam, kam politika míří,
  • role váže správný ServiceAccount a namespace a token má audience openbao.

Odmítnuté požadavky najdete v auditním logu v Loki ({namespace="openbao"}). Nedostane-li se aplikace k OpenBao vůbec (timeout místo 403), blokuje ji NetworkPolicy.

VM nejde vytvořit — pozůstatky předchozí instalace v Cephu

Po předchozí instalaci mohou ve sdíleném úložišti Ceph zůstat osiřelé RBD image (virtual-machine_<jméno>) nebo staré datové svazky. Vytvoření VM se stejným jménem pak selže. Zkontrolujte a vědomě ukliďte:

lxc list
lxc storage volume list remote | grep pampeliska
sudo rbd ls -p <pool> | grep pampeliska        # na serveru MicroCloudu

Mazání svazků a image je nevratné — ověřte, že nepatří běžící VM.

SSH odmítá připojení po reinstalaci VM

Nová VM na stejné IP adrese má nový klíč hostitele a SSH (i Ansible) hlásí změnu identity. Smažte starý záznam na řídicím stroji:

ssh-keygen -R 192.168.10.11      # pro každý reinstalovaný uzel

Alerty nechodí do ntfy

kubectl -n monitoring get secret ntfy-auth ntfy-alertmanager
kubectl -n monitoring logs deploy/ntfy-alertmanager --tail=50     # 401/403 = chybí účet nebo token
kubectl -n monitoring logs deploy/ntfy --tail=50

ntfy běží s výchozím přístupem deny-all a účty admin a alertmanager i token mostu jsou deklarativní ze Secretu ntfy-auth (a ntfy-alertmanager), které vytváří prepare-secrets.sh. Chybí-li Secret nebo nesedí token, spusťte prepare-secrets.sh, commitněte přepečetěné SealedSecrets podepsaně a nechte je nasadit Flux. Viz Alerty.

Uzel je NotReady

kubectl get nodes -o wide
kubectl describe node <uzel>
ssh <uživatel>@192.168.10.12 'sudo systemctl status k3s; sudo journalctl -u k3s --since "30 min ago"'

13Bezpečnost a hlášení zranitelností

  • Zranitelnosti hlaste e-mailem na support@pampeliska.io s předmětem „Security“. Kontaktní údaje jsou také v security.txt.
  • Postupujeme podle principu koordinovaného zveřejnění: nález nezveřejňujte, dokud není k dispozici oprava nebo dohodnutý termín.
  • Podrobnosti postupu jsou v souboru SECURITY.md v repozitáři platformy.

Bezpečnostní opatření platformy

  • NetworkPolicy (default-deny pro příchozí provoz) v namespaces postgres-system, gitea, semaphore, flux-system, pki, monitoring, longhorn-system a openbao (kubernetes/infra-config/network-policies/); povolené jsou jen vyjmenované toky.
  • Pod Security Admission na úrovni baseline pro namespaces platformy; výjimku privileged mají jen longhorn-system (Longhorn potřebuje přístup k disku uzlu) a gitea (CI runner s Docker-in-Docker). Definice: kubernetes/infrastructure/namespaces/namespaces.yaml.
  • Uzly: CIS hardening OS (role hardening: SSH, auditd, AppArmor) i kubeletu (protect-kernel-defaults), hostový firewall ufw s výchozím zákazem příchozího provozu (role firewall), šifrování Secrets v etcd.
  • OpenBao je dostupné jen ze správcovských sítí (allowlist v Traefiku) a blokuje účet po opakovaném chybném přihlášení — viz Tajemství.
Riziko: privilegovaný CI runner

CI runner Gitea Actions (kubernetes/apps/gitea-runner/) dnes běží v namespace gitea jako pod s privilegovaným kontejnerem Docker-in-Docker. Privilegovaný kontejner má prakticky plný přístup k uzlu: kdo ovládne CI úlohu (např. přes škodlivý workflow nebo závislost) a unikne z kontejneru, získá roota na uzlu clusteru. Spouštějte proto jen důvěryhodné workflow z vlastních repozitářů. Přesun runneru mimo cluster do samostatné VM je plánovaný — viz Roadmapa.

Audit Kubernetes API

K3s zapisuje auditní log kube-apiserveru podle politiky ansible/roles/k3s/templates/audit-policy.yaml.j2 do /var/log/kubernetes/audit/audit.log na každém serverovém uzlu. Grafana Alloy ho předává do Loki. V Grafaně: Explore → Loki a dotaz:

{job="k3s-audit"}
# např. jen zásahy do Secretů
{job="k3s-audit"} |= `"resource":"secrets"`
  • Co se loguje (úroveň Metadata — kdo, co, kdy, nad jakým objektem a s jakým výsledkem): všechny změny objektů (create, update, patch, delete), exec/attach, změny RBAC a každý přístup k Secretům, ConfigMapám, tokenům ServiceAccountů a SealedSecrets — i při čtení. Obsah požadavků a odpovědí se neukládá, hodnoty Secretů ani tokeny se do logu nedostanou.
  • Co se neloguje: běžná čtení ostatních objektů (get, list, watch), health a metrické endpointy (/healthz, /readyz, /livez, /metrics …), obnovy leases a události (events).
  • Retence je stejná jako u ostatních logů v Loki (výchozí 30 dní, viz Vysoká dostupnost); export do SIEM je katalogová integrace.

Doporučení pro provoz

  • Výsledky Trivy Operatoru (zranitelnosti, konfigurace, SBOM) procházejte pravidelně: kubectl get vulnerabilityreports,configauditreports,sbomreports -A.
  • Přístupové údaje ke spravovaným zařízením ukládejte do OpenBao, ne do Gitu ani do Key Store Semaphore; účet admin OpenBao používejte jen pro správu a nouzové situace.
  • Tajemství nikdy necommitujte nezapečetěná; ansible/vault_password, kubeconfig a zálohy klíčů držte mimo Git.
  • Patch verze K3s a komponent nasazujte dle lifecycle politiky.