XXOps实践:持续发布和部署

2020 年 3 月 18 日

XXOps实践:持续发布和部署

DevOps也好,NoOps也罢,关键看是否解决了业务问题,消除了痛点,这才是价值。


上周分享了一篇文章《有了CMDB,为什么还要应用配置管理》,主要讲了基础层面应该怎么做,那基础的东西做好了,如果用不起来,就没有价值,那我们今天就来看看在此基础之上的一些实践。


为什么要先做持续发布和部署?


首先,根本原因还是为了提升代码的交付效率(好像是句正确的废话),从技术上,主要原因还是因为从单体工程拆分成了服务化的应用。


单体工程的历史原因也很好理解,在创业初期,技术人员有限的情况下,为了快速找到正确的业务发展方向,技术人员必定会把全部的精力放到业务需求的实现上,这时技术或架构层面的事情是次要的,且在创业早期,用户量和业务模型也没有这么复杂,也没有必要搞复杂的架构出来,LNMP 足矣,从我们的实践来看这样的技术选择是无比正确的。


但是,随着业务量和业务复杂度的增加(如电商的用户、商家、店铺、商品、交易和支付体系),业务服务质量要求变得越来越苛刻(低时延、高可用),需求的交付周期却要求越来越短(脑补下产品经理站在程序员身后的场景),同时工程师数量也已经到了几百人规模,这时单体工程就暴露出很多问题,比如代码逻辑耦合严重、底层公共方法不敢动,代码量巨大已经没有任何一个人能对整个工程代码熟悉,上百个工程师同时向同一个工程提交代码,代码维护耗费的精力非常大。最终一张图看下当时的场景:



所以,解决方案向着 Java 服务化的方向演进(具体原因就不解释了),简单示意如下:



单体应用拆分成服务化应用后,应用的情况就变成了许许多多、大大小小的应用模式,下图示意:



这个时候遇到的最棘手的问题,就是发布问题,这么多的应用对应的代码都是不同的,且 Java 服务化之后涉及编译构建、服务优雅上下线等一系列等问题,环节也多了很多(后面会看到),这些环节单纯的靠手工执行脚本和人为的串联已经成为不可能的事情,这个直接影响到开发、测试和运维整个研发体系的效率,所以是摆在面前必须要首要解决的。


好的,接下来我们就开始做发布系统了,提炼一下,发布做的事情就是,将提交后的应用代码,进行编译打包,然后发布到应用对应 IP 主机的指定目录下,并且做到应用服务的优雅上下线(或者叫做优雅启停)。


理解起来不复杂,其实我们可以抽象出发布的最重要的三步,我们也是始终围绕着这三步在不断的完善,如下:



下面就分阶段详细描述三个主要环节:


代码提交环节(分支合并管理)


代码管理工具是 Gitlab,代码提交过程中最重要的就是对于分支合并的管理(这里又涉及到标准规范的制定),我们的策略示意大致如下:



描述如下:


1、master 分支,跟线上应用代码保持同步,也就是说随时可以发布到线上进行部署运行。


2、开发分支,通常以 feature/defect 来表示,比如开发一个新的需求,就会以当前 master 为基线,拉一个 feature 分支出来进行开发,同一个应用可以同时存在多个 feature 分支并行开发。


3、发布分支,以 release 表示,在发布时会将所有提交集成的代码 commit 合并,形成 release_环境_时间戳为分支名称,比如 release_dev_01_29_20_52_10 就代表该分支是在 1 月 29 号 20:52:10 在 dev 环境发布时创建的临时发布分支。


4、从预发进入线上时,会以当前预发环境的发布分支 release_pre_xxxx 为基线创建一个 release_online 分支,作为线上的发布分支,线上发布结束后会把 release_online 分支合并到 master 中,这也就保证了发布到线上的代码最终一定会跟 master 的代码保持一致。


编译构建环节


上面讲完了代码提交环节,下一个环节就是要构建了,以 Java 为例,构建我们用到了两个工具,Docker 和 Maven。Docker 主要用来提供一个干净独立的编译环境,Maven 作为我们的依赖管理和打包工具。整个构建过程如下:



以 Java 为例,简单描述如下:


1、首先准备好 JDK 的编译镜像,这个镜像环境与线上环境保持一致,当有新的构建任务进来时,就创建一个对应的 Docker 实例进行代码编译;


2、构建任务会根据应用配置管理中的 Git 地址,将代码 clone 下来放到指定的编译目录,Docker 实例启动后,将编译目录挂在到 Docker 实例中;


3、对于 Java 应用,在这个 Docker 实例环境中,就可以执行 mvn package 命令打包了,最终会生成一个可发布 xxx.war 的软件包;


4、同样的,对于 C++,Go、NodeJs,也会准备好类似的编译镜像,不同的是打包时,对于 C++是 cmake&make,Go 就是 go install 等等,最终也会生成一个可发布的软件包;


5、构建完成后,Docker 实例销毁;


这里面 Docker 发挥了一个很大的作用,就是提供了干净的,互不干扰的编译环境,且对于并发打包的情况,Docker 快速创建多个并行的实例出来提供编译环境,使用完销毁,这个效率上的优势也是非常大的。


6、关于配置管理,当时设计时考虑比较简单,我们的做法是同时做三个配置文件出来,dev_pom.xml,pre_pom.xml,online_pom.xml,分别对应开发、预发和线上三个不同的环境,根据发布的环境不同,将不同的配置文件替换上去。这样做其实可扩展性不够,对于多机房、多分组的情况会有更多的配置文件创建出来,且对于有敏感信息的配置项保密也不够(不过这些配置都已经加密挪到中间件的配置中心了),更好的办法可以考虑采用阿里早已开源的 auto-config 方案,这里就不细讲了。


