更新jenkins以及添加jenkins插件
GitLab Plugin(gitlab webhook触发构建插件)、Parameterized Trigger(参数化触发)、Maven Integration plugin(maven项目)、 Publish Over SSH(远程推送执行,CD必备)、Docker(基于Docker的master/slave构建任务)、Kubernetes plugin(基于Kubernetes的master/slave构建任务)
gitlab插件添加完后配置gitlab服务器
Manage Jenkins --> Configure System
API token 获取方式见gitlab配置
创建并配置jenkins流水线(pipeline)工程
配置gitlab hook钩子
配置jenkins流水线脚本并保存
jenkins pipeline脚本
gitlab自动触发构建脚本
pipeline {
agent any
parameters {
//这些参数可以变更
string defaultValue: 'hosted', description: '选择运行profile', name: 'selectProfile', trim: true
string defaultValue: 'konka-standard', description: '镜像命名空间', name: 'dockerImagePrefix', trim: true
string defaultValue: 'http://192.168.1.2225:2370', description: 'docker服务地址', name: 'dockerServerHost', trim: true
string defaultValue: '192.168.1.222:2375', description: 'docker仓库地址', name: 'dockerProxyImageHost', trim: true
}
environment {
//要检出的分支
GIT_CHECK_BRANCH = "feature/dev"
}
tools {
//配置maven;Manage Jenkins -> global tool configuration中的maven配置名称
maven 'maven-3.5.3'
}
stages{
stage('Checkout') {
steps {
echo 'checkout from git'
script{
echo 'admin manual build'
//检出代码语句见——》生成git代码检查语句
checkout([$class: 'GitSCM', branches: [[name: "*/${env.GIT_CHECK_BRANCH}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'ca590b95-256b-4edd-98bb-xxxxxxxxxx', url: 'http://github.com/xxx/yyy.git']]])
}
}
}
stage('Build Docker Image'){
steps {
echo 'build docker image step'
script{
echo 'begin build docker image'
//无法访问内网环境则将nexus.inner替换成nexus.http
sh "imgTag=${env.GIT_CHECK_BRANCH} && mvn clean package -P${params.selectProfile},nexus.inner,!buildprod:linux,!jave2.platform,docker -Dmaven.test.skip=ture -Ddocker.image.prefix=${params.dockerImagePrefix} -Ddocker.image.version=\${imgTag//\\//-}-$BUILD_NUMBER -Ddocker.nexus.hosted.server=${params.dockerProxyImageHost} -Ddocker.server.host=${params.dockerServerHost} "
echo 'end build docker image'
}
}
}
}
}
jenkins 手动触发构建,比自动触发多了手动设置的一些参数
pipeline {
agent any
parameters {
string defaultValue: 'hosted', description: '选择运行profile', name: 'selectProfile', trim: true
string defaultValue: 'feature/dev', description: '选择代码分支', name: 'selectBranch', trim: true
string defaultValue: 'konka-standard', description: '镜像命名空间', name: 'dockerImagePrefix', trim: true
string defaultValue: 'latest', description: '镜像版本tag', name: 'dockerImageTag', trim: true
string defaultValue: 'http://192.168.1.2225:2370', description: 'docker服务地址', name: 'dockerServerHost', trim: true
string defaultValue: '192.168.1.222:2375', description: 'docker仓库地址', name: 'dockerProxyImageHost', trim: true
string defaultValue: 'false', description: '是否构建lib镜像', name: 'selectLibImage', trim: true
string defaultValue: 'true', description: '是否构建应用镜像', name: 'selectAPPImage', trim: true
}
environment {
BUILD_DOCKER_LIB_IMAGE = "${params.selectLibImage}"
BUILD_DOCKER_APP_IMAGE = "${params.selectAPPImage}"
}
tools {
//配置maven;Manage Jenkins -> global tool configuration中的maven配置名称
maven 'maven-3.5.3'
}
stages {
stage('Checkout') {
steps {
echo 'checkout from git'
script{
echo 'admin manual build'
//检出代码语句见——》生成git代码检查语句
checkout([$class: 'GitSCM', branches: [[name: "*/${params.selectBranch}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'ca590b95-256b-4edd-98bb-xxxxxxxxxx', url: 'http://github.com/xxx/yyy.git']]])
}
}
}
stage('Build Docker Image'){
steps {
echo 'build docker image step'
script{
echo 'begin build docker image'
//无法访问内网环境则将nexus.inner替换成nexus.http
if (env.BUILD_DOCKER_LIB_IMAGE == 'true' && env.BUILD_DOCKER_APP_IMAGE != 'true'){
echo 'build docker lib image'
sh "mvn clean package -P${params.selectProfile},nexus.inner,!buildprod:linux,!jave2.platform,docker-lib -Dmaven.test.skip=ture -Ddocker.image.prefix=${params.dockerImagePrefix} -Ddocker.image.version=latest -Ddocker.nexus.hosted.server=${params.dockerProxyImageHost} -Ddocker.server.host=${params.dockerServerHost} "
}else if (env.BUILD_DOCKER_LIB_IMAGE != 'true' && env.BUILD_DOCKER_APP_IMAGE == 'true'){
echo 'build docker application image'
sh "imgTag=${params.dockerImageTag} && mvn clean package -P${params.selectProfile},nexus.inner,!buildprod:linux,!jave2.platform,docker -Dmaven.test.skip=ture -Ddocker.image.prefix=${params.dockerImagePrefix} -Ddocker.image.version=\${imgTag//\\//-} -Ddocker.nexus.hosted.server=${params.dockerProxyImageHost} -Ddocker.server.host=${params.dockerServerHost} "
}else if (env.BUILD_DOCKER_LIB_IMAGE == 'true' && env.BUILD_DOCKER_APP_IMAGE == 'true'){
echo 'build docker lib and application image'
sh "imgTag=${params.dockerImageTag} && mvn clean package -P${params.selectProfile},nexus.inner,!buildprod:linux,!jave2.platform,docker-lib,docker -Dmaven.test.skip=ture -Ddocker.image.prefix=${params.dockerImagePrefix} -Ddocker.image.version=\${imgTag//\\//-} -Ddocker.nexus.hosted.server=${params.dockerProxyImageHost} -Ddocker.server.host=${params.dockerServerHost} "
}
echo 'end build docker image'
}
}
}
}
}
生成git代码检查语句
手动触发的一些在构建时指定的参数配置
由于应用镜像依赖了lib基础镜像,所以需要手动构建一次lib基础镜像,并且在应用jar包依赖变更时,手动触发更新一次lib镜像,否则应用在构建时还是会使用老版本lib镜像
登录root管理员账号配置允许本地网络钩子函数访问服务
否则会出现错误:Urlis blocked:Request to the local network are not allowed
Manage Jenkins > Configure Global Security > Agent
安装Docker插件
Manage Jenkins > Manage Plugins
配置一个docker服务
修改docker.service:
/usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2370
systemctl deamon-reload && systemctl restart docker
如果docker更新后,此文件可能会被覆盖,需要重新配置
配置Docker Cloud
Manage Jenkins > Manage Nodes And Clouds > Configure Clouds > Add a new cloud :: Docker
配置Docker Agent 模板
上面的docker镜像一定要选择支持jnlp协议的镜像,用户可以基于官方镜像定制化一些自有镜像,以添加相应编译工具,不过不能修改容器的启动命令
ENTRYPOINT ["/usr/local/bin/jenkins-agent"]
新建jenkins pipeline工程
这里只提供一下pipeline脚本
label
pipeline{
agent {
//上面配置的Docker Agent templates的label
label 'docker-agent'
}
//依然可以使用参数化构建
parameters {
string defaultValue: 'hosted', description: '选择运行profile', name: 'selectProfile', trim: true
string defaultValue: 'feature/dev', description: '选择代码分支', name: 'selectBranch', trim: true
string defaultValue: 'http://192.168.1.2225:2370', description: 'docker服务地址', name: 'dockerServerHost', trim: true
string defaultValue: '192.168.1.222:2375', description: 'docker仓库地址', name: 'dockerProxyImageHost', trim: true
string defaultValue: 'false', description: '是否构建lib镜像', name: 'selectLibImage', trim: true
string defaultValue: 'true', description: '是否构建应用镜像', name: 'selectAPPImage', trim: true
}
//依然可以使用环境变量
environment {
BUILD_DOCKER_LIB_IMAGE = "${params.selectLibImage}"
BUILD_DOCKER_APP_IMAGE = "${params.selectAPPImage}"
}
stages {
stage('Checkout') {
steps {
echo 'checkout from git'
script{
echo 'admin manual build'
//检出git代码脚本
checkout([$class: 'GitSCM', branches: [[name: "*/${params.selectBranch}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'ca590b95-256b-4edd-98bb-xxxxxxxxxx', url: 'http://github.com/xxx/yyy.git']]])
}
}
}
stage('Build Docker Image'){
steps {
echo 'build docker image step'
script{
echo 'begin build docker image'
//使用sh脚本运行,及编译工程
//'''不可替换变量;"""可以替换变量,
//声明bash执行,否则可能导致某些语句出错
//git分支有两级如feature/dev,第一级作为docker镜像namespace,第二级+git commit short hash作为docker镜像tag
sh """#!/bin/bash
gitBranch=${params.selectBranch}
imgNamespace=\$(echo \$gitBranch | awk -F '/' '{print \$1}')
imgTag="\$(echo \$gitBranch | awk -F '/' '{print \$2}')-\$(git rev-parse --short HEAD)"
if [ "${env.BUILD_DOCKER_LIB_IMAGE}" = "true" -a "${env.BUILD_DOCKER_APP_IMAGE}" != "true" ]; then
echo 'build docker lib image'
mvn clean package -P${params.selectProfile},nexus.inner,!buildprod:linux,!jave2.platform,docker-lib -Dmaven.test.skip=true -Ddocker.image.prefix=\$imgNamespace -Ddocker.image.version=\$imgTag -Ddocker.nexus.hosted.server=${params.dockerProxyImageHost} -Ddocker.server.host=${params.dockerServerHost}
elif [ "${env.BUILD_DOCKER_LIB_IMAGE}" != "true" -a "${env.BUILD_DOCKER_APP_IMAGE}" = "true" ]; then
echo 'build docker application image'
mvn clean package -P${params.selectProfile},nexus.inner,!buildprod:linux,!jave2.platform,docker -Dmaven.test.skip=true -Ddocker.image.prefix=\$imgNamespace -Ddocker.image.version=\$imgTag -Ddocker.nexus.hosted.server=${params.dockerProxyImageHost} -Ddocker.server.host=${params.dockerServerHost}
elif [ "${env.BUILD_DOCKER_LIB_IMAGE}" = "true" -a "${env.BUILD_DOCKER_APP_IMAGE}" = "true" ]; then
echo 'build docker lib and application image'
mvn clean package -P${params.selectProfile},nexus.inner,!buildprod:linux,!jave2.platform,docker-lib,docker -Dmaven.test.skip=true -Ddocker.image.prefix=\$imgNamespace -Ddocker.image.version=\$imgTag -Ddocker.nexus.hosted.server=${params.dockerProxyImageHost} -Ddocker.server.host=${params.dockerServerHost}
fi
"""
echo 'end build docker image'
}
}
}
}
}
非labe方式
不推荐使用,可能会遇到不可预知的错误,例如无法跟jenkins master通信、entrypoint错误时导致镜像启动失败,或者启动动就停止
安装Kubernetes plugin插件
Manage Jenkins > Manage Plugins
安装kubernetes集群(具体安装方式不做说明),并获取kubernetes api-server
访问证书
可以通过任意一台能与kubernetes集群通信的服务器,运行如下命令获得:
cat ~/.kube/config | grep 'certificate-authority-data:' | awk '{print $2}' | base64 -d
配置kubernetes
Manage Jenkins > Manage Nodes And Clouds > Configure Clouds > Add a new cloud :: Kubernetes
创建k8s namespace:
kubectl create namespace jenkins-ci
配置默认的Pod Templates
容器名称及镜像非常重要,一不注意就会导致pod启动不成功和无法与jenkins master通信。
- 容器名称为jnlp:自定义镜像必须继承自
jenkins/inbound-agent
或直接使用jenkins提供的一些官方编译镜像,同时不能修改容器的启动参数ENTRYPOINT ["/usr/local/bin/jenkins-agent"]
,否则会导致pod容器启动失败,或者无法与jenkins master通信,致使pipeline脚本无法顺利执行。- 容器名称不为jnlp:jenkins会自动给pod添加一个
jenkins/inbound-agent
容器,与jenkins master通信,自定义的容器不能同时实现相似功能,否则jenkins会受到两个连接。但是自定义容器一定要处理好容器启动方法,否则会导致容器启动不了,或者启动就停止,致使pipeline脚本无法顺利执行。所以推荐直接将容器名称命名为jnlp,并使用jenkins官方提供的jnlp相关镜像或者依据这些镜像定制化一些私有功能及编译环境。
创建在k8s上构建的工程
有两种方式:一种是基于label筛选的pipeline实现,需要预先就配置好Pod模板(如上),此种方式可以实现参数化构建;一直是podTemplate脚本在脚本中指定pod模板参数,这种方式无法使用参数化构建。
基于pipeline实现的构建方式
pipeline{
agent {
//pod 模板的标签名称
label 'jenkins-slave-maven'
}
//可以使用参数化构建
parameters {
string defaultValue: 'hosted', description: '选择运行profile', name: 'selectProfile', trim: true
string defaultValue: 'feature/dev', description: '选择代码分支', name: 'selectBranch', trim: true
string defaultValue: 'http://192.168.1.2225:2370', description: 'docker服务地址', name: 'dockerServerHost', trim: true
string defaultValue: '192.168.1.222:2375', description: 'docker仓库地址', name: 'dockerProxyImageHost', trim: true
string defaultValue: 'false', description: '是否构建lib镜像', name: 'selectLibImage', trim: true
string defaultValue: 'true', description: '是否构建应用镜像', name: 'selectAPPImage', trim: true
}
//可以使用环境变量
environment {
BUILD_DOCKER_LIB_IMAGE = "${params.selectLibImage}"
BUILD_DOCKER_APP_IMAGE = "${params.selectAPPImage}"
}
stages {
stage('Checkout') {
steps {
echo 'checkout from git'
script{
echo 'admin manual build'
//git检出脚本
checkout([$class: 'GitSCM', branches: [[name: "*/${params.selectBranch}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'ca590b95-256b-4edd-98bb-xxxxxxxxxx', url: 'http://github.com/xxx/yyy.git']]])
}
}
}
stage('Build Docker Image'){
steps {
echo 'build docker image step'
script{
echo 'begin build docker image'
//使用sh脚本运行,及编译工程
//'''不可替换变量;"""可以替换变量,
//声明bash执行,否则可能导致某些语句出错
//git分支有两级如feature/dev,第一级作为docker镜像namespace,第二级+git commit short hash作为docker镜像tag
sh """#!/bin/bash
gitBranch=${params.selectBranch}
imgNamespace=\$(echo \$gitBranch | awk -F '/' '{print \$1}')
imgTag="\$(echo \$gitBranch | awk -F '/' '{print \$2}')-\$(git rev-parse --short HEAD)"
if [ "${env.BUILD_DOCKER_LIB_IMAGE}" = "true" -a "${env.BUILD_DOCKER_APP_IMAGE}" != "true" ]; then
echo 'build docker lib image'
mvn clean package -P${params.selectProfile},nexus.inner,!buildprod:linux,!jave2.platform,docker-lib -Dmaven.test.skip=true -Ddocker.image.prefix=\$imgNamespace -Ddocker.image.version=\$imgTag -Ddocker.nexus.hosted.server=${params.dockerProxyImageHost} -Ddocker.server.host=${params.dockerServerHost}
elif [ "${env.BUILD_DOCKER_LIB_IMAGE}" != "true" -a "${env.BUILD_DOCKER_APP_IMAGE}" = "true" ]; then
echo 'build docker application image'
mvn clean package -P${params.selectProfile},nexus.inner,!buildprod:linux,!jave2.platform,docker -Dmaven.test.skip=true -Ddocker.image.prefix=\$imgNamespace -Ddocker.image.version=\$imgTag -Ddocker.nexus.hosted.server=${params.dockerProxyImageHost} -Ddocker.server.host=${params.dockerServerHost}
elif [ "${env.BUILD_DOCKER_LIB_IMAGE}" = "true" -a "${env.BUILD_DOCKER_APP_IMAGE}" = "true" ]; then
echo 'build docker lib and application image'
mvn clean package -P${params.selectProfile},nexus.inner,!buildprod:linux,!jave2.platform,docker-lib,docker -Dmaven.test.skip=true -Ddocker.image.prefix=\$imgNamespace -Ddocker.image.version=\$imgTag -Ddocker.nexus.hosted.server=${params.dockerProxyImageHost} -Ddocker.server.host=${params.dockerServerHost}
fi
"""
echo 'end build docker image'
}
}
}
}
}
基于podTemplate实现的构建方式
//指定pod的label
def label = "k8s-worker"
//指定运行profile
def selectProfile = "hosted"
//指定代码分支
def selectBranch = "feature/dev"
//docker服务地址
def dockerProxyImageHost = "192.168.1.222:2375"
//docker服务地址
def dockerServerHost = "http://192.168.1.2225:2370"
//是否构建lib镜像
def buildDockerLibImage = "false"
//是否构建应用镜像
def buildDockerAppImage = "true"
podTemplate(containers: [
//容器模板
containerTemplate(args: '${computer.jnlpmac} ${computer.name}', //容器启动时参数
//容器镜像,直接使用jenkins官方提供的jnlp镜像
image: 'jenkins/jnlp-agent-maven:latest',
livenessProbe: containerLivenessProbe(execArgs: '', failureThreshold: 0, initialDelaySeconds: 0, periodSeconds: 0, successThreshold: 0, timeoutSeconds: 0),
//命名为jnlp,阻止jenkins添加默认jenkins/inbound-agent容器
name: 'jnlp',
resourceLimitCpu: '',
resourceLimitMemory: '',
resourceRequestCpu: '',
resourceRequestMemory: '',
ttyEnabled: true,
workingDir: '/home/jenkins/agent')],
//pod模板标签
label: label,
name: 'maven',
//pod所在namespace,如果不存在需要创建
namespace: 'jenkins-ci',
//通过环境变量传递参数
envVars: [envVar(key: 'BUILD_DOCKER_LIB_IMAGE', value: buildDockerLibImage),
envVar(key: 'GIT_CHECKOUT_BRANCH', value: selectBranch),
envVar(key: 'MAVEN_BUILD_PROFILE', value: selectProfile),
envVar(key: 'DOCKER_PROXY_IMAGE_HOST', value: dockerProxyImageHost),
envVar(key: 'DOCKER_SERVER_HOST', value: dockerServerHost),
envVar(key: 'BUILD_DOCKER_APP_IMAGE', value: buildDockerAppImage)],
//挂载一些目录到容器中
volumes: [
hostPathVolume(hostPath: '/usr/local/maven/repo', mountPath: '/usr/local/maven/repo'),
hostPathVolume(hostPath: '/usr/local/maven/maven/conf', mountPath: '/root/.m2')],
//指定工作目录挂载到宿主机目录
workspaceVolume: hostPathWorkspaceVolume('/var/lib/jenkins/')) {
node(label){
stage('Checkout') {
container('jnlp'){
echo 'checkout from git'
script{
sh 'env'
echo "admin manual build, checkout branct \${GIT_CHECKOUT_BRANCH}"
//git检出脚本
checkout([$class: 'GitSCM', branches: [[name: "*/\${GIT_CHECKOUT_BRANCH}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'ca590b95-256b-4edd-98bb-xxxxxxxxxx', url: 'http://github.com/xxx/yyy.git']]])
}
}
}
stage('Build Docker Image'){
container('jnlp'){
echo 'build docker image step'
script{
echo 'begin build docker image'
//使用sh脚本运行,及编译工程
//'''不可替换变量;"""可以替换变量,只能使用环境变量,无法使用参数化构建了
//声明bash执行,否则可能导致某些语句出错
//git分支有两级如feature/dev,第一级作为docker镜像namespace,第二级+git commit short hash作为docker镜像tag
sh """#!/bin/bash
gitBranch=\${GIT_CHECKOUT_BRANCH}
imgNamespace=\$(echo \$gitBranch | awk -F '/' '{print \$1}')
imgTag="\$(echo \$gitBranch | awk -F '/' '{print \$2}')-\$(git rev-parse --short HEAD)"
if [ "\${BUILD_DOCKER_LIB_IMAGE}" = "true" -a "\${BUILD_DOCKER_APP_IMAGE}" != "true" ]; then
echo 'build docker lib image'
mvn clean package -P\${MAVEN_BUILD_PROFILE},nexus.inner,!buildprod:linux,!jave2.platform,docker-lib -Dmaven.test.skip=true -Ddocker.image.prefix=\$imgNamespace -Ddocker.image.version=\$imgTag -Ddocker.nexus.hosted.server=\${DOCKER_PROXY_IMAGE_HOST} -Ddocker.server.host=\${DOCKER_SERVER_HOST}
elif [ "\${BUILD_DOCKER_LIB_IMAGE}" != "true" -a "\${BUILD_DOCKER_APP_IMAGE}" = "true" ]; then
echo 'build docker application image'
mvn clean package -P\${MAVEN_BUILD_PROFILE},nexus.inner,!buildprod:linux,!jave2.platform,docker -Dmaven.test.skip=true -Ddocker.image.prefix=\$imgNamespace -Ddocker.image.version=\$imgTag -Ddocker.nexus.hosted.server=\${DOCKER_PROXY_IMAGE_HOST} -Ddocker.server.host=\${DOCKER_SERVER_HOST}
elif [ "\${BUILD_DOCKER_LIB_IMAGE}" = "true" -a "\${BUILD_DOCKER_APP_IMAGE}" = "true" ]; then
echo 'build docker lib and application image'
mvn clean package -P\${MAVEN_BUILD_PROFILE},nexus.inner,!buildprod:linux,!jave2.platform,docker-lib,docker -Dmaven.test.skip=true -Ddocker.image.prefix=\$imgNamespace -Ddocker.image.version=\$imgTag -Ddocker.nexus.hosted.server=\${DOCKER_PROXY_IMAGE_HOST} -Ddocker.server.host=\${DOCKER_SERVER_HOST}
fi
"""
echo 'end build docker image'
}
}
}
}
}