Devops之基于Jenkins的CI与CD

作者:Carey

 

一、DevOps 简介

Gdevops:http://dbaplus.cn/

DevOps 是 Development 和 Operations 的组合,也就是开发和运维的简写。

DevOps 是针对企业中的研发人员、运维人员和测试人员的工作理念,是他们在应用开发、代码部署和质量测试等整条生命周期中协作和沟通的最佳实践,DevOps 强调 整个组织的合作以及交付和基础设施变更的自动化、从而实现持续集成、持续部署和持续交付。

DevOps 四大平台:代码托管(gitlab/svn)、项目管理(jira)、运维平台(腾讯蓝鲸/开源平台)、持续交付(Jenkins/gitlab)

1.1 什么是 DevOps

devops集成工具Jenkins devops与jenkins_服务器

1.2 为什么要推广 DevOps

DevOps 强调团队协作、相互协助、持续发展,然而传统的模式是开发人员只顾开发 程序,运维只负责基础环境管理和代码部署及监控等,其并不是为了一个共同的目标 而共同实现最终的目的,而 DevOps 则实现团队作战,即无论是开发、运维还是测试, 都为了最终的代码发布、持续部署和业务稳定而付出各自的努力,从而实现产品设计、 开发、测试和部署的良性循环,实现产品的最终持续交付。

1.3 DevOps 技术团队

devops集成工具Jenkins devops与jenkins_服务器_02

1.4 什么是持续集成(CI-Continuous integration)

持续集成是指多名开发者在开发不同功能代码的过程当中,可以频繁的将代码行合并 到一起并切相互不影响工作。

1.5 什么是持续部署(CD-continuous deployment)

是基于某种工具或平台实现代码自动化的构建、测试和部署到线上环境以实现交付高 质量的产品,持续部署在某种程度上代表了一个开发团队的更新迭代速率。

1.6 什么是持续交付(Continuous Delivery)

持续交付是在持续部署的基础之上,将产品交付到线上环境,因此持续交付是产品价 值的一种交付,是产品价值的一种盈利的实现。

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_03

1.7 常见的部署方式

开发自己上传--最原始的方案

开发给运维手动上传--运维自己手动部署

运维使用脚本复制--半自动化

结合 web 界面一键部署--自动化

1.8 常见的持续集成开源工具

在公司的服务器安装某种程序,该程序用于按照特定格式和方式记录和保存公司多名 开发人员不定期提交的源代码,且后期可以按照某种标记及方式对用户提交的数据进行还原。

1.8.1 CVS(Concurrent Version System)

早期的集中式版本控制系统,现已基本淘汰

会出现数据提交后不完整的情况

1.8.2 SVN(Subversion)--集中式版本控制系统

2000 年开始开发,目标就是替代 CVS 集中式管理,依赖于网络,一台服务器集中管 理目前依然有部分公司在使用

1.8.3 Gitlib—分布式版本控制系统

Linus 在 1991 年创建了开源的 Linux 内核,从此 Linux 便不断快速发展,不过 Linux 的壮大是离不开全世界的开发者的参与,这么多人在世界各地为 Linux 编写代码,那 Linux 内核的代码是如何管理的呢?事实是,在 2002 年以前,世界各地的志愿者把源 代码文件通过 diff 的方式发给 Linus,然后由 Linus 本人通过手工方式合并代码!你也许会想,为什么 Linus 不把 Linux 代码放到版本控制系统里呢?不是有 CVS、SVN 这些 免费的版本控制系统吗?因为 Linus 坚定地反对 CVS 和 SVN,这些集中式的版本控制 系统不但速度慢,且必须联网才能使用,但是也有一些商用的版本控制系统,虽然比 CVS、SVN 好用,但那是付费的,和 Linux 的开源精神不符,不过,到了 2002 年,Linux 系统已经发展了十年了,代码库之大让 Linus 很难继续通过手工方式管理了,社区的 弟兄们也对这种方式表达了强烈不满,于是 Linus 选择了一个商业的版本控制系统 BitKeeper,BitKeeper 的东家 BitMover 公司出于人道主义精神,授权 Linux 社区免费使用这个版本控制系统,但是安定团结的大好局面在 2005 年就被打破了,原因是 Linux 社区牛人聚集,不免沾染了一些梁山好汉的江湖习气,开发 Samba 的 Andrew 试图破 解 BitKeeper 的协议(这么干的其实也不只他一个),被 BitMover 公司发现了(监控 工作做得不错!),于是 BitMover 公司怒了,要收回 Linux 社区的免费使用权,这时候 其实 Linus 可以向 BitMover 公司道个歉,保证以后严格管教弟兄们,但这是不可能的, 而且实际情况是 Linus 自己花了两周时间自己用 C 写了一个分布式版本控制系统,这 就是 Git!一个月之内,Linux 内核的源码已经由 Git 管理了!牛是怎么定义的呢?大 家可以体会一下,然后 Git 迅速成为最流行的分布式版本控制系统,尤其是 2008 年, GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub, 包括 jQuery,PHP,Ruby 等等。

1.9 版本控制系统分类

1.9.1 集中式版本控制系统

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_04

任何的提交和回滚都依赖于连接服务器 SVN 服务器是单点

1.9.2 分布式版本控制系统

Git 在每个用户都有一个完整的服务器,然后在有一个中央服务器,用户可以先将代码提交到本地,没有网络也可以先提交到本地,然后在有网络的时候再提交到中央服务器,这样就大大方便了开发者的代码提交和回滚,而相比 CVS 和 SVN 都是集中式 的版本控制系统,工作的时候需要先从中央服务器获取最新的代码,改完之后需要提 交,如果是一个比较大的文件则需要足够快的网络才能快速提交完成,而使用分布式 的版本控制系统,每个用户都是一个完整的版本库,即使没有中央服务器也可以提交代码或者回滚,最终再把改好的代码提交至中央服务器进行合并即可。

devops集成工具Jenkins devops与jenkins_git_05

二、Gitlab 部署与使用

Gitlab 服务的安装文档:https://about.gitlab.com/install/

安装环境要求:https://docs.gitlab.com/ce/install/requirements.html

2.1 下载并部署 gitlab

2.1.1 gitlab 安装及使用

安装包下载地址:https://packages.gitlab.com/gitlab/gitlab-ce

rpm 包国内下载地址:https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/

ubuntu 国内下载地址:https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/ubuntu/pool/

2.1.2 下载安装gitlab

#这里事先从清华源下载最新版gitlab
#内存最少4G
[19:50:42 root@gitlab ~]#free -h
              total        used        free      shared  buff/cache   available
Mem:           3.8G        218M        2.2G         12M        1.4G        3.4G
Swap:          1.9G          0B        1.9G
[19:50:47 root@gitlab ~]#ls
gitlab-ce_13.11.3-ce.0_amd64.deb
#安装
[19:51:48 root@gitlab ~]#apt install ./gitlab-ce_13.11.3-ce.0_amd64.deb

2.1.3 gitlab 配置使用

[19:53:58 root@gitlab ~]#grep "^[a-Z]" /etc/gitlab/gitlab.rb
external_url 'http://192.168.10.185'  #修改这里为本机的IP

可选的配置邮件通知

gitlab_rails['smtp_enable'] = true     #是否开启
gitlab_rails['smtp_address'] = "smtp.qq.com"   #smtp服务器
gitlab_rails['smtp_port'] = 465      #访问端口
gitlab_rails['smtp_user_name'] = "2973707860@qq.com" #登录用户
gitlab_rails['smtp_password'] = "zdhudwexecjbdhda"   #授权码
gitlab_rails['smtp_domain'] = "qq.com"               #后缀
gitlab_rails['smtp_authentication'] = :login         
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = true
gitlab_rails['gitlab_email_from'] = "2973707860@qq.com" #用户
user["git_user_email"] = "2973707860@qq.com"  #登录用户

2.1.4 初始化服务

执行配置并启动服务

#头一次加载服务比较慢,确保内存大于4G
[19:54:12 root@gitlab ~]#gitlab-ctl reconfigure
Running handlers:
Running handlers complete
Chef Infra Client finished, 586/1555 resources updated in 04 minutes 07 seconds

Notes:
It seems you haven't specified an initial root password while configuring the GitLab instance.
On your first visit to  your GitLab instance, you will be presented with a screen to set a
password for the default admin account with username `root`.

gitlab Reconfigured!
#启动完成

2.1.5 gitlab 相关的目录有哪些

/etc/gitlab #配置文件目录
/run/gitlab #运行 pid 目录
/opt/gitlab #安装目录
/var/opt/gitlab #数据目录
/var/log/gitlab #日志目录

2.1.6 常用命令

