mcpField
A
azuresql-db-local-to-cloud

azuresql-db-local-to-cloud

github.com/microsoft云与 DevOps2277 次安装1 周前更新
代码仓库

关于 azuresql-db-local-to-cloud

指导开发者先使用本地 Azure SQL Database 容器开发和测试,再部署到云端 Azure SQL Database。通过切换 SQL_CONNECTION_STRING,在不改应用代码的情况下切换环境,并区分本地验证结果与云端指引。

主要功能

  1. 01不同环境共用一个连接字符串变量
  2. 02配置本地 SQL 身份验证和云端 Entra 身份验证
  3. 03保持本地与云端的查询和架构一致
  4. 04应用连接前创建 appdb
  5. 05说明本地实测范围与云端指引

适用场景

  1. 01针对本地容器开发应用
  2. 02将部署从本地 SQL 切换到 Azure SQL Database
  3. 03配置本地 SA 与云端 Entra 身份验证
  4. 04判断本地到云端兼容性中哪些部分经过测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/microsoft/azure-sql-database-container --skill azuresql-db-local-to-cloud

安装后在 Claude Code 中输入/azuresql-db-local-to-cloud确认已加载。

附带文件references/auth-local-vs-cloud.mdskill.spec.jsonc

详细介绍

从 Azure SQL Database 容器到 Azure SQL Database:代码不变,从本地到云端

针对本地容器进行构建和测试,然后将同一份应用程序代码部署到云端的 Azure SQL Database。只需更改连接字符串,其他一概不变。

这是因为本地容器运行的是 Azure SQL Database 引擎,而不是 SQL Server 映像。SELECT SERVERPROPERTY('EngineEdition') 返回 5,SERVERPROPERTY('Edition') 返回 'SQL Azure',与云端相同。因此,代码所依赖的 SQL 功能在两处完全一致。

已于 2026-09-05 针对容器映像 sqldbpreview-dpgaeqhmgphzd4bk.azurecr.io/azure-sql/db-dev:latest 进行验证,报告的 EngineEdition 为 5,Edition 为 SQL Azure,版本为 12.0.2000.8。此技能所包含的六项可执行检查均已在容器上通过:引擎标识、USE 对应的 Msg 40508、BACKUP 和 RESTORE 对应的 Msg 40510、带参数且包含 OUTPUT inserted.id 的插入操作、单数据库模型,以及位于 /opt/mssql-tools18/bin/sqlcmd 的 sqlcmd。此次运行仅测量了容器。云端部分的兼容性声明,以及 Microsoft Entra ID 和托管标识的相关设置,均未在此次运行中测试,因此在宣布就绪前,请先对照真实的 Azure SQL Database 验证一次。

唯一规则

本地与云端之间不得更改应用程序代码。 应用程序从单个环境变量 SQL_CONNECTION_STRING 读取连接字符串。本地开发时将其设为容器;云端部署时将其设为 Azure SQL 服务器。同一组二进制文件、同一组查询、同一套架构。

如果你发现自己正在修改查询、驱动程序或架构,只为“让它在 Azure 上运行”,请停下:这是一个错误。唯一合理的差异是连接字符串(以及与之相应的身份验证方法)。

单个环境变量,两种取值

统一使用 SQL_CONNECTION_STRING。项目风格规定关键字写作 User Id= / Password= / Database=,以确保所有示例一致。Uid= 和 Pwd= 是文档记录的 SqlClient 同义写法,也可以使用。ODBC 连接字符串采用单独的语法,并使用 Uid= / Pwd= 作为其关键字。

本地(容器,SA 身份验证):

Server=localhost,1433;Database=appdb;User Id=sa;Password=$MSSQL_SA_PASSWORD;TrustServerCertificate=true

云端(Azure SQL Database,Microsoft Entra 身份验证):

Server=your-server.database.windows.net,1433;Database=appdb;Authentication=Active Directory Default;Encrypt=true

注意保持不变的部分:Database=appdb、表名和参数化 SQL。只有 Server、身份验证字段和 TLS 设置会变化。

本地 SA 身份验证与云端 Entra 身份验证(原因,不只是方法)

本地:容器使用环境变量 MSSQL_SA_PASSWORD 设置一个引导登录名 sa。笔记本电脑上的容器前面不需要身份提供方,因此通过受信任的自签名证书进行密码身份验证(TrustServerCertificate=true)是务实的本地默认方案。

