注 4 http://martinfowler.com/bliki/BlueGreenDeployment.html
Blue-green deployment方法其实很简单,就是保持两套一样的生产环境,而实际上只有一套环境真正的对外提供服务(图中绿色环境),而另一套环境则处于待机状态(图中蓝色)。部署的时候,我们会先上线到蓝色环境中,如果测试没有问题了,再将路由切换到新的服务上。
Blue-green部署能带来如下好处。
- 最小化停机时间
- 快速回滚
- hot standby
而未来的开发和部署和可能就会像下面这样进行了。
- ① 开发人员将代码push到Git仓库
- ② CI工具通过webhook得到最新代码,构建Docker镜像并启动容器进行测试。
- ③ 测试通过后将镜像打标签后push到私有镜像Registry
- ④ CI工具通知CD工具
- ⑤ CD工具通过Mesos/Marathon等进行基于容器的部署
- ⑥ 测试没有问题后进行容器的切换(即Blue-green切换)
2. Docker架构解析
2.1. Docker整体结构
Docker是一个构建、发布、运行分布式应用的平台(见下图),Docker平台由Docker Engine(运行环境 + 打包工具)、Docker Hub(API + 生态系统)两部分组成。
图 Docker平台
从图中我们可以看到,Docker的底层是各种Linux OS以及云计算基础设施,而上层则是各种应用程序和管理工具,每层之间都是通过API来通信的。
Docker引擎
Docker引擎是一组开源软件,位于Docker平台的核心位置。它提供了容器运行时以及打包、管理等工具。
Docker引擎可以直观理解为就是在某一台机器上运行的Docker程序,实际上它是一个C/S结构的软件,有一个后台守护进程在运行,每次我们运行docker命令的时候实际上都是通过RESTful Remote API来和守护进程进行交互的,即使是在同一台机器上也是如此。
Docker Hub
Docker Hub是一个云端的分布式应用服务,它专注于内容、协作和工作流。Docker Hub除了可以托管、下载、查找Docker镜像之外,还提供了包括更管理、团队协作、生命周期流程自动化等功能,以及对第三方工具和服务的集成。
2.2. Docker镜像(image)
2.2.1. Docker镜像
Docker镜像是Docker系统中的构建模块(Build Component),是启动一个Docker容器的基础。
我们可以通过一个官方提供的示意图来帮助我们来理解一下镜像的概念。
Docker镜像位于bootfs之上,实际上bootfs在系统启动后会被卸载的。Docker镜像(Images)是分层的,这得益于其采用的联合文件系统,前面我们已经介绍过了。镜像是有继承(父子)关系的,每一层镜像的下面一层称为父镜像,没有父镜像的称为基础镜像(Base Iamge,其实叫做Root Image可能更确切,不过这可能容易和rootfs混淆)。
2.2.2. 镜像仓库
我们可以将Docker镜像仓库理解为Git仓库。Dcoker镜像仓库分为远程和本地,本地的概念好理解,而一般来说远程仓库就是Registry,包括官方的或者自建的私有Registry;我们通过docker pull和docker push命令在本地和远程之间进行镜像传输。
Docker镜像的命名规则和GitHub也很像。比如我们自己创建的仓库名称都是类似liubin/redis这样格式的,前面的liubin是用户名或namespace,后面是仓库名。
不过我们前面已经看到运行的ubuntu镜像的时候是仓库名就是ubuntu,而不带用户名前缀,这是表明它是由官方制作的,或者由官方认可的第三方制作的镜像。我们可以认为官方仓库提供的镜像都是安全的、最新的,所以也可以放心使用。
2.3. Docker容器(Container)
容器是一个基于Docker镜像创建、包含为了运行某一特定程序的所有需要的OS、软件、配置文件和数据,是一个可移植的运行单元。在宿主机来看,它只不过是一个简单的用户进程而已。
容器启动的时候,Docker会在镜像最上层挂载一个read-write的文件系统,即上图中标记为writable的Container层,容器将跑在这个文件系统上。这层可写的文件系统是容器中才有的概念,如果我们对此容器进行commit操作,那么该层文件系统则会被提交为一个新的只读的镜像层,并位于镜像层的最上面的。
我们可以认为Docker镜像是“静”的".exe"文件,只在“硬盘”上;而容器是“动”的,是在“内存中”的,要想启动一个容器,需要先把".exe"装载到内存。
镜像和容器具有如下的转换关系:
- 镜像 -> docker run -> 容器
- 容器 -> docker commit -> 镜像
有时候我们经常会将两个名称混用,不过这并不会影响我们的理解。
2.4. Docker Registry
Docker Registry是Docker架构中的分发模块,它用来存储Docker镜像,我们可以将它理解为GitHub。
Docker Hub是一个官方的Docker Registry,也是Docker镜像的默认存储位置。
当然从安全管理的角度上来说,我们可能更愿意在自己公司内部托管一个私有的Docker Registry,这可以通过使用Docker官方提供的Registry注 5软件实现。
注 5 Docker Registry https://github.com/dotcloud/docker-registry
运行私有Registry非常简单,这也是一个典型的Docker风格的应用发布例子。
docker run –p 5000:5000 registry
3. 使用Docker
3.1. 初识容器
3.1.1. 创建并启动容器
这里我们假定各位读者已经在自己的机器上安装好了Docker。Docker主要的命令就是docker了,它的参数很多,关于它的具体使用方法,可以参考官方的文档注 6,这里我们只简单的介绍其中一些常用的用法。
注 6 https://docs.docker.com/reference/commandline/cli/ 和 https://docs.docker.com/reference/run/
启动一个容器很简单,我们只需要运行docker run命令就可以了注 6。
注 6 为了方便区分,本文中运行命令的时候如果提示符为$,表示实在宿主机(Ubuntu)中,如果是#,则表示是在Docker容器中
$ sudo docker run -i -t ubuntu /bin/bash Unable to find image 'ubuntu' locally Pulling repository ubuntu e54ca5efa2e9: Pulling dependent layers ... 省略 ... 6c37f792ddac: Download complete ... 省略 ... root@81874a4a6d2e:/#
docker run命令会启动一个容器。参数ubuntu指定了我们需要运行的镜像名称,后面的bash则指定了要运行的命令,注意这个命令是容器中的命令,而不是宿主机中的命令。参数-i用来为容器打开标准输入以和宿主机进行交互,-t则会为容器分配一个终端。
在第一次启动某镜像的时候,如果我们本地还没有这个镜像,则Docker会先从远程仓库(Docker Hub)将容器的镜像下载下来,下载完成之后才会启动容器。
注意Docker里有一个很重要的概念就是容器ID或者镜像ID,比如这个例子里的e54ca5efa2e9。这个ID是一个容器或者镜像的唯一标识,它的长度为64位,不过很多时候都可以简写为12位,这也和Git很像。
3.1.2. 让Docker容器在后台运行
这时候我们可以使用-d参数来通过守护模式启动一个容器,这样容器将会在后台一直运行下去。这非常适合运行服务类程序。如果需要,我们可以再通过docker attach命令连接到运行中的容器。
3.1.3. 常用命令
docker ps
docker ps用来查看正在运行中的容器。
从下面的输出结果我们可以看出该容器状态(STATUS列)为已经停止执行,且没有错误(Exited后面的状态码)。
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 60bab6f881e5 ubuntu:latest /bin/bash 14 minutes ago Exited (0) 5 seconds ago agitated_hopper
docker ps命令的常用参数(及组合)如下。
- -a: 查看所有容器,包括已经停止运行的。
- -l: 查看刚刚启动的容器。
- -q: 只显示容器ID
- -l -q: 则可以返回刚启动的容器ID。
docker stop/start/restart
docker stop用来停止运行中的容器,同时你还可以用docker start来重新启动一个已经停止的容器。
docker restart可以重启一个运行中的容器。这就相当于对一个容器先进行stop再start。
3.2. 深入了解Docker镜像
在对Docker容器有一个简单的感性认识之后,我们再来深入了解一下Docker镜像的概念。
Docker镜像实际上就是一个tarball,它是一个能完整运行的OS系统,这非常像OS或VM镜像。它里面有基础OS、各种软件包及类库等。我们启动一个容器,相当于是启动了一个“基础OS”。
3.2.1. 标签(Tag)
我们还可以为镜像打标签,这也和Git非常相似。其实你也可能在前面留意到了,docker images的输出中有一列就是TAG的。我们在执行docker build或者docker commit的时候都可以同时为仓库名称指定一个TAG,格式为user_name/repo_name:tag,如果没有指定这个TAG,则默认为latest。
3.2.2. 常见镜像操作
这里我们再介绍一下对镜像常见的一些操作。
查看本地镜像列表
docker images命令用来列出当前系统中的所有本地镜像,即我们已经通过docker run或者docker pull下载下来的镜像,镜像文件保存在本地的/var/lib/docker文件夹下。
下载镜像到本地
只需要运行docker pull命令即可,命令非常简单,问题在于你的网路速度和连通性。
docker rmi用来从本地仓库中删除一个不再需要的镜像,即"rm image"的缩写。
3.3. 构建镜像
我们可以创建自己的Docker镜像,在我们