Serverless实战:通过 Component 实现多地域部署容灾

2020 年 7 月 30 日

Serverless实战:通过 Component 实现多地域部署容灾

单点故障是实际生产中无法避免的,单副本的存储方案也早已无法满足业务的可靠性要求。现在,我们通常都会做双机存储架构,会涉及到主备、主从、主主模式。



在 Serverless 架构下,高可用方案或者容灾方案是否还需要主备、主从以及主主等模式?如果还需要,那么又是什么样子?


Serverless 与多地域部署


针对服务可用性,几乎每个云厂商都会有非常高的承诺,但是我们也不能掉以轻心,认为不会出现故障导致不可用,容灾方案是必须要有的。


在传统主机时代有主备、主从以及主主模式,这个模式更多针对的是单台机器或者某个集群。但是在 Serverless 架构下,没有机器和集群的概念(至少在用户层面没有),是不是就表示在 Serverless 架构下无法做容灾?


理论上,云厂商会保证整个服务的可用性,如果云厂商管理的某个机器出现故障,机器会被及时剔除,确保新的函数在安全、稳定、健康的环境下启动,正常提供服务。但在实际情况中,由于某些原因,云厂商也可能会在某个地域出现大规模故障,这时如何确保服务依旧可用,而不是苦苦等待云厂商的恢复?


针对单地域解析的网站,我们可以实现多地域的主备方案。在云函数中,多地域的主备方案更加经济实惠,因为函数是按量付费的,完全可以将函数复制到其他的地域,只要不进行触发,就不会产生额外的费用。相对于传统主机时代的主备模式,这种主备方案显得更加人性化。



一般情况下,单地域部署的服务中比较容易出问题的是 API 网关服务、云函数服务、数据库服务。其中,数据库服务可以考虑跨地域主从、跨区域同步,一旦出现问题,就在函数中做一个负载,确保整体数据不会出现问题,在腾讯云的云数据库描述下可以看到:


本地 IDC 机房 MySQL 数据库与云数据库 MySQL 之间可以通过数据迁移服务实时同步数据,本地IDC机房如遇到断电、网络故障等引起数据库服务中断,可迅速切换数据库服务至作为灾难备份的云数据库 MySQL 实例,实现数据库容灾;同时,云数据库 MySQL 可支持同城多可用区灾备/跨城灾备,保障高可用。


所以说,数据库相关服务的容灾可以通过备用数据库来提高可用性。


那么云函数和 API 网关怎么解决?


以多地域部署容灾为例,我们可以考虑这样的架构:



同样是作为单地域解析服务,相对来说,多地域部署更加安全稳定,一旦某个地域的服务出现问题(例如 API 网关,云函数),都可以通过监控程序及时发现,并且迅速切换解析到其它地域。多地域部署的监控函数与时间触发器进行结合,定期进行网站可用性的排查,一旦出现问题,就可以在云解析层面进行解析切换,实现单地域服务的多地域部署容灾方案:



这个方案的逻辑是先请求服务是否可用,如果不可用,则获取容灾列表,剔除不可用的服务,并通过云解析进行可用区的解析。在实际生产中,一旦确定某个服务不可用,还要进行精确告警,在获得到不可用解析记录对应的服务之后,通过邮件或者企业微信、短信等方法进行告警。


至此,一个简单版的“高可用”服务就算做好了,有的读者可能有所疑问:


  1. 切换解析不需要时间吗?怎么确保TTL按时生效?

  2. 对服务进行修复,是否比切换解析更加靠谱呢?


针对问题 1,其实切换解析是需要时间的,虽然一小部分的机房可能不会按照设定的 TTL 进行生效,但是大部分的机房都是可以按照设定及时生效,所以需要注意的是,在添加解析的时候要尽可能地确保 TTL 时间短一些,目前腾讯云的云解析付费版最低 TTL 可以设置为 1s,免费版是 600s。


针对问题 2,在云函数上运行服务很少会因为流量太高导致服务不可用,或者服务中存在 bug 导致整个项目不可用,因为云厂商会解决很大一部分的可用性,例如流量并发问题等。另外,就算是程序中存在 bug,在 Serverless 架构下,函数的前一次后一次运行实际上关联性不大,所以完全可以通过告警来确定,人为排查消灭隐患。事实上,在 Serverless 架构下出现大规模服务性灾难,多数情况都是云厂商的问题(此处已经排除掉用户代码层面的 bug),而这种问题一旦出现,就不是我们能够掌控的了,是否可以修复、什么时候修复。