gitlab-rails #用于启动控制台进行特殊操作,比如修改管理员密码、打开数据库控制台( gitlab-rails dbconsole)等
gitlab-psql  #数据库命令行
gitlab-rake #数据备份恢复等数据操作
gitlab-ctl #客户端命令行操作行
gitlab-ctl stop #停止 gitlab
gitlab-ctl start #启动 gitlab
gitlab-ctl restar #重启 gitlab
gitlab-ctl status #查看组件运行状态
gitlab-ctl tail nginx #查看某个组件的日志

2.1.7 验证 gitlab 启动完成

[20:02:45 root@gitlab ~]#gitlab-ctl status
run: alertmanager: (pid 13266) 116s; run: log: (pid 12875) 186s
run: gitaly: (pid 13229) 119s; run: log: (pid 12415) 323s
run: gitlab-exporter: (pid 13240) 118s; run: log: (pid 12801) 204s
run: gitlab-workhorse: (pid 13204) 119s; run: log: (pid 12720) 224s
run: grafana: (pid 13357) 115s; run: log: (pid 13147) 139s
run: logrotate: (pid 12230) 338s; run: log: (pid 12251) 335s
run: nginx: (pid 12734) 219s; run: log: (pid 12744) 217s
run: node-exporter: (pid 13216) 119s; run: log: (pid 12781) 212s
run: postgres-exporter: (pid 13350) 116s; run: log: (pid 13039) 180s
run: postgresql: (pid 12459) 319s; run: log: (pid 12477) 316s
run: prometheus: (pid 13252) 118s; run: log: (pid 12844) 192s
run: puma: (pid 12664) 237s; run: log: (pid 12671) 236s
run: redis: (pid 12266) 332s; run: log: (pid 12291) 329s
run: redis-exporter: (pid 13242) 118s; run: log: (pid 12824) 198s
run: sidekiq: (pid 12682) 231s; run: log: (pid 12693) 230s
#验证端口及状态
#80 端口是在初始化 gitlib 的时候启动的,因此如果之前的有程序占用会导致初始化失败或无法访问!
[20:04:40 root@gitlab ~]#lsof -i:80
COMMAND   PID       USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx   12734       root    7u  IPv4  79016      0t0  TCP *:http (LISTEN)
nginx   12735 gitlab-www    7u  IPv4  79016      0t0  TCP *:http (LISTEN)
nginx   12736 gitlab-www    7u  IPv4  79016      0t0  TCP *:http (LISTEN)

2.1.8 登录 gitlab web 界面

devops集成工具Jenkins devops与jenkins_git_06

2.1.9 创建一个测试项目

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_07

2.2 git客户端

2.2.1 git 客户端测试 web1项目

#克隆项目
[20:13:27 root@gitlab ~]#git clone http://192.168.10.185/root/web1.git
[20:14:01 root@gitlab ~]#tree web1/
web1/
└── index.html
[20:14:17 root@gitlab ~]#cd web1/
[20:14:42 root@gitlab web1]#cat index.html 
<h1>zhangzhuo</h1>
#编辑文件并测试提交
[20:15:27 root@gitlab web1]#cat index.html
<h1>zhangzhuo</h1>
<h1>v11111111</h1>
[20:15:30 root@gitlab web1]#git config --global user.name "zhangzhuo"
[20:15:55 root@gitlab web1]#git config --global user.email "1191400158@qq.com"
[20:16:09 root@gitlab web1]#git add index.html 
[20:16:22 root@gitlab web1]#git commit -m "v1"
[master a8154b5] v1
 1 file changed, 1 insertion(+)
 #提交
[20:16:44 root@gitlab web1]#git push 
Username for 'http://192.168.10.185': root
Password for 'http://root@192.168.10.185': 
Counting objects: 3, done.
Writing objects: 100% (3/3), 259 bytes | 259.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To http://192.168.10.185/root/web1.git
   c0126a0..a8154b5  master -> master

git web 端验证数据

devops集成工具Jenkins devops与jenkins_服务器_08

2.2.2 gitlab数据保方式

SVN 与 CVS

每次提交的文件都单独保存,即按照文件的提交时间区分不同的版本,保存至不同的逻辑存储区域,后期恢复的时候直接基于之前版本恢复。

devops集成工具Jenkins devops与jenkins_服务器_09

Gitlab

Gitlab 与 SVN 的数据保存方式不一样,gitlab 虽然也会在内部对数据进行逻辑划分保存,但是当后期提交的数据如果和之前提交过的数据没有变化,其就直接快照之前的文件,而不是在将文件重新上传一份在保存一遍,这样既节省了空间又加快了代码提交速度。

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_10

2.2.4 常用 git 命令

使用 git 命令下载代码与提交代码等操作

devops集成工具Jenkins devops与jenkins_git_11

#如果要提交代码这个必须设置
git config --global user.name "name"  #设置全局用户名
git config --global user.email "xxx@xx.com"  #设置全局邮箱
git config --global --list #列出用户全局设置

git add index.html  #添加指定文件、目录或当前目录下所有数据到暂存区
git commit -m “11“  #提交文件到工作区
git status          #查看工作区的状态
git push            #提交代码到服务器
git pull            #获取代码到本地
git log             #查看操作日志
vim .gitignore      #定义忽略文件上传至 gitlab
git reset --hard HEAD^^ #git 版本回滚, HEAD 为当前版本,加一个^为上一个,^^为上上一个版本
git reflog # #获取每次提交的 ID,可以使用--hard 根据提交的 ID 进行版本回退
git reset --hard 5ae4b06 #回退到指定 id 的版本
git branch              #查看当前所处的分支
git checkout -b develop #创建并切换到一个新分支
git checkout develop    #切换分支

2.2.5 git 缓存区与工作区等概念

工作区:clone 的代码或者开发自己编写的代码文件所在的目录,通常是代码所在的一 个服务的目录名称。

暂存区:用于存储在工作区中对代码进行修改后的文件所保存的地方,使用 git add 添加。

本地仓库:用于提交存储在工作区和暂存区中改过的文件地方,使用 git commit 提交

远程仓库:多个开发共同协作提交代码的仓库,即 gitlab 服务器

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_12

2.3 gitlab 数据备份恢复

2.3.1 停止 gitlab 数据服务

[20:35:11 root@gitlab ~]#gitlab-ctl stop sidekiq
ok: down: sidekiq: 0s, normally up
[20:35:56 root@gitlab ~]#gitlab-ctl stop unicorn

2.3.2 手动备份数据

[20:38:46 root@gitlab ~]#gitlab-rake gitlab:backup:create  #在任意目录即可备份当前gitlab 数据
[20:39:23 root@gitlab ~]#gitlab-ctl start #备份完成后启动 gitlab
Deleting old backups ... skipping
Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data 
and are not included in this backup. You will need these files to restore a backup.
Please back them up manually.
#这个表示gitlab.rb and gitlab-secrets.json文件需要手动自己备份

2.3.3 查看要恢复的文件

/var/opt/gitlab/backups/ # Gitlab 数据备份目录,需要使用命令备份的
/var/opt/gitlab/nginx/conf  #nginx 配置文件
/etc/gitlab/gitlab.rb       #gitlab 配置文件
/etc/gitlab/gitlab-secrets.json  #key 文件
[20:43:42 root@gitlab ~]#ll /var/opt/gitlab/backups/
total 288
drwx------  2 git  root   4096 May 14 20:41 ./
drwxr-xr-x 21 root root   4096 May 14 20:02 ../
-rw-------  1 git  git  286720 May 14 20:41 1620996079_2021_05_14_13.11.3_gitlab_backup.tar

2.3.4 执行恢复

#先删除一些数据
在web页面把web1项目删除
#恢复数据之前先停止一些服务
[20:50:15 root@gitlab ~]#gitlab-ctl stop sidekiq
ok: down: sidekiq: 0s, normally up
[20:50:31 root@gitlab ~]#gitlab-ctl stop puma
ok: down: puma: 0s, normally up
[20:52:54 root@gitlab ~]#gitlab-ctl stop unicorn
#恢复数据
[20:55:03 root@gitlab ~]#gitlab-rake gitlab:backup:restore BACKUP=1620996079_2021_05_14_13.11.3
#BACKUP=备份文件名
备份文件名=unix时间戳_年_月_日_gitlab版本号

2.3.5 启动服务

[20:57:53 root@gitlab ~]#gitlab-ctl restart

2.3.6 查看web1项目是否恢复

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_13

2.4 常见的代码部署方式

2.4.1 蓝绿部署

蓝绿部署指的是不停老版本代码(不影响上一个版本访问),而是在另外一套环境部署新 版本然后进行测试,测试通过后将用户流量切到新版本,其特点为业务无中断,升级风险相对较小。

具体过程

1. 当前版本业务正常访问(V1)
2. 在另外一套环境部署新代码(V2),代码可能是增加了功能或者是修复了某些 bug
3. 测试通过之后将用户请求流量切到新版本环境
4. 观察一段时间,如有异常直接切换旧版本
5. 下次升级,将旧版本升级到新版本(V3)

