在 Ionic,咱们是 Docker 的铁杆粉丝。咱们的代码以及代码的依赖所有运行在 Docker 中,Docker 让咱们的产品更充分地利用计算资源,好比 Ionic Creator,以及即将到来的 Ionic.io 服务。html
使用 Docker 面对的一个挑战是,尽管咱们只是对咱们的代码作了一个小小的变动,咱们都必需要走一遍构建一个新容器的过程,把它拉取(pull)到咱们的服务器,并替代正在运行的版本。git
咱们全部的代码都存储在 GitHub,使用 Docker Registry(这里推荐下国内的 docker.cn,速度比官方的快不少,不用担忧“你懂的”问题) 来自动构建和存储咱们的代码,并使用 Ansible 来管理和部署咱们的容器到咱们的服务器上。即便是一个彻底自动化的过程,部署一个小变动均可能花费咱们 20 分钟或者更多的时间。通过头脑风暴,咱们意识到咱们有一个更好的方法来利用 Docker。github
在最初的容器构建以后,99% 的变动是纯代码。咱们不须要添加任何依赖,或者是改变任何代码运行所必需的东西。Docker 实际上只是一种封装基础架构的方式,要求咱们的代码运行在一个自包含的包中。由于咱们 99% 的变动都是代码,不是基础架构,咱们意识咱们不须要在每次变动的时候都努力从新构建咱们的基础架构。docker
让咱们解决这个问题的是 Docker 的杀手级特性 volumes。在咱们 Docker files 的第一次迭代中,咱们从 GitHub 拉取代码,并直接构建进容器中。如今,咱们故意把代码放在容器外面,并在容器启动的时候,经过加载一个主机卷(host volume) 来代替。当咱们想作一个新发布,Ansible 从 GitHub 上拉取 master 分支到咱们服务器上的 app 目录。这时,它经过检查来确保相关联的容器正在运行,若是没有在运行,它将启动这个容器并把 app 代码映射进容器。服务器
使得咱们的工做更便捷的另一个组件是由于咱们的大部分 app 是 Python 的(Django),咱们在 Docker 容器中使用 uWSGI 提供服务。uWSGI 有一个 touch reload 特性,能够监控指定的文件,当该文件被 touch 的时候,会重载 uWSGI 服务。在 Ansible 从 GitHub 拉取咱们的变动以后,咱们使用 Ansible 来 touch uwsgi.ini 文件,这会触发正在运行的容器中的 uWSGI 重载。咱们就是这样来运行咱们代码的更新版本的!架构
这是什么意思,简单地说,花费咱们 20+ 分钟的部署过程是这样的:app
相似的 10 秒的过程是这样的:ionic
咱们在 Docker 容器中使用 Supervisor 来启动容器中的进程运行。咱们的 supervisord.conf 文件看起来像下面这样:ide
[supervisord] nodaemon=true [program:uwsgi] command = /usr/local/bin/uwsgi --touch-reload=/path/to/code/in/container/uwsgi.ini --ini /path/to/code/in/container/uwsgi.ini
咱们经过 --touch-reload
选项来把 uwsgi.ini 文件做为触发文件。ui
当咱们启动咱们的容器,咱们添加一个包含咱们 app 代码的主机卷(host volume),该主机卷被映射到容器中的一个 app 路径,uWSGI 将从这个路径加载 app。
docker run -d -P -v /path/to/code/on/host:/path/to/code/in/container --name=container_name driftyco/testapp
Ansible 负责从 GitHub 克隆(clone)咱们应用程序的代码到咱们主机的 app 目录,确保 Docker 容器正在运行以及 touch 配置的 uWSGI touch-reload 文件。咱们已经建立了 playbooks 来直接部署咱们的每一个服务,所以部署仅仅是一个运行正确的问题。
对于一个快速代码部署,咱们运行一个包含这些任务的 playbook,并只须要几秒来运行:
- set_fact: host_volume="/path/to/code/on/host" - name: Git pull the latest code git: repo=git@github.com:{{ org }}/{{ container }}.git dest={{ host_volume }} accept_hostkey=yes force=yes - name: Gracefully reload uwsgi file: path={{ touch_file }} state=touch
若是咱们须要重启整个容器或者是更新咱们的系统包,咱们能够作一个容器部署,这将花费几分钟,使用这些任务:
- name: Add app dir if it doesn't yet exist file: path={{ host_volume }} owner=nobody group=docker recurse=yes state=directory sudo: yes - name: Pull Docker image command: "{{ item }}" ignore_errors: yes with_items: - docker pull {{ org }}/{{ container }} - docker stop {{ container }} - docker rm {{ container }} - name: Run Docker image with app volumes command: docker run -d -P -v {{ host_volume }}:{{ container_volume }} --name={{ container }} {{ extra_params }} {{ org }}/{{ container }}
对于一个全量部署,咱们按顺序运行这两个 playbooks;这是很是简单的。
由于 Docker 主要的一个方式是封装基础架构到一个自包含的,可部署的包。这不须要从新构建整个容器仅仅只是为了几个代码变动。经过在 Docker 中利用卷(volumes),咱们从容器中移除了代码,使得代码能独立于容器更新。最后,咱们可使用 UWSGI 的 touch reload 特性在容器中重启 UWSGI,并从卷(volume)中加载更新的代码。
注:本文做者是 Joel Weirauch,本文原文是 Fast code deployments with Docker