Jenkins学习笔记
# Jenkins 是什么?
Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建、测试和部署软件。
Jenkins 支持各种运行方式,可通过系统包、Docker 或者通过一个独立的 Java 程序。
官方文档:
- https://www.jenkins.io/ (opens new window)
- https://www.jenkins.io/zh/ (opens new window)
- https://www.jenkins.io/zh/doc/ (opens new window)
- https://www.jenkins.io/zh/doc/book/installing/ (opens new window)
下载Jenkins:
- https://www.jenkins.io/download/ (opens new window)
- http://mirrors.jenkins-ci.org/ (opens new window)
下载Jenkins插件:
# 安装Jenkins
Jenkins通常作为一个独立的应用程序在其自己的流程中运行, 内置Java servlet 容器/应用程序服务器(Jetty)。
Jenkins也可以运行在不同的Java servlet容器((如Apache Tomcat 或 GlassFish))中作为servlet运行。
Jenkins安装请查阅:https://www.jenkins.io/zh/doc/book/installing/ (opens new window)
# 系统要求
最低推荐配置:
- 256MB可用内存
- 1GB可用磁盘空间(作为一个Docker (opens new window)容器运行jenkins的话推荐10GB)
为小团队推荐的硬件配置:
- 1GB+可用内存
- 50 GB+ 可用磁盘空间
软件配置:
- Java 8(无论是Java运行时环境(JRE)还是Java开发工具包(JDK)都可以。)
- Docker(可选,将Jenkins作为Docker 容器运行)
# 使用war文件安装Jenkins
Jenkins的Web应用程序ARchive(WAR)文件版本可以安装在任何支持Java的操作系统或平台上。
要下载并运行Jenkins的WAR文件版本,请执行以下操作:
将最新的稳定Jenkins WAR包 (opens new window) 下载并上传到 /opt/jenkins 目录中。
安装字体
yum -y install fontconfig
1运行命令java -jar jenkins.war
cd /opt/jenkins # 使用默认端口运行Jenkins,默认端口是8080 java -jar jenkins.war # 使用指定端口运行Jenkins java -jar jenkins.war --httpPort=80 # 使用指定端口后台运行Jenkins nohup java -jar jenkins.war --httpPort=80 & nohup java -jar jenkins.war --httpPort=80 > nohup.log 2>&1 &
1
2
3
4
5
6
7
8
9浏览http://localhost:8080并等到Unlock Jenkins页面出现。
继续使用Post-installation setup wizard (opens new window)后面步骤设置向导。
非后台运行的Jenkins,使用
Ctrl+C
在终端中发送中断信号。后台运行的Jenkins,可以通过以下命令停止Jenkins。
# 找到进程ID ps aux | grep 'java -jar jenkins.war' # 杀死进程 kill -9 PID
1
2
3
4
# 使用yum命令安装Jenkins
使用yum命令安装Jenkins请查阅:https://pkg.jenkins.io/redhat-stable/ (opens new window)
将最新的稳定Jenkins rpm包 (opens new window) 下载并上传到 /opt/jenkins 目录中
安装Jenkins
yum install epel-release # repository that provides 'daemonize' yum install jenkins-2.319.2-1.1.noarch.rpm -y
1
2编辑jenkins的 /etc/init.d/jenkins 程序文件,添加java路径
[root@jenkins2 ~]# whereis java java: /usr/local/jdk1.8.0_321/bin/java /usr/local/jdk1.8.0_321/jre/bin/java vim /etc/init.d/jenkins
1
2
3
4编辑jenkins的 /etc/sysconfig/jenkins 配置文件,修改端口、系统运行账户
jenkins默认情况是使用Jenkins用户启动的,但这个用户目前系统并没有赋予权限,这里我们将启动用户修改为root;另外Jenkins的默认端口是8080,这个跟tomcat的默认端口有冲突,可以做一下更改。
vim /etc/sysconfig/jenkins
1启动和停止Jenkins
systemctl daemon-reload # 重新加载配置 systemctl start jenkins.service # 启动jenkins systemctl enable jenkins.service # 设置jenkins开机启动 systemctl stop jenkins.service # 关闭jenkins systemctl status jenkins.service # 查看jenkins状态
1
2
3
4
5
# 安装后设置向导
安装后设置向导请查阅:https://www.jenkins.io/zh/doc/book/installing/#setup-wizard (opens new window)
# 解锁 Jenkins
当您第一次访问新的Jenkins实例时,系统会要求您使用自动生成的密码对其进行解锁。
浏览到 http://localhost:8080(或安装时为Jenkins配置的任何端口),并等待 解锁 Jenkins 页面出现。
# 跳过安装插件
解锁 Jenkins之后,在 Customize Jenkins 页面内, 您可以安装任何数量的有用插件作为您初始步骤的一部分。
Jenkins默认使用国外地址,插件安装很慢,此处先跳过。
自定义Jenkins -> 选择插件来安装 -> 无
# 创建第一个管理员用户
最后,在customizing Jenkins with plugins之后,Jenkins要求您创建第一个管理员用户。 . 出现“ 创建第一个管理员用户 ”页面时, 请在各个字段中指定管理员用户的详细信息,然后单击 保存完成 。 当 Jenkins准备好了 出现时,单击开始使用 Jenkins。
Username: root
Password: 123456
# 修改插件下载地址
Manage Jenkins > Plugins > Available plugins,把Jenkins官方的插件列表下载到本地,接着修改地址文件,替换为国内插件下载地址。
将 /root/.jenkins/updates/default.json 的国外地址替换为国内地址。
# 进入插件文件目录 cd /root/.jenkins/updates # 备份插件源地址配置 cp default.json default.json.bak # 将国外官方地址替换为国内清华大学jenkins插件地址 sed -i 's/https:\/\/updates.jenkins.io\/download\/plugins/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/plugins/g' default.json && sed -i 's/https:\/\/www.google.com/https:\/\/www.baidu.com/g' default.json
1
2
3
4
5
6
7
8Manage Jenkins > Plugins > Advanced setting,把Update Site改为国内插件下载地址。
官方插件下载地址:https://updates.jenkins.io/update-center.json
国内插件下载地址:https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
Sumbit后,在浏览器输入: http://localhost:8080/restart(或安装时为Jenkins配置的任何端口) ,重启Jenkins
# Jenkins免密登录远程服务器
在Jenkins使用ssh-keygen生成密钥对,将公钥复制到远程服务器的~/.ssh/authorized_keys中。
安装插件Publish Over SSH (opens new window)。进入Manage Jenkins -> System -> Publish over SSH配置插件。
添加凭据 (opens new window)。进入Manage Jenkins -> Credentials添加凭据。
安装插件SSH Agent (opens new window)。创建一个Pipeline项目,添加以下内容,测试远程服务器连接。
pipeline {
agent any
stages {
stage('Login to Server') {
steps {
script {
// sshPrivateKey是上面添加凭据的ID
def sshPrivateKey = "ssh-key"
sshagent(credentials: [sshPrivateKey]) {
sh 'ssh root@192.168.1.31 "ls -al"'
}
}
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Jenkins常用插件
插件是为了适应组织或用户的需求,增强Jenkins环境的功能的主要手段。
插件以及它们的依赖能够自动地从更新中心 (opens new window)下载。更新中心是一个由Jenkins项目运营的服务,它提供了一个开源插件的清单,这些插件是由Jenkins社区的成员共同开发和维护的。
Jenkins 提供了几个不同的的方法在主机上安装插件:
- 在web UI使用 "插件管理器"。
- 使用Jenkins CLI (opens new window)
install-plugin
命令。
管理插件请查阅:https://www.jenkins.io/zh/doc/book/managing/plugins/ (opens new window)
# Role-based Authorization Strategy
在Jenkins管理界面选择“Manage Jenkins” > “Plugins” > “Available Plugins”,然后搜索“Role-based Authorization Strategy (opens new window)”,找到后点击安装即可。
# Pipeline
在Jenkins管理界面选择“Manage Jenkins” > “Plugins” > “Available Plugins”,然后搜索“Pipeline ”,找到后点击安装即可。
# Pipeline Stage View
如果Pipeline项目没有阶段视图,在Jenkins管理界面选择“Manage Jenkins” > “Plugins” > “Available Plugins”,然后搜索“Pipeline Stage View (opens new window)”,找到后点击安装,安装完成后重启服务。
# SSH
搜索SSH
在 Jenkins -> Manage Jenkins -> Manage Credentials 添加凭据
在 Manage Jenkins -> Configure System 添加远程服务器
- 测试
# SSH Pipeline Steps
# Git
在Jenkins管理界面选择“Manage Jenkins” > “Plugins” > “Available Plugins”,然后搜索“Git”,找到后点击安装即可。
# Jenkins全局工具
在Jenkins管理界面选择“Manage Jenkins” > “Tools”
配置JDK
[root@localhost apache-maven-3.9.8]$whereis java java: /usr/bin/java /usr/lib/jvm/jdk-17-oracle-x64/bin/java /usr/share/man/man1/java.1
1
2配置Git
[root@localhost jenkins]$whereis git git: /usr/bin/git /usr/share/man/man1/git.1.gz
1
2配置Maven
在Jenkins管理界面选择“Manage Jenkins” > “System”>"Global properties"
配置JAVA_HOME,JAVA_HOME=/usr/lib/jvm/jdk-17-oracle-x64
配置M2_HOME和PATH+EXTRA,解决Build时mvn: command not found
- M2_HOME=/opt/apache-maven-3.9.8
- PATH+EXTRA=$M2_HOME/bin
# 流水线语法
https://www.jenkins.io/zh/doc/book/pipeline/syntax/ (opens new window)
# agent 指令
agent 指令告诉Jenkins在哪里以及如何执行Pipeline或者Pipeline子集。
agent 指定流水线中的每个阶段都必须在某个地方(物理机,虚拟机或 Docker 容器)执行。
所有的Pipeline都需要 agent 指令。
agent 必须放在pipeline的顶层定义或stage中可选定义,放在stage中就是不同阶段使用不同的agent。
agent 指令更多选项和相关信息,可以查看 语法参考 (opens new window) 。
// agent any 告诉 Jenkins master 任意可用的agent都可以执行
agent any
// 通过标签指定 agent,比如某项目需要在JDK8中环境中构建
agent {
label 'jdk8'
}
agent {
node {
label 'jdk8'
}
}
// node 除了 label 选项,还支持自定义工作目录
agent {
node {
label 'jdk8'
customWorkspace '/var/lib/custom'
}
}
// label 支持过滤多标签
agent {
label 'windows && jdk8'
}
// 指定在Docker镜像中运行
agent {
docker {
image 'finleyma/circleci-nodejs-browser-awscli'
}
}
// 不分配 agent, 这样可以在具体的stages中定义
agent none
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
# Jenkinsfile 示例
https://www.jenkins.io/zh/doc/book/pipeline/jenkinsfile/ (opens new window)
# 免密登录远程服务器执行命令
pipeline {
agent any
parameters {
string(name: 'user', description: '远程主机的用户名',trim: true)
string(name: 'host', description: '远程主机的地址',trim: true)
string(name: 'command', description: '执行远程命令',trim: true)
}
stages {
stage('Execute Command In Remote Server') {
steps {
script {
echo "${params.user}"
echo "${params.host}"
echo "${params.command}"
def sshPrivateKey = "ssh-key"
sshagent(credentials: [sshPrivateKey]) {
sh "ssh ${params.user}@${params.host} \"${params.command}\""
}
}
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 为远程服务器添加公钥
pipeline {
agent any
parameters {
string(name: 'user', description: '远程主机的用户名',trim: true)
string(name: 'host', description: '远程主机的地址',trim: true)
string(name: 'authorizedKeysPath', description: 'authorized_keys路径',trim: true)
string(name: 'publicKey', description: '需要添加的公钥',trim: true)
}
stages {
stage('Add Public Key To Remote Server') {
steps {
script {
def sshPrivateKey = "ssh-key"
def command = "echo \"${params.publicKey}\" >> ${params.authorizedKeysPath}"
sshagent(credentials: [sshPrivateKey]) {
sh "ssh ${params.user}@${params.host} \"${command}\""
}
}
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 构建-执行测试用例-部署
Jenkinsfile (Declarative Pipeline)
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building..'
}
}
stage('Test') {
steps {
echo 'Testing..'
}
}
stage('Deploy') {
steps {
echo 'Deploying....'
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 拉取源码-构建-执行测试用例-部署
Jenkinsfile (Declarative Pipeline)
pipeline {
agent none
stages {
stage('Pull source code') {
steps {
echo 'Pulling..'
}
}
stage('Build') {
agent {
label 'jdk8'
}
steps {
echo 'Building..'
}
}
stage('Test') {
steps {
echo 'Testing..'
}
}
stage('Deploy') {
steps {
echo 'Deploying....'
}
}
}
}
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
# 部署项目
# Jenkinsfile
pipeline {
agent any
environment {
ACTION = "${action}"
ROLLBACK_VERSION = "${rollbackVersion}"
DEPLOY_ONLY = "deploy only"
BACKUP_AND_DEPLOY = "backup and deploy"
ROLLBACK = "rollback"
}
options {
// Pipeline Stage View 不显示 Declarative: Checkout SCM
skipDefaultCheckout(true)
}
stages {
stage('Pull project') {
steps {
script {
if (ACTION == "${DEPLOY_ONLY}" || ACTION == "${BACKUP_AND_DEPLOY}") {
checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: 'e8dd0227-84f1-4158-aaac-846185848fcf', url: 'git@gitlab.example.com:uuap/uuap-api.git']]])
}
}
}
}
stage('Build project') {
steps {
script {
if (ACTION == "${DEPLOY_ONLY}" || ACTION == "${BACKUP_AND_DEPLOY}") {
sh 'mvn clean package'
}
}
}
}
stage('Publish project') {
steps {
script {
if (ACTION == "${DEPLOY_ONLY}" || ACTION == "${BACKUP_AND_DEPLOY}") {
sh 'scp target/*.jar root@192.168.1.17:/opt/webapp/uuap-api-ssh'
}
}
}
}
stage('Start project') {
environment {
WORKSPACE_PATH = '/opt/webapp'
PROJECT_NAME = 'uuap-api-ssh'
LOG_FILE_PATH = "${WORKSPACE_PATH}/${PROJECT_NAME}/log.log"
}
steps {
script {
def remote = [:]
remote.name = 'applicationServer'
remote.host = '192.168.1.17'
remote.allowAnyHosts = true
withCredentials([usernamePassword(credentialsId: '1572b106-87a0-4be9-955d-afcba791d9a4', passwordVariable: 'password', usernameVariable: 'userName')]) {
remote.user = userName
remote.password = password
if (ACTION == "${BACKUP_AND_DEPLOY}") {
sshCommand remote: remote, command: """
cd ${WORKSPACE_PATH}/${PROJECT_NAME}/scripts
./backup.sh ${BUILD_NUMBER}
"""
}
if (ACTION == "${ROLLBACK}") {
sshCommand remote: remote, command: """
cd ${WORKSPACE_PATH}/${PROJECT_NAME}/scripts
./rollback.sh ${ROLLBACK_VERSION}
"""
}
sshCommand remote: remote, command: """
cd ${WORKSPACE_PATH}/${PROJECT_NAME}/scripts
./stop.sh ${ACTION}
"""
sshCommand remote: remote, command: """
cd ${WORKSPACE_PATH}/${PROJECT_NAME}/scripts
./start.sh ${ACTION}
"""
}
}
}
}
}
}
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# 使脚本具有执行权限
chmod +x ./start.sh
chmod +x ./stop.sh
chmod +x ./backup.sh
chmod +x ./rollback.sh
# 执行脚本
./start.sh
./stop.sh
./backup.sh
./rollback.sh
2
3
4
5
6
7
8
9
10
11
# start.sh
#!/bin/bash
JAVA_PATH=/usr/local/jdk1.8.0_321/bin/java
PROJECT_PATH=/opt/webapp/uuap-api-ssh
LOG_FILE_PATH=${PROJECT_PATH}/log.log
file_name="uuap-api.jar"
action=$1
DEPLOY_ONLY="deploy only"
BACKUP_AND_DEPLOY="backup and deploy"
ROLLBACK="rollback"
rollbackResult=false
rollbackResult_path=${workspace_directory}/rollbackResult.txt
cat ${rollbackResult_path}/ | while read line
do
rollbackResult=$line
done
echo "rollbackResult=${rollbackResult}"
cd ${PROJECT_PATH}
if [[ "${action}" == "${DEPLOY_ONLY}" || "${action}" == "${BACKUP_AND_DEPLOY}" || "${action}" == "${ROLLBACK}" && "${rollbackResult}" == "true" ]]; then
nohup ${JAVA_PATH} -jar uuap-api.jar > log.log &
tail -f ${LOG_FILE_PATH} | while read line
do
echo $line
is_startup=`echo $line|grep "Started WebApplication"|wc -l`
if [ $is_startup -eq 1 ] ; then
kill -9 `ps axu|grep "tail -f ${LOG_FILE_PATH}"|grep -v "grep"|awk '{printf $2}'`
fi
done
fi
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
# stop.sh
#!/bin/bash
action=$1
DEPLOY_ONLY="deploy only"
BACKUP_AND_DEPLOY="backup and deploy"
ROLLBACK="rollback"
rollbackResult=false
rollbackResult_path=${workspace_directory}/rollbackResult.txt
cat ${rollbackResult_path}/ | while read line
do
rollbackResult=$line
done
echo "rollbackResult=${rollbackResult}"
pid=`ps -ef | grep uuap-api.jar | grep -v grep | awk '{print $2}'`
if [[ -n "$pid" && (( "${action}" == "${DEPLOY_ONLY}" || "${action}" == "${BACKUP_AND_DEPLOY}" || "${action}" == "${ROLLBACK}" && "${rollbackResult}" == "true" )) ]]; then
kill -9 $pid
fi
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# backup.sh
#!/bin/bash
BUILD_NUMBER=$1
workspace_directory="/opt/webapp/uuap-api-ssh"
backup_directory="/opt/webapp_backup/uuap-api-ssh/${BUILD_NUMBER}"
file_name="uuap-api.jar"
# 如果备份目录不存在,则创建
if [ ! -d ${backup_directory} ]; then
mkdir -p ${backup_directory}
fi
# 将当前构建文件拷贝到备份目录中
if [ -f "${workspace_directory}/${file_name}" ]; then
cp ${workspace_directory}/${file_name} ${backup_directory}/${file_name}
fi
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# rollback.sh
#!/bin/bash
rollbackVersion=$1
workspace_directory="/opt/webapp/uuap-api-ssh"
backup_directory="/opt/webapp_backup/uuap-api-ssh/${rollbackVersion}"
file_name="uuap-api.jar"
rollbackResult_path=${workspace_directory}/rollbackResult.txt
cd ${workspace_directory}
if [ -d ${backup_directory} ]; then
rm -rf uuap-api.jar log.log
cp ${backup_directory}/${file_name} ${workspace_directory}/${file_name}
echo "true" | tee ${rollbackResult_path}
else
echo "rollback version '${rollbackVersion}' is not exist."
echo "false" | tee ${rollbackResult_path}
fi
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 参数化构建
# 部署问题
# nohup: failed to run command ‘java’: No such file or directory
解决方案一:
source /etc/profile
nohup java -jar uuap-api.jar > log.log &
2
解决方案二:
JAVA_PATH=/usr/local/jdk1.8.0_321/bin/java
nohup ${JAVA_PATH} -jar uuap-api.jar > log.log &
2