feat(kb-site): serwis nginx na PIHA + manifesty (wzorzec narty27)
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.
This commit is contained in:
parent
9591c8b688
commit
2c5894d70b
|
|
@ -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
|
||||
|
|
|
|||
5
services/kb-site/README.md
Normal file
5
services/kb-site/README.md
Normal file
|
|
@ -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)
|
||||
28
services/kb-site/docker-compose.yml
Normal file
28
services/kb-site/docker-compose.yml
Normal file
|
|
@ -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:
|
||||
10
services/kb-site/env.example
Normal file
10
services/kb-site/env.example
Normal file
|
|
@ -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/<service>/ 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
|
||||
21
services/kb-site/healthcheck.sh
Executable file
21
services/kb-site/healthcheck.sh
Executable file
|
|
@ -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
|
||||
33
services/kb-site/service.yaml
Normal file
33
services/kb-site/service.yaml
Normal file
|
|
@ -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
|
||||
Loading…
Reference in a new issue