- Вступление
- Цели
- DBUP
- Отчет HTML
- Создать приложение консоли DBUP
- Program.cs Файл
- Конфигурация действий GitHub
- Резюме
Как многие из вас уже знают, я являюсь руководителем отдела технологий в Первый порт Анкет
Ключевой частью моей роли является предоставление видения первогопорта «People First». Для этого крайне важно, чтобы я выбрал правильную технологию для лечения предоставления услуг, которые помогают облегчить жизнь клиентов.
Сегодня я хочу поговорить о своем выборе Dbup
DBUP-это открытый .NET .NET Библиотека, которая помогает вам развернуть изменения в базах данных SQL Server. Он отслеживает, какие сценарии SQL уже запускались, и запускает сценарии изменения, необходимые для актуальности вашей базы данных.
Вы можете задать вопрос, почему Dbup а не Ef миграции ?
Я использовал их много, и я должен сказать, что DBUP кажется мне самым чистым решением. Мне не нравится C# «обертки», чтобы сгенерировать SQL для меня. DDL — это простой язык, и я не думаю, что нам нужен специальный инструмент для его создания.
Здесь, в Firstport, мы также используем Terraform Чтобы построить инфраструктуру SQL Azure и, конечно же, Github Actions. Тем не менее, в основном основное внимание будет уделено DBUP и на рабочих процессах Action GitHub.
Цели
Есть ряд целей, к которым мы стремимся:
- Мы хотим, чтобы это было просто
- Мы хотим, чтобы это было повторяемым
- Мы хотим использовать тот же процесс для разработки, QA и производства наших изменений
DBUP
По своей сути DBUP — бегун сценария. Изменения, внесенные в базу данных, сделаны с помощью скрипта:
Script001_addtablex.sql Script002_addcolumnfirstportIdTotablex.sql script003_addcolumncustomeridTotablex.sql
DBUP работает через консольное приложение, которое вы пишете сами, поэтому вы контролируете, какие параметры использовать, и вам не нужно много кода.
Вы связываете эти сценарии и говорите DBUP, чтобы запустить их. Он сравнивает этот список с списком, хранящимся в базе данных назначения. Будут запускаться любые сценарии, которых нет в списке баз данных этого назначения. Сценарии выполняются в алфавитном порядке, а результаты каждого сценария отображаются на консоли. Очень просто внедрить и понять.
Checking whether journal table exists.. Journal table does not exist Is upgrade required: True Beginning database upgrade Checking whether journal table exists.. Journal table does not exist Executing Database Server script 'DbUpLeaseExtract.BeforeDeploymentScripts.001_CreateLeaseExtractSchemaIfNotExists.sql' Checking whether journal table exists.. Creating the [SchemaVersions] table The [SchemaVersions] table has been created Upgrade successful Success!
Это прекрасно работает, когда вы развертываете в среде разработки или тестирования. В Firstport мы предпочитаем, чтобы наши DBA одобряли сценарии перед тем, как отправиться на производство. Может быть, и среда для проведения или предварительного производства. Этот процесс утверждения имеет важное значение, особенно когда вы впервые начинаете развертывание баз данных.
Отчет HTML
Миграционные сценарии-обоюдоострый меч. У вас есть полный контроль, что дает вам большую силу. Тем не менее, это также легко испортить. Все зависит от типа совершенных изменений и навыков SQL писателя. Доверие DBA к этому процессу будет низким, когда неопытные разработчики C# пишут эти миграционные сценарии.
В Firstport мы добавили некоторый код для создания отчета HTML. Это метод расширения, в котором вы даете ему путь отчета, который вы хотите генерировать. Это означает, что этот раздел уходит от:
var result = upgrader.PerformUpgrade(); // Display the result if (result.Successful) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Success!"); } else { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(result.Error); Console.WriteLine("Failed!"); }
К:
if (args.Any(a => a.StartsWith("--PreviewReportPath", StringComparison.InvariantCultureIgnoreCase))) { // Generate a preview file so GitHub Actions can generate an artifact for approvals var report = args.FirstOrDefault(x => x.StartsWith("--PreviewReportPath", StringComparison.OrdinalIgnoreCase)); report = report.Substring(report.IndexOf("=") + 1).Replace(@"""", string.Empty); var fullReportPath = Path.Combine(report, "UpgradeReport.html"); Console.WriteLine($"Generating the report at {fullReportPath}"); upgrader.GenerateUpgradeHtmlReport(fullReportPath); } else { var result = upgrader.PerformUpgrade(); // Display the result if (result.Successful) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Success!"); } else { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(result.Error); Console.WriteLine("Failed!"); } } }
Этот код будет генерировать отчет, содержащий все сценарии, которые будут запускаться.
Создать приложение консоли DBUP
С приведенными выше функциями мы собрали .NET Core DBUP Console Console приложение для развертывания в Azure SQL. Затем мы составим процесс в действиях GitHub, чтобы запустить это приложение консоли.
Я выбрал .net Ядро Over .net Структура, потому что его можно построить и работать где угодно. DBUP — это .NET Стандартная библиотека. DBUP будет работать так же хорошо в .net Платформное приложение.
Давайте запустим нашу IDE по выбору и создадим .NET Приложение основной консоли. Я использую VSCODE Чтобы построить это приложение консоли. Я предпочитаю это полной взорванной Visual Studio.
Консольное приложение требует некоторых сценариев для развертывания. Я собираюсь добавить три папки и заполнить их некоторыми файлами сценариев:
Я рекомендую вам добавить префикс, такой как 001, 002 и т. Д., В начало имени файла сценария. DBUP запускает сценарии в алфавитном порядке, и этот префикс помогает убедиться, что сценарии выполняются в правильном порядке.
По умолчанию .NET не будет включать эти файлы сценариев при создании приложения консоли, и мы хотим включить эти файлы сценариев в качестве встроенных ресурсов. К счастью, мы можем легко добавить ссылку на эти файлы, включив этот код в файл .csproj:
Весь файл выглядит так:
Exe net5.0
Program.cs Файл
Последний шаг, направленный на то, чтобы сделать это приложение, — добавить необходимый код в программе. Приложение принимает параметры из командной строки, а действия GitHub будут настроены для отправки в следующих параметрах:
ConnectionString : Для этой демонстрации мы отправляем это как параметр вместо хранения его в файле конфигурации. PreviewReportPath : Полный путь для сохранения отчета о предварительном просмотре. Полный параметр пути является необязательным. Когда он отправляется, мы генерируем предварительный отчет HTML для действий GitHub для загрузки в хранилище Blob -Blob. Когда он не будет отправлен, код сделает фактическое развертывание. Давайте начнем с выталкивания строки подключения из аргумента командной строки:
static void Main(string[] args) { var connectionString = args.FirstOrDefault(x => x.StartsWith("--ConnectionString", StringComparison.OrdinalIgnoreCase)); connectionString = connectionString.Substring(connectionString.IndexOf("=") + 1).Replace(@"""", string.Empty);
DBUP использует беглый API. Нам нужно рассказать об наших папках, тип сценария каждую папку и порядок, в котором мы хотим запустить сценарии. Если вы используете сценарии, встроенные в параметр сборки с помощью поиска STARTSWITH, вам необходимо предоставить полное пространство имен в вашем поиске.
var upgradeEngineBuilder = DeployChanges.To .SqlDatabase(connectionString, null) .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), x => x.StartsWith("DbUpLeaseExtract.BeforeDeploymentScripts."), new SqlScriptOptions { ScriptType = ScriptType.RunAlways, RunGroupOrder = 0 }) .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), x => x.StartsWith("DbUpLeaseExtract.DeploymentScripts"), new SqlScriptOptions { ScriptType = ScriptType.RunOnce, RunGroupOrder = 1 }) .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), x => x.StartsWith("DbUpLeaseExtract.PostDeploymentScripts."), new SqlScriptOptions { ScriptType = ScriptType.RunAlways, RunGroupOrder = 2 }) .WithTransactionPerScript() .LogToConsole(); var upgrader = upgradeEngineBuilder.Build(); Console.WriteLine("Is upgrade required: " + upgrader.IsUpgradeRequired());
Обновление было построено, и он готов к запуску. В этом разделе мы вводем проверку для параметра отчета об обновлении. Если этот параметр установлен, не запускайте обновление. Вместо этого генерируйте отчет для действий GitHub для загрузки в хранилище Blob Blob:
if (args.Any(a => a.StartsWith("--PreviewReportPath", StringComparison.InvariantCultureIgnoreCase))) { // Generate a preview file so GitHub Actions can generate an artifact for approvals var report = args.FirstOrDefault(x => x.StartsWith("--PreviewReportPath", StringComparison.OrdinalIgnoreCase)); report = report.Substring(report.IndexOf("=") + 1).Replace(@"""", string.Empty); var fullReportPath = Path.Combine(report, "UpgradeReport.html"); Console.WriteLine($"Generating the report at {fullReportPath}"); upgrader.GenerateUpgradeHtmlReport(fullReportPath); } else { var result = upgrader.PerformUpgrade(); // Display the result if (result.Successful) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Success!"); } else { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(result.Error); Console.WriteLine("Failed!"); } }
Когда мы собираем все это вместе, это выглядит так:
using System; using System.IO; using System.Linq; using System.Reflection; using DbUp; using DbUp.Engine; using DbUp.Helpers; using DbUp.Support; namespace DbUpLeaseExtract { class Program { static void Main(string[] args) { var connectionString = args.FirstOrDefault(x => x.StartsWith("--ConnectionString", StringComparison.OrdinalIgnoreCase)); connectionString = connectionString.Substring(connectionString.IndexOf("=") + 1).Replace(@"""", string.Empty); var upgradeEngineBuilder = DeployChanges.To .SqlDatabase(connectionString, null) .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), x => x.StartsWith("DbUpLeaseExtract.BeforeDeploymentScripts."), new SqlScriptOptions { ScriptType = ScriptType.RunAlways, RunGroupOrder = 0 }) .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), x => x.StartsWith("DbUpLeaseExtract.DeploymentScripts"), new SqlScriptOptions { ScriptType = ScriptType.RunOnce, RunGroupOrder = 1 }) .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), x => x.StartsWith("DbUpLeaseExtract.PostDeploymentScripts."), new SqlScriptOptions { ScriptType = ScriptType.RunAlways, RunGroupOrder = 2 }) .WithTransactionPerScript() .LogToConsole(); var upgrader = upgradeEngineBuilder.Build(); Console.WriteLine("Is upgrade required: " + upgrader.IsUpgradeRequired()); if (args.Any(a => a.StartsWith("--PreviewReportPath", StringComparison.InvariantCultureIgnoreCase))) { // Generate a preview file so GitHub Actions can generate an artifact for approvals var report = args.FirstOrDefault(x => x.StartsWith("--PreviewReportPath", StringComparison.OrdinalIgnoreCase)); report = report.Substring(report.IndexOf("=") + 1).Replace(@"""", string.Empty); var fullReportPath = Path.Combine(report, "UpgradeReport.html"); Console.WriteLine($"Generating the report at {fullReportPath}"); upgrader.GenerateUpgradeHtmlReport(fullReportPath); } else { var result = upgrader.PerformUpgrade(); // Display the result if (result.Successful) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Success!"); } else { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(result.Error); Console.WriteLine("Failed!"); } } } } }
Конфигурация действий GitHub
У нас есть пара разных рабочих процессов. Один запускается по запросу на притяжение, чтобы сгенерировать отчет HTML, а другой — на слиянии, чтобы запустить фактические сценарии базы данных против экземпляра Target Azure SQL.
Сначала у нас есть это, чтобы убедиться, что он работает только по запросам на привлечение изменений в каталоге DB-reploy:
name: Create DB Delta Report on: pull_request: branches: - develop paths: - 'db-deploy/**'
Далее мы проверяем код и пронзитель, используя супер-линтер :
jobs: db-delta-report: name: db-delta-report runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@master - name: Lint Code Base uses: github/super-linter@master env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} VALIDATE_ALL_CODEBASE: true VALIDATE_MD: true VALIDATE_CSHARP: true VALIDATE_SQL: true
Далее мы настроем .net ядро и создайте проект:
- name: Setup .NET Core uses: actions/setup-dotnet@main with: dotnet-version: '5.0.x' - name: Cache Packages uses: actions/cache@v2 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} restore-keys: | ${{ runner.os }}-nuget - name: Restore dependencies working-directory: db-deploy/lease_extract run: dotnet restore - name: Build Console App working-directory: db-deploy/lease_extract run: dotnet publish --no-restore --output DbUpLeaseExtract
Этот следующий шаг вызывает сценарий PowerShell, который запускает приложение Console. Мы используем Зашифрованные секреты Чтобы пройти в строке подключения к базе данных:
- name: Create DB Delta Report env: LEASE_EXTRACT_DB_CONNECTION_STRING_DEV: ${{ secrets.LEASE_EXTRACT_DB_CONNECTION_STRING_DEV }} run: ./db-deploy/scripts/db-delta-report-dev.ps1 shell: pwsh
Это сценарий PowerShell, который звонит:
$packagePath = "db-deploy/lease_extract/DbUpLeaseExtract" $connectionString = $Env:LEASE_EXTRACT_DB_CONNECTION_STRING_DEV $reportPath = "db-deploy/lease_extract/DbUpLeaseExtract" $dllToRun = "$packagePath/DbUpLeaseExtract.dll" $generatedReport = "$reportPath/UpgradeReport.html" if ((test-path $reportPath) -eq $false){ New-Item $reportPath -ItemType "directory" } dotnet $dllToRun --ConnectionString="$connectionString" --PreviewReportPath="$reportPath"
В настоящее время API GitHub не может прикрепить файл к PR -комментариям, поэтому в качестве обходного пути я решил загрузить отчет HTML в хранилище Blob Blob и ссылку на него в комментарии PR:
- name: Upload DB Delta Report to Azure Blob uses: azure/powershell@v1 with: inlineScript: | az storage blob upload --account-name storageaccountname --container-name '$web' --file "db-deploy/lease_extract/DbUpLeaseExtract/UpgradeReport.html" --name UpgradeReport.html azPSVersion: "latest" - name: Comment on PR uses: unsplash/comment-on-pr@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: msg: "Please review the [DB Delta Report](https://storageaccountname.z33.web.core.windows.net/)"
Как только это было рассмотрено нашими DBA, и запрос на притяжение объединяется. Происходит другой рабочий процесс:
name: DB Upgrade on: push: branches: - develop paths: - 'db-deploy/**'
Вместо того, чтобы запустить отчет HTML, он запускает обновление в базе данных, вызывая другой сценарий:
- name: Deploy DB Upgrade env: LEASE_EXTRACT_DB_CONNECTION_STRING_DEV: ${{ secrets.LEASE_EXTRACT_DB_CONNECTION_STRING_DEV }} run: ./db-deploy/scripts/db-upgrade-dev.ps1 shell: pwsh
Единственное в этом сценарии, это то, что PreviewReportPath Переключатель отсутствует:
$packagePath = "db-deploy/lease_extract/DbUpLeaseExtract" $connectionString = $Env:LEASE_EXTRACT_DB_CONNECTION_STRING_DEV $reportPath = "db-deploy/lease_extract/DbUpLeaseExtract" $dllToRun = "$packagePath/DbUpLeaseExtract.dll" $generatedReport = "$reportPath/UpgradeReport.html" if ((test-path $reportPath) -eq $false){ New-Item $reportPath -ItemType "directory" } dotnet $dllToRun --ConnectionString="$connectionString"
В качестве последнего шага мы отслеживаем все наши развертывания в Код климатической скорости — Если вы еще не используете его, я настоятельно рекомендую вам проверить:
- name: Send Deployment to Code Climate run: curl -d "token=${{ secrets.VELOCITY_DEPLOYMENT_TOKEN }}" -d "revision=${GITHUB_SHA}" -d "repository_url=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" -d "branch=develop" -d "environment=db-dev" -d "version=${GITHUB_RUN_NUMBER}" https://velocity.codeclimate.com/deploys
Эта же конвенция будет выполнена для Dev, QA & Prod и т. Д. С помощью филиалов, выровненных по каждой среде.
Резюме
DBUP действительно помог Firstport создать надежный конвейер развертывания для баз данных. Теперь DBA (и другие) могут пересмотреть изменения с помощью действий GitHub, прежде чем они будут развернуты. Возможность пересмотреть изменения должна помочь укрепить доверие к процессу и помочь ускорить принятие.
В этом посте я продемонстрировал один метод для достижения автоматических развертываний базы данных с использованием действий GitHub. Существует множество других решений, и если вы используете структуру сущности или аналогично, эти инструменты имеют встроенную поддержку миграции, но основной подход будет таким же.
Я надеюсь, что смогу помочь вам узнать что -то новое сегодня и поделиться тем, как мы делаем что -то здесь, в Первый порт Анкет
Любые вопросы, свяжитесь с Twitter
Оригинал: «https://dev.to/ghostinthewire5/how-firstport-execute-a-database-as-code-strategy-using-dbup-terraform-github-actions-2b58»