从零上⼿,打造前端CICD⼯作流(简单易学!)
CI/CD 是 Continuous Intergration/Continuous Deploy 的简称,翻译过来就是持续集成/持续部署。CD 也会被解释为持续交付(Continuous Delivery),但是对于软件⼯程师⽽⾔,最直接接触的应该是持续部署。
我刚开始⼯作时,就有接触过CI的概念,那个时候主要是团队 QA(质量保证)使⽤ hudson 对⼯程进⾏质量扫描,跑⼀些基础的⾃动化测试。当时印象最深的⼀幕就是 QA 对我说:”你的代码静态告警了,赶紧改⼀下...“。
现在⼀想,我不禁感到诧异,”咦?我们当时没有⽤ ESLint 吗?记不清楚了...“于是我翻了下 ESLint 的更新记录,发现那时候 ESLint 的⼤版本号才刚到3,VSCode 的 ESLint 插件也还是⽐较早期的版本,可能还没普及开吧。
后⾯我也慢慢地听到了 Jenkins, Travis CI 这样⼀些名词,但是由于太菜,我⼀个都不会⽤。
⽽且我发现,我对 CI/CD 并没有什么兴趣,为什么呢?因为我还没有使⽤它的动机。
构建/部署那些事
构建/部署说的简单点,就是先利⽤ webpack 或者 gulp 这类的⼯具把⼯程打包,然后把打包得到的⽂件放在服务器上某个托管静态资源的Web 容器⾥,像 Java 就可以放在 Tomcat,不过现在流⾏⽤ Nginx 托管静态资源。有了 Web 容器,前端打包的那些⽂件(⽐如index.html, main.js等等)就可以被访问到了,这个相信⼤家都懂。
16年~18年时,我还不负责打包部署这些事(另⼀⽅⾯也是因为前端根本没权限碰服务器啊,),所以我压根没关注打包部署这些事情。
18年到19年时,我开始负责打包部署了。当时完全没这⽅⾯经验,Linux 命令都是靠着⼀边百度⼀边敲。不过我清楚地记得,之前在测试组那间办公室看他们⽤的是xshell和xftp,把这俩⼯具搞来⽤后,我觉得部署真是简单,我只要跑个脚本,安静地等 webpack 和 gulp 的⼯作流结束后,把⽂件通过 xftp 传到服务器就⾏,只要注意不要操作出错就⾏了(显然,⼈为操作就容易出错,这也是个隐患)。由于构建部署的频率不⾼,项⽬数量也不是很多,这⼀年我基本应付得过来。
直到去年,我⼿底下有差不多5个项⽬,接近10个前端⼯程。在这种⽇常部署节奏下,我觉得 xshell+xftp 也救不了我,虽然这些项⽬不是天天都发版上线,但是测试环境还是经常发的,每天光部署这事我就够烦躁,写代码经常被打断,⽽且也⾮常浪费时间。
我想着要寻求些改变了,但我还是没考虑 CI/CD 这事,因为我觉得我好像还是不太懂 CI/CD。于是我考虑先⽤ shell 脚本来做构建/部署的事情,所以后来就有了这么两篇探索性的⽂章:
⾃动化部署的⼀⼩步,前端搬砖的⼀⼤步[1]
零基础学java要多久
前端⾃动化部署的深度实践[2]
靠着这⼀波脚本的探索,我基本上也是过渡到半⾃动化的阶段了,这种焦虑的状况基本上得到了⼀些缓解。但是,我发现我的电脑还是扛不住,风扇急速旋转的声⾳能让我⾃闭。。。毕竟⼀边跑本地开发环境,⼀边还可能同时跑1~2个⼯程的构建/部署脚本,再加上电脑运⾏的其他软件,这发热量你懂的!
所以,构建/部署这活不应该由我的电脑来承担,它太累了。
⽽且,我也不想⼿动触发部署脚本了,太累了,是时候让代码学会⾃⼰部署了。也就是这个时候,我对 CI/CD 就有了诉求。
由于我们的代码是托管在⾃建的 gitlab 服务器上,所以 CI/CD 这块我直接选择了⽤ gitlab ⾃带的 CI/CD 能⼒。⼯作之余,我差不多花了两天时间去熟悉gitlab CI/CD的⽂档[3]。
然后我按照⽂档先把环境搭建好,接着⼀遍遍地调试.l配置⽂件,我记得第⼀次成功跑完⼀个 Pipeline 前,我⼀共失败了⼤概11次,这个过程挺折磨⼈,有时候你就是不知道到底哪⾥配错了。
不过调通这个流程后,你就会觉得这整个试错的过程都是值得的。Nice!
CI/CD到底⼲了啥?
其实我前⾯也提到了,⼀个版本发布的过程,主要就是分为以下⼏个步骤:
代码合并:测试环境或⽣产环境都有独⽴的分⽀,等所有待发版的代码都合并到对应分⽀后,就可以考虑发版了。
打包:或者叫构建。以⽣产环境部署为例,我们切到⽣产环境分⽀并 pull 最新代码后,就可以开始打包步骤了。这⼀步主要是通过⼀些 bundler 完成的,⽐如 webpack。⽽打包命令嘛,⼀般都是定义在package.json的scripts中了,我这⼉定义的命令是build:prod,所以只要运⾏npm run build:prod就⾏了。
部署:把打包得到的⽂件放在 web 容器中,⽽ web 容器通常在 Linux 服务器上,这涉及到远程传输⽂件,这个时候我们⼀般要借助shell 脚本或者 xftp。
⽽ CI/CD 做的事情就是:⽤⾃动化技术接管流程。
监控Mutation
我的诉求是:当代码合并到某个分⽀后,gitlab能⾃动帮我执⾏完打包和部署这两个步骤。
所以,⾸先就必须有代码变动的监控能⼒。这个确实有,如果你有关注过git hook[4],就知道这是可以实现的。
⽽且,绝⼤部分代码托管平台都提供了 webhooks,能监控不少事件,⽐如 push 和 merge。
这也就是说,即便不使⽤代码托管平台提供的 CI/CD 能⼒,开发者也有能⼒实现⾃⼰的 CI/CD 机制。
ps:当然,除了 CI/CD,做短信/邮件通知也是可⾏的,只要你敢去尝试,基于平台开放的能⼒,我们能做很多事情。⾃研 CI/CD 的事情我们就不去搞了,⼈家造的轮⼦已经6翻了,直接拿来⽤。
回归主题,只要我监控到代码变动了,服务器端⾃动执⾏构建/部署脚本即可。
Gitlab CI/CD是怎么⼯作的
软件服务于⽣活,也源于⽣活。Gitlab CI/CD 设计了很多概念,其中我觉得最有意思的是:Pipeline 和 Runner。
Pipeline
Pipeline是CI/CD的最上层组件,它翻译过来是管道,其实你可以将之理解为流⽔线,每⼀个符合.l触发规则的 CI/CD 任务都会产⽣⼀个 Pipeline。这个概念就有点像⼯⼚中的车间流⽔线了,我们知道车间中有很多条流⽔线,不同的流⽔线可能会处理同⼀类型的⽣产任务,也可能处理不同类型的⽣产任务。当⼀条流⽔线空闲的时候,就有可能会被⽤来安排执⾏其他的⽣产任务。⽽ Gitlab 的 Pipeline 虽然没有空闲的概念,⼀个 Pipeline 执⾏结束后也不会被复⽤,但是会将资源让出来给其他的 Pipeline,所以和车间流⽔线也有异曲同⼯之妙。
Runner
有了流⽔线,还必须有⾟勤的⼯⼈进⾏⽣产作业,Runner在 Gitlab Pipeline 中就扮演着⼯⼈⾓⾊,根据我们下达的指令进⾏作业。
Runner的类型
在 Gitlab 中,Runner 有很多种,分为Shared Runner, Group Runner, Specific Runner。
Shared Runner 可以理解为机动⼈员,他可能会在⼯⼚的各个流⽔线机动作业,随时⽀援!在整个 Gitlab 应⽤中,Shared Runner 可以服务于各个 Project。