测试 使用 Jenkins 在 Ubuntu 下构建 Rails 持续集成环境

lokyoung · 2016年12月09日 · 最后由 uestc_bird 回复于 2017年02月09日 · 10607 次阅读
本帖已被管理员设置为精华贴

前言

持续集成 (continuous integration),就是在敏捷开发中经常提到的 CI。
每一次代码提交更新都要通过 CI 中的自动化测试,这样可以 尽早发现现有的 bug。其目的在于让 产品快速迭代的同时,尽可能保持高质量
以我们做 Rails 的开发为例,为例保证项目的质量,我们都会写一定的自动化测试 (例如 RSpec、minitest)。在多人协作的项目中,我们会基于当前的开发 (develop) 分支,开一个新的分支进行开发,在代码合并到 develop 分支前,跑一遍测试,通过了才能合并。如果没有 CI,这样的一个操作都是开发人员手动在本地操作的,这样也会带来一些潜在的问题。首先由于测试都是由手动触发并且在本地运行,所以每次开发人员都必须要记住去跑测试,并且这个操作有时也会是很耗时的。其次每一个人本地的环境不一定是一致的,同样的测试在某一个开发人员本地通过了,在另一个人的 PC 或者线上并不一定可以通过。在引入了 CI 之后,每一次提交都会在 CI 服务器上运行测试,这样不仅将之前需要手动触发的操作自动化,同时也保证了测试是在统一的环境 (CI 服务器) 下运行的。

CI 保证了交付的质量和效率,给开发团队提供了极大的帮助。不过现有的 CI 服务价格通常都十分昂贵 (以 RubyChina 使用的 Travis CI 为例,对于开源项目是免费的,但是私有项目的最低价格高达$69/month)。所以很多团队会选择开源的持续集成工具搭建自己的 CI 服务,其中最出名的就是Jenkins了。社区里 Jenkins 相关的中文资料不多,我最近正好在做相关的工作,所以就整理成了这篇博客。

环境搭建

在这里首先介绍下我使用服务器的一些基本信息,我使用的是 digitalocean 的$10/month 的 VPS(单核 CPU、1GB 内存、30GB SSD,最开始时候用的是$5,但是每次使用 jenkins 进行 build 时都会因为内存不够而进程崩溃),使用的操作系统是 Ubuntu 14.04.5 x64,Jenkins 的版本是 2.19.4。你也可以选择在本地操作系统直接搭建或者使用 Vagrant 搭建。

Jenkins Setup

在 Ubuntu 上安装 jenkins

$ 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

这几段命令执行完成之后 jenkins 成功安装在你的机器上并且运行在 8080 端口上了,同时你系统中也新建了一个名为jenkins的用户。 这时你可以在浏览器中访问你服务器的 8080 端口,页面会提示你系统的初始密码存储在/var/lib/jenkins/secrets/initialAdminPassword这个文件里。从里面获取密码粘贴到界面的输入框后就完成了认证。认证之后会让你给你两个安装 plugin 的选项,我有些选择恐惧症,所以就选择了安装推荐的 plugin。 安装完成后会让你填写用户名、密码之类的基本信息,以后从浏览器登录 jenkins 后台时需要用到。填写完成后就进入了 jenkins 的 dashboard。

由于之后会使用 jenkins 用户安装 Ruby,需要 root 权限。所以在这里我们给予该用户 root 权限。

# 把jenkins用户加入sudo用户组
$ adduser jenkins sudo
# 设置密码(也可以选择在visudo设置NOPASSWD让用户请求sudo权限时不需要输入密码)
$ passwd jenkins

Ruby 环境搭建

首先安装 Ruby 相关的一些依赖

$ sudo su - jenkins
$ sudo apt-get install autoconf bison build-essential libssl-dev libyaml-dev libreadline6 libreadline6-dev zlib1g zlib1g-dev

在这里我使用了rvm去管理 CI 服务器上的 Ruby 版本。 安装 rvm

$ gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
$ \curl -sSL https://get.rvm.io | bash

将 rvm 加入 shell profile 中,在.bashrc 文件下加入下面这行

