🙅 持续集成和部署(CI 和 CD)
!本篇文章过于久远,其中观点和内容可能已经不准确,请见谅!~
想分享的是现代团队协作的基础建设,简单梳理下日常持续集成的方法
持续集成是现代团队协作的基础建设了,一个成熟的团队肯定会搭建自己的整个持续集成和持续部署流程。
持续集成和部署,大部分需要理清从构建到部署中间需要哪些步骤和状态,手动怎么做的,声明或者脚本实现一模一样的流程,加上一些错误边界处理、通知或者触发条件等,很多流程相似又不太相同。
具体选择哪些部署方式、哪些工具、什么技术、是否需要人工介入、通知等都有太多的选择了,不过目标最终都是一致的,用自动化的流程解放人力。
持续,很大程度上可以理解成:流程和自动化。一旦把一个重复的 workflow 分解成不同的阶段,可以对应有不同的确定动作和期望,那么就可以使用自动化来代替人工,进而实现可重复、快速迭代、频繁发布。
而持续集成、持续交付、持续部署这些词,强调的是不同的业务目标,比如:
- 持续集成是强调开发过程中的自动化构建、测试之类的工作,目的是将确定功能正确集成到产品中,大部分是在开发阶段,运行频繁。大部分的目的是将诸如大量的测试用例运行、耗时的编译、开发的模块连接到完整产品等需求,使用一些自动化的手段在独立的环境里面运行测试用例,然后构建出结果。
- 持续交付是以交付为目的,新的功能交付给产品团队或者客户进行评审检查,这一步包含完整的功能,产出可以进行交付的结果,这个基本上在需求基本测试完成之后,目标是可以小范围内测、测试环境或者预发布环境。
- 持续部署是确定功能和需求完成,完成了基本的构建,也通过了质量检查和测试工作,使用可持续的工作流将功能部署到生产环境。
现在基本上大大小小的团队都会接入持续的工作流,基础建设比较好的公司会搭建很多集成平台:
- 代码仓库:提供代码存储、MergeRequest、代码评审、质量分析、安全扫描、webhooks 的功能,这个是最基本的,源码管理都需要有,自己搭建 Gitlab,或者找私有仓库的 SaaS。
- ISSUE 平台:包含项目、需求、任务、里程碑、BUG 追踪等,甚至还有文档、文件等内容管理,管控开发进度等。
- 自动化构建平台:被 webhooks 触发、定时或者手动构建,提供单元测试、端到端测试、集成测试、UI 测试、人工测试等测试结果,同时还可能会提供静态代码分析、动态代码分析、安全审查等给出代码漏洞,最终构建出一个制品产出,最后可能是压缩文件、npm、docker 镜像或者jar包等。
- 自动部署平台:通过比如代码合并到主分支或者发布分支,然后自动打包构建出生产版本,上传、拉取镜像等方式部署到云服务器、集群、k8s编排系统等。
整个流程如果细说起来那太多了,这篇文章说下我个人的博客持续集成,然后简单提下公司项目的持续集成,都是我亲手搭建,还算理解深刻,大部分采用的成熟软件和平台,成本很低,体验还算不错。
我自己博客的发展经历了 博客平台、wordpress、从头搭建、静态构建,现在是在用 Gatsby 结合 mdx,表现力强、插件丰富、能使用 react、维护简单等。
写文章的时候体验还可以,直接 md 文件,文件夹聚合。但是每次部署的时候就比较麻烦了,需要 build 然后将之后的 public 文件夹传到服务器。源码到可访问的网站需要这些过程,中间历经了这几个选择:
有一台云服务器,有公网 IP,所以第一选择是搭建一个 nginx 静态服务器,然后把构建结果放到服务目录就ok了。
最简单的是在本地将文件都生成之后,上传到服务器即可。每次 yarn build 产出 public 上传每个文件就行了,有几个方案:
- 服务端启动一个 node server 接收文件上传,然后放置到对应的位置,文件一个个传;
- 采用 ftp server,文件夹同步到服务器;
- 本地打包,然后压缩成 zip 文件,ssh、ftp 或者简单 node servre 上传压缩文件,服务端接收后解压缩到对应的位置。
想要一键操作的话,整个持续部署机制可以做到一个命令,yarn deploy 然后触发下面的流程:
- yarn build 将源文件构建出 public 文件夹作为静态文件目录;
- node zip.js 将 public 压缩成 zip 单文件;
- node upload.js 将 zip 文件“弄”到服务端;
- 服务器端运行一个 node server 监听上传,或者使用 ftp server 接收文件;
- 脚本确定上传的文件后,调用服务端的 api 解压文件;
- 部署完毕,通知。
虽然不用一步步操作,一条命令即可,但是比较麻烦的在于每次写完之后,运行命令、等待上传,确认没问题才能离开,而项目越来越大之后构建和上传都比较耗时,整个流程很不可持续。
好处是不需要本地构建,也不需要盯着传输,只需要服务端自己运行拉取源文件就行,手动操作也不复杂 git pull,yarn build 两个命令。
自动构建和部署的过程:
- 客户端新文章写完之后,git 打上 tag 推送,git 仓库设置 webhook ,将新 tag 的消息发送给服务器;
- 服务器一个 node server 监听到 git 仓库的消息触发;
- node server 运行构建和部署机制;
- 自动 git pull 最新的代码;
- 运行 yarn build;
- 文件会自动构建并输出到版本文件夹;
- 将静态服务文件夹链接到最新版本的文件夹即可立即完成部署;
- 部署完成,通知。
比较省事,不需要本地做什么事情,只需要关心代码仓库的状态即可。
但是体验下来不好的地方在于:
- 构建环境影响服务器环境。node 需要保证版本,还可能受其他的项目影响;
- 需要一个监听服务响应 githook。
- 缓存很大。yarn install 之后能有 1.5G 的存储,这个虽然没啥影响,但是还是感觉有点 dirty。
- 构建错误和状态不可控。响应服务挂了、编译错误之类的没办法监控。
- 服务器访问不太理想。没有添加 CDN 服务,访问一旦上来之后,响应时间就有点卡顿了。
部署一段时间之后有一些改进:
- 使用容器代替环境。这样 node 版本和文件就不受服务器本身影响,比较干净,挂了也能够快速启动。
- 打包环境、部署环境分离。打包容器运行完之后产生一个部署镜像,打包的缓存在镜像中,眼不见心不烦。
- 错误通知。打包阶段做了错误提示,容器状态监控错误提示。
- 使用 CDN 来作为访问入口,源站保证可用即可。
基本能解决大部分的问题,小而美的感觉,各个服务也都十分可控。
持续构建的部分放到了 dockerfile 里面,部署和运行部分也是标准通用的操作镜像行为,不用侵入到业务里面了。
计划使用静态服务是因为自己的云服务器性能确实有限,挂载了很多服务,不太稳定,需要一些精力来维护。所以想要迁移到托管的环境上,不再花精力维护服务器状态。
有很多的选择:github pages 服务、Coding 静态页面、七牛云存储、腾讯云存储等都能完成这个任务。
其中 github pages 服务访问不太好,一开始就 pass 了。Coding 静态页面当时非 VIP 用户需要有中间页,而且业务多次变更,访问起来也不太稳定的样子。七牛云存储和腾讯对象储存,腾讯云体量大一些,相关云服务比较多,所以选择了腾讯云存储。
具体配置不说了,html 文件扔上去,开启静态网站即可,然后打开cdn功能,域名配置到cdn,设置回源策略等就能提供服务了。
第一阶段用的是本地构建和部署的脚本,和自己的服务器相同,只是服务器用的是云存储的sdk来接收文件。
- yarn build 将源文件构建出 public 文件夹作为静态文件目录;
- node deploy 将 public 文件按照过滤规则一个个上传到云存储里;
- 部署完毕,通知。
现在云函数很流行,腾讯云存储和腾讯云函数非常搭,提供了事例和触发事件。我们可以设置某一个路径的zip文件新建或更新就运行解压缩程序。
ci 服务也有很多,Coding 与腾讯合作后叫腾云 😅,目前感觉专注与devops,所以自动构建和部署做的很好,整个git仓库一直他家的。
感觉整个自动构建和部署都是腾讯家的:
- 用 coding 的 git 仓库存储
- 每次新版本确认,提交新的 tag
- Coding 新 tag 触发自动构建,然后用 jenkins 脚本,自动运行构建逻辑。
- Jenkins 控制流程完成获取环境变量、拉取代码、安装依赖、打包构建、压缩成 zip、最后上传到腾讯 cos。
- 腾讯云存储添加云函数,检测到新的 zip 文件就自动运行解压程序
- 解压完成,部署结束。
整个流程在一些关键节点处理下边界条件就能达到非常稳定的部署流程,触发条件只需要新的 tag 或者一个按钮点击。
而且全部对于个人开发者都有免费的额度资源,非常优雅好用。
除了持续构建和部署,博客项目整个托管和持续化很有意思:
- 仓库存储用的 coding 服务,算是 SaaS;
- 项目中的动态数据用的是 leancloud 的数据存储和相关 api,比如文章阅读数的更新,这是 BaaS;
- 博客的评论用的是 gitalk,本质上用的是 github 的 issues 功能和登录相关的开放接口;
- 一些即时通信和信息推送用的是 leancloud 集成的相关服务这是 BaaS;
- 根据数据存储在存储之后触发简单计算,用的是 leancloud 的云引擎,这是标准的 FaaS。
- 持续构建用的coding的自动构建;
- 自动构建出来的压缩包用云函数解压到对象存储,这是云函数 Faas;
- 对象存储作为静态网站;
- 静态网站作为源站添加 CDN;
所以整个项目都没有用到自己的服务器,每个步骤都用了非常优秀的项目来支撑,成本非常低,而且能够根据使用量付费,感觉非常的有意思。
公司项目的不同在于,出于保密、成本和可迁移的考虑,没有完全采用某一个完整的可持续方案,结合了内网、外网的可持续方式。
环境比较不同的地方:
- 首先是代码仓库是在内网,外部公网是没办法访问的,压根没有路由到外网。
- 内外网之分,内部出于测试的目的搭建有测试环境,一般部署最新的预发布分支。
- 然后是架构上有内网、自己的 IDC 机房和云服务,架构比较复杂,不可能 All in Cloud。
服务架构出于保密不能细说,大致提一些功能关键词,其中用到的技术栈关键的有: GitLab、Jenkins、Docker ( + Registry )、自建的部署管理等。
其中自建的部署管理管理全部的线上环境、制品库、当前在线版本、部署和发布计划等,算是私人订制,比较方便的了解。
产品复杂度上来之后,线上线下、各个服务状态、部署和发布等都比较混乱,所以针对这个事情,负责了这个项目,目的是把资源管理起来,包括服务器、容器、制品等,分别的状态管理、部署上线等,功能设计上尽可能全面,实现上渐进式实现,最终目标是实现整个项目的资源、状态、服务和告警功能。
- 基本的管理全部服务资源,包括云服务、IDC、内网机器的状态监控和报警。
- 各个服务容器的管理,服务容器、资源配置、上线版本和状态监控,目前除了特别的服务,几乎全部用容器包装编排,每台机器用来什么服务,每个服务多少资源等。
- 制品库的管理,有哪些制品,每个制品的业务、版本、部署运行说明等。
- 构建 hooks 和相关运行机制,Jenkins 服务管理。
在这个工具的帮助下,相关构建和可持续都比较好做了。
- 偏后端的底层的架构,使用较全的单元测试保证代码质量,自动生成文档来保证协作。
- 中间件使用 php 来搭建接口服务,每个接口有单元测试和性能测试。
- 使用容器来搭建不同业务的微服务。
- 容器的制品库维护和部署。
- 使用 VPN 网络拉取制品库,然后根据部署计划上线服务。
安卓、IOS、H5 以及 Flutter 的每日版本提测,里程碑版本打包等。
- 每个开发保证每日合并的分支都是可编译的,部分开发提交后需要代码评审再合并到测试分支;
- 每日凌晨之后定时触发构建,然后运行每个项目的测试,没问题之后编译出测试版本,打包成制品推送到制品库,自动部署到内网测试环境,然后给测试人员发送提测通知和相关更新日志。
- 测试人员使用接口服务和客户端进行人工相关测试用例测试,然后反馈到 bug 任务中。
- 开发再根据 bug 的紧急程度排期,从任务列表中开始开发任务。
这些步骤并不是严格的,遇到一些特殊情况可能某些环节被跳过,但是大部分情况执行这个流程,算是可持续的一个实践,目的也是为了保证严格的代码质量,不会出现积压很久的严重问题。
为了保证开发进度,很多时候一些刚接手的人会觉得这些很麻烦,说要快速迭代,可能不会合并到测试分支,或者不会做单元测试,要权限不去做代码评审,整个可持续流程也并没有严格的限制这些行为,但是流程正义本身并不是限制速度的,而是用固定的流程优化效率和质量的平衡,可持续的意义所在,所以每次开会的时候都会强调流程和专业,这对每个人和公司来说都非常重要。
可靠,和持续可靠。
感谢您的阅读,本文由 Ubug 版权所有。如若转载,请注明出处:Ubug(https://ubug.io/blog/ci-cd)