jenkins 实现应用产品归档

前言

        项目上需要适配一套麒麟环境 arm 架构环境的产品,经过适配之后需要部署到项目的生产环境提供服务,在适配完毕之后需要对此次适配进行一次归档,即通过 jenkins 来将适配完毕之后环境进行一次构建归档,归档内容:将产品所用到的镜像归档至指定的 harbor 仓库,容器所用到的 yaml 文件、应用构建好的 jar 包,以及应用所用到的初始化 sql 数据等配置统一打包并将 tar 包归档至公司内部的 ftp 服务器当中,项目上在部署的时候可以通过 ftp 服务器对外提供的页面将部署所需要的部署包下载到本地进行部署。


一、首先通过脚本的方式创建一套 jenkins 归档 job

1、jenkinsCli 拉取模板 job

        通过 jenkins 的 cli 命令将模板 job 拉到本地(使用别名设置 jenkins 的 cli 命令),并创建存放修改 git 地址和 job 名称的 txt 文件;

mkdir /root/jenkinsjob
cd /root/jenkinsjob
touch gitadd.txt
touch proname.txt
jcli get-job xxx归档/cityos-quota > demo.xml

 2、填写拉取代码的 git 地址 & proname

        将需要创建的 job 的 git 地址和 jobname 分别填写不通的文件当中;

 

3、编写 shell 脚本

        通过编写脚本来将不通的 job 自动化创建至 jenkins,在脚本当中通过:变量传参、for 循环、sed、jenkinsCli 等命令来实现自动化创建 Jenkins Job;

vim tranfer.sh
sh tranfer.sh

4、查看 job 任务,创建成功;

二、编写 Pipeline 流水线脚本

代码如下(示例):

        以下则是此次归档的 jenkins 整个流水线,通过该流水线套用所有通过脚本来创建的 jenkins job,每一个 jenkins job 对应一个后端应用,其中主要是容器化部署,通过将应用的代码从 git 仓库拉取到 jenkins 服务器,并使用 maven 命令将拉取下来的代码打包并生成 jar 包,通过编写 DockerFile 的方式将 jar 包录入至镜像当中并推送至 harbor 仓库,通过 CMD 模块来执行启动 jar 包(应用)的容器,其次是将应用所需要的 sql 数据、apollo 的配置信息、生成的 jar 包等数据封装成 tar 包,使用 ansible 来将打成的 tar 包推送至 ftp 服务器。

def packagePath=""


