说起来,Jenkins其实和Linux自带的crontab有点像,都是满足特定条件后执行脚本。但是Jenkins触发任务的形式更加灵活,并不局限于定时,所以还是很有用的。
安装
按照惯例,遇到Linux的问题先说环境:系统是Ubuntu16.04,已经提前安装了JDK 1.8,没有用docker,并且已经打开了防火墙。有时候安装好了但访问不了,就是因为防火墙没打开。
其实安装流程很简单,按照官网的流程走一遍就行:
wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins
如果这么安装能成功,那最好了。但是往往会遇到各种问题。我遇到的报错信息是这样:
Aug 06 22:33:57 iZuf699cacb5hvp8me6ft6Z systemd[1]: Starting LSB: Start Jenkins at boot time...
Aug 06 22:33:57 iZuf699cacb5hvp8me6ft6Z jenkins[24569]: ERROR: No Java executable found in current PATH: /bin:/usr/bin:/sbin:/usr/sbin
Aug 06 22:33:57 iZuf699cacb5hvp8me6ft6Z jenkins[24569]: If you actually have java installed on the system make sure the executable is in the aforementioned path and that 'type -p java' re
Aug 06 22:33:57 iZuf699cacb5hvp8me6ft6Z systemd[1]: jenkins.service: Control process exited, code=exited status=1
Aug 06 22:33:57 iZuf699cacb5hvp8me6ft6Z systemd[1]: Failed to start LSB: Start Jenkins at boot time.
Aug 06 22:33:57 iZuf699cacb5hvp8me6ft6Z systemd[1]: jenkins.service: Unit entered failed state.
Aug 06 22:33:57 iZuf699cacb5hvp8me6ft6Z systemd[1]: jenkins.service: Failed with result 'exit-code'.
一开始我看到Start Jenkins at boot time
,以为是端口冲突,因为这个报错是网络问题,而且Jenkins的默认端口是8080,和服务器上已经有的Tomcat冲突了。所以修改Jenkins的配置文件,把端口改成8085:
# /etc/default/jenkins
HTTP_PORT=8085
# /etc/init.d/jenkins
check_tcp_port "http" "$HTTP_PORT" "8085" "$HTTP_HOST" "0.0.0.0" || return 2
虽然看网上说只需要修改前一个,但最好两个文件都改了,因为只修改其中一个还是有可能报错。
解决了端口问题之后再次启动,仍然报错。这次是提示No Java executable found in current PATH
,但是我已经安装了Java……按照他给的提示,输入type -p java
进行检查。如果是配置不正确,输出应该为空;但是JDK的路径好好地躺在那呢。而且Tomcat就在8080跑得好好的,说明不是这个问题。建立软链接:
ln -s /usr/local/jdk1.8/bin/java /usr/bin/java
但是还是报错。事实上,不应该在/usr/bin里建立链接,而应该在/bin里建立链接。也就是说,命令应该长这样:
ln -s /usr/local/jdk1.8/bin/java /bin/java
至于原因,因为我是在root用户下操作的,/usr目录属于其他用户,自然没用。同样的,对于其他用户,软链接放在/bin也不一定有用。比如下文会提到的,Jenkins属于其他用户,如果放在/bin里会遇到问题。但是这里只是启动服务,而且启动服务(作为系统服务,开机自动启动)确实需要root权限,所以放在/bin里是合适的。
如果不出问题,Jenkins应该可以访问了,地址是[服务器IP]:[之前设置的Jenkins端口],比如192.168.0.1:8085。
然后就是初始化设置,这里不再赘述,按照引导输入解锁密码后,选择安装推荐的插件(一般来说推荐的就足够了),一路往下就行。
一般来说,安装完并且设置完用户后,页面会自动跳转。当看到这个界面的时候,就可以开始下一步的配置了:
这里可能遇到白屏的问题。如果Jenkins安装完之后一直白屏,也就是一直卡在SetupWizard [Jenkins] 这个页面,首先试试刷新页面,如果刷新页面之后仍然没有反应,到服务器上重启Jenkins服务(systemctl restart jenkins
)一般就能解决。
至于无法访问,一般是配置问题,最好去检查一下之前的端口设置,以及Java环境是否配置正确。
配置
首先需要强调的是,Jenkins运行时是没有root权限的。因为我们从/etc/passwd文件里可以看到:
jenkins:x:110:119:Jenkins,,,:/var/lib/jenkins:/bin/bash
Jenkins就是一个普通用户,所以与它相关的设置要在/usr/bin目录下(比如软链接),进行操作的时候也需要用到sudo命令。这一点非常重要,可以避免很多不必要的麻烦。
因为我们操作一般是在root用户下操作的,但是Jenkins执行的时候没有root权限,如果不注意这些,很有可能出现找不到命令或者Permission denied之类的bug。
因为是前端的部署(项目源码在Github上,但不部署到Github page,不然为什么不用Travis呢?),肯定需要在服务器上build,所以需要Node环境,也就是node + npm(一般来说,新版本的node都自带npm)。
安装的过程其实很简单,就是在官网上下载Linux的压缩包,解压后将/bin目录添加到环境变量里(或者软链接,下面会提到)。安装的教程网上有很多,就不赘述了。不过有三点需要注意:
-
最好去英文网站下,中文网上的Linux压缩包是32位的,而现在大部分服务器已经是64位了,尤其是云服务器。虽然一般来说64位能兼容32位,但是最好还是用64位的,以免增加不必要的麻烦。
-
不要用apt之类的包管理工具直接安装。以我这次的Vue项目为例,官方明确表示需要8.9+的node:
Vue CLI 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。
而通过apt下载的node基本上是4左右的老版本,还需要手动设置和升级。至于下载解压后配置环境变量和通过apt安装后升级哪个更好,就见仁见智吧。
-
如果只需要用到node和npm两个命令,通过软链接放进/usr/bin里是可以的;但是如果某些依赖需要npx,以及为了避免一些可能存在的麻烦,可以选择添加环境变量。但是如果添加环境变量,一定要添加到对应用户(一般是jenkins吧……应该没人无聊到去改?)的环境变量里,不然Jenkins会无法识别node命令,在执行脚本的时候报错
/usr/bin/env: ‘node’: No such file or directory
。
安装好了之后,记得用node -v
和npm -v
确认一下。
然后就是配置SSH。因为需要和Github仓库关联,所以SSH是必要的。获取SSH秘钥的方法跟Git一样,就不再赘述了,基本上就是通过ssh-keygen -t rsa
,然后进行设置;而且基本上支持SSH服务的服务器上自带ssh-keygen,也不需要什么额外的配置。然后/root/.ssh/id_rsa是私钥,/root/.ssh/id_rsa.pub是公钥,后面会用到。
生成SSH秘钥并且添加到Github里之后,记得clone一次试试,看看能否正常运作。
然后去Jenkins的“凭据”里添加一个全局的凭据,位置在这里:
类型选SSH私钥,然后输入从/root/.ssh/id_rsa里获得的私钥并保存就行:
接下来还需要配置一下Github的webhook服务。到系统管理 > 系统设置里找到Github,然后点击高级,选择“为 Github 指定另外一个 Hook URL”:
然后把这里的hook URL复制到对应仓库的webhook里即可:
添加之后Github会发送一些测试数据来测试连通性。通了之后就可以继续了。关于这一步的必要性,虽然有些人说可以不用,但实测直接用Jenkins的地址无法连通。
然后就是添加一个项目。新建任务的时候最好选freestyle,后面也是基于freestyle的操作。
源码管理选择Git,然后把仓库的Git地址写进去,然后credentials选之前设置的那个凭据(我直接用了默认名称jenkins):
填写完成之后可能还会显示Please enter Git repository,稍微等一下就行,感觉Jenkins反应稍微有点迟缓……
如果这一步出现HTTP ERROR 403:
刷新一下页面就行;我推测是因为网络波动或者网络环境有变化。
构建触发器选择Github hook trigger,因为Github有相应的webhook,不需要太多额外的配置。
构建一般选shell脚本吧……然后就是把自己的操作流程写成脚本放上去:
写Linux脚本并不是什么大问题,但还是有几点需要注意:
-
一般来说,Jenkins会自动识别服务器的脚本运行环境。但是如果执行不了,可能需要在最前面加上类似于
#!/bin/bash
的语句来帮助识别。 -
不要忘了Jenkins没有root权限,所以命令前面要加上sudo,不然会因为Permission denied导致构建失败。
-
如果在脚本里用到了git pull,有可能会报错Please tell me who you are,需要设置一下用户名和邮箱。如果已经设置了用户名和邮箱,但是还是报这个错:
- 首先检查是不是设置在root用户下的(这个很常见,相当于没设置);
- 然后,不要在脚本里使用类似于
sudo git config --global user.email "you@example.com"
的命令来进行设置,会报错。 - 此外,没必要在服务器上设置(而且可能还得切换到Jenkins这个用户上)。
解决这个问题,用Jenkins自带的Git plugin就行了,这个插件在系统设置里:
-
如果在移动文件的过程中遇到用了sudo还是显示Permission denied,或者Jenkins直接没有权限使用sudo的情况,可以试试chmod 777来给权限(一般来说,放在/usr里的文件Jenkins是有权限的),或者直接修改/etc/sudoers给Jenkins权限:
jenkins ALL=(ALL) NOPASSWD: ALL
在/etc/sudoers的最后一行添加上面的语句即可。如果对Jenkins不放心,可以把后面的ALL改成需要执行的命令。
-
如果在执行脚本的过程中长时间卡住,甚至是直接无法访问Jenkins(包括其他服务也无法访问,比如SFTP,类似于死机),一般来说有两种情况:
-
服务器硬件不行,比如硬盘空间不足(这个有可能不是卡住,而是直接失败,看情况),或者是内存不足。这种情况,如果是硬盘空间不足,清理一下服务器;如果是内存不足,关掉后台运行的一些进程(比如jar包)或者直接重启试试。如果还不行,这边的建议是加钱(。
-
有可能脚本需要命令行交互,或者是启动了某个常驻命令行的服务。针对前一种,比如使用cp命令复制的时候,如果遇到覆盖,会要求确认;但Jenkins自己并不能响应,所以就会一直卡住。解决这个问题一般是加上-f来强制执行,比如把
sudo cp -r
换成sudo cp -rf
。而后一种,比如我在build的时候用到了webpack bundle analyzer的打包分析插件,但是这个启动之后会一直占用8888端口,并且常驻命令行,导致Jenkins无法完成构建,一直卡在这里:Webpack Bundle Analyzer is started at http://127.0.0.1:8888 Use Ctrl+C to close it
解决这个问题也没什么好办法,只能说尽量避免使用这种服务吧……实在不行可以在服务器上手动杀死那个常驻的进程。
# 找到8888端口对应进程的pid sudo lsof -i :8888 # 用上一步找到的pid杀死进程 sudo kill -9 xxxx
-
再往后?保存呗。然后可以点击左上角的立即构建,或者去对应的仓库里push一次来进行测试。收到通过Github的webhook传来的消息后,Jenkins会立刻开始执行脚本。执行脚本过程中遇到的一些常见问题上面已经提到了。
如果看到左下角的build history里变成了蓝色,说明构建成功了:
如果变成红色,说明构建失败。不要随便怀疑Jenkins,绝大多数情况下是自己的脚本写错了。
再然后?没有然后了。我们已经可以从手动部署中解脱出来了(笑)。