部署环节


以上,代码提交和编译构建完成后,就该进入发布到线上的部署环节了,也就是将代码发布到应用对应 IP 主机的指定目录下,并且能够优雅的上下线应用服务,貌似很简单,但是,看下图:



这个过程的环节还是比较多的,这些环节内部又会有很多的细节,所以整个部署环节是很复杂的,下面将整体思路介绍一下:


0、从 CMDB 中,拿到应用-主机 IP 对应关系,然后再从 1 开始做,后面的过程可以是针对单台机器做,也可以是分批或分组多台机器同时做。(从第 0 步开始,原因就是我们上一篇文章里面说的 CMDB 和应用配置管理的基础要先打好,这个基础没有,下面的环节就无法顺畅地执行);


1、检查每台机器上的服务是否正常运行的,如果是正常服务的,说明可以发布,但是服务本身异常,就要记录或跳过了;


2、下载 war 包到指定目录(应用的目录信息,应用配置管理又发挥作用了);


3、将监控关闭,以免服务下线和停止产生误告警;


4、优雅下线,包含 RPC 服务从 LB 下线或 Web 服务从 Nginx 下线(如果不提供 http 服务就不涉及),下线动作均通过 API 接口调用方式实现;


5、下线后自动检测无新的流量进入,停止应用,发布代码,然后启动应用(这时应用配置管理里面,启停命令等等就又发挥作用了);


6、优雅上线,进行健康监测,检查进程和应用状态是否正常,如果全部监测通过,开始上线服务,开启监控;


7、分批发布,这里简单提一下,假设我们一个应用有 100 台主机,这个时候做发布不可能全部一把停掉,这样服务会中断,但是也不能一台台的做,这样效率又太低,所以我们可以折中分批发,比如可以分 5 批,每批 20 台,也可以分 10 批,每批 10 台,这样既可以保证在线升级,也可以保证效率能够跟的上。当然复杂一点,还有分组分批,或者按步长分批,第一批 5 台,第二批 10 台,后面每批 20 台等等。示例如下:



整个过程下来我们可以看到:


1、基于场景入手,将业务流程梳理细致,做到细分环节,每步自动化,流程串联


2、上篇文章提到的 CMDB 和应用配置管理的内容,在这一部分无时无处不在发挥着基础的作用


效果:NoOps or DevOps?


发布系统上线后,整个过程已经可以做到开发全程自助发布,之前运维还在参与审批,后来审批环节也省略,在发布这个过程中,全流程 NoOps。效果如下图所示:



本文转载自成哥的世界公众号。


原文链接:https://mp.weixin.qq.com/s/sybuQrfPshT_M3yW2OS6yw


2020 年 3 月 18 日 20:06109

评论

发布
暂无评论
发现更多内容

微服务部署测试简单实践

MySQL从删库到跑路

zookeeper 微服务 dubbo Sprint Boot session

AQS 都看完了,Condition 原理可不能少!

程序员小航

Java 源码 源码阅读 JUC Condition

智能的本质—DIKW结构

良少

学习 AI 智能 DIKW

LeetCode题解:242. 有效的字母异位词,哈希表两次循环,JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

架构师训练营第一期 - 第三周课后 - 作业一

极客大学架构师训练营

区块链技术在司法行业的服务应用

CECBC区块链专委会

区块链 司法

极客时间架构师培训 1 期 - 第 3周总结

Kaven

第三周学习心得

alpha

极客大学架构师训练营

极客时间架构师培训 1 期 - 第 3 周作业

Kaven

架构师训练营第三周学习总结

听夜雨

极客大学架构师训练营

第三周用组合设计模式编写程序

Geek_fabd84

为啥你用@JsonFormat注解时,LocalDateTime会反序列化失败?

冰河

springboot LocalDateTime JsonFormat

ARTS Week12

丽子

架构师训练营第 1 期 - 第三周课后练习

Anyou Liu

极客大学架构师训练营

如何理解区块链行业的安全问题?

CECBC区块链专委会

区块链 人工智能 大数据

区块链到底是什么?它为什么如此受人关注

CECBC区块链专委会

区块链 金融

架构师训练营第 3 周作业

netspecial

极客大学架构师训练营

架构师训练营 - 第三周作业

一个节点

极客大学架构师训练营

go语言设计的理解-工程化语言

superman

Java go 面向对象编程

LeetCode题解:242. 有效的字母异位词,哈希表一次循环,JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

架构师训练营 - 第三周总结

一个节点

极客大学架构师训练营

spring-boot-route(一)Controller接收参数的几种方式

Java旅途

Java Spring Boot

架构师训练营第一期 - 第三周课后 - 作业二

极客大学架构师训练营

UML学习笔记

Edison

UML

区块链12年:应用在了哪些领域?

CECBC区块链专委会

区块链 教育 金融 物流

一个草根的日常杂碎(10月1日)

刘新吾

随笔杂谈 生活记录 社会百态

互联网上正规平台的辨别方法?被AG黑网投黑钱拿回的技巧方案

InfoQ_6b6a6317a692

最完整的PyTorch数据科学家指南(1)

计算机与AI

学习 PyTorch

第三周作业

alpha

极客大学架构师训练营

第三周 学习总结

Yangjing

极客大学架构师训练营

架构师训练营第三周作业

听夜雨

极客大学架构师训练营

XXOps实践:持续发布和部署-InfoQ