如果是 API 网关层面出现问题,可以通过上一层来解决,例如云解析的切换;如果是函数层面出现问题,可以考虑切换到 API 网关到同区域的备用函数;如果是函数服务的整个区域性故障(概率非常低),可以考虑切换解析到备用区。


如果不是单地域提供服务,那么就需要考虑多地域部署、多地域就近接入以及多地域容灾方案。



从上图可以看到,我们将一个域名,划分地域进行解析,例如华北、华东两个地区,就可分别解析到北京 1 区、北京 2 区两个不同的机房中两台不同的服务器上。当其中一个服务不可用时,其他区域不受影响,可以使用云函数对解析进行修改,将其解析到每个地区的备用服务上。



整体结构图:



传统的服务需要每个地区部署多套服务,无疑大大增加了成本,但是在 Serverless 架构下,即使部署了多套云函数还是按量付费,大大节约了成本。


若采用 Serverless 架构作为后端服务,以华北地区为例,华北地区用户在访问后端服务的时候,通过 DNS 重定向到北京区的 API 网关,然后再由 API 网关触发北京区云函数,此时我们需要两个云函数对服务进行监控,一个函数对云函数进行监控,当云函数服务失效之后,可以将 API 网关绑定到备用的函数上,另一个监控是对 API 网关进行监控,当某个地域的 API 网关失效之后,可以对解析进行修改,重定向到备用地域的 API 网关上,尽可能快速的保证服务的可用性。


如果想要让服务更稳定,可以增加数据库/对象存储的主备与监控,以及数据库/对象存储封装函数的主备与监控,这样就可以在四个层次做监控告警以及服务保障:在我们的服务中,通常数据库或者存储模块不会同时在多个区域部署,也可能只有一套主备服务,此时,可以通过对数据库和对象存储做一层封装,即在外层增加一个对应的函数,通过内网对数据库以及对象存储进行数据转发等,通过外部云函数调用 invoke(专线调用)大幅度降低由于网络原因造成的延时,当云数据库/对象存储出现问题,在接入层(数据库/对象存储封装函数)一侧,进行切换,将云数据库/对象存储切换到备用服务上,并进行告警;当接入层发生故障,无法继续服务时,在逻辑函数初(北京区/上海区/广州区),切换封装函数,即通过内部专线(函数间调用)调用备用接入层函数;同理,当逻辑函数/业务函数出现故障,监控脚本对 API 网关侧的后端服务进行切换,切换到备用逻辑函数/业务函数上;如果当 API 网关出现故障,无法继续提供服务,则只能在解析层面做切换。在整个这样的一个流程中,每一阶段或者说每一层面都有自身的负载机制,主备策略,可以根据不同组件出现故障的实际情况,进行多层级的自动切换,进而保证业务可用时长的一个最大化。


此处,有读者可能有疑问:为什么某个函数会无法提供服务?底层服务的容灾机制,不是云厂商要提供的么?理论上,这个容灾机制是云厂商提供的,并且函数是无状态的,只要确保业务逻辑无问题,是不需要进行某些层级的主备容灾等。但是实际上,各个云厂商均没有办法保证某个服务 100%可用。例如 2019 年 6 月 2 日凌晨 2 点,亚马逊云 AWS 突发受大规模故障,直到当天下午 1 点 48 分故障解除,故障时间将近 12 小时。所以,虽然云厂商会提供相对安全与可靠的容灾机制和服务,但还是很有必要在自身层面额外处理一下,尽可能保证服务安全与稳定。


通过 Serverless Framework 进行多地域部署与解析


函数的多地域部署


以腾讯云为例,基础的 Component 跨地域部署不是很容易实现。虽然修改区域将函数部署到多个地域是可以实现的,但实际部署时每个区域的函数还会有一些额外的配置,所以这个时候可以借助多地域部署的组件来实现:tencent-scf-multi-region


相对于传统的tencent-scf组件而言,这个组件的 yaml 将region字段变成了兼容list形式,同时增加了子地域的额外配置,例如 yaml:


