
官网https://www.jenkins.io/下载jenkins,可选择适合你自己系统的版本,也可选择不依赖于系统的docker(前提本机安装了docker)或war包(本机安装了java),如下开启服务端。

执行如上命令出现如下密码,复制这串密码,浏览器打开http://localhost:8080,粘贴到管理员密码里。点击安装推荐插件,创建管理员用户yu,密码123,到这里jenkins已经安装完成了(装在win上),主要用来配合git服务器。

在Manage Jenkins中选择Global Tool Configuration添加git路径如下。

Create a job,选择Freestyle project,命名为newjob,点击配置。





如上如果选择execute shell会出现系统找不到指定文件报错。liunx 下使用execute shell,并加上#!/bin/bash,cat ./README.md。这里的脚本可以做的事很多,比如构建项目,做代码测试等。如下点击控制台输出,可查看fetch拉取github代码并执行cat ./README.md脚本。

jenkins轮询github,有更新就pull下来,然后执行输入的构建脚本。


在指定服务器上安装docker客户端,使用docker命令进行下载jenkins-docker版本如下:
docker pull jenkins/jenkins:2.54
运行jenkins-docker images,进入对应container,并相应安装vim、docker、gcc/g++、make、cppcheck等调试、编译环境,运行jenkins-docker命令如下:-name指定image名字。-p内部container和外部对应关系。-v外部地址和container内部地址对应关系,在container内部修改jenkins的话,外部也会更新。/etc/localtime时间同步。
docker run --privileged=true -d --name jenkins -p 8080:8080 -v /var/run/docker.sock:/var/run/docker.sock -v /home/ju/jenkins_home:/var/jenkins_home/ -v /etc/localtime:/etc/localtime jenkins/jenkins:v4
进入docker container内部命令如下:
docker exec -u 0 -it jenkins bash


保存docker images为tar包命令如下,-o(output):
docker save –o jenkins_v4.tar jenkins/jenkins:v4
加载tar包为docker images命令如下,和上面相反:
docker load -input jenkins_v4.tar
GitLab配置Access Token,用于jenkins连接GitLab获取GitLab API接口,GitLab页面->用户->Settings->Access Tokens。
生成GitLab个人准入秘钥,设置Name,Expires at默认不填永久有效,Scopes全部勾选后,点击Create personal access token,生成后,页面上会提示"Your new personal access token has been created"。
Jenkins配置GitLab,在jenkins界面点击Manage Jenkins->Configure System:

Jenkins配置界面查找GitLab,添加connect name与Gitlab host URL:

添加Credentials,点击Add->jenkins,进行添加对应的秘钥,Kind选择GitLab API Token,API Token添加GitLab personal api token,ID添加为jenkins_gitlab后,点击Add。

选择Credentials为GitLab API token,点击Test Connection,验证jenkins与GitLab是否建立连接,显示为”Success”。

在jenkins主界面点击New Item,可新建job或者复制已有job,此处直接复制已有job,拷贝后的job不要运行build,避免回填错误数据到GitLab。

项目名为example的Jenkins pipeline script同下面导入的,可自行修改。

选择对应job后点击Configure进行job配置,在job配置界面查到Build Triggers,进行配置job触发条件,勾选“Build when a change is pushed to GitLab. GitLab webhook URL: http://10.75.92.229:8080/project/sw61_bsp_build”,勾选状态如下图。






