feat(open-webui): инстанс Open WebUI для человека + Cluster/postgres в его ns #79

Merged
hermes merged 1 commit from refs/pull/79/head into main 2026-09-20 20:01:35 +00:00
hermes commented 2026-09-20 19:57:38 +00:00 (Migrated from git.grachevko.ru)

Инстанс Open WebUI на человека: UI к Hermes API, одна модель (hermes-agent), вход только через Zitadel, база — свой Postgres человека.

ns grachevko

  • postgres — Cluster/postgres (CNPG 1.30). В инфстансе живут базы всех личных приложений человека: каждое приложение объявляет свою базу и роль объектами Database/DatabaseRole, отдельный Cluster на приложение не заводится. instances: 1 (репликация — про доступность, не про сохранность), 10Gi proxmox-ssd. Bootstrap создаёт только пару app/app — конвенция оператора 1.30, обойти нечем (cloudnative-pg#3242); приложения на неё не опираются.
  • open-webui — официальный чарт по OCI (oci://ghcr.io/open-webui/helm-charts/open-webui, тег 16.5.0 = appVersion 0.11.3, сверен живым манифестом ghcr). Образ по digest'у v0.11.3@sha256:41daa0cf….
    • сабчарты ollama/pipelines/tika/terminals выключены, websocket.manager: "" + websocket.redis.enabled: false (свой Redis однорепличному инстансу не нужен);
    • strategy: Recreate под RWO-PVC, readOnlyRootFilesystem: true, uid 1000, emptyDir на /tmp;
    • вход только SSO: ENABLE_LOGIN_FORM=false + OAUTH_AUTO_REDIRECT=true (аварийный вход — /auth?form=true), первый вход через Zitadel становится админом;
    • пробы /health (startup с запасом на миграции Alembic) и /health/db;
    • доверие внутреннему CA — из Bundle: ConfigMap internal-ca, SSL_CERT_FILE/REQUESTS_CA_BUNDLE/CURL_CA_BUNDLE на ca.crt.

Креды

  • Пароль роли БД и WEBUI_SECRET_KEY генерируются в кубе: ESO Password + refreshPolicy: CreatedOnce + target.immutable + creationPolicy: Orphan. Наивная привязка генератора к периодическому ExternalSecret давала бы новый ключ на каждом refreshInterval — сессии падали бы по расписанию.
  • WEBUI_SECRET_KEY обязателен: backend/open_webui/env.py v0.11.3 делает SystemExit, если он пуст, а entrypoint образа кладёт свой ключ в /app/backend/.webui_secret_key — рядом с кодом, то есть вне тома.
  • DatabaseRole/open-webui берёт пароль из Secret'а типа kubernetes.io/basic-auth (требование CNPG) с меткой cnpg.io/reload; приложение читает те же username/password через DATABASE_USER/DATABASE_PASSWORD, потому что databaseUrl чарта — литерал, а URL собирается из DATABASE_* (env.py).
  • В Vault лежит ровно одно значение — client secret Zitadel (kv/ai/open-webui): его выдаёт провайдер, сгенерировать нельзя.

Ключ Hermes API — Secret hermes-api-key, сгенерированный в кубе один раз; его читают оба пода в ns grachevko (Hermes — env API_SERVER_KEY, Open WebUI — ключ подключения).
⚠️ Требуется правка данных (не git), сразу после выката: удалить строку API_SERVER_KEY= из /opt/data/.env тома и перекатить под hermes — .env грузится с override=True и до этого побеждает env пода. Пока строка жива, Open WebUI будет получать 401 от /v1 (Hermes работает старым ключом из .env).

Сверка

  • flate test all -p ./kubernetes/flux/cluster — 90 passed, 0 blocked (на ветке с уже влитым #78).
  • По рендеру flate build all: порт сервиса 8080 ↔ backendRef в HTTPRoute, все env-ссылки ↔ ключи Secret'ов, mountPath ↔ SSL_CERT_FILE; все пути значений существуют в values.yaml чарта (своего values.schema.json у него нет, опечатка молча игнорировалась бы).
  • Сертификат менять не нужно: TLS-хендшейк на 192.168.40.11 с SNI chat.<домен> против бандла внутреннего CA проходит (wildcard *.<домен>).

Остаётся сделать вручную (не git): DNS-запись chat → 192.168.40.11 (сейчас имя отдаёт публичный адрес — ловит wildcard) и правка .env выше.

Инстанс Open WebUI на человека: UI к Hermes API, одна модель (`hermes-agent`), вход только через Zitadel, база — свой Postgres человека. **ns grachevko** - `postgres` — `Cluster/postgres` (CNPG 1.30). В инфстансе живут базы всех личных приложений человека: каждое приложение объявляет свою базу и роль объектами `Database`/`DatabaseRole`, отдельный Cluster на приложение не заводится. `instances: 1` (репликация — про доступность, не про сохранность), 10Gi `proxmox-ssd`. Bootstrap создаёт только пару `app`/`app` — конвенция оператора 1.30, обойти нечем (cloudnative-pg#3242); приложения на неё не опираются. - `open-webui` — официальный чарт по OCI (`oci://ghcr.io/open-webui/helm-charts/open-webui`, тег `16.5.0` = appVersion `0.11.3`, сверен живым манифестом ghcr). Образ по digest'у `v0.11.3@sha256:41daa0cf…`. - сабчарты `ollama`/`pipelines`/`tika`/`terminals` выключены, `websocket.manager: ""` + `websocket.redis.enabled: false` (свой Redis однорепличному инстансу не нужен); - `strategy: Recreate` под RWO-PVC, `readOnlyRootFilesystem: true`, uid 1000, emptyDir на `/tmp`; - вход только SSO: `ENABLE_LOGIN_FORM=false` + `OAUTH_AUTO_REDIRECT=true` (аварийный вход — `/auth?form=true`), первый вход через Zitadel становится админом; - пробы `/health` (startup с запасом на миграции Alembic) и `/health/db`; - доверие внутреннему CA — из Bundle: ConfigMap `internal-ca`, `SSL_CERT_FILE`/`REQUESTS_CA_BUNDLE`/`CURL_CA_BUNDLE` на `ca.crt`. **Креды** - Пароль роли БД и `WEBUI_SECRET_KEY` генерируются в кубе: ESO `Password` + `refreshPolicy: CreatedOnce` + `target.immutable` + `creationPolicy: Orphan`. Наивная привязка генератора к периодическому ExternalSecret давала бы новый ключ на каждом `refreshInterval` — сессии падали бы по расписанию. - `WEBUI_SECRET_KEY` обязателен: `backend/open_webui/env.py` v0.11.3 делает `SystemExit`, если он пуст, а entrypoint образа кладёт свой ключ в `/app/backend/.webui_secret_key` — рядом с кодом, то есть вне тома. - `DatabaseRole/open-webui` берёт пароль из Secret'а типа `kubernetes.io/basic-auth` (требование CNPG) с меткой `cnpg.io/reload`; приложение читает те же username/password через `DATABASE_USER`/`DATABASE_PASSWORD`, потому что `databaseUrl` чарта — литерал, а URL собирается из `DATABASE_*` (`env.py`). - В Vault лежит ровно одно значение — client secret Zitadel (`kv/ai/open-webui`): его выдаёт провайдер, сгенерировать нельзя. **Ключ Hermes API** — Secret `hermes-api-key`, сгенерированный в кубе один раз; его читают оба пода в ns `grachevko` (Hermes — env `API_SERVER_KEY`, Open WebUI — ключ подключения). ⚠️ **Требуется правка данных (не git), сразу после выката**: удалить строку `API_SERVER_KEY=` из `/opt/data/.env` тома и перекатить под `hermes` — `.env` грузится с `override=True` и до этого побеждает env пода. Пока строка жива, Open WebUI будет получать `401` от `/v1` (Hermes работает старым ключом из `.env`). **Сверка** - `flate test all -p ./kubernetes/flux/cluster` — **90 passed, 0 blocked** (на ветке с уже влитым #78). - По рендеру `flate build all`: порт сервиса `8080` ↔ `backendRef` в HTTPRoute, все env-ссылки ↔ ключи Secret'ов, `mountPath` ↔ `SSL_CERT_FILE`; все пути значений существуют в `values.yaml` чарта (своего `values.schema.json` у него нет, опечатка молча игнорировалась бы). - Сертификат менять не нужно: TLS-хендшейк на 192.168.40.11 с SNI `chat.<домен>` против бандла внутреннего CA проходит (wildcard `*.<домен>`). **Остаётся сделать вручную** (не git): DNS-запись `chat` → 192.168.40.11 (сейчас имя отдаёт публичный адрес — ловит wildcard) и правка `.env` выше.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
grachevko/home-ops!79
No description provided.