Контейнеризуємо MS SQL у парі з SQL Server Data Tools (SSDT)

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Привіт, мене звати Степан Чубанян, наразі займаю посаду Senior Software Engineer (.NET) в Grid Dynamics. Але не дотнетом єдиним, також захоплююсь всілякими DevOps-практиками. Ця стаття буде цікава .NET розробникам (або тим, хто використовує MS SQL) та, можливо, трошечки девопсам.

Навіщо взагалі нам контейнеризувати базу даних SQL? Будемо її запускати в контейнері на production оточенні? О ні, не будемо. Так-так, ми знаємо, що це цілком можливо, хоча й не без складнощів, але наразі не про це. Тож нащо нам ця авантюра?

Звичайно, що для розробки та тестування. Це може бути дуже корисно, зручно та досить легко для втілення, особливо якщо в нас MS SQL, а також SQL Server Data Tools (далі SSDT), яка дозволить нам легко керувати змінами та ініціалізацією бази даних.

У нашому випадку база буде запускатися в контейнері більше для того, щоб зручним чином передати базу даних QA-спеціалісту, який просто затягне образ контейнера та запустить, наприклад, у Docker Compose ваш застосунок, що буде дивитися в сусідній контейнер з БД. Таким чином, QA-спеціаліст може тестувати ваш застосунок на окремій та ізольованій БД, причому кожну таску можна тестувати окремо зі своєю версією БД.

Для чого SSDT

Сама SSDT дозволяє зручно керувати змінами в БД, це як контроль версії, так і застосування цих змін (публікація БД). Все це робиться більше в підході Database First, себто ми працюємо з БД більше як в SQL Server Management Studio. Це така альтернатива, що може бути до вподоби тим, хто хоче більше контролю над БД та не задоволений підходом Code First.

Створимо проєкт бази даних в SSDT

Для початку треба створити простий проєкт SSDT (наразі Visual Studio працює з SSDT найкращим чином)

Далі додамо трошки табличок.

Я намагався зберегти структуру БД якомога простішою, тому тут лише 2 таблички: Users та UserRoles

Насправді цього достатньо для того, щоб опублікувати нашу БД на екземпляр SQL Server. Тицяємо правою кнопкою по проєкту та обираємо відповідний пункт меню Publish.

У наступному віконці потрібно вказати рядок підключення до нашого сервера БД (Target database connection). Також, якщо є необхідність, можна більш точно вказати налаштування публікації, натиснувши на кнопку Advanced. Всі ці налаштування з рядком підключення, назвою БД, налаштуваннями тощо можна зберегти в профіль публікації (Save Profile As...) та потім просто завантажувати під час публікації (Load Profile).

Після того, як ви вкажете усі необхідні налаштування, можете згенерувати скрипт публікації (Generate Script) для створення/оновлення БД вручну або можете це зробити прямо через Visual Studio (Publish).

Як це працює? SSDT порівнює наш опис в проєкті та фактичну структуру БД, після чого генерує скрипт для синхронізації стану, щоб реальність в нашій БД відповідала очікуванням, що ми зазначили в проєкті.

Автоматизуємо процес за допомогою Docker на Windows

Окей, окей, чому на Windows, ваше питання я вже чую. Я згодний, що це може для когось здатися якимось збоченням, але насправді буває так, що тільки такий варіант є можливим. Наприклад, якщо ваш проєкт десь на .NET Framework 4.8 та перелізти на .NET 5 здається неможливим або керівництво не бачить в цьому сенсу, але вам все ж хочеться спробувати запхати той застосунок разом із БД до контейнерів, щоб відчути смак тих докерів та докер-компоузів. Принаймні в мене так було) Якщо буде попит — пишіть, я можу адаптувати це для Linux, без проблем, там навіть ще простіше все.

Тож розберімося, що ми автоматизуємо:

  • Build проєкту бази даних, щоб отримати .dacpac файл, який є артефактом і необхідний для публікації
  • Опублікувати базу даних використовуючи .dacpac файл
  • Запустити БД в контейнері

Звучить досить просто. В реалізації воно в принципі так сам просто, але це не точно)

Build проєкту бази даних

Для того, щоб зібрати проєкт бази даних, msbuild — це все, що нам потрібно:

msbuild AwesomeDB.sqlproj

Ця команда на виході створить .dacpac файл.

Опублікувати базу даних

Для публікації БД нам потрібен sqlpackage. Скоріш за все, у вас його нема і потрібно буде його встановити:

