边缘计算业务部署实践

边缘计算是一种分布式计算的架构,作为云计算的延伸,通过将计算能力从中心节点下沉到靠近用户的边缘节点,提供更低延时的计算服务。边缘计算节点,距离用户更近。

通常情况下,边缘节点之间的内网是隔离的。跟企业IDC生产环境通常也通过公网互联。直接导致的后果就是,企业级的运维保障能力和平台难以覆盖到边缘计算节点。当然任何事情都不是绝对的。比如Zabbix可以通过Zabbix-proxy实现跨网监控,Jenkins也可以通过Slave节点实现跨网构建交付。但是很多时候,出于很多原因,比如有些私有化方案,耦合内部资源或者涉及业务拆分交付等等,可能会导致企业级能力覆盖不到。此时我们不会坐等时间去解决一切问题,而是需要考虑从零到一,实现一套独立的解决方案。

如果把基于企业内网已有能力体系基础上的业务部署行为比作现代社会的基建工程,那在脱离内网能力的边缘节点部署业务,就好比在原始森林里盖房子。一切都需要从零开始就地取材。需要着重强调的是,本篇的实际背景是在跨公网的多个独立网络环境没有其它任何自动化基础设施的情况下,实现快速部署上线一套生产可用可运维的分布式应用服务。笔者期望以此为例,梳理一下从零到一部署一套完整应用服务以及搭建功能基本完善的周边运维设施的一般思路,对缺乏业务运维经验的同学在较为全面的认识业务部署的全流程和业务运维的工作内涵方面有所启发。

以下从几个方面介绍一下相关思路和实现过程:

  • 前期的筹备和调研
  • 生产环境的规范约定
  • 部署到生产环境
  • 自动化流程的建立
  • 监控告警的设计实现

前期的筹备和调研

前期筹备阶段需要做大量的准备和信息收集的工作,如基准测试、成本预估、成效预测等等,此外如果是全新业务或新接手的业务,则需要通过跟业务同学深入沟通了解或者参考现有相同或相似架构的服务,对业务应用整体有较为全面的了解。便于后续过程中的整体考量和扩展性设计。

了解业务架构

本次部署的业务通过腾讯云覆盖全国15个边缘节点,服务器总量在200台左右。涉及nginx,各类C++业务服务。主体数据规模约100G,增量冷数据约10G,增量热数据1G以下,需要每天不定时更新。本篇主要从运维角度总结了服务部署的一般思路。因此弱化了业务细节,仅提供一张简洁的业务架构图说明部署和维护的复杂性即可。

业务架构图

技术选型

技术选型主要指在工具链和技术方案选择时应遵循的一些基本原则。首先应该考虑的是业务场景的匹配程度,其次是实现成本和维护成本,开源方案还需考虑社区活跃程度,最后考虑的是人的因素,如已有的知识储备情况,以及团队对技术方案认同和擅长程度。

容灾设计

首先需要强调的是,这里提到的容灾是指工具链的容灾而非业务容灾。

关于容灾设计应该如何考虑。有些同学会惯性的执念于工具的高可用,实际的容灾设计是用户感知程度、服务容错能力以及容灾成本的多种因素平衡的结果,容灾能力在达到一定程度之后的提升成本可能是指数级上升的。因此出于成本考虑,很多工具链角色是单台服务器加冷备的方式,如基于Jenkins的持续交付工具,制品库,监控系统server端等。

如何规避单点带来的风险?个人考虑应该满足以下三点:

  • 首先单点上的服务不应跟外部服务器有功能耦合。如监控服务采用agent push数据就不如监控server pull的方式更易于恢复;
  • 其次单点服务器应该在业务容忍的时间范围内实现快速替换。可以通过文件备份,如Jenkins、prometheus的服务、配置以及数据皆为文件形式存储,相比结构化数据存储就比较容易实现快速替换;
  • 单点服务器突发故障不会对生产环境应用造成不可控的影响。如在边缘节点服务器本地设置应用数据更新的缓冲目录,更新过程中不管是制品库突发故障还是自动化引擎出现问题都不会导致服务状态未知。

IP资源管理和网络规划

对于过百服务器的业务场景,直接使用IP管理业务是不现实的。个人认为原因有三:

  • IP地址的识别度低,直接导致的结果是使用过程中极易出错;
  • 直接应用IP在部署阶段会增加自动化的难度;
  • IP的唯一性会导致配置文件难以统一。

