我们有一堆SQL脚本(.sql文件),它们正在从一台服务器手动部署到另一台服务器。
所有SQL脚本都是通过Azure中的SQL Project管理的。
如何通过Azure CI / CD部署SQL脚本?
答案 0 :(得分:1)
管理数据库架构以及如何在您的环境中提升数据库架构应同等考虑应用程序和基础架构的部署。这是一些高级指南。
源代码管理-您需要将脚本放入源代码管理。如果您使用的是Azure DevOps,请使用Git存储库存储所生成的所有脚本。此外,我们在源代码控制中需要它们,以便我们的CI / CD工具可以实现。如果您是DBA,这听起来很奇怪,很抱歉,您必须习惯它。
1b。 启用拉取请求-将源存储库配置为仅接受来自拉取请求的更改。这样可以确保在将架构更改接受到存储库之前先对其进行检查。改进团队的知识共享并提高整体质量,因为它可以在部署之前发现错误。
安全性-锁定数据库,以便随机用户无法部署随机更改。创建一个专用帐户来应用数据库架构更改,并仅向连续交付工具提供凭据。在此模型下,如果不在源代码管理中,则它不存在。我们的CI / CD工具将负责为我们部署这些更改。
使用工具-停止手动操作!我们的团队决定使用一个名为db-migrate的开源框架来管理对数据库架构的更改。我们选择db-migrate是因为它是开源的,并且可以在许多不同的平台上工作。 Microsoft使用EntityFramework Code-First Migrations,它是db-migrate的基础。
迁移的工作原理:基本上,每次您需要修改数据库时,都会创建一个包含SQL脚本更改的“迁移”。当该工具针对您的数据库运行时,它将在数据库中创建一个表以跟踪先前已运行的迁移,因此该工具仅运行任何新的迁移。简而言之,迁移应该是非破坏性的,以防止数据丢失,并且一旦将脚本应用于任何数据库,就应将其视为只读。 (使用迁移sql脚本后,切勿更改它;创建一个新的迁移)
持续集成-每当将新迁移签入源代码管理时,您的CI服务器都会将脚本打包为下一个阶段的工件。
连续交付-连续交付系统获取构建工件并针对每个目标环境运行db-migrate工具(node.js)。 CD工具使用专用的SQL用户帐户,该帐户允许进行数据库架构更改。如#2所示,这应该是部署更改的唯一方法。
更新:使用实体框架
从上面列出的方法中可以看出,显然,我非常有兴趣将数据库架构更改限制为特定的用户帐户,以防止使用具有不受限制的访问权限的用户帐户意外地部署应用程序。您的里程可能会有所不同,但是您可以使用以下几种方法:
使用migrate.exe执行迁移-EntityFramework附带了此工具,可以将您的代码优先迁移作为部署步骤。这与我在上面使用db-migrate的方式非常相似。
在应用程序启动期间编写迁移脚本-您可以将Web应用程序初始化逻辑中的代码编写到perform any outstanding migrations。这也许是最容易实现的,但要求您使用具有不受限制的访问权限的数据库帐户运行应用程序。另外,如果数据库迁移存在问题,则直到应用程序已部署后您才知道。从CI / CD的角度来看,我想使部署失败并可能回滚,然后再完全弄乱站点。
配置发布配置文件-您可以将发布配置文件配置为outlined in this article以“更新数据库”。这会在应用程序启动时有效地将databaseInitializer添加到web.config中以更新数据库。这样做有类似的缺点,但至少可以让您拥有一个不同的用户帐户来应用数据库更改。
请注意,完全可以将存储过程作为资源嵌入到迁移中,然后从迁移中调用原始sql语句:
基本迁移:
/// <summary>
/// Custom DbMigration with helper methods
/// </summary>
public abstract class BaseDbMigration : DbMigration
{
/// <summary>
/// Apply a SQL statement stored in an embedded resource
/// </summary>
/// <param name="resourceName"></param>
protected void SqlFromEmbeddedResource(string resourceName)
{
Assembly assembly = typeof(BaseDbMigration).Assembly;
string baseNamespace = typeof(BaseDbMigration).Namespace;
resourceName = baseNamespace + "." + resourceName;
bool exists = assembly.GetManifestResourceNames().Where(r => r == resourceName).SingleOrDefault() != null;
if (exists)
{
string sql = null;
using (var stream = assembly.GetManifestResourceStream(resourceName))
{
var reader = new StreamReader(stream);
sql = reader.ReadToEnd();
}
base.Sql(sql);
}
}
}
示例迁移:
/// <summary>
/// Migration: Deploy Stored Proc
/// </summary>
public partial class CalculateTotalsV1 : BaseDbMigration
{
/// <summary>
/// </summary>
public override void Up()
{
base.SqlFromEmbeddedResource("sp_CalculateTotals.v1.Up.sql");
}
/// <summary>
/// </summary>
public override void Down()
{
base.SqlFromEmbeddedResource("sp_CalculateTotals.v1.Down.sql");
}
}
此外,如果要使数据库迁移与应用程序部署分离,则应该不用说,要将代码优先迁移分离到它们自己的.net程序集中。这样可以更轻松地调用迁移。