Jenkins 配置gitlab的 pipeline流水线任务_gitlab pipeline
配置jenkins到gitlab仓库权限
1,jenkins拉取代码的3种方式
需要拉取gitlab仓库的权限。这里有三种方法,按环境自己选择。
- 1, 在gitlab创建专属Jenkins的账号密码。最简单,但是不怎么安全。
- 2,配置 Jenkins 的 SSH 公钥到 GitLab。主要用于 git 命令拉取 git@gitlab.com:… 仓库。安全
- 3, 配置 GitLab Personal Access Token(PAT)到 Jenkins。HTTPS 访问 GitLab API。
在jenkins只拉取仓库的话,推荐第二种。安全,没有时间限制,不暴露其他信息。
注意这种方式,拉取代码仓库,只能用git用户 命令拉取git仓库名,如:git@198.19.249.92:project-1/xxl-job.git。
2,配置SSH密钥公钥
Jenkins >>. 系统管理 >> 凭证管理 >> 全局 >> add credentials
gitlab ssh key添加jenkins对应的公钥即可。
jenkins 全局工具配置
java项目,主要两个工具,jdk和maven。
1,JDK
jdk可以下载指定版本放在服务器,解压后,指定文件夹即可。
也可以选择自动安装,触发java命令就会自动安装。
2,maven
maven也可以下载配置,会自动安装。
小坑,创建 自由项目,并不会自动安装。maven任务 和pipline任务,测试可以自动安装。
流水线pipeline任务
这里以xxl job 项目测试,这个也是java项目,可以练练手。先将源码上传至gitlab仓库。
项目地址:https://github.com/xuxueli/xxl-job/
1,Pipeline script 基础语法
流水线,只需要这一个文件即可,相当于界面配置换成命令配置。
基础语法
pipeline {
agent any
stages {
stage(
\'Hello\'
) {
steps {
echo
\'Hello World\'
}
}
}
}
pipeline 定义一个流水线脚本
agent 指示 Jenkins 为整个流水线分配一个执行器(在 Jenkins 环境中的任何可用代理/节点上)和工作区。
stages 全部的工作都在这里执行
stage 每个工作开始
steps jenkinsfile 声明式脚本往这里面写
echo 写一个简单的字符串到控制台输出。
默认提供了示例,可自行参考。“流水线语法” 可以自动生成命令。如下,自动生成了,拉取代码仓库命令。
2,xxl job 构建部署的pipeline文件
四个步骤,拉取代码 >> 构建 >> 复制拷贝 >> 远程运行。
environment 环境参数,主要是服务器相关信息。jar包位置根据安装的方式可能有所不一样,注意一下。
tools 需要用到的工具,配置好了会自动调用或安装。
拉取代码 :git拉取
构建命令: mvn clean package -Dmaven.test.skip=true
拷贝: rsync命令拷贝
运行:远程运行服务器的start.sh脚本启动服务。
pipeline {
agent any
environment {
APP_NAME =
\'xxl-job-admin\'
BUILD_NAME =
\'xxl_job_pip\'
REMOTE_IP_LIST =
\'198.19.249.107\'
//
支持多个IP集群
PORT =
\'8080\'
REMOTE_DIR =
\"/app/${APP_NAME}\"
JAR_PATTERN =
\"${JENKINS_HOME}/workspace/${BUILD_NAME}/${APP_NAME}/target/${APP_NAME}-*.jar\"
}
tools {
maven
\"maven-3.9.6\"
jdk
\'jdk17\'
}
stages {
stage(
\'gitlab代码拉取\'
) {
steps {
git branch:
\'dev\'
, credentialsId:
\'25238080-093d-4e3c-884c-f70a027eb1c9\'
, url:
\'git@198.19.249.92:project-1/xxl-job.git\'
echo
\'拉取dev分支成功\'
}
}
stage(
\'mvn执行构建\'
) {
steps {
sh
\"mvn -v\"
sh
\"java --version\"
sh
\"mvn clean package -Dmaven.test.skip=true\"
echo
\"构建完成\"
}
}
stage(
\'拷贝 JAR 到远程服务器\'
) {
steps {
script {
def jarFile = sh(script:
\"ls ${JAR_PATTERN} 2>/dev/null | head -n 1\"
, returnStdout:
true
).trim()
if
(!jarFile) {
error(
\"未找到 JAR 包: ${JAR_PATTERN}\"
)
}
for
(ip
in
REMOTE_IP_LIST.
split
()) {
echo
\"==> 拷贝到远程 IP: ${ip}\"
//
检查并创建目录
sh
\"\"
\"
ssh
-o StrictHostKeyChecking=no root@${ip}
\'mkdir -p ${REMOTE_DIR} /app/logs /app/shell\'
\"\"
\"
//
拷贝 jar 包
sh
\"\"
\"
rsync
-az ${jarFile} root@${ip}:${REMOTE_DIR}/
\"\"
\"
}
echo
\"拷贝完成\"
}
}
}
stage(
\'远程部署服务\'
) {
steps {
script {
for
(ip
in
REMOTE_IP_LIST.
split
()) {
echo
\"==> 部署到远程 IP: ${ip}\"
//
生成并执行远程启动命令
sh
\"\"
\"
ssh
-o StrictHostKeyChecking=no root@${ip}
\'/bin/bash /app/shell/start.sh ${APP_NAME} ${PORT}\'
\"\"
\"
}
}
echo
\"服务部署完成\"
}
}
}
}
流水线编写完成后,并不能马上构建,需要Approve script,同意脚本后才能生效,相当于多了个审核步骤。
3,构建pipeline
构建完成后,可以在Pipeline看到每个步骤命令,从而更精确的排查问题。
构建完成,也可以选择其中一个步骤重新运行,更加灵活
扩展
1,参数化选择部署模式
虽然pipeline流水构建完成后,可以选择其中一个步骤重新执行。但是如果想简单点,构建的时候就选择,比较方便。就用到参数化选择。
需求:可以把 DEPLOY_MODE
扩展为 3 种模式:
build_and_deploy
:构建 + 拷贝 + 部署(完整流程)only_deploy
:仅拷贝 + 部署(跳过构建)only_build
:仅构建(不部署)
修改后流程如下(以xxl job演示为例) :
pipeline {
agent any
parameters {
choice(
name:
\'DEPLOY_MODE\'
,
choices: [
\'build_and_deploy\'
,
\'only_deploy\'
,
\'only_build\'
],
description:
\'选择执行模式\'
)
}
environment {
APP_NAME =
\'xxl-job-admin\'
BUILD_NAME =
\'xxl_job_pip\'
REMOTE_IP_LIST =
\'198.19.249.107\'
PORT =
\'8080\'
REMOTE_DIR =
\"/app/${APP_NAME}\"
JAR_PATTERN =
\"${JENKINS_HOME}/workspace/${BUILD_NAME}/${APP_NAME}/target/${APP_NAME}-*.jar\"
}
tools {
maven
\"maven-3.9.6\"
jdk
\'jdk17\'
}
stages {
stage(
\'gitlab代码拉取\'
) {
when {
expression { params.DEPLOY_MODE !=
\'only_deploy\'
}
}
steps {
git branch:
\'dev\'
, credentialsId:
\'25238080-093d-4e3c-884c-f70a027eb1c9\'
, url:
\'git@198.19.249.92:project-1/xxl-job.git\'
echo
\'拉取dev分支成功\'
}
}
stage(
\'mvn执行构建\'
) {
when {
expression { params.DEPLOY_MODE !=
\'only_deploy\'
}
}
steps {
sh
\"mvn -v\"
sh
\"java --version\"
sh
\"mvn clean package -Dmaven.test.skip=true\"
echo
\"构建完成\"
}
}
stage(
\'拷贝 JAR 到远程服务器\'
) {
when {
expression { params.DEPLOY_MODE !=
\'only_build\'
}
}
steps {
script {
def jarFile = sh(script:
\"ls ${JAR_PATTERN} 2>/dev/null | head -n 1\"
, returnStdout:
true
).trim()
if
(!jarFile) {
error(
\"未找到 JAR 包: ${JAR_PATTERN}\"
)
}
for
(ip
in
REMOTE_IP_LIST.
split
()) {
echo
\"==> 拷贝到远程 IP: ${ip}\"
//
检查并创建目录
sh
\"\"
\"
ssh
-o StrictHostKeyChecking=no root@${ip}
\'mkdir -p ${REMOTE_DIR} /app/logs /app/shell\'
\"\"
\"
//
拷贝 jar 包
sh
\"\"
\"
rsync
-az ${jarFile} root@${ip}:${REMOTE_DIR}/
\"\"
\"
}
echo
\"拷贝完成\"
}
}
}
stage(
\'远程部署服务\'
) {
when {
expression { params.DEPLOY_MODE ==
\'build_and_deploy\'
|| params.DEPLOY_MODE ==
\'only_deploy\'
}
}
steps {
script {
for
(ip
in
REMOTE_IP_LIST.
split
()) {
echo
\"==> 部署到远程 IP: ${ip}\"
//
生成并执行远程启动命令
sh
\"\"
\"
ssh
-o StrictHostKeyChecking=no root@${ip}
\'/bin/bash /app/shell/start.sh ${APP_NAME} ${PORT}\'
\"\"
\"
}
}
echo
\"服务部署完成\"
}
}
}
}
保存后,修改下描述就可以构建了
2,参数化选择部署环境
需求:手动选择分支(如 dev
, test
, main
),让每个分支构建时部署到不同服务器。
每个分支构建后部署到指定环境(如测试、预发布、生产)。
//
定义全局变量,不能定义environment中,因为
env
变量的赋值是只读的,scripe块中修改
env
值不会生效。
def REMOTE_IP_LIST =
\'\'
pipeline {
agent any
parameters {
choice(name:
\'BRANCH_NAME\'
, choices: [
\'dev\'
,
\'test\'
,
\'main\'
], description:
\'请选择构建分支\'
)
}
environment {
APP_NAME =
\'xxl-job-admin\'
BUILD_NAME =
\'xxl_job_pip\'
PORT =
\'8080\'
REMOTE_DIR =
\"/app/${APP_NAME}\"
JAR_PATTERN =
\"${JENKINS_HOME}/workspace/${BUILD_NAME}/${APP_NAME}/target/${APP_NAME}-*.jar\"
//
各环境服务器 IP(后期只改这里)
DEV_IPS =
\'198.19.249.107\'
TEST_IPS =
\'198.19.249.102\'
MAIN_IPS =
\'198.19.249.103 198.19.249.104\'
}
tools {
maven
\"maven-3.9.6\"
jdk
\'jdk17\'
}
stages {
stage(
\'设置部署服务器\'
) {
steps {
script {
switch (params.BRANCH_NAME) {
case
\'dev\'
:
REMOTE_IP_LIST =
env
.DEV_IPS
break
case
\'test\'
:
REMOTE_IP_LIST =
env
.TEST_IPS
break
case
\'main\'
:
REMOTE_IP_LIST =
env
.MAIN_IPS
break
default:
error
\"未知分支:${params.BRANCH_NAME}\"
}
echo
\"当前分支:${params.BRANCH_NAME}\"
echo
\"部署服务器:${REMOTE_IP_LIST}\"
}
}
}
stage(
\'gitlab代码拉取\'
) {
steps {
git branch:
\"${params.BRANCH_NAME}\"
, credentialsId:
\'25238080-093d-4e3c-884c-f70a027eb1c9\'
, url:
\'git@198.19.249.92:project-1/xxl-job.git\'
echo
\'拉取dev分支成功\'
}
}
stage(
\'mvn执行构建\'
) {
steps {
sh
\"mvn -v\"
sh
\"java --version\"
sh
\"mvn clean package -Dmaven.test.skip=true\"
echo
\"构建完成\"
}
}
stage(
\'拷贝 JAR 到远程服务器\'
) {
steps {
script {
def jarFile = sh(script:
\"ls ${JAR_PATTERN} 2>/dev/null | head -n 1\"
, returnStdout:
true
).trim()
if
(!jarFile) {
error(
\"未找到 JAR 包: ${JAR_PATTERN}\"
)
}
for
(ip
in
REMOTE_IP_LIST.
split
()) {
echo
\"==> 拷贝到远程 IP: ${ip}\"
//
检查并创建目录
sh
\"\"
\"
ssh
-o StrictHostKeyChecking=no root@${ip}
\'mkdir -p ${REMOTE_DIR} /app/logs /app/shell\'
\"\"
\"
//
拷贝 jar 包
sh
\"\"
\"
rsync
-az ${jarFile} root@${ip}:${REMOTE_DIR}/
\"\"
\"
}
echo
\"拷贝完成\"
}
}
}
stage(
\'远程部署服务\'
) {
steps {
script {
for
(ip
in
REMOTE_IP_LIST.
split
()) {
echo
\"==> 部署到远程 IP: ${ip}\"
//
生成并执行远程启动命令,根据脚本选择参数,我个人脚本运行需要传2个参数
sh
\"\"
\"
ssh
-o StrictHostKeyChecking=no root@${ip}
\'/bin/bash /app/shell/start.sh ${APP_NAME} ${PORT}\'
\"\"
\"
}
}
echo
\"服务部署完成\"
}
}
}
}
3,版本控制选择发布
对于以往打包过的jar 保存好,然后动态获取所有的包,选择发布。
Active Choices Plugin 是在 Jenkins 中动态生成参数(如版本号列表)最推荐、最灵活的方式之一,特别适合“从已构建产物目录中选择版本进行部署”的场景。
系统管理 >> 插件管理 >> Active Choices Plugin 安装即可。
在任务配置“参数化构建过程”,多出三个参数:
一般用得多的是第1个和第2个。
不需要根据其他参数来匹配就用第1个,参数化选择Active Choices Parameter,然后Groovy script 写脚本即可。
示例Groovy脚本:
#获取目标文件夹的jar包,并以版本号,只显示数字:3.1.0
def
dir
= new File(
\"/var/lib/jenkins/workspace/xxl_job_pip/xxl-job-admin/target\"
)
if
(!
dir
.exists())
return
[
\"无可用版本\"
]
def files =
dir
.listFiles().findAll { it.name.endsWith(
\".jar\"
) }
def versions = files.collect { it.name.replaceAll(
\"xxl-job-admin-(.*)\\\\.jar\"
,
\"\\$1\"
) }
return
versions.
sort
().reverse()
#获取目标文件夹的jar包,并以整个包名显示:xxl-job-admin-3.1.0.jar
def
dir
= new File(
\"/var/lib/jenkins/workspace/xxl_job_pip/xxl-job-admin/target\"
)
if
(!
dir
.exists())
return
[
\"无可用版本\"
]
def files =
dir
.listFiles().findAll { it.name.endsWith(
\".jar\"
) }
def filenames = files*.name
return
filenames.
sort
().reverse()
这里介绍主要使用第二个参数Active Choices Reactive Parameter
需求:根据其他参数选择显示版本号。
1 :选择“全新构建发布”,显示 “不适用”或者“空”
2:选择“以往版本发布”,显示所有包名。
添加一个普通 选项参数:
名称:RELEASE_TYPE,选项:全新构建发布 以往版本发布
添加另一个Active Choices Reactive Parameter参数:
name: APP_VERSION
Script: Groovy Script
//
判断是否为“以往版本发布”
if
(RELEASE_TYPE !=
\"以往版本发布\"
) {
return
[
\"不适用\"
]
}
//
读取存储目录下的所有 jar 文件
def
dir
= new File(
\"/opt/jar/xxl-job-admin\"
)
if
(!
dir
.exists())
return
[
\"目录不存在\"
]
def files =
dir
.listFiles().findAll { it.name.endsWith(
\".jar\"
) }
if
(files.isEmpty())
return
[
\"无可用版本\"
]
return
files*.name.
sort
().reverse()
Fallback Script
,只有在脚本执行失败或者返回的类型不是上面提到的类型时执行,可以填可以空着。
Choice Type: Single Select (单选即可)
Referenced Parameters:填写 RELEASE_TYPE
(关键!表示依赖此参数)
配置完成后,可以选择 Build with Parameters,查看是否显示成功.
Pipeline script :
最新流程为: 部署环境 >> Git 拉取代码 >> Maven 构建 >> 存储备份 JAR 包 >> 拷贝 JAR 到远程服务器 >> 远程启动服务
相对比前面的脚本,增加了 存储备份 步骤,备份以时间戳重命名(如果构建版本带版本号,可以不要时间戳)。
将构建的包复制到指定文件夹,都从此文件夹部署相应的包至服务器。
def REMOTE_IP_LIST =
\'\'
pipeline {
agent any
parameters {
choice(name:
\'BRANCH_NAME\'
, choices: [
\'dev\'
,
\'test\'
,
\'main\'
], description:
\'请选择构建分支\'
)
choice(name:
\'RELEASE_TYPE\'
, choices: [
\'全新构建发布\'
,
\'以往版本发布\'
], description:
\'发布方式\'
)
}
environment {
APP_NAME =
\'xxl-job-admin\'
BUILD_NAME =
\'xxl_job_pip\'
PORT =
\'8080\'
JAR_PATTERN =
\"${JENKINS_HOME}/workspace/${BUILD_NAME}/${APP_NAME}/target/\"
JAR_PATH =
\"/opt/jar/${APP_NAME}\"
REMOTE_DIR =
\"/app/${APP_NAME}\"
DEV_IPS =
\'198.19.249.107\'
TEST_IPS =
\'198.19.249.102\'
MAIN_IPS =
\'198.19.249.103 198.19.249.104\'
}
tools {
maven
\"maven-3.9.6\"
jdk
\'jdk17\'
}
stages {
stage(
\'部署环境\'
) {
steps {
script {
switch (params.BRANCH_NAME) {
case
\'dev\'
: REMOTE_IP_LIST =
env
.DEV_IPS;
break
case
\'test\'
: REMOTE_IP_LIST =
env
.TEST_IPS;
break
case
\'main\'
: REMOTE_IP_LIST =
env
.MAIN_IPS;
break
default: error
\"未知分支:${params.BRANCH_NAME}\"
}
echo
\"部署环境分支:${params.BRANCH_NAME}\"
echo
\"部署方式:${params.RELEASE_TYPE}\"
if
(params.RELEASE_TYPE !=
\'全新构建发布\'
) {
echo
\"部署版本包: ${params.APP_VERSION}\"
}
echo
\"部署服务器:${REMOTE_IP_LIST}\"
}
}
}
stage(
\'Git 拉取代码\'
) {
when {
expression {
return
!params.APP_VERSION }
//
如果是空,表示全新构建
}
steps {
git branch:
\"${params.BRANCH_NAME}\"
,
credentialsId:
\'25238080-093d-4e3c-884c-f70a027eb1c9\'
,
url:
\'git@198.19.249.92:project-1/xxl-job.git\'
echo
\"拉取 ${params.BRANCH_NAME} 分支成功\"
}
}
stage(
\'Maven 构建\'
) {
when {
expression {
return
!params.APP_VERSION }
//
只在构建新版本时执行
}
steps {
sh
\"mvn -v\"
sh
\"java --version\"
sh
\"mvn clean package -Dmaven.test.skip=true\"
echo
\"构建完成\"
}
}
stage(
\'存储备份 JAR 包\'
) {
when {
expression {
return
!params.APP_VERSION }
//
只在构建新版本时执行
}
steps {
script {
def regex =
\"^${env.APP_NAME}(-[0-9.]+)?\\\\.jar\\$\"
def jarFile = sh(
script:
\"ls -t ${env.JAR_PATTERN} | grep -E \'${regex}\' | head -n 1\"
,
returnStdout:
true
).trim()
if
(!jarFile) {
error(
\"未找到构建生成的 jar 包\"
)
}
//
获取当前时间戳
def timestamp = sh(script:
\"date +%Y%m%d%H%M\"
, returnStdout:
true
).trim()
//
生成带时间的备份文件名
def newFileName = jarFile.replace(
\".jar\"
,
\"-${timestamp}.jar\"
)
sh
\"\"
\"
mkdir
-p ${
env
.JAR_PATH}
cp
${
env
.JAR_PATTERN}${jarFile} ${
env
.JAR_PATH}/${newFileName}
ls
-tp ${
env
.JAR_PATH}/*.jar |
grep
-
v
\'/\\$\'
|
tail
-n +11 |
xargs
-r
rm
--
\"\"
\"
echo
\"备份完成,保留最新 10 个版本:${jarFile}\"
}
}
}
stage(
\'拷贝 JAR 到远程服务器\'
) {
steps {
script {
def jarFileName
def jarFilePath
if
(params.APP_VERSION?.trim()) {
//
指定了版本:用选定的版本文件名
jarFileName = params.APP_VERSION.trim()
jarFilePath =
\"${env.JAR_PATH}/${jarFileName}\"
}
else
{
//
未指定版本:自动找最新的
jarFileName = sh(script:
\"ls -t ${env.JAR_PATH}/*.jar | head -n 1\"
, returnStdout:
true
).trim().tokenize(
\"/\"
).last()
jarFilePath =
\"${env.JAR_PATH}/${jarFileName}\"
}
if
(!jarFileName || !fileExists(jarFilePath)) {
error(
\"未找到 JAR 包用于部署: ${jarFilePath}\"
)
}
echo
\"部署使用 JAR 包:${jarFilePath}\"
for
(ip
in
REMOTE_IP_LIST.
split
()) {
echo
\"拷贝到远程 IP: ${ip}\"
sh
\"\"
\"
ssh
-o StrictHostKeyChecking=no root@${ip}
\'mkdir -p ${REMOTE_DIR} /app/logs /app/shell\'
rsync
-az ${jarFilePath} root@${ip}:${REMOTE_DIR}/
\"\"
\"
}
}
}
}
stage(
\'远程启动服务\'
) {
steps {
script {
for
(ip
in
REMOTE_IP_LIST.
split
()) {
echo
\"部署到远程 IP: ${ip}\"
//
sh
\"\"
\"
ssh
-o StrictHostKeyChecking=no root@${ip}
\'/bin/bash /app/shell/start.sh ${APP_NAME} ${PORT}\'
\"\"
\"
}
}
echo
\"${env.APP_NAME}服务部署完成\"
}
}
}
}