import: merge .wiki/concepts/ from temp prefix into existing dir (history preserved via merge+rename)
This commit is contained in:
151
.wiki/concepts/mssql-container-data-restore.md
Normal file
151
.wiki/concepts/mssql-container-data-restore.md
Normal file
@@ -0,0 +1,151 @@
|
||||
---
|
||||
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]].
|
||||
Reference in New Issue
Block a user