日常项目开发过程中,一般都有多套环境,比如开发、测试和生产。各个环境部署的代码版本不一致,手动一个个来部署效率低且容易出错。如果项目采用了敏捷开发方式,可能每天需要部署几十次。手动方式更加不可行,因此必须要把多环境的部署工作自动化。本文将介绍在笔者参与项目中实际推行的一种方案,供大家参考。
整体方案

当开发人员 push 代码到 develop、release 和 master 分支时将自动触发构建 Docker 镜像,然后部署到分支对应的开发环境。
Git 分支模型
每套环境部署的代码版本都不一样,还在开发中的功能只能部署到开发环境,已经开发完成的功能可以部署到测试环境,测试通过的功能才能部署到生产环境。依托于 Git 强大的分支功能,我们可以轻松地为每个环境维护一个分支,适当的时候在分支之间进行代码的合并。但分支多了容易混乱,开发人员可能搞不清什么时候应该提交到哪个分支。所以团队里需要制定一个分支管理规范,或者说确定一个分支模型。

上图是 Git-flow 的分支模型。图中 develop 和 master 为长线分支,feature、release 和 hotfix 为短线分支。develop、release 和 master 分别部署到开发环境、测试环境和生产环境。develop 分支用来汇总下个版本功能的提交,在下个版本的 release 分支创建之前不允许提交非下个版本的功能到 develop 分支。当下个版本的所有功能开发完成后,从 develop 分支创建下个版本的 release 分支来测试,测试过程中 bug 修复需要提交到该 release 分支。测试完成后,将 release 分支合并到 master 分支去上线,同时还要把 release 分支上的 bug 修复合并回 develop 分支,最后删除 release 分支。
Git-flow 的安装请参考官方文档,安装完成后会给 git 命令增加一个子命令 flow,使用这个子命令可以简化分支操作。
初始化
为了能在 Git 项目里使用 git flow 命令,需要先初始化 Git-flow。
1> git flow init 2Initialized empty Git repository in /Users/jagger/tmp/git-flow/.git/ 3No branches exist yet. Base branches must be created now. 4Branch name for production releases: [master] 5Branch name for "next release" development: [develop] 6 7How to name your supporting branch prefixes? 8Feature branches? [feature/] 9Release branches? [release/] 10Hotfix branches? [hotfix/] 11Support branches? [support/] 12Version tag prefix? []
所有需要输入的地方直接回车,使用默认值就可以。
开发
对新功能的开发,如果功能较大建议创建一个 feature 分支来进行,小的话就直接在 develop 分支上进行。当 feature 分支功能开发完成后,合并到 develop 分支,然后删除该 feature 分支。
1> git flow feature start rss-feed 2Switched to a new branch 'feature/rss-feed' 3 4Summary of actions: 5- A new branch 'feature/rss-feed' was created, based on 'develop' 6- You are now on branch 'feature/rss-feed' 7 8Now, start committing on your feature. When done, use: 9 10 git flow feature finish rss-feed 11 12> git flow feature finish rss-feed 13Switched to branch 'develop' 14Already up-to-date. 15Deleted branch feature/rss-feed (was 810a2a4). 16 17Summary of actions: 18- The feature branch 'feature/rss-feed' was merged into 'develop' 19- Feature branch 'feature/rss-feed' has been removed 20- You are now on branch 'develop'
通过简单的 git flow feature start 和 git flow feature finish 两条指令就能完成上述工作。
测试
当下个版本的功能都已开发完成,就可以从 develop 分支创建下个版本的 release 分支来测试。测试过程中的 bug 修复需要提交到该 release 分支。
1> git flow release start 2.0 2Switched to a new branch 'release/2.0' 3 4Summary of actions: 5- A new branch 'release/2.0' was created, based on 'develop' 6- You are now on branch 'release/2.0' 7 8Follow-up actions: 9- Bump the version number now! 10- Start committing last-minute fixes in preparing your release 11- When done, run: 12 13 git flow release finish '2.0'
发布
测试通过后,合并 release 分支到 master 和 develop 分支,然后删除 release 分支。
1> git flow release finish 2.0 2Switched to branch 'master' 3Deleted branch release/2.0 (was 810a2a4). 4 5Summary of actions: 6- Latest objects have been fetched from 'origin' 7- Release branch has been merged into 'master' 8- The release was tagged '2.0' 9- Release branch has been back-merged into 'develop' 10- Release branch 'release/2.0' has been deleted
Docker 自动化部署
各个环境的分支代码都有了,但怎么快速可靠地把分支代码部署到对应环境了?使用 Docker 可以大大简化部署的复杂性,因为它解决了应用对系统环境的依赖问题,使得应用部署变成了一项十分简单的工作。借助于 GitLab Webhook 和 容器编排服务,可以实现当分支代码有 push 的时候,自动把该分支代码部署到对应环境。
这里以 DaoCloud 容器编排服务为例来讲解。当然大家可以选择任意其它第三方或自己搭建的容器编排服务,操作方式都类似。
创建项目
一个项目对应一个 Git 仓库,项目存在的目的是为了构建和部署应用镜像。DaoCloud 除了支持 GitHub、Bitbucket 这类公有服务,自己搭建的 GitLab 服务也支持。

