1 Jenkins分布式
1.1 Jenkins分布式的使用场景
在众多 Job 的场景下,单台 jenkins master 同时执行代码 clone、编译、打包及构建,其性能可能会出现瓶颈从而会影响代码部署效率,jenkins 官方提供了 jenkins 分布式构建,将众多 job 分散运行到不同的 jenkins slave 节点,大幅提高并行 job 的处理能力
1.2 Slave节点的划分
当部署的服务器众多时, 可以考虑使用分布式, Slave节点的划分需要根据业务情况进行划分
根据机房划分: 如果有多个机房, 一般可以在主机房, 部署Jenkins Master, 在每个机房部署两个Jenkins Slave实现高可用, 每个机房的Slave节点只负责本机房的部署
根据业务划分: 当某个业务需要部署的机器众多时, 可以给这个业务单独部署Slave节点, 这些Slave节点只负责该业务的代码部署
1.3 Slave节点的部署
10.0.0.159 jenkins-slave-1
10.0.0.169 jenkins-slave-2
4G 2c
Slave 服务器需要手动创建工作目录,如果 slave 需要执行编译 job,则也需要配置 java 环境并且安装 git、svn、maven 等与 master 相同的基础运行环境,另外也要创建与 master 相同的数据目录,因为脚本中调用的路径都是和master一样的路径,此路径在master 与各 node 节点必须保持一致
1.3.1 各Slave节点安装jdk, git
apt -y install openjdk-8-jdk git
1.3.2 各Slave节点创建/var/lib/jenkins
目录
该目录用来保存jenkins数据, mater节点的目录由安装jenkins包时自动创建, slave节点需要手动创建
mkdir -p /var/lib/jenkins
1.3.3 Master节点添加Slave节点
1.3.3.1 添加slave-1 10.0.0.159
- 保存后, 点击刷新状态, 验证添加成功
- 添加成功后, 会在slave节点启动一个java进程, 该进程是由master节点拷贝过去的jar包启动的
root@jenkins-slave-1:~# find / -name remoting.jar
/var/lib/jenkins/remoting.jar
root@jenkins-slave-1:~# ps aux | grep java
root 7825 5.5 3.8 3478560 154860 ? Ssl 01:42 0:04 java -jar remoting.jar -workDir /var/lib/jenkins -jar-cache /var/lib/jenkins/remoting/jarCache
1.3.3.2 添加slave-1 10.0.0.169
再次添加slave节点, 可以从第一个slave-1进行复制, 只需要修改名称, ip地址, 账号密码等即可
2 Pipeline
官方介绍: https://jenkins.io/2.0/ jenkins 2.X 官方介绍
https://jenkins.io/zh/doc/book/pipeline/ 官方 pipline 示例
pipline 是帮助 Jenkins 实现 CI 到 CD 转变的重要角色,是运行在 jenkins 2.X 版本的核心件,简单来说 Pipline 就是一套运行于 Jenkins 上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂发布流程,从而实现单个任务很难实现的复杂流程编排和任务可视化,Pipeline 的实现方式是一套Groovy DSL,任何发布流程都可以表述为一段 Groovy 脚本
2.1 Pipeline语法
Stage:阶段,一个 pipline 可以划分为若干个 stage,每个 stage 都是一个操作步骤,比如 clone 代码、代码编译、代码测试和代码部署,阶段是一个逻辑分组,可以跨多个 node 执行。
Node:节点,每个 node 都是一个 jenkins 节点,可以是 jenkins master 也可以是jenkins agent,node 是执行 step 的具体服务器。
Step:步骤,step 是 jenkins pipline 最基本的操作单元,从在服务器创建目录到构建容器镜像,由各类 Jenkins 插件提供实现,一个 stage 中可以有多个 step,例如: sh “make”
2.2 Pipeline优势
可持续性:jenkins 的重启或者中断后不影响已经执行的 Pipline Job
支持暂停:pipeline 可以选择停止并等待人工输入或批准后再继续执行
可扩展:通过 groovy 的编程更容易的扩展插件
并行执行:通过 groovy 脚本可以实现 step,stage 间的并行执行,和更复杂的相互依赖
关系
2.3 Pipeline Job测试
2.3.1 创建Pipeline
2.3.2 Hello World流水线示例
2.4 Pipeline案例
Pipeline相比Shell命令的优点就是可以指定构建任务运行在哪个Jenkins服务器上, 只需要按照Pipeline的语法, 把Shell脚本改成Pipeline格式, 并且指定运行在哪个Jenkins节点即可
- Shell脚本执行构建
cd /var/lib/jenkins/workspace/projectA-web1
tar czvf myapp.tar.gz index.html
scp myapp.tar.gz tomcat@10.0.0.199:/data/tomcat/tomcat_zip
scp myapp.tar.gz tomcat@10.0.0.209:/data/tomcat/tomcat_zip
scp myapp.tar.gz tomcat@10.0.0.219:/data/tomcat/tomcat_zip
ssh tomcat@10.0.0.199 "/etc/init.d/tomcat.sh stop"
ssh tomcat@10.0.0.209 "/etc/init.d/tomcat.sh stop"
ssh tomcat@10.0.0.219 "/etc/init.d/tomcat.sh stop"
ssh tomcat@10.0.0.199 "tar xvf /data/tomcat/tomcat_zip/myapp.tar.gz -C /data/tomcat/tomcat_file/myapp/"
ssh tomcat@10.0.0.209 "tar xvf /data/tomcat/tomcat_zip/myapp.tar.gz -C /data/tomcat/tomcat_file/myapp/"
ssh tomcat@10.0.0.219 "tar xvf /data/tomcat/tomcat_zip/myapp.tar.gz -C /data/tomcat/tomcat_file/myapp/"
ssh tomcat@10.0.0.199 "/etc/init.d/tomcat.sh start"
ssh tomcat@10.0.0.209 "/etc/init.d/tomcat.sh start"
ssh tomcat@10.0.0.219 "/etc/init.d/tomcat.sh start"
2.4.1 脚本式Pipeline案例
- 准备工作:
利用Pipeline工具, 生成克隆代码的语法
git credentialsId: '53b5595a-d8c1-4a36-adfd-e80a378da79c', url: 'git@10.0.0.239:qq/web-02.git'
- 步骤1: 先用伪代码写出Pipeline框架
node {
stage("代码clone"){
git credentialsId: '53b5595a-d8c1-4a36-adfd-e80a378da79c', url: 'git@10.0.0.239:qq/web-02.git'
}
stage("代码测试"){
echo "代码测试"
}
stage("代码编译"){
echo "代码编译"
}
stage("服务停止"){
echo "服务停止"
}
stage("代码copy"){
echo "代码copy"
}
stage("服务启动"){
echo "服务启动"
}
}
- 步骤2: 指定构建的运行节点
node("节点标签"){
}
node ("jenkins-slave-1-10.0.0.159") {
stage("代码clone"){
git credentialsId: '53b5595a-d8c1-4a36-adfd-e80a378da79c', url: 'git@10.0.0.239:qq/web-02.git'
}
stage("代码测试"){
echo "代码测试"
}
stage("代码编译"){
echo "代码编译"
}
stage("服务停止"){
echo "服务停止"
}
stage("代码copy"){
echo "代码copy"
}
stage("服务启动"){
echo "服务启动"
}
}
验证构建运行在slave-1
代码克隆到workspace目录下, 如果想自定义代码克隆的目录, 那么就要手动使用git命令去克隆, 不能用流水线语法生成的命令克隆
root@jenkins-slave-1:~# ll /var/lib/jenkins/workspace/
total 16
drwxr-xr-x 4 root root 4096 Jul 6 15:40 ./
drwxr-xr-x 4 root root 4096 Jul 6 15:40 ../
drwxr-xr-x 3 root root 4096 Jul 6 15:40 pipeline-test1/
drwxr-xr-x 2 root root 4096 Jul 6 15:40 'pipeline-test1@tmp'/
- 步骤3: 代码打包
node ("jenkins-slave-1-10.0.0.159") {
stage("代码clone"){
sh "cd /var/lib/jenkins/workspace/pipeline-test1 && rm -rf ./*" # 克隆代码前, 把原有的代码删除
git credentialsId: '53b5595a-d8c1-4a36-adfd-e80a378da79c', url: 'git@10.0.0.239:qq/web-02.git'
sh "echo 代码克隆完成"
}
stage("代码打包"){
sh "cd /var/lib/jenkins/workspace/pipeline-test1" # 进到workspace
sh "tar czvf myapp.tar.gz ./index.html" # 把代码打包
sh "echo 代码打包完成"
}
stage("代码编译"){
echo "代码编译"
}
stage("服务停止"){
echo "服务停止"
}
stage("代码copy"){
echo "代码copy"
}
stage("服务启动"){
echo "服务启动"
}
}
验证打包成功
root@jenkins-slave-1:/var/lib/jenkins/workspace/pipeline-test1# ls
index.html myapp.tar.gz
- 步骤4: 在slave-1节点上, 把myapp.tar.gz包拷贝到后端的三个tomcat服务器
slave-1节点需要对tomcat服务器做免密认证
root@jenkins-slave-1:~# ssh-keygen # slave节点用root启动的remoting.jar程序, 因此, 生成root用户的公钥
root@jenkins-slave-1:~# ssh-copy-id tomcat@10.0.0.199 # tomcat服务器用的tomcat账号启动tomcat, 因此, 公钥要复制到tomcat用户下
root@jenkins-slave-1:~# ssh-copy-id tomcat@10.0.0.209
root@jenkins-slave-1:~# ssh-copy-id tomcat@10.0.0.219
node ("jenkins-slave-1-10.0.0.159") {
stage("代码clone"){
sh "cd /var/lib/jenkins/workspace/pipeline-test1 && rm -rf ./*"
git credentialsId: '53b5595a-d8c1-4a36-adfd-e80a378da79c', url: 'git@10.0.0.239:qq/web-02.git'
sh "echo 代码克隆完成"
}
stage("代码打包"){
sh "cd /var/lib/jenkins/workspace/pipeline-test1"
sh "tar czvf myapp.tar.gz ./index.html"
sh "echo 代码打包完成"
}
stage("服务停止"){
sh "ssh tomcat@10.0.0.199 '/etc/init.d/tomcat.sh stop'"
sh "ssh tomcat@10.0.0.209 '/etc/init.d/tomcat.sh stop'"
sh "ssh tomcat@10.0.0.219 '/etc/init.d/tomcat.sh stop'"
}
stage("代码copy"){
sh "scp /var/lib/jenkins/workspace/pipeline-test1/myapp.tar.gz tomcat@10.0.0.199:/data/tomcat/tomcat_zip"
sh "scp /var/lib/jenkins/workspace/pipeline-test1/myapp.tar.gz tomcat@10.0.0.209:/data/tomcat/tomcat_zip"
sh "scp /var/lib/jenkins/workspace/pipeline-test1/myapp.tar.gz tomcat@10.0.0.219:/data/tomcat/tomcat_zip"
}
stage("服务启动"){
echo "服务启动"
}
}
root@tomcat-1:~# ll /data/tomcat/tomcat_zip
total 12
drwxr-xr-x 2 tomcat tomcat 4096 Jul 4 23:10 ./
drwxr-xr-x 5 tomcat tomcat 4096 Jul 3 00:19 ../
-rw-r--r-- 1 tomcat tomcat 263 Jul 6 16:12 myapp.tar.gz
- 步骤5: 代码解压替换, 服务启动
node ("jenkins-slave-1-10.0.0.159") {
stage("代码clone"){
sh "cd /var/lib/jenkins/workspace/pipeline-test1 && rm -rf ./*"
git credentialsId: '53b5595a-d8c1-4a36-adfd-e80a378da79c', url: 'git@10.0.0.239:qq/web-02.git'
sh "echo 代码克隆完成"
}
stage("代码打包"){
sh "cd /var/lib/jenkins/workspace/pipeline-test1"
sh "tar czvf myapp.tar.gz ./index.html"
sh "echo 代码打包完成"
}
stage("服务停止"){
sh "ssh tomcat@10.0.0.199 '/etc/init.d/tomcat.sh stop'"
sh "ssh tomcat@10.0.0.209 '/etc/init.d/tomcat.sh stop'"
sh "ssh tomcat@10.0.0.219 '/etc/init.d/tomcat.sh stop'"
}
stage("代码copy"){
sh "scp /var/lib/jenkins/workspace/pipeline-test1/myapp.tar.gz tomcat@10.0.0.199:/data/tomcat/tomcat_zip"
sh "scp /var/lib/jenkins/workspace/pipeline-test1/myapp.tar.gz tomcat@10.0.0.209:/data/tomcat/tomcat_zip"
sh "scp /var/lib/jenkins/workspace/pipeline-test1/myapp.tar.gz tomcat@10.0.0.219:/data/tomcat/tomcat_zip"
}
stage("代码解压替换"){
sh "ssh tomcat@10.0.0.199 'rm -rf /data/tomcat/tomcat_file/myapp/* && tar xvf /data/tomcat/tomcat_zip/myapp.tar.gz -C /data/tomcat/tomcat_file/myapp'"
sh "ssh tomcat@10.0.0.209 'rm -rf /data/tomcat/tomcat_file/myapp/* && tar xvf /data/tomcat/tomcat_zip/myapp.tar.gz -C /data/tomcat/tomcat_file/myapp'"
sh "ssh tomcat@10.0.0.219 'rm -rf /data/tomcat/tomcat_file/myapp/* && tar xvf /data/tomcat/tomcat_zip/myapp.tar.gz -C /data/tomcat/tomcat_file/myapp'"
}
stage("服务启动"){
sh "ssh tomcat@10.0.0.199 '/etc/init.d/tomcat.sh start'"
sh "ssh tomcat@10.0.0.209 '/etc/init.d/tomcat.sh start'"
sh "ssh tomcat@10.0.0.219 '/etc/init.d/tomcat.sh start'"
}
}
先提交一个新版本v11到master分支, 然后测试构建结果
- 如果需要部署开发分支的代码, 只需要用流水线语法生成开发分支的拉取命令
- 这样就完成了通过脚本式流水线, 指定构建jenkins节点, 将代码部署到指定的服务器
2.4.2 利用Jenkinsfile来做代码部署
将部署脚本, 写到Jenkinsfile中, 上传到gitlab上, 通过gitlab上的Jenkinsfile来完成部署
避免人为把jenkins上写的部署脚本修改, 导致部署失败
- 步骤1: 将代码写到Jenkinsfile, 上传到gitlab
root@git-client:~# cd /opt
root@git-client:/opt# cd web-02/
root@git-client:/opt/web-02# vim Jenkinsfile
root@git-client:~# cd /opt
root@git-client:/opt# cd web-02/
root@git-client:/opt/web-02# vim Jenkinsfile
root@git-client:/opt/web-02# git branch
develop
* master
root@git-client:/opt/web-02# git add .
root@git-client:/opt/web-02# git commit -m "commit Jenkinsfile"
[master a57882c] commit Jenkinsfile
1 file changed, 50 insertions(+)
create mode 100644 Jenkinsfile
root@git-client:/opt/web-02# git push -u origin
Username for 'http://10.0.0.239': developer-01
Password for 'http://developer-01@10.0.0.239':
Counting objects: 3, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 763 bytes | 763.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To http://10.0.0.239/qq/web-02.git
8f5052d..a57882c master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
- 步骤2: 修改构建配置
- 步骤3: 项目构建
这时在构建时, Jenkins会克隆gitlab里的Jenkinsfile, 做解析, 然后执行. 这个文件会随着开发的项目代码一起提交, 开发结束后, 会修改Jenkinsfile一并提交到gitlab, 之后只需要在Jenkins上构建即可
2.4.3 声明式Pipeline案例
pipeline{
agent { label "jenkins-slave-1-10.0.0.159" }
stages {
stage("代码clone"){
steps{
sh "cd /var/lib/jenkins/workspace/pipeline-test1 && rm -rf ./*"
git credentialsId: '53b5595a-d8c1-4a36-adfd-e80a378da79c', url: 'git@10.0.0.239:qq/web-02.git'
sh "echo 代码克隆完成"
}
}
stage("代码打包"){
steps{
sh "cd /var/lib/jenkins/workspace/pipeline-test1"
sh "tar czvf myapp.tar.gz ./index.html"
sh "echo 代码打包完成"
}
}
stage("服务停止"){
steps{
sh "ssh tomcat@10.0.0.199 '/etc/init.d/tomcat.sh stop'"
sh "ssh tomcat@10.0.0.209 '/etc/init.d/tomcat.sh stop'"
sh "ssh tomcat@10.0.0.219 '/etc/init.d/tomcat.sh stop'"
}
}
stage("代码copy"){
steps{
sh "scp /var/lib/jenkins/workspace/pipeline-test1/myapp.tar.gz tomcat@10.0.0.199:/data/tomcat/tomcat_zip"
sh "scp /var/lib/jenkins/workspace/pipeline-test1/myapp.tar.gz tomcat@10.0.0.209:/data/tomcat/tomcat_zip"
sh "scp /var/lib/jenkins/workspace/pipeline-test1/myapp.tar.gz tomcat@10.0.0.219:/data/tomcat/tomcat_zip"
}
}
stage("代码解压替换"){
steps{
sh "ssh tomcat@10.0.0.199 'rm -rf /data/tomcat/tomcat_file/myapp/* && tar xvf /data/tomcat/tomcat_zip/myapp.tar.gz -C /data/tomcat/tomcat_file/myapp'"
sh "ssh tomcat@10.0.0.209 'rm -rf /data/tomcat/tomcat_file/myapp/* && tar xvf /data/tomcat/tomcat_zip/myapp.tar.gz -C /data/tomcat/tomcat_file/myapp'"
sh "ssh tomcat@10.0.0.219 'rm -rf /data/tomcat/tomcat_file/myapp/* && tar xvf /data/tomcat/tomcat_zip/myapp.tar.gz -C /data/tomcat/tomcat_file/myapp'"
}
}
stage("服务启动"){
steps{
sh "ssh tomcat@10.0.0.199 '/etc/init.d/tomcat.sh start'"
sh "ssh tomcat@10.0.0.209 '/etc/init.d/tomcat.sh start'"
sh "ssh tomcat@10.0.0.219 '/etc/init.d/tomcat.sh start'"
}
}
}
}
提交一个新版本v13, 测试构建
- 之后, 可以把声明式脚本写入到Jenkinsfile里, 和每个版本的代码一起提交, 如果需要回滚, 那么每次回滚后, 直接构建即可, 无需修改Jenkinsfile, 因为每个版本的Jenkinsfile都是和代码一起的.