本文使用SpringBoot项目完成一个简单的Jenkins实践,利用docker-maven插件实现Docker从build到run再到push的完整流程,大大简化了Jenkins相关工作,不必再为Jenkins配置证书和编写额外的脚本.

写在前面

简化的关键在于fabric8io的docker-maven-plugin插件,利用它可以实现用maven指令对docker远程控制,网上其他文章的思路大多是利用spotify的maven插件完成docker镜像的build和push,再编写脚本文件,让Jenkins通过SSL在docker主机上远程执行脚本,达到在远程主机运行docker容器的目的.
这种做法第一是太麻烦,远程执行脚本需要配置SSL免密码访问,第二是SpringBoot项目和脚本耦合度高,由于镜像是在SpringBoot的插件完成相关构造的,而脚本其实就是死代码,必须人为地保证两者的统一协调.
而使用fabric8io的docker-maven-plugin则可以完整地控制docker镜像/容器的生命流程,从而避开这些问题.当然,用脚本自由度会比较高,可以做其他的事情,这个见仁见智.

以下是关于docker远程控制和插件使用的两篇文章:
Docker 守护进程+远程连接+安全访问
最强大的Docker插件 fabric8io/docker-maven-plugin

另外,Jenkins跑起来是比较耗资源的,如果Jenkins运行时突然挂掉,那多半是因为内存不足.我是使用docker来运行Jenkins的,有时候它还会把别的docker容器给挤挂了,无奈下只能另买一台阿里的学生机供Jenkins使用

如下图,是对Jenkins容器的单独监控,可以看到工作时运行内存占用在1.2G左右,这还只是在Docker内部,如果docker还有其他的容器在运行,再加上linux其他的服务,其实内存很容易就不够用的,另外,即使不挂,内存不够的情况下也可能导致Jenkins内部的任务执行出错,这种错误往往来得很莫名其妙,下文遇到的时候再介绍.
Jenkins容器监控

一 部署Jenkins

我使用的是docker部署,指令如下

docker run -p 9003:8080 -p 9004:50000 -v /opt/docker-volume/jenkins2:/var/jenkins_home -u root  --name lin_jenkins -d  jenkins/jenkins:lts

这里需要注意的是,jenkins/jenkins:lts并不是默认的官方镜像,官方镜像已经被废弃(deprecated )了,jenkins在Docker Hub的镜像首页推荐使用jenkins/jenkins:lts.

另外,因为Jenkins会产生比较多的文件(比方说一些插件和运行环境)在/var/jenkins_home目录下,所以建议将这个目录挂载出来

接下来登录 IP:9003 就可以访问Jenkins了

二 配置Jenkins环境

1 初次登录

  1. 使用管理员密码
    初次访问Jenkins需要使用管理员密码,密码在”/var/jenkins_home/secrets/initialAdminPassword”下.
    由于我们已经把/var/jenkins_home挂载到/opt/docker-volume/jenkins2了,所以直接运行cat /opt/docker-volume/jenkins2/secrets/initialAdminPassword就可以获取密码,复制粘贴点继续
  2. 安装推荐的插件
  3. 创建第一个管理员用户,如果只是想尝试的话也可以直接点下面小字的”使用admin账户继续”
  4. 一直按下一步指定进入Jenkins

2 全局工具配置

“系统管理”->”全局工具配置”
全局工具配置
因为是在docker内,使用外部系统的JDK和Maven等工具比较麻烦,所以我这里直接使用自动下载,这里需要自己选定配置的工具有JDK,Git,Maven

注意,全局工具只有在第一次用到的时候才会去下载,以Maven为例,如果含”mvn”命令的shell脚本在第一次”调用顶层Maven目标”之前执行,则会报“sh: mvn: not found”错误
1. JDK
我选用的是JDK8,注意自动安装JDK的话需要Oracle账号
JDK
2. Git
Git
3. Maven
Maven

最后记得点 Save保存

3 插件管理

“系统管理”->”插件管理”
先点击”可选插件”,再通过右上角的”过滤”来筛选,这里我们需要安装两个插件,GitLab,Maven Integration

  • GitLab插件的作用是当gitlab有push时触发jenkins拉取代码和将构建状态发送回GitLab
  • Maven Integration用来简化建造Maven工程.

4 全局安全配置

“系统管理”->”全局安全配置”
关闭防止跨站点请求伪造,原因如下:

webhooks与jenkins配合使用时提示:HTTPStatus403-Novalidcrumbwasincludedintherequest,这是因为jenkins在http请求头部中放置了一个名为.crumb的token。在使用了反向代理,并且在jenkins设置中勾选了“防止跨站点请求伪造(Prevent Cross Site Request Forgery exploits)”之后此token会被转发服务器apache/nginx认为是不合法头部而去掉。导致跳转失败。

防止跨站点请求伪造

5 系统设置

“系统管理”->”系统设置”

1. 配置环境变量

