如何用jenkins優雅的CI/CD

之前一直用gitlabCI/CD,去年換了個坑,新廠這邊如今是用jenkinsCI/CD,之前聽過jenkins,但一直沒有用過,雖然建議過用gitlabCI/CD,但因爲各類歷史包袱,你們仍是堅持用jenkins,既然用這個,我固然不能對這玩意徹底不瞭解啊,開始各類蒐羅資料,瞭解jenkins相關的知識。前端

流水線

構建一個自由風格的軟件項目(free style project)

公司的流水線主要用的是這種,這個流水線有幾個問題:node

1. 對構建機有必定的依賴,好比你要執行`npm install`或者`go mod vendor`,構建機必需要安裝了`node`和`go`環境,若是對版本有要求就更尷尬了
2. 若是同一個項目,我想指定分支來構建,這裏不太好處理
複製代碼

流水線(workflow job)

這個流水線挺好的,能夠動態設置pipeline script內容,用來作DevOps開發CI/CD挺好的,根據用戶設置,用接口動態修改pipeline script的內容,不過也有一些小問題:git

1. 若是同一個項目,我想指定分支來構建,這裏不太好處理
複製代碼

多分支流水線(workflow multi branch project)

多分支流水線這個如今是我如今主要用的,若是你的項目有多個分支,每一個分支下都有jenkinsfile,構建的時候會根據你選的分支來構建,這樣用來構建、發佈不一樣的分支就很方便golang

image.png

pipeline script

流水線的問題解決了,如今的問題是怎麼構建的問題了,基於之前用.gitlab-ci.yml的經驗,這裏我仍是打算用docker來構建,有幾處好處:docker

1. 對構建機的依賴比較小,只要有docker就能夠了
2. 不用擔憂構建環境被破壞問題,添加新構建機也很方便
複製代碼

這裏要先安裝jenkins插件docker pipeline,而且在構建機安裝dockerjenkinspipeline script的方法,能夠看官方文檔,裏面寫的很清楚了,如下是目前的jenkinsfilenpm

前端 jenkinsfile

pipeline {
    environment {
        // 改這幾個就能夠了
        PROJECT = 'example-front'
        DEPLOYMENT_NAME = 'example-front-deployment'
        // 改這幾個就能夠了

        DOCKERHUB = 'xxxx.com'
        BUILD_TIME = sh(script:"date '+%Y%m%d%H%M'", returnStdout: true).trim()
        // LABEL = 'nodelabel' // 若是要指定構建機,請設置構建機 label
        IMAGE_TAG = "${DOCKERHUB}/${PROJECT}:${BRANCH_NAME}-${BUILD_TIME}-${BUILD_ID}"
        // 我這用了apollo 配置服務,配置服務地址寫到 jenkins的憑據裏了
        APOLLO_QA = credentials('APOLLO_QA')
        APOLLO_PROD = credentials('APOLLO_PROD')
    }
    agent any

    options {
        retry(3)
    }

    stages {
        stage('init') {
            steps {
                script {
                    // 初始化環境變量,這樣後面就不用寫 when 指令了
                    if (env.BRANCH_NAME == 'test') {
                        env.APOLLO = APOLLO_QA
                        env.NAMESPACE = 'qa-ns'
                        env.K8SCONFIG = '/root/.kube/qa-ns.kubeconfig'
                   }else if (env.BRANCH_NAME == 'master') {
                        env.APOLLO = APOLLO_PROD
                        env.NAMESPACE = 'prod-ns'
                        env.K8SCONFIG = '/root/.kube/prod-ns.kubeconfig'
                    }
                }
            }
        }
        stage('build-dist') {
            agent {
                docker {
                    image 'node:14.17.0'
                    // label "${LABEL}" // 若是要指定構建機,請設置構建機 label
                }
            }
            steps {
                sh 'npm install --registry=https://registry.npm.taobao.org/ && npm run build'
                // 把構建的製品存起來,把 fingerprint 設置爲 true,後期若是有問題,
                // 能夠經過 Check File Fingerprint 快速定位到構建制品的流水線
                archiveArtifacts artifacts: 'dist/', fingerprint: true
            }
        }
        stage('build-image') {
            agent {
                docker {
                    image 'docker:19.03.12'                    
                    args '-v /var/run/docker.sock:/var/run/docker.sock -v /root/.docker:/root/.docker'
                    // label "${LABEL}" // 若是要指定構建機,請設置構建機 label
                }
            }
            steps {                
                sh "docker build ."
                // registry已經在構建機登陸了,經過 -v /root/.docker:/root/.docker 把登陸信息共享過來了,
                // 這裏沒有辦法,我沒有 registry 的帳號信息,
                // 建議你們把 registry 的信息寫到 jenkins 的憑據裏面,經過 credentials 來讀取,
                // 而後用 docker login 登陸
                sh "docker push ${IMAGE_TAG}"
            }
        }
        stage('deploy') {
            agent {
                docker {
                    image 'roffe/kubectl:v1.13.2'
                    // 這裏是共享k8s信息,緣由同上,沒有帳號,建議同上,用憑據來管理
                    args '-v /root/.kube:/root/.kube'
                    // label "${LABEL}" // 若是要指定構建機,請設置構建機 label
                }
            }
            steps {
                sh "kubectl --kubeconfig ${K8SCONFIG} -n ${NAMESPACE} set image deployment/${DEPLOYMENT_NAME} ${PROJECT}=${IMAGE_TAG}"
            }
        }
    }
}

