原创文章,转载请务必将下面这段话置于文章开头处。
本文转发自技术世界原文链接 http://www.jasongj.com/spark/ci_cd/

本文所述内容基于某顶级互联网公司数万节点下 Spark 的 CI 与 CD & CD 实践。为了提高本文内容的可借鉴性,隐去了公司特有内容,只保留通用部分

Spark CI 持续集成实践

CI 介绍

持续集成是指,及时地将最新开发的且经过测试的代码集成到主干分支中。

持续集成的优点

  • 快速发现错误 每次更新都及时集成到主干分支中,并进行测试,可以快速发现错误,方便定位错误
  • 避免子分支大幅偏离主干分支 主干在不断更新,如果不经常集成,会产生后期集成难度变大,甚至难以集成,并造成不同开发人员间不必要的重复开发
  • 为快速迭代提供保障 持续集成为后文介绍的持续发布与持续部署提供了保证

Spark CI 实践

目前主流的代码管理工具有,Github、Gitlab等。本文所介绍的内容中,所有代码均托管于私有的 Gitlab 中。

鉴于 Jenkins 几乎是 CI 事实上的标准,本文介绍的 Spark CI CD & CD 实践均基于 Jenkins 与 Gitlab。

Spark 源码保存在 spark-src.git 库中。

由于已有部署系统支持 Git,因此可将集成后的 distribution 保存到 Gitlab 的发布库(spark-bin.git)中。

每次开发人员提交代码后,均通过 Gitlab 发起一个 Merge Requet (相当于 Gitlab 的 Pull Request)

每当有 MR 被创建,或者被更新,Gitlab 通过 Webhook 通知 Jenkins 基于该 MR 最新代码进行 build。该 build 过程包含了

  • 编译 Spark 所有 module
  • 执行 Spark 所有单元测试
  • 执行性能测试
  • 检查测试结果。如果有任意测试用例失败,或者性能测试结果明显差于上一次测试,则 Jenkins 构建失败

Jenkins 将 build 结果通知 Gitlab,只有 Jenkins 构建成功,Gitlab 的 MR 页面才允许 Merge。否则 Gitlab 不允许 Merge

另外,还需人工进行 Code Review。只有两个以上的 Reviewer 通过,才能进行最终 Merge

所有测试与 Reivew 通过后,通过 Gitlab Merge 功能自动将代码 Fast forward Merge 到目标分支中

该流程保证了

  • 所有合并进目标分支中的代码都经过了单元测试(白盒测试)与性能测试(黑盒测试)
  • 每次发起 MR 后都会及时自动发起测试,方便及时发现问题
  • 所有代码更新都能及时合并进目标分支

Spark CD 持续交付

CD 持续交付介绍

持续交付是指,及时地将软件的新版本,交付给质量保障团队或者用户,以供评审。持续交付可看作是持续集成的下一步。它强调的是,不管怎么更新,软件都是可随时交付的。

这一阶段的评审,一般是将上文集成后的软件部署到尽可能贴近生产环境的 Staging 环境中,并使用贴近真实场景的用法(或者流量)进行测试。

持续发布的优点

  • 快速发布 有了持续集成与持续发布,可快速将最新功能发布出来,也可快速修复已知 bug
  • 快速迭代 由于发布及时,可以快速判断产品是否符合产品经理的预期或者是否能满足用户的需求

Spark CD 持续发布实践

这里有提供三种方案,供读者参考。推荐方案三

方案一:单分支

正常流程