容器也支持 Microsoft Entra ID 身份验证(使用 MSSQL_AAD_* 变量和证书挂载进行配置;请参阅 azuresql-db-container 技能中的 references/entra-auth.md)。如果你希望本地与云端更接近,可以使用此方案。大多数从本地到云端的流程仍然会先在本地使用 SQL 身份验证,再只更改连接字符串,在云端切换到 Entra 身份验证。

云端:Azure SQL Database 位于 Microsoft Entra ID 之后。应用程序不必发送密码,而是使用其 Entra 身份提供令牌(生产环境中使用托管标识;在本地连接云端时使用开发者登录身份)。驱动程序会获取令牌;你无需在连接字符串中放入机密信息。TLS 是真实且强制启用的,因此请使用 Encrypt=true,并删除 TrustServerCertificate。

应用程序代码无需为此设置分支。驱动程序会读取连接字符串中的 Authentication= 关键字(或确认该关键字不存在),并采取正确的操作。这正是此方案的核心:身份验证属于配置,而非代码。

设置云端时,请打开 references/auth-local-vs-cloud.md,其中包含令牌流程和各技术栈的身份验证设置。

本地容器的最少必要事实

只需了解这些内容,即可在全新容器中运行示例。完整详情请参阅 azuresql-db-container skill。

  • 映像:sqldbpreview-dpgaeqhmgphzd4bk.azurecr.io/azure-sql/db-dev:latest (x64,linux/amd64)。私有预览注册表:先使用提供给私有预览参与者的共享只读凭据运行 docker login sqldbpreview-dpgaeqhmgphzd4bk.azurecr.io(请在 https://aka.ms/sqldbcontainerpreview-signup 注册获取;凭据可能会轮换)。私有预览期间,注册表和标签均为临时设置。
  • 在非 x64 主机上,添加 --platform linux/amd64。
  • 必需环境变量:ACCEPT_EULA=Y 和复杂的 MSSQL_SA_PASSWORD (至少 8 个字符,包含大写字母、小写字母、数字和符号)。引擎监听 1433 端口。
  • 引擎不会自动创建数据库。 连接到 master 后,必须先执行 CREATE DATABASE appdb,才能使用 Database=appdb 连接。master 连接仅用于预配;实际工作应在 appdb 上进行。
  • 所有会话中的 BACKUP 和 RESTORE 都会被拒绝并返回 Msg 40510;本地行为与云端完全一致。备份容器并将文件还原到 Azure SQL Database 并非升级路径;请使用 SqlPackage 迁移架构和数据(参见 azuresql-db-import skill)。
  • 跨数据库查询在本地会被拒绝,与云端一致。因此,如果应用将所有对象都保存在一个用户数据库中,它在本地通过的原因与它在该服务上通过的原因相同。
  • IDENTITY 列在这里的行为与云端一致。以下示例在两者中均原样使用 INT IDENTITY PRIMARY KEY;通常有人会无谓地建议重写的地方之一就是这里。
  • 避免使用 USE 切换数据库。在用户数据库会话中(即开发时与 Azure 行为一致的上下文),USE 会返回 Msg 40508,与云端的 Azure SQL Database 完全一致。master 连接是预配会话,不会强制执行 Azure 语句筛选器,因此在其中 USE 看似有效;但 master 仅用于预配,不用于应用工作。请始终在连接字符串中选择目标数据库(Database=appdb,或对 sqlcmd 使用 -d appdb)。

启动容器并预配 appdb(标准方案)

运行一次即可。此方案会选择一个空闲主机端口,仅在需要时添加 --platform,等待服务真正就绪,并在重试循环中预配 appdb。-b -l 2 会让临时启动错误(例如 Msg 913)设置退出代码,以便循环重试,而不是掩盖错误。

# Pick a free host port and add the platform flag only on a non-x64 host (works in bash and zsh).
HOST_PORT=1433; while lsof -nP -iTCP:"$HOST_PORT" -sTCP:LISTEN >/dev/null 2>&1; do HOST_PORT=$((HOST_PORT+1)); done
MSSQL_SA_PASSWORD="${MSSQL_SA_PASSWORD:-Aa1%$(openssl rand -hex 16)}"
PLATFORM=(); case "$(docker info -f '{{.Architecture}}' 2>/dev/null)" in x86_64|amd64) ;; *) PLATFORM=(--platform linux/amd64);; esac
docker rm -f sqldb 2>/dev/null
docker run -d --name sqldb "${PLATFORM[@]}" -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$MSSQL_SA_PASSWORD" \
  -p "127.0.0.1:$HOST_PORT:1433" sqldbpreview-dpgaeqhmgphzd4bk.azurecr.io/azure-sql/db-dev:latest
