持续集成由以下环节组成:
当开发者将修改推送到版本库时,版本库会通知 CI服务,CI服务则会取出一份代码,交给 Runner进行检测,Runner将检测结果汇报给 CI服力。当然,开发者在提交代码之前,应该已经在自己的环境里做过必要的测试了。
Meteor已经选定 Velocity做为官方测试框架,通过 meteor run --test可以调用不同的测试工具完成单元测试到集成测试的各种测试,自带 reporter,同时也考虑了对 CI服务的支持。
例如https://github.com/dcsan/velo-ci是对接 Travis CI服务的示例,主要工作就是写了个 .trivis.yml放在项目根目录下,这样<通过还不清楚的机制>,meteor run --test 运行测试时会将测试结果汇报给 Trivis服务器。Trivis是 github.com提供的配套云端CI服务,对于开源项目免费,商业项目收费。
使用 Gitlab搭建内部的代码库通常是出于价格、网络速度、私有性方面的考虑,因此再搭建一个内部的CI服务也就顺理成章了。不过,其实合理设置内部的 Runner 服务器,使用公有 CI服务也是可行的。
服务器有很多选择。Gitlab是比较流行的仿 github 代码服务,它从2013年开始提供配套的 Gitlab-CI,这个CI还比较新,因此功能相对简单一点,界面相应的新颖一些。另一个更为成熟的开源 CI服务是 Jenkins,Gitlab一直没有提供对 Jenkins的支持,倒是 Jenkins社区提供了支持 Gitlab的插件。Gitlab如果内置对 Jenkins的支持,使用起来会更方便,比如 Gitlab内置了对 JIRA的支持,开发者可以在 commit message 里提一下 Closes #MyPrj-7,代码推送到版本库之后,Gitlab会通知 JIRA修改对应的 issue的状态。
所以嘛,既然上了 Gitlab,那就 Gitlab-CI吧。麻烦的是,东西太新,资料较少,折腾起来费劲。
首先当然是安装环境,如果机器富裕,直接用Omnibus安装包安装就好了,会更容易一点。我这没那么宽裕,所以是在一台 Docker服务器里折腾。
首先当然是做好 Docker环境了,然后参照https://peteris.rocks/blog/docker-gitlab-and-gitlab-ci/的说明进行安装。这个说明是能找到的最详细的傻瓜式教程了,不过还是有几个重要细节没提到,半道上出各种问题,还是得回头去看 github上原始的项目,好在sameersbn的说明文档很详细,重要的信息都在。现在看我这个改良版就行了,当然要愿意踩一遍坑加强一下感受,也是群众喜闻乐见的。
安装过程如下:
另外,所有的容器我都加上了 --restart="always",这样机器重起后就不用手工起动容器了。
EMail的话建议给 gitlab专门开一个用户,默认会用 GMail SMTP,国内的服务器小心连不上。
#创建 Redis缓存服务,其实不是必须的。
docker run --name=gitlab-redis --restart="always" -d sameersbn/redis:latest
#创建数据库,喜欢 MySQL的话去 github上看说明,我还蛮喜欢 Postegresql的,有几年工作就跟丫博斗。
docker run --name=gitlab-postgresql --restart="always" -d \
-e 'DB_NAME=gitlab' -e 'DB_USER=gitlab' -e 'DB_PASS=gitlab' \
-v /opt/gitlab/db:/var/lib/postgresql \
sameersbn/postgresql
#终于干正事了,创建 GitLab容器。
#VIRTUAL_HOST=dev.my.com这个环境变量对于 GitLab没用,是给还没安装的 nginx-proxy准备的,后面会说明。
#GITLAB_*是用来生成 GitLab配置文件用的:
# HOST一定要写外部ip或域名,不然 GitLab 网页上提示的代码库 URL全是 localhost的;
# 两个 PORT分别是 web服务和 ssh服务在 docker容器上的端口
#SMTP_*自然是配置怎么往外发邮件的,默认用GMail,我倒是挺喜欢 GMail的,可惜身在墙内呀。
#第一个 -v是将 Docker Host也就是主机上的 /home/git/data目录映射到容器内用来存储数据,不然容器一关数据就没了。
docker run --name=gitlab --restart="always" -d \
-e 'VIRTUAL_HOST=gitlab.my.com' \
-e 'GITLAB_HOST=gitlab.my.com' \
-e 'GITLAB_PORT=10080' -e 'GITLAB_SSH_PORT=10022' \
-e 'GITLAB_BACKUPS=daily' \
-e 'GITLAB_CI_HOST=ci.my.com' \
-e 'SMTP_DOMAIN=ym.163.com' \
-e 'SMTP_HOST=smtp.ym.163.com' \
-e 'SMTP_PORT=25' \
-e '[email protected]' \
-e 'SMTP_PASS=<你的密码>' \
-p 10022:22 -p 10080:80 \
-v /opt/gitlab/repos:/home/git/data \
-v /var/run/docker.sock:/run/docker.sock \
-v $(which docker):/bin/docker \
--link gitlab-postgresql:postgresql \
--link gitlab-redis:redisio \
sameersbn/gitlab
注意:第一次启动 GitLab会在 compile assests之类的环节工作很久,在完成之前访问 10080端口应该会出 502或者503错误,这是正常的!你可以用 docker logs git-lab 看看进行到哪了。如果看到下面这一堆就是好了:
2015-04-22 01:46:06,781 INFO success: sidekiq entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2015-04-22 01:46:06,781 INFO success: unicorn entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2015-04-22 01:46:06,781 INFO success: cron entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2015-04-22 01:46:06,781 INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2015-04-22 01:46:06,781 INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
这不是凭空变出来的,安装好 GitLab-CI以后它会提示你用这样一个 url去创建 app,不过 ci_dommain_name部分极有可能是 localhost或者 docker internal ip,不要用这个值,不然下一步你会发现 GitLab-CI网站上回 Gitlab进行 OAuth授权的链接都点不开。
把 GitLab生成的 AppID和AppSecret都记下来,或者开着这个页面别动也成,一会要用。
Peteris Rocks没提这两个重要的环境变量!如果已经把 GitLab-CI运行起来了,docker rm gitlab-ci重来吧。
#GITLAB_*说明 GitLab服务在哪里,虽然已经 --link了,但是要是不想被一堆奇奇怪怪的 URL困扰,_URL还是写上吧。
#-v让容器把数据库等文件存到 /opt/gitlab-ci/data里,
# 如果练手时 rm gitlab-ci-postgresql了,再创建容器时会报数据库已经存在了,这时可以去这个目录把 db目录删了。
docker run --name=gitlab-ci --restart="always" -d \
-p 11080:80 \
-e 'VIRTUAL_HOST=ci.my.com' \
-e 'GITLAB_URL=http://gitlab.my.com' \
-e 'GITLAB_APP_ID=刚才生成的ID' \
-e 'GITLAB_APP_SECRET=刚才生成的密钥' \
-e 'SMTP_DOMAIN=ym.163.com' \
-e 'SMTP_HOST=smtp.ym.163.com' \
-e 'SMTP_PORT=25' \
-e '[email protected]' \
-e 'SMTP_PASS=<密码>' \
-v /opt/gitlab-ci/data:/home/gitlab_ci/data \
-v /var/run/docker.sock:/run/docker.sock \
-v $(which docker):/bin/docker \
--link gitlab:gitlab \
--link gitlab-ci-postgresql:postgresql \
--link gitlab-ci-redis:redisio \
sameersbn/gitlab-ci
它怎么知道 GitLab在哪里呢?创建 GitLab-CI容器的命令里给的环境变更GITLAB_URL呀。
登录、授权之后,浏览器会跳回 GitLab-CI,现在 GitLab-CI就装好了。它会提示你:
Now you need Runners to process your builds.
Checkout the GitLab Runner section to install it
呵呵。
#这个容器会监视所有的 docker容器的启动,并扫描容器内部的 80端口映到 docker host哪个端口了,然后在 nginx里建对应的虚拟主机,将该容器的域名(由容器内的环境变指定)上的HTTP请求转发过去。
docker run -d --name="nginx-proxy" --restart="always" -p 80:80 \
-v /var/run/docker.sock:/tmp/docker.sock jwilder/nginx-proxy
GitLab官方已经把Runner环境程序都弄好了,放在https://gitlab.com/gitlab-org/gitlab-ci-runner项目里了。我们并不需要从头写程序访问 GitLab CI 的 API才能制作 Runner环境。
这一步需要 CI-Runner容器能够顺利访问 GitLab服务器(注意不是GitLab-CI而是GitLab),以便检出代码。以ssh方式的话需要安装信任证书。
CI_SERVER_URL/api/v1/runners/register.json
BODY:{
token:REGISTRATION_TOKEN,
tag_list:RUNNER_TAG_LIST,
description:RUNNER_DESCRIPTION
}
CI_SERVER_URL/api/v1/runners/register.json
CI_SERVER_URL/api/v1/builds/{id}.json"(id是GitLab-CI给的build id)
所以制作 meteor velocity runner就是要把环境准备好,然后用让项目调用 meteor run --test,用 console reporter 输出测试进度,测试失败时meteor run返回一个非0值即可。
话说,GitLab CI Runner其实简单,为此搭上一堆 ruby环境其实挺不值当的,什么时间有空拿 node.js重写一个就完美了。显然我不是第一个这么想的人,https://github.com/evanlucas/gcr已经做了。
从头写个 Dockerfile也没多麻烦,https://registry.hub.docker.com/u/vdubyna/gitlab-ci-docker-runner/就是个从头写的例子。直接用 docker hub上做好的也有,比如 bobey/docker-gitlab-ci-runner-node就不少人用。我们前面都在用 sameersbn的工作成果,其实也有一个 sameersbn/gitlab-ci-runner。用 Trusted Build派生一个环境多少能省点事。不过既然找到了 gcr,就做一个这样的吧:
gcr info [client] checking for builds...
gcr http 201 http://ci.my.com/api/v1/builds/register.json
gcr info [client] submitting build 1 to coordinator...
gcr http PUThttp://ci.my.com/api/v1/builds/1.json
gcr http 200 http://ci.my.com/api/v1/builds/1.json
gcr info [client] submitting build 1 to coordinator...
gcr http PUThttp://ci.my.com/api/v1/builds/1.json
gcr http 200 http://ci.my.com/api/v1/builds/1.json
gcr info [client] submitting build 1 to coordinator...
gcr http PUThttp://ci.my.com/api/v1/builds/1.json
gcr http 200 http://ci.my.com/api/v1/builds/1.json
gcr info [client] submitting build 1 to coordinator...
gcr http PUThttp://ci.my.com/api/v1/builds/1.json
gcr http 200 http://ci.my.com/api/v1/builds/1.json
gcr info [client] submitting build 1 to coordinator...
gcr http PUThttp://ci.my.com/api/v1/builds/1.json
gcr http 200 http://ci.my.com/api/v1/builds/1.json
gcr info [client] submitting build 1 to coordinator...
gcr http PUThttp://ci.my.com/api/v1/builds/1.json
gcr http 200 http://ci.my.com/api/v1/builds/1.json
gcr info [runner] build success [1]
Velocity如果读到环境变量VELOCITY_CI 有设置的话,就会以 CI模式运行,具体说就是在development环境里,会对每个已注册的testFramework调用 /reset和 /run,也就是每次运行时会把所有类型的测试都执行一遍。
!目前碰到的问题是 Mocha的 client tests不会执行,在Host Ubuntu 14.04里是好的,可见是 ubuntu:latest这个环境里有什么不对,还没有找到解决办法。怀疑是 Mocha对于 headless linux支持有问题,但是 mike:mocha里其实有PhantomJS的脚本,不过文档里没有提到类似 JASMINE_BROWSER=PhantomJS的选项,代码里也还没找到怎么启用这个脚本。测试下来 Jasmine的 client unit也有它的问题。
测试下来,2G内存的虚拟主机跑这个全 docker 的CI环境还是太免强了。还是直接在 VPS里搭环境省心。虽然 Docker 具有迁移方便的优点,不过将来把 GitLab/CI/Runner分别迁到自己的服务器里其实也没多麻烦。