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

6.1 KiB
Raw Blame History

title, type, tags, sources, updated
title type tags sources updated
MSSQL контейнер с восстановленными production data — паттерн concept
mssql
docker
recovery
named-volume
chown
../sources/nas-recovery-session-2026-05-18.md
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

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.

# 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):

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:

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.