蓝绿部署适用的场景

1. 不停止老版本,额外部署一套新版本,等测试发现新版本 OK 后,删除老版本。
2. 蓝绿发布是一种用于升级与更新的发布策略,部署的最小维度是容器,而发布的最小维度是应
用。
3. 蓝绿发布对于增量升级有比较好的支持,但是对于涉及数据表结构变更等等不可逆转的升级,
并不完全合适用蓝绿发布来实现,需要结合一些业务的逻辑以及数据迁移与回滚的策略才可以完全
满足需求。

devops集成工具Jenkins devops与jenkins_git_14

2.4.2 金丝雀发布

金丝雀发布也叫灰度发布,是指在黑与白之间,能够平滑过渡的一种发布方式,灰度发 布是增量发布的一种类型,灰度发布是在原有版本可用的情况下,同时部署一个新版本 应用作为“金丝雀”(小白鼠),测试新版本的性能和表现,以保障整体系统稳定的情况 下,尽早发现、调整问题。

金丝雀发布、灰度发布步骤组成

1. 准备好部署各个阶段的工件,包括:构建工件,测试脚本,配置文件和部署清单文件。
2. 从负载均衡列表中移除掉“金丝雀”服务器。
3. 升级“金丝雀”应用(排掉原有流量并进行部署)。
4. 对应用进行自动化测试。
5. 将“金丝雀”服务器重新添加到负载均衡列表中(连通性和健康检查)。
6. 如果“金丝雀”在线使用测试成功,升级剩余的其他服务器。(否则就回滚)灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。

灰度发布/金丝雀部署适用的场景

1. 不停止老版本,额外搞一套新版本,不同版本应用共存。
2. 灰度发布中,常常按照用户设置路由权重,例如 90%的用户维持使用老版本,10%的用户尝鲜新版本。
3. 经常与 A/B 测试一起使用,用于测试选择多种方案。

2.4.3 滚动发布

滚动发布,一般是取出一个或者多个服务器停止服务,执行更新,并重新将其投入使用。 周而复始,直到集群中所有的实例都更新成新版本。

2.4.4 A/B 测试

A/B 测试也是同时运行两个 APP 环境,但是蓝绿部署完全是两码事,A/B 测试是用来测 试应用功能表现的方法,例如可用性、受欢迎程度、可见性等等,蓝绿部署的目的是安 全稳定地发布新版本应用,并在必要时回滚,即蓝绿部署是一套正式环境环境在线,而 A/B 测试是两套正式环境在线。

三、部署web服务器环境

devops集成工具Jenkins devops与jenkins_git_15

3.1 tomcat服务器

#安装tomcat略过
#文件准备
[11:53:47 root@tomcat1 ~]#mkdir /data/tomcat/{tomcat_appdir,tomcat_webdir} -p
/data/tomcat/tomcat_appdir -p #保存 web 压缩包
/data/tomcat/tomcat_webdir    #保存解压后的 web 目录
/usr/local/tomcat/webapps/   #tomcat app 加载目录,在 server.xml 定义
/usr/local/tomcat/webapps/app/ #Java 代码目录
[11:56:37 root@tomcat2 ~]#echo `hostname` >/usr/local/tomcat/webapps/app/index.html
#启动
[11:56:58 root@tomcat2 ~]#systemctl enable --now tomcat
#测试
[11:57:34 root@tomcat2 ~]#curl 127.0.0.1:8080/app/
tomcat2.zhangzhuo.org

3.2 keepalived服务准备

#修改配置文件
vrrp_instance VI_1 {
    state MASTER   
    interface eth0 
    virtual_router_id 80
    priority 100    
    advert_int 1    
    authentication {
        auth_type PASS
        auth_pass 1111 
   }
   unicast_src_ip 127.0.0.1
   unicast_peer{
       127.0.0.1
   }
    virtual_ipaddress {
        192.168.10.100/24 dev eth0 label eth0:1 
    }
}
#启动服务
[14:17:34 root@haproxy ~]#systemctl enable --now keepalived

3.3 haproxy服务准备

#修改配置文件
listen stats
	mode http
	bind 0.0.0.0:9999
	stats enable      
	log global
    stats hide-version
	stats uri /haproxy-status
	stats auth haadmin:123456
    stats refresh 5s
    stats admin if TRUE
listen web_host_staticrr
    bind 0.0.0.0:80
    mode http
    log global
    balance static-rr
    option forwardfor
    server tomcat1 192.168.10.183:8080 weight 1 check inter 3000 fall 3 rise 5
    server tomcat2 192.168.10.184:8080 weight 1 check inter 3000 fall 3 rise 5
#启动
[14:20:42 root@haproxy HAProxy]#systemctl enable --now haproxy.service 
Created symlink /etc/systemd/system/multi-user.target.wants/haproxy.service → /lib/systemd/system/haproxy.service.

3.4 测试

[14:22:45 root@haproxy HAProxy]#curl 192.168.10.100/app/
tomcat2.zhangzhuo.org
[14:22:52 root@haproxy HAProxy]#curl 192.168.10.100/app/
tomcat1.zhangzhuo.org

四、Jenkins部署与基础配置

官方网站:https://jenkins.io/zh/

4.1 配置 java 环境并部署jenkins

4.1.1 java 环境配置

JDK版本需要去官网查看对应jenkins的版本需要什么版本的JDK

#这里安装JDK1.8
[14:30:34 root@jenkins ~]#ls
jdk-8u281-linux-x64.tar.gz
[14:30:37 root@jenkins ~]#mv jdk-8u281-linux-x64.tar.gz /usr/local/src/
[14:30:58 root@jenkins ~]#cd /usr/local/src/
[14:31:12 root@jenkins src]#tar xf jdk-8u281-linux-x64.tar.gz 
[14:31:23 root@jenkins src]#ln -s jdk1.8.0_281 /usr/local/jdk
#修改环境变量
[14:32:40 root@jenkins src]#vim /etc/profile

#退出终端重新登录测试
[14:36:46 root@jenkins ~]#java -version
java version "1.8.0_281"
Java(TM) SE Runtime Environment (build 1.8.0_281-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.281-b09, mixed mode)

4.1.2 启动 Jenkins

国内的源下载地址:https://mirrors.tuna.tsinghua.edu.cn/jenkins/

4.1.2.1 通过 jar 包直接启动 jenkins
#配置hosts
[14:48:37 root@jenkins ~]#vim /etc/hosts
192.168.10.181 jenkins.zhangzhuo.org
#这里下载war包启动
[14:51:02 root@jenkins ~]#java \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=12345 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-Djava.rmi.server.hostname="192.168.10.181" \
-jar jenkins.war &\
4.1.2.2 使用tomcat安装
#安装tomcat,这里tomcat必须以root身份运行
[14:54:20 root@jenkins webapps]#mkdir jenkins
[14:54:44 root@jenkins webapps]#mv jenkins.war jenkins
[14:56:15 root@jenkins jenkins]#unzip jenkins.war 
[14:56:15 root@jenkins jenkins]#rm jenkins.war
[15:00:03 root@jenkins jenkins]#chown -R tomcat: /usr/local/tomcat/
#启动tomcat
[15:02:15 root@jenkins ~]#systemctl restart tomcat.service
4.1.2.3 包方式安装
#先安装jdk之后安装jenkins,jdk中的二进制执行文件必须放置到/usr/bin中
[14:03:36 root@ubuntu18-04 jenkins]#apt install -y daemon
[14:04:17 root@ubuntu18-04 jenkins]#dpkg -i jenkins_2.277.4_all.deb
#数据文件位置
[14:05:03 root@ubuntu18-04 jenkins]#ls /var/lib/jenkins/
#配置文件位置
[14:05:43 root@ubuntu18-04 jenkins]#vim /etc/default/jenkins

4.1.3 访问web页面

devops集成工具Jenkins devops与jenkins_git_16

4.1.4 选择安装 jenkins 插件

如果现实 jenkins 已离线,将以下文件中的更新检查地址改成国内清华大学地址,然后重启 jenkins 即可:https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json

[15:23:39 root@jenkins ~]#cat .jenkins/hudson.model.UpdateCenter.xml 
<?xml version='1.1' encoding='UTF-8'?>
<sites>
  <site>
    <id>default</id>
    <url>https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json</url>
  </site>
</sites>
[15:24:11 root@jenkins ~]#cat /etc/hosts
192.168.10.181 jenkins.zhangzhuo.org
114.114.114.114 www.google.com   #添加这行

4.1.5 插件安装过程

devops集成工具Jenkins devops与jenkins_服务器_17

4.1.6 手动安装插件

国内插件下载地址:https://mirrors.tuna.tsinghua.edu.cn/jenkins/plugins/

