CI/CD概述

持续集成

  • 迟早集成(CI)是当下最流行的应用程序开放实践方式
  • 程序员在代码库中集成了修复bug、新特性开放或是功能革新
  • CI工具通过自动构建和自动测试来验证结果。这还可以检测到当前程序代码的问题,迅速提供反馈

1.png

Jenkins概述

  • Jenkins是由java编写的一款开发软件
  • 作为一款非常流行的CI(持续集成)工具,用于构建和测试各种项目
  • Jenkins的主要功能是监视重复工作的执行,例如软件工程的构建或在cron下设置的jobs

Jenkins特点

  • 简单、可扩展、用户界面友好
  • 支持各种SCM(软件配置管理)工具,如SVN、CIT、CVS等
  • 能够构建各种风格的项目
  • 可以选择安装多种插件
  • 跨平台,几乎可以支持所有平台

安装Jenkins

2.png

1
2
3
4
5
6
7
[root@node2 ~]$ sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo

[root@node2 ~]$ sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key

[root@node2 ~]$ yum install fontconfig java-11-openjdk -y
[root@node2 ~]$ yum install jenkins -y
[root@node2 ~]$ systemctl enable --now jenkins.service

或(建议)

1
2
3
[root@node2 ~]$ yum install fontconfig java-1.8.0-openjdk -y
[root@node2 ~]$ rpm -ivh jenkins-2.344-1.1.noarch.rpm
[root@node2 ~]$ systemctl enable --now jenkins.service

初始化Jenkins

  • Jenkins默认运行在8080端口
  • 浏览器访问

3.png

1
2
[root@node2 ~]$ cat /var/lib/jenkins/secrets/initialAdminPassword
ab74be14c5ba4ca7b2bf05e3c799ab81
  • 如若网络较慢,先不安装插件
  • 选择插件安装–无–安装
  • 使用admin账户继续
  • 可把密码修改为一个简单的,例000000

配置安装插件源

  • 默认的官方站点安装缓慢,修改配置进行加速
1
2
3
4
[root@node2 ~]$ sed -i 's/https:\/\/updates.jenkins.io\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' /var/lib/jenkins/updates/default.json 
[root@node2 ~]$ sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' /var/lib/jenkins/updates/default.json

[root@node2 ~]$ systemctl restart jenkins.service

安装插件

  • Git Parameter # git参数

  • Localiztion Chinese(Simplified) # 中文

  • Dingtalk # 钉钉机器人

  • 在钉钉群里新建一个机器人

4.png

  • 在jenkins中添加机器人

5.png

  • 测试成功后报存

准备Git仓库

本地仓库

初始化mysite项目

  • 创建名为mysite的项目
  • 在mysite中创建index.html文件
  • 将工作区文件确认至版本仓库
1
2
3
4
5
6
7
8
[root@node2 ~]$ git init mysite
[root@node2 ~]$ cd mysite/

[root@node2 mysite]$ vim index.html
<h1>My Web Site</h1>

[root@node2 mysite]$ git add .
[root@node2 mysite]$ git commit -m "init data"

tag标签

  • 如果达到一个重要的阶段,并希望永远记住那个特别的提交快照,可以使用git tag给它打上标签
  • 将初始化完毕的mysite项目打上标签1.0
1
[root@node2 mysite]$ git tag 1.0

升级mysite

  • 在mysite中进行内容修改
  • 将工作区文件确认至版本仓库
  • 将当前提交增加一个tag2.0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@node2 mysite]$ vim index.html
<h1>My Web Site</h1>
<h2>2nd version</h2>

[root@node2 mysite]$ git add .
[root@node2 mysite]$ git commit -m "my site 2.0"
[root@node2 mysite]$ git tag 2.0

[root@node2 mysite]$ git tag
1.0
2.0
[root@node2 mysite]$ git status
# On branch master
nothing to commit, working directory clean

远程仓库

创建项目

  • 创建名为mysite的仓库

6.png

创建用户

7.png

授权

8.png

设置免密登录

9.png

1
2
3
4
[root@node2 mysite]$ git remote -v
[root@node2 mysite]$ git remote add origin git@192.168.1.11:devops/mysite.git
[root@node2 mysite]$ git push -u origin --all
[root@node2 mysite]$ git push -u origin --tags

管理jenkins项目

构建项目

下载git插件

  • 为了使得Jenkins可以使用git的tag,需要下载git parameter插件
  • 也要按照git命令
  • 点击“系统管理” - > “管理插件”

创建自由风格项目

  • 新建任务

10.png

  • 开启钉钉机器人

11.png

  • 添加Git Parameter参数

12.png

源码管理

  • 源码采用git

13.png

  • 将源码checkout到子目录

14.png

构建工程

  • 构建工程
  • 选择指定的标签

15.png

检验结构

  • 选择指定的标签

16.png

  • 查看日志输出

17.png

查看本地结果

  • 构建好的项目出现在/var/lib/jenkins/workspace/
1
2
3
4
5
6
7
[root@node2 ~]$ cd /var/lib/jenkins/workspace/
[root@node2 workspace]$ ls
mysite
[root@node2 workspace]$ ls mysite/
mysite-1.0
[root@node2 workspace]$ cat mysite/mysite-1.0/index.html
<h1>My Web Site</h1>
  • 再次构建2.0版本任务
  • 再次查看
1
2
[root@node2 workspace]$ ls mysite/
mysite-1.0 mysite-2.0

分发服务器管理

优化构建工程

  • 在Jenkins服务器安装apache,用于分发 应用程序
  • 为了方便应用服务器下载,Jenkins构建的工程应该打包成为一个文件
  • 为了应用服务器可以获知下载的程序文件是没有损坏的,应该为其生成md5

