本文讲的是用Git子模块和Docker Compose实现高效开发工作流,
【编者的话】搭建开发环境一直让程序员们头疼,本文使用Git子模块和Docker Compose实现高效率的开发工作流,让程序员能够轻松搭建出开发环境,把精力投入到需要开发的应用本身。
问题
自我们从Continuous Software雇佣了第一位远程开发的程序员以来,我们就意识到精简开发工作流的重要性。当新入职的程序员接手由很多应用组成的复杂项目时,我们想尽量避免以下这几个问题:- 缺少stack模块:Node.js、PHP、PostgreSQL等
- 不清楚项目组件/应用的总体概况
- 本地配置冲突:监听端口、数据库配置等
此外,就我个人经验而言,我们这样的程序员太容易找不着北了。曾经,我入职一个公司的第一整天都花在搭建开发环境上,试图去理解所有的东西怎么才能一起工作,而无法直接就去理解公司开发的应用到底是怎么工作的。
方案
在具体介绍如何解决上述问题之前,我先介绍下我们项目使用的开发工作流。我们的每个项目都在Bitbucket上有自己的Team(和GitHub上的Organization对应)。每个应用都会在Team下创建一个Repository(比如,
api
、
dashboard
、
cpanel
)。在这些子模块之上,创建了一个名为
development
的Repository。子模块的同一层级有
README.md
和
docker-compose.yml
两个文件。
kytwb@continuous:~/path/to/<project>/$ ls -la total 40 drwxrwxr-x 11 kytwb amine 4096 Mar 14 16:30 . drwxr-xr-x 4 kytwb amine 4096 Nov 1 20:17 .. drwxr-xr-x 20 kytwb amine 4096 Mar 11 14:24 api drwxr-xr-x 11 kytwb amine 4096 Mar 3 13:21 cpanel drwxr-xr-x 10 kytwb amine 4096 Mar 12 11:37 dashboard -rw-r--r-- 1 kytwb amine 2302 Mar 2 15:28 docker-compose.yml drwxrwxr-x 9 kytwb amine 4096 Mar 14 16:30 .git -rw-r--r-- 1 kytwb amine 648 Dec 22 17:20 .gitmodules -rw-r--r-- 1 kytwb amine 1706 Dec 17 16:41 README.md
当新程序员加入项目时,只需在Bitbucket上浏览
development
repository,根据
README.md
的步骤就可以快速搭建环境。具体步骤如下所示:
$ git -v $ docker -v $ docker-compose -v $ git clone git@bitbucket.com:<project>/development.git <project> && cd <project> $ git submodule init && git submodule update $ git submodule foreach npm install $ docker-compose up -d
至此,一切就都已经搭建好,并运行在本地机器上了。
实现原理
本章介绍我们是如何实现上述工作流的。前提条件
$ git -v $ docker -v $ docker-compose
由于我们的开发堆栈完全基于Docker,所以,程序员需要先安装Docker。这时他们不需要特别熟悉Docker,只需要在开发时使用Docker即可,我们间接地将他们引入到了容器的世界,之后会以此为桥梁向他们解释如何使用Docker实现持续集成、持续交付等等。
README.md
中并没有详细介绍如何
安装Docker
,因为安装很简单。
当docker-compose还叫 Fig 的时候我们就已经用它来编排开发堆栈里的容器。之后Docker收购了Fig,重命名为Docker Compose。有人提议将Docker Compose合并到Docker代码里,但是基于很多原因最终并没有这么做,所以Docker Compose仍然需要单独安装。
同样地,本文没有详细介绍 Docker Compose的安装 ,因为很简单。
搭建仓库(Repository)
如前所述,需要创建一个开发仓库,以及为每个应用创建对应的仓库。这里我们创建了api
、
dashboard
和
cpanel
。当创建这些仓库的时候,重点关注
development
仓库的搭建。
$ git clone git@bitbucket.com:<project>/development.git <project> && cd <project>
现在将应用程序的仓库添加为
development
仓库的子模块,只需要键入如下命令:
$ git submodule add git@bitbucket.org:<project>/api.git $ git submodule add git@bitbucket.org:<project>/dashboard.git $ git submodule add git@bitbucket.org:<project>/cpanel.git
这样,你的
development
仓库根目录下会创建出
.gitmodules
文件。程序员也就可以在克隆
development
repository的时候一次得到所有的应用程序并运行:
$ git submodule init && git submodule update
更多子模块的信息,请参考 Git官方文档 。
Docker化一切
现在我们已经搭建好了development
仓库,可以通过
cd
的方式访问所有不同的应用程序。接下来我们要用之前提到的编排工具:Docker Compose来容器化所有的应用及其配置。
首先从
api
应用程序开始。打开
docker-compose.yml
,为API声明一个容器,并为这个容器选择基础镜像。本示例中的代码基于Node.js,因此选择官方Node.js镜像:
api: image: dockerfile/nodejs
这时,运行命令
docker-compose up -d
会创建出一个名为
<project>_api_1
的容器,这个容器什么也不做(启动后立即退出)。运行命令
docker-compose ps
可以得到由
docker-compose.yml
编排的所有容器的信息。
接下来配置
api
容器,使其多一些功能。为了实现这个目的,我们需要:
- 将源代码挂载到容器中
- 声明用什么命令运行应用
- 暴露合适的端口以供访问应用
这样配置文件类似:
api: image: dockerfile/nodejs volumes: - ./api/:/app/ working_dir: /app/ command: npm start ports: - "8000:8000"
现在再运行
docker-compose up -d
,就启动了
api
应用,可以在
http://localhost:8000
访问它。这个程序可能会崩溃,可以使用
docker-compose logs api
检查容器日志。
这里,我怀疑
api
的崩溃是因为它连不上数据库。因此需要添加
database
容器,并让
api
容器能够使用它。
api: image: dockerfile/nodejs volumes: - ./api/:/app/ working_dir: /app/ command: npm start ports: - "8000:8000" links: - database database: image: postgresql ports: - "5432:5432"
通过创建
database
容器,并将其连接到
api
容器,我们就可以在
api
容器里找到
database
。要想展示API的环境(比如,
console.log(process.env)
),必须使用如下变量,比如
POSTGRES_1_PORT_5432_TCP_ADDR
和
POSTGRES_1_PORT_5432_TCP_PORT
。这是我们在API的配置文件里使用的关联到数据库的变量。
通过link指令,这个数据库容器被认为是API容器的依赖条件。这意味着Docker Compose在启动API容器之前一定会先启动数据库容器。
现在我们用同样的方式描述其它应用程序。这里,我们可以通过环境变量
API_1_PORT_8000_TCP_ADDR
和
API_1_PORT_8000_TCP_PORT
,将
api
连接到
dashboard
和
cpanel
应用。
- ./api/:/app/ working_dir: /app/ command: npm start ports: - "8000:8000" links: - database database: image: postgresql dashboard: image: dockerfile/nodejs volumes: - ./dashboard/:/app/ working_dir: /app/ command: npm start ports: - "8001:8001" links: - api cpanel: image: dockerfile/nodejs volumes: - ./api/:/app/ working_dir: /app/ command: npm start ports: - "8002:8002" links: - api
就像之前为数据库修改API配置文件一样,可以为dashboard和cpanel应用使用类似的环境变量,从而避免硬编码。
现在可以再次运行
docker-compose up -d
命令和
docker-compose ps
命令:
kytwb@continuous:~/path/to/<project>$ docker-compose up -d Recreating <project>_database_1... Recreating <project>_api_1... Creating <project>_dashboard_1... Creating <project>_cpanel_1... kytwb@continuous:~/path/to/<project>$ docker-compose ps Name Command State Ports ---------------------------------------------------------------------------------- <project>_api_1 npm start Up 0.0.0.0:8000->8000/tcp <project>_dashboard_1 npm start Up 0.0.0.0:8001->8001/tcp <project>_cpanel_1 npm start Up 0.0.0.0:8002->8002/tcp <project>_database_1 /usr/local/bin/run Up 0.0.0.0:5432->5432/tcp
应用应该就已经启动并运行了。
从
http://localhsot:8000
可以访问api。
从
http://localhsot:8001
可以访问dashboard。
从
http://localhsot:8002
可以访问cpanel。
更进一步
本地路由
在使用docker-compose up -d
运行所有容器之后,可以通过
http://localhost:<application_port>
访问我们的应用。基于当前配置,我们可以很容易地使用
jwilder/nginx-proxy
加上本地路由功能,这样就可以使用和生产环境类似的URL访问本地应用了。比如,通过
http://api.domain.local
访问
http://api.domain.com
的本地版本。
jwilder/nginx-proxy
镜像将一切变得很简单。只需要在
docker-compose.yml
里加上描述去创建一个名为
nginx
的新容器。根据
jwilder/nginx-proxy
的README文件(挂载Docker守护进程socket,暴露80端口)配置该容器就可以了。之后,在现有容器里再添加额外的环境变量
VIRTUAL_HOST
和
VIRTUAL_PORT
,如下:
api: image: dockerfile/nodejs volumes: - ./api/:/app/ working_dir: /app/ command: npm start environment: - VIRTUAL_HOST=api.domain.local - VIRTUAL_PORT=8000 ports: - "8000:8000" links: - database database: image: postgresql dashboard: image: dockerfile/nodejs volumes: - ./dashboard/:/app/ working_dir: /app/ command: npm start environment: - VIRTUAL_HOST=dashboard.domain.local - VIRTUAL_PORT=8001 ports: - "8001:8001" links: - api cpanel: image: dockerfile/nodejs volumes: - ./api/:/app/ working_dir: /app/ command: npm start environment: - VIRTUAL_HOST=cpanel.domain.local - VIRTUAL_PORT=8002 ports: - "8002:8002" links: - api nginx: image: jwilder/nginx-proxy volumes: - /var/run/docker.sock:/tmp/docker.sock ports: - "80:80"
nginx
容器会检查所有运行在Docker守护进程之上(通过挂载的
docker.sock
文件)的容器,为每个容器创建合适的nginx配置文件,并设置
VIRTUAL_HOST
环境变量。
要想完成本地路由的搭建,还需要在
etc/hosts
里添加所有的
VIRTUAL_HOST
。我是手动用node.js的
hostile
包来完成这个工作的,不过我猜应该可以自动化实现,就像
jwilder/nginx-proxy
可以根据nginx配置文件动态变化一样。这里需要再研究一下。
现在可以再次运行
docker-compose up -d
,然后使用和生产环境一样的url访问应用程序,只需用
.local
TLD代替
.com
TLD。
建议
本文发表在AirPair上,如果你对更进一步这一章有任何建议,请随意fork并修改它。如果你发现本文有任何错误,也请帮忙修改。原文链接:Efficient development workflow using Git submodules and Docker Compose(翻译:崔婧雯)
===========================
译者介绍
崔婧雯,现就职于VMware,高级软件工程师,负责桌面虚拟化产品的质量保证工作。曾在IBM WebSphere业务流程管理软件担任多年系统测试工作。对虚拟化,中间件技术有浓厚的兴趣。
原文发布时间为:2015-03-20
本文作者:崔婧雯
本文来自云栖社区合作伙伴DockerOne,了解相关信息可以关注DockerOne。
原文标题:用Git子模块和Docker Compose实现高效开发工作流