目前支持CI/CD的平台有:
虽然Jenkins只是支持持续集成的众多工具之一,Jenkins ≠ 持续集成,但目前只要一提到Jenkins,人们就会联想到持续集成、CI&CD,可见其使用范围之广,用户之多。同时资料也最为丰富、社区最活跃,所以选择Jenkins也没有什么好犹豫的。
工具 | 说明 |
---|---|
Jenkins | 基础平台 |
Sonar&Sonarqube | 静态代码扫描 |
Python3+Allure+Pytest+Requests(接口)+Selenium(web)+UiAutomator2(APP) | 自动化测试环境 |
docker | 容器引擎 |
Harbor/dockerhub | 官方的docker镜像注册服务器(可以在本地搭建Harbor代替) |
Kubernetes | 容器编排管理工具,用于部署应用程序 |
Gitlab/Github | 源代码仓库(可以在本地搭建gitlab代替) |
NodeJS | 前端构建环境 |
Java+Maven | 后端构建环境 |
CI 阶段目标
阶段目标一:自动化构建
先解决“人工拉取代码-执行编译-通知团队”这个原本手动执行效率低下的问题,最终效果:研发提交代码–>触发webhook–>Jenkins自动拉取代码–>编译打包–>通过插件实时企微群通知,后续的发布替换还是由手动完成,一定程度上达到了节约时间,降本增效的目的。
工具:jenkins、gitlab、gerrit
阶段目标二:静态代码扫描
在CI阶段,自动化拉取代码、自动编译只是过程,不是目的,重要的是要能够实现快速检验、快速反馈。这其中很重要的一个手段就是单元测试。但就目前团队现状而言,单元测试不现实。
工具:sonar/sonarqube
我们采取的是接入自动化静态代码扫描来替代单元测试,最终效果:研发提交代码–>触发webhook–>Jenkins自动拉取代码–>静态代码扫描–>编译打包,实现最基本的静态代码扫描,代码覆盖率检测,避免出现低级语法规范和一些安全隐患。
阶段目标三:自动发布
解决手动替换发包的问题:自动编译打包后,通过shell脚本自动将发布包发布到开发、测试等各个环境。
工具:docker、shell、python、harbor
CD 阶段目标
阶段目标四:自动化冒烟测试
应当说持续交付这个阶段最考验的就是交付产物交付到各个环境以后的自动化测试能力,需要具备完善的自动化测试手段,以此来保障快速反馈,否则很难体现CD的效果。除了前面提到的单元测试、静态代码扫描,还包括如:组件集成测试、接口测试、UI测试、性能测试、安全测试等等。
工具:Allure、Pytest
在阶段目标四中,我们主要实现两个类型的自动化测试:
接口测试:校验各个接口是否连通,接口业务场景是否能够正常串联执行;
UI测试:校验系统是否能够正常登录,一些基本的冒烟测试用例是否正常执行;
阶段目标五:Pipeline流水线改造
将以上流程全部通过自动化手段实现以后,再利用Pipeline流水线语法进行改造,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂发布流程。
工具:jenkins-pipeline
本节搭建的目标是:
服务器规划
服务器 | IP | 用途 |
---|---|---|
node-252 | 192.168.71.252 | jenkins agent,sonarqube,docker |
node-253 | 192.168.71.253 | jenkins master,harbor |
node-249 | 192.168.70.249 | jenkins agent,gitlab |
由于笔者jenkins安装的版本较高(Jenkins 2.401.2),所以要求java版本至少是11或以上的,笔者这里重新装java
注意:这里不光是安装jenkins的服务器需要较高版本的java,节点机也需要较高版本,否则无法连接节点
参考 CHAPTER 1 Jenkins部署与基础配置 1.1.2 红帽系安装
参考 gitlab/gerrit 2.1 环境准备
参考 CHAPTER 1 Docker入门 1.3.1 yum安装
参考 CHAPTER 12 Compose(一)12.2.2 二进制包
参考 CHAPTER 5 Jenkins & SonarQube
账号:admin
密码:yurq
参考 harbor(docker仓库)仓库部署
6bDg3XQ81Y3NDWJhtIVacMDyqeNzj3oO
参考 CHAPTER 3 Jenkins SVN GItlab 3.2.1 搭建gitlab服务器(使用官方镜像搭建)
步骤简述:
[root@node-252 mypython]# cat mypython.py
#!/usr/bin/env python
from flask import Flask
app=Flask(__name__)
@app.route('/')
def index():
return 'welcome to my webpage!'
if __name__=="__main__":
app.run(port=8080,host="0.0.0.0",debug=True)
[root@node-252 mypython]# cat Dockerfile
FROM lmurawsk/python2.7:latest
LABEL maintainer="mrhelloworld.com"
WORKDIR /usr/local
RUN mkdir -p /plugins
COPY package/*.whl /plugins/
RUN pip install /plugins/click-3.0-py2.py3-none-any.whl
RUN pip install /plugins/itsdangerous-1.1.0-py2.py3-none-any.whl
RUN pip install /plugins/MarkupSafe-1.1.0-cp27-cp27mu-manylinux1_x86_64.whl
RUN pip install /plugins/Werkzeug-0.16.0-py2.py3-none-any.whl
RUN pip install /plugins/Jinja2-2.11.0-py2.py3-none-any.whl
RUN pip install /plugins/Flask-0.12.5-py2.py3-none-any.whl
COPY ./mypython.py /usr/local/
EXPOSE 8080
CMD python /usr/local/mypython.py
由于我们要使用python flask的web功能,但是镜像中没有相关插件,所以需要把插件打包到镜像中
[root@node-252 mypython]# cp ../mypython_bak/*.whl .
[root@node-252 mypython]# ll
total 648
-rw-r--r-- 1 root root 11 Jul 10 05:43 blog.txt
-rw-r--r-- 1 root root 57939 Jul 10 20:34 click-3.0-py2.py3-none-any.whl
-rw-r--r-- 1 root root 1104 Jul 10 20:32 Dockerfile
-rw-r--r-- 1 root root 81748 Jul 10 20:34 Flask-0.12.5-py2.py3-none-any.whl
-rw-r--r-- 1 root root 16743 Jul 10 20:34 itsdangerous-1.1.0-py2.py3-none-any.whl
-rw-r--r-- 1 root root 126742 Jul 10 20:34 Jinja2-2.11.0-py2.py3-none-any.whl
-rw-r--r-- 1 root root 24542 Jul 10 20:34 MarkupSafe-1.1.0-cp27-cp27mu-manylinux1_x86_64.whl
-rw-r--r-- 1 root root 208 Jul 10 05:43 mypython.py
-rw-r--r-- 1 root root 6161 Jul 10 05:43 README.md
-rw-r--r-- 1 root root 327277 Jul 10 20:34 Werkzeug-0.16.0-py2.py3-none-any.whl
后续可以制作python2.7+flask相关插件的镜像
上传代码
[root@node-252 mypython]# cat ~/.gitconfig
[user]
name = yurq
email = [email protected]
[credential]
helper = store
[http]
sslVerify = false
[root@node-252 MyPython]# git commit mypython.py
[detached HEAD 6182db3] flask
1 file changed, 8 insertions(+), 2 deletions(-)
[root@node-252 mypython]# git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 396 bytes | 396.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
To http://192.168.70.249:9980/devs/mypython.git
f3047fb..0249dc8 main -> main
上传dockerfile
[root@node-252 mypython]# git add Dockerfile
[root@node-252 mypython]# git commit Dockerfile
[main 059968e] add dockerfile
1 file changed, 25 insertions(+)
create mode 100644 Dockerfile
[root@node-252 mypython]# git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 657 bytes | 657.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To http://192.168.70.249:9980/devs/mypython.git
0249dc8..059968e main -> main
创建jenkins项目,配置节点和下载相关插件
配置sonar
由于网络原因,docker-build-step插件一直下载不了,所以手动写了构建/推送命令
由于网络原因,有些插件下载不了,所以笔者又创建了一个jenkins项目并在其他节点运行,以模拟生产场景
该示例中有很多步骤都是手动运行的,其原因是很多jenkins插件由于网络原因无法下载,在下例中将逐步改进
说明:在该示例中,我们将在示例一的基础上实现更多功能,包括:
本节中,我们需要安装的jenkins的插件有:
如果添加webhook提示
Url is blocked: Requests to the local network are not allowed
的问题,在gitlab->menu->admin area->Setting->network->Outbound requests
勾选:Allow requests to the local network from web hooks and services即可解决
[root@node-252 mypython]# cat dockerfile2
FROM lmurawsk/python2.7:latest
LABEL maintainer="mrhelloworld.com"
WORKDIR /usr/local
RUN mkdir -p /plugins
COPY package/*.whl /plugins/
RUN pip install /plugins/click-3.0-py2.py3-none-any.whl
RUN pip install /plugins/itsdangerous-1.1.0-py2.py3-none-any.whl
RUN pip install /plugins/MarkupSafe-1.1.0-cp27-cp27mu-manylinux1_x86_64.whl
RUN pip install /plugins/Werkzeug-0.16.0-py2.py3-none-any.whl
RUN pip install /plugins/Jinja2-2.11.0-py2.py3-none-any.whl
RUN pip install /plugins/Flask-0.12.5-py2.py3-none-any.whl
[root@node-252 mypython]# docker build -f dockerfile2 -t yurq/python2.7:latest .
[root@node-252 mypython]# docker tag yurq/python2.7:latest 192.168.71.253:80/python2.7/yurq_python2.7:latest
[root@node-252 mypython]# docker push 192.168.71.253:80/python2.7/yurq_python2.7:latest
excute docker command
不太好用,直接用命令行替代Docker build and publish
可以使用[root@node-252 mypython]# cat mypython.py
#!/usr/bin/env python
from flask import Flask
app=Flask(__name__)
@app.route('/')
def index():
return 'welcome to my webpage! power by python!'
if __name__=="__main__":
app.run(port=8080,host="0.0.0.0",debug=True)
[root@node-252 mypython]# git commit -m "add power by" mypython.py
[main 9f4af70] add power by
1 file changed, 1 insertion(+), 1 deletion(-)
[root@node-252 mypython]# git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 293 bytes | 293.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
To http://192.168.70.249:9980/devs/mypython.git
de86dd3..9f4af70 main -> main