利用Azure API Management 零停机迁移静态资源数据

背景

最近遇到一个运维的任务,因为安全和隐私的法规要求,原来的静态资源数据(图片、附件等)要求迁移到对应地区的存储中(海外)。目前已有静态资源约2亿个文件,总大小20TB,每日增长在15万左右。根据估算,如果直接进行跨区域在线复制,大约需要1个月左右的时间才能完成复制。而这个过程中又会不断写入新的数据。

方案

考虑到迁移过程中不能停机中止业务进行,所以想到了一个是否可以建立一个机制,当用户请求静态资源时,先尝试从新的存储中读取。如果新的存储中找不到对应的资源,那么就尝试从旧的存储中读取。由于一些原因,目前没有办法考虑应用的改造。之前有另外一个想法,新的静态资源存储在一个新目录下,然后通过7层的负载均衡器来判断应该访问哪个存储,结果受限于实际情况只能采用尝试的办法。

 具体步骤考虑如下:

  1. 创建一个新的CDN服务,用来加速静态资源的访问。原有的CDN服务因为区域问题安排下线
  2. 使用 Azure API Management 服务来实现重试。当CDN回源时,APIM会先请求后端 Azure Blob 存储(海外),如果 Blob 中不存在,则重新尝试从原有国内服务器获取
  3. 更新 DNS 解析到新的 CDN 服务,这个时候由于Blob 中没有文件,实际所有请求都是发到了国内存储中
  4. 修改应用配置,将新文件写入海外 Blob 中。这时候如果用户请求新的文件,APIM 会从 Blob存储中获取资源
  5. 利用 azcopy 工具复制国内存储中的静态资源到海外Azure Blob中,直到完成复制
  6. (可选)修改 CDN 的源,直接从Azure Blob中获取静态资源,删除 APIM 配置

思考点:

  • 为什么使用APIM?原因很简单,不需要开发代码,并且可以复用现有的
  • APIM尝试访问静态资源时,因为考虑到老的图片的远高于新图片的数量,所以先尝试访问 国内存储(source)
  • 应用无法修改的情况下,增加了一个中间件用于适配不同的协议
  • 因为CDN的存在,所以理论上APIM可以不用再做缓存
  • 考虑优先迁移最近被访问到的数据,这样可以减少重试次数

POC验证

从目前来看,关键点是 APIM 如何实现重试。

1. 创建2个 Azure Blob ,其中分别代表国内存储和海外存储,另外新建一个APIM。

2. 打开APIM,新增2个Backends

 3. 在 APIM 中新增一个API,用于请求静态资源

4. 设置 API 的Policies

<policies>
    <inbound>
        <base />
        <set-backend-service backend-id="blob-source" />
    </inbound>
    <backend>
        <retry condition="@(context.Response.StatusCode == 404)" count="2" interval="1" first-fast-retry="true">
            <choose>
                <when condition="@(context.Response.StatusCode == 404)">
                    <set-backend-service backend-id="blob-target" />
                </when>
            </choose>
            <forward-request />
        </retry>
    </backend>
    <outbound>
        <base />
    </outbound>
</policies>

这里解释一下,首先在 inbound 里设置了 backend-id 为源存储服务,这个发送到这个API的请求会被转发到刚才 Backends 添加的对应服务器地址中。

然后在 backend 配置中增加了一个重试(retry),条件是当 backend 返回的 response.StatusCode 为404的时候,立刻(first-fast-retry)进行重试。并且在重试的时候我们重新设置 backend-id 为新的存储服务。

这里用了一个choose,我一开始理解是不需要的,但实际上如果不加会无法实现我们的效果。

5. 在 source 和 target 存储中分别创建 images 容器,然后分别上传 2张不同的图片(名称也不同)。然后如果我从source访问只存在于target存储中的文件,就会出现404错误。

6. 尝试从 APIM 访问图片,无论输入哪个图片名称都可以访问成功。使用跟踪看了一下日志,当第一个服务器返回404后,请求了第二个服务器。 

 7. 当访问一个真实不存在的图片时,会反馈一个404页面,这个不想看到,所以增Policies,在outbound里,当最终404时,不要再返回下面这个error的具体信息,而是set-body为空。