这里主要是设置”全局属性”中的”环境变量”
这里我们要设置的环境变量是Maven,如果环境变量没有设置好的话,会导致在非Maven任务(如自由风格任务)的时候使用shell命令是提示找不到mvn指令(Cannot run program “mvn” error=2, No such file or directory),或者是找不到其他的指令也是同理
另外,虽然上一步已经设置好Maven工具,但现在还没有下载,可以通过运行任务时在Maven顶级目录调用-v查看,这里我直接给出路径,前面的路径是一样的,后面的是安装时候给的Maven名

/var/jenkins_home/tools/hudson.tasks.Maven_MavenInstallation/MyMaven

环境配置如下图,就是增加了Maven的目录,再将maven目录下的bin添加到原Path里,使bin下的mvn指令可以在Path找到,就可以执行了
Path的值为:(用冒号而不是分号分隔)

$Path:$MAVEN_HOME/bin:

环境变量配置

2. 配置Gitlab

很多教程都有配这个,但这个又不能用于”源码管理”模块拉取代码,我一直很困惑这个配置有什么用,然后就找到了该插件的github项目地址才弄明白,链接:https://github.com/jenkinsci/gitlab-plugin

上面说了”GitLab”插件的作用是当gitlab有push时触发jenkins拉取代码和将构建状态发送回GitLab,这分别对应了两种状态:GitLab-to-JenkinsJenkins-to-GitLab,这里就是配置对应的授权信息的

  • 第一是Enable authentication for ‘/project’ end-point
    这个打个勾就可以了,它是负责GitLab-to-Jenkins身份验证的开关,如果这里关闭的话,任何人只要知道你项目的钩子地址(webhook URL)就可以疯狂触发任务,而且通常webhook URL又是按规则生成的,很容易猜,不安全,所以不建议关闭身份认证.
    另外,身份认证有两种,全局认证和每个项目认证,全局认证并没有减轻多少工作量(毕竟每个项目的webhook URL不能一样),而且还会带来全局密码泄露的风险,所以通常用的都是项目单独认证.感兴趣的可以去上面给的gitlab插件项目地址看使用说明.

  • 第二就是GitLab connections
    这里提供的是Jenkins-to-GitLab身份验证,此身份验证配置仅用于访问GitLab API以将构建状态发送到GitLab。它不用于克隆git repos。克隆凭证(通常是SSH凭证)应该在git插件中单独配置。

    1. 第一步,先到GitLab去获取授权token
      “User Settings”->”Access Tokens”
      注意这里使用的token权限范围是api
      GitLab Api Access Token
      点击创建后会出现Token,复制下来即可
    2. 点击Gitlab下Credentials旁边的Add添加授权信息
      添加凭证
    3. Test Connection,出现Success即可
      完整配置成功

三 配置Jenkins任务

1 创建任务

返回首页,选择”创建一个新任务”,如下,这里我们创建的是一个自由风格的项目,也可以选择maven项目(这是由于我们上面装的插件才有的),maven项目会配置好Maven环境变量,也就是说可以在shell运行”mvn”指令而不需要我们上面的环境变量配置,选定好确认进入
创建任务

2 丢弃旧的构建

这里保持构建的最大个数我设置为10
保持构建的最大个数

3 源码管理

源码管理选择Git

  • 配置Repository
    填写项目git地址,下面再配置访问凭据,过程和上面配置Api Token 的差不多,只不过是换成了使用用户名和密码.
    另外,需要注意,更常见的做法是使用SSH用户名配合私钥访问GitLab(SSH Username with private key),这是GitLab的教程说明https://gitlab.com/help/ssh/README#generating-a-new-ssh-key-pair,这里我就直接用密码登录了,有兴趣自己看
    Repositories
  • 配置Additional Behaviours
    另外还有一个大坑要注意,源码拉取的默认超时时间是10分钟,而GitLab本身访问就慢,大一点的项目在第一次拉取的时候往往会因为超时而拉取失败,这里就需要我们自定义超时时间.
    首先找到Additional Behaviours,点击旁边的Add,选择Advanced clone behaviours选项,在出来的配置里对Timeout (in minutes) for clone and fetch operations配置,我填的是200分钟
    超时配置

4 构建触发器

  • Jenkins配置
    选择Build when a change is pushed to GitLab. ,打开高级菜单,配置Allowed branches,默认是允许所有分支触发,这里我限制为只有 master才可以.关键要配置的是Secret token,点击Generate产生token
    “GitLab webhook URL”和”Secret token”GitLab需要用到,”GitLab webhook URL”是触发地址,”Secret token”则是访问认证,如上文所说,如果关闭了Enable authentication for ‘/project’ end-point的话,只要知道”GitLab webhook URL”任务就可能会被恶意触发
    触发器配置
  • GitLab配置
    到项目Settings下找到Integrations,在URL和Secret Token填入上一步获取到的值,然后点击Add webhook
    GitLab hook

5 构建

