Files
admin/.wiki/concepts/mssql-container-data-restore.md

152 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: MSSQL контейнер с восстановленными production data — паттерн
type: concept
tags: [mssql, docker, recovery, named-volume, chown]
sources: [../sources/nas-recovery-session-2026-05-18.md]
updated: 2026-05-19
---
# MSSQL container с production data
## Проблема
Восстановить MSSQL базу в Docker-контейнере на Windows-хосте из:
- Готовых `.mdf/.ldf` файлов production (взяты из tar `/var/opt/mssql/` source-контейнера)
- + 5 user-databases + системные (master/model/msdb/tempdb)
## Что НЕ работает: bind-mount production data
```yaml
volumes:
- ./production-data/mssql:/var/opt/mssql
```
**Не работает** на Windows Docker Desktop. MSSQL-контейнер требует:
- `master.mdf` owned by `mssql` user (UID 10001) или root
- `chmod` на файлах внутри `/var/opt/mssql/log/` (логи, .xel, .trc)
На Windows DD bind-mount через WSL2-слой не позволяет:
- Файлы видятся как owned by root or other UID — MSSQL отказывается: `Your master database file is owned by root.`
- `chmod` внутри контейнера фейлится: `Operation not permitted`
Симптом: MSSQL стартует, в логах ругается на chmod, потом стартует ещё раз и зависает.
## Что работает: named volume + chown через temp container
Идея: создать named volume, скопировать данные внутрь с правильным chown, потом примонтировать к MSSQL.
```yaml
# docker-compose.yml — финальная версия
services:
mssql:
image: mcr.microsoft.com/mssql/server:2019-latest
container_name: mssql
environment:
ACCEPT_EULA: "Y"
MSSQL_SA_PASSWORD: ${SA_PASSWORD}
MSSQL_PID: Developer
ports:
- "1433:1433"
volumes:
- mssql_data:/var/opt/mssql
networks:
- proxy
restart: unless-stopped
volumes:
mssql_data:
networks:
proxy:
external: true
```
Заполнение volume (один раз перед `docker compose up -d`):
```powershell
docker volume create mssql_mssql_data
docker run --rm \
-v mssql_mssql_data:/dest \
-v <local-path-to-production-data>/mssql:/src:ro \
alpine cp -a /src/. /dest/
docker run --rm -v mssql_mssql_data:/dest \
alpine chown -R 10001:0 /dest
docker compose up -d
```
После этого MSSQL стартует с production data, делает crash recovery в master.mdf, поднимается за 30-60 секунд (если CU контейнера == CU source) или 5-30 минут (если CU source старше — script upgrade mode).
## sqlcmd "$( var-substitution" pitfall
Альтернативный путь — restore из `.sql` script (export через "Generate Scripts" в SSMS). Файл 547 MB с CREATE+INSERT'ами всей БД.
**Не запустить через `sqlcmd -i file.sql`** без флага `-x`. Потому что:
- В данных встречаются jQuery JS-сниппеты типа `INSERT ... VALUES (N'$(function(){ ... })')`
- sqlcmd по умолчанию интерпретирует `$(varname)` как переменную для подстановки.
- На `$(function(){` парсер ломается → "Syntax error near command '('" в середине файла.
Фикс: `sqlcmd -x` (disable variable substitution).
```
docker exec mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P '<pw>' -C -x \
-i /var/opt/mssql/backup/MoreThenCms.sql
```
## SA-аккаунт disabled в production
Production SQL Server конфигурации часто **отключают `sa`** (security best practice). Пароль может быть прав, но account disabled → `Login failed for user 'sa'. Reason: The account is disabled.`
Фикс: **`mssql-conf set-sa-password`** в offline-mode (server stopped) под root:
```
# Stop running container
docker compose stop
# Run offline mssql-conf in temp container с тем же volume
docker run --rm --user 0:0 \
-e ACCEPT_EULA=Y \
-e MSSQL_SA_PASSWORD='<new-pw>' \
-v mssql_mssql_data:/var/opt/mssql \
mcr.microsoft.com/mssql/server:2019-latest \
/opt/mssql/bin/mssql-conf set-sa-password
# Запуск нормального container
docker compose start
```
`mssql-conf set-sa-password` сбрасывает пароль И **enables sa**. Если env-pw совпадает с тем что ожидает приложение — приложение продолжает работать.
## Production passwords reuse
Замеченный паттерн: у пользователя один пароль `fXkH4@8O%3pc` используется как:
- SA password (MSSQL)
- Connection string password для user `snolla` (CMS DB user)
- Возможно где-то ещё
Лучше: rotate after recovery, разделить.
## CU mismatch warning
Если master.mdf был из старой CU (например 2019 CU14), а контейнер `2019-latest` (например CU32-GDR), MSSQL после старта войдёт в **script upgrade mode** на 10-30 минут:
```
Server is in script upgrade mode. Only administrator can connect at this time.
```
Видно в логах сотни строк `spid9s ... Deleting AlwaysOnAgReplicas...` — это нормальный msdb-upgrade. Подождать. Один раз. После завершения — никаких задержек на следующих стартах.
## Healthcheck path для 2019-latest image
В современных image SQL Server инструменты лежат в `/opt/mssql-tools18/bin/sqlcmd` (с TLS-флагом `-C`), а не `/opt/mssql-tools/bin/sqlcmd`:
```yaml
healthcheck:
test: ["CMD-SHELL", "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P \"$$MSSQL_SA_PASSWORD\" -C -Q 'SELECT 1' -b -o /dev/null || exit 1"]
```
Связано: [[snolla-recovery-vm]] (Web.config указывает на `10.0.2.2:1433` = host из VM-NAT), [[windows-recovery-host]].