天天新消息丨devops-5:从0开始构建一条完成的CI CD流水线

来源:博客园

2023-04-20 18:42:51


(资料图)

从0开始构建一条完成的CI CD流水线前文中已经讲述了静态、动态增加agent节点,以动态的k8s cloud为例,下面就以Maven构建Java程序为例,开始构建出一条完整的CI CD流水线。实现功能目标: 1.分别可以根据分支和tag从源码仓库clone代码 2.拿到源码后开始编译 3.构建image,并push到镜像仓库 4.部署到对应k8s集群 5.部署成功后,钉钉告警以上是此pipeline实现的功能,后续计划: 1.通过webhooks实现源码仓库push代码后,自动出发pipeline运行 2.增加SonarQube代码质量检测步骤 3.配合argoCD实现自动CD后续文章会陆续更新,敬请期待。镜像准备以k8s cloud当做agent的话,肯定需要一个基础镜像,镜像中需要有git、java和maven这些必要的工具环境,当然,可以使用jenkins提供的tools功能来配置工具导入到环境中,例如:
tools {        maven "apache-maven-3.8.6"     }
这种方式是比较方便的,但是每到一个新宿主机都要去主动下载一遍,还需要在jenkins中配置下载地址和方式,迁移时也比较麻烦,所以这里就采用一劳永逸的方法,把这些环境都提前打包到agent的镜像中,方便以后使用。这里选用的基础镜像是jenkins官方的agent镜像:
docker pull jenkins/agent:latest
这个官方镜像内已经包含git、java环境,以及后边需要和jenkins master建立连接的agent.jar包,所以我们只需再将maven包打包进去即可。maven工具包的准备官方下载maven包:
wget https://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.tar.gz
maven的配置(可选)很多java程序会用maven来进行构建,maven中又存在很多依赖组件(常用的是jar包、war包、pom等,也可把Zip包等通过POM文件定义为依赖组件),这个时候就会有一个仓库的概念,这个仓库分为三种类型,即:central:中央仓库,是由Maven社区提供的资源仓库,它包含了大量的常用程序库组件(jar包)。默认Maven的中央仓库地址为:http://repo1.maven.org/maven2/local:本地仓库,是存放maven环境本地的一个文件夹,此文件夹在第一次运行Maven命令时就创建了。Maven在执行构建任务时,根据依赖关系从中心仓库、或远程仓库下载依赖组件到本地仓库,然后本地仓库的内容供项目引用。remote:远程仓库,例如项目需要指定外部其他公司、或开源组织的jar包,这些依赖组件通用性等原因,未纳入Maven中央仓库,这个时候就要手动指定一个私有的远程仓库来拉取依赖。修改Maven中央仓库地址一般Maven的中央仓库由于网络问题会访问不到,这个时候可以修改地址为国内的Maven仓库地址或者公司私有的仓库地址,例如阿里的:http://maven.aliyun.com/nexus/content/groups/public修改步骤是要修改apache-maven-3.8.6/conf/settings.xml文件中以下字段:修改为:
      nexus-aliyun      central      Nexus aliyun      http://maven.aliyun.com/nexus/content/groups/public    
若修改后未生效,可以检查代码pom.xml中是否指定了仓库地址,类似语句:
            springsource-repos        SpringSource Repository        http://repo.spring.io/release/    