注意,这一部分要根据自己项目的功能来.需要注意的是,如果构建步骤里需要运行程序,那Jenkins会认为程序的运行也是构建的一部分,简单来说,就是运行个Java Web服务,由于程序一直不结束,所以构造也一直不会停止.
解决办法是保证成后台运行,并用BUILD_ID=dontKillMe避免构建结束时进程被杀.

以使用fabric8io的docker-maven插件为例:
相关使用见:
Docker 守护进程+远程连接+安全访问
最强大的Docker插件 fabric8io/docker-maven-plugin
这里我们分两部分来,因为run比较特殊,把它单独放在第二部分

  • 第一:调用顶层Maven目标
    注意,Maven版本那里不要选默认,要用自己的版本,目标那里输入Maven指令,不含”mvn”
    用Maven环境执行程序,执行如下指令,利用对远程docker主机的控制完成对原容器和镜像的移除,再新build镜像并推送
clean package docker:stop docker:remove docker:build docker:push
  • 第二:执行shell
    1. 用”BUILD_ID=dontKillMe “使Job退出后不杀掉在构建过程产生的Java进程(即我们的SpringBoot服务)
    2. 用”nohup mvn docker:run & “将SpringBoot的web服务伪装成后台运行,是Job得以退出而不是一直输出SpringBoot的控制台信息
BUILD_ID=dontKillMe
nohup mvn docker:run &

如果只是想尝试一下运行,不使用docker相关的话,则直接用shell运行如下指令即可

BUILD_ID=dontKillMe
nohup mvn clean package spring-boot:run &

构建

保存就可以了

四 测试运行

可以在工程主页点立即构建,或者在GitLab的Webhooks那里点Test,选择Push events,第一次为了验证和GitLab的连接,就用第二种方式吧
Test
然后就可以了,第一次拉取代码会比较花时间,而且可能失败.

五 常见问题

  1. Jenkins启动后自动结束退出
    可能原因:内存不足
  2. Jenkins执行shell脚本,提示“sh: mvn: not found”
    • 可能原因1:未配置好环境变量
    • 可能原因2:Jenkins的全局工具要等到第一次使用时才会下载,如果含”mvn”命令的shell脚本在第一次”调用顶层Maven目标”之前,则会报错
  3. Jenkins执行shell脚本,提示“未找到命令”
    可能原因:Jenkins默认情况下执行shell脚本是使用非登录方式,然而非登录方式不会加载 /etc/profile 文件,因此找不到在/etc/profile设置的环境变量,会提示“命令未找到”,解决办法是:在 Execute shell 中 添加如 #!/bin/sh -l 命令修改为登录方式即可
  4. “调用顶层Maven目标”时报错Cannot run program “mvn” error=2, No such file or directory
    可能原因:Maven版本那里选择了默认(中文是(默认),英文是(Default),都是有括号的)而没有选择你自己配置的
  5. shell脚本/ ant / maven执行完毕,但Jenkins仍持续运行
    可能原因:”Spawning processes from build“,详情见https://wiki.jenkins.io/display/JENKINS/Spawning%20processes%20from%20build,简单地理解就是Jenkins构建的一部分可能是使用构建结果启动新的应用程序服务器,而Jenkins会捕获子进程的输出,所以看起来就是构建日志在监控重Shell/Maven等创建出来的程序的控制台输出,这个时候关闭Jenkins也会顺带地关闭其子进程而使web服务结束,解决办法就是将新进程标志为非Jenkins创建的,以此来躲避ProcessTreeKiller的追杀.ProcessTreeKiller是靠BUILD_ID来追踪识别的,所以把它改了就行BUILD_ID=dontKillMe,注意,如果Jenkins Pipeline使用JENKINS_NODE_COOKIE而不是BUILD_ID.同时,该进程不能是在前台运行的,可以通过daemonize 来包装成守护进程,但这需要额外安装,我使用的方法是用nohup + 指令 + &.
  6. Jenkins任务结束后 将 Web进程(守护进程)杀掉
    可能原因:见上
  7. Maven执行package时莫名出错,而且不给出原因
    可能原因:请检查内存,保证可用内存不低于100M
  8. 如果是构建Maven项目的话,中间是相当于”调用顶层Maven目标”的build,前后各可用添加Steps,我实践发现有时在后面的”Post Steps”并不一定在中间的”Build”执行完后才开始,但是Per Steps一定在Post Steps之前执行.所以当发生次序问题时可以通过插入无关步骤来修正顺序,当然,这一条我也不确定,各位看看就好.
  9. 其他
    暂时没想到,Jenkins的重启方法是访问:Jenkins地址/restart

参考:
https://wiki.jenkins.io/display/JENKINS/ProcessTreeKiller
https://wiki.jenkins.io/display/JENKINS/Spawning+processes+from+build
https://github.com/jenkinsci/gitlab-plugin
https://blog.csdn.net/wangbin0016/article/details/41948171
Docker 守护进程+远程连接+安全访问
最强大的Docker插件 fabric8io/docker-maven-plugin

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