解决方案:

  • 公有云VPC环境的私有DNS服务。内网解析服务一般费用较低,可以考虑。不建议自建私域DNS,首先会对业务产生单点风险,其次维护成本较高;
  • 本地绑hosts,为服务器设计别名,可以简化管理复杂度。别名一般由以下信息的部分或全部组成,服务名以及序列号.网络类型.地区信息.产品信息.企业标识。服务器别名和服务域名在设计中较为类似,尽量避免信息重复,单个字段过长等问题。本地绑hosts的方法虽然不是十分优雅,但是非常实用。对于千台以下规模的服务器治理非常有效,可以简化和规范配置文件、inventory文件以及相关自动化脚本的定义。

以IP资源信息为核心,可以延展出与之相关联的服务器配置信息、业务应用信息、带宽及运营商信息、成本信息等许多基础信息,这些基础信息的简单汇总就是最原始的CMDB。

服务器IP和别名的映射信息,作为最基本和最根本的业务信息。如果不能实现平台化管理建议至少应该提交到Git中进行版本化管理。

网络规划涉及私有网络和公网IP。私有网络一般涉及网段规划和业务IP分配规划。合理的内网IP规划可以通过IP信息直接识别区域业务或应用,便于实现更好管理。由于公有云不同区域提供的默认子网网段可能冲突而且识别度较低,因此一般不建议直接使用默认子网。公网IP一般分配较随机,一般需要考虑运营商、计费模式及费用成本、带宽限制等方面。

设计制品库

制品库本质上是存储制品的仓库,核心功能是存储。因此制品库可以是一个制品库平台(如:nexus、artifactory)也可以是一个存储系统甚至一台大硬盘服务器。

在考虑制品库的阶段,主要考虑以下几个方面的问题:

  • 版本管理
  • 科学规划目录
  • 过期制品清理

具体到本次部署需求,制品库的角色实际上是一个制品缓存层,不需要长期持久化制品的历史版本。另外不存在人为直接读写制品库中的制品的情况,因此可以暂不考虑权限体系。

制品库是ci、cd的边界,因此在实际场景中该角色不可或缺。

搭建堡垒机

堡垒机是生产环境的入口。一般具有安全审计,风险拦截,权限管理等功能。在实际场景中,由于虽然会有各方面的条件限制,但我们仍需要考虑搭建堡垒机的角色,主要是实现如下功能,收紧入口降低安全风险,实现自动化,方便批处理执行。实际场景中一般应该满足以下需求:

  • 严格的安全组访问策略控制访问入口;
  • 与生产环境必要的网络和权限互通;
  • 快捷登录服务器的配置,通常有以下三种方式:
    • 密码登录。明文存储密码极易造成密码泄漏,极不推荐;
    • 互信登录。风险阻断功能丧失,容易导致入侵风险被数倍放大;
    • 证书登录。相较上两种方式的优势在于,证书文件可以单独管理,授予最小访问权限,用完即删。
      严格来说,以上三种方式均有不同程度的风险隐患。但是在小规模的隔离网络环境中,在基建匮乏的现实背景下,使用证书登录相对更可取。
  • 批处理能力。批处理工具非常丰富,以ansible为例,在堡垒机上除了需要部署ansible环境以外,还需储备以下资源:
    • inventory文件清单。以地区为维度、以服务维度等多种维度创建inventory文件,当出现紧急问题时便于快速介入;
    • 必要的预案脚本。如部署、故障检测、信息收集、日志分析等。

生产环境的规范约定

生产环境规范是运维和开发人员长期积累的最佳实践的条文描述。在前期筹备阶段就应该已经清晰明确。但由于其尤为重要,因此单独强调一下。规范约定整体参考软件交付的12要素,结合实际业务总结为以下几个方面的内容:

边界规范

如数据制备,制品生成,配置管理等,当然如果能全流程把控最好,但是实际场景中,如果由于历史原因、现实原因、其它原因等各种原因导致无法做到全流程把控的话,就应该在尊重事实的基础上做好跟开发同学的边界约定。明确了边界就明确了责任。明确了责任之后任何事情都相对更容易推进。

版本规范

和上游数据制作者以及应用构建者约定新版本数据和制品的版本交付规范,一般建议遵循语义化版本控制规范(SemVer)。