Download and install SqlPackage — SQL Server | Microsoft Docs

Після встановлення запустимо команду для публікації:

sqlpackage.exe /a:Publish /sf:"db.dacpac" /tsn:. /tdn:AwesomeDb

Трохи поясню аргументи:

/a:Publish - action Publish
/sf:"db.dacpac" - source file db.dacpac
/tsn:. - Target Server Name . (this PC)
/tdn:AwesomeDb - Target Database Name AwesomeDb

Такий output маємо на виході:

Publishing to database 'AwesomeDB' on server '.'.
Initializing deployment (Start)
Initializing deployment (Complete)
Analyzing deployment plan (Start)
Analyzing deployment plan (Complete)
Updating database (Start)
Creating database AwesomeDB...
Creating SqlTable [dbo].[UserRoles]...
Creating SqlTable [dbo].[Users]...
Creating SqlForeignKeyConstraint [dbo].FK_Users_UserRoles_UserRoleId]...
Update complete.
Updating database (Complete)
Successfully published database.
Time elapsed 0:01:32.53:

Запустити БД в контейнері

Всередині контейнеру нам потрібно встановити усі залежності: такі як SSDT Build Tools, msbuild, sqlpackage, — після чого виконати те саме, що ми тільки-но робили вручну.

Ось наш Dockerfile (gist):

FROM mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2019 AS builder
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# add SSDT build tools
RUN nuget install Microsoft.Data.Tools.Msbuild -Version 10.0.61804.210
# add SqlPackage tool
ENV download_url="https://download.microsoft.com/download/6/E/4/6E406E38-0A01-4DD1-B85E-6CA7CF79C8F7/EN/x64/DacFramework.msi"
RUN Invoke-WebRequest -Uri $env:download_url -OutFile DacFramework.msi ; \
    Start-Process msiexec.exe -ArgumentList '/i', 'DacFramework.msi', '/quiet', '/norestart' -NoNewWindow -Wait; \
    Remove-Item -Force DacFramework.msi
COPY /AwesomeDB .
RUN msbuild AwesomeDB.sqlproj \
    /p:SQLDBExtensionsRefPath="C:\Microsoft.Data.Tools.Msbuild.10.0.61804.210\lib\net46" \
    /p:SqlServerRedistPath="C:\Microsoft.Data.Tools.Msbuild.10.0.61804.210\lib\net46"
FROM octopusdeploy/mssql-server-windows-express:latest
# Install SQLPackage 
RUN Invoke-WebRequest -Uri https://go.microsoft.com/fwlink/?linkid=2157302 -OutFile sqlpackage.zip; Expand-Archive -Path sqlpackage.zip -DestinationPath sqlpackage
# Add the DACPAC to the image
COPY --from=builder ["C:/bin/Debug/AwesomeDB.dacpac", "/db.dacpac"]
# Configure external build arguments to allow configurability.
ARG DBNAME=AwesomeDB
ARG PASSWORD
# Configure the required environmental variables
ENV ACCEPT_EULA=
ENV SA_PASSWORD=$env:PASSWORD
RUN "C:\sqlpackage\sqlpackage.exe" /a:Publish /sf:"db.dacpac" /tsn:. /tdn:$env:DBNAME

Покладіть цей файл у папку з вашим solution та насолоджуйтесь) Білдіть образ, використовуйте його на локальній машині або запуште в Container Registry та можете ділитися цим із вашою командою.

Висновок

Такий підхід допоможе нам знизити до мінімуму кількість проблем, що виникають при розгортанні БД у процесі розробки/тестування шляхом автоматизації.

На одному з проєктів ми виконали таку автоматизацію білда образу контейнеру, після чого пушили його у приватний реєстр. Далі будь-який член команди міг спулити образ нової версії БД — і це було супер зручно для QA спеціалістів в команді, тому що вони мали можливість протестити конкретну фічу на ізольованому локальному оточенні, включаючи БД.

Також можете подивитися на бонусний приклад автоматизації цього добра в Gitlab CI(.gitlab-ci.yml gist):

stages:
  - build
.shared_windows_runners:
  tags:
  - shared-windows
  - windows
  - windows-1809
build-job:
  stage: build
  extends:
  - .shared_windows_runners
  script:
    - docker build -t awesomedb:1.0 .

Тож автоматизуйте, але не просто так, а якщо в цьому є реальний профіт)