#下载插件
需要什么插件去国内的镜像源下载
#安装插件
1.可以在web管理端安装
2.可以把下载的hpi包放到jenkins运行用户家目录的.jenkins/plugins/目录下,重启jenkins

4.1.7 配置 jenkins 权限管理

基于角色的权限管理,先创建角色和用户,给角色授权,然后把用户管理到角色。

安装插件

Role-based #基于角色的认证策略

devops集成工具Jenkins devops与jenkins_git_18

安装后重启jenkins

创建新用户

Jenkins—系统管理—管理用户

devops集成工具Jenkins devops与jenkins_版本控制系统_19

更改认证方式

Jenkins—系统管理—全局安全配置

默认创建的用户登录后可以做任何操作,取决于默认的认证授权方式。

devops集成工具Jenkins devops与jenkins_服务器_20

创建角色

Jenkins—系统管理--Manage and Assign Roles(管理和分配角)

devops集成工具Jenkins devops与jenkins_服务器_21

添加角色

devops集成工具Jenkins devops与jenkins_git_22

将用户关联到角色

devops集成工具Jenkins devops与jenkins_服务器_23

测试普通用户登录

登录成功之的界面,没有系统管理权限,只能执行被授权过的 job 且没有了管理员权限

创建项目授权

devops集成工具Jenkins devops与jenkins_git_24

绑定项目授权

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_25

4.1.8 jenkins 邮箱配置

配置 jenkins 管理员邮箱

Jenkins—系统管理—系统设置

devops集成工具Jenkins devops与jenkins_git_26

发件配置

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_27

4.2 基于ssh key拉取代码

创建linux主机的ssh密钥

#创建
[17:11:30 root@haproxy ~]#ssh-keygen
#查看
[17:12:36 root@haproxy ~]#ll .ssh/
total 16
drwx------ 2 root root 4096 May 18 17:11 ./
drwx------ 5 root root 4096 May 18 17:11 ../
-rw------- 1 root root    0 Apr 12 18:27 authorized_keys
-rw------- 1 root root 1675 May 18 17:11 id_rsa       #私钥
-rw-r--r-- 1 root root  408 May 18 17:11 id_rsa.pub   #公钥

gitlab中添加linux主机的公钥

[17:12:40 root@haproxy ~]#cat .ssh/id_rsa.pub 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC54j6P73qWUQaQibLbfg7DwlRFWX1ozVvLEYInt59k4KaRTFY6OYFvYLvb+NMXIHRmfYmyrWPwXuGyQx5xaXuMd3X1pP0mfKTYyosU2OG09ngWpoEjlPlp0pH75WBOGU4ZWaOcgTYwBme0k6Af98W6CGnsk9+Sal69TSbMTHERK/oB0g4EsZgZ4iDnth9xqtWMcczqRlteZAGmQUQjtarnRW2D5Pf02W8PFONlhyEPgw9/G891ALBa8er9m6wFS59YRLG1MELoIbMERYSf8SnpngREhG+GLjPlKhc8FCKSxk/f3SXponrVkOlSBByHQBi0LpBexu+Brz22szuOiPJV root@haproxy.zhangzhuo.org

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_28

测试拉取代码

[17:17:03 root@haproxy ~]#git clone git@192.168.10.185:root/web1.git
Cloning into 'web1'...
remote: Enumerating objects: 27, done.
remote: Counting objects: 100% (27/27), done.
remote: Compressing objects: 100% (18/18), done.
remote: Total 27 (delta 5), reused 23 (delta 3), pack-reused 0
Receiving objects: 100% (27/27), 86.98 KiB | 9.66 MiB/s, done.
Resolving deltas: 100% (5/5), done.

4.3 配置 jenkins 到 gitlab 非交互拉取代码

jenkins 服务器添加证书

Jenkins-凭据-jenkins—全局凭据—添加凭据

#先获取私钥
[17:17:06 root@haproxy ~]#cat .ssh/id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAueI+j+96llEGkImy234Ow8JURVl9aM1byxGCJ7efZOCmkUxW
OjmBb2C72/jTFyB0Zn2Jsq1j8F7hskMecWl7jHd19aT9Jnyk2MqLFNjhtPZ4FqaB
I5T5adKR++VgThlOGVmjnIE2MAZntJOgH/fFughp7JPfkmpevU0mzExxESv6AdIO
BLGYGeIg57YfcarVjHHM6kZbXmQBpkFEI7Wq50Vtg+T39NlvDxTjZYchD4MPfxvP
dQCwWvHq/ZusBUufWESxtTBC6CGzBEWEn/Ep6Z4ERIRvhi4z5SoXPBQiksZP390l
6aJ61ZDpUgQch0AYtC6QXsbvga89trM7jojyVQIDAQABAoIBAAvtPVBk8Dum1UH+
48/7hZz/4gJVHJvV2VbtN6muuXZyh515BWtU9z4bOdKCY6DvPyi4U1Z4k5n/rIGQ
lup19yxrGdF6FZgblUQnssbSp6DB07C4XD7ZNyuYDC/aZtR8ASvVOY84Jy7rqknG
0xZaAoDOO2Wnk0fEtXF1+tBO805DpPucDkld+UhqXpRcJNS5hTJQ32eGF7Igrpi4
pPwCzoaspNwCmVyreo8JifXg6yGg5LE2WD0Wc3L00CJ662L66caHz0bT43S5dJ+f
0U9BYKZ4MOpxp7HQqR7903A/774NR55GNbVl6IuVKayFWHSyBp8ZCMv/VxzavZdq
IUUL2R0CgYEA7kocPrCZaHfnermyEnV9qrermssgXvPj+K7a1nXt+tn/vlYTuINF
4+hr+Ptut0Q05mOBKj+UzdHVaP6WhTBS9rsob23XV0b42BsABP1LrdhDSpoRZ8TE
m9/2vM+Mrm/qRWE2kNi3iXM6W3BlSjpdlXxpxi3pA6jIPfvbGK+aCWcCgYEAx7MF
JIjci4ml9yqfllaPzCxBIRyu1KXlWyE1vg+wCJpSEkBrMMFWtesNbbT8KhSabG9A
NhOXUgrzxgrEQwaCYkU5nWCKpFzbk8xyzBiXAD9UEN45L5ptfnjQlHAZI8aA3Ow+
uOMozOMd65hCSRMUmPHHHBKKU5MklQ8O4u+rBOMCgYEA5IMtkvDILObPlWF+4zTI
zm5Uop0o7eX1OQZ11jRBg86K8nyZbNh2rZi18o8sHZPLJyVFOL7AdKBFh+qdbhxp
czMIE9PCLnBhMtyxqBc/+/bokSIujtSjtteQRoDC6IVqpB4VtdIeMDHQROAEQqq2
FCS7StG8I0Y4wThFY+cCUQECgYB7lQjQtDWv0K45iFIcalMw6g4gHK0BAe0u+R1W
EkVA/adejikHkg5EJEM6Ki+HJxrtkedtz6/sYMS9QTp1tBiKEgb2oqEJ2EE2dOZx
8aG40HgIvE0tbWbnRoLl6X1VS2nBzh1+s20zZjXE74PNzxYHFFDAyaR61etkp13y
CJmQOwKBgHqiwv3wLPAQKrXukcQtvPQ6RfTWDw3EFsva1LvefMuq2RCPs9XSGSGq
nMI0JKmOvd4fJIr+WqPd7LnrW0DXpBF3x1iqvZykBRjVygmKFFNVbjgigJqh1l8l
0LbFUntHXcpe/Euf+whUBjFnCxGVqj6HrxXGpdwgdK43x4bejYi/
-----END RSA PRIVATE KEY-----

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_29

jenkins创建project测试

devops集成工具Jenkins devops与jenkins_服务器_30

点击构建,构建后查看拉取的代码

[17:11:10 root@jenkins ~]#ll /var/lib/jenkins/workspace/git
total 108
drwxr-xr-x 3 root root  4096 May 18 17:27 ./
drwxr-xr-x 6 root root  4096 May 18 17:24 ../
-rw-r--r-- 1 root root 87093 May 18 17:27 1.jpg
-rw-r--r-- 1 root root    51 May 18 17:27 1.txt
drwxr-xr-x 8 root root  4096 May 18 17:34 .git/
-rw-r--r-- 1 root root   153 May 18 17:27 index.html

4.3.1 将代码部署至后端服务器

构建-执行 shell,脚本内容