如下图所示,基于单分支的 Spark 持续交付方案如下

  • 所有开发都在 spark-src.git/dev(即 spark-src.git 的 dev branch) 上进行
  • 每周一将当前最新代码打包,放进 spark-bin.git/devspark-${ build # }(如图中第 2 周的 spark-72)文件夹内
  • spark-prod 指向当前 spark-dev 指向的文件夹(如图中的 spark-71 )
  • spark-dev 指向 spark-${ build # }(如图中的 spark-72)
  • 自动将 spark-bin.git 最新内容上线到 Staging 环境,并使用 spark-dev 进行测试
  • spark-prod 比 spark-dev 晚一周(一个 release 周期),这一周用于 Staging 环境中测试

注:

  • 蓝色圆形是正常 commit
  • 垂直虚线是发布时间点,week 1、week 2、week 3、week 4
  • 最上方黑色粗横线是源码时间线
  • 下方黄色粗横线是 release 时间线
  • 绿色方框是每周生成的 release,带 build #
  • 蓝色方框是开发版本的 symbolic
  • 橘色方框是线上版本的 symbolic

bug fix

在 Staging 环境中发现 spark-dev 的 bug 时,修复及集成和交付方案如下

  • 如果在 Staging 环境中发现了 spark-dev 的 bug,且必须要修复(如不修复,会带到下次的 spark-prod 的 release 中),则提交一个 commit,并且 commit message 包含 bugfix 字样(如图中黑色圆形 commit 9 所示)
  • 该 bugfix 被 Merge 后,Jenkins 得到通知
  • Jenkins 发现该 commit 是 bugfix,立即启动构建,生成spark-${ build \# }(如图中的 spark-73)
  • spark-dev 指向 spark-${ build \# } (如图中的 spark-73 )

hot fix

生产环境中发现 bug 时修复及交付方案如下

  • 如果发现线上版本(即 spark-prod)有问题,须及时修复,则提交一个 commit,并且 commit message 包含 hotfix 字样 (如图中红色圆形 commit 9 所示)
  • 该 hotfix 被 Merge 后,Jenkins 得到通知
  • Jenkins 发现该 commit 是 hotfix,立即启动构建,生成 spark-${ build \# }(如图中的 spark-73)
  • spark-dev 与 spark-prod 均指向 spark-${ build \# } (如图中的 spark-73 )

Pros.

  • spark-src.git 与 spark-bin.git 都只有一个分支,维护方便
  • spark-prod 落后于 spark-dev 一周(一个 release),意味着 spark-prod 都成功通过了一周的 Staging 环境测试

Cons.

  • 使用 spark-prod 与 spark-dev 两个 symbolic,如果要做灰度发布,需要用户修改相应路径,成本较高
  • hotfix 时,引入了过去一周多(最多两周)未经 Staging 环境中通过 spark-dev 测试的 commit,增加了不确定性,也违背了所有非 hotfix commit 都经过了一个发布周期测试的原则

方案二:两分支

正常流程

如下图所示,基于两分支的 Spark 持续交付方案如下

  • spark-src.gitspark-bin.git 均包含两个分支,即 dev branch 与 prod branch
  • 所有正常开发都在 spark-src.git/dev 上进行
  • 每周一(如果是 weekly release)从 spark-src.git/dev 打包出一个 release 放进 spark-bin.git/devspark-${ build \# } 文件夹内(如图中第 2 周上方的的 spark-2 )。它包含了之前所有的提交(commit 1、2、3、4)
  • spark-bin.git/dev 的 spark 作为 symbolic 指向 spark-${ build \# } 文件夹内(如图中第 2 周上方的的 spark-2)
  • spark-src.git/prod 通过 fast-forward merge 将 spark-src.git/dev 一周前最后一个 commit 及之前的所有 commit 都 merge 过来(如图中第 2 周需将 commit 1 merge 过来)
  • spark-src.git/prod 打包出一个 release 放进 spark-bin.git/prodspark-${ build \# } 文件夹内(如图中第 2 周下方的的 spark-1 )
  • spark-bin.git/prod 的 spark 作为 symbolic 指向 spark-${ build \# }

bug fix

在 Staging 环境中发现了 dev 版本的 bug 时,修复及集成和交付方案如下

  • spark-src.git/dev上提交一个 commit (如图中黑色的 commit 9),且 commit message 包含 bugfix 字样
  • Jenkins 发现该 commit 为 bugfix 后,立即构建,从 spark-src.git/dev 打包生成一个 release 并放进 spark-bin.git/devspark-${ build \# } 文件夹内(如图中第二周与第三周之间上方的的 spark-3 )
  • spark-bin.git/dev 中的 spark 作为 symbolic 指向 spark-${ build \# }

hot fix

在生产环境中发现了 prod 版本的 bug 时,修复及集成和交付方案如下

  • spark-src.git/dev 上提交一个 commit(如图中红色的 commit 9),且 commit message 包含 hotfix 字样
  • Jenkins 发现该 commit 为 hotfix 后,立即将 spark-src.git/dev 打包生成 release 并 commit 到 spark-bin.git/devspark-${ build \# } (如图中上方的 spark-3 )文件夹内。 spark 作为 symbolic 指向该 spark-${ build \# }
  • 通过 cherry-pick 将 commit 9 double commit 到 spark-src.git/prod(如无冲突,则该流程全自动完成,无需人工参与。如发生冲突,通过告警系统通知开发人员手工解决冲突后提交)
  • spark-src.git/prod 打包生成 release 并 commit 到 spark-bin.git/prodspark-${ build \# } (如图中下方的 spark-3 )文件夹内。spark作为 symbolic 指向该spark-${ build \# }

Pros.

  • 无论是 dev 版还是 prod 版,路径都是 spark。切换版对用户透明,无迁移成本
  • 方便灰度发布
  • hotfix 不会引入未经测试的 commit,稳定性更有保障
  • prod 版落后于 dev 版一周(一个 release 周期),即 prod 经过了一个 release 周期的测试,稳定性强

Cons.

  • hot fix 时,使用 cherry-pick,但 spark-src.git/dev(包含 commit 1、2、3、4、5) 与 spark-src.git/prod(包含 commit 1) 的 base 不一样,有发生冲突的风险。一旦发生冲突,便需人工介入
  • hot fix 后再从 spark-src.git/dev 合并 commit 到 spark-src.git/prod 时需要使用 rebase 而不能直接 fast-forward merge。而该 rebase 可能再次发生冲突
  • bug fix 修复的是当前 spark-bin.git/dev 的 bug,即图中的 commit 1、2、3、4 后的 bug,而 bug fix commit 即 commit 9 的 base 是 commit 5,存在一定程度的不一致
  • bug fix 后,第 3 周时,最新的 spark-bin.git/dev 包含了 bug fix,而最新的 spark-bin.git/prod 未包含该 bugfix (它只包含了 commit 2、3、4 而不包含 commit 5、9)。只有到第 4 周,spark-bin.git/prod 才包含该 bugfix。也即 Staging 环境中发现的 bug,需要在一周多(最多两周)才能在 prod 环境中被修复。换言之,Staging 环境中检测出的 bug,仍然会继续出现在下一个生产环境的 release 中
  • spark-src.git/devspark-src.git/prod 中包含的 commit 数一致(因为只允许 fast-forward merge),内容也最终一致。但是 commit 顺序不一致,且各 commit 内容也可能不一致。如果维护不当,容易造成两个分支差别越来越大,不易合并

方案三:多分支

正常流程

如下图所示,基于多分支的 Spark 持续交付方案如下

  • 正常开发在 spark-src.git/master 上进行
  • 每周一通过 fast-forward merge 将 spark-src.git/master 最新代码合并到 spark-src.git/dev。如下图中,第 2 周将 commit 4 及之前所有 commit 合并到 spark-src.git/dev
  • spark-src.git/dev 打包生成 release 并提交到 spark-bin.git/devspark-${ build \# }(如下图中第 2 周的 spark-2) 文件夹内。spark 作为 symbolic,指向该 spark-${ build \# }
  • 每周一通过 fast-forward merge 将 spark-src.git/master 一周前最后一个 commit 合并到 spark-src.git/prod。如第 3 周合并 commit 4 及之前的 commit
  • 上一步中,如果 commit 4 后紧临有一个或多个 bugfix commit,均需合并到 spark-src.git/prod 中,因为它们是对 commit 4 进行的 bug fix。后文介绍的 bug fix 流程保证,如果对 commit 4 后发布版本有多个 bug fix,那这多个 bug fix commit 紧密相连,中间不会被正常 commit 分开
  • spark-src.git/prod 打包生成 release 并提交到 spark-bin.git/prodspark-${ build \# }(如下图中第 2 周的 spark-2) 文件夹内。spark 作为 symbolic,指向该 spark-${ build \# }

bug fix

在 Staging 环境中发现了 dev 版本的 bug 时,修复及集成和交付方案如下

  • 如下图中,第 2 周与第 3 周之间在 Staging 环境中发现 dev 版本的 bug,在 spark-src.git/dev(包含 commit 1、2、3、4) 上提交一个 commit(如图中黑色的 commit 9),且 commit message 中包含 bugfix 字样
  • Jenkins 发现该 bugfix 的 commit 后立即执行构建,将 spark-src.git/dev 打包生成 release 并提交到 spark-bin.git/devspark-${ build \# }(如图中的 spark-3) 文件夹内,spark 作为 symbolic,指向该 spark-${ build \# }
  • 通过 git checkout master 切换到 spark-src.git/master ,再通过 git rebase dev 将 bugfix 的 commit rebase 到 spark-src.git/master,如果 rebase 发生冲突,通过告警通知开发人员人工介入处理冲突
  • 在一个 release 周期内,如发现多个 dev 版本的 bug,都可按上述方式进行 bug fix,且这几个 bug fix 的 commit 在 spark-src.git/dev 上顺序相连。因此它们被 rebase 到 spark-src.git/master 后仍然顺序相连

hot fix

在生产环境中发现了 prod 版本的 bug 时,修复及集成和交付方案如下

  • spark-src.git/prod 中提交一个 commit,且其 commit message 中包含 hotfix 字样
  • Jenkins 发现该 commit 为 hotfix,立即执行构建,将 spark-src.git/prod 打包生成 release 并提交到 spark-bin.git/prodspark-${ build \# }(如图中的 spark-3) 文件夹内,spark 作为 symbolic,指向该 spark-${ build \# }
  • 通过 git checkout master 切换到 spark-src.git/master,再通过 git rebase prod 将 hotfix rebase 到 spark-src.git/master
  • 在一个 release 周期内,如发现多个 prod 版本的 bug,都可按上述方式进行 hot fix

灰度发布

本文介绍的实践中,不考虑多个版本(经实践检验,多个版本维护成本太高,且一般无必要),只考虑一个 prod 版本,一个 dev 版本

上文介绍的持续发布中,可将 spark-bin.git/dev 部署至需要使用最新版的环境中(不一定是 Staging 环境,可以是部分生产环境)从而实现 dev 版的部署。将 spark-bin.git/prod 部署至需要使用稳定版的 prod 环境中

回滚机制

本文介绍的方法中,所有 release 都放到 spark-${ build \# } 中,由 spark 这一 symbolic 选择指向具体哪个 release。因此回滚方式比较直观

  • 对于同一个大版本(dev 或者 prod)的回滚,只需将 spark 指向 build # 较小的 release 即可
  • 如果是将部分环境中的 prod 版迁至 dev 版(或者 dev 版改为 prod 版)后,需要回滚,只需将 dev 改回 prod 版(或者将 prod 版改回 dev 版)即可

Pros.

  • 正常开发在 spark-src.git/master 上进行,Staging 环境的 bug fix 在 spark-src.git/dev 上进行,生产环境的 hot fix 在 spark-src.git/prod 上进行,清晰明了
  • bug fix 提交时的 code base 与 Staging 环境使用版本的 code 完全一致,从而可保证 bug fix 的正确性
  • bug fix 合并回 spark-src.git/master 时使用 rebase,从而保证了 spark-src.git/devspark-src.git/master 所有 commit 的顺序与内容的一致性,进而保证了这两个 branch 的一致性
  • hot fix 提交时的 code base 与 生产环境使用版本的 code 完全一致,从而可保证 hot fix 的正确性
  • hot fix 合并回 spark-src.git/master 时使用 rebase,从而保证了 spark-src.git/devspark-src.git/master 所有 commit 的顺序性及内容的一致性,进而保证了这两个 branch 的一致性
  • 开发人员只需要专注于新 feature 的开发,bug fix 的提交,与 hot fix 的提交。所有的版本维护工作全部自动完成。只有当 bug fix 或 hot fix rebase 回 spark-src.git/master 发生冲突时才需人工介入
  • spark-bin.git/devspark-bin.git/prod 将开发版本与生产版本分开,方便独立部署。而其路径统一,方便版本切换与灰度发布

Cons.

  • 在本地 spark-src.git/master 提交时,须先 rebase 远程分支,而不应直接使用 merge。在本方案中,这不仅是最佳实践,还是硬性要求
  • 虽然 bug fix 与 hot fix commit 都通过 rebase 进入 spark-src.git/master。但发生冲突时,需要相应修改 spark-src.git/master 上后续 commit。如上图中,提交红色 commit 9 这一 hot fix 后,在 rebase 回 spark-src.git/master 时,如有冲突,可能需要修改 commit 2 或者 commit 3、4、5。该修改会造成本地解决完冲突后的版本与远程版本冲突,需要强制 push 回远程分支。该操作存在一定风险

Spark CD 持续部署

持续部署是指,软件通过评审后,自动部署到生产环境中

上述 Spark 持续发布实践的介绍都只到 "将 *** 提交到 spark-bin.git" 结束。可使用基于 git 的部署(为了性能和扩展性,一般不直接在待部署机器上使用 git pull --rebase,而是使用自研的上线方案,此处不展开)将该 release 上线到 Staging 环境或生产环境

该自动上线过程即是 Spark 持续部署的最后一环

Spark 系列文章

Spark 灰度发布在十万级节点上的成功实践 CI CD的更多相关文章

  1. .Net在Windows上使用Jenkins做CI/CD的那些事

    背景 最近入职了一家新公司,公司各个方面都让我非常的满意,我也怀着紧张与兴奋的心情入职后,在第一天接到了领导给我的第一个任务——把整个项目的依赖引用重新整理并实施项目的CI/CD. 本篇的重点主要分享 ...

  2. 前端静态站点在阿里云自建 K8S DevOps 集群上优雅的进行 CI/CD

    目录 网站 域名 K8S DevOps 集群 私有 Gitlab 使用 Docker 编译站点 * Dockerfile * 构建编译 Image * 测试编译 Image * 推送镜像到 Aliyu ...

  3. 《Spark 官方文档》在Mesos上运行Spark

    本文转自:http://ifeve.com/spark-mesos-spark/ 在Mesos上运行Spark Spark可以在由Apache Mesos 管理的硬件集群中运行. 在Mesos集群中使 ...

  4. Spring cloud架构中利用zuul网关实现灰度发布功能

    蓝绿发布.金丝雀发布(灰度发布).AB测试 首先,了解下这几种发布方式的基础概念. 目前常见的发布策略有蓝绿发布.金丝雀发布(灰度发布).AB测试这几种,在国内的开发者中,对这几个概念有独立的理解.蓝 ...

  5. Istio最佳实践:在K8s上通过Istio服务网格进行灰度发布

    Istio是什么? Istio是Google继Kubernetes之后的又一开源力作,主要参与的公司包括Google,IBM,Lyft等公司.它提供了完整的非侵入式的微服务治理解决方案,包含微服务的管 ...

  6. Kubernetes 1.14发布:对Windows节点的生产级支持、Kubectl更新与持久本地卷通用版本已全面到来

    今天,我们高兴地宣布Kubernetes 1.14版本的正式亮相,这亦是我们在2019年当中进行的首次发布!Kubernetes 1.14版本由31项增强功能组成,具体包括:10项稳定版功能,12项b ...

  7. 【转】Spring Framework灰度发布

    今天简单介绍下SpringFramework微服务中几种服务发布策略以及实现方式.我接触过的有蓝绿.滚筒和灰度发布. 蓝绿发布: 简单说就像美帝选总统投票一样,非蓝即绿一刀切,这个其实也是传统软件架构 ...

  8. 蓝绿部署、红黑部署、AB测试、灰度发布、金丝雀发布、滚动发布的概念与区别(转)

    出处:https://www.baidu.com/link?url=QjboallwNm_jxcL3fHG57wEakiBfAs_3-TChTGu1eBXstlHEsGBc-NDA7AKTqsiroB ...

  9. 使用gitlab, jenkins搭建CI(持续集成)系统(4) 灰度发布publish

    publish环境是正式环境,和dev, test, prepublish环境不同的是,正式环境一般要更加谨慎一些,发布的时候需要有一个灰度过程,即:分多次部署,每次部署几个服务器节点,验证没有问题以 ...

随机推荐

  1. JAVA 把小数分成整数和小数

    在进行进制转换的时候,我们需要把小数分为整数和小数两部分. 这里介绍两种方法. 第一种举个例子:1.23分为1 和 0.23 第二种:1.23 分为 1 和23 有时需要具体情况具体分析自己需要哪种类 ...

  2. 第九次java课堂笔记

  3. 【转载】C++对象成员与构造函数

    一个类的对象可以作为另一个类的数据成员,此时把该对象称为类的对象成员. 当一个类中出现对象成员时,该类的构造函数就要为对象成员初始化,对象成员的初始化必须在构造函数的初始化表中完成. 注意: 初始化对 ...

  4. vscode 创建.net core项目初体验

    微软的virtual studio编辑器那是宇宙第一大编辑器,可惜就是太笨重,遇到性能差一些的电脑设备,简直无法快速的编辑项目. 而vs code编辑器轻便易用,想要编辑哪种项目,只需扩展插件就OK, ...

  5. ubuntu16.04安装pycharm

    Ubuntu16.04下,默认安装了python2.7和python3.5,在终端下,输入“Python”  或“python3”可查看具体版本. 1.安装PyCharm前,先配置PyCharm的JD ...

  6. springmvc初始化失败问题跟踪

    1.问题 访问路径http://10.118.30.52:8088/helloWorld/hello后会报404错误,原因是springmvc配置文件中的包扫描路径错误.修改配置如下: <con ...

  7. C语言作业(心理魔术)

    #include "stdafx.h" #include "stdio.h" #include "stdlib.h" #include &q ...

  8. Exploit-Exercises nebule 旅行日志(四)

    接着上次的路程继续在ubuntu下对漏洞的探索练习,这次是level03了 先看下level03的问题描述: 精炼下问题,在/home/flag03的目录下有个crontab的文件是每分钟都在执行 这 ...

  9. k8s的flannel的pod运行一段时间init error

    问题现象 使用Kubeadm部署的flannel网络运行一段时间后,提示init:Error错误,查看具体的信息如下: [root@node1 ~]# kubectl describe pod kub ...

  10. Java冒泡法和二分法

    最近去一家公司面试,手贱在人家CTO面前自告奋勇写了一把冒泡法,结果在交换数据的时候出了洋相,回家反思,写下如下代码,对自己算是一个鞭策,得到的教训是不要眼高手低,低调前行. package com. ...