策略规范

如数据制备的时间策略,数据完整性的校验方法,配置校验方法,热加载触发方式等。

应用(部署)规范

应用部署过程中的明确可落地的规范,是实现自动化和提升稳定性的基础。

  • 应用名,业务名称要固定,要能体现业务特点;
  • 应用路径,包括对制品存放的路径规范,生产环境部署的路径规范以及目录层级含义;
  • 配置文件,配置文件命名、格式、存放路径、加载方式;
  • 服务端口约定,应支持通过参数或配置文件指定,应考虑多模块实例场景下的端口扩展性,一般不建议使用超过10000的端口,因为大流量服务需要限制端口范围避免和监听端口冲突,过大的端口会导致端口范围的减小。另外80端口只有root用户有权占用;
  • 数据、代码、配置、日志目录的相互剥离:
    • 数据目录应该独立且平行于代码目录;
    • 代码和配置应该拆分开;
    • 代码目录下禁止保留应用日志目录;
    • 日志和数据目录一般较大,应避免直接存放在根分区下,而应存放在数据盘分区下单独管理。
  • 启动方式,可以考虑以下方式:
    • 用systemd管理启动;
    • 用supervisor管理进程启动;
    • 自定义脚本启动,自定义脚本要支持在任意目录下通过相对路径或绝对路径执行。
  • 应用需开机启动以保证服务器重启后应用能够正常对外服务;
  • 进程支持自动拉起,降低业务不可用时间的必要条件;
  • 应用日志规范:

    应用日志规范作为应用规范的一部分,是需要在部署实施之前就明确的。

    • 日志命名,包含日志文件名的生成规则,以及存放路径等信息;
    • 日志滚动,主要包含两个方面的信息,一是日志文件的切分周期,二是本地日志文件保留个数;
    • 日志字段定义,如需要基于日志信息进行监控或数据分析,应明确日志的每个字段的定义,分隔符以及关键字等信息。

(日志)数据回收流程规范

生产环境回传数据是常见的需求,以业务日志回传作为最典型的离线数据传输场景。此种场景需要考虑以下几个方面的因素:

  • 时效性要求。日志数据的回收应该在满足业务时效性的前提下,实现最小成本回收,因此对业务日志回收的时效性应该明确;
  • 错峰。在满足时效性的前提下,避免带宽叠加,尤其在公有云的跨公网传输的场景;
  • 本地压缩。根据数据规模大小,太小的数据也可能没有压缩的必要,由于压缩比较消耗资源,因此较大的数据压缩应该避免影响业务的正常运行的前提下,尽可能节省带宽;
  • 日志滚动。避免磁盘写满。磁盘写满是生产环境中最为低级的问题,但却又极易出现,并且对应用的影响极大;
  • 被动拉取。容灾性好。被动拉取首先可以做到日志回传流程集中管理,避免散布到生产环境的多台服务器上,另外流程更可控,主动推送的方式很难确认最新数据文件的完整性;
  • 日志文件完整性。如果存在多个阶段的回传,要避免一个阶段的传输尚未结束,另一个阶段直接开始。

交付规范即可运维标准

  • 安全合规。
    • 通过公有云的安全组访问控制策略,限制网段、IP、端口等的访问规则,在不影响业务正常运行的前提下开放最小访问权限;
    • 避免非对外业务端口直接监听公网IP;
    • rsync在白名单访问的基础上增加鉴权认证、限制到具体目录读写操作的权限粒度;
    • 配置文件中排除密码等敏感信息;
    • 其它必要的安全考量。
  • 具备一定的冗余算力。在处理业务正常峰值的基础上,对于异常波动应有一定的耐受能力;
  • 生产环境服务部署的一致性。同一个或者同一组服务的多台服务器相当于一台服务器的多个副本,即无状态部署;
  • 生产环境无垃圾数据文件。生产环境中存留垃圾数据会对后续的维护产生不同程度的干扰,因此应及时清理;
  • 滚动写入。所有持续写盘的进程或任务都应该具备与之搭配的在保留满足需求数据量的前提下的自动清理数据的流程,使磁盘持续具备足够的可写空间;
  • 可扩展性考量。当应用规模增加或缩小时,不会导致整体大的调整。