[17:38:50 root@jenkins ~]#cat web1.sh
#!/bin/bash
scp /var/lib/jenkins/workspace/test1/*  192.168.10.183:/usr/local/tomcat/webapps/app/
scp /var/lib/jenkins/workspace/test1/*  192.168.10.184:/usr/local/tomcat/webapps/app/
ssh 192.168.10.183 "systemctl restart tomcat"
ssh 192.168.10.184 "systemctl restart tomcat"

devops集成工具Jenkins devops与jenkins_版本控制系统_31

之后执行构建测试

devops集成工具Jenkins devops与jenkins_版本控制系统_32

4.4 构建触发器(钩子)

构建触发器(webhook),有的人称为钩子,实际上是一个 HTTP 回调,其用于在开发人 员向 gitlab 提交代码后能够触发 jenkins 自动执行代码构建操作

以下为新建一个开发分支,只有在开发人员向开发(develop)分支提交代码的时候才会触发代码构建,而向主分支提交的代码不会自动构建,需要运维人员手动部署代码到生产环境。

4.4.1 jenkins 安装插件

#系统管理-管理插件-可选插件-Gitlab Hook 和 Gitlab Authenticatio

注意事项

https://www.jenkins.io/security/advisory/2018-05-09/

  • 在 jenkins 系统管理--全局安全设置,认证改为登录用户可以做任何事情
  • 取消跨站请求伪造保护的勾选项
  • Gitlab Hook Plugin 以纯文本形式存储和显示 GitLab API 令牌

devops集成工具Jenkins devops与jenkins_版本控制系统_33

4.4.2 jenkins 配置构建触发

产生token认证

[17:38:52 root@jenkins ~]#openssl rand -hex 12
9a8ee675b89d136d3d8721d4

devops集成工具Jenkins devops与jenkins_服务器_34

jenkins 验证分支 job 配置文件

[18:43:36 root@jenkins ~]#tail /var/lib/jenkins/jobs/git/config.xml
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <authToken>9a8ee675b89d136d3d8721d4</authToken>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders/>
  <publishers/>
  <buildWrappers/>

curl 命令测试触发并验证远程触发构建

[18:43:40 root@jenkins ~]#curl http://192.168.10.181:8080/job/test1/build?token=9a8ee675b89d136d3d8721d4

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_35

4.4.3 脚本触发

SHELL脚本

[19:16:00 root@jenkins ~]#cat 1.sh
curl http://192.168.10.181:8080/job/test1/build?token=9a8ee675b89d136d3d8721d4
curl http://192.168.10.181:8080/job/test2/build?token=9a8ee675b89d136d3d8721d4
curl http://192.168.10.181:8080/job/test3/build?token=9a8ee675b89d136d3d8721d4

4.5 构建后项目关联

用于多个 job 相互关联,需要串行执行多个 job 的场景,可以通过安装插件 Parameterized Trigger 触发执行其他 project。

devops集成工具Jenkins devops与jenkins_git_36

构建完成后触发另外一个 project

devops集成工具Jenkins devops与jenkins_服务器_37

4.6 jenkins 分布式

在众多 Job 的场景下,单台 jenkins master 同时执行代码 clone、编译、打包及构建, 其性能可能会出现瓶颈从而会影响代码部署效率,影响 jenkins 官方提供了 jenkins 分布式构建,将众多 job 分散运行到不同的 jenkins slave 节点,大幅提高并行 job 的处理能力。

4.6.1 配置 slave 节点 java环境

Slave 服务器创建工作目录,如果 slave 需要执行编译 job,则也需要配置 java 环境并且安装 git、svn、maven 等与 master 相同的基础运行环境,另外也要创建与 master 相同的数据目录,因为脚本中调用的路径只有相对一 master 的一个路径,此路径在 master 与各 node 节点必须保持一致。

[19:27:40 root@haproxy ~]#mkdir -p /var/lib/jenkins
#安装java
[19:30:32 root@haproxy ~]#java -version
java version "1.8.0_281"
Java(TM) SE Runtime Environment (build 1.8.0_281-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.281-b09, mixed mode)

4.6.2 添加 slave 节点

Jenkins—系统管理—节点管理—新建节点

devops集成工具Jenkins devops与jenkins_版本控制系统_38

部分 jenkins slave信息

devops集成工具Jenkins devops与jenkins_版本控制系统_39

之后返回启动节点验证状态

devops集成工具Jenkins devops与jenkins_服务器_40

验证 slave 进程状

[20:06:23 root@haproxy jenkins]#ps -ef | grep java
root       3066   2985  5 20:08 ?        00:00:05 java -jar slave.jar
root       3108   2402  0 20:10 pts/0    00:00:00 grep --color=auto java
[20:10:35 root@haproxy jenkins]#ls /var/lib/jenkins/
slave.jar

4.7 pipline

官方介绍:https://jenkins.io/2.0/

官方 pipline 示例: https://jenkins.io/zh/doc/book/pipeline/

pipline 是帮助 Jenkins 实现 CI 到 CD 转变的重要角色,是运行在 jenkins 2.X 版本的核心插件,简单来说 Pipline 就是一套运行于 Jenkins 上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂发布流程,从而实现单个任务很难实现的复杂流程编排和任务可视化,Pipeline 的实现方式是一套 Groovy DSL,任何发布流程都可以表述为一段 Groovy 脚本。

4.7.1 pipline语法

Stage:阶段,一个 pipline 可以划分为若干个 stage,每个 stage 都是一个操作步骤, 比如 clone 代码、代码编译、代码测试和代码部署,阶段是一个逻辑分组,可以跨多 个 node 执行。

Node:节点,每个 node 都是一个 jenkins 节点,可以是 jenkins master 也可以是 jenkins agent,node 是执行 step 的具体服务器。

Step:步骤,step 是 jenkins pipline 最基本的操作单元,从在服务器创建目录到构建容器镜像,由各类 Jenkins 插件提供实现,一个 stage 中可以有多个 step,例如: sh “make”

4.7.2 pipline 优势

可持续性:jenkins 的重启或者中断后不影响已经执行的 Pipline Job

支持暂停:pipline 可以选择停止并等待人工输入或批准后再继续执行。

可扩展:通过 groovy 的编程更容易的扩展插件。

并行执行:通过 groovy 脚本可以实现 step,stage 间的并行执行,和更复杂的相互依赖关系

4.7.3 pipline job 测试

创建 pipline job

devops集成工具Jenkins devops与jenkins_版本控制系统_41

测试简单 pipline job 运行

node {
    stage("clone代码"){
        echo "clone代码"
    }
    stage("代码构建"){
        echo "代码构建"
    }
    stage("代码测试"){
        echo "代码测试"
    }
    stage("代码部署"){
        echo "代码部署"
    }
}

jenkins Web界面配置

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_42

执行 pipline job

devops集成工具Jenkins devops与jenkins_git_43

生成流水线脚本

devops集成工具Jenkins devops与jenkins_版本控制系统_44

更改 pipline job

node {
    stage("clone代码"){
        echo "clone代码"
        git branch: 'develop', credentialsId: '58339617-450b-43fc-8428-9780dba30611', url: 'git@192.168.10.185:root/web1.git'
    }
    stage("代码构建"){
        echo "代码构建"
    }
    stage("代码测试"){
        echo "代码测试"
    }
    stage("代码部署"){
        echo "代码部署"
    }
}

执行 jenkins job

devops集成工具Jenkins devops与jenkins_版本控制系统_45

验证 git clone 日志及代码文件

devops集成工具Jenkins devops与jenkins_git_46

#代码文件位置
[20:46:51 root@jenkins ~]#ls /var/lib/jenkins/workspace/test2/
1.jpg  1.txt  index.html

pipline 中执 shell 命令打包代码

node {
    stage("clone代码"){
        echo "clone代码"
        git branch: 'develop', credentialsId: '58339617-450b-43fc-8428-9780dba30611', url: 'git@192.168.10.185:root/web1.git'
    }
    stage("代码构建"){
        echo "代码构建"
        sh 'cd /var/lib/jenkins/workspace/test2/ && tar czvf code.tar.gz ./*'
    }
    stage("代码测试"){
        echo "代码测试"
    }
    stage("代码部署"){
        echo "代码部署"
    }
}

4.7.4 pipline 部署示例

node {
    stage("clone代码"){
        echo "clone代码"
        git branch: 'develop', credentialsId: '58339617-450b-43fc-8428-9780dba30611', url: 'git@192.168.10.185:root/web1.git'
    }
    stage("代码打包"){
        sh 'cd /var/lib/jenkins/workspace/test2/ && tar czvf code.tar.gz ./*'
    }
    stage("代码复制"){
        sh 'cd /var/lib/jenkins/workspace/test2/ && scp code.tar.gz 192.168.10.183:/usr/local/tomcat/webapps/app/'
        sh 'cd /var/lib/jenkins/workspace/test2/ && scp code.tar.gz 192.168.10.184:/usr/local/tomcat/webapps/app/'
    }
    stage("代码部署"){
        sh 'ssh 192.168.10.183 "cd /usr/local/tomcat/webapps/app/ && tar xvf code.tar.gz && rm -rf code.tar.gz"'
        sh 'ssh 192.168.10.184 "cd /usr/local/tomcat/webapps/app/ && tar xvf code.tar.gz && rm -rf code.tar.gz"'
    }
    stage("重启tomcat服务"){
    	sh 'ssh 192.168.10.183 "systemctl restart tomcat"'
    	sh 'ssh 192.168.10.184 "systemctl restart tomcat"'
    }
}

运行

devops集成工具Jenkins devops与jenkins_git_47

4.7.5 指定 node 节点 运行 job

node 节点需要安装 git 命令

apt-get install git -y

node 节点需要打通与 web server 免密钥登录

[21:14:38 root@haproxy web1]#ssh-copy-id 192.168.10.183
[21:14:38 root@haproxy web1]#ssh-copy-id 192.168.10.184

jenkins pipeline 代码

node ("jenkins-slave1"){
    stage("clone代码"){
        echo "clone代码"
        git branch: 'develop', credentialsId: '58339617-450b-43fc-8428-9780dba30611', url: 'git@192.168.10.185:root/web1.git'
    }
    stage("代码打包"){
        sh 'cd /var/lib/jenkins/workspace/test3/ && tar czvf code.tar.gz ./*'
    }
    stage("代码复制"){
        sh 'cd /var/lib/jenkins/workspace/test3/ && scp code.tar.gz 192.168.10.183:/usr/local/tomcat/webapps/app/'
        sh 'cd /var/lib/jenkins/workspace/test3/ && scp code.tar.gz 192.168.10.184:/usr/local/tomcat/webapps/app/'
    }
    stage("代码部署"){
        sh 'ssh 192.168.10.183 "cd /usr/local/tomcat/webapps/app/ && tar xvf code.tar.gz && rm -rf code.tar.gz"'
        sh 'ssh 192.168.10.184 "cd /usr/local/tomcat/webapps/app/ && tar xvf code.tar.gz && rm -rf code.tar.gz"'
    }
    stage("重启tomcat服务"){
    	sh 'ssh 192.168.10.183 "systemctl restart tomcat"'
    	sh 'ssh 192.168.10.184 "systemctl restart tomcat"'
    }
}

执行结果

devops集成工具Jenkins devops与jenkins_版本控制系统_48

4.8 视图

视图可用于归档 job 进行分组显示,比如将一个业务的视图放在一个视图显示,安装完成 build pipeline 插件之后将会有一个+号用于创建视图。

4.8.1 pipline视图

安装 build pipeline插件

devops集成工具Jenkins devops与jenkins_服务器_49

4.8.2 列表视图

列表视图使用场景比较多,用于将一个业务的job保存至一个列表视图进行分类管理, 即不同业务的 job 放在不同的列表视图中。

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_50

五、代码质量测试

官方网站:http://www.sonarqube.org

SonarQube 是一个用于代码质量管理的开放平台,通过插件机制,SonarQube可以 集成不同的测试工具,代码分析工具,以及持续集成工具,例如 Hudson/Jenkins 等。

下载地址:https://www.sonarqube.org/downloads/

七个维度检测代码质量

复杂度分布:代码复杂度过高将难以理解
重复代码:程序中包含大量复制、粘贴的代码而导致代码臃肿,sonar 可以展示源码中重复严重的地方
单元测试统计:统计并展示单元测试覆盖率,开发或测试可以清楚测试代码的覆盖情况
代码规则检查:检查代码是否符合规范
注释率:若代码注释过少,特别是人员变动后,其他人接手比较难接手;若过多,又不利于阅读
潜在的 Bug:检测潜在的 bug
结构与设计:找出循环,展示包与包、类与类之间的依赖、检查程序之间耦合度

5.1 代码测试工具 SonarQube 简介

7.9.x 版本不再支持 MySQL数据库

官方说明:https://docs.sonarqube.org/latest/setup/upgrade-notes/

5.2 安装SonarQube-server

环境官方说明:

https://docs.sonarqube.org/latest/requirements/hardware-recommendations/

https://docs.sonarqube.org/latest/requirements/requirements/

5.2.1 安装jdk11

[14:32:25 root@haproxy ~]#apt install openjdk-11-jdk
[14:32:16 root@haproxy ~]#java -version
openjdk version "11.0.11" 2021-04-20
OpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.18.04)
OpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.18.04, mixed mode, sharing)

5.2.2 系统配置

[14:29:16 root@haproxy ~]#vim /etc/sysctl.conf
vm.max_map_count=524288
fs.file-max=131072
[14:29:46 root@haproxy ~]#vim /etc/security/limits.conf
sonarqube   -   nofile   131072
sonarqube   -   nproc    8192
#创建用户
[14:30:42 root@haproxy ~]#useradd jack
#重启服务器

5.2.3 上传下载的安装文件

[14:33:18 root@haproxy ~]#mkdir /data
[14:34:01 root@haproxy ~]#ls
sonarqube-8.9.0.43852.zip
[14:34:44 root@haproxy ~]#chown jack.jack -R /data
[14:36:19 root@haproxy ~]#useradd -s /bin/bash -m jack
[14:36:48 root@haproxy ~]#su - jack
jack@haproxy:~$ cd /data
jack@haproxy:/data$ unzip sonarqube-8.9.0.43852.zip 
jack@haproxy:/data$ ln -s /data/sonarqube-8.9.0.43852 /data/sonarqube
jack@haproxy:/data$ rm sonarqube-8.9.0.43852.zip 
jack@haproxy:/data$ ls
sonarqube  sonarqube-8.9.0.43852

5.2.4 安装pgSQL

[14:42:27 root@haproxy ~]#apt install postgresql
#配置
[14:43:45 root@haproxy ~]#vim /etc/postgresql/10/main/postgresql.conf
listen_addresses = '0.0.0.0'
[14:45:11 root@haproxy ~]#vim /etc/postgresql/10/main/pg_hba.conf
host    all             all             0.0.0.0/0            md5
[14:46:19 root@haproxy ~]#systemctl restart postgresql
#进入数据库创建数据库授权
[14:46:42 root@haproxy ~]#su - postgres
postgres@haproxy:~$ psql -U postgres 
psql (10.16 (Ubuntu 10.16-0ubuntu0.18.04.1))
Type "help" for help.
postgres=# create database sonar;
CREATE DATABASE
postgres=# create user sonar with encrypted password '123456';
CREATE ROLE
postgres=# grant all privileges on database sonar to sonar;
GRANT
postgres=# alter database sonar owner to sonar;
ALTER DATABASE
postgres=# \q

5.2.5 sonarqube配置启动

#修改配置
[15:00:25 root@haproxy ~]#su - jack 
jack@haproxy:~$ cd /data/sonarqube
jack@haproxy:/data/sonarqube$ vim conf/sonar.properties 
sonar.jdbc.username=sonar
sonar.jdbc.password=123456
sonar.jdbc.url=jdbc:postgresql://192.168.10.182/sonar
#启动服务
jack@haproxy:/data/sonarqube$ ./bin/linux-x86-64/sonar.sh start
#查看日志检查是否启动成功
jack@haproxy:/data/sonarqube$ tail logs/sonar.20210519.log
2021.05.19 06:59:41 INFO  app[][o.s.a.SchedulerImpl] Process[ce] is up
2021.05.19 06:59:41 INFO  app[][o.s.a.SchedulerImpl] SonarQube is up 
#最后出现这个表示启动成功

5.2.6 登录web页面安装中文

在线安装插件

默认登录的端口号为9000,默认登录用户名密码admin

devops集成工具Jenkins devops与jenkins_版本控制系统_51

离线安装插件

#插件安装目录
[15:17:26 root@haproxy ~]#ls /data/sonarqube/extensions/plugins/
README.txt  sonar-l10n-zh-plugin-8.9.jar

安装完成后重启服务

jack@haproxy:/data/sonarqube$ ./bin/linux-x86-64/sonar.sh restart

5.2.7 配置service文件

[Unit]
Description=SonarQube service
After=syslog.target network.target

[Service]
Type=simple
User=jack
Group=jack
PermissionsStartOnly=true
ExecStart=/usr/bin/nohup /usr/bin/java -Xms32m -Xmx32m -Djava.net.preferIPv4Stack=true -jar /data/sonarqube/lib/sonar-application-8.9.0.43852.jar
StandardOutput=syslog
LimitNOFILE=131072
LimitNPROC=8192
TimeoutStartSec=5
Restart=always
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

5.3 安装sonar-scanner扫描器

下载地址:https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/

官方文档:https://docs.sonarqube.org/latest/analysis/scan/sonarscanner/

5.3.1 部署 sonar-scanner

sonarqube 通过调用扫描器 sonar-scanner 进行代码质量分析,即扫描器的具体工作就是扫描代码

[15:26:57 root@jenkins ~]#cd /usr/local/src/
[15:27:21 root@jenkins src]#ls
sonar-scanner-cli-4.6.2.2472-linux.zip
[15:27:46 root@jenkins src]#unzip sonar-scanner-cli-4.6.2.2472-linux.zip 
[15:27:58 root@jenkins src]#ln -s /usr/local/src/sonar-scanner-4.6.2.2472-linux /usr/local/src/sonar-scanner
[15:28:45 root@jenkins src]#rm sonar-scanner-cli-4.6.2.2472-linux.zip 
[15:28:53 root@jenkins src]#ls
sonar-scanner  sonar-scanner-4.6.2.2472-linux
[15:28:54 root@jenkins src]#cd /usr/local/src/sonar-scanner
[15:29:11 root@jenkins sonar-scanner]#vim conf/sonar-scanner.properties 
[15:29:42 root@jenkins sonar-scanner]#cat conf/sonar-scanner.properties
#Configure here general information about the environment, such as SonarQube server connection details for example
#No information about specific project should appear here

#----- Default SonarQube server
sonar.host.url=http://192.168.10.182:9000

#----- Default source code encoding
sonar.sourceEncoding=UTF-8

5.3.2 准备测试代码

[15:32:19 root@jenkins ~]#ls
sonar-examples-master.zip
[15:32:20 root@jenkins ~]#unzip sonar-examples-master.zip 
[15:32:32 root@jenkins ~]#cd sonar-examples-master/projects/languages/php/php-sonar-runner
[15:33:01 root@jenkins php-sonar-runner]#ls -l
total 16
-rw-r--r-- 1 root root  453 Jul 25  2016 README.md
-rw-r--r-- 1 root root  331 Jul 25  2016 sonar-project.properties
drwxr-xr-x 2 root root 4096 Jul 25  2016 src
-rw-r--r-- 1 root root  272 Jul 25  2016 validation.txt
[15:33:29 root@jenkins php-sonar-runner]#cat sonar-project.properties
# Required metadata
sonar.projectKey=org.sonarqube:php-simple-sq-scanner  #自定义项目 key
sonar.projectName=PHP :: Simple Project :: SonarQube Scanner r #项目名称,会显示在web
sonar.projectVersion=1.0 #项目版本

# Comma-separated paths to directories with sources (required)
sonar.sources=src #源代码目录

# Language
sonar.language=php  #代码语言类型

# Encoding of the source files
sonar.sourceEncoding=UTF-8 #编码格式

5.3.3 在源代码目录执行扫描

#手动在当前项目代码目录执行扫描,以下是扫描过程的提示信息,扫描的配置文件 sonar-project.propertie 每个项目都要有

需要在web关闭认证

devops集成工具Jenkins devops与jenkins_版本控制系统_52

[15:38:32 root@jenkins php-sonar-runner]#/usr/local/src/sonar-scanner/bin/sonar-scanner

扫描结果

devops集成工具Jenkins devops与jenkins_版本控制系统_53

5.4 jenkins 执行代码扫描

5.4.1 jenkins 安装 SonarQube 插件

安装插件 SonarQube Scanner,然后配置 SonarQube server,系统管理-系统设置。

devops集成工具Jenkins devops与jenkins_版本控制系统_54

5.4.2 添加 sonarquebe URL

Jenkins—系统管理—系统设置--SonarQube servers

devops集成工具Jenkins devops与jenkins_git_55

5.4.3 让 jenkins 添加 Sonarscann扫描器

Jenkins--系统管理-全局工具配置

devops集成工具Jenkins devops与jenkins_版本控制系统_56

5.4.4 配置扫描

选择自己的项目(linuxNN-job1-develop)-构建-execute sonarqube scanner,将配置文件的内容修改成如下格式填写完成后点保存:

[15:52:46 root@jenkins ~]#cat sonar-project.properties
sonar.projectKey=web1-apps
sonar.projectName=web1-apps
sonar.projectVersion=1.0
sonar.sources=./
sonar.language=html 
sonar.sourceEncoding=UTF-8

5.4.5 配置项目进行扫描

devops集成工具Jenkins devops与jenkins_版本控制系统_57

六、实战案例

将代码部署到web服务

6.1 部署环境架构

devops集成工具Jenkins devops与jenkins_服务器_58

实现的目的

devops集成工具Jenkins devops与jenkins_服务器_59

如果代码有bug还需要实现代码的回滚

6.2 gitlab创建代码仓库

新建一个项目叫 web1用于代码发布,创建俩个分支master与develop

devops集成工具Jenkins devops与jenkins_版本控制系统_60

确认jenkins服务器可以免密拉取代码

[16:27:53 root@jenkins ~]#git clone git@192.168.10.185:root/web1.git
Cloning into 'web1'...
remote: Enumerating objects: 17, done.
remote: Counting objects: 100% (17/17), done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 50 (delta 6), reused 6 (delta 1), pack-reused 33
Receiving objects: 100% (50/50), 89.31 KiB | 29.77 MiB/s, done.
Resolving deltas: 100% (15/15), done.
[16:28:23 root@jenkins ~]#ls
sonar-project.properties  web1

6.3 jenkins创建项目

6.3.1 实现后端服务器的分组

创建项目后先创建参数,实现项目服务器的分组,为以后的实现代码的灰度发布实现做准备

devops集成工具Jenkins devops与jenkins_服务器_61

创建脚本

#创建一个目录用来存放脚本
[16:37:03 root@jenkins ~]#mkdir /data/jenkins -p
[16:45:43 root@jenkins ~]#cat /data/jenkins/web1.sh
#!/bin/bash
GROUP="$1"


if [ ${GROUP} == "GROUP1" ];then
    HOST_IP='192.168.10.183'
    echo "${HOST_IP}"
elif [ ${GROUP} == "GROUP2" ];then
    HOST_IP='192.168.10.184'
    echo "${HOST_IP}"
elif [ ${GROUP} == "GROUP3" ];then
    HOST_IP='192.168.10.183 192.168.10.184'
    echo "${HOST_IP}"
fi
[16:45:45 root@jenkins ~]#chmod +x /data/jenkins/web1.sh

devops集成工具Jenkins devops与jenkins_git_62

测试

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_63

6.3.2 代码克隆,打包实现

再次添加参数

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_64

devops集成工具Jenkins devops与jenkins_devops集成工具Jenkins_65

修改脚本

[16:50:37 root@jenkins ~]#mkdir /data/jenkins/git -p
[17:06:43 root@jenkins jenkins]#cat web1.sh 
#!/bin/bash
DATE=`date +"%Y-%m-%d_%H-%M-%S"`
GROUP="$1"
BRANCH="$2"

if [ ${GROUP} == "GROUP1" ];then
    HOST_IP='192.168.10.183'
    echo "${HOST_IP}"
elif [ ${GROUP} == "GROUP2" ];then
    HOST_IP='192.168.10.184'
    echo "${HOST_IP}"
elif [ ${GROUP} == "GROUP3" ];then
    HOST_IP='192.168.10.183 192.168.10.184'
    echo "${HOST_IP}"
fi
#下载代码打包
cd /data/jenkins/git && rm -rf * &&  git clone -b ${BRANCH} git@192.168.10.185:root/web1.git && mv web1 web1-${DATE} && tar zcf web1-${DATE}.tar.gz web1-${DATE}

#执行构建成功后效果
[17:07:33 root@jenkins jenkins]#ls git/
web1-2021-05-19_17-06-24  web1-2021-05-19_17-06-24.tar.gz

6.3.3 代码测试

脚本实现

[17:10:20 root@jenkins jenkins]#cat web1.sh
#!/bin/bash
DATE=`date +"%Y-%m-%d_%H-%M-%S"`

GROUP="$1"
BRANCH="$2"

if [ ${GROUP} == "GROUP1" ];then
    HOST_IP='192.168.10.183'
    echo "${HOST_IP}"
elif [ ${GROUP} == "GROUP2" ];then
    HOST_IP='192.168.10.184'
    echo "${HOST_IP}"
elif [ ${GROUP} == "GROUP3" ];then
    HOST_IP='192.168.10.183 192.168.10.184'
    echo "${HOST_IP}"
fi

cd /data/jenkins/git && rm -rf * &&  git clone -b ${BRANCH} git@192.168.10.185:root/web1.git && mv web1 web1-${DATE} && tar zcf web1-${DATE}.tar.gz web1-${DATE}

#测试代码
cd /data/jenkins/git/web1-${DATE} && /usr/local/src/sonar-scanner/bin/sonar-scanner

分支文件准备测试文件

#master分支
[17:11:36 root@jenkins ~]#git clone -b master git@192.168.10.185:root/web1.git
[17:12:10 root@jenkins ~]#cd web1/
[17:13:00 root@jenkins web1]#cat sonar-project.properties
sonar.projectKey=web1-develop
sonar.projectName=web1-develop
sonar.projectVersion=1.0
sonar.sources=./
sonar.language=html 
sonar.sourceEncoding=UTF-8
[17:13:02 root@jenkins web1]#git add .
[17:13:20 root@jenkins web1]#git commit -m "add sonar"
[17:13:32 root@jenkins web1]#git push
#develop分支
[17:15:36 root@jenkins ~]#git clone -b develop git@192.168.10.185:root/web1.git
[17:15:51 root@jenkins ~]#cd web1/
[17:16:30 root@jenkins web1]#cat sonar-project.properties
sonar.projectKey=web1-develop
sonar.projectName=web1-develop
sonar.projectVersion=1.0
sonar.sources=./
sonar.language=html 
sonar.sourceEncoding=UTF-8 
[17:16:46 root@jenkins web1]#git add .
[17:16:51 root@jenkins web1]#git commit -m "add sonar"
[17:17:03 root@jenkins web1]#git push

构建测试

devops集成工具Jenkins devops与jenkins_git_66

6.3.4 代码拷贝解压

#现在后端web服务器创建存放代码的目录
[17:30:24 root@tomcat2 ~]#mkdir /data/tomcat/{appstar,apps} -p
#代码拷贝脚本
[17:33:51 root@jenkins jenkins]#cat web1.sh
#!/bin/bash
....
#代码拷贝
for IP in ${HOST_IP};do
    scp /data/jenkins/git/web1-${DATE}.tar.gz ${IP}:/data/tomcat/appstar
    ssh ${IP} "tar xf /data/tomcat/appstar/web1-${DATE}.tar.gz -C /data/tomcat/apps/"
done

6.3.5 服务器从Haproxy下线

#脚本中动态下线服务器
for IP in ${HOST_IP};do
    ssh 192.168.10.182 'echo "disable server web1/'${IP}'" | socat stdio /var/lib/haproxy/haproxy.sock'
done
#haproxy需要的配置
stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin
listen web1
    bind 0.0.0.0:80
    mode http
    log global
    balance static-rr
    option forwardfor
    server 192.168.10.183 192.168.10.183:8080 weight 1 check inter 3000 fall 3 rise 5
    server 192.168.10.184 192.168.10.184:8080 weight 1 check inter 3000 fall 3 rise 5

构建测试

devops集成工具Jenkins devops与jenkins_git_67

6.3.6 停止后端服务器链接代码

#脚本代码
for IP in ${HOST_IP};do                                                             
    ssh ${IP} "systemctl stop tomcat.service"
    ssh ${IP} "rm -rf /usr/local/tomcat/webapps/*"
    ssh ${IP} "ln -sf /data/tomcat/apps/web1-${DATE} /usr/local/tomcat/webapps/web1"
done
#构建执行测试
[17:57:22 root@tomcat1 ~]#ls /usr/local/tomcat/webapps/
web1

6.3.7 启动服务

for IP in ${HOST_IP};do    
    ssh ${IP} "systemctl start tomcat.service"
done

6.3.8 检测服务是否可用,可用把服务上线

sleep 10
for IP in ${HOST_IP};do
    NUM=`curl -s  -I -m 10 -o /dev/null  -w %{http_code}  http://${IP}:8080/web1/index.html`
    if [[ ${NUM} -eq 200 ]];then
       echo "${node} web URL 测试通过,即将添加到负载"
       ssh 192.168.10.182 'echo "enable server web1/'${IP}'" | socat stdio /var/lib/haproxy/haproxy.sock'
    else
       echo "${node} 测试失败,请检查该服务器是否成功启动tomcat"
    fi
done

6.3.9 代码回滚

for IP in ${HOST_IP};do
    echo $IP
    NOW_VERSION=`ssh ${IP} ""/bin/ls -l  -rt /usr/local/tomcat/webapps/ | awk -F"->" '{print $2}'  | tail -n1""`
    NOW_VERSION=`basename ${NOW_VERSION}`
    echo $NOW_VERSION,"NOW_VERSION"
    NAME=`ssh  ${IP}  ""ls  -d -l  -rt  /data/tomcat/apps/* | grep -B 1 ${NOW_VERSION} | head -n1 | awk '{print $9}'""`
    echo $NAME,""NAME
    ssh ${IP} "rm -rf /usr/local/tomcat/webapps/*  && ln -sv  ${NAME} /usr/local/tomcat/webapps/web1"
done

6.3.10 删除历史构建的代码

for IP in ${HOST_IP};do
    ssh ${IP} 'rm -rf /data/tomcat/appstar/*'
    NUM=`ssh ${IP} "ls -rt -d /data/tomcat/apps/* | wc -l"`
    if [ ${NUM} -gt 5 ];then
        NAME=`ssh ${IP} ""ls -l -rt -d /data/tomcat/apps/* | awk '{print $9}' | head -n1""`
        ssh ${IP} "rm -rf ${NAME}"
    fi  
done

6.4 使用函数进行封装并且实现回滚功能

添加新的参数

devops集成工具Jenkins devops与jenkins_git_68

devops集成工具Jenkins devops与jenkins_git_69

最终版本的脚本

 

[20:57:03 root@jenkins jenkins]#cat web1.sh #!/bin/bash DATE=`date +"%Y-%m-%d_%H-%M-%S"` GROUP="$1" BRANCH="$2" IP_list(){ if [ ${GROUP} == "GROUP1" ];then HOST_IP='192.168.10.183' echo "${HOST_IP}" elif [ ${GROUP} == "GROUP2" ];then HOST_IP='192.168.10.184' echo "${HOST_IP}" elif [ ${GROUP} == "GROUP3" ];then HOST_IP='192.168.10.183 192.168.10.184' echo "${HOST_IP}" fi } clone_code(){ echo "即将开始从clone ${BRANCH}分支代码" cd /data/jenkins/git && rm -rf * && git clone -b ${BRANCH} git@192.168.10.185:root/web1.git && mv web1 web1-${DATE} && tar zcf web1-${DATE}.tar.gz web1-${DATE} echo "${BRANCH}分支代码打包完成" } scanner_code(){ cd /data/jenkins/git/web1-${DATE} && /usr/local/src/sonar-scanner/bin/sonar-scanner echo "代码扫描完成,请打开sonarqube查看扫描结果" } scp_tar(){ for IP in ${HOST_IP};do scp /data/jenkins/git/web1-${DATE}.tar.gz ${IP}:/data/tomcat/appstar ssh ${IP} "tar xf /data/tomcat/appstar/web1-${DATE}.tar.gz -C /data/tomcat/apps/" done echo "代码分发完成" } down_node(){ for IP in ${HOST_IP};do ssh 192.168.10.182 'echo "disable server web1/'${IP}'" | socat stdio /var/lib/haproxy/haproxy.sock' echo "$IP从负载均衡192.168.10.182下线成功" done } stop_tomcat(){ for IP in ${HOST_IP};do ssh ${IP} "systemctl stop tomcat.service" echo "${IP}节点tomcat已经停止" done } code_deployment(){ for IP in ${HOST_IP};do ssh ${IP} "rm -rf /usr/local/tomcat/webapps/*" ssh ${IP} "ln -sf /data/tomcat/apps/web1-${DATE} /usr/local/tomcat/webapps/web1" echo "${IP}节点代码部署完成" done } start_tomcat(){ for IP in ${HOST_IP};do ssh ${IP} "systemctl start tomcat.service" echo "${IP}节点tomcat已经启动" done } web_test(){ echo "正在测试后端服务器是否可用..." sleep 10 for IP in ${HOST_IP};do NUM=`curl -s -I -m 10 -o /dev/null -w %{http_code} http://${IP}:8080/web1/index.html` if [[ ${NUM} -eq 200 ]];then echo "${node} web URL 测试通过,即将添加到负载" ssh 192.168.10.182 'echo "enable server web1/'${IP}'" | socat stdio /var/lib/haproxy/haproxy.sock' else echo "${node} 测试失败,请检查该服务器是否成功启动tomcat" fi done } rollback_last_version(){ for IP in ${HOST_IP};do echo $IP NOW_VERSION=`ssh ${IP} ""/bin/ls -l -rt /usr/local/tomcat/webapps/ | awk -F"->" '{print $2}' | tail -n1""` NOW_VERSION=`basename ${NOW_VERSION}` echo $NOW_VERSION,"NOW_VERSION" NAME=`ssh ${IP} ""ls -d -l -rt /data/tomcat/apps/* | grep -B 1 ${NOW_VERSION} | head -n1 | awk '{print $9}'""` echo $NAME,""NAME ssh ${IP} "rm -rf /usr/local/tomcat/webapps/* && ln -sv ${NAME} /usr/local/tomcat/webapps/web1" echo