1) 环境模块导入, groovy语言
import hudson.*
2) 清空工作空间
node(""){
stage("clear workspace"){
cleanWs()
}
}
3) 下载源码
node(""){
stage("download code"){
echo "download code"
if (env.gitlabSourceBranch == null){ //当gitlab过来触发的job的话,环境变量上就会有gitlabSourceBranch字串
git branch: "sw61", credentialsId: '18f53bc3-dc50-4178-acde-d628264ee453', url: 'https://gitlab-dbd...com/bsp_diag/prj_bsp.git' // null说明不是gitlab触发
}else{ //gitlab做了push或merge操作时触发,credentialsId就是Access Tokens
git branch: env.gitlabSourceBranch, credentialsId: '18f53bc3-dc50-4178-acde-d628264ee453', url: env.gitlabSourceRepoHttpUrl
}
}
}
4) 源码cppcheck扫描
try{
node(""){
stage("cppcheck"){
echo "cppcheck starting"
pwd()
sh "/home/cppcheck-1.75/cppcheck 4 --enable=all --inconclusive --xml --xml-version=2 "+env.WORKSPACE+" 2> cppcheck.xml"
publishCppcheck ignoreBlankFiles: true, pattern: 'cppcheck.xml'
def cppcheck_file_obj = new File(env.WORKSPACE+"/cppcheck.xml")
if (cppcheck_file_obj.isFile()){
echo "cppcheck.xml file exist"
result = sh returnStdout: true ,script: "cat "+env.WORKSPACE+"/cppcheck.xml"
if (result == ""){
echo "analyse cppcheck.xml failure,cppcheck.xml is empty"
currentBuild.result="FAILURE"
}else{
echo "analyse cppcheck.xml"
cppcheck_result = sh returnStdout: true ,script: "cat "+env.WORKSPACE+"/cppcheck.xml | grep 'cppcheck version'"
if(cppcheck_result == ""){
echo "analyse cppcheck.xml failure,cppcheck.xml format error"
currentBuild.result="FAILURE"
}else{
final_result = sh returnStdout: true ,script: '''cat '''+env.WORKSPACE+'''/cppcheck.xml | grep severity=\\"error\\"'''
if(final_result == ""){
echo "analyse cppcheck.xml pass"
}else{
echo "analyse cppcheck.xml failure,cppcheck.xml has error"
currentBuild.result="FAILURE"
}
}
}
}else{
echo "cppcheck.xml file not exist"
currentBuild.result="FAILURE"
}
}
}
}catch(e) {
echo "cppcheck failure"
currentBuild.result="FAILURE"
}
5) 源码编译
try{
node(""){
stage("compile code"){
echo "compile code"
sh "make" //在docker里编译
}
}
}catch(e) {
echo "compile code failure"
currentBuild.result="FAILURE"
}
6) 验证扫描与编译结果,成功后归档相关文件
node(""){
stage("archive"){
echo "archive"
if(currentBuild.result == 'FAILURE') {
echo "Something unexpected happened. Please inspect Jenkins logs. Skip archive."
}else{
sh '''
mkdir build
cp cppcheck.xml build/
cp *.ko build/
tar -zcf archive.tar.gz build/
'''
archiveArtifacts artifacts: '*.tar.gz', followSymlinks: false
}
}
}
7) 根据扫描和编译结果,回填数据到GitLab
stage("commit gitlab"){
echo "commit gitlab"
if(currentBuild.result == 'FAILURE') {
updateGitlabCommitStatus(name: 'sw61_bsp_build', state: 'failed')
}else{
updateGitlabCommitStatus(name: 'sw61_bsp_build', state: 'success')
}
}
GitLab推送修改至仓库新分支,验证push event可触发jenkins job,jenkins回填数据至GitLab。




如下点击Actions按钮后选择自定义流程。

如上点击后产生如下main.yml文件。

如上点击提交代码后,Actions其实正在运行了。

如下是.yml文件怎么写?三类:name,on(触发时机),jobs(具体任务)。

name:流程命名为,会显示在图像化界面上。
on:[push] push代码时会触发。如下是src下文件发生改动的主分支下提交的代码才会触发当前的流程。

如下是每15分钟执行一次当前流程,有schedule(固定时间触发)就不能写push事件了。

具体触发的任务在jobs下,job1是命名,job2执行前必须是要job1执行完成。

runs-on是枚举类型,必须写如下8种。

steps:- (数组):git clone xxx 这样麻烦,如下uses中actions是github上有一官方用户叫actions,有一个仓库叫checkout,这仓库里面流程就是把代码下载下来【uses参数后面写另一个代码仓库,run参数后面写shell指令】。actions/setup-node@v1配置ubuntu-latest会安装好nodejs,这样下面npm就可用了。