<policies>
    <inbound>
        <base />
    </inbound>
    <backend>
        <retry condition="@(context.Response.StatusCode == 404)" count="2" interval="1" first-fast-retry="true">
            <choose>
                <when condition="@(context.Response.StatusCode == 404)">
                    <set-backend-service backend-id="blob-target" />
                </when>
            </choose>
            <forward-request />
        </retry>
    </backend>
    <outbound>
        <base />
        <choose>
            <when condition="@(context.Response.StatusCode == 404)">
                <return-response>
                    <set-status code="404" reason="NotFound" />
                    <set-body />
                </return-response>
            </when>
        </choose>
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

8. 添加一个 CDN 服务,配置一个 Endpoint,origin type 为 API Management

 9. 测试一下从CDN的域名访问两张不同的图片,没有问题,可以正常访问。测试图片来源于互联网。

10. 我们在 azcopy 的基础上优先迁移最近被访问的图片,所以增加了一个 eventhubs ,当用户访问的图片不在 source 中,我们就发送一个消息到队列中。然后通过其他一些服务(比如 logic app)订阅消息,实现异步迁移。

11. 给APIM增加一个记录器,目前居然没有UI可以操作,只能通过调用API的方式进行。(文章参考),首先准备请求的 json 文件, 配置 eventhubs 的 connectionString

{
  "properties": {
    "loggerType": "azureEventHub",
    "description": "adding a new logger",
    "credentials": {
      "name": "apim",
      "connectionString": "Endpoint=sb://static-resources-migration-task.servicebus.windows.net/;SharedAccessKeyName=apim;SharedAccessKey=xxxx;EntityPath=migration-task"
    }
  }
}

12. 创建logger

$subscriptionId="xxxx"
$resourceGroupName="static_resources_migration"
$serviceName="static0resources0controller"
$loggerId="migrationtask"

$API_URI="https://management.azure.com/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.ApiManagement/service/${serviceName}/loggers/${loggerId}?api-version=2021-08-01"
$ACCESS_TOKEN=(az account get-access-token --resource=https://management.azure.com/ --query accessToken -o tsv)
$beareruthValue = "Bearer $ACCESS_TOKEN"

$body = [IO.File]::ReadAllBytes("ApiManagementCreateEHLogger.json")
$Headers = @{
    Authorization = $beareruthValue
}

Invoke-RestMethod -Method PUT -Uri $API_URI  -ContentType application/json  -Headers $Headers -Body $body 

13. 执行完成,得到返回结果

14. 增加发送到 eventhubs 的 Policies ,只有访问 source 没有,并返回404 以后才记录到 event hubs 中。这里不考虑重复发送的问题,订阅此事件的应用来做判断就好了。

<policies>
    <inbound>
        <base />
    </inbound>
    <backend>
        <retry condition="@(context.Response.StatusCode == 404)" count="2" interval="1" first-fast-retry="true">
            <choose>
                <when condition="@(context.Response.StatusCode == 404)">
                    <set-backend-service backend-id="blob-target" />
                    <log-to-eventhub logger-id="migrationtask">@{
        return new JObject(
            new JProperty("EventTime", DateTime.UtcNow.ToString()),
            new JProperty("ServiceName", context.Deployment.ServiceName),
            new JProperty("RequestId", context.RequestId),
            new JProperty("RequestPath", context.Request.Url.Path),
            new JProperty("OperationName", context.Operation.Name)
        ).ToString();
    }</log-to-eventhub>
                </when>
            </choose>
            <forward-request />
        </retry>
    </backend>
    <outbound>
        <base />
        <choose>
            <when condition="@(context.Response.StatusCode == 404)">
                <return-response>
                    <set-status code="404" reason="NotFound" />
                    <set-body />
                </return-response>
            </when>
        </choose>
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

15. 利用 Service Bus Exporer 查看发送到 Eventhubs 的记录。 

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值