pipeline{
    // 通过 agent 模块来指定 job 运行在哪个 jenkins 节点上
    agent { node {label 'master'}}
    stages {
        stage('CleanWs'){
            steps {
                cleanWs()
            }
        }
        // 通过 checkout 模块将代码拉取至集成服务器
        stage('Preparation') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: "$tag"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'sz-1289378471-cacdjklajfdsahjg', url: "$gitaddress"]]])
            }
        }
        // 使用 maven 命令将拉取的代码进行编译打包,生成 jar 包
        stage('Build') {
            steps {
                sh script:  '''
                    mvn clean package -Dmaven.test.skip=true
                '''
            }
        }

        // 通过设置变量的方式来获取 jar 包所在的路径,变量指定命令为 find 查找
        stage('GetBinaryFile'){
            steps {
                script {
                    def temp = sh returnStdout: true ,script: '''find $WORKSPACE -name ${javaname}'''
                    packagePath=temp.trim()
                    echo packagePath
                    if(packagePath == ''){
                        echo '未找到程序包,请检查javaname配置是否正确?'
                        error('Binaryfile Not Found!')
                    } else {
                        def getjarrname = sh returnStdout: true ,script: """ls ${packagePath} | awk -F '/' '{print \$NF}'"""
                        jarrname=getjarrname.trim()
                    }
                }
            }
        }
        // 此步骤将 dockerfile 模板拷贝至 jenkins 工作目录,并构建 docker 镜像到本地
        stage('DockerImageBuild'){
            steps {
                script {
                        sh """
                        short_commitid=`git rev-parse --short HEAD`
                        mkdir -p /test/k8s/bak/${proname}
                        test -e /test/k8s/tmp/${proname} && mv /test/k8s/tmp/${proname} /test/k8s/bak/${proname}/`date +%Y%m%d%H%M%S`
                        mkdir -p /test/k8s/tmp/${proname}/files/
                        cp $packagePath /test/k8s/tmp/${proname}/
                        cat /test/k8s/dockerfile/Dockerfile-arm-www
                        cp /test/k8s/dockerfile/Dockerfile-arm-www /test/k8s/tmp/${proname}/Dockerfile
                        if [ -d "$WORKSPACE/${proname}/docker/files/" ];then
                            cp -r $WORKSPACE/${proname}/docker/files/ /test/k8s/tmp/${proname}/
                        elif [ -d "$WORKSPACE/docker/files/" ];then
                            cp -r $WORKSPACE/docker/files/ /test/k8s/tmp/${proname}/
                        fi
                        cd /test/k8s/tmp/${proname}
                        sed -i 's/PRONAME/${proname}/g' Dockerfile
                        sed -i 's/PACKAGEPATH/${jarrname}/g' Dockerfile
                        test DOCKER_CLI_EXPERIMENTAL=enabled
                        docker buildx build --platform=linux/arm64 -t ${harbor}/${product}/${proname}:`date +%Y%m%d%H%M%S`_\$short_commitid . --load
                        """
                }
            }
        }

        // 此步骤通过变量的方式来获取镜像的 tag 为了推送镜像至 harbor
        stage('DockerImageUrl'){
            steps {
                script {
                    def getimagetag = sh returnStdout: true ,script: """docker images |grep  ${harbor}/${product}/${proname} |awk '{print \$2}' |head -n 1"""
                    imagetag = getimagetag.trim()
                }
                echo """获取 url 路径为${imagetag}"""
            }
        }


        // 此步骤通过引用上面的变量来将镜像推送至 harbor 仓库
        stage('DockerImagePath & Push'){
            steps{
                sh script:  """
                echo ${harbor}/${product}/${proname}:${imagetag}
                """
            }
        }


        // 此步骤通过 k8s 环境的 config 文件来获取 k8s 配置文件所在环境的资源 yaml
        stage('CreateK8sYaml'){
            steps {
                script {
                    sh """
                    if [ ! -d "$WORKSPACE/${proname}/" ];then
                        cd $WORKSPACE/
                    else
                        cd $WORKSPACE/${proname}/
                    fi
                    kubectl --kubeconfig /test/k8s/k8sconfig/${k8s_configfile} get deployments.apps -n ${k8s_namespace} ${proname} -o yaml > deploy.yaml
                    kubectl --kubeconfig /test/k8s/k8sconfig/${k8s_configfile} get service -n ${k8s_namespace} ${proname}-service -o yaml > service.yaml
                    echo --- > resources/k8s.yaml
                    yq eval 'del(.status,.metadata.annotations,.metadata.managedFields,.metadata.creationTimestamp,.metadata.generation,.metadata.namespace,.metadata.resourceVersion,.metadata.selfLink,.metadata.uid,.spec.template.metadata.annotations,.spec.template.metadata.creationTimestamp)' deploy.yaml >> resources/k8s.yaml
                    echo --- >> resources/k8s.yaml
                    yq eval 'del(.status,.metadata.annotations,.metadata.managedFields,.metadata.creationTimestamp,.metadata.namespace,.metadata.resourceVersion,.metadata.selfLink,.metadata.uid,.spec.clusterIP)' service.yaml >> resources/k8s.yaml
                    echo --- >> resources/k8s.yaml
                    """
                    try {
                        sh "cd $WORKSPACE/;kubectl --kubeconfig /test/k8s/k8sconfig/${k8s_configfile} get ingress -n ${k8s_namespace} ${proname}-ingress -o yaml > ingress.yaml"
                        sh "cd $WORKSPACE/;yq eval 'del(.status,.metadata.annotations.field*,.metadata.annotations.kubectl*,.metadata.namespace,.metadata.managedFields,.metadata.creationTimestamp,.metadata.generation,.metadata.resourceVersion,.metadata.selfLink,.metadata.uid)' ingress.yaml >> resources/k8s.yaml"
                    }
                    catch(ex){
                        echo "Stage is always successful"
                        echo ex.getMessage()
                    }
                }
            }
        }
        

        stage('GetYamlPath') {
        //查询当前Yaml文件的实际路径
            steps {
               script {
                    def YamlPath = sh returnStdout: true ,script: """find $WORKSPACE/${proname} -name k8s.yaml"""
                    YamlUrl = YamlPath.trim()
                }
                echo """获取 yamlurl 路径为${YamlUrl}"""
            }
        
        }


        // 此步骤是将 k8s 原来的镜像地址替换成 job 刚生成的镜像地址
        stage('ReplaceMirror'){
            steps {
                script {
                    def imagesrc = sh returnStdout: true ,script: """cat ${YamlUrl} |grep harbor.testss.cn/www |grep ${proname} |awk '{print \$2}'"""
                    imageSrc = imagesrc.trim()
                }
                echo """${imageSrc}"""
                sh """
                   eval sed -i 's#${imageSrc}#${harbor}/${product}/${proname}:${imagetag}#g' ${YamlUrl}
                """
                echo """镜像替换成功"""
            }
        }
        
    
        // 此步骤因为环境问题需要注释 yaml 文件中的一些内容
        stage('modify'){
            steps {
                sh """
                    sed -i '68,70s/^/#/g' ${YamlUrl}
                    sed -i '77,91s/^/#/g' ${YamlUrl}
                    sed -i '96,98s/^/#/g' ${YamlUrl}
                    sed -i 's#-javaagent:/test/server/skywalking/agent/skywalking-agent.jar# #g' ${YamlUrl}

                    docker rmi -f ${harbor}/${product}/${proname}:${imagetag}
                """
            }
        }

        // 此步骤是将打包好的 job 资源封装成 tar 包
        stage('CopyResource'){
            steps {
                sh """
                    mkdir -p /test/k8s/release/${proname}/resources/
                    if [ ! -d "$WORKSPACE/${proname}/" ];then
                        cp -r $WORKSPACE/resources/* /test/k8s/release/${proname}/resources/
                    else
                        cp -r $WORKSPACE/${proname}/resources/* /test/k8s/release/${proname}/resources/
                    fi
                    release=\$(echo ${tag} |awk -F '/' '{print \$NF}')
                    cd /test/k8s/release/
                    mv ${proname} ${proname}-TAG-\${release}
                    tar -czf ${proname}-TAG-\${release}.tar.gz ${proname}-TAG-\${release}
                """
            }
        }

        // 此步骤则是调用 ansible 来将
        stage('Storage'){
            steps{
                sh script:  """
                    release=\$(echo ${tag} |awk -F '/' '{print \$NF}')
                    packagefile="/test/k8s/release/\${proname}-TAG-\${release}.tar.gz"
                    ansible-playbook /test/playbooks/release-java-backend.yml -e "version=$product_version srcfile=\$packagefile product=$product proname=$proname" -i /test/playbooks/hosts
                    ansible-playbook /test/playbooks/release-java-backend-jar.yml -e "version=$product_version srcfile=$packagePath product=$product proname=$proname" -i /test/playbooks/hosts
                """
            }
        }

        // 此步骤为回收 tar 包,为了防止 jenkins 服务器资源不足
        stage('Rmrelease'){
            steps {
                sh """
                    release=\$(echo ${tag} |awk -F '/' '{print \$NF}')
                    datetime=`date +%Y%m%d%H%M%S`
                    mkdir -p /test/k8s/bak/${proname}/\${datetime}/
                    mv -f /test/k8s/release/${proname}-TAG-\${release}.tar.gz /test/k8s/bak/${proname}/\${datetime}/
                    rm -rf /test/k8s/release/${proname}-TAG-\${release}
                """
            }
        }

    }
}

