作者:关陞阳 米连科技运维开发工程师
公司背景
北京米连科技有限公司成立于2015年9月份,国家高新技术企业及中关村高新技术企业。2019全年营业额接近10亿人民币,主营业务为通过自主研发的视频社交产品-伊对APP服务单身恋爱社交人群。公司已获近亿美元级B轮融资。
伊对是当前飞速成长的恋爱社交平台,当前活跃红娘40000+,每月撮合近1000万场相亲,仍在快速增长中。伊对APP拥有更真实的恋爱社区,以真实的社交网络,创造更多恋爱机会。注册用户2020年12月就突破1亿,平台每月可以为1500万多对单身男女创造恋爱机会,每天撮合50万多次视频相亲,活跃红娘、月老超40,000人,用户的日均使用时长达到了40分钟。
伊对是用户价值与商业价值统一协调的大众市场,恋爱新物种,陌生人社交领域的阳光荷尔蒙,与婚恋赛道全完不同,我们的红娘全部来自于伊对用户,并不是我们的员工。
非容器时代
go代码仓库:1
痛点
网关层上线没有滚动
每次网关部署都会造成大量报错,放到现在都是P0级别的事故。
微服务相互调用写死
没有负载均衡,没有健康检查,每次微服务上线都会引起大量报错;增减某个服务的实例数量需要改动所有调用方的配置。
yd-BB=IP-BB-1;IP-BB-2 yd-CC=IP-CC-1 yd-DD=IP-DD-1,IP-DD-2
没有配置管理
配置分布在各个ECS上,经常会出现同一个服务的配置在不同服务器上不一致;
不到30个微服务出现了4种配置文件格式:ini、yaml、toml、xml;
配置文件内部不统一,例如多个地址的分隔符,有逗号和分号。
go代码仓库单一
一个go项目中按照文件目录区分服务,实现自动打包很麻烦。
上线缺少标准化流程
上线部署流程控制在一个在线表格维护,依赖人去手动维护,经常漏写错写。
无法确认线上代码以及配置的版本
当时的部署方式是手动在打包服务器拉取代码,打包之后通过ansible命令推送二进制包到对应ECS,再进行文件替换之后重启supervisor;
日志服务里面无法体现当前代码的版本,经常需要查看具体运行的二进制文件的md5值判断线上的代码具体是哪个commit产生的。
docker时代
go代码仓库:50+
为了解决非容器时代的痛点,我们做了这些事
代码仓库拆分
由业务研发组长实施,基于业务模块使用gitlab的group以及subgroup拆分出来多个单一功能的微服务代码仓库以及公共模块仓库。
半自动打包上线
在每个代码仓库中使用gitlab-ci进行打包。
# 项目所属组 GROUP_NAME: go # 项目所属子组 SUBGROUP_NAME: user_info # 配置管理项目地址 CONFIG_PROJECT: go-config # 自动部署时目标机器组标签 DEPLOY_HOST_GROUP: user-info stages: - build - deploy - dev
在一个独立的gitlab仓库管理配置
服务器环境由supervisor切换到docker,docker的镜像可以确定具体代码和版本,日志服务改为采集容器日志,可以看到产生日志的镜像。
基于gitlab-ci实现自动构建docker镜像,构建完成后基于ansible脚本更新线上ECS,镜像的tag由分支-代码commit-配置commit组成。
每次打包基于commit,每次部署基于镜像,实现简单标准化上线。回滚是执行上一次部署。
网关层滚动部署
基于ansible以及阿里云命令行工具实现了一套网关滚动部署流程,逐个把要更新的ECS从SLB的虚拟服务器组中权重调0,再停止服务,更新镜像,自检成功后再把权重恢复。
服务发现
基于ETCD实现简单服务发现功能,单个微服务扩缩不需要修改其他服务的配置文件,基于ansible实现了滚动部署。
痛点
ECS使用率低,需要手动调度负责
需要手动平衡各个ECS上运行的container,ECS平均规格小,使用率低,数量多,实例扩缩流程繁琐。
必须运维手动||半手动部署
没有自主上线时有一周2个运维加起来一共220次上线部署,那一周我们2个运维无法开展任何其他工作,每天全部的精力都耗费在上线部署,身边随时都有不同的研发围着排队上线,很低效。
定时任务被上线打断
逐渐一些微服务代码仓库出现了同时包含grpc以及定时任务逻辑的情况,在docker环境下往往是通过docker启动脚本传递一个特殊环境变量作为定时任务开关,让定时任务仅在一台ECS上执行。多次出现长执行时间定时任务执行期间被上线部署打断引起的事故。
命令行工具不适合高频率自动化部署
ansible或者kubectl这种命令行工具手动使用很方便,但是在作为自动部署工具时太慢了,每次部署或者回滚都有一个几秒的初始化命令行环境的时间。而且使用命令行工具做部署的话,部署逻辑是在shell脚本中维护的,比较难实现复杂上线功能,例如灰度部署。
缺乏监控告警体系
原生云监控不满足需要,经常漏报错报。
缺少对go服务公共模块依赖的查询
例如common v1.9.2这个版本有个重大更新,研发很难确定哪些微服务引用的common版本低于v1.9.2。
kubernetes时代
go代码仓库:170+
实例组数量:240+
为了解决docker时代的痛点,我们做了这些事
实现运维平台
在运维平台上实现了一套权限系统,有合适权限的研发可以自己执行部署以及回滚,一切操作都留有记录
运维平台上线后解放了运维人员大量精力,实现了非特殊部署完全不需要运维参与,让我们运维有时间精力去研究或开发其他运维工具,大大提高整体研发工作效率。最近2个月仅有2.24%的上线单是由运维部署的。运维平台功能完善之后有一天多个业务线同时发版,当天总共上线+回滚次数超过210,全程无运维参与。
迁移至kuberbetes
很幸运阿里云提供的Terway集群完美适配我们docker时代基于ETCD的服务发现逻辑,Pod的ip和ECS ip在同一层,大大减少了迁移的周期,降低了迁移的难度。
抽象出实例组概念,实例组是一个微服务仓库代码的一种运行方式,在运维平台维护,类似与helm的chart。一个微服务仓库可以拥有多个实例组,例如一个微服务仓库代码可以同时启动无状态grpc服务、有状态异步处理服务以及定时任务。每次部署的最小单位是一个实例组。
实例组由yaml模板和参数组成,如果一个微服务需要一个罕见配置,可以快速创建一个专用的yaml模板实现,不要求部署逻辑维护全量的k8s控制器结构体,仅维护最通用的参数,这样大大降低了开发成本,在kubernetes集群内使用的ECS节点规格大了很多,使用率也有显著提高。
基于kubernetes api用go实现了一套部署逻辑
api比命令行工具快很多,还能实现很多额外的功能,例如查看Pod事件等,代码也比脚本更容易维护。
自建prometheus监控以及告警体系
基于prometheus和运维平台实现了秒级监控告警。
go服务依赖查询功能
每次上线打包时向运维平台上报go.sum,把当前线上环每个微服务的具体依赖存在数据库里面,在运维平台实现查询页面。
k8s服务资源监控以及自动调整
周期性从自建prometheus里面拉取数据,基于服务过去的资源使用量和服务优先级生成新的资源限制。
痛点
缺少更细粒度的流量控制
目前仅实现了基于流量百分比的灰度,而且大量ab测试逻辑在代码里面,不利于维护。
在gitlab仓库维护的配置文件不够灵活
无法实现热更新,或者环境级别的配置管理。
基于Cronjob实现的定时任务不够灵活
不能方便的实现紧急停止、临时触发等功能。
业务服务总体资源使用不符合阿里云ECS规格
业务服务使用的内存很少,CPU比内存接近4:1,集群内存使用率低。
研发人员对运维平台不熟悉运维平台
功能越来越复杂,很少有研发能充分使用,尤其是新同事。
未来
探索Service mesh
使用nacos
自己实现定时任务逻辑
将一些缓存redis部署到k8s集群
增加运维平台引导