hello_world:  component: '@serverless/tencent-scf-multi-region'  inputs:    codeUri: ./    description: This is a template function    region:       -ap-guangzhou      - ap-shanghai    environment:      variables:        ENV_FIRST: env1        ENV_SECOND: env2    handler: index.main_handler    memorySize: 128    name: hello_world    runtime: Python3.6    timeout: 3    events:      - apigw:          name: serverless_test          parameters:            protocols:              - http            description: the serverless service            environment: release            endpoints:              - path: /users                method: POST              - path: /usersss                method: POST    ap-guangzhou:      environment:        variables:          ENV_FIRST: env2    ap-shanghai:      events:        - apigw:            name: serverless_test            parameters:              protocols:                - http              description: the serverless service              environment: release              endpoints:                - path: /usersd                  method: POST
复制代码


测试部署效果:


$ sls --debug      DEBUG ─ Resolving the template's static variables.    DEBUG ─ Collecting components from the template.    DEBUG ─ Downloading any NPM components found in the template.    DEBUG ─ Analyzing the template's components dependencies.    DEBUG ─ Creating the template's components graph.    DEBUG ─ Syncing template state.    DEBUG ─ Executing the template's components graph.    DEBUG ─ Compressing function hello_world file to /Users/dfounderliu/Desktop/ServerlessComponents/test/scf_test/.serverless/hello_world.zip.    DEBUG ─ Compressed function hello_world file successful    DEBUG ─ Uploading service package to cos[sls-cloudfunction-ap-guangzhou-code]. sls-cloudfunction-default-hello_world-1583816589.zip    DEBUG ─ Uploaded package successful /Users/dfounderliu/Desktop/ServerlessComponents/test/scf_test/.serverless/hello_world.zip    DEBUG ─ Creating function hello_world    DEBUG ─ Updating code...     DEBUG ─ Updating configure...     DEBUG ─ Created function hello_world successful    DEBUG ─ Setting tags for function hello_world    DEBUG ─ Creating trigger for function hello_world    DEBUG ─ Starting API-Gateway deployment with name hello_world.ap-guangzhou-hello_world.serverless_test in the ap-guangzhou region    DEBUG ─ Service with ID service-p14470dc created.    DEBUG ─ API with id api-pg3ihnoi created.    DEBUG ─ Deploying service with id service-p14470dc.    DEBUG ─ Deployment successful for the api named hello_world.ap-guangzhou-hello_world.serverless_test in the ap-guangzhou region.    DEBUG ─ API with id api-op4jqvba created.    DEBUG ─ Deploying service with id service-p14470dc.    DEBUG ─ Deployment successful for the api named hello_world.ap-guangzhou-hello_world.serverless_test in the ap-guangzhou region.    DEBUG ─ Deployed function hello_world successful    DEBUG ─ Compressing function hello_world file to /Users/dfounderliu/Desktop/ServerlessComponents/test/scf_test/.serverless/hello_world.zip.    DEBUG ─ Compressed function hello_world file successful    DEBUG ─ Uploaded package successful /Users/dfounderliu/Desktop/ServerlessComponents/test/scf_test/.serverless/hello_world.zip    DEBUG ─ Creating function hello_world    DEBUG ─ Updating code...     DEBUG ─ Updating configure...     DEBUG ─ Created function hello_world successful    DEBUG ─ Setting tags for function hello_world    DEBUG ─ Creating trigger for function hello_world    DEBUG ─ Starting API-Gateway deployment with name hello_world.ap-shanghai-hello_world.serverless_test in the ap-shanghai region    DEBUG ─ Service with ID service-7daktopz created.    DEBUG ─ API with id api-4v40ce4u created.    DEBUG ─ Deploying service with id service-7daktopz.    DEBUG ─ Deployment successful for the api named hello_world.ap-shanghai-hello_world.serverless_test in the ap-shanghai region.    DEBUG ─ API with id api-emkl7ov4 created.    DEBUG ─ Deploying service with id service-7daktopz.    DEBUG ─ Deployment successful for the api named hello_world.ap-shanghai-hello_world.serverless_test in the ap-shanghai region.    DEBUG ─ Starting API-Gateway deployment with name hello_world.ap-shanghai-hello_world.serverless_test in the ap-shanghai region    DEBUG ─ Using last time deploy service id service-7daktopz    DEBUG ─ Updating service with serviceId service-7daktopz.    DEBUG ─ API with id api-2zag45hq created.    DEBUG ─ Deploying service with id service-7daktopz.    DEBUG ─ Deployment successful for the api named hello_world.ap-shanghai-hello_world.serverless_test in the ap-shanghai region.    DEBUG ─ Deployed function hello_world successful      hello_world:       ap-guangzhou:         Name:        hello_world        Runtime:     Python3.6        Handler:     index.main_handler        MemorySize:  128        Timeout:     3        Region:      ap-guangzhou        Namespace:   default        Description: This is a template function        APIGateway:           - serverless_test - POST - http://service-p14470dc-1256773370.gz.apigw.tencentcs.com/release/users          - serverless_test - POST - http://service-p14470dc-1256773370.gz.apigw.tencentcs.com/release/usersss      ap-shanghai:         Name:        hello_world        Runtime:     Python3.6        Handler:     index.main_handler        MemorySize:  128        Timeout:     3        Region:      ap-shanghai        Namespace:   default        Description: This is a template function        APIGateway:           - serverless_test - POST - http://service-7daktopz-1256773370.sh.apigw.tencentcs.com/release/users          - serverless_test - POST - http://service-7daktopz-1256773370.sh.apigw.tencentcs.com/release/usersss          - serverless_test - POST - http://service-7daktopz-1256773370.sh.apigw.tencentcs.com/release/usersd      35s › hello_world › done