修改Maven本地仓库路径Maven本地仓库路径默认为 ${user.home}/.m2/repository可以直接在此进行修改,也可以在构建时用参数指定:
mvn clean install -Dmaven.repo.local=/home/maven/local_repo/
也可以在构建时指定配置文件地址:
mvn clean install -s /home/maven/settings.xml
开始构建镜像准备好的物料包及Dockerfile:
[root@node01 agent-jenkins]# lsapache-maven-3.8.6.tar.gz  Dockerfile  jenkins-agent kubectl.tar.gz
这里要说下jenkins-agent这个脚本文件,这个脚本文件也是官方提供的,源码文件在这里:https://github.com/jenkinsci/docker-inbound-agent,这是专门用来agent连接jenkins master的,采用的jnlp的方式。查看Dockerfile内容
[root@node01 agent-jenkins]# cat Dockerfile FROM jenkins/agent:latestUSER rootADD apache-maven-3.8.6.tar.gz /opt/ADD kubectl.tar.gz /usr/local/bin/ENV PATH $PATH:/opt/apache-maven-3.8.6/bin/COPY jenkins-agent /usr/local/bin/CMD ["/bin/sh","-c","/usr/local/bin/jenkins-agent"]
用于CD环节的工具,这里添加了kubectl命令,可根据需要添加。构建镜像
# docker build -t registry.example.com:5000/jenkins/agent:v1 .# docker push registry.example.com:5000/jenkins/agent:v1
registry.example.com:5000 是我的私有仓库配置k8s cloud的pod Template前边镜像准备完毕,下边要准备一个pod yaml模板,来运行每次临时加入和运行job的agent,默认情况下,k8s cloud会有一个名称为jnlp的容器专门来和jenkins master连接,然后我们可以再启动一个容器专门来跑Pipeline的job,但这里有一点要注意,如果pod中有多个容器,我们需要在Pipeline中指定某个在哪个容器中运行,这个具体怎么指定后边再说,我们这里采用覆盖截jnlp容器的方式来实现全部的工作都由一个container来完成,最终pod Template如下:
apiVersion: "v1"kind: "Pod"metadata:  name: jenkins-agent  namespace: "default"spec:  containers:  - env:    - name: "MAVEN_HOME"      value: "/opt/apache-maven-3.8.6/"    image: "registry.example.com:5000/jenkins/agent:v1"    imagePullPolicy: "IfNotPresent"    name: "jnlp"    resources:      limits:        memory: "2G"        cpu: "1500m"      requests:        memory: "1G"        cpu: "100m"    volumeMounts:    - mountPath: "/root/.m2"      name: "m2"      readOnly: false    - mountPath: "/home/jenkins/agent"      name: "workspace-volume"      readOnly: false    - mountPath: "/usr/bin/docker"      name: "docker-client"      readOnly: true    - mountPath: "/var/run/docker.sock"      name: "docker-engine"      readOnly: true  volumes:  - hostPath:      path: "/root/.m2"      type: "DirectoryOrCreate"    name: "m2"  - hostPath:      path: "/home/jenkins"    name: "workspace-volume"  - hostPath:      path: "/usr/bin/docker"      type: File    name: "docker-client"  - hostPath:      path: "/var/run/docker.sock"      type: Socket    name: "docker-engine"
这里有四个volume:m2:这个是用作maven的本地仓库路径,使用hostpath挂载到了本地目录,当然也可以存储到某些共享存储中,目的就是让依赖包只下载一次。workspace-volume:这个是将jenkins的工作目录也使用hostpath挂载。docker-client:docker命令的挂载,用于build、push等命令docker-engine:docker engine的挂载,用于build、push等gitlab项目克隆simple-java-maven-app项目地址:https://github.com/jenkins-docs/simple-java-maven-app,将此项目克隆到本地gitlab即可。网访问github慢的话,可以git我的码云:https://gitee.com/vfancloud/simple-java-maven-app.gitPipeline编写创建凭证1.代码仓库我们使用前边搭建的gitlab,需要提前将gitlab的用户凭证在Jenkins创建好,方便后边Jenkins下载代码使用:系统管理—>凭证管理—>创建Username with password类型凭证(id需要记住,Pipeline中会使用)2.我们的服务是部署在k8s集群中,所以还需要目标k8s的kubeconfig凭证,用来管理操控目标k8s:系统管理—>凭证管理—>创建Secret file类型凭证一般项目都会有多个环境,所以每个环境的kubeconfig凭证都要提前创建好。3.镜像仓库的账号密码也要提前准备好,Username with password类型即可。安装插件一些常用的必须插件,要提前安装:GitGit ParameterDingTalkbuild user vars pluginPipeline此Pipeline起一个示例效果,有些功能点可以省略或者选择使用,酌情增删即可:
pipeline {    agent {      kubernetes {        cloud "kubernetes-internal"  //指定cloud name        inheritFrom "jenkins-agent"  //指定podTemplate,新版本已经不再用label指定        namespace "default"      }    }    environment {        GIT_CERT = credentials("vfan-gitlab")  //gitlab用户凭证        HARBOR_HOST = "registry.example.com:5000"        SERVER_NAME = "simple-java-maven-app"    }/*    tools {        maven "apache-maven-3.8.6" 镜像有maven环境了,可以不指定    } */       options {        buildDiscarder(logRotator(numToKeepStr: "10"))  //保持历史构建的最大个数        timeout(20)  //默认单位分钟,20分钟        timestamps()  //Pipeline开始时间以及每个step执行开始时间    }    parameters {        choice(            name: "GIT_REPO_URL",            choices: "http://10.85.122.128:880/vfan/simple-java-maven-app.git",            description: "Git Repo example environment"        )        choice(            name: "GIT_TYPE",            choices: ["branch", "tag"],            description: "Git Repo example brance"        )        choice(            name: "GIT_REPO_BRANCE",            choices: ["master", "dev", "test"],            description: "Git Repo example brance"        )        gitParameter name: "GIT_TAG",            type: "PT_TAG",            branch: "master",            branchFilter: ".*",            defaultValue: "",            selectedValue: "TOP",            sortMode: "DESCENDING_SMART",            listSize: "1",                description: "Select you git tag."        choice(            name: "ENVIRONMENT",             choices: ["INT", "DEV", "PROD"],             description: "Select deployment environment"        )    }    stages {        stage("git clone branch") {            when {                 expression { params.GIT_TYPE == "branch" }            }            steps {                git(                    branch: params.GIT_REPO_BRANCE,                     credentialsId: env.GIT_CERT,                     url: params.GIT_REPO_URL                )            }            post {                success {                    sh """                    echo "use branch build"                    git status                    """                }            }        }        stage("git clone tag") {            when {                 expression { params.GIT_TYPE == "tag" }            }            steps {              checkout([$class: "GitSCM",               branches: [[name: "${GIT_TAG}"]],               userRemoteConfigs: [[credentialsId: env.GIT_CERT, url: params.GIT_REPO_URL]]])            }            post {                success {                    sh """                    echo "use tag build"                    git status                    """                }            }        }        stage("Maven Build") {            steps {                sh "mvn -B -DskipTests clean package"            }        }        stage("Test") {            steps {                sh "mvn test"            }            post {                always {                    junit "target/surefire-reports/*.xml"                }            }        }        stage("Deliver") {            steps {                sh "./jenkins/scripts/deliver.sh"            }        }        stage("Docker build && push") {            steps {                withCredentials([usernamePassword(credentialsId: "harbor-auth", passwordVariable: "HARBOR_PASSWD", usernameVariable: "HARBOR_USER")]) {                    sh """                        echo "Other operations..."                        echo "Start building..."                        date -d "+8 hour" +%Y%m%d_%H%M%S > /tmp/date                        BUILD_TIME=`cat /tmp/date`                        docker build --build-arg APP_NAME=simple-java-maven-app -t ${HARBOR_HOST}/${SERVER_NAME}:${GIT_REPO_BRANCE}_${BUILD_TIME} .                        echo "Build complete."                        docker login $HARBOR_HOST -u $HARBOR_USER -p $HARBOR_PASSWD                        docker push ${HARBOR_HOST}/${SERVER_NAME}:${GIT_REPO_BRANCE}_${BUILD_TIME}                        docker rmi ${HARBOR_HOST}/${SERVER_NAME}:${GIT_REPO_BRANCE}_${BUILD_TIME}                    """                }            }        }        stage("Deploy to k8s"){            input{                message "Should we continue deploy?"                ok "Yes, we should."            }            environment {                // 提前创建好secret file类型的凭据                KUBE_CONFIG_INT = credentials("mycluster_int")                // KUBE_CONFIG_DEV = credentials("mycluster_dev")                // KUBE_CONFIG_PROD = credentials("mycluster_prod")            }            steps{                sh"""                    BUILD_TIME=`cat /tmp/date`                    case $ENVIRONMENT in                        "INT")                            kubectl set image deployment ${SERVER_NAME} --kubeconfig=${KUBE_CONFIG_INT} app=${HARBOR_HOST}/${SERVER_NAME}:${GIT_REPO_BRANCE}_${BUILD_TIME}                            kubectl rollout status deployment ${SERVER_NAME} --kubeconfig=${KUBE_CONFIG_INT}                        ;;                        "DEV")                            kubectl set image deployment ${SERVER_NAME} --kubeconfig=${KUBE_CONFIG_DEV} app=${HARBOR_HOST}/${SERVER_NAME}:${GIT_REPO_BRANCE}_${BUILD_TIME}                            kubectl rollout status deployment ${SERVER_NAME} --kubeconfig=${KUBE_CONFIG_DEV}                        ;;                    esac                    echo "Deployment complete."                """            }        }    }    post {         success{             echo "Deployment succeeded."            dingtalk (                robot: "myapp-dingding-robot",                type: "MARKDOWN",  // 发什么类型的消息,有TEXT、LINK、MARKDOWN、和ACTION_CARD,参考https://jenkinsci.github.io/dingtalk-plugin/guide/pipeline.html                at: [],                atAll: false,                title: "Jenkins发版成功",                text: [                    "## 构建结果:**${currentBuild.result}**",                    "---",                    "## 构建信息",                    "---",                    "- 项目名称:${SERVER_NAME}",                    "- 构建环境:${ENVIRONMENT}",                    "- 构建分支:${GIT_REPO_BRANCE}",                    "- 构建标签:${GIT_TAG}",                    "- 项目地址:${GIT_REPO_URL}",                    "- 构建用户:${env.BUILD_USER}"                    ],            //    messageUrl: "",            //    picUrl: "",            //    singleTitle: "",            //    btns: [],            //    btnLayout: "",             //    hideAvatar: false            )        }        failure{            echo "Deployment failed."            dingtalk (                robot: "myapp-dingding-robot",                type: "MARKDOWN",  // 发什么类型的消息,有TEXT、LINK、MARKDOWN、和ACTION_CARD,参考https://jenkinsci.github.io/dingtalk-plugin/guide/pipeline.html                at: [],                atAll: false,                title: "Jenkins发版失败",                text: [                    "## 构建结果:**${currentBuild.result}**",                    "---",                    "## 构建信息",                    "---",                    "- 项目名称:${SERVER_NAME}",                    "- 构建环境:${ENVIRONMENT}",                    "- 构建分支:${GIT_REPO_BRANCE}",                    "- 构建标签:${GIT_TAG}",                    "- 项目地址:${GIT_REPO_URL}",                    "- 构建用户:${env.BUILD_USER}"                    ],            //    messageUrl: "",            //    picUrl: "",            //    singleTitle: "",            //    btns: [],            //    btnLayout: "",             //    hideAvatar: false            )        }    }}
测试运行Pipeline运行完成,钉钉也已收到通知,后续更新更多内容。

关键词:

尚品宅配、索菲亚竞推低价套餐 回击欧派还是跟风炒作?

中国定制家居上市公司三大龙头有“欧索尚”之称,它们的举动往往能够影[详细]
2023-09-09

Gartner最新报告:2024年全球电动汽车总出货量有望突破1800万辆【附新能源乘用车市场分析】

图片来源:摄图网近日,据市场调查机构Gartner近日发布的最新报告,预[详细]
2023-09-09

示范区召开城镇公共管网供水价格调整听证会

9月8日,示范区召开城镇公共管网供水价格调整听证会。会议充分听取消费[详细]
2023-09-09

玩家扮演保罗迷路进入神秘森林 探索森隐世界的秘密

近日,一款名为《恶神》的心灵恐怖冒险游戏在PS5和PC平台上推出,支持[详细]
2023-09-09

俄罗斯外交部召见亚美尼亚驻俄大使

每经AI快讯,当地时间9月8日,俄罗斯外交部发布消息称,在近期亚美尼亚[详细]
2023-09-09

《王者荣耀》KPL 夏季赛总决赛 9 月 10 日开打:重庆狼队对战广州 TTG

快科技9月8日消息,《王者荣耀》2023KPL夏季赛总决赛将于本周日(9月10[详细]
2023-09-09

如果眼睛近视了怎么办(眼睛近视了怎么办)

开始好好保护 1 看书或者电脑电视不要时间过长,45-60分休息10-15分钟[详细]
2023-09-08

来了解下:超声炮做一次能维持多久?超声炮多久可以做第二次?

超声炮做一次能维持多久?超声炮多久可以做第二次?做一次超声炮就能够[详细]
2023-09-08

【石榴花开 籽籽同心】吉林延边:奏响乡村振兴新乐章,绘就民族团结“同心圆”

央视网消息(记者王静远隋佳桐邢明):初秋的延边,草木葱茏,天高云淡[详细]
2023-09-08

2023至2024可可托海国际滑雪度假区价格表

2023至2023年可可托海国际滑雪度假区价格表可可托海国际滑雪度假区位于[详细]
2023-09-08
版权所有: 大河制冷网 All Rights Reserved
京ICP备2022022245号-75
联系邮箱:435 226 40@qq.com