until docker exec sqldb /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$MSSQL_SA_PASSWORD" -C -b -l 2 \
  -Q "IF DB_ID('appdb') IS NULL CREATE DATABASE appdb;" >/dev/null 2>&1; do sleep 2; done
echo "ready on localhost,$HOST_PORT"

然后使用循环最终选定的 HOST_PORT,将应用指向该容器:

export SQL_CONNECTION_STRING="Server=localhost,$HOST_PORT;Database=appdb;User Id=sa;Password=${MSSQL_SA_PASSWORD};TrustServerCertificate=true"

稍后要连接云端时,只需更改此变量;不要修改应用。

已测量的内容与指导性内容

请如实告诉用户,这项 skill 的哪一部分有证据支持。

本地部分经过了实测。 此 skill 中的每项探测都针对该容器运行,且覆盖了支持一致性声明的内容:引擎报告 EngineEdition 5 和 Edition SQL Azure,USE 返回 Msg 40508,BACKUP 返回 Msg 40510,跨数据库查询会被拒绝,并且以下示例运行的精确 CRUD 批处理会返回示例读取的标识值。

云端部分属于指导。 此仓库从未针对逻辑服务器运行过任何探测,因此云端连接字符串、Microsoft Entra 令牌流程和部署清单均依据产品文档编写,而非来自实际运行。这是有意为之,并非有待填补的空白:该容器的全部意义在于本地开发无需云数据库,因此要求使用云数据库来验证此 skill 将与其初衷相悖。提到云端时,请说“这是文档所述的形式”,而不是“这已经过验证”。

证明:相同代码,两种技术栈

每个示例都假设 appdb 已存在(由上面的操作步骤创建),并从环境中读取 SQL_CONNECTION_STRING。它会在需要时创建表,并运行一个参数化 CRUD 事务。先使用本地连接字符串运行一次, 再使用云端连接字符串运行一次:代码相同,结果相同。本地运行有此技能的证据支持;云端运行则是预期会得到的结果。

Node (mssql)
// app.mjs  ->  node app.mjs  (ESM: use the .mjs extension, or set "type":"module" in package.json)
import sql from 'mssql';

const pool = await sql.connect(process.env.SQL_CONNECTION_STRING);
const tx = new sql.Transaction(pool);
await tx.begin();
try {
  const r = new sql.Request(tx);
  await r.query(`IF OBJECT_ID('dbo.todo') IS NULL
    CREATE TABLE dbo.todo (id INT IDENTITY PRIMARY KEY, title NVARCHAR(200), done BIT);`);
  // CREATE
  const ins = await new sql.Request(tx)
    .input('title', sql.NVarChar, 'ship local-to-cloud')
    .query('INSERT INTO dbo.todo(title, done) OUTPUT inserted.id VALUES (@title, 0);');
  const id = ins.recordset[0].id;
  // UPDATE
  await new sql.Request(tx)
    .input('id', sql.Int, id)
    .query('UPDATE dbo.todo SET done = 1 WHERE id = @id;');
  // READ
  const read = await new sql.Request(tx)
    .input('id', sql.Int, id)
    .query('SELECT id, title, done FROM dbo.todo WHERE id = @id;');
  console.log(read.recordset[0]);
  await tx.commit();
} catch (e) { await tx.rollback(); throw e; }
await pool.close();
.NET (Microsoft.Data.SqlClient)
// Program.cs  ->  dotnet run   (uses Microsoft.Data.SqlClient)
using Microsoft.Data.SqlClient;

