责任编辑如是说一类如前所述码云的稳步软件系统组织工作流或其与此同时实现,听众能透过写作责任编辑,从 0 与此同时实现两个制造级以 Gitee 库房为核心理念的云原生植物稳步软件系统组织工作流。在日常生活合作开发操作过程中,一般来说会有数个销售业务或模块与此同时合作开发,那些在 git 组织工作操作过程中将都以组成部分的方式共存在两个标识符库房中,当合作开发顺利完成后向正式发布组成部分递交 PR 以分拆到正式发布组成部分顺利完成正式发布,此时他们的试验组织工作就落在了对那些 PR 的布署和试验上,对于数个 PR 的布署他们有什么方便、快捷、智能化的布署计划呢?只好责任编辑就以合作开发自然环境中常 PR 与此同时布署和试验为例,与此同时实现两个包涵了 Gitee 源代码库房、 Jenkins 、 Harbor 、 Kubernetes 、 Helm 等模块,能与此同时实现从标识符代销到正式发布上架的全套稳步软件系统销售业务流程。透过智能化处置构筑操作过程,很大的精简了日常生活插值组织工作的维数。
组织工作流洼瓣
构架模块表明
码云:国内最小标识符代销网络平台、亚洲地区第三大标识符代销网络平台、亚洲地区最大英文标识符代销网络平台。提供标识符代销、标识符产品质量分析、资金管理、标识符模拟等服务项目平台虚拟化公有云服务项目
Gitee Jenkins Plugin :码云如前所述 GitLab Jenkins Plugin 合作开发的 Jenkins 插件。用作实用性 Jenkins 异步,拒绝接受码云网络平台推送的 WebHook 促发 Jenkins 进行智能化稳步软件系统或稳步布署,并可将构筑状况意见反馈回码云网络平台。
Harbor :由 VMware 开放源代码的 Docker 快照库房,作为专有快照库房最合适的优先选择。
Jenkins :如前所述 Java 合作开发的一类稳步软件系统辅助工具
Kubernetes :是用作手动布署、扩充和管理罐子化( containerized )插件的开放源代码系统。
Helm :Kubernetes 的包命令行
组织工作销售业务流程表明
1. 编码并推送到码云合作开发者编码顺利完成后,将标识符推送到码云,透过促发由资金管理员预设的 Webhook 规则促发 Jenkins 作业。这里他们使用码云的 Gitee-Jenkins-Plugin插件顺利完成 Jenkins 端和码云端的实用性。Jenkins 安装及实用性操作过程见 Jenkins with Gitee-Jenkins-Plugin ,有需要的听众能前往该文档进行参考。2. 在 Jenkins 中根据项目中编写的 Jenkinsfile 执行完整的构筑和正式发布销售业务流程。Jenkinsfile是两个文责任编辑件,其中包涵 Jenkins Pipeline 的定义,一般来说和源代码一起管理。Jenkins Pipeline 是对销售业务流程的手动表达,用作将软件从版本控制一直传递到用户和客户。合作开发操作过程中对软件所做的每项更改(在源标识符管理中进行的)都需要经过复杂的操作过程才能正式发布。此操作过程涉及以可靠且可重复的方式构筑软件,以及透过数个试验和布署阶段来逐步升级已构筑的软件(在 Jenkins 中称为两个build )。Jenkins Pipeline 提供了一组可扩充的辅助工具集,用作透过管道特定于域的语言(DSL)语法以标识符的方式( pipelines “as code” )对简单到复杂的交付管道进行建模。下面的销售业务流程图是在 Jenkins Pipeline 中轻松建模的一类 CD 计划的示例(图片来自官方文档):pipeline {
agent any
stages {
stage(build images and assets) {
when {
not{
anyOf {
environment name:giteePullRequestState, value: closed environment name: giteePullRequestState, value: merged}
}
}
failFast true
parallel {
stage(add start comment to GiteePR) {
steps {
addGiteeMRComment comment: “+ CI triggered, building… [BUILD](“ + env.RUN_DISPLAY_URL + “)”}
}
stage(build frontend assets) {
steps {
shset -u
if [[ $(echo $giteePullRequestDescription|grep without_compare|wc -l) -gt 0 ]]; then
echo “skip compare”
rm -rf $JENKINS_HOME/nfs/$giteePullRequestIid/public
mkdir -p $JENKINS_HOME/nfs/$giteePullRequestIid/public
cp -r $JENKINS_HOME/nfs/global/public/assets $JENKINS_HOME/nfs/$giteePullRequestIid/public/
cp -r $JENKINS_HOME/nfs/global/public/webpacks $JENKINS_HOME/nfs/$giteePullRequestIid/public/
else
rm -rf $JENKINS_HOME/atompi_workspace/assets-builder/atompi
rm -rf $JENKINS_HOME/atompi_workspace/assets-builder/out
cp -r $WORKSPACE $JENKINS_HOME/atompi_workspace/assets-builder/atompi
cd $JENKINS_HOME/atompi_workspace/assets-builder && DOCKER_BUILDKIT=1 $JENKINS_HOME/bin/docker -H tcp://docker:2375 build -o out .
rm -rf $JENKINS_HOME/nfs/$giteePullRequestIid/public
mkdir -p $JENKINS_HOME/nfs/$giteePullRequestIid/public
cp -r $JENKINS_HOME/atompi_workspace/assets-builder/out/* $JENKINS_HOME/nfs/$giteePullRequestIid/public/
fi
}
}
stage(build frontend images) {
steps {
shset -u
rm -rf $JENKINS_HOME/atompi_workspace/frontend/atompi
cp -r $WORKSPACE $JENKINS_HOME/atompi_workspace/frontend/atompi
cd $JENKINS_HOME/atompi_workspace/frontend && $JENKINS_HOME/bin/docker -H tcp://docker:2375 build -t hub.atompi.cc/atompi_ci/frontend:v3.0.0-$giteePullRequestIid .
$JENKINS_HOME/bin/docker -H tcp://docker:2375 push hub.atompi.cc/atompi_ci/frontend:v3.0.0-$giteePullRequestIid
$JENKINS_HOME/bin/docker -H tcp://docker:2375 rmi hub.atompi.cc/atompi_ci/frontend:v3.0.0-$giteePullRequestIid
}
}
stage(build backend image) {
steps {
shset -u
rm -rf $JENKINS_HOME/atompi_workspace/backend/atompi
cp -r $WORKSPACE $JENKINS_HOME/atompi_workspace/backend/atompi
cd $JENKINS_HOME/atompi_workspace/backend && $JENKINS_HOME/bin/docker -H tcp://docker:2375 build -t hub.atompi.cc/atompi_ci/backend:v3.0.0-$giteePullRequestIid .
$JENKINS_HOME/bin/docker -H tcp://docker:2375 push hub.atompi.cc/atompi_ci/backend:v3.0.0-$giteePullRequestIid
$JENKINS_HOME/bin/docker -H tcp://docker:2375 rmi hub.atompi.cc/atompi_ci/backend:v3.0.0-$giteePullRequestIid
}
}
}
}
stage(deploy) {
when {
not{
anyOf {
environment name: giteePullRequestState, value:closed environment name: giteePullRequestState, value: merged}
}
}
steps {
shset -u
cd $JENKINS_HOME/atompi_workspace/CI-atompi-helm && sed “s/CPRID/${giteePullRequestIid}/g” values.yaml.template > values.yaml
cp $WORKSPACE/config/atompi.yml.cm ./charts/backend/templates/configmap-atompi-yml.yaml
cp $WORKSPACE/config/environments/production.rb.cm ./charts/backend/templates/configmap-production-rb.yaml
$JENKINS_HOME/bin/helm uninstall -n ci-atompi-$giteePullRequestIid ci-atompi-$giteePullRequestIid || echo “release does not exists”
sleep 10 && $JENKINS_HOME/bin/kubectl delete ns ci-atompi-$giteePullRequestIid || echo “ns ci-atompi-$giteePullRequestIid does not exists”
sleep 5 && $JENKINS_HOME/bin/kubectl create ns ci-atompi-$giteePullRequestIid || echo “namespace already exists”
cd $JENKINS_HOME/atompi_workspace/CI-atompi-helm && $JENKINS_HOME/bin/helm upgrade -i -n ci-atompi-$giteePullRequestIid ci-atompi-$giteePullRequestIid ./
addGiteeMRComment comment:.atompi.cc)
访问该 url 前 需要本地 dns 设置为192.168.1.1
}
}
stage(delete) {
when {
anyOf {
environment name:giteePullRequestState, value: closed environment name: giteePullRequestState, value: merged}
}
steps {
shset -u
echo $giteePullRequestState
$JENKINS_HOME/bin/helm uninstall -n ci-atompi-$giteePullRequestIid ci-atompi-$giteePullRequestIid
sleep 30
$JENKINS_HOME/bin/kubectl delete ns ci-atompi-$giteePullRequestIid
rm -rf $JENKINS_HOME/nfs/$giteePullRequestIid
curl -X DELETE -H Accept: text/plain “http://hub.atompi.cc/api/repositories/atompi_ci/backend/tags/v3.0.0-$giteePullRequestIid”
curl -X DELETE -H Accept: text/plain “http://hub.atompi.cc/api/repositories/atompi_ci/frontend/tags/v3.0.0-$giteePullRequestIid”
addGiteeMRComment comment: “+ CI Closed”}
}
}
post {
failure {
addGiteeMRComment comment: “+ CI build failure! [BUILD](“ + env.RUN_DISPLAY_URL + “)”}
aborted {
addGiteeMRComment comment: “+ CI build aborted! [BUILD](“ + env.RUN_DISPLAY_URL + “)”}
}
}
pipeline {…} :在声明式的 Pipeline 语法中, pipeline块定义了整个管道中顺利完成的所有组织工作。
agent any:用作表明在任何可用 agent 上执行此管道或其任何阶段。agent 即 Jenkins 集群中的构筑节点,他们能给那些节点指定标签,让某些有特定需求的构筑操作过程在那些带有特定标签的节点上运行。
stages {…} : 包涵管道的构筑步骤集
stage(build images and assets) {…}:定义其中某个构筑操作过程(括弧中的内容为该 stage 的标题,用作展示在 Jenkins web 界面及日志中),其中when 块定义了执行本 stage 的判断条件,如果为真则执行,否则跳过; steps 块定义了本 stage 真正执行的操作步骤,如 sh表示在 agent 上执行 shell 脚本、addGiteeMRComment 表示透过 Gitee-Jenkins-Plugin 插件调用码云接口,向当前构筑的 PR 推送评论信息。
failFast true 及 parallel {…} :他们能看到某些 stage 块中有 parallel {…} 块,与此同时 parallel {…} 块中又包涵数个 stage 块,这样的语法的意思是在 parallel {…}块中的stage 为并行执行的,不在 parallel {…} 中的 stage 是按照从上到下的顺序串行执行,只有在上两个 stage 成功执行完后才会进入下两个 stage ,而 parallel {…}块中定义的stage 会并行执行, parallel {…} 块前一行的 failFast true 表示当 parallel {…} 块中的某个 stage 出现错误时,整个 parallel {…}块都退出,并结束parallel {…} 块中的所有 stage 不管那些 stage 是否执行顺利完成,与此同时标记上层 stage 为错误退出。
post {…} :定义所有 stages 执行顺利完成后的后续操作,不一定需要 post块,当他们需要根据stages 结束状况来判断是否执行后续操作时,他们能定义两个 post 块,如本案例中定义了当 stages 结果为 failure (失败)或 aborted(拒绝,一般来说时人为的,比如 web 界面上手动停止本次构筑)时将构筑信息推送到码云的 PR 评论中。
本案例的 pipeline 语法到这里就如是说完了,更详细的语法规则可查看官方在线文档。接下来他们对整个构筑销售业务流程再进行一遍梳理,详细的表明一下本案例中的 pipeline 干了什么,与此同时也是对责任编辑如是说的组织工作流做两个全面的讲解。当 Jenkins 收到码云推送的 Webhook 请求并匹配到促发规则时,他们实用性的 Job 进入 build状态。
build 开始之后, Jenkins 透过 git 插件从码云拉取指定标识符到 workspace 与此同时读取标识符库房中的 JenkinsfileJenkinsfile后, Jenkins 开始按照Jenkinsfile 中声明的销售业务流程开始执行构筑
进入 stages 中的第两个 stage : build images and assets, 该 stage 执行如下操作:
判断当前 PR 的状况,不为 closed 或 merged 则执行后面的操作(对于已经关闭或分拆的 PR 他们不需要再执行构筑);
如果判断结果为真,则执行加下来的并行执行的 stage ;
并行执行:向码云当前 PR 页面推送构筑开始的评论信息,并附带本次构筑的链接,方便 PR 负责人实时查看构筑操作过程;
并行执行:从 PR 描述( Gitee-Jenkins-Plugin 插件能透过自然环境变量 $giteePullRequestDescription获取 PR 的描述文本 ) 判断是否需要编译前端静态资源,再执行后面的编译前端静态资源并推送到前端静态资源共享的 nfs server 、构筑前端 docker 快照并推送到 harbor 、构筑后端 docker 快照并推送到 harbor ,那些构筑都是透过sh 定义两个 shell 脚本,在 agent 上执行这个脚本顺利完成构筑;
并行操作执行顺利完成后开始执行布署的 stage :
透过 agent 上的 Helm 客户端安装他们预先编写好的 helm chart ,这里他们能使用 shell 脚本修改values.yaml.template等实用性模板文件,与此同时,对于不通的 PR 布署在同两个 Kubernetes 集群中,他们以 PRID 命名 namespace ,区分每个 PR 为单独两个 namespace ,从而与此同时实现对于不同的 PR 都有独立的实用性文件和运行自然环境。
布署顺利完成后,他们将 PRID 和他们的 Ingress 域名进行拼接,并以 markdown 的格式将当前构筑结果和 Kubernetes 集群中服务项目的入口链接推送到当前 PR 的评论中;
对于已经分拆或者关闭的 PR ,在码云上操作 PR 状况修改为“关闭”或者“分拆”时,再次促发 Jenkins 流水线,与此同时进入delete 的 stage ,该 stage的组织工作就是将已布署的服务项目从 Kubernetes 集群中删除并将不再使用的 docker 快照和静态资源从 harbor 和 nfs server 中删除,顺利完成整个 CI 系统的清理组织工作,让系统能稳步运行而不需要认为干预。
至此他们在 Jenkinsfile中定义的流水线就走完了。3. 正式发布顺利完成后用户即可透过 Kubernetes 暴露的 Ingress 请求入口访问他们正式发布的服务项目。正式发布顺利完成后,他们透过在 Kubernetes 设置集群内服务项目的访问方式来让用户能够访问到布署在 Kubernetes 集群中的服务项目。他们能实用性 LB 、端口转发或 DNS 实用性等,以访问群集中的插件。这里他们透过在 Kubernetes 集群中安装 Ngins Ingress controller 与此同时实用性 Ingress 规则来让用户访问集群内服务项目。Ingress 是两个 API 对象,它定义了允许外部访问群集中服务项目的规则。更多关于 Ingress 的表明见官方文档Ingress 的安装及实用性见文档:Kubernetes(https://gitee.com/atompi/Prod-K8S-HA-Installer)构架模块布署
Harbor(https://blog.atompi.com/2020/08/03/%E7%A6%BB%E7%BA%BF%E5%AE%89%E8%A3%85%20Harbor%20v2/)
Kubernetes(https://gitee.com/atompi/Prod-K8S-HA-Installer)
Helm :Helm 从 v3 版本开始不再需要安装 tiller,对于热衷于使用 Helm 来管理 Kubernetes 应用安装包的用户来说,无疑是史诗级升级。从 v3 版本开始,他们只需要下载 Helm 二进制文件即可直接与 Kubernetes 集群交互,前提是~/.kube/ 目录下存在有权限的 config文件,这对已经安装过 Kubernetes 集群的人来说并不是个问题。Helm 二进制文件下载地址(https://github.com/helm/helm/releases)。
Jenkins with Gitee-Jenkins-Plugin(https://blog.atompi.com/2020/08/03/Docker%20Compose%20%E5%AE%89%E8%A3%85%20Jenkins/)
扩充
责任编辑案例中的 Jenkinsfile 所定义的流水线适用作合作开发自然环境多 PR 与此同时布署试验的使用场景。他们只需要对其中部分 stage稍作修改,同样能应用与制造自然环境的构筑与布署。比如,对于 release 自然环境他们只需要构筑和正式发布 release 组成部分即可,因此不存在 PR 的概念,也不需要执行 PR 评论的操作。他们能将 PR 状况的判断、addGiteeMRComment去除;将 agent 只向制造自然环境的构筑节点;将 docker 快照推送至制造自然环境的 harbor 库房;使用制造自然环境的 helm chart 将 docker 快照正式发布到制造自然环境的 Kubernetes 集群。对于不是布署到 Kubernetes 集群的应用,他们能将构筑 docker 快照的操作替换为构筑制品(如:二进制文件、 jar/war 包等)的操作,与此同时将docker push操作替换为将制品推送到制品库(如:nexus 等)的操作,此时,正式发布将不再使用 helm 辅助工具,而是使用他们自定义的正式发布销售业务流程辅助工具(如:shell 脚本、 ansible-playbook 等)。总之,责任编辑的案例是对如前所述码云的云原生植物稳步软件系统组织工作流的两个实现参考,他们能在这个组织工作流的基础上创造更多的最佳实践。当然,责任编辑与此同时实现的案例也存在的些许不足,比如对于多 PR 的布署,总是存在这样的场景:有数个 PR 与此同时更新了标识符,此时候的构筑队列是串行的,在有限的节点资源下,他们的构筑组织工作会出现饱和并且需要排队的现象,这样对于两个需要编译前端静态资源的 PR 来说需要等待的时间会非常漫长,从而导致队列中的其他构筑一同等待,降低了构筑效率。在此,责任编辑给出一类解决计划,与此同时也是作者再在使用的一类计划:将编译静态资源的组织工作下放到每两个布署中。Jenkins 构筑组织工作是瞬时的,而布署在 Kubernetes 集群中的应用是长时间运行的,由此他们一般来说会为 Kubernetes 集群分配更多的节点而尽量减少 Jenkins 集群的节点,当然他们能将 Jenkins 布署在 Kubernetes 集群中,共享 Kubernetes 集群资源,但他们为了管理方便,一般来说将这两个角色分离开来。因此,对于合作开发自然环境的多 PR 布署试验的使用场景,他们能将组织工作量小的、统一的组织工作交给 Jenkins 执行,而需要大量资源的编译组织工作下放到每两个布署中,即 Kubernetes 集群中,使用 Kubernetes 集群资源来并向的编译数个 PR 的静态资源,这样每两个 Jenkins 作业只需要很短的时间顺利完成实用性和布署的组织工作即可结束本次构筑,立刻开始下两个构筑,这样排队时长就大幅度缩减了。为了与此同时实现这种计划,他们需要提前构筑最新的基础标识符库房组成部分(如某一批 PR 是如前所述某个组成部分的最新递交开始的,他们能把 release 组成部分或者 master 组成部分作为基础组成部分,这些组成部分一般来说是最近正式发布到制造自然环境的组成部分)的 docker 快照作为基础快照,与此同时编译一次静态资源作为基础,后续的 PR 编译静态资源时能如前所述这个基础进行增量构筑,这样能进一步减少编译时间。他们在每天正式发布制造自然环境时间促发 release 构筑的作业,执行一次基础自然环境构筑,并推送到合作开发自然环境的 harbor 及 nfs server ,在此基础上后续的 PR 在正式发布时不再单独构筑 docker 快照,在 helm chart 中他们把 docker image tag 修改为固定的 release 版本。在 PR 布署顺利完成后,他们增加了两个 modify 的 stage ,这个 stage 的组织工作就是透过 kubectl辅助工具在指定的 PR 布署中执行特点的编译脚本,在布署顺利完成( helm chart 正式发布顺利完成并切所有 pod 均成功启动 )时在指定的 pod 中执行编译组织工作,这样就与此同时实现了使用 Kubernetes 集群中的资源顺利完成组织工作量较大的编译组织工作。与此同时实现的 pipeline 如下:stage(modify) {
when {
not {
anyOf {
environment name: giteePullRequestState, value: closed
environment name: giteePullRequestState, value: merged
}
}
}
steps {
sh #!/bin/bash
set-u
if [[ $(echo $giteePullRequestDescription|grep without_compile|wc -l) -gt 0]]; then
min=1 max=10 while [ $min -le $max]
do if [[ “Running” == $($JENKINS_HOME/bin/kubectl getpo -n ci-atompi-$giteePullRequestIid | grep ciatompibe|awk {print $3}) ]]; then
BACKEND_POD_NAME=$($JENKINS_HOME/bin/kubectl getpo -n ci-atompi-$giteePullRequestIid | grep ciatompibe|awk {print $1})
MIRACLE_POD_NAME=$($JENKINS_HOME/bin/kubectlget po -n ci-atompi-$giteePullRequestIid | grep ciatompife|awk {print $1})
$JENKINS_HOME/bin/kubectl exec -n ci-atompi-$giteePullRequestIid $BACKEND_POD_NAMEbash /nohup_pull.sh $giteePullRequestIid
$JENKINS_HOME/bin/kubectl exec -n ci-atompi-$giteePullRequestIid $MIRACLE_POD_NAMEbash /nohup_pull.sh $giteePullRequestIid
break else min=`expr $min + 1`
sleep 60fi
done
else min=1 max=10 while [ $min -le $max]
do if [[ “Running” == $($JENKINS_HOME/bin/kubectl getpo -n ci-atompi-$giteePullRequestIid | grep ciatompibe|awk {print $3}) ]]; then
BACKEND_POD_NAME=$($JENKINS_HOME/bin/kubectl getpo -n ci-atompi-$giteePullRequestIid | grep ciatompibe|awk {print $1})
MIRACLE_POD_NAME=$($JENKINS_HOME/bin/kubectl getpo -n ci-atompi-$giteePullRequestIid | grep ciatompife|awk {print $1})
$JENKINS_HOME/bin/kubectl exec -n ci-atompi-$giteePullRequestIid $BACKEND_POD_NAMEbash /nohup_compile.sh $giteePullRequestIid
$JENKINS_HOME/bin/kubectl exec -n ci-atompi-$giteePullRequestIid $MIRACLE_POD_NAMEbash /nohup_pull.sh $giteePullRequestIid
break else min=`expr $min + 1`
sleep 60fi
done
fi
addGiteeMRComment comment:
<summary>CI Opened布署已顺利完成,正在启动服务项目。[点我试验](http:// + env.giteePullRequestIid + .atompi.cc)访问该 url 前 需要本地 dns 设置为192.168.1.1}
当然,对于制造自然环境而言,我们是不能容忍编译组织工作占用制造自然环境资源的,所幸的是,在制造自然环境中他们不存在多 PR 与此同时布署的场景,因此就不需要将编译组织工作下放,他们正常的执行前面如是说的正式发布销售业务流程即可。作者:atompi原文:https://my.oschina.net/atompi/blog/4466071往期精彩回顾
树莓派系统推出64位版本
Flutter 2.10正式发布,稳定支持Windows
计算机史上最疯狂一幕
觉得不错,请点个在看呀