[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"

安装项目中的 Ruby 版本 (我的是 2.3.1)

$ rvm install 2.3.1
$ rvm use 2.3.1
# 安装bunlder
$ gem install bundler

安装一些 gem 相关的依赖,因为很多时候我们的 rails 应用需要 JavaScript runtime,所以在这里我也安装了 nodejs

$ sudo apt-get install libcurl3-dev libpq-dev nodejs

安装 PostgreSQL

在我的服务器上我使用 PostgreSQL 作为数据库

# 安装postgres
$ sudo apt-get install postgresql postgresql-contrib
# 设置postgres password,这里为了演示我直接设置成'password'
$ sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'password';"

接着配置好 database.yml 文件,在这里我直接在 jenkins 用户 home 目录下创建。

# ~/ci_database.yml

default: &default
  host:    localhost
  adapter: postgresql
  encoding: unicode
  pool: 5

test:
  <<: *default
  database: jenkins_test
  username: postgres
  password: password

ssh 配置

由于我们的 repo 大多数都是用 Git 进行管理的,所以我们也需要在 server 上安装 git

$ sudo apt-get install git

生成 jenkins 用户的 ssh key

$ ssh-keygen

这里由于我的项目的 repo 在 GitHub 上,所以之后我就以集成 GitHub 为例。
在 shell 中打印出 ssh 公钥,拷贝到你的 Github 用户下

$ cat ~/.ssh/id_ras.pub

建立 ssh 和 Github 的连接

$ ssh -T [email protected]

创建 jenkins job

首先进入你的 jenkins dashboard,点击页面左上方的New Item,在新的页面中输入你的 CI job 的名称,然后选择Freestyle project之后点击 ok 进入下一步。
下一个页面中是对你项目的一些配置。选择 GitHub project 后填写你项目在 GitHub 中的 url。
接着在Source Code Management中选择Git,在Repository URL填写 repo 的 url,由于我们之前配置好了 ssh,所以直接填写 ssh url([email protected]:username/repo.git)。
之后选择需要构建的分支,在Branch Specifier我填写了空,代表所有分支都要进行构建。
接着在Build中选择Add build step -> Execute shell,接着在其中添加你的 build 脚本。

下面贴出我的 build 脚本,大家可以针对自己需求自己定制。

#!/bin/bash -x                                 # 指定执行本段脚本的shell为bash,默认情况会使用sh
source ~/.bashrc                               # 读取rvm
bundle install
cp ~/my_database.yml ./config/database.yml     # 将之前写好的数据库配置yml文件拷贝到当前项目中
RAILS_ENV=test bundle exec rake db:setup       # 初始化数据库
RAILS_ENV=test bundle exec rake test           # 运行测试,我使用的是minitest

这时候基本的 Jenkins job 就配置好了,在 dashboard 中你可以自己点击 build 手动触发。每一次 build 都会从 Git repo 中拉取代码,运行你的 build 脚本。只有当脚本中所有的流程都通过并且成功之后,这个 job 的状态才会是成功的。
不过这对我们来说还是不够的,现在只能通过手动触发 build,还并不能在我们每次 commit 并且 push 到 Git repo 后自动完成构建。所以我们需要将我们当前配置好的 Jenkins job 和 Git repo 进行集成。

GitHub 集成

Commit 后自动构建项目

我们在 dashboard 中选中我们的 Project,点击Configuration再次进入配置界面。
Build Triggers配置触发构建的条件。这里我勾选了Build when a change is pushed to GitHub,这样会在每次 commit push 到 GitHub 上之后进行构建。这也是我们在项目中的通常做法。不过光在 Jenkins 中配置了是不够的,我们需要 GitHub 在每次收到 push 后通过 webhook 告知 Jenkins 可以开始进行构建。
在 GitHub 中进入你 project 的 repository,选择Settings -> Webhooks -> Add webhook接着填写你的 Jenkins hooks url,这段 url 就是 jenkins server 的 url 加上/github-webhook。例:http://my-jenkins-server/github-webhook/ 。 在Which events would you like to trigger this webhook? 中选择 Just the push event。 配置好后点击Add webhook
Note: 这里需要你的 jenkins 安装了 GitHub plugin,如果你在安装 jenkins 时选择了安装推荐的 plugin,是会给你默认装上的。

完成后再进行 commit 和 push,你会发现在 push 之后会自动触发 jenkins 的 build 环节。

更改 GitHub commit 状态

使用 CI 服务集成到 Github 时,通常有三种状态:

  1. pending: 代表构建正在执行中,尚未完成。
  2. passed: 代表构建成功。
  3. failed: 构建失败,通常可能由某些测试没有通过导致。

如果想显示这些状态,就需要 Jenkins 将构建的状态进行同步。

生成 GitHub access token

这里将 Jenkins 的 build 时的一些状态同步到 GitHub 上,Jenkins 向 GitHub 发送请求去同步这些状态,所以需要 GitHub 的 access_token。 进入你 GitHub 账号的 Settings 页面中,选中Developer settings -> Personal access tokens -> Generate access token。在Select scopes中选择 token 可以操作的 scope,选中repoadmin:repo_hook,之后点击Generate token。token 生成后复制到你的剪贴板上,我们在 Jenkins 的配置中会用到它。

配置 jenkins GitHub Configuration

进入 Jenkins 的 dashboard,点击左侧的Manage Jenkins -> Configure System,找到GitHub这栏,点击 Add GitHub Server,之后在 Credentials 那栏中点击Add。在 Kind 中选择Secret text,将之前复制的 access token 粘贴到 Secret 那栏,在 ID 中可以选择给这条 credential 命名。最后点击Add就完成了,然后在 Credentials 那栏选中你刚刚添加的 credential。点击屏幕下方的Save保存配置修改。

设置 commit status

回到 dashboard,进入 job 的 Configuration 界面,在Build中选择Add build step -> Set build status to "pending" on GitHub commit。添加后拖动这个 build step 到之前添加的Execute shell之前。这样才能保证在 build 开始前就把 commit status 设置成 pending。

之后在Post-build Actions中选择Add post-build action -> Set GitHub commit status。在Status result那栏中选择One of default messages and statuses。配置好后点击Save保存。这样配置完成之后,你在 GitHub 中每一次 commit 的状态都会随着 CI 的状态而改变。

结语

持续集成给现在的开发工作带来了很大的便利,将构建操作自动化,可以显著帮助开发者尽早发现现有系统中的问题。完成稳定而又高效的迭代。Jenkins 作为一个开源的持续集成软件,可以让我们可以自定义搭建一个免费的持续服务。
Jenkins 中也提供了各种各样强大的插件(例如 RVM 插件,可以让 RVM 安装并且使用当前 project 中指定的 Ruby 版本),现在的 Jenkins 自身也支持持续部署、持续交付的功能。个人认为 Jenkins 对于开发者还是有相当大的学习使用价值的。我在这里也仅仅是一个 Getting Started 的介绍,有不正确的地方希望大家指正,也希望有感兴趣的朋友以后能在社区里一起交流相关的问题,大家共同进步。

参考资料

Jenkins CI for Rails 4, RSpec, Cucumber, Selenium
Jenkins CI on Ubuntu

试试 Docker,再也不用为配置环境操劳 😃

#1 楼 @shawzt 嗯啊谢谢建议,确实如此,下一步可以做一个 Jenkins for Rails 的 Docker Image😃

👍 👍 。另外还可以加上 Rubocop 和 SimpleCov 的插件检测代码质量和测覆盖率。之前也用 jenkins + bitbucket 搭了这样的 CI 环境,可惜在团队里面推不起来😂

#3 楼 @ACzero 我们正好有项目有这样的需求,我最近就弄了下😀

gitlab CI 蛮好用的, job 都放在 Docker 里面跑

我觉得这篇文章可以加精了!推荐加精~ 加入 Wiki 也可以的

huacnlee 将本帖设为了精华贴。 12月13日 13:06

#5 楼 @42thcoder 谢谢推荐,之前没有尝试过 gitlab,准备深入了解下!

#6 楼 @pengedy 谢谢,其实就是篇 Getting Started 的教程,整理下分享给大家~

非常不错!

需要 登录 后方可回复, 如果你还没有账号请 注册新账号