10x系列之Docker在Clay.io


【编者的话】近日,Clay.io的Zoli Kahan开始了“10X”系列博文的撰写。通过这个系列博文,Zoli将分享如何只使用一个很小的团队支撑Clay.io的大规模应用。本文是整个系列的第三篇,DockerOne接下来将会对整个系列文章进行翻译。

简介

Clay,我们的部署过程非常有趣。真的!因为我们使用Docker。Docker是一个用于简化应用隔离与部署的容器化工具。其基本思路是,Docker会在一个虚拟的隔离环境中运行你的应用,这有点像虚拟机,但没有额外开销。你以一个“基础镜像”起步,然后使用一个Dockerfile描述如何创建一个“容器”。

综述

docker-diagram.png

我们的部署过程是这样的:
  • 将所有要发布的代码合并到git的master分支
  • git tag打上发布标签(比如 v1.0.0)
  • 构建Docker镜像(代码锁定)
  • docker tag打上发布标签(比如 v1.0.0)
  • 将Docker镜像推送到registry

  • 更新集群容器版本(零停机)
    • 确认备用容器正在运行
    • 升级主容器
    • 等待新的主容器上线
    • 升级备用容器

  • 更新生产集群容器版本(零停机)
    • (与上同)


这个过程确保了在过渡环境和生产环境中运行的是完全相同的代码,同时允许我们在发生错误时回滚到上一个Docker容器发布版本。

Clay.io的Dockerfile

这是来自我们的移动应用的一个示例Dockerfile(源码):
FROM dockerfile/nodejs:latest

Install Git

RUN apt-get install -y git

Add source

ADD ./node_modules /opt/clay-mobile/node_modules
ADD . /opt/clay-mobile

WORKDIR /opt/clay-mobile

Install app deps

RUN npm install

CMD ["npm", "start"]

这个文件不言自明。它只是简单的从当前目录复制源码到容器中。环境变量将用于引入敏感的或动态的配置。重点说明一下:npm start实际上是在为生产环境编译并压缩代码:
// package.json
{
"scripts": {
"build": "node_modules/gulp/bin/gulp.js build"
"start": "npm run build &&
          ./node_modules/pm2/bin/pm2 start ./bin/server.coffee
            -i max
            --name clay_mobile
            --no-daemon
            -o /var/log/clay/clay_mobile.log
            -e /var/log/clay/clay_mobile.error.log",
}
}

这是因为我们需要使用生产环境变量重新构建静态文件。请访问我们的GitHub查看更多示例:github.com/clay.io

Docker镜像的基本部署

在Clay,我们将镜像托管在docker registry上。

因为Docker,部署我们的应用到过渡环境和生产环境中非常简单。整个过程(从未标记容器,到过渡环境,由Ansible自动完成)如下:

Local machine / Build server

docker build -t clay/mobile .
docker push clay/mobile


Staging / Production server

docker pull clay/mobile
docker run
--restart on-failure
-v /var/log/clay:/var/log/clay
-p 50000:3000
-e CLAY_MOBILE_HOST=XXXX
-e CLAY_API_URL=XXXX
-e FC_API_URL=XXXX
-e NODE_ENV=production
-e PORT=3000
--name mobile
-d
-t clay/mobile:VERSION

(注意:49,152 - 65,535端口通常用于私有应用)

零停机更新

你可能注意到在上述启动脚本中,我们使用PM2来处理集群多个服务器进程。PM2支持零停机更新,不过因为它存在于容器内,而我们从不在运行时修改容器内代码,所以我们没使用这个功能。PM2只是单纯的用于获取多个服务器的核心。

零停机更新的关键点在于运行两个服务器进程。一个主进程和一个备用进程。我们通过给两个容器部署分配不同的端口实现这一点:
docker run ... -e PORT=50000 --name mobile
docker run ... -e PORT=50001 --name mobile-backup

HAProxy负责在主服务器宕机时将流量重分配到备用服务器上:

example haproxy.cfg

backend mobile
mode http
balance roundrobin
server app1 x.x.x.x:50000 check
server app1b x.x.x.x:50001 check backup
server app2 x.x.x.x:50000 check
server app2b x.x.x.x:50001 check backup

后续将有篇关于HAProxy的文章,介绍更多关于我们是如何使用HAProxy在服务器间进行负载均衡的详情。

以下部署过程将完全由Ansible自动完成:
  1. 确认备用健康容器状态(Ansible)
  2. docker pull clay/mobile:v1.0.0

  3. 杀死主容器(网络请求自动重路由到备用服务器)
    • docker rm -f mobile

  4. 更新主容器,并重启
    • docker run ...

  5. 一旦主容器恢复,网络请求将移回到主服务器
  6. 杀死并更新备用容器
    • docker rm -f mobile-backup && docker run ...


如果发生了任何错误,只要简单的恢复到上一个镜像版本:
docker run -t clay/mobile:v0.0.12

结语

如果你错过了之前的10x文章:可以点击下面的链接阅读:

Architecture中文翻译
Logging中文翻译

原文:10x: Docker at Clay.io

0 个评论

要回复文章请先登录注册