注:该文由 adetante 编写,原文地址为 Service discovery with Docker前端
这篇博客的第一篇文章,我将写一篇基于 Docker 容器构建一个不可变架构的解决方案的文章。node
这个主题将经过系列文章来描述,从最简单的案例到更复杂的架构。git
整体的思想是设计一个“基于概念验证”的解决方案,它容许:github
这个逻辑架构是很是简单的,一个无状态的应用经过负载均衡器访问。web
每一个应用的实例运行在它本身的 docker 容器中。docker
为了动态配置管理,当咱们启动和中止一个新容器的时候,咱们想后端能自动注册进负载均衡器。这是基本需求,叫作**服务发现***:咱们想负载均衡器能自动发现提供服务的容器。shell
在这篇文章中,全部的节点将运行在相同的 docker 主机上。这是很是简单的,可是这是实现基础概念的第一个方法。而后咱们将经过容许在不一样主机上透明的部署来是架构复杂化。数据库
第一个示例将使用如下工具实现:json
目标是减小或消除组件之间的“手动”的链接。当你把你的应用程序推送进生产的时候,全部的这些事情均可以配置:数据库服务器的主机和端口,REST 服务的 URL 等等,在一个高可扩展的架构中,这些链接能够动态改变。一个新的后端能够被添加,一个数据库节点能够被中止。你的应用须要适应这种动态环境。ubuntu
这里有一些工具能够管理这些需求(Apache Zookeeper, etcd, ...)。这些工具的广泛原则是:当启动的时候,一个服务的实例必须注册进配置服务器。当中止的时候(完美中止或是 Crash 了),节点必须从配置服务中移除掉。注册后,其余服务能够在配置服务器中搜索到提供制度服务的实例列表(主机和端口)。
Synapse 是一个简单的服务发现的工具。Synapse 与如下俩个组件一块儿使用:
第一个解决方案将使用 Synapse 和 检查 Docker 容器实现。
Synapse 管理一个运行在安装了 Docker 的相同的主机上的以 8080 端口运行着的 HAproxy 实例。
Synapse 检查 Docker 来发现容器是否运行着一个指定镜像而且暴露一个指定端口。为每个匹配的容器,Synapse 把其添加进 HAproxy 的配置。
对于这个示例,咱们从一个干净的 *Ubuntu 14.04 amd64 安装开始。
安装步骤已经在 Docker 的文档中描述了:
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9 $ sudo sh -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list" $ sudo apt-get update $ sudo apt-get install lxc-docker
把如下行添加进 /etc/default/docker
,使得 Docker API 在 tcp 上可用:
DOCKER_OPTS="-H 127.0.0.1:4243"
重起 docker:
$ sudo service docker restart
最后,定义如下环境变量来以便 docker 客户端使用 tcp API:
$ export DOCKER_HOST=tcp://127.0.0.1:4243
从 Docker 仓库获取最新的 Ubuntu 镜像
$ sudo -E docker pull ubuntu:latest
启动一个新的容器
$ sudo -E docker run -ti ubuntu bash
在这个容器中,安装 nodejs
$ apt-get update && apt-get install -y nodejs
在这个容器中,使用如下内容建立一个简单的 nodejs 脚本 /server.js
var http = require('http'); var os = require('os'); var server = http.createServer(function (request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.end("Hello from " + os.hostname() + "\n"); }); server.listen(8000); console.log("Server running at http://127.0.0.1:8000/");
在这个容器中,使用如下内容编写启动脚本 /run.sh
#! /bin/sh /usr/bin/nodejs /server.js $ chmod a+x /run.sh
中止容器而且建立一个新的镜像:
$ exit # Get the ID of the container $ sudo -E docker ps -a # Change 3796ab3f5b76 in the following command with the ID listed above $ sudo -E docker commit 3796ab3f5b76 local/nodeapp # Remove the old container $ sudo -E docker rm 3796ab3f5b76
在主机上安装 synapse
$ sudo apt-get install build-essential ruby ruby-dev haproxy $ sudo gem install synapse
编辑 /etc/default/haproxy
把 ENABLED 设置成 1
。
启动一个后端实例
启动一个 webapp 容器的实例:
$ sudo -E docker run -d -p 8000 local/nodeapp /run.sh
经过直接在这个容器中调用 nodejs 来测试。咱们必须首先获取暴露的公共端口。
# Get the public port (mapped to 8000 in the container, here 49153) $ sudo docker ps $ curl http://127.0.0.1:49153 # Responds with "Hello from {container_id}"
使用 Synapse 自动配置 HAproxy
使用如下内容建立一个 /etc/synapse.json.conf
配置文件:
{ "services": { "nodesrv": { "discovery": { "method": "docker", "servers": [ { "name": "localhost", "host": "localhost" } ], "container_port": 8000, "image_name": "local/nodeapp" }, "haproxy": { "port": 8080, "listen": [ "mode http", "option httpchk /", "http-check expect string Hello" ] } } }, "haproxy": { "reload_command": "service haproxy reload", "config_file_path": "/etc/haproxy/haproxy.cfg", "do_writes": true, "do_reloads": true, "global": [ "chroot /var/lib/haproxy", "user haproxy", "group haproxy", "daemon" ], "defaults": [ "contimeout 5000", "clitimeout 50000", "srvtimeout 50000" ] } }
咱们能够在这个文件中看到:
services.nodesrv.discovery
: 配置的观察者。这里咱们使用 Docker API 来发现容器运行的名为 local/nodeapp 的镜像以及它暴露的 8080 端口services.nodesrv.haproxy
:配置与 nodesrv service 有关的相关的 HAproxy 端口haproxy
:被 Synapse 管理的全局配置实例启动 HAproxy 和 Synapse
$ sudo service haproxy start $ sudo synapse -c /etc/synapse.json.conf
经过直接调用 HAproxy(监听 8080 端口)来测试
$ curl http://localhost:8080 # Responds Hello from {container_id}
用 nodeapp 启动第二个容器:
$ sudo -E docker run -d -p 8000 local/nodeapp /run.sh
经过 HAproxy 测试一些请求。几秒后,每一个节点都将响应。
在一个新的 shell 中,运行一下循环,每两秒调用一次 HAproxy:
while : do curl http://localhost:8080 sleep 2 done
HAproxy 不是经过 container1 就是经过 container2 响应。
中止其中一个容器:
$ sudo -E docker stop {container_id}
几秒后,仅仅剩下的容器响应。
可是在以前咱们能够看到一些 503 Service Unavailable
错误。这是因为 Synapse 发现中止的容器而且从代理移除它的时候。
在第一篇文章中,我配置 HAprxoy 从 Docker 容器发现后端节点。Synapse 对使这个进程自动化给予了不少帮助。尽管如此,这个解决方案还有一些缺点:
在下一篇文章中,这个解决方案将被扩展成容许在多主机透明部署。