配置分发服务器

  • 通过web服务为应用服务器提供应用程序
  • 下载目录为/var/www/tml/deploy/packages
1
2
3
4
[root@node2 mysite]$ yum install -y httpd  
[root@node2 mysite]$ mkdir -p /var/www/html/deploy/packages
[root@node2 mysite]$ chown -R jenkins.jenkins /var/www/html/deploy/
[root@node2 mysite]$ systemctl enable --now httpd.service

修改工程构建工程

  • 为下载的应用打包,以及生成md5可以能在工作过程中增加构建步骤完成

18.png

19.png

  • 添加的命令
1
2
3
4
5
6
7
8
9
10
pkg_dir=/var/www/html/deploy/packages
cp -r mysite-$webver $pkg_dir
cd $pkg_dir
rm -rf mysite-$webver/.git
tar -czf mysite-$webver.tar.gz mysite-$webver
rm -rf mysite-$webver
md5sum mysite-$webver.tar.gz | awk '{print $1}' > mysite-$webver.tar.gz.md5
cd ..
[ -f live_ver ] && cat live_ver > last_ver
echo -n $webver > live_ver
  • 再次构建任务
  • 浏览器访问http://192.168.1.12/deploy/
  • 也可在本地查看
1
2
3
4
5
6
[root@node2 deploy]$ ls
last_ver live_ver packages
[root@node2 deploy]$ ls packages/
mysite-1.0.tar.gz mysite-1.0.tar.gz.md5 mysite-2.0.tar.gz mysite-2.0.tar.gz.md5
[root@node2 deploy]$ cat packages/mysite-1.0.tar.gz.md5
04da2023827e4fef79846aed7e31843f

自动化部署框架

自动化部署

服务器规划

  • 为了方便版本切换,可以规划如下目录

    /var/www/download用于存储下载的目录

    /var/www/deploy用于存储解压的应用

  • 创建/var/www/html/current软链接,指向需要部署的应用版本

1
2
3
4
[root@node2 ~]# cd /var/www/
[root@node2 www]# ls
cgi-bin html
[root@node2 www]# mkdir deploy download

下载应用

  • 编写下载应用的功能代码

    通过位置参数指定要下载的版本

    位置参数是live下载当前版本

    位置参数是last下载前一个版本

    如果已经下载,则不需要重复下载

校验文件

  • 编写校验文件代码

    计算指定文件的md5值

    将md5值与发布服务器提供的md5进行比较,以确认下载的文件无误

发布应用

  • 编写应用发布

    根据指定的版本,创建/var/www/html/current链接,指向到不同的发布版本

自动化部署实现

完成自动化部署功能

  • 完成下载检查是否有新版本功能
  • 完成文件校验功能
  • 完成部署功能
  • 测试结果

清空jenkins服务器

1
2
3
4
5
6
7
8
9
10
[root@node2 ~]# cd /var/www/html/
[root@node2 html]# ls
deploy
[root@node2 html]# cd deploy/
[root@node2 deploy]# ls
last_ver live_ver packages
[root@node2 deploy]# rm -rf l*
[root@node2 deploy]# ls
packages
[root@node2 deploy]# rm -rf packages/*

编写python脚本

  • 也可以另起一台虚拟机
  • 编写名为deploy_webpython文件
1
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
import wget
import requests
import os
import hashlib
import tarfile

def has_new_ver(ver_fname, ver_url):
'如果服务器上有新版本返回True,否则返回False'
# 如果本地没有版本文件,则有新版本
if not os.path.exists(ver_fname):
return True

# 如果本地没有版本文件,则有新版本
with open(ver_fname) as fobj:
local_ver = fobj.read()

r = requests.get(ver_url)
if local_ver != r.text:
return True

return False

def file_ok(fname, md5url):
'如果文件未损坏,返回True,否则返回False'
m = hashlib.md5()
with open(fname, 'rb') as fobj:
while 1:
data = fobj.read(4096)
if not data:
break
m.update(data)

r = requests.get(md5url)
if m.hexdigest() == r.text.strip():
return True
else:
return False

def deploy(app_fname, deploy_dir, dest):
'用于部署软件到web服务器'
# 解压
tar = tarfile.open(app_fname)
tar.extractall(path=deploy_dir)
tar.close()

# 拼接出解压后目录的绝对路径
app_dir = os.path.basename(app_fname)
app_dir = app_dir.replace('.tar.gz', '')
app_dir = os.path.join(deploy_dir, app_dir)

# 创建链接
if os.path.exists(dest):
os.remove(dest)
os.symlink(app_dir, dest)



if __name__ == '__main__':
# 判断服务器上是否有新版本
ver_url = 'http://192.168.1.10/deploy/live_ver'
ver_fname = '/var/www/deploy/live_ver'
if not has_new_ver(ver_fname, ver_url):
print('未发现新版本')
exit(1)

# 下载新版本
r = requests.get(ver_url)
app_url = 'http://192.168.1.10/deploy/packages/mysite-%s.tar.gz' % r.text
app_fname = '/var/www/download/mysite-%s.tar.gz' % r.text
wget.download(app_url, app_fname)


# 校验下载的软件包是否损坏 如果损坏则删除
md5url = app_url + '.md5'
if not file_ok(app_fname, md5url):
print('文件已损坏')
os.remove(app_fname)
exit(2)

# 部署软件
deploy_dir = '/var/www/deploy'
dest = '/var/www/html/current'
deploy(app_fname, deploy_dir, dest)

# 更新本地版本文件
if os.path.exists(ver_fname):
os.remove(ver_fname)
wget.download(ver_url, ver_fname)

  • Jenkins构建项目后,执行python脚本
  • 浏览器查看python虚拟机ip(192.168.1.10/current)验证