前言:
童鞋们估计已经抱怨过n次这个问题,为啥托管磁盘不能被用来制作镜像或者制作快照后在其他订阅或其它区域(Region)来创建虚拟机,说好的 Infrastructure as a code 呢,刚刚激动的做好了镜像,把应用架构描述成 infra code,一键点亮。跨了订阅,跨了区域玩不转啦,XX**。不要慌,这个事儿并非没有解,就是稍微麻烦,你可以把托管磁盘做导出到目标订阅或区域的存储账户,然后在目标订阅内或区域内创建镜像。一两个订阅还好,一大把订阅那不是很坑,本文后面就带大家以玩的心态,边了解 Azure 的新服务 EventGrid,LogicApp,ACI,然后一起来配合使用来实现跨订阅或跨地域的托管磁盘的自动导出拷贝。
架构逻辑:
通过 EventGrid 来监听 VM 创建事件,当事件发生后,LogicApp 作为该事件的 Subscriber 消费者,触发自动化流水线,将该虚拟机的磁盘拷贝到目的订阅或目的区域的存储账户中,拷贝过程通过azcopy来完成,选择azcopy的原因速度块。整个架构中采用 Severless 的思路,所以将 azcopy 封装到一个做好的容器镜像中,在调用时通过在 Serverless 的 Azure Container Instance 中进行执行,执行完毕资源回收,可以享受容器启动的敏捷性的同时实现按市场付费计算资源的要求。
逻辑实现细节:
整个逻辑实现的重点在LogicApp,也就是自动化流水线的实现,流水线的规程可以参阅如下 LogicApp 的 Code,大家可以导入自己的环境在里面进行学习和修改。犯懒的童鞋也别难过,借此安利一把,Image 共享这个服务 Azure 平台马上会当作功能给到大家 Preview,点我了解更多
1 { 2 "$connections": { 3 "value": { 4 "aci": { 5 "connectionId": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/resourceGroups/eventgrid/providers/Microsoft.Web/connections/aci", 6 "connectionName": "aci", 7 "id": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/providers/Microsoft.Web/locations/southeastasia/managedApis/aci" 8 }, 9 "arm": { 10 "connectionId": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/resourceGroups/eventgrid/providers/Microsoft.Web/connections/arm", 11 "connectionName": "arm", 12 "id": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/providers/Microsoft.Web/locations/southeastasia/managedApis/arm" 13 }, 14 "azureeventgrid": { 15 "connectionId": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/resourceGroups/eventgrid/providers/Microsoft.Web/connections/azureeventgrid", 16 "connectionName": "azureeventgrid", 17 "id": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/providers/Microsoft.Web/locations/southeastasia/managedApis/azureeventgrid" 18 } 19 } 20 }, 21 "definition": { 22 "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 23 "actions": { 24 "Compose": { 25 "inputs": { 26 "vmname": "@substring(body(‘Parse_JSON‘)?[‘subject‘],add(16,lastIndexOf(body(‘Parse_JSON‘)?[‘subject‘],‘virtualMachines/‘)),sub(length(body(‘Parse_JSON‘)?[‘subject‘]),add(16,lastIndexOf(body(‘Parse_JSON‘)?[‘subject‘],‘virtualMachines/‘))))" 27 }, 28 "runAfter": { 29 "Parse_JSON": [ 30 "Succeeded" 31 ] 32 }, 33 "type": "Compose" 34 }, 35 "Compose_2": { 36 "inputs": "@triggerBody()", 37 "runAfter": {}, 38 "type": "Compose" 39 }, 40 "Compose_3": { 41 "inputs": "@body(‘Parse_JSON_3‘)?[‘storageProfile‘][‘osDisk‘]", 42 "runAfter": { 43 "Parse_JSON_3": [ 44 "Succeeded" 45 ] 46 }, 47 "type": "Compose" 48 }, 49 "Create_container_group": { 50 "inputs": { 51 "body": { 52 "location": "southeastasia", 53 "properties": { 54 "containers": [ 55 { 56 "name": "@{concat(‘azcp‘,rand(1,100))}", 57 "properties": { 58 "command": [ 59 "/bin/bash", 60 "-c", 61 "azcopy --destination $destination --source $source --dest-key $destkey" 62 ], 63 "environmentVariables": [ 64 { 65 "name": "destination", 66 "value": "@{concat(‘https://diskclone.blob.core.windows.net/diskclone/‘,outputs(‘Compose_3‘)?[‘name‘])}" 67 }, 68 { 69 "name": "source", 70 "value": "@body(‘mdsasurl‘)?[‘accessSAS‘]" 71 }, 72 { 73 "name": "destkey", 74 "value": "yrbf9VRMOFN70+tAmoianfx1TGwJPAp+26aQ2g7EdB+4UqNBSoa3bJP9uEwJy2eGUz5y1NPnZx8XuKJP0W3p8w==" 75 } 76 ], 77 "image": "acrbuildpilot.azurecr.io/azcopy:v1", 78 "resources": { 79 "requests": { 80 "cpu": 1, 81 "memoryInGB": 1 82 } 83 } 84 } 85 } 86 ], 87 "imageRegistryCredentials": [ 88 { 89 "password": "iKccXllxyrn8aoSfPtQdUZTzDLIh/VRv", 90 "server": "acrbuildpilot.azurecr.io", 91 "username": "acrbuildpilot" 92 } 93 ], 94 "osType": "Linux", 95 "restartPolicy": "Never" 96 } 97 }, 98 "host": { 99 "connection": {100 "name": "@parameters(‘$connections‘)[‘aci‘][‘connectionId‘]"101 }102 },103 "method": "put",104 "path": "/subscriptions/@{encodeURIComponent(‘4507938f-a0ac-4571-978e-7cc741a60af8‘)}/resourceGroups/@{encodeURIComponent(‘aci‘)}/providers/Microsoft.ContainerInstance/containerGroups/@{encodeURIComponent(concat(‘azcp‘, rand(1, 100)))}",105 "queries": {106 "x-ms-api-version": "2017-10-01-preview"107 }108 },109 "runAfter": {110 "mdsasurl": [111 "Succeeded"112 ]113 },114 "type": "ApiConnection"115 },116 "Parse_JSON": {117 "inputs": {118 "content": "@outputs(‘Compose_2‘)",119 "schema": {120 "properties": {121 "data": {122 "properties": {123 "authorization": {124 "properties": {125 "action": {126 "type": "string"127 },128 "evidence": {129 "properties": {130 "role": {131 "type": "string"132 }133 },134 "type": "object"135 },136 "scope": {137 "type": "string"138 }139 },140 "type": "object"141 },142 "claims": {143 "properties": {144 "aio": {145 "type": "string"146 },147 "appid": {148 "type": "string"149 },150 "appidacr": {151 "type": "string"152 },153 "aud": {154 "type": "string"155 },156 "e_exp": {157 "type": "string"158 },159 "exp": {160 "type": "string"161 },162 "groups": {163 "type": "string"164 },165 "http://schemas.microsoft.com/2012/01/devicecontext/claims/identifier": {166 "type": "string"167 },168 "http://schemas.microsoft.com/claims/authnclassreference": {169 "type": "string"170 },171 "http://schemas.microsoft.com/claims/authnmethodsreferences": {172 "type": "string"173 },174 "http://schemas.microsoft.com/identity/claims/objectidentifier": {175 "type": "string"176 },177 "http://schemas.microsoft.com/identity/claims/scope": {178 "type": "string"179 },180 "http://schemas.microsoft.com/identity/claims/tenantid": {181 "type": "string"182 },183 "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname": {184 "type": "string"185 },186 "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": {187 "type": "string"188 },189 "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": {190 "type": "string"191 },192 "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname": {193 "type": "string"194 },195 "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn": {196 "type": "string"197 },198 "iat": {199 "type": "string"200 },201 "in_corp": {202 "type": "string"203 },204 "ipaddr": {205 "type": "string"206 },207 "iss": {208 "type": "string"209 },210 "name": {211 "type": "string"212 },213 "nbf": {214 "type": "string"215 },216 "onprem_sid": {217 "type": "string"218 },219 "puid": {220 "type": "string"221 },222 "uti": {223 "type": "string"224 },225 "ver": {226 "type": "string"227 }228 },229 "type": "object"230 },231 "correlationId": {232 "type": "string"233 },234 "operationName": {235 "type": "string"236 },237 "resourceProvider": {238 "type": "string"239 },240 "resourceUri": {241 "type": "string"242 },243 "status": {244 "type": "string"245 },246 "subscriptionId": {247 "type": "string"248 },249 "tenantId": {250 "type": "string"251 }252 },253 "type": "object"254 },255 "dataVersion": {256 "type": "string"257 },258 "eventTime": {259 "type": "string"260 },261 "eventType": {262 "type": "string"263 },264 "id": {265 "type": "string"266 },267 "metadataVersion": {268 "type": "string"269 },270 "subject": {271 "type": "string"272 },273 "topic": {274 "type": "string"275 }276 },277 "type": "object"278 }279 },280 "runAfter": {281 "Compose_2": [282 "Succeeded"283 ]284 },285 "type": "ParseJson"286 },287 "Parse_JSON_2": {288 "inputs": {289 "content": "@outputs(‘Compose‘)",290 "schema": {291 "properties": {292 "vmname": {293 "type": "string"294 }295 },296 "type": "object"297 }298 },299 "runAfter": {300 "Compose": [301 "Succeeded"302 ]303 },304 "type": "ParseJson"305 },306 "Parse_JSON_3": {307 "inputs": {308 "content": "@body(‘Read_a_resource‘)?[‘properties‘]",309 "schema": {310 "properties": {311 "hardwareProfile": {312 "properties": {313 "vmSize": {314 "type": "string"315 }316 },317 "type": "object"318 },319 "networkProfile": {320 "properties": {321 "networkInterfaces": {322 "items": {323 "properties": {324 "id": {325 "type": "string"326 }327 },328 "required": [329 "id"330 ],331 "type": "object"332 },333 "type": "array"334 }335 },336 "type": "object"337 },338 "osProfile": {339 "properties": {340 "adminUsername": {341 "type": "string"342 },343 "computerName": {344 "type": "string"345 },346 "linuxConfiguration": {347 "properties": {348 "disablePasswordAuthentication": {349 "type": "boolean"350 }351 },352 "type": "object"353 },354 "secrets": {355 "type": "array"356 }357 },358 "type": "object"359 },360 "provisioningState": {361 "type": "string"362 },363 "storageProfile": {364 "properties": {365 "dataDisks": {366 "type": "array"367 },368 "imageReference": {369 "properties": {370 "offer": {371 "type": "string"372 },373 "publisher": {374 "type": "string"375 },376 "sku": {377 "type": "string"378 },379 "version": {380 "type": "string"381 }382 },383 "type": "object"384 },385 "osDisk": {386 "properties": {387 "caching": {388 "type": "string"389 },390 "createOption": {391 "type": "string"392 },393 "diskSizeGB": {394 "type": "number"395 },396 "managedDisk": {397 "properties": {398 "id": {399 "type": "string"400 },401 "storageAccountType": {402 "type": "string"403 }404 },405 "type": "object"406 },407 "name": {408 "type": "string"409 },410 "osType": {411 "type": "string"412 }413 },414 "type": "object"415 }416 },417 "type": "object"418 },419 "vmId": {420 "type": "string"421 }422 },423 "type": "object"424 }425 },426 "runAfter": {427 "Read_a_resource": [428 "Succeeded"429 ]430 },431 "type": "ParseJson"432 },433 "Read_a_resource": {434 "inputs": {435 "host": {436 "connection": {437 "name": "@parameters(‘$connections‘)[‘arm‘][‘connectionId‘]"438 }439 },440 "method": "get",441 "path": "/subscriptions/@{encodeURIComponent(‘c04b3c63-8dfe-4f98-be18-e71ff67a1f4e‘)}/resourcegroups/@{encodeURIComponent(‘eventgrid‘)}/providers/@{encodeURIComponent(‘Microsoft.Compute‘)}/@{encodeURIComponent(concat(‘virtualMachines/‘, body(‘Parse_JSON_2‘)?[‘vmname‘]))}",442 "queries": {443 "x-ms-api-version": "2017-12-01"444 }445 },446 "runAfter": {447 "Parse_JSON_2": [448 "Succeeded"449 ]450 },451 "type": "ApiConnection"452 },453 "deallocate_vm": {454 "inputs": {455 "host": {456 "connection": {457 "name": "@parameters(‘$connections‘)[‘arm‘][‘connectionId‘]"458 }459 },460 "method": "post",461 "path": "/subscriptions/@{encodeURIComponent(‘c04b3c63-8dfe-4f98-be18-e71ff67a1f4e‘)}/resourcegroups/@{encodeURIComponent(‘eventgrid‘)}/providers/@{encodeURIComponent(‘Microsoft.Compute‘)}/@{encodeURIComponent(concat(‘virtualMachines/‘, body(‘Parse_JSON_2‘)?[‘vmname‘]))}/@{encodeURIComponent(‘deallocate‘)}",462 "queries": {463 "x-ms-api-version": "2017-12-01"464 }465 },466 "runAfter": {467 "Compose_3": [468 "Succeeded"469 ]470 },471 "type": "ApiConnection"472 },473 "mdsasurl": {474 "inputs": {475 "content": "@body(‘storage_sasurl‘)",476 "schema": {477 "properties": {478 "accessSAS": {479 "type": "string"480 }481 },482 "type": "object"483 }484 },485 "runAfter": {486 "storage_sasurl": [487 "Succeeded"488 ]489 },490 "type": "ParseJson"491 },492 "storage_sasurl": {493 "inputs": {494 "body": {495 "access": "Read",496 "durationInSeconds": 3600497 },498 "host": {499 "connection": {500 "name": "@parameters(‘$connections‘)[‘arm‘][‘connectionId‘]"501 }502 },503 "method": "post",504 "path": "/subscriptions/@{encodeURIComponent(‘c04b3c63-8dfe-4f98-be18-e71ff67a1f4e‘)}/resourcegroups/@{encodeURIComponent(‘eventgrid‘)}/providers/@{encodeURIComponent(‘Microsoft.Compute‘)}/@{encodeURIComponent(concat(‘disks/‘, outputs(‘Compose_3‘)?[‘name‘]))}/@{encodeURIComponent(‘beginGetAccess‘)}",505 "queries": {506 "x-ms-api-version": "2017-03-30"507 }508 },509 "runAfter": {510 "deallocate_vm": [511 "Succeeded"512 ]513 },514 "type": "ApiConnection"515 }516 },517 "contentVersion": "1.0.0.0",518 "outputs": {},519 "parameters": {520 "$connections": {521 "defaultValue": {},522 "type": "Object"523 }524 },525 "triggers": {526 "When_a_resource_event_occurs": {527 "inputs": {528 "body": {529 "properties": {530 "destination": {531 "endpointType": "webhook",532 "properties": {533 "endpointUrl": "@{listCallbackUrl()}"534 }535 },536 "filter": {537 "includedEventTypes": [538 "Microsoft.Resources.ResourceWriteSuccess"539 ],540 "subjectBeginsWith": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/resourcegroups/eventgrid/providers/Microsoft.Compute/virtualMachines"541 },542 "topic": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/resourceGroups/eventgrid"543 }544 },545 "host": {546 "connection": {547 "name": "@parameters(‘$connections‘)[‘azureeventgrid‘][‘connectionId‘]"548 }549 },550 "path": "/subscriptions/@{encodeURIComponent(‘c04b3c63-8dfe-4f98-be18-e71ff67a1f4e‘)}/providers/@{encodeURIComponent(‘Microsoft.Resources.ResourceGroups‘)}/resource/eventSubscriptions",551 "queries": {552 "x-ms-api-version": "2017-09-15-preview"553 }554 },555 "splitOn": "@triggerBody()",556 "type": "ApiConnectionWebhook"557 }558 }559 }560 }
参考资料:
因为 EventGrid 和 LogicApp 学习成本都非常低,所以没有 Step by Step 给大家说明操作步骤,这里为了方便大家学习,给大家列举几个文档,帮助大家快速上手。
EventGrid 入门:https://docs.microsoft.com/en-us/azure/event-grid/overview
EventGrid 消息格式:https://docs.microsoft.com/en-us/azure/event-grid/event-schema
LogicApp 入门:https://docs.microsoft.com/en-us/azure/logic-apps/logic-apps-overview
LogicApp Connector 手册:https://docs.microsoft.com/en-us/connectors/
LogicApp 内置函数手册:https://docs.microsoft.com/en-us/azure/logic-apps/workflow-definition-language-functions-reference