看了几天的docker,感觉好极了。现在回到我一开始的目标:构建一个团队内部的持续集成(下文统称CI)环境,并梳理出适合我们自己的工作流。
今天我们主要是来搭建依赖的环境:
之前学习docker时,一直都是基于自己的工作机,装的是win7 64bit,win下和docker相关的问题可以看我之前的文章。由于我们现在是要搭建一个自动化CI环境,提供web ui来供用户使用,所以环境搭建部分的操作完全不需要使用者参与,就不存在之前考虑的所谓的OS水土不服问题!介于网上多数资料都是使用ubuntu来作为docker的宿主系统,所以我这里就放弃了亲爱的centos(其实centos也是没问题的,我只是低调的炫耀自己通吃而已~见谅)。
老规矩,先来共享一下相关的文献资料:
我们这次不使用win下的dockerToolbox来安装docker了,而是直接在虚拟机中安装unbuntu系统,然后直接在unbuntu中安装docker:
1
|
curl -sSL https://get.docker.com/ | sudo sh
|
目前这样的安装方式,依然需要注意,后面运行gitlab容器时配置的端口映射只是将容器的端口映射到了ubuntu虚拟机系统上,如果想让物理机直接访问,还需要在virtualBox上再进行一次端口映射,具体步骤在这篇文章中我已经详细介绍过了。
docker环境装好后,就需要开始下载所需要的镜像了:
由于我们是在国内,所以直接使用hub.docker.com来下载无异于浪费生命,还好国内也有良心镜像库:
这里需要提醒的是,尽管灵雀云上显示的sameersbn/gitlab
镜像版本是”7.14.1”,不过别担心,只是md文件没有更新而已,可以切换到页面的“版本”选项卡来确认latest
版本。
这样基本上20分钟就可以把三个镜像全部下载到本地啦~
镜像下载完毕后,根据官方文档我们需要创建对应的docker容器来启动相关的服务,我并没有使用官方提供的第一种方法(docker-compose),主要是不熟悉~
我们手动来完成三个容器的启动:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#启动postgresql容器
docker run --name gitlab-postgresql -d \
--env 'DB_NAME=gitlabhq_production' \
--env 'DB_USER=gitlab' --env 'DB_PASS=password' \
--env 'DB_EXTENSION=pg_trgm' \
--volume /srv/docker/gitlab/postgresql:/var/lib/postgresql \
index.alauda.cn/sameersbn/postgresql
#启动redis容器
docker run --name gitlab-redis -d \
--volume /srv/docker/gitlab/redis:/var/lib/redis \
index.alauda.cn/sameersbn/redis
#最后启动gitlab容器
docker run --name gitlab -d \
--link gitlab-postgresql:postgresql --link gitlab-redis:redisio \
--publish 10022:22 --publish 10080:80 \
--env 'GITLAB_PORT=10080' --env 'GITLAB_SSH_PORT=10022' \
--env 'GITLAB_SECRETS_DB_KEY_BASE=kazaffisagoodcoder' \
--volume /srv/docker/gitlab/gitlab:/home/git/data \
index.alauda.cn/sameersbn/gitlab
|
注意,上面的命令我已经改成使用我们本地镜像的名字了,还有在启动gitlab容器时我使用了一个自定义的字符串来赋值GITLAB_SECRETS_DB_KEY_BASE
,由于是本地测试环境,外加我实在不知道如何通过在--env
命令中使用shell变量来使用pwgen -Bsv1 64
生成的随机字符串(手动输入简直等于自杀!)~知道如何做的朋友请留言赐教,不胜感激。
根据自己的硬件配置,可能需要稍等那么一分钟左右,在做好物理机端口映射配置后,我们就可以在本地浏览器中访问http://localhost:10080/来使用gitlab了!(可能由于gitlab首次启动初始化问题,你可能会看到gitlab提示的500错误,稍等一下再刷新即可)
现在你基本上就已经拥有了本地的gitlab环境,你可以使用本地的git客户端来创建一个gitlab测试项目,试试clone到本地,试试commit到gitlab,应该妥妥的~
这部分内容文章开头给的文档并没有汉化,不过没关系,我们都懂英文,对吧?
出于性能考虑官方不推荐将Runner安装在和gitlab同一台物理机上,出于安全考虑也不推荐将其安装在独立的物理机上,靠,也只能用docker来跑了!
这里还有两个概念:
前者适配有特殊需求的项目,同时也可以避免重要的项目runner资源被占用的问题。
而如果多个项目的优先级一样,并且有非常相似的依赖环境,那使用共享Runner是一个不错的选择。
既然我们决定使用docker容器来跑runner,那就先选个镜像吧,官方提供的是:gitlab-ci-multi-runner,我是用的灵雀云镜像了,注意,这里灵雀云镜像上的md介绍依然是错误的,镜像本身是和官方镜像一致的,小不完美啊 :———(~
还要说的一点是,官方提供的这个镜像是纯净镜像,不包含任何其它的环境(例如java环境或node环境),而你需要根据自己的项目实际情况来创建满足的自定义镜像。官方提供了几个语言环境的例子~
这种使用runner的思路相当于我们拿这个运行着runner的docker容器当作一个“物理机”,然后在这台“物理机”上做.gitlab-ci.yml
指定要做的事儿。在此基础上,我们依然可以让Runner执行docker build模式,这里就涉及到“docker-in-docker”的思想了。具体细节官方提供了比较清晰的文档。(其实,如果你使用文档中提到的“docker-in-docker”方案的话,那实际上就是“docker-in-(docker-in-docker)”,好绕啊~)
提到Runner的executor的类型,文档里有提到不少(shell,docker,ssh,等等),差别我感觉都不是太大,主要还是shell和docker两大类。
理论姿势就这么多,接下来我们跑起我的runner容器:
1
2
3
4
5
6
7
|
mkdir -p /srv/docker/gitlab-runner
docker run --name gitlab-ci-multi-runner -d --restart=always \
--volume /srv/docker/gitlab-runner:/home/gitlab_ci_multi_runner/data \
--link gitlab:gitlab --env='CI_SERVER_URL=http://gitlab/ci' \
--env='RUNNER_TOKEN=你gitlab生成的token' \
--env='RUNNER_DESCRIPTION=myrunner' --env='RUNNER_EXECUTOR=shell' \
index.alauda.cn/sameersbn/gitlab-ci-multi-runner
|
这里需要提醒的是两点:
接下来刷新你的gitlab管理员后台,你将会看到注册成功的Runner信息。
还没完,由于我们使用的是shell
模式,所以我们还需要进入到runner容器中来安装docker环境:
1
2
3
4
|
docker exec -it gitlab-ci-multi-runner bash
curl -sSL https://get.docker.com/ | sudo sh
sudo usermod -aG docker gitlab-runner
sudo -u gitlab-runner -H docker info
|
如果可以看到正确的docker版本信息,那就说明一切顺利。但怎么可能那么顺利!! 事实证明我太天真了,在docker容器中由于文件系统是只读的,所以无法安装docker环境~至少我们现在的做法还不行,如下图:
{% image http://pic.yupoo.com/kazaff/FCMGzWlk/143Y2S.png '' '' %}
这里说一个插曲,当得到这个结论后的我失望的打算将这个悲剧的镜像删除,我在gitlab admin后台的runner页面点击“remove”按钮,然后又将对应的docker容器也删除掉。当我再次使用相同的命令打算启动一个干净的runner容器时,我发现gitlab的runner页面不在提示成功注册runner了!我彻底方了!一切都和第一次执行流程一致,为啥这次就不认了呢?
于是我发现了一个秘密,上面的创建runner容器的命令中有一个参数:
1
|
--volume /srv/docker/gitlab-runner:/home/gitlab_ci_multi_runner/data
|
我在宿主机的/srv/docker/gitlab-runner
目录下找到了答案,原来一直试图想找到的config.toml
文件在这里,也是因为这个文件的缘故所以gitlab才不在接受我的runner注册的!删除并重建这个目录即可!
走了弯路不可怕,可怕的是接下来不知道怎么办?没关系,我们现在先在宿主机上安装gitlab-ci-multi-runner环境,并使用官方提供的docker-in-docker方式来增加一台runner:
1
2
3
4
5
6
7
8
9
|
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | sudo bash
sudo apt-get install gitlab-ci-multi-runner
sudo gitlab-ci-multi-runner register -n \
--url http://localhost:10080/ci \
--token 你gitlab生成的token \
--executor docker \
--description "My Docker Runner" \
--docker-image "index.alauda.cn/library/docker:latest" \
--docker-privileged
|
记住,我们这里要填写的gitlab-ci coordinator URL应该是:http://localhost:10080/ci
。
这里还有个细节是官方文档没有提到的,那就是让注册好的Runner运行起来,不过其实默认安装好gitlab-ci-multi-runner后它就自己已经以服务的方式注册到系统里了(服务名:gitlab-runner),每次注册runner导致config.toml
文件变更后都会自动触发该服务的reload。但了解一下这个细节还是对分析问题很有帮助的!
这样我们就应该有两个runner了(一个是shell类型,一个是docker类型):
//todo ubuntu -> docker-in-docker -> runner -> docker engine
这个文件应该放在我们的项目repo的根目录下,用来描述当发生目标行为后,runner要做的工作。官方文档有对其语法的描述。
环境基本上就搭建好了,下一篇我们就开始设计工作流了。
趁热打铁,根据之前我们自己搭建的gitlab-ci环境,我们来跑一个demo项目,爽一下~
我们的目的是,程序员提交代码到gitlab,会触发自动创建镜像,并上传到公共的镜像仓库(目前先这么搞)。
根据官方的文档,我们先在gitlab上搭建一个项目,结构如下:
1
2
3
4
5
6
7
|
/.
|->.dockerignore
|->.gitignore
|->.gitlab-ci.yml
|->Dockerfile
|->app.js
|->package.json
|
其中,.dockerignore
文件内容:
1
2
3
|
.git
.gitlab-ci.yml
README.md
|
.gitignore
:
1
|
node_modules
|
.gitlab-ci.yml
:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
image: docker:latest
services:
- docker:dind
build:image:
stage: build
script:
- docker build -t index.alauda.cn/kazaff/hello-kazaff .
- docker login -u kazaff -p 我的密码 index.alauda.cn
- docker push index.alauda.cn/kazaff/hello-kazaff
tags:
- docker
|
Dockerfile
:
1
2
3
4
5
6
7
8
9
|
FROM node:slim
COPY . /myapp/
WORKDIR /myapp/
RUN npm install
CMD ["node", "app"]
EXPOSE 80
|
app.js
:
1
2
3
4
5
6
7
8
|
var koa = require('koa');
var app = koa();
app.use(function *(){
this.body = 'Hello, Kazff';
});
app.listen(80);
|
package.json
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
{
"name": "docker-gitlab-node-demo",
"version": "1.0.0",
"description": "test for gitlab-ci",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"demo",
"gitlab-ci"
],
"author": "kazaff",
"license": "MIT",
"dependencies": {
"koa": "^1.2.0"
}
}
|
好了,把上面这个项目提交到我们的gitlab环境下,就会触发自动的“build”了(确保你的gitlab配置开启了”build”)!
不过怎么可能顺利呢?第一次一般都是失败的,查看错误日志:
1
2
3
4
5
6
7
8
9
10
|
gitlab-ci-multi-runner 1.1.2 (78b3f82)
Using Docker executor with image node:argon ...
Pulling docker image node:argon ...
Running on runner-e02dcb0d-project-1-concurrent-0 via 21f65ebc58fa...
Cloning repository...
Cloning into '/builds/root/welcome'...
fatal: unable to access 'http://gitlab-ci-token:xxxxxx@localhost:10080/root/nodeDemo.git/': Failed to connect to localhost port 10080: Connection refused
ERROR: Build failed: exit code 1
|
你的错误信息可能和我的不完全一样,不过问题是一样的:Runner启动的docker容器里无法访问到localhost:10080
这个地址(能访问到才怪)。这一般是由于我们的测试环境没有使用域名导致的,gitlab论坛里也不少人讨论这个问题,如果你是在部署正式的gitlab环境,那你自然会有一个域名来使用。不过我这里只是搭建测试环境,所以我使用了一种投机的方法:
修改Runner的/etc/gitlab-runner/config.toml
文件,在其中的[runner.docker]
下增加:
1
|
extra_hosts = ["localhost:172.17.0.1"]
|
意思是,让runner启动的容器中将”localhost”域映射到我的ubuntu宿主机,这样runner请求”localhost:10080”时就会被路由到正确的地址,不推荐正式环境下这么做~
然后再次触发build,这次看看还会碰到什么问题么:
1
|
write /var/lib/docker/tmp/xxx: no space left on device
|
这次悲剧了,我的ubuntu虚拟主机分配的磁盘空间太小,结果现在提示硬盘控件不足……(这里我发现其实我分配的磁盘控件(8G)才使用了2G多一点,不明白VirtualBox为什么不给虚拟机扩展容量了!)
我只能删除掉一些不使用的镜像和容器来腾出一些地儿,再次build,好家伙,虚拟机直接死掉了。
[直播]
对,目前还处于无响应状态,不知道为啥,我只能等。。。
已经快二十分钟,妈蛋!
我方了,咋办?
……
我强行重启虚拟机后一切都归零了,gitlab container无法正常启动了,娃哈哈哈哈~玩我呢是吧?也没有任何错误日志给我参考,得了,重新装一次环境得了~
重新安装环境很快的其实,我们只需要停止所有的容器,然后删除重建一下/srv/docker/gitlab/
这个目录即可,该目录就是之前我们搭建环境时往相关容器中加载的本地卷,重建它们以为这之前在gitlab上的操作都不要了,再次start相关容器,一个干净的gitlabCI环境就好了(docker的魔力)~~
除此之外由于我们的gitlabCI重建后Runner的token也更新了,所以要重新register一下我们的Runner,一切又和好如初!
不怕死的我再一次尝试build,这次会成功么?(有了这次教训,建议你再重新开始build之前先对虚拟机做一个快照,这样再失败就可以直接回滚了~)
由于之前碰到了磁盘空间不足的问题,所以这里就建议使用微容器,可以省下来一笔相当可观的空间和流量啊,Dockerfile的第一行改成:
1
|
FROM iron/node:dev
|
好了,漫长的等待后(全看网速)就可以看到灵雀云中已经成功创建好镜像了!
这里需要 强烈提醒:在测试该镜像时:
1
|
docker run -it --name hello-kazaff -p 10081:80 index.alauda.cn/kazaff/hello-kazaff
|
一定要加-it
,不然你就傻逼了,像我一样无法退出该docker容器了。。。。草!有知道如何解决这个问题的朋友请一定要给我留言啊!