另外需要着重强调的是生产环境应严禁开启应用的debug模式。

部署实施

部署实施的流程大致总结如下。其中如果存在大规模数据传输过程,应该考虑和轻量的初始化操作剥离。

部署流程图

以下将针对重要环节适当展开阐述。

服务器初始化

所谓服务器的初始化操作,是指在部署之前,所有服务器需都要执行的统一的定制化操作步骤。一般至少需要考虑如下几个方面:

  • 定制修改ssh、rsync服务的默认端口,如果将监听端口修改为较大的tcp端口时,应注意在开机流程中,ssh和rsync应该在业务应用启动前启动,以避免可能造成的端口占用和冲突;
  • 初始化rsync配置,建议按照最小权限原则配置目录范围和读写权限,另外还需要考虑必要的身份鉴权;
  • 初始化硬盘。包括新建分区和挂载目录,属于相对比较程式化的操作;
  • 部署相关agent,如监控agent、安全相关的agent等全机群部署的基础应用;
  • 内核参数初始化,如文件打开数限制,backlog限制,随机端口生成范围限制等等;
    • 内核参数优化过程中需要注意用户限制不能超过内核限制,否则将带来灾难性后果。
  • 其它必要的定制更改。

部署业务应用

业务部署过程依赖于此前规划的IP、服务器别名映射信息,这是提升部署效率和降低错误率的基础。除此之外还要注意以下几点:

  • 首先实现方式,个人推荐shell + ansible ad-hoc。有些读者可能认为ansible-playbook才是规范操作和最佳实践。但是shell和ansible ad-hoc是互补的搭档,二者组合非常灵活强大,另外收集执行过程中的标准日志和错误日志方面非常方便。当然ansible-playbook的任务编排能力也非常强大,也是官方推荐的使用方式。选择没有对错,适合的就是最好的;
  • 需要注意交互和非交互环境的环境变量差异;
  • 部署过程应该记录日志,日志内容包括时间,执行的原始命令、标准输出、错误输出、命令退出状态码等信息。日志审计是自动化的重要基础保障;
  • 软件定制打包是自动化和规范化的基石。将通用的优化修改集成到软件安装包中可以减少部署的复杂度。将生产环境的规范设计集成到安装包中,更容易实现和推广。当然,如果需求来了现定制打包估计是来不及的,此时可以通过原始软件包+定制逻辑脚本代替定制软件包;
  • 数据缓冲目录。此前多次提到,数据文件较大时,使用数据缓冲目录可以屏蔽掉网络传输和本地文件拷贝之间的速度差异。同时可以隔离数据传输过程中,突发异常对生产环境的影响。

交付生产的两个必要条件,一是较为完善的监控告警,二是持续交付流程的建立。

自动化实现

自动化的实现需要一个自动化引擎支撑。实际上目前成熟的自动化引擎工具非常丰富,由于本次部署不涉及容器应用,因此云原生相关的解决方案暂且不提。传统的经典工具也有很多选择。比如gitlabci、Jenkins、walle、rundeck、go-task等等。但基于实际场景,对于自动化引擎,精细化的权限控制,支持自动触发、支持日志审计、支持任务队列、备份简单、GitOps等多方面的考虑。结合个人的使用经验,最后确定使用Jenkins作为自动化引擎。大方向确定之后面临的新的问题就是应该选择自由风格还是流水线任务?一般认为流水线更加优雅,扩展性更好,更易于管理。但是自由风格的任务创建简单,更易于集成插件功能。具体到边缘计算项目更适合流水线任务类型。因为随着节点的增加,任务需要持续扩展。

以下分别从业务场景和功能需求角度分析如何实现自动化。

业务场景

  • 自动任务。
    • 增量数据更新,不定时产生增量数据交付到生产环境,需支持通过接口传参触发。
  • 手动任务。
    • 代码、配置文件更新;
    • 大规模数据更新;
    • 数据和代码组合更新。
  • 更新粒度。使用Jenkinsfile定义pipeline更适配对于更新粒度的要求。
    • 以功能模块为粒度组合任务;
    • 以地区为粒度组合任务。

