From 2c5894d70bde2fa08fc214291c389317b77ac5fd Mon Sep 17 00:00:00 2001 From: oskar Date: Tue, 4 Aug 2026 17:59:05 +0200 Subject: [PATCH] feat(kb-site): serwis nginx na PIHA + manifesty (wzorzec narty27) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit services/kb-site/ — nginx:alpine serwujacy wolumen kb-site_content (:ro, /usr/share/nginx/html), pelny layout z CLAUDE.md: docker-compose.yml, service.yaml, README-wskaznik, env.example (swiadomie pusty — brak sekretow), healthcheck.sh. Wpis w hosts/piha/services.yaml. Port hosta 8250, NIE 8240 z opisu zadania: 8240 jest juz zajete przez narty27 (services/narty27/docker-compose.yml). Blok statyczny PIHA to 8210 paperless, 8220 nextcloud, 8230 kb-query, 8240 narty27 -> 8250 to nastepny wolny. Uzgodnione z operatorem. exposure: public — w odroznieniu od narty27 ta wystawka ma byc dostepna z internetu przez vhost npm@PIHA kb.okit.pl; bind :8250 jest upstreamem proxy, nie punktem wejscia. Tresc jest czystym artefaktem repo (wyjscie scripts/kb/gen_pages.py), zyje wylacznie w wolumenie kb-site_kb-site_content — bez binda pod /opt/homelab/data i bez zadania backupu: odtworzeniem jest regeneracja z kb/. Sprawdzone lokalnie: docker compose config -q, bash -n healthcheck.sh, yaml.safe_load na obu manifestach. --- hosts/piha/services.yaml | 20 +++++++++++++++++ services/kb-site/README.md | 5 +++++ services/kb-site/docker-compose.yml | 28 ++++++++++++++++++++++++ services/kb-site/env.example | 10 +++++++++ services/kb-site/healthcheck.sh | 21 ++++++++++++++++++ services/kb-site/service.yaml | 33 +++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+) create mode 100644 services/kb-site/README.md create mode 100644 services/kb-site/docker-compose.yml create mode 100644 services/kb-site/env.example create mode 100755 services/kb-site/healthcheck.sh create mode 100644 services/kb-site/service.yaml diff --git a/hosts/piha/services.yaml b/hosts/piha/services.yaml index 74f6c11..1865d47 100644 --- a/hosts/piha/services.yaml +++ b/hosts/piha/services.yaml @@ -165,6 +165,26 @@ services: # No backup job, no /opt/homelab/data bind. config_path: services/narty27 + kb-site: + role: static-html-host # public KB slice generated by scripts/kb/gen_pages.py + deployment_model: docker-compose + exposure: public # via npm@PIHA vhost kb.okit.pl; container binds LAN :8250 + offline_required: false + depends_on: + local: [] + external: [] + ports: + - name: http + container_port: 80 + host_port: 8250 + protocol: tcp + runtime: + # No config and no secrets. Content is a pure artifact of the repo: it + # lives only in the Docker named volume kb-site_kb-site_content and is + # refreshed by regenerating from kb/ (kb/runbooks/kb-site-deploy.md). + # No backup job, no /opt/homelab/data bind. + config_path: services/kb-site + # --- Known unmanaged containers on piha (recon B5/B6, 2026-07-27) ---------- # ~28 running containers have no entry above and are deliberately NOT being # added piecemeal — bringing them under desired state is a later stage diff --git a/services/kb-site/README.md b/services/kb-site/README.md new file mode 100644 index 0000000..3146c16 --- /dev/null +++ b/services/kb-site/README.md @@ -0,0 +1,5 @@ +# kb-site + +Public slice of the knowledge base (`kb.okit.pl`) — static HTML generated from `kb/**/*.md` by `scripts/kb/gen_pages.py`, served by nginx on PIHA. + +Dokumentacja: [kb/services/kb-site.md](../../kb/services/kb-site.md) diff --git a/services/kb-site/docker-compose.yml b/services/kb-site/docker-compose.yml new file mode 100644 index 0000000..d991ac6 --- /dev/null +++ b/services/kb-site/docker-compose.yml @@ -0,0 +1,28 @@ +services: + kb-site: + image: nginx:alpine + container_name: kb-site + restart: unless-stopped + ports: + # PIHA 82x0 static-HTTP block: 8210 paperless, 8220 nextcloud, + # 8230 kb-query, 8240 narty27 -> 8250 is the next free slot. + # Publicly the site is reached only through the npm@PIHA vhost + # kb.okit.pl; this bind is the proxy's upstream. + - "8250:80" + volumes: + # Generated output of scripts/kb/gen_pages.py — never committed, never + # bind-mounted from the repo. Read-only: nginx only serves it; writes go + # through the helper-container procedure in kb/runbooks/kb-site-deploy.md + # (docker cp cannot write into a :ro mount). + - kb-site_content:/usr/share/nginx/html:ro + # busybox wget — nginx:alpine ships no curl. index.html is generated on + # every run, so it is the one file that must always be there. + healthcheck: + test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1/index.html"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 5s + +volumes: + kb-site_content: diff --git a/services/kb-site/env.example b/services/kb-site/env.example new file mode 100644 index 0000000..85022ed --- /dev/null +++ b/services/kb-site/env.example @@ -0,0 +1,10 @@ +# kb-site has NO configuration and NO secrets. +# +# The port bind (8250:80) is static and the content lives in the +# kb-site_kb-site_content Docker volume, generated from kb/ by +# scripts/kb/gen_pages.py. This file exists only to keep the +# services// layout from CLAUDE.md complete — there is nothing to +# copy to .env. +# +# The public address is a generator argument, not an env var: +# python3 scripts/kb/gen_pages.py --base-url https://kb.okit.pl diff --git a/services/kb-site/healthcheck.sh b/services/kb-site/healthcheck.sh new file mode 100755 index 0000000..33f11e8 --- /dev/null +++ b/services/kb-site/healthcheck.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Healthcheck for kb-site (nginx:alpine serving the generated public KB) + +# Container must be running +if ! docker ps --filter "name=kb-site" --filter "status=running" | grep -qw "kb-site"; then + echo "[FAIL] kb-site container is not running" + exit 1 +fi + +# The index is generated on every run, so both the bare root and /index.html +# must answer. An empty volume means the content was never loaded — see +# kb/runbooks/kb-site-deploy.md. +for path in "" index.html; do + if ! curl -sf -o /dev/null "http://127.0.0.1:8250/${path}"; then + echo "[FAIL] kb-site is not serving /${path} on 127.0.0.1:8250 (content loaded?)" + exit 1 + fi +done + +echo "[OK] kb-site is healthy" +exit 0 diff --git a/services/kb-site/service.yaml b/services/kb-site/service.yaml new file mode 100644 index 0000000..5cbdc84 --- /dev/null +++ b/services/kb-site/service.yaml @@ -0,0 +1,33 @@ +service: + name: kb-site + owner_node: piha + role: static-html-host # public slice of the KB, rendered by scripts/kb/gen_pages.py + exposure: public # public via npm@PIHA (kb.okit.pl). The container itself binds + # 8250 on the LAN; npm is the sole public entry point. + dependencies: [] # nginx serving a local volume — nothing else required at runtime + ports: + - container: 80 + host: 8250 + protocol: tcp + healthcheck: + type: http + endpoint: http://127.0.0.1:8250/index.html # content must be loaded first (see runbook) + interval: 30s + timeout: 10s + retries: 5 + restart_policy: unless-stopped + persistence: + # Docker named volume kb-site_kb-site_content (compose project prefix), NOT a + # bind under /opt/homelab/data. The content is a pure artifact: regenerate it + # from the repo with scripts/kb/gen_pages.py, no backup job needed. + paths: + - kb-site_kb-site_content + runtime: + config_files: [] # no .env — the port bind is static, no secrets + env_vars: [] + content: + # Only kb/ documents with `visibility: public` are published; the generator + # is fail-closed (no frontmatter / no visibility field = private). + generator: scripts/kb/gen_pages.py + leak_check: scripts/kb/gen_pages.py --check # must pass before publishing + source: kb/**/*.md