如何使用 Azure 资源管理器模板手动迁移 Azure IoT 中心

使用 Azure 门户、Azure 资源管理器模板和 Azure IoT 中心服务 SDK 将 IoT 中心迁移到新区域、新层级或新配置。

如果想要执行以下操作,本文中的步骤会很有用:

若要迁移中心,你需要一个对原始中心具有管理访问权限的订阅。 可将新的中心放到新的资源组和区域、原始中心所在的同一订阅甚至新的订阅中。 不能使用相同的名称,因为中心名称必须全局唯一。

比较自动和手动迁移步骤

本文的结果与如何使用 Azure CLI 自动迁移 IoT 中心一文中类似,但过程不同。 在开始之前,请确定适合你的方案的过程。

  • 手动过程(本文):

    • 迁移设备注册表以及路由和终结点信息。 必须在新的 IoT 中心手动重新创建其他配置详细信息。
    • 迁移大量设备(例如,超过 100,000 台)的速度更快。
    • 使用 Azure 存储帐户传输设备注册表。
    • 从 ARM 模板输出中清理针对路由和文件上传终结点的连接字符串,而且你需要手动重新添加这些字符串。
  • Azure CLI 过程:

    • 迁移设备注册表、路由和终结点信息以及其他配置详细信息,例如IoT Edge 部署或自动设备管理配置。
    • 迁移少量设备(例如,最多 10,000 台)更轻松。
    • 不需要 Azure 存储帐户。
    • 收集针对路由和文件上传终结点的连接字符串,并将其包含在 ARM 模板输出中。

注意事项

在迁移 IoT 中心之前,有几件事需要考虑。

  • 确保原始位置中所有可用的功能在新位置中也可用。 某些服务以预览版提供,因此并非所有功能都可以在任何位置使用。

  • 在创建和验证迁移的版本之前,不要删除原始资源。 一旦删除某个中心,就会将其永久删除,并且没有任何办法可以恢复,因此也就无法检查设置或数据来确保正确地复制中心。

  • 原始 Azure IoT 中心的数据不会迁移。 该数据包括设备消息、云到设备 (C2D) 的命令以及作业相关的信息,例如计划和历史记录。 指标和日志记录结果也不会迁移。

  • 需要针对迁移造成的停机时间做好安排。 将设备克隆到新中心需要花费一定的时间。 如果使用导入/导出方法,基准测试表明,移动 500,000 个设备可能需要大约两个小时,而移动 100 万个设备大约需要四个小时。

  • 无需关闭或更改设备,即可将设备复制到新中心。

    • 如果设备最初是使用 DPS 预配的,请更新其注册以指向新的 IoT 中心。 然后,重新预配设备以更新存储在每个设备中的连接信息。

    • 否则,你必须使用导入/导出方法来移动设备,然后必须修改设备才能使用新中心。 例如,可将设备设置为使用孪生所需属性中的 IoT 中心主机名。 设备将采用该 IoT 中心主机名,请断开设备与旧中心的连接,然后将其重新连接到新中心。

  • 需要更新任何证书,以便可将其用于新资源。 此外,你可能已在 DNS 表中的某个位置定义了中心,并且需要更新这些 DNS 信息。

方法

下面是我们建议的迁移 IoT 中心的常规方法。

  1. 将中心及其设置导出到资源管理器模板。

  2. 对模板进行所需的更改,例如,为迁移的中心更新所有地方显示的名称和位置。 对于模板中用于消息路由终结点的任何资源,请在模板中更新该资源的密钥。

  3. 将模板导入到位于新位置的新资源组。 此步骤将创建新的 IoT 中心。

  4. 请根据需要进行调试。

  5. 添加未导出到模板中的任何内容。

    例如,使用者组就不会导出到模板中。 需要手动将使用者组添加到模板中,或者在创建中心后使用 Azure 门户来添加。

  6. 将设备从原始中心复制到新中心。 此过程在管理已注册到 Azure IoT 中心的设备部分进行了介绍。

如何处理消息路由

如果你的中心使用消息路由,则导出中心模板的过程涉及路由配置,但不涉及资源本身。 如果要将 IoT 中心迁移到新区域,则必须选择是将路由资源也移动到新位置,还是将它们留在原地并继续“按原样”使用。 将消息路由到不同区域中的终结点资源可能会导致性能轻微下降。

