152 lines
6.1 KiB
Markdown
152 lines
6.1 KiB
Markdown
---
|
||
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]].
|