单点故障是实际生产中无法避免的,单副本的存储方案也早已无法满足业务的可靠性要求。现在,我们通常都会做双机存储架构,会涉及到主备、主从、主主模式。
在 Serverless 架构下,高可用方案或者容灾方案是否还需要主备、主从以及主主等模式?如果还需要,那么又是什么样子?
Serverless 与多地域部署
针对服务可用性,几乎每个云厂商都会有非常高的承诺,但是我们也不能掉以轻心,认为不会出现故障导致不可用,容灾方案是必须要有的。
在传统主机时代有主备、主从以及主主模式,这个模式更多针对的是单台机器或者某个集群。但是在 Serverless 架构下,没有机器和集群的概念(至少在用户层面没有),是不是就表示在 Serverless 架构下无法做容灾?
理论上,云厂商会保证整个服务的可用性,如果云厂商管理的某个机器出现故障,机器会被及时剔除,确保新的函数在安全、稳定、健康的环境下启动,正常提供服务。但在实际情况中,由于某些原因,云厂商也可能会在某个地域出现大规模故障,这时如何确保服务依旧可用,而不是苦苦等待云厂商的恢复?
针对单地域解析的网站,我们可以实现多地域的主备方案。在云函数中,多地域的主备方案更加经济实惠,因为函数是按量付费的,完全可以将函数复制到其他的地域,只要不进行触发,就不会产生额外的费用。相对于传统主机时代的主备模式,这种主备方案显得更加人性化。
一般情况下,单地域部署的服务中比较容易出问题的是 API 网关服务、云函数服务、数据库服务。其中,数据库服务可以考虑跨地域主从、跨区域同步,一旦出现问题,就在函数中做一个负载,确保整体数据不会出现问题,在腾讯云的云数据库描述下可以看到:
本地 IDC 机房 MySQL 数据库与云数据库 MySQL 之间可以通过数据迁移服务实时同步数据,本地IDC机房如遇到断电、网络故障等引起数据库服务中断,可迅速切换数据库服务至作为灾难备份的云数据库 MySQL 实例,实现数据库容灾;同时,云数据库 MySQL 可支持同城多可用区灾备/跨城灾备,保障高可用。
所以说,数据库相关服务的容灾可以通过备用数据库来提高可用性。
那么云函数和 API 网关怎么解决?
以多地域部署容灾为例,我们可以考虑这样的架构:
同样是作为单地域解析服务,相对来说,多地域部署更加安全稳定,一旦某个地域的服务出现问题(例如 API 网关,云函数),都可以通过监控程序及时发现,并且迅速切换解析到其它地域。多地域部署的监控函数与时间触发器进行结合,定期进行网站可用性的排查,一旦出现问题,就可以在云解析层面进行解析切换,实现单地域服务的多地域部署容灾方案:
这个方案的逻辑是先请求服务是否可用,如果不可用,则获取容灾列表,剔除不可用的服务,并通过云解析进行可用区的解析。在实际生产中,一旦确定某个服务不可用,还要进行精确告警,在获得到不可用解析记录对应的服务之后,通过邮件或者企业微信、短信等方法进行告警。
至此,一个简单版的“高可用”服务就算做好了,有的读者可能有所疑问:
切换解析不需要时间吗?怎么确保TTL按时生效?
对服务进行修复,是否比切换解析更加靠谱呢?
针对问题 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 ─ 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%可用性几乎是一个不可能事件,但我们可以共同探讨相对优秀的高可用方案。
评论