功能需求

  • 精细化的权限控制。触发任务的用户不允许修改任务配置定义,仅允许执行和查看日志。固化流程有利于规范任务定义、推动规范落地,保障任务的低错误率和失败率;
  • 支持自动触发。实际上是提高研效的基础。如数据的定时和不定时频繁更新,可在数据制作流程的最后一步集成交付流程的触发操作。使整个数据制作交付流程更加流畅;
  • 支持任务队列。避免任务时空重叠产生的风险;
  • 日志审计。是自动化的必要条件。没有日志审计的自动化带来的风险是不可想象的;
  • 备份简单。一定程度上弥补了Jenkins单点的风险;
  • 任务可编程和GitOps,提升效率降低风险。

设计思路

设计思路

实现效果

  • 自动触发的任务
    jenkins_auto

  • 手动触发的任务
    jenkins_manual

  • 任务完成通知
    jenkins_manual

监控告警

监控的基本功能大致分为指标数据采集、数据可视化以及告警消息推送三部分。监控选型过程中,在满足需求的基础上,需要考虑部署和维护成本。Prometheus在系统指标监控层面无疑在部署和维护方面成本都是相对较低的。原因在于它不依赖于其它组件部署,时序数据存储于本地,便于备份和恢复。另外grafana社区对于prometheus监控的dashboard模板也非常丰富,可以快速搭建出基础的监控功能。

对于nginx的监控需要第三方模块nginx-vts的支持或基于lua的metric库支持。个人比较倾向于使用vts模块,因为之前定制业务的nginx rpm包时顺带定制了vts扩展的rpm包,开箱即用,部署非常方便。nginx-vts的配置文件是相对固化的,在此不再详细展开。

对于应用级监控,如进程存活状态,进程的各类硬件资源消耗等需要引入process-exporter,学习成本稍高,但是使用非常方便属于一劳永逸的学习投入。

监控使用prometheus,告警只能使用alertmanager + webhook或mail实现。alertmanager的学习成本比较高需要对PromQL有基本的了解。入门还算容易,最大的优点还是无外部组件依赖。

  • 监控架构
    监控告警架构图

监控可视化

  • 系统状态。node_exporter采集的数据,非常全面,几乎涵盖了所有关系的系统指标数据;
  • nginx请求状态。nginx-vts模块暴露的metrics,主要关注请求量、延迟和5xx的错误率;
  • 应用进程状态。process-exporter采集的数据展示,主要关注进程存活以及进程的资源使用情况;
  • 业务指标状态。业务指标数据依赖于业务埋点。

可视化效果图

告警

  • 基础告警。应至少涵盖服务器存活,Load状态,cpu、内存、磁盘使用率,网卡出入带宽、网络连接数等;
  • 应用告警。业务进程存活,基础进程如crond、rsync等存活状态,nginx进程存活,nginx错误状态码请求量,nginx请求的平均响应时间等;
  • 告警合并。适度控制告警数量,有利于更加高效地发现并处理异常;
  • 日志审计。必要时候用于问题溯源;
  • 阈值合理。过于敏感的告警等于没有告警;
  • 告警实现。Prometheus + alertmanager + webhook + 企微机器人。

实现效果

  • prometheus alerts
    alert

  • 告警通知
    alert

最后总结

一篇写下来终于领悟到什么叫眼高手低,没写之前觉得有无数观点可以输出,真正开始整理的时候才发现想把这些想法条理清楚地表述出来并不容易。在此不禁对那些默默输出原创有价值文章的人表示敬意。业务部署和写文档一样,没有实践之前觉得没有什么复杂性可言,实际实施过程中才发现很多细节问题需要通盘考虑。快糙猛的部署行为也许可以达到业务火速上线的效果。但是欠下的技术债会在很长一个周期内潜移默化的影响着业务品质。因此我们在追求业务快速迭代的同时,一定要夯实基础。尽可能避免偷工减料的行为拉低了业务稳定性。

行文至此,才发现文字已经堆积了不少,但还不确定观点是否表达清楚。在略显凌乱的文字中间,希望对缺乏业务部署经验的同学一些思路上的启发,同时也是对自己整个项目实施过程的全面总结。当然,由于个人见识和理解的局限性,造成了客观上论述的片面性,敬请各路大神不吝批评指正,进而实现对内容的持续完善。由于篇幅限制,具体的配置细节和脚本逻辑没有进一步展开。如遇到相关疑问,也欢迎线下探讨交流。

纸上得来终觉浅。所谓的经验都需要通过更多的反复实践才能有更深刻的理解。