三、编写 ansible-playbook 剧本

        通过编写 ansible 来实现 jenkins 服务器与 ftp 服务器之间的 package 传输,将 jenkins 本地生成的归档包传输至目标服务器;

---
 - hosts: 1.x.x.x  # 指定 jenkins 的任务跑在哪个目标服务器上,此处也可以填写 hosts 文件
   remote_user: root        # 执行该剧本任务使用的用户角色
   vars:                    # 定义变量传参
     - product: test
     - dest:  /data/package/{{product}}
     - proname: test
   tasks:                   # 定制任务
     - name: create timestamp      # 该步骤是同步时间
       command: date +%F-%H-%M
       register: playtime

     - name: create dir day        # 创建归档目录
       file: path={{dest}}/{{version}}/ state=directory

     - name: get package name      # 查看目标服务器的归档包是否存在,并将结果赋值给 package 这个变量
       shell:  ls {{dest}}/{{version}} |grep {{proname}}-TAG 
       register: package
       ignore_errors: true

     - name: backup packages       # 判断上个任务的指令是否为空,如果为空则执行备份归档包的指令
       shell: mv {{dest}}/{{version}}/{{package.stdout}} /data/backup/{{package.stdout}}-{{playtime.stdout}}
       when: package.stdout != ""

     - name: push package to remote jenkins&ansible hosts    # 将归档包推送至归档服务器
       copy: src={{srcfile}} dest={{dest}}/{{version}}/

四、运行 Jenkins Job 实现 CICD 集成发布 


总结

本次博客是记录在环境当中产品应用的归档流程,通过 jenkins 实现整个 cicd 的持续集成&交付,更加方便快捷的实现应用产品的归档,减缓项目上部署的压力,当项目需要部署产品时,可以通过 ftp 服务器来快速下载归档的 package。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值