Docker 实践(四): Beta 环境容器化
最近把公司的 beta
环境做了容器化,目前达到的效果是代码提交到 gitlab
,触发 webhook
将代码部署到测试服务器,然后就可以根据前后端不同的分支组合的域名来访问,从而省去了每次前后端代码都合并到 master
分支才能测试的环节。
系统架构
域名路由
nginx-proxy 是一个 Docker 容器, 是实现本文解决方案的神器,只需简单的配置,便可实现为多个容器路由的功能。
1. 在服务器上安装并运行
docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
2. 泛域名解析
假设有域名 foo.bar, 使用泛域名解析将 *.foo.bar解析到当前服务器即可。
3. 启动下游容器
假设有一个运行 web 服务的容器,只需要在启动的时候加上 -e VIRTUAL_HOST=web.foo.bar
,就可以实现对该容器的访问了。
docker run -e VIRTUAL_HOST=web.foo.bar ...
注意:如果使用 docker-compose 管理多个容器的时候,docker-compose 会为这些容器创建一个网桥,这样会使得后来手动创建的容器没法被路由,所以这里不建议使用 docker-compose 来管理。
可视化管理
ui-for-docker 是一个基于 docker remote api 实现的 web 管理界面,虽然界面不怎么样,但是基本上常用的功能已经实现了,前端使用 angular 实现,可以根据自己的需求作修改。
修改方法
clone 代码
npm install && bower install
grunt run (更多操作可参看 gruntFile.js 文件)
代码部署
使用 Gitlab 提供的 webhook 功能完成自动部署代码,期间遇到一些问题:
文件写入权限
通过 webhook 的请求执行用户是 www-data,如果要在 hook 脚本中进行文件的写入操作是总会遇到权限问题,最暴力的办法就是给目录全部 777 或者在 Dockerfile 中将 www-data 用户加入 sudo 组,并且无需输入密码。
echo " data-www ALL=NOPASSWD: ALL" >> /etc/sudoers
当然,正式的环境中需要对权限做严格控制,做到最小权限原则。
hook 响应超时
Gitlab 触发 hook 请求之后会一直等待服务器返回 HTTP 200
的状态码,如果没收到,会重复触发 hook,如果 hook 脚本执行时间过长会导致 HTTP 请求超时,或者一次代码提交触发多次部署请求,所以最后我选择用异步的方式,hook 脚本只需要接受上线指令,写入队列,上线过程交给另外一个脚本来完成。
为了减少环境配置的麻烦,可以使用文件队列,php 可参考 Filefifo.php。
可将队列目录挂载到 webhook 容器上,即可实现容器内部写入,主机读取队列(可使用supervisor 来管理该进程),完成代码部署的过程。
日志回显
使用 supervisor 管理上线脚本进程,需要把日志回显到 web 界面,首先在 supervisor 的配置文件中配置好日志路径,然后创建一个 websocket 容器,将日志目录挂载到容器中,再tail 读取日志文件,实时显示在 web 界面。
可参考 websocket。
下游容器
下游容器将前后端环境做了分离,降低耦合度,在使用过程中会减少一些麻烦。分离时会遇到浏览器同源策略的问题,可将前后端容器做互联,然后后端容器反向代理到前端容器。
在部署代码的时候可根据不同的分支将代码部署到不同的目录,然后创建容器的时候把不同的分支目录挂载到不同的容器中,即可实现开发分支的随意搭配。
精简镜像
对于一些服务,有时候只需要一个非常简单的 web 容器就可以,比如上面的前端容器,但是如果使用 ubuntu + nginx 的配置的时候会发现镜像要几百兆,很不划算,所以考虑精简镜像。
受这篇文章的启发,打算自己用 go 实现一个简单的 web 服务器,这样 build 好的镜像基本上只有十几兆,但是因没有做相关的测试,所以还不敢放在线上。
最后选择了使用 alpine + nginx 的策略,build 之后的镜像之后几十兆,已经减少了很多冗余的东西,具体可参考simple-nginx。
结语
经过无数次踩坑和不停的折腾,终于可以勉强使用了,当然还存在一些瑕疵,之后会慢慢修复。
感谢期间帮助过我的每一位同事。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。