使用GitLab-CI,在Docker上部署Hubot制作的Bot
有一个名为Hubot的聊天机器人开发和执行框架。
它在Node.js上运行,所以只要将其部署到Heroku或适当的服务器上即可,但是我想趁机学习一下,所以打算将其部署到Docker上。
此外,因为我在GitLab上管理了机器人的源代码,所以我希望将其推送到GitLab后,能够自动部署到Docker。
整体形象
GitLab-Runner、Build VM、HomeBot、Redis在Docker上运行。
HomeBot是用Hubot创建的机器人,在GitLab上进行源代码管理。
运行在Docker上的GitLab-Runner会启动Build VM,并在Docker上启动homebot和Redis。
直观地说,这就像一个三层Docker in Docker in Docker的结构。
只是,由于构建VM在构建完成后会崩溃,所以无法将homebot和Redis安置在这里,因为它们需要持续运行。
(也许有其他方法吗?)
通过使用一种名为Docker in Docker的机制,可以从Docker容器中调用宿主机上的Docker功能,
使用Docker中的Build VM将homebot和Redis部署到宿主机的Docker上。
同时,决定将Build VM也在宿主机的Docker上运行,最终实际情况如下所示。
稍微离题一下
这里制作的Bot是使用智能音箱Google Home来控制照明和空调的设备。基础部分是参考了这个网站。
智能家居 – 废物箱
如果您对这类事情感兴趣的话,不妨来看看这篇文章。
前提条件 (Qian ti tiao jian)
-
- GitLab ver.9.1.3
-
- GitLab-runner ver.10.5.0
-
- Docker ver.18.03.0-ce
-
- Hubot ver.2.19
- Redis ver.4.0.8
準備Hubot開発環境和GitLab存储库。
请参考外部网站准备Hubot的环境。
我认为不需要特别解释,但请在GitLab上创建存储库并进行推送。
Hubot的开发环境配置文件应该是这样的。
root
├ bin
├ lib
├ scripts
├ .gitignore
├ .hubot_history
├ external-scripts.json
├ package.json
├ Procfile
└ README.md
准备 Dockerfile
将用于构建 Bot 运行的 Docker 镜像的 Dockerfile 添加到仓库中。
root
├ bin
├ lib
├ scripts
├ .gitignore
├ .hubot_history
├ dockerfile <-NEW!!
├ external-scripts.json
├ package.json
├ Procfile
└ README.md
Dockerfile的意思是一个文本文件,它包含一系列的命令和指令,用于在Docker容器中构建和配置镜像。
FROM centos
RUN yum -y update
RUN yum -y install epel-release
RUN yum -y install git nodejs npm
COPY . /app/bot/homebot
WORKDIR /app/bot/homebot
RUN npm install
CMD sh /app/bot/homebot/bin/hubot -a slack
在Docker Build中,我没有做太多的事情。
只是选择了 centos 镜像作为基础(没有特别的含义,只是个人喜好),
然后获取了 git、nodejs 和 npm,在仓库里将其配置到 /app/bot/homebot,
并进行 npm install 安装。
运行时只需要启动 Hubot 即可。
到目前为止,应该已经能够进行手动部署了。
我会在Docker主机上克隆Git并确认构建成功。
# cd 適当なフォルダ
# git clone http://GitLabのリポジトリURL
# cd リポジトリのフォルダ
# docker build -t homebot ./
此外,您可以使用构建的镜像启动Hubot,并验证已实施的响应。
# docker run --rm -it -w /app/bot/homebot/ homebot sh /app/bot/homebot/bin/hubot
homebot> [Mon Mar 26 2018 13:02:49 GMT+0000 (UTC)] INFO hubot-redis-brain: Using default redis on localhost:6379
homebot> homebot ping
homebot> PONG
homebot> exit
GitLab Runner的准备工作
为了进行Docker中的Docker操作,
需要在启动的Docker容器内执行Docker命令。
因此,需要事先准备好Docker客户端。
mkdir -p /root/gitlab-runner/bin
curl -fsSL https://get.docker.com/builds/Linux/x86_64/docker-17.05.0-ce.tgz | tar -xzC /root/gitlab-runner/bin/ --strip=1 docker/docker
获取Docker客户端请参考以下链接:
https://qiita.com/minamijoyo/items/c937fb4f646dc1ff064a
另外,打开GitLab并记录下用于注册gitlab-runner的registration-token。
启动和注册GitLab-Runner
GitLab-CI将称为Runner的CI执行引擎常驻在执行CI的计算机上。
这种机制使得可以在不同的操作系统上运行构建任务,或者增加CI服务器的数量,以实现灵活的操作。
本次我们要将GitLab Runner也在Docker上运行并保持常驻状态。
首先,启动gitlab-runner的官方镜像。
♯ docker run -d \
--name gitlab-runner-docker \
-h gitlab-runnder \
-v /root/gitlab-runner/config:/etc/gitlab-runner \
-v /root/gitlab-runner/bin/docker:/usr/local/bin/docker \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner
关键是将三个音量挂载。
-
- /etc/gitlab-runner
-
- gitlab-runnerの設定ファイルがここに保存されます。
-
- ホストにマウントしておくことでコンテナを作り直しても設定を復元できます。
-
- /usr/local/bin/docker
-
- 上で取得したdockerクライアントを/usr/local/bin/dockerに配置します。
-
- /var/run/docker.sock
- Docker in Dockerで、ホストのDockerを操作するためにdocker.sockを共有します。
在启动的容器中执行gitlab-runner register命令,并将其注册到GitLab中。
♯ docker exec -it gitlab-runner-docker \
gitlab-runner register --non-interactive \
--url https://(GitLabのURL)/ci \
--registration-token XXXXXXXXXXXXXXXXXX \
--executor docker \
--description "Docker-in-Docker" \
--tag-list docker-in-docker \
--docker-image "docker" \
--docker-privileged \
--docker-volumes /var/run/docker.sock:/var/run/docker.sock \
--docker-volumes /root/gitlab-runner/bin/docker:/usr/local/bin/docker
每个参数的值如下:
-
- url
-
- GitLabのURLです。「/ci」は付けても付けなくてもよいらしいですが、一応つけておきます。
-
- registration-token
-
- 上で、GitLab上で確認したregistration-tokenを指定します。
-
- executor
-
- dockerを指定して、CI実行時に新たにdockerコンテナを起動してその中でビルドします。他にも直接シェル上で実行するshellがよく使われると思いますが、dockerがおすすめらしいので、そちらを指定します。
-
- tag-list
-
- 実行するrunnerを選ぶためのキーワードを指定します。何でもよいのですが、runnerの実行環境にあった名前が良いです。
-
- docker-*
-
- runnerから起動されるdockerコンテナ(=Build VM)の情報を指定します。
docker-image
デフォルトのDockerイメージで、ここでは基本的にgitlab-ci.ymlで指定するので適当に。
docker-privileged
Build VMは特権モードで起動します。これはもしかすると不要かもしれません。
docker-volumes
runnerを作った時と同じですが、ホストのdockerを操作するためのsockとdockerクライアントを配置します。
注意点としては、docker in dockerといっても、マウントするボリュームはBuild VMのパスではなく、ホストのパスになる点です。
打开GitLab,并确认是否已注册runner。
最后,打开刚才注册的runner的编辑选项,并将其关联到GitLab的存储库中。
虽然根据设置可能不需要这样做,但为了安全起见,我们建议手动进行该配置。
准备部署
要将Bot与Slack进行连接,需要在Slack上添加Hubot应用,并将API令牌提供给Bot。
请打开Slack的Hubot页面,并记下HUBOT_SLACK_TOKEN。
创建构建和部署脚本
用上面创建的运行器启动的Docker容器应该与该命令大致相同。
在此过程中,我们将尝试构建和部署,并创建脚本。
# docker run --privileged --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /root/gitlab-runner/bin/docker:/usr/local/bin/docker \
docker /bin/bash
构建
进行存储库的克隆,并进行Docker构建。
# cd 適当なフォルダ
# git clone http://GitLabのリポジトリURL
# cd リポジトリのフォルダ
# docker build -t homebot ./
部署
在启动之前,先停止和删除旧版的Redis和Homebot容器。
# docker stop "redis" || echo ignore errors.
# docker rm "redis" || echo ignore errors.
# docker stop "homebot" || echo ignore errors.
# docker rm "homebot" || echo ignore errors.
# docker run --name redis -d -p 6379:6379 -v /root/redis/data:/data redis redis-server --appendonly yes
# docker run --name homebot -e REDIS_URL=http://127.0.0.1:6379/hubot -e HUBOT_SLACK_TOKEN=YYYYYYYYYYY --net=host -d homebot
在docker stop或docker rm时,如果指定的容器不存在会导致错误,所以需要忽略错误以继续执行构建脚本。
redis容器使用官方镜像,进行端口映射(-p 6379:6379)、挂载持久化数据文件夹(-v /root/redis/data:/data),并指定写入选项(–appendonly yes)。
homebot容器通过环境变量指定redis的URL和HUBOT_SLACK_TOKEN,并使用–net=host选项指定与主机相同的网络。
–net=host在普通情况下不需要使用。因为这次创建的机器人需要通过脚本使用红外发送模块(RM-Mini3),所以需要将探测IP设置为与外部网络相同,并进行了指定。
将.gitlab-ci.yml文件放置
将.gitlab-ci.yml文件放在代码仓库的根目录。
这样一来,当使用git push命令时,GitLab将自动运行CI流程。
root
├ bin
├ lib
├ scripts
├ .gitignore
├ .gitlab-ci.yml <- NEW!!
├ .hubot_history
├ dockerfile
├ external-scripts.json
├ package.json
├ Procfile
└ README.md
将之前测试过的构建和部署脚本记录在.gitlab-ci.yml中。
.gitlab-ci.yml 的翻译为中文是:.gitlab-ci 配置文件。
image: centos
stages:
- build
- deploy
build:
stage: build
tags:
- docker-in-docker
script:
- docker build -t homebot .
deploy to production:
stage: deploy
tags:
- docker-in-docker
variables:
REDIS_URL: redis://127.0.0.1:6379/hubot
HUBOT_SLACK_TOKEN: YYYYYYYYYYYYYYY
script:
- docker stop "redis" || echo ignore errors.
- docker rm "redis" || echo ignore errors.
- docker stop "homebot" || echo ignore errors.
- docker rm "homebot" || echo ignore errors.
- docker run --name redis -d -p 6379:6379 -v /root/redis/data:/data redis redis-server --appendonly yes
- docker run --name homebot -e REDIS_URL=$REDIS_URL -e HUBOT_SLACK_TOKEN=$HUBOT_SLACK_TOKEN --net=host -d homebot
- docker rmi `docker images -f "dangling=true" -q` || echo ignore errors.
在构建过程中有两个阶段,分别是构建(build)和部署(deploy)。
虽然与之前创建的脚本基本相同,但我将REDIS_URL和HUBOT_SLACK_TOKEN这两个参数设为环境变量。此外,我给docker-in-docker添加了一个标签,以便将其应用到新创建的Runner身上。最后的”docker rmi ~”是为了避免在不停地构建中积累无名称的镜像,所以删除了这些镜像。尽管有可能会误删意外的镜像,但这个方法并不太理想。
尝试一下
到目前为止,应该已经能够将代码推送到GitLab并启动Docker容器了。
所以,我来试试Push。
在打开GitLab仓库的Pipelines时,可以看到正在构建。
将HUBOT_SLACK_TOKEN从存储库中删除
如果将HUBOT_SLACK_TOKEN提交到存储库中就无法删除它。
可能想得太多了,但是当未来有更多开发人员加入存储库时,
任何人都可以在Slack上操纵Bot可能不是好事。
GitLab有一个名为“Secret Variables”的功能,它可以用来管理这种非公开参数,
所以我会尝试使用它。
「Secret Variables」可以在存储库页面→设置→CI/CD管道中找到。
在这里将其定义为HUBOT_SLACK_TOKEN_PRODUCTION。
在此定义的变量可以作为环境变量在Runner上使用。
我們需要修改.gitlab-ci.yml文件,以使用這個環境變數。
.gitlab-ci.yml can be paraphrased in Chinese as “.gitlab-ci.yml”.
image: centos
stages:
- build
- deploy
build:
stage: build
tags:
- docker-in-docker
script:
- docker build -t homebot .
deploy to production:
stage: deploy
tags:
- docker-in-docker
variables:
REDIS_URL: redis://127.0.0.1:6379/hubot
HUBOT_SLACK_TOKEN: $HUBOT_SLACK_TOKEN_PRODUCTION #Changed!
script:
- docker stop "redis" || echo ignore errors.
- docker rm "redis" || echo ignore errors.
- docker stop "homebot" || echo ignore errors.
- docker rm "homebot" || echo ignore errors.
- docker run --name redis -d -p 6379:6379 -v /root/redis/data:/data redis redis-server --appendonly yes
- docker run --name homebot -e REDIS_URL=$REDIS_URL -e HUBOT_SLACK_TOKEN=$HUBOT_SLACK_TOKEN --net=host -d homebot
- docker rmi `docker images -f "dangling=true" -q` || echo ignore errors.
将生产服务器和演示服务器分开。
现在,已经可以将代码推送到GitLab并进行部署了,但是只通过推送就直接部署到生产服务器,感觉有点可怕,可能会出现故障。如果首先部署到暂存服务器并确认其正常运行,然后再部署到生产服务器,心里就舒畅多了。
既然正在使用Docker,我想简单地增加这些实例的数量。
方法上,可以稍微参考GitLab Flow,按照以下方式进行:
* 提交到主分支 → 部署到Staging环境
* 提交到生产分支 → 部署到生产环境
顺便提一下,GitLab上有一个名为”Manual actions”的功能,据说可以通过在GitLab的Pipeline上点击按钮进行部署。
听起来很有趣,但是由于会依赖特定的CI功能,会降低移植性,所以我们通过git的分支来实现。
为了增加 Stage 环境的部署,需要修改 .gitlab-ci.yml 文件。
.gitlab-ci.yml的意思是GitLab持续集成配置文件。
image: centos
stages:
- build
- deploy
build:
stage: build
script:
- docker build -t homebot .
tags:
- docker-in-docker
deploy to staging:
stage: deploy
variables:
REDIS_URL: redis://127.0.0.1:16379/hubot
HUBOT_SLACK_TOKEN: $HUBOT_SLACK_TOKEN_STAGING
script:
- docker stop "redis-staging" || echo ignore errors.
- docker rm "redis-staging" || echo ignore errors.
- docker stop "homebot-staging" || echo ignore errors.
- docker rm "homebot-staging" || echo ignore errors.
- docker run --name redis-staging -d -p 16379:6379 -v /root/redis-staging/data:/data redis redis-server --appendonly yes
- docker run --name homebot-staging -e REDIS_URL=$REDIS_URL -e HUBOT_SLACK_TOKEN=$HUBOT_SLACK_TOKEN --net=host -d homebot
- docker rmi `docker images -f "dangling=true" -q` || echo ignore errors.
only:
- master
tags:
- docker-in-docker
deploy to production:
stage: deploy
variables:
REDIS_URL: redis://127.0.0.1:6379/hubot
HUBOT_SLACK_TOKEN: $HUBOT_SLACK_TOKEN_PRODUCTION
script:
- docker stop "redis" || echo ignore errors.
- docker rm "redis" || echo ignore errors.
- docker stop "homebot" || echo ignore errors.
- docker rm "homebot" || echo ignore errors.
- docker run --name redis -d -p 6379:6379 -v /root/redis/data:/data redis redis-server --appendonly yes
- docker run --name homebot -e REDIS_URL=$REDIS_URL -e HUBOT_SLACK_TOKEN=$HUBOT_SLACK_TOKEN --net=host -d homebot
- docker rmi `docker images -f "dangling=true" -q` || echo ignore errors.
only:
- production
tags:
- docker-in-docker
変わった点は、 deployステージとして、「deploy to staging」を増やして、以下のように設定します。
* コンテナ名を「redis-staging」「homebot-staging」と本番とは別の名前にする
* Radisのポートを16379にする
* Radisの永続化先を本番とは別にする
* SlackにHubotの設定をもう1つ作って、上で説明した「Secret Variables」に「HUBOT_SLACK_TOKEN_STAGING」としてトークンを設定しておきます。HUBOT_SLACK_TOKENにはその値を設定します。
また、「deploy to staging」はmasterブランチでのみ、「deploy to production」はproductionブランチでのみ、ビルドされるように「only」を設定してしておきます。
我试着按下去
当将代码推送到主分支(master branch)时,可以发现会运行部署到暂存环境(staging)的构建;当将代码推送到生产分支(production branch)时,可以发现会运行部署到生产环境的构建。
查看Docker容器时,发现每个有homebot和homebot-staging、radis和radis-staging两个实例。
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cbb3f1c3f3cd homebot "/bin/sh -c 'sh /app…" 4 minutes ago Up 4 minutes homebot-staging
deb43da1e0bc redis "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 0.0.0.0:16379->6379/tcp redis-staging
bc126c964ccc homebot "/bin/sh -c 'sh /app…" 4 minutes ago Up 4 minutes homebot
51eea4d849c9 redis "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 0.0.0.0:6379->6379/tcp redis
897153d3393f aea904bf7887 "gitlab-runner-cache…" 5 minutes ago Exited (0) 5 minutes ago runner-ccce4edb-project-22-concurrent-0-cache-3c3f060a0374fc8bc39395164f415a70
b6b8ff1edcf9 aea904bf7887 "gitlab-runner-cache…" 5 minutes ago Exited (0) 5 minutes ago runner-ccce4edb-project-22-concurrent-0-cache-0a45d2bc3900e132e076ffa38608b42a
2a32bac99e7a gitlab/gitlab-runner "/usr/bin/dumb-init …" 4 days ago Up 5 minutes gitlab-runner-docker
在Slack上的Hubot也能正确地响应。
尝试使用GitLab Environments
根据我的理解,GitLab有一个名为“环境(Environments)”的功能,可以注册部署目标。
既然有这个功能,我想尝试一下。
我会在.gitlab-ci.yml中添加环境变量。
如果写上URL,它将链接到GitLab上。
由于这次Hubot本身没有前端,我将指定到Slack上的Hubot直接消息页面。
.gitlab-ci.yml
image: centos
stages:
- build
- deploy
build:
stage: build
script:
- docker build -t homebot .
tags:
- docker-in-docker
deploy to staging:
stage: deploy
variables:
REDIS_URL: redis://127.0.0.1:16379/hubot
HUBOT_SLACK_TOKEN: $HUBOT_SLACK_TOKEN_STAGING
script:
- docker stop "redis-staging" || echo ignore errors.
- docker rm "redis-staging" || echo ignore errors.
- docker stop "homebot-staging" || echo ignore errors.
- docker rm "homebot-staging" || echo ignore errors.
- docker run --name redis-staging -d -p 16379:6379 -v /root/redis-staging/data:/data redis redis-server --appendonly yes
- docker run --name homebot-staging -e REDIS_URL=$REDIS_URL -e HUBOT_SLACK_TOKEN=$HUBOT_SLACK_TOKEN --net=host -d homebot
- docker rmi `docker images -f "dangling=true" -q` || echo ignore errors.
only:
- master
environment:
name: review
url: https://banban55remocon.slack.com/messages/D9S1U9C7P/
tags:
- docker-in-docker
deploy to production:
stage: deploy
variables:
REDIS_URL: redis://127.0.0.1:6379/hubot
HUBOT_SLACK_TOKEN: $HUBOT_SLACK_TOKEN_PRODUCTION
script:
- docker stop "redis" || echo ignore errors.
- docker rm "redis" || echo ignore errors.
- docker stop "homebot" || echo ignore errors.
- docker rm "homebot" || echo ignore errors.
- docker run --name redis -d -p 6379:6379 -v /root/redis/data:/data redis redis-server --appendonly yes
- docker run --name homebot -e REDIS_URL=$REDIS_URL -e HUBOT_SLACK_TOKEN=$HUBOT_SLACK_TOKEN --net=host -d homebot
- docker rmi `docker images -f "dangling=true" -q` || echo ignore errors.
only:
- production
environment:
name: production
url: https://banban55remocon.slack.com/messages/D9K4Z5AKB/
tags:
- docker-in-docker
当在 Git Push 并构建之后,在 GitLab 仓库的 Environment 中,会以以下方式展示,即呈现两个环境。
通过点击”重新部署”按钮来触发重新构建,而点击旁边的链接按钮可以打开Slack页面。
总结
-
- GitLab-CIを使って、Git PushするとBotをデプロイできるようになりました。
-
- GitLab-RunnerやBotはDocker上で動作し、必要に応じて数を変えられます
- ↑を利用して、本番サーバ―とステージングサーバーの2つを簡単に立てることができました