版本约束
软件的不同版本,或者插件的不同版本,在使用起来都有可能带来不可预知的影响,因此需要统一整理,固定下来,不允许轻易变更。
- 软件列表
JDK:
1.8.0_121Tomcat:
8Maven:
3.3.9Jenkins:
2.249.1
- 插件 一些插件非常重要,在某些版本中未必好用,比如用户权限控制插件,因此我们验证了当前好用的版本之后,赶忙打了镜像,以备扩容的时候可以用。
LDAP:
1.24Role-based Authorization Strategy:
2.10 注意此插件绝不可擅自升级Git:
4.4.1Git Parameter:
0.9.13
注意:
插件的安装或升级在运维团队需严格管控,管理员安装插件时,务必与组内同步,先做评估,经过测验,再行安装或升级。因为一些插件的升级可能带来未知问题与影响。
Jenkins 自身规范
0. 主机配置
配置:
在越大越好的前提下,最好不低于8C16G
,建议16C32G
,注意优化 Tomcat。存储:
建议100G 系统盘
+1T 数据盘
。盘的规格越高越好。最低SSD
,其中数据盘可酌情加,数据盘一定要用所能提供的最高规格的盘,这是保障 Jenkins 使用体验的最核心。其他:
其他没有什么注意点,按照一般主机配置初始化即可。
1. 如果扩容
Jenkins 新增 slave 不要单独从头部署,应从原 slave 主机打镜像,然后进行新增扩容,只需要调整 agent 脚本启动即可注册,agent 通过 docker 维护。
2. JENKINS_HOME
需要注意,我们的 JENKINS_HOME
统一在:
$ tail -n1 /etc/profile
export JENKINS_HOME=/data/.jenkins
严禁非此标准的情况。
3. WORKSPACE
需要注意,我们的 WORKSPACE
统一在:
$ grep workspaceDir /data/.jenkins/config.xml
<workspaceDir>${ITEM_ROOTDIR}/workspace</workspaceDir>
严禁非此标准的情况。
4. 编译缓存目录
因为根目录磁盘并不大,所以要修改缓存目录到数据目录中,缓存目录统一存放在/data/.cache
目录之下,下一层目录以语言栈为标识。
如下列举了常见语言编译过程中缓存目录的设置方法。
- maven
vim /usr/local/maven/conf/settings.xml # 修改 <localRepository>/data/.cache/java/.m2/repository</localRepository>
- node
$ npm config get cache
/root/.npm
# 设置 npm 全局包下载路径
$ npm config set prefix "/data/.cache/node/node_global"
# 设置 npm 缓存路径
$ npm config set cache "/data/.cache/node/node_cache"
- pip
$ cat ~/.config/pip/pip.conf
[global]
cache-dir = /data/.cache/pip
- go
export GOPATH=/data/go export GOCACHE=/data/.cache/go-build
5,备份方面
理论上,当我们整体面向 git 管理 Jenkins 源文件之后,其实已经不需要对 Jenkins 进行什么备份,即便是完全宕机无法恢复,也能够依赖 git 所存有的项目引导文件对项目进行恢复。
不过,我们还是最好使用 thinBackup
对一些基础元数据进行备份即可。
3. 其他约定
构建过程中,我们会依赖一些公共组件,或者共享库,这些内容都应该使用统一的目录或者仓库,从而便于统一维护与管理。在说具体约定之前,首先说一个前置约定,亦即所有第三方需要统一固定的目录,都应该在 /data/.jenkins/other
之下。
1. 版本目录
要实现回滚,有赖于本地版本目录,此目录我们统一约定在:
$ ls /jenkins_sync/version/
注意:
此处的 /jenkins_sync
目录是一个 nfs
(也可以直接使用云产品的 nfs) 挂载在各个 node 节点的目录,类似 version 之类的内容,都可以放到这个目录之内。
2. 剧本目录
剧本是项目在发布过程中使用的 ansible 剧本等内容。
关于命名,我们约定如下规范:
仓库:
https://gitlab.eryajf.net/jenkins/deploy-playbook.git命名规范:
ansible-{语言栈}
关于使用,我们约定如下规范:
- 我们约定一切剧本的更改都需要同步到 git 仓库中,可以创建一个自动推送 job,提交之后自动将此项目同步到 Jenkins 主机上来。
- 正式启用之后,严禁直接修改 master 提交,必须经过其他分支测试验证过之后才能合并到 master,因为这是核心,牵一发而动全身。
- 每次运行构建的时候,都会有一个 stage 单独拉取 playbook,剧本的暂存目录为:
${WORKSPACE}/ansible_tmp
3,共享库
共享库是为我们将项目模板提取出来之后提供的一种高效率方案。
关于命名,我们约定如下规范:
仓库:
https://gitlab.eryajf.net/jenkins/shared-library.git命名规范:
尽量做到见名知意。
关于使用,我们约定如下规范:
- 项目非特殊不得绕开共享库单独配置 pipeline,部分项目采用流水线外置到仓库中,并通过目录进行分类,这种策略虽然将 jenkinsfile 版本控制了,但是并不利于以后模板化的方向,因此不推荐过度使用这种方案。
- 正式启用之后,严禁直接修改 master 提交,必须经过其他分支测试验证过之后才能合并到 master,因为这是核心,牵一发而动全身。如有变更,最好做到二人 check。
- 现在已经将共享库的内容接入到了 ci 测试,普通的语法错误都能够检测出来,从而无法合并到主分支。
- 共享库作为抽象出来的构建模板,不能够毫无节制地添加模板,而应该更多去思考如何在原有共享库中做到更优的兼容,过多地模板对维护来说,将会是灾难。
- 共享库模板中的内容固然可以进行再次声明函数进行抽离,但是我并不建议这么做,过多地调用性抽离,会让一个模板的流程逻辑复杂化,反而会给后期的应用以及维护带来更多的认知成本,这是不划算的。
4. 风格约定
我们约定统一的 pipeline 风格为 声明式
,声明式是固定语法,接近原生 shell 的使用方式,对运维也更加友好。事实上这不仅仅是风格统一的问题,还可以做到研究成果共享。
5. 项目命名约定
项目命名规范,决定了 权限
配置的便利以及可用。项目命名原则上与 gitlab 仓库中项目命名保持一致,然后在前边添加环境作为区隔,如果项目名为 admin,那么不同环境的命名应该为:
- test-admin
- pre-admin
- prod-admin
如果一个项目仓库将会部署多个子项目,那么命名风格应该保持队形:
- test-admin-(a/b/c)
以此类推。
6. 授权方面
首先人员通过对接 openLDAP 进行同步,配置方式参考:openLDAP 集成# Zabbix,Jenkins,GitLab,JumpServer,Harbor,Nginx 实战 (opens new window)。
授权统一使用 openLDAP 用户分组绑定 Jenkins 权限角色的方式进行,不再通过个人绑定角色。
- 测试环境发布权限要求 w3 以上。
- 灰度环境发布权限要求 w4 以上。
- 正式环境发布权限要求 w5 以上。
一个业务通常分三组角色对应三个环境的应用,同理也要在 openLDAP 创建三个分组,将分组绑定到角色上,而后的维护则在运维平台上只需要将人员加入到分组即可。
4. Jenkinsfile 编写约定
1. 变量
准确简洁的变量名是可读性及易维护性的重要保障,针对脚本的变量名应当至少遵循以下规范:
- 自定义环境变量命名约定:
统一采用大写单词与下划线拼接方式命名,自定义变量必须要有备注。
- 变量名必须有实际含义, 避免英文下划线以外的特殊字符。
- 避免变量名过短 (数据或对象内容复杂而变量名无法推断其含义) 与过长(变量名接近甚至超过半行造成视觉阅读困难)。
- 变量名尽量避开 groovy 语言自身关键字,避开环境变量。
- 无特殊需求,避免修改流水线默认生成的环境变量。
- 不允许出现后置流程引用未落位的变量,否则容易造成不熟悉者的维护障碍。
- 在固定的拼接处理中, 应尽量减少定义语句使用的嵌套层数,例如:
// 套娃式定义
def pathA = "path1"
def paraZ = "${pathA}/path2"
def paraB = "${pathZ}/path3"
def paraY = "${pathB}/path4"
def paraX = "${pathY}/path5"
// 展开嵌套, 具有更好的可读性
def paraX = "path1/path2/path3/path4/path5"
2. stage 与标签命名
stage 定义了每个节点的任务内容,应该使用简洁清晰的文字对该阶段任务进行注释。名字不能超过 7 个字,失败原因也要做到简单清晰。
3. 镜像风格定义
命名规则如下:
server_name commit date build_id
harbor.eryajf.net/multienv/eryajf-back-admin:16c525_20201013114449_10
在实际使用中变量定义为:
BASE_IMAGE_NAME = "harbor.eryajf.net/multienv/${SERVICE_NAME}"
env.IMAGE_NAME = sh(script: "echo ${BASE_IMAGE_NAME}:" + "${COMMIT_ID}_" + "`date'+%Y%m%d%H%M%S'`" + "_${BUILD_ID}", returnStdout: true).trim() // 构建版本号
我们并不吝惜利用所有该当利用的变量来组建项目镜像的 tag,这是出于大量实际运维经验而言,我们希望在不影响构建稳定的情况,尽可能地提供出有利于我们快速定位的信息,这将在无数次后续问题排查中,一一受益!
注意:
最开始这里把分支也加入到了版本当中,但是后来的实践当中发现,分支可能会是 feature/gocover
,这种分支在镜像 tag 当中就会报错,所以废弃掉了!
5,维护以及使用
- 插件的安装或升级需严格管控,管理员安装插件时,务必与组内同步,先做评估,经过测验,再行安装或升级。因为一些插件的升级可能带来未知问题与影响。
- 流水线尽量使用一种语法,比如声明式,这样以后大家各自研究的成果可以直接共享,便于一起迭代前进。
- 尽可能将单个流水线的主逻辑放在更少的地方,这样对于后期的维护以及变更绝对是更加高效且省力的,不要一个流水线七零八落调用了五六个地方,非常不利于快速预览与定位。当然,一些非重要的公共逻辑,可以放在约定好的固定地方统一调用,比如通知脚本,回滚用的库,共享库等。
- 构建过程中所用工具,一定要版本统一,不给自己留坑,比如 mvn,go,node 等。
- 根据标准化的流水线,需要输出对研发的接入标准,让所有的研发项目 / 框架都来适配我们运维的标准,严禁接入非标的项目,除非走研发经理特批,运维需要严防这条线。
- 运维自己的流水线迭代更新也需要走正规的开发流程,调试环境和正式使用环境分开,代码合并的时候也需要互相 review。
- master 节点不允许运行任务,只作为调度节点。
- 不允许变更虚拟机的环境,不允许随意在虚拟机安装软件,如果构建过程有环境依赖,则必须将环境依赖注入到容器中,通过 docker 进行构建。
- 一起向追求四个现代化看齐,即规范化,标准化,高效化,优雅化。
镜像风格定义,这里提到的分支名问题。我们实践中分支名的需求比较大,所以还是希望保留它,我的处理方式是把 `/` 转成 `-` 或者 ‘_’ 。避免与开发规范分支名分隔符相同就好了,如 ‘feature/a_b_c’ 转成 ‘feature-a_b_c’,用于来还是可以接受。