如果中心使用消息路由,你可以采用两种做法。

  • 将用于路由终结点的资源移到新位置。

    1. Azure 门户中或通过资源管理器模板自己手动创建新资源。

    2. 在新位置创建的所有资源皆需进行重命名,因为它们需要具有全局唯一的名称。

    3. 在创建新中心之前,在新中心的模板中更新资源名称和资源密钥。 创建新中心时,这些资源应该存在。

  • 不要移动用于路由终结点的资源。 请“在原地”使用这些资源。

    1. 在编辑模板的步骤中,需要检索每个路由资源的密钥并将其放入模板,然后再创建新中心。

    2. 中心仍引用原始路由资源,并按配置将消息路由到这些资源。 由于中心和路由终结点资源不在同一位置,因此性能会略微下降。

准备将中心迁移到另一个区域

本部分提供有关迁移中心的具体说明。

将原始中心导出到资源模板

  1. 登录 Azure 门户

  2. 导航到要移动的 IoT 中心。

  3. 从中心的属性和设置列表中选择“导出模板”。

    Screenshot showing the command for exporting the template for the IoT hub.

  4. 选择“下载”以下载模板。 将文件保存到可以再次找到的某个位置。

    Screenshot showing the command for downloading the template for the IoT hub.

查看模板

转到下载的模板,该模板包含在 zip 文件中。 提取 zip 文件,并找到名为 template.json 的文件。

以下示例适用于没有路由配置的一般中心。 它是位于 chinanorth 区域的名为 ContosoHub 的 S1 层中心(有 1 个单元):

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "IotHubs_ContosoHub_connectionString": {
            "type": "SecureString"
        },
        "IotHubs_ContosoHub_containerName": {
            "type": "SecureString"
        },
        "IotHubs_ContosoHub_name": {
            "defaultValue": "ContosoHub",
            "type": "String"
        }
    },
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Devices/IotHubs",
            "apiVersion": "2021-07-01",
            "name": "[parameters('IotHubs_ContosoHub_name')]",
            "location": "chinanorth",
            "sku": {
                "name": "S1",
                "tier": "Standard",
                "capacity": 1
            },
            "identity": {
                "type": "None"
            },
            "properties": {
                "ipFilterRules": [],
                "eventHubEndpoints": {
                    "events": {
                        "retentionTimeInDays": 1,
                        "partitionCount": 4
                    }
                },
                "routing": {
                    "endpoints": {
                        "serviceBusQueues": [],
                        "serviceBusTopics": [],
                        "eventHubs": [],
                        "storageContainers": []
                    },
                    "routes": [],
                    "fallbackRoute": {
                        "name": "$fallback",
                        "source": "DeviceMessages",
                        "condition": "true",
                        "endpointNames": [
                            "events"
                        ],
                        "isEnabled": true
                    }
                },
                "storageEndpoints": {
                    "$default": {
                        "sasTtlAsIso8601": "PT1H",
                        "connectionString": "[parameters('IotHubs_ContosoHub_connectionString')]",
                        "containerName": "[parameters('IotHubs_ContosoHub_containerName')]"
                    }
                },
                "messagingEndpoints": {
                    "fileNotifications": {
                        "lockDurationAsIso8601": "PT1M",
                        "ttlAsIso8601": "PT1H",
                        "maxDeliveryCount": 10
                    }
                },
                "enableFileUploadNotifications": false,
                "cloudToDevice": {
                    "maxDeliveryCount": 10,
                    "defaultTtlAsIso8601": "PT1H",
                    "feedback": {
                        "lockDurationAsIso8601": "PT1M",
                        "ttlAsIso8601": "PT1H",
                        "maxDeliveryCount": 10
                    }
                },
                "features": "None",
                "disableLocalAuth": false,
                "allowedFqdnList": []
            }
        }
    ]
}

编辑模板

必须先进行一些更改,然后才能使用该模板在新区域中创建新中心。 使用 Visual Studio Code 或文本编辑器来编辑模板。