复制代码


通过以上部署,就可以成功将函数部署到不同的地域,并且针对不同地域进行额外的配置。


上层服务的多地域部署与解析


就目前来看,tencent-scf更多是一个基础组件,更多人则是在使用 Koa、Express 等组件,那么对于这种相对高阶的组件,是否可以多地域部署呢?从目前文档的描述来看是可以进行多地域部署,且可以进行自动解析,以tencent-tornado为例:


TornadoTest:  component: '@serverless/tencent-tornado'  inputs:    region:      - ap-guangzhou      - ap-shanghai    functionName: tornadoFunctionTest    tornadoProjectName: app    code: ./    functionConf:      timeout: 10      memorySize: 256      environment:        variables:          TEST: vale      vpcConfig:        subnetId: ''        vpcId: ''    apigatewayConf:      protocols:        - http      environment: release      customDomain:        - domain: anycodestest1.com          isDefaultMapping: 'FALSE'          pathMappingSet:            - path: /              environment: release          protocols:            - http        - domain: anycodestest2.com          isDefaultMapping: 'FALSE'          pathMappingSet:            - path: /              environment: release          protocols:            - http    cloudDNSConf:      ttl: 603      status: enable    ap-guangzhou:      functionConf:        timeout: 20      apigatewayConf:        protocols:          - https      cloudDNSConf:        recordLine:          - 电信          - 联通

复制代码


部署结果:


  TornadoTest:     functionName: tornadoFunctionTest    ap-shanghai:       apiGatewayServiceId: service-mdnjhsp3      url:                 http://service-mdnjhsp3-1256773370.sh.apigw.tencentcs.com/release/    ap-guangzhou:       apiGatewayServiceId: service-nh6xgutk      url:                 https://service-nh6xgutk-1256773370.gz.apigw.tencentcs.com/release/    DNS:          Please set your domain DNS: f1g1ns1.dnspod.net | f1g1ns1.dnspod.net

复制代码


通过这样一个组件,就可以完成框架的跨地域部署与解析。


总结


至此,我们基本完成了一个基于 Serverless 的高可用框架的案例探讨,从对单解析的分析,到对多解析的策略制定,再到最后基于 Serverless 架构的高可用模型,虽然文中案例仅仅可以作为学习参考使用,在实际生产中会面临很多难题,有些地方还需要针对实际项目做一些特殊的修改和修正,但是这样一个简单的基于 Serverless 架构的高可用模型,基本上可以发挥出 Serverless 架构的特点,包括 Serverless 架构的按量付费功能,在不使用的时候则不需要收费,在部署多套云函数的时候不会因为部署量增加而产生额外的费用;在这个项目中,包括 API 网关等触发器对函数进行触发,也会包括函数间的编排和调用,更有 FaaS 与 BaaS 紧密结合,通过专线跨地域 Invoke,在外层还会增加多套监控告警函数以及 DNS 切换函数、云函数切换函数等尽可能保证服务的稳定与可用性;为了更加简单的进行多地域部署,还通过了 Serverless Framework 实现了多地域部署方案。


在实际生产生活中,无论是单地域服务还是多地域就近接入服务,多地域部署容灾都是很重要的,尤其在 Serverless 架构下,按量付费让主备模式成本骤降,100%可用性几乎是一个不可能事件,但我们可以共同探讨相对优秀的高可用方案。


2020 年 7 月 30 日 16:592160

评论

发布
暂无评论
发现更多内容
Serverless实战:通过 Component 实现多地域部署容灾-InfoQ