如何通过Azure DevOps流水线实现两个Azure ServiceBus实例故障转移自动化?
Azure ServiceBus跨区域故障转移+队列清理的Azure DevOps YAML自动化实现
需求明确
我们有两个跨区域Azure ServiceBus实例(SB-A/区域1、SB-B/区域2),已启用手动故障转移,需要通过Azure DevOps YAML流水线实现双向故障转移全流程自动化,且关键操作节点支持人工决策触发。核心流程(以A→B为例):
- 检查实例配对状态,未配对则用预设别名SB-PAIR-AB完成配对
- 验证配对后执行故障转移,确认SB-B成为活动实例、SB-A变为被动
- 人工确认后删除被动实例所有队列(需先断开配对,完成删除后重新配对)
- B→A故障转移流程完全对称
YAML流水线实现(带人工审批)
以下流水线通过参数控制故障转移方向,嵌入manualValidation实现人工决策触发,覆盖所有需求节点:
parameters: - name: failoverDirection displayName: 故障转移方向 type: string default: AtoB values: - AtoB - BtoA variables: # 根据方向动态赋值资源信息,避免重复代码 ${{ if eq(parameters.failoverDirection, 'AtoB') }}: activeNamespace: SB-B activeRG: SB-B-rg activeSub: B-subscription passiveNamespace: SB-A passiveRG: SB-A-rg passiveSub: A-Subscription partnerNamespaceId: "/subscriptions/xxxxxxxx/resourceGroups/SB-B-rg/providers/Microsoft.ServiceBus/namespaces/SB-B" sourceNamespace: SB-A sourceRG: SB-A-rg ${{ else }}: activeNamespace: SB-A activeRG: SB-A-rg activeSub: A-Subscription passiveNamespace: SB-B passiveRG: SB-B-rg passiveSub: B-subscription partnerNamespaceId: "/subscriptions/xxxxxx/resourceGroups/SB-A-rg/providers/Microsoft.ServiceBus/namespaces/SB-A" sourceNamespace: SB-B sourceRG: SB-B-rg aliasName: SB-PAIR-AB jobs: - job: Failover_Flow displayName: 跨区域故障转移全流程 pool: vmImage: 'ubuntu-latest' steps: # 1. 检查配对状态 - script: | az login --service-principal -u $(servicePrincipalId) -p $(servicePrincipalKey) --tenant $(tenantId) # 检查别名是否存在 aliasExists=$(az servicebus georecovery-alias show --alias $(aliasName) --resource-group $(sourceRG) --namespace-name $(sourceNamespace) --subscription $(passiveSub) --query "name" -o tsv || echo "NotFound") if [ "$aliasExists" == "NotFound" ]; then echo "##vso[task.setvariable variable=isPaired]false" else # 检查配对状态是否正常 pairingStatus=$(az servicebus georecovery-alias show --alias $(aliasName) --resource-group $(sourceRG) --namespace-name $(sourceNamespace) --subscription $(passiveSub) --query "properties.provisioningState" -o tsv) if [ "$pairingStatus" == "Succeeded" ]; then echo "##vso[task.setvariable variable=isPaired]true" else echo "##vso[task.setvariable variable=isPaired]false" fi fi displayName: 检查ServiceBus实例配对状态 env: servicePrincipalId: $(azureServicePrincipalId) servicePrincipalKey: $(azureServicePrincipalKey) tenantId: $(azureTenantId) # 2. 自动完成配对(未配对时执行) - script: | az servicebus georecovery-alias set \ --alias $(aliasName) \ --resource-group $(sourceRG) \ --namespace-name $(sourceNamespace) \ --partner-namespace $(partnerNamespaceId) \ --subscription $(passiveSub) # 轮询等待配对完成(最多等待10分钟) for i in {1..20}; do status=$(az servicebus georecovery-alias show --alias $(aliasName) --resource-group $(sourceRG) --namespace-name $(sourceNamespace) --subscription $(passiveSub) --query "properties.provisioningState" -o tsv) if [ "$status" == "Succeeded" ]; then echo "配对完成" exit 0 fi echo "等待配对完成,当前状态:$status" sleep 30 done echo "配对超时" exit 1 displayName: 执行ServiceBus实例配对 condition: eq(variables.isPaired, 'false') # 3. 执行故障转移并验证活动实例 - script: | # 触发故障转移 az servicebus georecovery-alias fail-over \ --alias $(aliasName) \ --namespace-name $(activeNamespace) \ --is-safe-failover y \ --resource-group $(activeRG) \ --subscription $(activeSub) # 验证活动实例 activeStatus=$(az servicebus georecovery-alias show --alias $(aliasName) --resource-group $(activeRG) --namespace-name $(activeNamespace) --subscription $(activeSub) --query "properties.role" -o tsv) if [ "$activeStatus" != "Primary" ]; then echo "活动实例验证失败" exit 1 fi passiveStatus=$(az servicebus georecovery-alias show --alias $(aliasName) --resource-group $(passiveRG) --namespace-name $(passiveNamespace) --subscription $(passiveSub) --query "properties.role" -o tsv) if [ "$passiveStatus" != "Secondary" ]; then echo "被动实例验证失败" exit 1 fi echo "故障转移验证成功:$(activeNamespace)为活动实例,$(passiveNamespace)为被动实例" displayName: 执行故障转移并验证实例状态 # 4. 人工决策:确认是否删除被动实例队列 - task: manualValidation@0 displayName: 人工确认是否删除被动实例队列 inputs: notifyUsers: '运维团队邮箱列表' instructions: '确认当前被动实例为$(passiveNamespace),是否执行队列删除操作?' onTimeout: 'reject' # 5. 断开配对(必须操作,否则无法删除队列) - script: | az servicebus georecovery-alias delete \ --alias $(aliasName) \ --resource-group $(passiveRG) \ --namespace-name $(passiveNamespace) \ --subscription $(passiveSub) \ --yes # 等待断开完成 sleep 60 displayName: 断开ServiceBus实例配对 # 6. 批量删除被动实例所有队列 - script: | # 获取所有队列名称 queueNames=$(az servicebus queue list --resource-group $(passiveRG) --namespace-name $(passiveNamespace) --subscription $(passiveSub) --query "[].name" -o tsv) if [ -z "$queueNames" ]; then echo "被动实例无队列,无需删除" exit 0 fi # 循环删除队列 for queue in $queueNames; do echo "删除队列:$queue" az servicebus queue delete \ --name $queue \ --namespace-name $(passiveNamespace) \ --resource-group $(passiveRG) \ --subscription $(passiveSub) done displayName: 批量删除被动实例所有队列 # 7. 重新发起配对 - script: | az servicebus georecovery-alias set \ --alias $(aliasName) \ --resource-group $(sourceRG) \ --namespace-name $(sourceNamespace) \ --partner-namespace $(partnerNamespaceId) \ --subscription $(passiveSub) # 轮询等待配对完成 for i in {1..20}; do status=$(az servicebus georecovery-alias show --alias $(aliasName) --resource-group $(sourceRG) --namespace-name $(sourceNamespace) --subscription $(passiveSub) --query "properties.provisioningState" -o tsv) if [ "$status" == "Succeeded" ]; then echo "重新配对完成" exit 0 fi echo "等待重新配对完成,当前状态:$status" sleep 30 done echo "重新配对超时" exit 1 displayName: 重新发起ServiceBus实例配对
关键命令优化说明
- 配对状态检查:新增
az servicebus georecovery-alias show命令判断配对是否存在且状态正常,避免盲目执行配对操作 - 批量删除队列:通过
az servicebus queue list动态获取所有队列名,实现批量删除,无需手动指定队列名称 - 断开配对命令:补充
az servicebus georecovery-alias delete命令,解决配对状态下无法删除队列的问题 - 状态轮询验证:在配对、故障转移步骤中加入轮询逻辑,确保操作完成后再进入下一个节点,避免异步操作导致的流程错误
注意事项
- 流水线需配置Azure服务连接(Service Principal),确保拥有ServiceBus实例的读写权限
manualValidation任务的notifyUsers需替换为实际运维人员邮箱,确保人工审批及时触发- 可根据实际需求调整轮询等待时间(当前为每30秒检查一次,最多等待10分钟)
内容的提问来源于stack exchange,提问作者Vowneee
相关产品推荐
相关产品推荐

