一、引言
对于互联网(特别是研发、测试、运维)同行们来说,对CICD应该是不陌生的。本篇主要来讲一讲在k8s环境中,如何规划并搭建“每天上百次部署需求的CICD方案”。
二、环境背景
三、工具选型
CICD涉及到的功能较多,支撑不同功能的工具也很多(开源产品、商业产品、各云厂家提供的云效产品等,感兴趣的可自行查阅),我们根据实际情况,综合考虑后选型如下:
四、架构拓扑
基于以上选型的技术栈,组合起来的架构拓扑,如下:
为了支撑“高并发构建任务+合理利用资源”的场景,我们将ci job任务运行在k8s中的临时pod中(任务执行结束后,pod自动销毁)。尽可能利用k8s的碎片资源。
接下来我们就来看一看各环节的实施细节。
五、标准规范
面对几百个应用模块,没有相对统一的标准和规范,只会让后期的维护人员“不堪重负”。因此,我们(和研发团队)定制了一些标准规范,主要涉及到的内容如下:
1、应用名称
统一标准的应用名称,可以关联上cicd job名称、k8s svc名称、deployment名称、container名称、告警指标标签等,不需要额外维护映射关系。
2、环境与分支
所有应用不同的环境,绑定不同的分支,对应关系如下:
3、语言类型与编译规范
同类型的语言,统一标准的构建方式,ci过程中只需要判断语言类型就可以获取对应的编译指令(如果存在差异,只能另行打分支标签作为判断条件了)。
4、运行环境
为了减少CD的逻辑,我们采用统一的运行环境,包含:运行目录、启动方式(start.sh ${env})、端口、日志路径等。
这里主要是根据自己环境的需求来定制。
六、CI方案实施
接下来我们就按照CICD标准的任务流程来讲一讲,如何将定制的方案进行落地实施的。关于各组件的部署,相对来说比较简单,这里就不多说了(有问题也可以私信交流)。这里主要聚焦于整体功能链路的打通。整个链路流程大概可参考一下流程:
1、自动化构建部署
我们采用gitlab的webhook功能,将需要自动部署的项目/环境和对应的jenkins job进行关联,实现相关分支提交代码时,自动触发构建任务, 配置片段如下:
gitlab配置:
jenkins job配置:
2、jenkins动态创建slave节点
基于jenkins的k8s plugin和inbound功能实现: 执行job时,在k8s集群中自动创建临时slave 节点,job运行完成后,自动销毁,释放相关资源。 配置片段:
1)需要先创建k8s的rbac,配置jenkins到k8s中的认证,然后创建cloude :
2)配置pod template,按不同的语言类型规划,用于区分环境差异:
需要注意的是,为了提高编译效率,配置了缓存机制,需要配置pv/pvc到任务job的pod。
3、运行CI构建环境
基于jenkins的pipeline功能实现:根据不同的项目语言类型和环境,选择不同的代码分支和构建环境,配置片段如下:
node() {
stage('Set Agent') {
AGENT_LABEL = "${params.LANGUAGE}"
}
}
pipeline {
agent {
kubernetes {
inheritFrom "${AGENT_LABEL}"
}
}
stages {
stage("Initialization") {
steps {
script {
if (params.BUILD_ENV == 'prod') {
env.GIT_BRANCH = 'release'
} else {
env.GIT_BRANCH = "${params.BUILD_ENV}"
}
}
}
}
stage('Git Clone Code') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: "${env.GIT_BRANCH}"]],
extensions: [
[$class: 'RelativeTargetDirectory', relativeTargetDir: 'code-clone'],
[$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: true]],
userRemoteConfigs: [[credentialsId: "jenkins", url: "${GITLAB_URL}"]]])
}
}
stage('Compile Package') {
steps {
script {
def LANGUAGE = "${params.LANGUAGE}"
container("${LANGUAGE}") {
sh "bash -x codecompile.sh ${LANGUAGE}"
}
}
}
}
stage('Image Build And Publish') {
steps {
script {
container('docker') {
sh "bash -x imagebuild.sh ${LANGUAGE}"
}
}
}
}
}
4、质量检测与单元测试
质量检测和单元测试是软件开发中保证可用性的重要组成部分, 业界采用较多的工具是SonarQube (保证代码“做好事儿”)和 JUnit(保证代码“做对事儿”) ,二者定位不同但常协同工作,共同构建从 “单元测试” 到 “全面代码质量分析” 的保障体系。可通过jenkins相关插件来集成SonarQube 和 JUnit,再通过JaCoCo统计报告,实现“质量门禁”功能 ,效果参考:
在 CI/CD 流程中,二者的结合能有效降低代码缺陷率,提升软件可维护性和安全性。
5、代码编译和镜像构建
根据上文中的标准规范, 基于不同的语言参数变量,获取编译指令,如:
function JUDGE_COMPILE_CMD() {
if [[ ${LANGUAGE} == "java" ]];then
EXEC_COMPILE_CMD="mvn clean package -DskipTests"
elif [[ ${LANGUAGE} == "golang1-17" || ${LANGUAGE} == "golang1-18" ]] ;then
EXEC_COMPILE_CMD="build.sh"
elif [[ ${LANGUAGE} == "php" ]] ;then
EXEC_COMPILE_CMD="xxxxx.sh"
elif [[ ${LANGUAGE} == "nodejs" ]] ;then
EXEC_COMPILE_CMD="npm install && npm i nodeinstall -g && nodeinstall"
fi
}
镜像构建同理,根据语言,获取相应的基础镜像,关于dockerfile,支持自定义和动态生成。 示例片段参考:
function DOCKER_FILE() {
if [[ ! -f "deploy/Dockerfile" ]]; then
echo "FROM ${BASE_IMAGE}" > Dockerfile
echo "WORKDIR /home/services" >> Dockerfile
echo "ADD start_env/ /home/services/" >> Dockerfile
mkdir -p ./start_env/logs
if [[ ${LANGUAGE} == "java" ]]; then
cp -a code-clone/target/${SERVICE_NAME}.jar ./start_env/project/${SERVICE_NAME}.jar
elif [[ ${LANGUAGE} == "nodejs" ]]; then
cp -a code-clone/dist ./start_env/project
else
cp -a code-clone ./start_env/project
fi
else
cp code-clone/deploy/Dockerfile ./
fi
}
function IMAGE_BUILD() {
docker build -t "${IMAGE_NAME}" .
docker push ${IMAGE_NAME}
}
代码只用于展示方案效果,作为参考使用。
关于CI交付产物,还有一种方案,即: CI过程中不做镜像构建,以编译包为产物,上传至存储介质中,在发布时,通过脚本拉取CI产物运行。具体可根据实际环境选择。
七、CD方案实施
对于CD部分,Jenkins原生或者采用k8s plugin方式部署都不是很友好,因此我们采用云原生交付工具argocd。接下来我们看一看一些细节:
1、CICD打通
我们在jenkins中将CI和CD用了单独job做分离,且每个环境独立的CD job(可独立发布,多环境部署也不会堵塞),在pipeline中定义了:当CI构建成功时,自动触发CD,参考片段:
build job: "${SERVICE_ENV}", parameters: [
string(name: "IMAGE_VERSION", value: "${IMAGE_VERSION}")
]
2、部署
部署的逻辑很简单:修改相关应用配置中的版本,然后提交到过gitlab仓库,采用argocd兼听则明配置变更,当发生变更时,触发部署;为了提高部署效率,我们并没有用原生的监听/触发机制(有时间延迟?),而是在提交配置后,直接执行sync操作,从而触发部署。argocd和jenkins是完全独立的工具,为了增加用户体验感,我们通过脚本方式将jenkins和argocd串了起来,无需ci之后登陆argocd发布。 可通过jenkins job直接触发:
argocd app sync "$JOB_BASE_NAME" --grpc-web
3、部署状态与异常信息捕获
触发部署后,需要获取部署的进度、状态, 以及发布异常时,查看错误信息。错误信息通常包含k8s集群的编排故障和应用运行的error log。可以通过登陆argocd查看,但如果不想跨平台登陆,就需要想办法整合在jenkins中了。可以通过api接口或者cli的方式获取。参考效果:
4、结果同步到飞书
构建/发布的结果/状态可以实时推送到工作协同工具上,常用的钉钉、飞书、企业微信,都有相关插件。我们用的是飞书,默认插件只能实现群机器人方式(信息发送到群里),也可以采用应用机器人,通过接口实现消息只发送给个人,效果参考:
5、关于发布窗口和权限控制
为了保障线上稳定,让“变更”可控,我们针对版本迭代定制了发版日(如周二/周四?),同时:对项目发布权限做严格控制, 只有责任人和leader才可以发布。
jenkins在权限控制方面可以通过Role-based Authorization Strategy插件来管理,但发布窗口,并没有原生的比较好用的方式,当然也可以自己实现:在执行前加入相关判断逻辑。
我们因为有现成的运管平台,就将整个CICD的功能基于jenkins封装了一层,然后将发布窗口和权限管理的功能交由平台实现,CICD只承担集成交付功能。
CICD方案较多,可根据实际场景选型。希望本篇案例能给大家带来一些帮助。欢迎一起探讨!
作者丨ywjw996
来源丨公众号:运维经纬(ID:zyyw9999)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn
如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721