每次分支代码有 push 的时候,都会触发构建镜像。

通过流程定义来配置要执行的操作和顺序,一般按照“测试 => 构建 => 部署”这样的流程。每个操作都可以单独配置触发条件,比如 push 到指定分支。
创建主机集群
主机集群由安装了 Docker Engine 的多台服务器构成,通过在服务器上安装 DaoCloud Monitor 来把它加入到某个主机集群。部署应用时需指定用来运行应用的主机集群,或者集群里的某台服务器。

上面的 dev 集群只包含一台本地的 MacBook Pro。
编排应用
编排应用就是把应用编制排放到主机集群上去运行。大多数时候应用都依赖其它后端服务,比如 MySQL、Redis 等,启动应用时需要把它们一起启动起来,并且相互之间可互相访问。这样的一组应用在 Docker Compose 里称为 Stack。
下面我们创建了三个开发环境的 Stack,分别为应用服务 Stack zqc-dev,数据库服务 Stack db-dev,缓存服务 cache-dev。之所以不把数据库和缓存服务跟应用服务放在一个 Stack 里,是因为数据库和缓存服务属于公共服务,会被多个应用服务共享,放在某个应用服务里不合适。
Docker Compose 会为每个 Stack 创建一个默认的网络,以便 Stack 里的各个服务之间可以互相访问。那么 Stack 之间了?zqc-dev 需要访问 db-dev 里的数据库服务和 cache-dev 里的缓存服务。很简单,只要把 zqc-dev 里的应用服务加入到 db-dev 和 cache-dev 的网络里就可以了。具体可参考下面的应用服务 Docker Compose 配置文件。

应用服务 Stack。
1version: '2' 2services: 3 zqc-server: 4 image: daocloud.io/jaggerwang/zqc-server:develop-6be5996 5 environment: 6 ZQC_DIR_DATA: /data 7 ZQC_SERVER_DEBUG: 'true' 8 ZQC_SESSION_SECRETKEY: xxxxxxxx 9 ZQC_LOG_LEVEL: debug 10 ZQC_MONGODB_ZQC_ADDRS: mongo:27017 11 ZQC_REDIS_ZQC_ADDRESS: redis:6379 12 ZQC_REDIS_ZQC_DB: 1 13 ZQC_STORAGE_BUCKETS_ZQC_ENDPOINT: oss-cn-shanghai.aliyuncs.com 14 ports: 15 - 10200:1323 16 volumes: 17 - /Users/jagger/data/zqc/server:/data 18 networks: 19 - db-dev 20 - cache-dev 21networks: 22 db-dev: 23 external: 24 name: db-dev_default 25 cache-dev: 26 external: 27 name: cache-dev_default
应用服务 Docker Compose 配置文件。

数据库服务 Stack。
1version: '2' 2services: 3 mysql: 4 image: index.docker.io/library/mysql:5.7 5 environment: 6 MYSQL_ROOT_PASSWORD: xxxxxxxx 7 ports: 8 - 10000:3306 9 volumes: 10 - /Users/jagger/data/db/mysql:/var/lib/mysql 11 mongo: 12 image: index.docker.io/library/mongo:3.4 13 ports: 14 - 10001:27017 15 volumes: 16 - /Users/jagger/data/db/mongodb:/data/db
数据库服务 Docker Compose 配置文件。

缓存服务 Stack。
1version: '2' 2services: 3 redis: 4 image: redis:4.0 5 command: 6 - redis-server 7 - --appendonly 8 - 'yes' 9 ports: 10 - 10010:6379 11 volumes: 12 - /Users/jagger/data/cache/redis:/data
缓存服务 Docker Compose 配置文件。
参考资料
本文转自 https://blog.jaggerwang.net/git-docker-multiple-env-deploy/,如有侵权,请联系删除。
