天天看点

伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来

作者:关陞阳 米连科技运维开发工程师

公司背景

北京米连科技有限公司成立于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项目中按照文件目录区分服务,实现自动打包很麻烦。

上线缺少标准化流程

上线部署流程控制在一个在线表格维护,依赖人去手动维护,经常漏写错写。

伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来

无法确认线上代码以及配置的版本

当时的部署方式是手动在打包服务器拉取代码,打包之后通过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仓库管理配置

伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来

服务器环境由supervisor切换到docker,docker的镜像可以确定具体代码和版本,日志服务改为采集容器日志,可以看到产生日志的镜像。

基于gitlab-ci实现自动构建docker镜像,构建完成后基于ansible脚本更新线上ECS,镜像的tag由分支-代码commit-配置commit组成。

每次打包基于commit,每次部署基于镜像,实现简单标准化上线。回滚是执行上一次部署。

伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来

网关层滚动部署

基于ansible以及阿里云命令行工具实现了一套网关滚动部署流程,逐个把要更新的ECS从SLB的虚拟服务器组中权重调0,再停止服务,更新镜像,自检成功后再把权重恢复。

伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来

服务发现

基于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时代的痛点,我们做了这些事

实现运维平台

伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来

在运维平台上实现了一套权限系统,有合适权限的研发可以自己执行部署以及回滚,一切操作都留有记录

伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来

运维平台上线后解放了运维人员大量精力,实现了非特殊部署完全不需要运维参与,让我们运维有时间精力去研究或开发其他运维工具,大大提高整体研发工作效率。最近2个月仅有2.24%的上线单是由运维部署的。运维平台功能完善之后有一天多个业务线同时发版,当天总共上线+回滚次数超过210,全程无运维参与。

迁移至kuberbetes

很幸运阿里云提供的Terway集群完美适配我们docker时代基于ETCD的服务发现逻辑,Pod的ip和ECS ip在同一层,大大减少了迁移的周期,降低了迁移的难度。

抽象出实例组概念,实例组是一个微服务仓库代码的一种运行方式,在运维平台维护,类似与helm的chart。一个微服务仓库可以拥有多个实例组,例如一个微服务仓库代码可以同时启动无状态grpc服务、有状态异步处理服务以及定时任务。每次部署的最小单位是一个实例组。

伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来
伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来

实例组由yaml模板和参数组成,如果一个微服务需要一个罕见配置,可以快速创建一个专用的yaml模板实现,不要求部署逻辑维护全量的k8s控制器结构体,仅维护最通用的参数,这样大大降低了开发成本,在kubernetes集群内使用的ECS节点规格大了很多,使用率也有显著提高。

基于kubernetes api用go实现了一套部署逻辑

api比命令行工具快很多,还能实现很多额外的功能,例如查看Pod事件等,代码也比脚本更容易维护。

伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来

自建prometheus监控以及告警体系

基于prometheus和运维平台实现了秒级监控告警。

go服务依赖查询功能

伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来

每次上线打包时向运维平台上报go.sum,把当前线上环每个微服务的具体依赖存在数据库里面,在运维平台实现查询页面。

k8s服务资源监控以及自动调整

周期性从自建prometheus里面拉取数据,基于服务过去的资源使用量和服务优先级生成新的资源限制。

伊对云原生CI_CD改造历程公司背景非容器时代docker时代kubernetes时代未来

缺少更细粒度的流量控制

目前仅实现了基于流量百分比的灰度,而且大量ab测试逻辑在代码里面,不利于维护。

在gitlab仓库维护的配置文件不够灵活

无法实现热更新,或者环境级别的配置管理。

基于Cronjob实现的定时任务不够灵活

不能方便的实现紧急停止、临时触发等功能。

业务服务总体资源使用不符合阿里云ECS规格

业务服务使用的内存很少,CPU比内存接近4:1,集群内存使用率低。

研发人员对运维平台不熟悉运维平台

功能越来越复杂,很少有研发能充分使用,尤其是新同事。

未来

探索Service mesh

使用nacos

自己实现定时任务逻辑

将一些缓存redis部署到k8s集群

增加运维平台引导