- deleted .tasks/windows-host-fallback-backup-daily.md (MSSQL removed on decommission 2026-06-08) - recovery-architecture-snapshot.md: marked ИСТОРИЧЕСКАЯ ЗАПИСЬ; removed 3 broken [[wiki-links]] (cms-server-port-leak-fix, cms-admin-assets-root-folder-seed, webconfig-password-xml-escape) - snolla-recovery-vm.md: marked УДАЛЕНА 2026-06-08 - ruvds-iis-host.md: struck 2 resolved risks (imgproxy SPOF, LE renewal); cross-ref winacme - future-resilient-architecture-goals.md: dead task link → plain text - mssql-on-vds.md: frontmatter fix; Backup TODO section replaced with implemented block (Express COPY_ONLY, sqlcmd, bind-mount pattern) - vds-kzntsv.md: added MSSQL row to software stack table - log.md: update entries for lint + mssql backup implementation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.2 KiB
MSSQL on vds-kzntsv
Запущен 2026-05-22 как часть mssql-vds-migration. SPOF с windows-recovery-host снят для CMS DB park.
Architecture
IIS snolla site (web.config)
→ mssql.kzntsv.site:1433 (LE/DNS A → 89.253.255.94)
→ VDS traefik :1433 (TCP entrypoint, HostSNI(*))
→ mssql container :1433 (self-signed TLS terminated по TDS)
→ /var/opt/mssql/data/{5 prod DBs}
Stack: /opt/stacks/databases/mssql/{docker-compose.yml,.env,data,backups}. Networks proxy only.
Image: mcr.microsoft.com/mssql/server:2022-latest (RTM-CU25, build 16.0.4255.1, on Ubuntu 22.04.5 LTS).
Edition: Express (MSSQL_PID=Express) — production licensing OK, 10 GB/DB cap (max .mdf сейчас stostayer 968 MB → 10× запас).
Memory: MSSQL_MEMORY_LIMIT_MB=2048 env (Express auto-caps на 1410 в любом случае).
TLS: self-signed cert, mssql generates automatically. Client connects with TrustServerCertificate=True. Traefik forwards raw TCP — TLS terminates at mssql.
Migration recipe (one-shot, executed 2026-05-22)
Источник: mcr.microsoft.com/mssql/server:2019-latest Developer на windows-recovery-host (volume mssql_mssql_data 26.6 GB; real data ~2.4 GB; .ldf logs 12 GB inflated).
-- 1. Source: BACKUP DATABASE for each prod DB (5 total)
BACKUP DATABASE [<db>] TO DISK='/var/opt/mssql/backups/<db>.bak'
WITH COMPRESSION, COPY_ONLY, INIT, STATS=50;
-- Compressed total: 486 MB (10× от .mdf size — built-in compression на UTF-16/varchar pads)
# 2. Extract from container → scp → VDS bind
docker cp mssql:/var/opt/mssql/backups/. C:/tmp/mssql-backups/
scp C:/tmp/mssql-backups/*.bak vitya@vds.kzntsv.site:/tmp/mssql-bak-staging/
ssh vitya@vds.kzntsv.site 'sudo mv /tmp/mssql-bak-staging/*.bak /opt/stacks/databases/mssql/backups/
sudo chown 10001:0 /opt/stacks/databases/mssql/backups/*.bak'
# 3. compose + .env (см. /opt/stacks/databases/mssql/)
# MSSQL_SA_PASSWORD из pass show mssql-vds/sa-password (32-char alnum, no special chars)
# 4. Traefik: добавить TCP entrypoint в /opt/stacks/traefik/data/traefik.yml
entryPoints:
mssql:
address: ":1433"
# + port 1433 в /opt/stacks/traefik/docker-compose.yml ports list
# + ufw allow 1433/tcp
# + sudo docker compose up -d (RECREATE, не restart — port mapping)
-- 5. RESTORE per DB
RESTORE DATABASE [<db>] FROM DISK='/var/opt/mssql/backups/<db>.bak' WITH REPLACE, STATS=50;
-- 2019 → 2022 schema upgrade автоматический (versions 953→957). DBCC CHECKDB PHYSICAL_ONLY clean.
-- 6. CRITICAL: server-level logins НЕ переезжают в .bak (sysadmin/master DB only).
-- Создать manually + fix orphan users в каждой DB:
CREATE LOGIN snolla WITH PASSWORD = '<from-source-web.config>', CHECK_POLICY = OFF;
USE MoreThenCms;
ALTER USER snolla WITH LOGIN = snolla;
-- Repeat for каждой DB где web.config упоминает данного user'а.
Gotchas
- Logins vs users: .bak восстанавливает DB-level users (с их role memberships + permissions), но НЕ server logins. Sites с
User Id=Xв conn-string ловятLogin failed for user 'X'после restore, пока не выполнитьCREATE LOGIN+ALTER USER ... WITH LOGIN. - Traefik recreate, не restart: добавление port mapping в
docker-compose.ymlтребует recreate контейнера (docker compose up -d), неrestart.restartоставит старые published ports. - PowerShell +
pass(bash script):pass showв PowerShell возвращает NULL (PS не запускает bash-скрипты напрямую). Нужноbash -c "pass show ..."или literal. Симптом:Login failed for user 'sa'несмотря на правильный пароль на VDS. MSSQL_SA_PASSWORDспец-символы: rotation password generator должен excludeавать+/=(Base64) и shell-meta ($,`,",',\). Иначе env-var passing через ssh heredoc или sed конструкторы ломаются. Пример безопасного:[Convert]::ToBase64String($bytes) -replace '[+/=]','x'.- MSSQL Linux user uid:
mssqluser = uid 10001. Bind-mount должен бытьchown 10001:0чтобы контейнер read/write. Parent dir ownsvitya:vityaчтобы можно было редактировать compose без sudo.
Backup integration
Реализовано 2026-06-11 в /opt/stacks/backup/scripts/run.sh (см. vds-backup-rsync-kreknin). Блок между Step 1 (DB dumps) и Step 2 (rsync):
source /opt/stacks/databases/mssql/.env # injects MSSQL_SA_PASSWORD
for DB in MoreThenCms StayerCalculator StayerPrice TireService stostayer; do
docker exec mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P "$MSSQL_SA_PASSWORD" -C -b \
-Q "BACKUP DATABASE [$DB] TO DISK='/var/opt/mssql/backups/${DB}.bak' WITH COPY_ONLY, INIT"
mv /opt/stacks/databases/mssql/backups/${DB}.bak "$DUMP_DIR/mssql-${DB}.bak"
done
unset MSSQL_SA_PASSWORD
Gotcha: Express Edition не поддерживает WITH COMPRESSION — только COPY_ONLY, INIT. .bak файлы создаются в bind-mount /var/opt/mssql/backups/ (uid 10001:10001); root mv их в $DUMP_DIR для rsync на kreknin. 5 DBs ~ 486 MB uncompressed. sqlcmd path: /opt/mssql-tools18/bin/sqlcmd.
Rollback recipe
- Revert IIS web.config'ы (см.
.bak-pre-vds-cutover-20260522файлы). iisreset /restart(или ждать auto-recycle ~30 sec).- Убедиться windows-host MSSQL контейнер всё ещё running (acceptance 48ч uptime до decommission).
- Если уже decommissioned:
docker start mssql(volume сохранён ещё неделю по acceptance).