timeout-minutes默认30分钟超时时间,指定超时时间如1分钟,1分钟内没有执行完会强制结束。如下指定3个版本的nodejs分别去测试。


如下是自己写的main.yml文件并执行。



如下nginx容器在当前ubuntu-latest环境下启动起来了。



开源项目的actions日志可以被看到,所以如下日志打印*,但变量已经写进去了,比如登录其他网站用到密码时候,在这个脚本中其实是可用的。


如上项目生成压缩包,剪切到桌面进行解压,并用idea打开,并点击idea中提示的Enable Auto-import。如下DaocloudApplication.java和DaocloudApplicationTests.java是默认有的,MyController.java和MySrc.java是自己添加的。

//MySvc.java
package com.example.daocloud;
import org.springframework.stereotype.Service;
@Service
public class MySvc {
public int add(int a,int b){
return a+b;
}
}
//MyController.java
package com.example.daocloud;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
public class MyController {
@Autowired
MySvc mySvc;
@GetMapping("/")
public String index(){
return "hello spring boot"; //首页
}
@GetMapping("/add")
public String add(int a, int b ){
return mySvc.add(a,b)+""; //最后加双引号是将数字变为字符串
}
}

如上运行后,浏览器输入如下。

如下添加测试接口在test文件夹下,本机控制台mvn test。
package com.example.daocloud;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DaocloudApplicationTests {
@Autowired
MySvc mySvc; // 对服务进行测试,只要测试mySvc
@Test
public void addTest() { // 测试用例,点击这行的开始播放按钮
int res = mySvc.add(772,119);
assert res == 891; // 892测试不通过,控制台报红
}
@Test
public void addTest2() {
int res = mySvc.add(556,119);
assert res == 675;
}
}
在GitHub上创建空的名为daocloud的仓库。如下push时出现Permission时将origin用的ssh换成https。



mvn test这个测试任务是在云端自动帮我们完成,云端并没有安装maven,云端是基于docker镜像的,改为如下。

// Dockerfile
FROM maven:3 AS bd
WORKDIR /code
COPY ./ /code
RUN mvn package -Dmaven.test.skip=true //会跳过测试,在target文件夹下生成jar包
// 如上利用maven3环境对代码进行打包
// 如下将这个jar包拷贝到java8镜像的根目录下并重命名为app.jar
FROM java:8
COPY --from=bd /code/target/*.jar /app.jar
CMD java -jar /app.jar // CMD是启动指令
在idea控制台中重新add,commit,push添加Dockerfile文件到github(上面3中项目)。再回到如下构建任务页面,点击保存,Dockerfile就是在项目根目录下。

如下返回流程定义页面,点击如下手动触发。

如下可以点进去查看日志。

如下先将github项目代码git clone拷贝到云端的一台机器上,加载了自己.yml默认配置文件,拉取了在测试时配置好的maven3镜像,真正运行配置了mvn test(下载了springboot依赖,时间长)。

如上测试阶段,如下构建阶段,docker build里就是上面Dockerfile内容,镜像构建完后,需要上传到daocloud的官方仓库上。


如下就可将镜像部署到自己机器上了。

如下解决上面的不自动化,点击导入主机,添加新主机。

如下用crt连接win上装的虚拟机(需安装docker),输入下面脚本。

如上点击查看主机出现如下。

如下在流程定义中添加发布阶段。


点击左侧的应用 - 创建应用 - 部署最新版本,如下ubuntu就是虚拟机。




如下创建子网穿透,可让互联网访问到。




修改了idea中代码并重新add,commit,push,通过提交代码方式重新触发了流程。

在集群管理中点击管理主机,可看见有容器(刚才被发布的)正在运行。

如下还要在应用中删除32768旧隧道,并新建32769新隧道。

执行CI:添加依赖,代码测试,将代码打包入docker并重新构建镜像。执行CD:生产机监听DaoCloud变化,拉取镜像并构建容器。