Сподобалась стаття? Натискай «Подобається» внизу. Це допоможе автору виграти подарунок у програмі #ПишуНаDOU

👍ПодобаєтьсяСподобалось5
До обраногоВ обраному1
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
На одному з проєктів ми виконали таку автоматизацію білда образу контейнеру, після чого пушили його у приватний реєстр. Далі будь-який член команди міг спулити образ нової версії БД — і це було супер зручно для QA спеціалістів в команді, тому що вони мали можливість протестити конкретну фічу на ізольованому локальному оточенні, включаючи БД.

У Java є бібліотека як TestContainers. Вона портована і на .NET:

github.com/...​ers/testcontainers-dotnet

Її перевага в тому, що вона дозволяє запустити в тестах будь-який контейнер Docker. У чому перевага вашого підходу? Адже для нього потрібний цей SQL Server Data Tools. А якщо вам потрібно буде зробити те ж саме завтра, але для Oracle/Postgres/Mongo?

Якщо буде потрібно зробити те ж саме, але з іншою БД, то вочевидь треба буде щось міняти, однак в нас такої потреби не було.

Нам потрібно було передавати додаток та БД тестувальникам, далі вони в себе розгортали і зверху або писали тести, або мануально тестували.

SSDT ми вже використовували до цього, з цього і пішло таке рішення.

testcontainers ми не використовували через те що про нього ми і не чули, так як коли ми це зробили, тільки перша версія порту на .NET вийшла(початок 2020).
Плюс я бачу шо testcontainers надає просто порожню БД, проблема публікації в ньому не вирішується. Також не зрозуміло, як піднімати додаток разом із БД просто для мануального тестування, а не тільки для автоматизованих тестів.

Тут є багато рішень:
1) Ви можете створити Dockerfile, в якому запускатимуться потрібні вам SQL скрипти (і вкажете цей Docker Image для TestContainers)
2) Ці скрипти можна запускати в run-time вже при тестуванні. У Spring Framework таке можливе.

Ну ми так і зробили, написали Dockerfile, як описано у статті, де SSDT публікував схему БД і запускали потім в Docker Compose.

Так, якщо нам би знадобився TestContainers, його можна було б там вказати. Але нам було достатньо Docker Compose

Якщо просто потрібно запустити проект разом із БД для мануального тестування, то Java ми робимо так.
Упаковуємо додаток у Docker Image, а потім запускаємо все разом через Docker Compose.

Саме так ми і робили. Той образ з БД потім запускався разом із додатком в Docker Compose

Ось наш Dockerfile (gist):

Я так зрозумів, ви зробили Windows контейнер (виходячи з Dockerfile). А чому не контейнер Linux?

А я там в самій статті написав. В нашому кейсі ми ще були на .NET Framework, якщо буде запит я можу перевести на Linux

В репозиторіїї майкрософту лежить хелм профайл та купа прикладів як підняти в різних контейнерах

Можливо, я не бачив, зараз спробував знайти, але не вийшло. Поділіться, будь-ласка, посиланням, якщо є така можливість

github.com/microsoft/mssql-docker

там треба походити вправо-вліво
погано структуровано

А, бачив, дякую. По факту я використовую в своєму прикладі майже те саме. То форк того, що ви скинули.

Такий підхід допоможе нам знизити до мінімуму кількість проблем, що виникають при розгортанні БД у процесі розробки/тестування шляхом автоматизації.

а які саме проблеми вирішували? бо наче сбілдити та запаблішити проект на якому завгодно хості чи білд машині ж можна без контейнеру без необхідності кожен раз запускаючи контейнер витрачати час на встановлення компонентів і т.д.. ну і там де юзають SSDT для розробки напевно стоіїть msbuild/ssdt і так всюди як на дев машинах так і на агентах.

Дякую за коментар
Не зовсім зрозумів цю частину:

без необхідності кожен раз запускаючи контейнер витрачати час на встановлення компонентів

Але в цілому зрозумів питання. Якщо коротко, то у випадку, якщо вам, як тестувальнику, треба багато переключатися між різними версіями пар додаток+БД без зайвих рухів, тобто ручних коригувань конфігів і connectionStrings, ручних публікацій БД і взагалі використання Visual Studio. А натомість використовувати якусь просто команду, на кшталт

BRANCH_NAME=Features/mega-feature docker-compose up 

В нашому варіанті ця команда пулила образи відповідної гілки БД та додатку, після чого запускала локально для тестування.

Підписатись на коментарі