[TOC]
0x00 知识扩展 1.Jenkins 如何根据代仓库的 webhook 自动触发拉取提交的分支代码并构建? 描述: 通常每个项目代码库都会有不同的分支,(如果你没有用多分支流水线的情况下)对于普通的流水线项目我们可以让一条流水线来支持多个分支的发布,其实有时候你会发现每个分支的集成步骤都是差不多的,对于常规的我们可以安装使用git parameter
插件,其次还需配置参数化构建过程。
方式1.手动动态拉取指定分支
1.配置 GitHub 或者 Gitlab 项目 URL 地址, 并指定 GitLab Connection。
2.配置 参数化构建过程 , 使用 Git 参数 输入变量名称【TagBranchName】,选择参数类型【分支或标签】以及设置默认值【origin/master】。
3.执行 Build with Parameters
选择查看构建部署可用的Tag或Branch名称?
此时流水线代码可以这样写:
[TOC]
0x00 知识扩展 1.Jenkins 如何根据代仓库的 webhook 自动触发拉取提交的分支代码并构建? 描述: 通常每个项目代码库都会有不同的分支,(如果你没有用多分支流水线的情况下)对于普通的流水线项目我们可以让一条流水线来支持多个分支的发布,其实有时候你会发现每个分支的集成步骤都是差不多的,对于常规的我们可以安装使用git parameter
插件,其次还需配置参数化构建过程。
方式1.手动动态拉取指定分支
1.配置 GitHub 或者 Gitlab 项目 URL 地址, 并指定 GitLab Connection。
2.配置 参数化构建过程 , 使用 Git 参数 输入变量名称【TagBranchName】,选择参数类型【分支或标签】以及设置默认值【origin/master】。
3.执行 Build with Parameters
选择查看构建部署可用的Tag或Branch名称?
此时流水线代码可以这样写:1 2 checkout([$class: 'GitSCM' , branches: [[name: "origin/${params.TagBranchName}" ]], doGenerateSubmoduleConfigurations: false , extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${env.GITLAB_PUB}" , url: "${env.GITLAB_URL}" ]]])
weiyigeek.top-手动动态拉取分支
方式2.使用 GitLab Plugin 让代码提交自动触发拉取
1.配置构建触发器, 此处启用Enabled GitLab triggers
, 下拉到Build when a change is pushed to GitLab
,获得 GitLab webhook URL,并点击高级获得 Secret Token, 注意需要安装【 GitLab Plugin 】插件以配置 GitLab 以向您的 Jenkins 实例(如 GitLab CI)发送 POST 请求。。
1 Build when a change is pushed to GitLab. GitLab webhook URL: http://jenkins.weiyigeek.top/project/HelloWorld
2.通常企业内部代码仓库会采用Gitlab作为私有的代码仓库, 为了使项目可以自动触发我们需要针对指定项目设置Webhook, 打开方式->项目->设置->Webhook设置-> 填入 GitLab webhook URL 以及 配置的 Secret token。
3.勾选【Tag push events】通常只是打Tag时触发。
此时流水线代码可以这样写:1 2 3 4 5 6 7 8 9 if ( env.gitlabSourceBranch != null ) { checkout([$class: 'GitSCM' , branches: [[name: "origin/${env.gitlabSourceBranch}" ]], doGenerateSubmoduleConfigurations: false , extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${env.GITLAB_PUB}" , url: "${env.GITLAB_URL}" ]]]) updateGitlabCommitStatus name: '代码拉取' , state: 'success' } else { checkout([$class: 'GitSCM' , branches: [[name: "${params.PREJECT_BRANCHTAG}" ]], doGenerateSubmoduleConfigurations: false , extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${env.GITLAB_PUB}" , url: "${env.GITLAB_URL}" ]]]) updateGitlabCommitStatus name: '代码拉取' , state: 'success' }
weiyigeek.top-webhook自动触发
插件参考地址: https://plugins.jenkins.io/gitlab-plugin/
温馨提示: 在 groovy 中如果变量不存在其值为null而非字符串类型的null。
方式3.使用 Generic Webhook Trigger Plugin 让代码提交自动触发拉取 描述: 在 Github 或者 Gitlab 中的 webhook 触发到底给 Jenkins 发了什么, 以Gitlab为例我们从下述看到其发送的POST请求。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 Content-Type: application/json X-Gitlab-Event: Tag Push Hook X-Gitlab-Token: 11c503a496e0a573d278357836dd3e1c4f { "object_kind" : "tag_push" , "event_name" : "tag_push" , "before" : "0000000000000000000000000000000000000000" , "after" : "013f157ce81016be5bce9fda641633ba28141055" , "ref" : "refs/tags/Test_cet_v1.0.0" , "checkout_sha" : "32a16de55f7d0c6bc6dcbb1b110db10016a2ba27" , "message" : "Test_cet_v1.0.0" , "user_id" : 5, "user_name" : "weiyigeek" , "user_username" : "project" , "user_email" : "" , "user_avatar" : "http://gitlab.weiyigeek.top/uploads/-/system/user/avatar/5/avatar.png" , "project_id" : 70, "project" : { "id" : 70, "name" : "xxsbpt" , "description" : "信息上报管理端" , "web_url" : "http://gitlab.weiyigeek.top/project/xxsbpt" , "avatar_url" : null, "git_ssh_url" : "ssh://git@gitlab.weiyigeek.top/project/xxsbpt.git" , "git_http_url" : "http://gitlab.weiyigeek.top/project/xxsbpt.git" , "namespace" : "weiyigeek" , "visibility_level" : 0, "path_with_namespace" : "project/xxsbpt" , "default_branch" : "master" , "ci_config_path" : "" , "homepage" : "http://gitlab.weiyigeek.top/project/xxsbpt" , "url" : "ssh://git@gitlab.weiyigeek.top/project/xxsbpt.git" , "ssh_url" : "ssh://git@gitlab.weiyigeek.top/project/xxsbpt.git" , "http_url" : "http://gitlab.weiyigeek.top/project/xxsbpt.git" }, "commits" : [ { "id" : "32a16de55f7d0c6bc6dcbb1b110db10016a2ba27" , "message" : "xxsbpt cet" , "title" : "xxsbpt cet" , "timestamp" : "2022-05-18T09:34:39+08:00" , "url" : "http://gitlab.weiyigeek.top/project/xxsbpt/-/commit/32a16de55f7d0c6bc6dcbb1b110db10016a2ba27" , "author" : { "name" : "weiyigeek" , "email" : "weiyigeek@weiyigeek.top" }, "added" : [], "modified" : ["KsXxsbPt/pom.xml" ], "removed" : [] } ], "total_commits_count" : 1, "push_options" : { }, "repository" : { "name" : "xxsbpt" , "url" : "ssh://git@gitlab.weiyigeek.top/project/xxsbpt.git" , "description" : "信息上报管理端" , "homepage" : "http://gitlab.weiyigeek.top/project/xxsbpt" , "git_http_url" : "http://gitlab.weiyigeek.top/project/xxsbpt.git" , "git_ssh_url" : "ssh://git@gitlab.weiyigeek.top/project/xxsbpt.git" , "visibility_level" : 0 } }
步骤 01.有了上面的请示例,我们可以在jenkins Job 里 Generic Webhook Trigger 构建触发器中进行设置,首先需要进行勾选,获取到触发URL (http://jenkins.weiyigeek.top/generic-webhook-trigger/invoke)。
步骤 02.然后在Post content parameters中进行配置,例如此处我们想要获取到ref的值可以这样配置。1 2 3 4 5 6 7 8 9 10 11 Variable: ref Expression: $.ref Value filter: refs/tags/ Default value:master
weiyigeek.top-Post content parameters
温馨提示: 除此之外我们还可以获得Header parameters
以及Request parameters
, 并且新增 Post content parameters
参数例如再获取 message 信息。1 2 Variable: message Expression: $.message
步骤 03.为了保证webhook未授权访问, 我们可以对其设置 Token 在认证时可以采用如下方式。
Query parameter invoke?token=weiyigeek-secrets
A token header token: TOKEN_HERE
A Authorization: Bearer header Authorization: Bearer TOKEN_HERE
步骤 04.配置Optional filter, 此处作用是条件判断,当只有符合Expression中定义的正则的变量Text 才会触发发版(CICD)否则不会发版。Text处的变量是通过前面Post content parameters
区域提取的变量 message 。
weiyigeek.top-变量正则表达式匹配
步骤 05.在Gitlab对应的项目中添加Webkook触发接口,即打开方式->项目->设置->Webhook设置-> 填入 GitLab webhook URL (http://jenkins.weiyigeek.top/generic-webhook-trigger/invoke?token=weiyigeek-secrets
)
步骤 06.此时假如我们向代码仓库中打一个Tag, 例如 git tag -a Test_devops_v1.0.0 -m "Test_devops_v1.0.0"
等待触发 jenkins 拉取。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 String BUILD_ENV = "" String BUILD_NAME = "" String BUILD_VERSION = "" pipeline { node(k8s) { stage('Generic Webhook Trigger' ) { dir('/home/jenkins/agent/workspace/test' ) { def currentBranch = ref git branch: currentBranch, credentialsId: 'xxxx' , url: 'http://gitlab.weiyigeek.top/devops.git' def arr = ref.split("_" ) as List BUILD_ENV = arr[0 ] BUILD_NAME = arr[1 ] BUILD_VERSION = arr[1 ] } } } }
2.如何在Jenkins pipeline中获取shell命令得返回值? 描述: 在 Pipeline 流水线中通常需要对执行的sh命令获取其值,我们可以通过如下几种方式获取其标准输出或者其执行状态。
无需返回值,仅执行shell命令
1 2 3 4 5 6 7 8 9 10 11 12 pipeline{ stages { stage ('代码拉取' ) { steps { sh 'whoami && uname -a' } } } }
获取标准输出
1 2 3 4 5 6 7 8 result = sh returnStdout: true ,script: "<shell command>" result = result.trim() result = sh(script: "<shell command>" , returnStdout: true ).trim() sh "<shell command> > commandResult" result = readFile('commandResult' ).trim()
获取执行状态
1 2 3 4 5 6 7 8 result = sh returnStatus: true ,script: "<shell command>" result = result.trim() result = sh(script: "<shell command>" , returnStatus: true ).trim() sh '<shell command>; echo $? > status' def r = readFile('status' ).trim()
温馨提示: trim()
方法用于删除字符串的头尾空白符,空白符包括:空格、制表符 tab、换行符等其他空白符等, 在 Jenkins 流水线中非常重要,因为命令执行后总是会在其末尾添加一个换行符。
3.如何在Jenkins pipeline中获取项目的commit id与commit msg并设置为环境变量? 描述: 在CICD中通常我们需要构建的消息提示, 而提示的相关信息必不可少的就是本次构建代码提交的id以及其代码提交主要修改信息等,方便运维以及开发人员进行后续测试、部署以及运维。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 sh label: 'commit_id' ,returnStatus: true ,script: 'git rev-parse HEAD' sh label: 'short_commit_id' ,returnStatus: true ,script: 'git rev-parse --short HEAD' sh label: 'short_commit_id' ,returnStatus: true ,script: 'git rev-parse --short HEAD ${GIT_COMMIT}' sh label: 'short_commit_id' ,returnStatus: true ,script: 'git log --oneline -1 | awk \'{print \$1}\'' sh label: 'git_commitid' ,returnStdout: true , script: """ git show --oneline --ignore-all-space --text | head -n 1 | cut -d ' ' -f 1 |tr -d '\\n' """ stage('get_commit_msg' ) { steps { script { env.GIT_COMMIT_MSG = sh (script: 'git log -1 --pretty=%B ${GIT_COMMIT}' , returnStdout: true ).trim() env.GIT_COMMIT_MSG = sh label: 'git_commitmsg' ,returnStdout: true , script: """ git show --oneline --ignore-all-space --text | head -n 1 |tr -d '\\n' """ } } }
0x01 入坑出坑 问题1.在Jenkins流水线中运行dockerIndocker镜像环境测试docker命令显示 dial unix /var/run/docker.sock: connect: permission denied 错误信息,解决方法如下:
错误信息:1 2 ~ $ docker ps Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json: dial unix /var/run/docker.sock: connect: permission denied
问题原因: 由于镜像中的docker执行者权限较低或者不属于docker组,则无操作权限。
解决办法: 1 2 3 4 5 6 7 8 9 10 11 12 sudo docker images sudo usermod -a -G docker jenkins sudo gpasswd -a $USER docker newgrp - docker sudo chmod 666 /var/run/docker.sock
问题2.在Kubernetes集群中运行自定义动态slave工作节点时报hudson.remoting.Channel$CallSiteStackTrace: Remote call to JNLP4-connect connection
错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 pipeline { agent { kubernetes { cloud 'kubernetes' namespace 'devops' inheritFrom 'jenkins-slave' showRawYaml 'false' workingDir '/home/jenkins/agent' nodeSelector 'kubernetes.io/hostname=weiyigeek-201' yaml """\ apiVersion: kind: Pod metadata: labels: app: 'jenkins-jnlp' job: ${JOB_NAME_NUMBER} spec: serviceAccountName: 'jenkins-sa' automountServiceAccountToken: false securityContext: runAsUser: 1000 # default UID of jenkins user in agent image containers: - name: 'jnlp' image: 'alpine-jenkins-jnlp:v2.330' imagePullPolicy: 'IfNotPresent' command: ["/bin/sh"] args: ["-c","/usr/local/bin/jenkins-agent.sh && cat"] tty: true env: - name: JAVA_OPTS value: '-Xms512m -Xmx1g -Xss1m' resources: limits: {} requests: memory: '512Mi' cpu: '500m' volumeMounts: - name: 'maven-cache' mountPath: '/home/jenkins/.m2' - name: 'docker-socket' mountPath: '/var/run/docker.sock' volumes: - name: maven-cache hostPath: path: /nfsdisk-31/appstorage/mavenRepo - name: docker-socket hostPath: path: /var/run/docker.sock """ } } stages { stage ('declarative Pipeline - kubernetes plugin Test' ) { steps { echo "[*] kubernetes 插件测试" sh "mvn -version" sh "release-cli -v" sh "sonar-scanner -v" sh "kubectl version" sh "sleep 666" sh "docker --version && sudo docker ps" } } } }
1 2 3 env: - name: JAVA_OPTS value: '-Xms512m -Xmx1g -Xss1m'
问题3.记一次由于外部K8S集群证书到期导致Jenkins无法生成动态agent节点错误解决 错误信息:
1 2 3 4 [Pipeline] node Still waiting to schedule task All nodes of label ‘Jenkins项目名称-_50-m4dq2’ are offline Aborted by Jenkins 管理员
weiyigeek.top-由于K8S证书到期导致Jenkins无法生成动态agent节点错误图
问题原因: 由于外部K8S集群相关组件证书更新,导致Jenkins Clouds 的 Kubernetes 链接证书过期无法正常使用,需重新生成新的 cert.pfx 并上传到凭据中心。
解决办法: 可参照此篇文章进行【 https://blog.weiyigeek.top/2020/12-30-531.html#独立Jenkins-Master节点接入外部K8s集群 】进行更新Jenkins上连接K8S集群的APIServer证书,或者按照如下方法进行。
步骤 01.K8S外部集群证书提取与生成 Kubernetes 的 server certificate key 和Client P12 Certificate File。
点击 Jenkins Dashboard -> 凭据 -> 系统 -> 全局凭据 (unrestricted) -> 选择【旧凭据项目】-> 更新 -> 选择上传上面生成的 cert.pfx 到 jenkins 中 -> 输入其凭据密码。
weiyigeek.top-更新外部K8S集群凭据
步骤 02.重新生成新的 cert.pfx Jenkins 所用的证书。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 kubectl create namespace devops kubectl create clusterrolebinding jenkins --clusterrole cluster-admin --serviceaccount=devops:jenkins cat ~/.kube/config | grep "certificate-authority-data" | sed s/[[:space:]]//g | cut -d ':' -f 2 | base64 -d > ca.crt cat ~/.kube/config | grep "client-certificate-data" | sed s/[[:space:]]//g | cut -d ':' -f 2 | base64 -d > client.crt cat ~/.kube/config | grep "client-key-data" | sed s/[[:space:]]//g | cut -d ':' -f 2 | base64 -d > client.key openssl pkcs12 -export -out cert.pfx -inkey client.key -in client.crt -certfile ca.crt
步骤 03.点击 Dashboard -> 节点列表 -> Configure Clouds (左侧) -> 选择【Kubernetes】选项卡 -> 连接测试。 (若入下图所示则配置成功)
weiyigeek.top-外部K8S集群配置链接测试