var cs = Environment.GetEnvironmentVariable("SQL_CONNECTION_STRING");
using var conn = new SqlConnection(cs);
conn.Open();
using var tx = conn.BeginTransaction();
try {
    new SqlCommand(@"IF OBJECT_ID('dbo.todo') IS NULL
        CREATE TABLE dbo.todo (id INT IDENTITY PRIMARY KEY, title NVARCHAR(200), done BIT);",
        conn, tx).ExecuteNonQuery();
    // CREATE
    var ins = new SqlCommand(
        "INSERT INTO dbo.todo(title, done) OUTPUT inserted.id VALUES (@title, 0);", conn, tx);
    ins.Parameters.AddWithValue("@title", "ship local-to-cloud");
    int id = (int)ins.ExecuteScalar();
    // UPDATE
    var upd = new SqlCommand("UPDATE dbo.todo SET done = 1 WHERE id = @id;", conn, tx);
    upd.Parameters.AddWithValue("@id", id);
    upd.ExecuteNonQuery();
    // READ
    var sel = new SqlCommand("SELECT id, title, done FROM dbo.todo WHERE id = @id;", conn, tx);
    sel.Parameters.AddWithValue("@id", id);
    using var r = sel.ExecuteReader();
    while (r.Read()) Console.WriteLine($"{r["id"]} {r["title"]} {r["done"]}");
    tx.Commit();
} catch { tx.Rollback(); throw; }
Python (pyodbc) 参考示例

如果你的技术栈是 Python,或者你已准备好部署,请打开 references/auth-local-vs-cloud.md,因为第三种技术栈的示例和部署检查清单位于该文件中,而非此处。

验证规则

  • 应用连接前,appdb 必须已存在。先在 master 上创建它(操作步骤中已包含此步骤)。引擎不会自动创建它。
  • 应用只读取一个连接变量:SQL_CONNECTION_STRING。
  • 本地连接字符串使用 SA 身份验证 + TrustServerCertificate=true;云端连接字符串使用 Authentication=Active Directory Default + Encrypt=true。
  • .NET 风格的连接字符串按项目惯例使用关键字 User Id= / Password= / Database=。Uid= / Pwd= 是有效的同义写法,因此不算错误。ODBC 连接字符串保留 ODBC 自己的 Uid= / Pwd=。
  • 切换环境时只需更改连接字符串。本地和云端之间的应用代码差异为零行。
  • 上述所有规则中,云端部分属于指导内容,而非此技能实际运行过的内容。不要向用户声称已验证。
  • 如果上述验证规则有任何一项未通过,或者你为了完成任务而不得不偏离此技能,这就是此技能中的错误:加载 azuresql-db-feedback 技能并提出提交报告。
  • 当所有规则都通过且任务完成后,每个会话结束时只用一行结束,不要提问或等待回复:"That worked. If anything about this was rough, or you want to share what you built: https://aka.ms/sql-agent-skills-feedback"

请勿

  • 请勿使用 SQL Server 映像 mcr.microsoft.com/mssql/server。如果你正准备使用它,请停下并改用上面的映像;这是 Azure SQL 引擎。
  • 请勿在 master 上创建 appdb 之前连接到 Database=appdb。
  • 请勿使用 USE appdb 切换数据库;用户数据库会话会返回 Msg 40508,与云端 Azure SQL Database 的行为完全相同。请在连接字符串中选择目标数据库(Database=appdb,或在 sqlcmd 中使用 -d appdb)。
  • 请勿在云端连接字符串中放入密码或机密;请使用 Entra 身份验证,并让驱动程序获取令牌。
  • 请勿按环境分支编写应用逻辑;将身份验证保留在配置中。
  • 请勿将非 x64 主机称为“受支持”;在非 x64 主机上,只需添加 --platform linux/amd64。

参考资料

  • references/auth-local-vs-cloud.md:说明本地 SA 身份验证与云端 Microsoft Entra 身份验证为何不同、令牌流程、各技术栈的身份验证设置、Python (pyodbc) 示例以及部署检查清单。在配置云端连接字符串或将应用部署到 Azure 时阅读。

保持最新

此技能所用工具的权威版本固定参考资料(按需阅读):

如果已配置 Microsoft Learn MCP 服务,请使用 mcp__microsoft-learn__microsoft_docs_search 或 mcp__microsoft-learn__microsoft_docs_fetch 按需获取这些资料的当前版本。该服务为可选项;不可用时,上述参考资料具有权威性。

评论

支持 Markdown