编辑中心名称和位置

  1. 删除顶部的容器名称参数部分。 ContosoHub 没有关联的容器。

    "parameters": {
      ...
        "IotHubs_ContosoHub_containerName": {
            "type": "SecureString"
        },
      ...
    },
    
  2. 删除 storageEndpoints 属性。

    "properties": {
      ...
        "storageEndpoints": {
        "$default": {
            "sasTtlAsIso8601": "PT1H",
            "connectionString": "[parameters('IotHubs_ContosoHub_connectionString')]",
            "containerName": "[parameters('IotHubs_ContosoHub_containerName')]"
        }
      },
      ...
    
    
  3. 如果要将中心移动到新区域,请更改“资源”下的“位置”属性。

    "location": "chinanorth",
    

更新路由终结点资源

导出已配置路由的中心的资源管理器模板时,你会看到导出的模板中并未提供这些资源的密钥。 它们的放置由星号表示。 在导入新中心的模板并创建该中心之前,必须在门户中转到这些资源并检索密钥,然后填充密钥。

如果还移动了路由资源,请一并更新每个终结点的名称、ID 和资源组。

  1. 检索所有路由资源所需的密钥,并将其放到模板中。 可以从 Azure 门户中的资源检索密钥。

    • 例如,如果要将消息路由到某个存储容器,请在门户中找到相应的存储帐户。 在“设置”部分下选择“访问密钥”,然后复制其中的一个密钥。 首次导出模板时,密钥如下所示:

      "connectionString": "DefaultEndpointsProtocol=https;
      AccountName=fabrikamstorage1234;AccountKey=****",
      "containerName": "fabrikamresults",
      

      检索存储帐户的帐户密钥后,请将其输入到模板上的子句 AccountKey=**** 中,取代星号。

    • 对于服务总线队列,获取与 SharedAccessKeyName 匹配的共享访问密钥。 下面是 JSON 中的密钥和 SharedAccessKeyName

      "connectionString": "Endpoint=sb://fabrikamsbnamespace1234.servicebus.chinacloudapi.cn:5671/;
      SharedAccessKeyName=iothubroutes_FabrikamResources;
      SharedAccessKey=****;
      EntityPath=fabrikamsbqueue1234",
      
    • 上述操作对于服务总线主题和 Azure 事件中心连接也适用。

通过加载模板创建新中心

使用编辑后的模板创建新中心。 若要移动路由资源,应在新位置设置资源,并更新模板中的引用,以使其匹配。 如果不移动路由资源,应将其包含在模板中并对其使用更新的密钥。

  1. 登录 Azure 门户

  2. 选择“创建资源”。

  3. 在搜索框中,搜索并选择“模板部署(使用自定义模板部署)”。 在模板部署屏幕上,选择“创建”。

  4. 在“自定义部署”页面上,选择“在编辑器中生成自己的模板”,以便可以从文件上传模板。

    Screenshot showing the command for building your own template.

  5. 选择“加载文件”。

    Screenshot showing the command for uploading a template file.

  6. 浏览到已编辑的新模板并将其选中,然后选择“打开”。 随即会在编辑窗口中加载该模板。 选择“保存”。

    Screenshot showing loading the template.

  7. 在“自定义部署”页上填写以下字段。

    订阅:选择要使用的订阅。

    资源组:选择现有资源组或创建新资源组。

    区域:如果你选择了现有的资源组,则系统会为你填充与资源组位置匹配的区域。 如果创建了新资源组,这便是该资源组所在的位置。

    连接字符串:填充中心的连接字符串。

    “中心名称”:为新中心命名。

    Screenshot showing the custom deployment page

  8. 选择“查看 + 创建”按钮。

  9. 选择“创建”按钮。 门户会验证你的模板并部署新中心。 如果有路由配置数据,该数据将包含在新中心内,但会指向位于先前位置的资源。

    Screenshot showing the final custom deployment page

使用导入/导出方法将设备移动到新中心

应用程序面向 .NET Core,因此可以在 Windows 或 Linux 上运行它。 可以下载示例,检索连接字符串,为要运行的位设置标志,然后运行应用程序。 无需打开代码即可完成这些操作。

下载示例

  1. 在此处使用 IoT C# 示例:适用于 C# 的 Azure IoT SDK。 下载 zip 文件并将其解压缩到计算机。

  2. 相关代码位于 ./iothub/service/samples/how to guides/ImportExportDevicesSample 中。 无需查看或编辑代码即可运行应用程序。

  3. 若要运行应用程序,请指定三个连接字符串和五个选项。 可以作为命令行参数传入此数据,也可以使用环境变量,或结合使用这两种方法。 我们将以命令行参数的形式传递选项,并以环境变量的形式传递连接字符串。

    原因在于,连接字符串很长很杂乱,且不太可能会更改,但你可能想要更改这些选项并多次运行应用程序。 若要更改某个环境变量的值,必须关闭命令窗口以及所用的开发工具(无论是 Visual Studio,还是 Visual Studio Code)。

选项

下面是运行应用程序时需要指定的五个选项:

  • “addDevices”(参数 1)- 若要添加为你生成的虚拟设备,请将此选项设置为 True。 这些设备将添加到源中心。 另外,请设置 numToAdd(参数 2)以指定要添加的设备数。 可以注册到中心的设备的最大数目是 1,000,000。 此选项的目的是进行测试。 可以生成特定数量的设备,然后将其复制到另一个中心。

  • “copyDevices”(参数 3)- 将此选项设置为 True 即可将设备从一个中心复制到另一个中心。

  • “deleteSourceDevices”(参数 4)-- 将此选项设置为 True 即可删除已注册到源中心的所有设备。 建议等到你已确定所有设备均已转移,再运行此选项。 一旦删除设备,就再也不能将其恢复。

  • “deleteDestDevices”(参数 5)-- 将此选项设置为 True 即可删除已注册到目标中心的所有设备。 若要多次复制设备,可能需要执行此操作。

基本命令是 dotnet run,告知 .NET 生成本地 csproj 文件,然后运行该文件。 在运行此命令之前,请将命令行参数添加到末尾。

命令行类似于以下示例:

    // Format: dotnet run add-devices num-to-add copy-devices delete-source-devices delete-destination-devices

    // Add 1000 devices, don't copy them to the other hub, or delete them. 
    // The first argument is true, numToAdd is 50, and the other arguments are false.
    dotnet run true 1000 false false false 

    // Copy the devices you just added to the other hub; don't delete anything.
    // The first argument is false, numToAdd is 0, copy-devices is true, and the delete arguments are both false
    dotnet run false 0 true false false 

使用环境变量来表示连接字符串

  1. 若要运行该示例,需要获取新旧 IoT 中心的连接字符串,以及可用于存储临时工作文件的存储帐户的连接字符串。 我们将在环境变量中存储这些连接字符串的值。

  2. 若要获取连接字符串值,请登录到 Azure 门户

  3. 将连接字符串放到可方便检索它们的某个位置,例如记事本。 如果复制以下代码,则可将连接字符串直接粘贴到占位符所在的位置。 请不要在等号两边添加空格,否则会改变变量名称。 此外,不需要用双引号括住连接字符串。 如果用引号括住存储帐户连接字符串,脚本将运行失败。

    在 Windows 中设置环境变量:

    SET IOTHUB_CONN_STRING=<put connection string to original IoT hub here>
    SET DEST_IOTHUB_CONN_STRING=<put connection string to destination IoT hub here>
    SET STORAGE_ACCT_CONN_STRING=<put connection string to the storage account here>
    

    在 Linux 中设置环境变量:

    export IOTHUB_CONN_STRING="<put connection string to original IoT hub here>"
    export DEST_IOTHUB_CONN_STRING="<put connection string to destination IoT hub here>"
    export STORAGE_ACCT_CONN_STRING="<put connection string to the storage account here>"
    
  4. 对于 IoT 中心连接字符串,请在门户中转到每个中心。 可以在中心的“资源”中搜索。 如果你知道某个资源组,可以转到“资源组”,选择该资源组,然后从该资源组的资产列表中选择中心。

  5. 在中心的“设置”中选择“共享访问策略”,然后选择“iothubowner”并复制其中的一个连接字符串。 对目标中心执行相同的操作。 将连接字符串添加到相应的 SET 命令。

  6. 对于存储帐户连接字符串,请在“资源”中或其“资源组”下找到并打开该存储帐户。

  7. 在“设置”部分下,选择“访问密钥”并复制其中的一个连接字符串。 将连接字符串放到包含相应 SET 命令的文本文件中。

现在,你已在包含 SET 命令的文件中设置了环境变量,并知道命令行参数有哪些。 让我们运行该示例。

运行示例应用程序并使用命令行参数

  1. 打开命令提示符窗口。 选择“Windows”,然后键入 command prompt 打开命令提示符窗口。

  2. 逐个复制用于设置环境变量的命令,将其粘贴到命令提示符窗口,然后按 Enter。 完成后,在命令提示符窗口中键入 SET 以查看环境变量及其值。 将这些命令复制到命令提示符窗口后,除非打开新的命令提示符窗口,否则不再需要复制。

  3. 在命令提示符窗口中切换目录,直到进入 ./ImportExportDevicesSample(ImportExportDevicesSample.csproj 文件所在的目录)。 然后键入以下命令(包括命令行参数)。

    // Format: dotnet run add-devices num-to-add copy-devices delete-source-devices delete-destination-devices
    dotnet run arg1 arg2 arg3 arg4 arg5
    

    dotnet 命令将生成并运行应用程序。 由于在运行应用程序时会传入选项,因此每次运行应用程序时都可以更改这些选项的值。 例如,你可能想要运行该应用程序一次并创建新设备,然后再次运行该应用程序,并将这些设备复制到新中心,等等。 你还可以在同一次运行中执行所有这些步骤,不过,在确定迁移完成之前,最好不要删除任何设备。 下面是一个示例:我们创建了 1000 个设备,然后将其复制到另一个中心。

    // Format: dotnet run add-devices num-to-add copy-devices delete-source-devices delete-destination-devices
    
    // Add 1000 devices, don't copy them to the other hub or delete them. 
    dotnet run true 1000 false false false 
    
    // Do not add any devices. Copy the ones you just created to the other hub; don't delete anything.
    dotnet run false 0 true false false 
    

    验证设备已成功复制之后,可以从源中心删除设备,如下所示:

    // Format: dotnet run add-devices num-to-add copy-devices delete-source-devices delete-destination-devices
    // Delete the devices from the source hub.
    dotnet run false 0 false true false 
    

使用 Visual Studio 运行示例应用程序

  1. 若要在 Visual Studio 中运行应用程序,请将当前目录切换到 azureiot.sln 文件所在的文件夹。 然后在命令提示符窗口中运行以下命令,在 Visual Studio 中打开解决方案。 必须在设置环境变量的同一个命令窗口中执行此操作,因此这些变量是已知的。

    azureiot.sln
    
  2. 右键单击项目“ImportExportDevicesSample”并选择“设为启动项目”。

  3. 在 ImportExportDevicesSample 文件夹中 Program.cs 的顶部,设置五个选项的变量。

    // Add randomly created devices to the source hub.
    private static bool addDevices = true;
    //If you ask to add devices, this will be the number added.
    private static int numToAdd = 0; 
    // Copy the devices from the source hub to the destination hub.
    private static bool copyDevices = false;
    // Delete all of the devices from the source hub. (It uses the IoTHubConnectionString).
    private static bool deleteSourceDevices = false;
    // Delete all of the devices from the destination hub. (Uses the DestIotHubConnectionString).
    private static bool deleteDestDevices = false;
    
  4. 按 F5 运行应用程序。 运行完应用程序后,可以查看结果。

查看结果

可以在 Azure 门户中查看设备,并确认它们是否出现在新位置。

  1. 使用 Azure 门户转到新中心。 选择该中心,然后选择“IoT 设备”。 你将会看到从旧中心复制到新中心的设备。 还可以查看新中心的属性。

  2. 检查导入/导出错误:在 Azure 门户中转到 Azure 存储帐户,然后查看 ImportErrors.logdevicefiles 容器。 如果此文件为空(大小为 0),则表示未发生任何错误。 如果尝试多次导入同一设备,第二次导入时会拒绝该设备,并将一条错误消息添加到日志文件中。

提交更改

此时,你已将中心复制到新位置,并已将设备迁移到新中心。 接下来需要进行相应更改,使设备能够与新中心配合工作。

若要提交更改,需要执行以下步骤:

  • 更新每个设备以更改 IoT 中心主机名,将 IoT 中心主机名指向新中心。 应使用首次预配设备时所用的相同方法执行此操作。

  • 更改引用旧中心的所有应用程序,使其指向新中心。

  • 完成后,新中心应会启动并运行。 旧中心应该没有活动的设备并处于断开连接状态。

回滚所做更改

如果你决定回滚更改,请执行以下步骤:

  • 更新每个设备以更改 IoT 中心主机名,将 IoT 中心主机名指向旧中心。 应使用首次预配设备时所用的相同方法执行此操作。

  • 更改引用新中心的所有应用程序,使其指向旧中心。 例如,如果使用的是 Azure Analytics,则可能需要重新配置 Azure 流分析输入

  • 删除新中心。

  • 如果你有路由资源,则旧中心上的配置仍应指向正确的路由配置,并且在旧中心重启后,它应该仍可使用这些资源。

检查结果

若要检查结果,请将 IoT 解决方案更改为指向位于新位置的中心,然后运行它。 换而言之,请对新中心执行以前对旧中心所执行的相同操作,并确保它们正常工作。

如果已实施路由,请测试并确保将消息正确路由到资源。

清理

在确定新中心已启动并运行,并且设备可正常工作之前,请不要清理资源。 另外,如果使用了路由功能,请务必对此功能进行测试。 准备就绪后,执行以下步骤来清理旧资源:

  • 删除旧中心(如果尚未这样做)。 这会从该中心删除所有活动的设备。

  • 如果已将路由资源移到新位置,可以删除旧路由资源。

后续步骤

现已将 IoT 中心连同设备一起迁移到了新区域中的新中心。 有关对 IoT 中心内的标识注册表执行批量操作的详细信息,请参阅批量导入和导出 IoT 中心设备标识