目标
本文面向开发工程师,旨在开发过程中掌握程序如何通过gitlab来完成自动化流水线,而非gitlab CI/CD的详细说明文档。
提问
在开发过程中什么时候,我们需要关注到程序的编译-打包?
答:一开始就应该关注,之前在极客时间中学到的一个名词:“以始为终”,在做事之前,先考虑结果,根据结果来确定要做的事情。 那么对于开发来说,在编码起始我们需要关注代码是如何编译-打包的。而在编码过程中,也需要保持每日提交,每日构建—— 持续集成。
这一点的好处:大大的降低上线前代码阅读,合并,解决冲突的问题。
理解
在没有gitlab CI/CD时,该如何进行编译-打包-升级?大致步骤如下:
- 编写makefile文件;
- 登录编译打包服务器;
- 执行make build操作,生成软件包;
- 上传到制品库中;
- 登录生产环境;
- 从制品库下载软件包;
- 执行make install/update 操作;触发程序停止-拷贝-重启操作;
在云原生环境下,后续的4-7更换成:
- 使用软件包,生成docker镜像;
- 上传docker镜像至 镜像仓库;
- 登录生产环境/使用升级平台;
- 触发容器进项更新;
那么以上操作,可以抽象成:编写指令阶段->执行阶段->上传制品->触发升级。即:程序存在哪几个阶段,在哪里分步骤执行,输出制品包,上传到对应仓库,等待升级触发。
对应gitlab CI/CD的步骤:
- 打开CI/CD配置;
- 配置可执行runner;
- 编写.gitlab-ci.yaml;
- 推送代码后,触发stage执行;
- 每个stage对应不同的job,输出特定的结果;
- 结束。
前三个操作完成后,后面即可每次推送触发构建,大大提升研发效能。接下来分批细化操作;
开启CI/CD
打开CI/CD需要拥有当前项目的主程序员及以上权限。 >
当打开gitlab对应的项目后,也许存在左边导航栏中没有CI/CD小火箭的标识。那么点击Settings->General->Visibility, project features, permissions 右边的Expand->Repository->CI/CD 勾选上。
配置可执行Runner
Runner 是运行来自GitLab 的 CI/CD 作业的代理。既然是代理,需要让gitlab认识,并且有权限执行。那么就要完成注册Runner的操作。
注册Runner操作:操作参考:gitlab.cn/docs/runner…
其中在注册完成后,需要在程序中启用对应的Runner程序:
打开项目地址->settings->CI/CD->Runner Expand->Assigned project runners->选择对应的Runner。
在如果存在多个Runner,在gitlab-ci.yaml中通过使用Tag来启用不同的Runner。
Runner上应该包含当前项目编译所需要的所有依赖环境。所以在当前云原生环境下,通过build 打包镜像的dockerfile来维护。
Runner在k8s的pod中使用打包镜像来完成。
编写.gitlab-ci.yaml
.gitlab-ci.yaml需要放在**仓库根目录下。**而最简单的.gitlab-ci.yaml ,只需要两件事:
- stages:控制整个CI/CD流水线分成哪几个阶段;
- jobs:控制每个阶段下所执行的job;
若干个job对应一个stages;而每个job在默认情况下为并行的方式执行;
如何控制同一个stage下不同job的顺序?
- 通过needs关键字:上一个job执行完成后启动。
- 通过when: manual关键字;
- 使用dependencies关键字:与needs不同,其表明依赖上一个job的输出结果为依赖;
- 使用rules : 通过条件语句来判断执行。
在job执行过程中,需要使用某些环境变量该如何处理?
比较常使用的方式如下:
- 使用预定义变量;
与ansible相似,在流水线执行中,存在预定义变量可以执行使用,如:CI_PROJECT_NAME(gitlab项目名)。
- 在.gitlab-ci.yaml中使用variables 关键字定义。
其他的参考:gitlab.cn/docs/jh/ci/…
对于大仓模式,该如何编写.gitlab-ci.yaml?
在根目录下的.gitlab-ci.yaml中,通过include关键字,导入子应用的.gitlab-ci.yaml; 在子.gitlab-ci.yaml中定义每个阶段执行的job即可。
触发stage执行
在默认情况下,推送代码则会触发流水线的执行,但是往往在开发过程中,存在需要自己指定流水线执行的时间;那么就需要使用** workflow:rules 来执行整个流水线创建/运行时机**。
另外如果需要控制job的运行实际,可以通过job: rules来控制。
完成以上的操作,就可以愉快的进行每日持续构建的工作了!
调试的经验项
之前也有说,在云原生下,往往通过在K8S中通过POD的方式来执行持续集成操作,第一个卡点:准备编译镜像;
编译镜像:包含所有程序的编译环境,在大厂中往往由工程技术部来维护。 作为开发,往往急需特定环境的情况下,更喜欢自己动手。
所以我在开发过程中,往往会自己编写相关的编译镜像的dockerfile保存在自己主机/程序的deps目录下。在执行相关操作时,有以下几个经验:
- 提高编译速度;
在编译镜像中,尽可能的保存程序所有需要下载的依赖,不能将依赖放到本程序的Dockerfile中。
- 调试dockerfile;
在自己编写dockerfile时,往往不可能一次完全编写对,所以每一次的执行失败,可以通过 docker run -it --rm 进入到最后一次成功的地方,手动执行下一步操作。 能在本地环境的验证的操作,尽可能在本地完成验收再上传。
- 完全能预知输出制品结果
在云原生场景下,往往需要进一步的进行docker build操作,将制品包打成docker镜像,所以在上一步输出制品时,需要对制品的目录了如指掌。 一般情况下,建议在流水线中输出当前制品的目录层级。
- 控制运行镜像大小
运行镜像往往都只包含程序的二进制文件以及相关依赖的动态库,如golang大部分情况下 运行镜像只有20~40M。 这样的好处在于升级分发过程中,k8s拉取镜像的数据,降低升级的不可用窗口期。
总结
作为程序员来说,不仅仅是要磨练自己的编码水平,还需要扩展自己的知识面。要对自己的程序如何输出,又在什么环境下执行了如指掌。 (之前面试某个大厂,问了一嘴面试官是否知道基础设施,竟然直接把我拉黑了)
突然意识到,后续总结一下如何高效在云原生环境下开发的技巧性文章,敬请期待。