複製代碼

後端(go) jenkinsfile

pipeline {
    environment {
        PROJECT = 'example-front'
        DEPLOYMENT_NAME = 'example-front-deployment'
        DOCKERHUB = 'xxxx.com'
        BUILD_TIME = sh(script:"date '+%Y%m%d%H%M'", returnStdout: true).trim()
        IMAGE_TAG = "${DOCKERHUB}/${PROJECT}:${BRANCH_NAME}-${BUILD_TIME}-${BUILD_ID}"
        APOLLO_QA = credentials('APOLLO_QA')
        APOLLO_PROD = credentials('APOLLO_PROD')
    }
    agent any
    options {
        retry(3)
    }
    stages {
        stage('init') {
            steps {
                script {
                    if (env.BRANCH_NAME == 'test') {
                        env.APOLLO = APOLLO_QA
                        env.NAMESPACE = 'qa-ns'
                        env.K8SCONFIG = '/root/.kube/qa-ns.kubeconfig'
                   }else if (env.BRANCH_NAME == 'master') {
                        env.APOLLO = APOLLO_PROD
                        env.NAMESPACE = 'prod-ns'
                        env.K8SCONFIG = '/root/.kube/prod-ns.kubeconfig'
                    }
                }
            }
        }
        stage('build-main') {
            agent {
                docker {
                    image 'golang:1.14-alpine'
                }
            }
            steps {
                sh 'go mod vendor'
                sh 'go build -o main main.go'
                archiveArtifacts artifacts: 'main', fingerprint: true
            }
        }
        stage('build-image') {
            agent {
                docker {
                    image 'docker:19.03.12'                    
                    args '-v /var/run/docker.sock:/var/run/docker.sock -v /root/.docker:/root/.docker'
                }
            }
            steps {                
                sh "docker build ."
                sh "docker push ${IMAGE_TAG}"
            }
        }
        stage('deploy') {
            agent {
                docker {
                    image 'roffe/kubectl:v1.13.2'
                }
            }
            steps {
                sh "kubectl --kubeconfig ${K8SCONFIG} -n ${NAMESPACE} set image deployment/${DEPLOYMENT_NAME} ${PROJECT}=${IMAGE_TAG}"
            }
        }
    }
}

複製代碼

如今咱們點一下構建,就會依次執行這些job,最終更新k8s的容器後端

有些構建任務,若是想並行執行的話,能夠了解下 parallel 指令markdown

相關文章
相關標籤/搜索