9.9 KiB
title, type, tags, related, sources, updated
| title | type | tags | related | sources | updated | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sched — VDS deploy runbook (ядро daemon + admin API, MariaDB, стек sched) | concept |
|
|
2026-08-27 |
sched → VDS deploy runbook (ядро daemon)
⚙️ СЛУЖЕБНЫЙ РАНБУК — только для администратора проекта
.admin. Применять/выполнять шаги может только.admin(оператор проекта admin). Из проекта не выносить: не копировать в другие вики, не пересказывать, не публиковать. Нужен деплой или прод-операция по этому ранбуку — поставить задачу админу (.admin) и написать письмо.
Развёртывание ядра sched (шедулер: daemon + in-process admin API) на инфра-VDS
(89.253.255.94), storage-адаптер MariaDB (общая MariaDB-стек 11, отдельная база sched).
Первый деплой — 2026-08-27 (task:790). Вводные с 2026-08-27: org/scope @schedjs
(github.com/schedjs/schedjs), self-host на MariaDB (не sqlite).
Параметры
| Что | Значение |
|---|---|
| Хост | VDS kzntsv 89.253.255.94 |
| Стек Portainer | Id 28, name sched, endpointId 1 |
| Образ | registry.kzntsv.site/sched-custom:0.12.1-mysql (custom single-binary) |
| Compose source-of-truth | admin/host-stacks/vds-kzntsv/sched.compose.yml |
| tasks.json source-of-truth | admin/host-stacks/vds-kzntsv/sched.tasks.json → VDS /opt/stacks/sched/tasks.json |
| Даемон | published @schedjs/daemon@0.12.1 + @schedjs/storage-mysql@0.4.0 + @schedjs/admin-api@0.3.0 + @schedjs/mcp@0.4.1 + @schedjs/ui@0.3.0 |
| БД | MariaDB mariadb:3306 (сеть shared-dbs), база sched, роль sched@%. Креды: pass sched/mysql |
| Admin API | https://sched.vds.kzntsv.site (traefik websecure, letsEncrypt). SCHED_ADMIN_KEY = pass sched/admin-key |
| mem_limit | 512m (канон app-стеков) |
| Сети | shared-dbs + proxy (обе external) |
Артефакты
- Код/пакеты: публикуются на npm
@schedjs/*(см.sched-publish-runbook). Dev-исходники — репоsched(Gitea). - Dockerfile. Стандартный
apps/daemon/Dockerfileсобирает из@schedjs/daemon. Для MariaDB — композиция адаптера. В репо добавленapps/daemon/Dockerfile.mysql(public npm, без токена):Сборка:FROM node:24-alpine WORKDIR /app; ENV NODE_ENV=production RUN echo '{"name":"schedd","private":true,"packageManager":"yarn@4.18.0"}' > package.json \ && corepack enable && printf 'nodeLinker: node-modules\nnpmMinimalAgeGate: 0\n' > .yarnrc.yml \ && CI=1 yarn add @schedjs/daemon@0.12.1 @schedjs/storage-mysql@0.4.0 \ && rm -f .yarnrc.yml && yarn cache clean ENV PATH="/app/node_modules/.bin:${PATH}"; EXPOSE 8080; VOLUME ["/data"]; ENTRYPOINT ["schedd"]docker build -f apps/daemon/Dockerfile.mysql -t registry.kzntsv.site/sched-daemon:0.12.1-mysql apps/daemon. Единственный слой, который важно композить —@schedjs/storage-mysql(иначе--storage mysqlпадаетERR_MODULE_NOT_FOUND).
Первичный деплой (сделано 2026-08-27)
0. База (однократно).
# на VDS, root внутри mariadb контейнера
docker exec mariadb mariadb -uroot -p"$ROOT_PW" -e "
CREATE DATABASE IF NOT EXISTS sched CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'sched'@'%' IDENTIFIED BY '<pw>';
GRANT ALL PRIVILEGES ON sched.* TO 'sched'@'%'; FLUSH PRIVILEGES;"
Креды → pass insert -m sched/mysql (user/pass/db/host).
1. Образ (custom single-binary). apps/daemon/Dockerfile.custom собирает один образ с опубликованными @schedjs/{daemon,storage-mysql,admin-api,mcp,ui} + custom-entry.mjs — один HTTP-сервер на :8080:
/api/*→ админ API (health open, остальное BearerSCHED_ADMIN_KEY)/mcp→ Streamable HTTP MCP (projection of admin API,SCHED_MCP_READONLY=1read-only)/→ UI morda (web-components)/sched-ui.bundle.js→ статический UI-бандл Сборка:docker build -f apps/daemon/Dockerfile.custom -t registry.kzntsv.site/sched-custom:0.12.1-mysql apps/daemon. → ЕДИНЫЙ бинарь: даемон + UI-морда + MCP не плодит отдельные контейнеры (обратная связь vitya).createDaemonдаётengine+storage(admin server НЕ bind — маршрутизация своя). MCP/морда импортируются по file-URL (subpath-импорты заблокированы npmexports-маской).
2. tasks.json. Ядро стартует с {"tasks":[]} (воркеры — отдельными тасками). Закинуть на VDS:
scp sched.tasks.json vds.kzntsv.site:/tmp/ ; ssh vds.kzntsv.site 'sudo mkdir -p /opt/stacks/sched && sudo cp /tmp/sched.tasks.json /opt/stacks/sched/tasks.json && sudo chmod 644 /opt/stacks/sched/tasks.json'
3. Стек Portainer. compose в host-stacks/vds-kzntsv/sched.compose.yml, env через Portainer API (SCHED_DB_PASS, SCHED_ADMIN_KEY). Create standalone/string?endpointId=1.
Ключевой момент — MYSQL_URL + TLS. MariaDB-стек 11 запущен с --require-secure-transport=ON и self-signed TLS. Подключение через mariadb:3306 (внутренняя сеть shared-dbs) без TLS НЕ проходит: fatal: Connections using insecure transport are prohibited. Фикс — ssl в connstring:
MYSQL_URL=mysql://sched:${SCHED_DB_PASS}@mariadb:3306/sched?ssl={"rejectUnauthorized":false}
mysql2 парсит ssl из query-string (ConnectionConfig.parseUrl), self-signed → rejectUnauthorized:false.
Реденплой (обновление образа)
# 1. build + push нового тега (см. артефакты)
# 2. на VDS docker pull ДО restart (иначе застрянет на старом digest)
ssh vds.kzntsv.site 'docker pull registry.kzntsv.site/sched-daemon:<new>'
# 3. source-of-truth compose: сменить image: тег
# 4. Portainer PUT /api/stacks/28?endpointId=1 {stackFileContent, env:[...], prune:false, pullImage:true}
⚠️ PUT сбрасывает env-массив, если не передать env (gotcha 4b portainer-канон).
⚠️ Синхронизировать source-of-truth compose после PUT (иначе следующий redeploy из гита откатит тег).
Verify
# контейнер
docker ps -a --filter name=schedd --format "{{.Status}}" # Up ... (healthy)
docker logs --tail 5 schedd # "daemon started (tasks=..., storage=mysql db=sched, ...)"
# health (публичный, открыт)
curl -ksS https://sched.vds.kzntsv.site/api/health # {"ok":true,"uptimeMs":...,"version":"0.12.1"}
# авторизованный доступ (SCHED_ADMIN_KEY = Bearer)
curl -ksS -H "Authorization: Bearer $AK" https://sched.vds.kzntsv.site/api/tasks # {"tasks":[]}
curl -ksS https://sched.vds.kzntsv.site/api/tasks # 401 (без Bearer)
# таблицы в MariaDB
docker exec mariadb mariadb -uroot -p"$ROOT_PW" -e "USE sched; SHOW TABLES; SELECT version FROM sched_schema_version;"
# → scheduled_tasks, schedules, task_runs, sched_schema_version; version = 6
Rollback
PUT стека 28 с предыдущим тегом образа (или {"tasks":[]} пустой cron). Старые теги в registry.
Gotchas
- TLS к MariaDB обязателен —
--require-secure-transport=ON, без?ssl={"rejectUnauthorized":false}→ daemon в restart-loop сfatal: insecure transport prohibited. - basicAuth — оставлен ВЫКЛ (первый деплой). У daemon самодостаточный
SCHED_ADMIN_KEY(Bearer) — второй слой./api/healthоткрыт (для мониторинга), всё остальное 401 без Bearer. Если нужен front-layer basicAuth — добавить middleware в traefik labels (сгенерировать bcrypt-хэшhtpasswd -nbB), НО хэш передавать аккуратно (traefik может не принять$2y$при кривой передаче). --admin-host 0.0.0.0— daemon слушает на всех интерфейсах внутриproxy-сети для traefik. Наружу VDS-порт НЕ публикуется (нетports:), только через traefik.- Воркеры НЕ в этом ядре —
task:1190(yt-digest),task:1311(tg-digest) отдельные; добавляются как задачи вtasks.json. SCHED_ADMIN_KEY— вpass sched/admin-key(не basic-auth; это разные вещи).- storage-mysql сам применяет миграции на старте (
sched_schema_version, версия 6) — внешний инструментарий не нужен, но БД должна быть доступна с правами на CREATE/ALTER.
Связи
- concepts/portainer-stack-management-vds.md — общий Portainer-канон (JWT, gotchas, mem_limit)
- concepts/sched-publish-runbook.md — паблиш @schedjs на npm/GitHub
- concepts/runbooks-index.md — индекс ранбуков
- Репо sched:
docs/content/docs/13.self-hosting.md— композиция storage-адаптеров, security model