<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[运维进行时]]></title> 
<link>https://blog.liuts.com/index.php</link> 
<description><![CDATA[互联网运维与架构]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[运维进行时]]></copyright>
<item>
<link>https://blog.liuts.com/post/242/</link>
<title><![CDATA[构建一个高可用及自动发现的Docker基础架构-HECD[原创]]]></title> 
<author>刘天斯 &lt;liutiansi@gmail.com&gt;</author>
<category><![CDATA[Docker]]></category>
<pubDate>Sun, 19 Oct 2014 15:36:23 +0000</pubDate> 
<guid>https://blog.liuts.com/post/242/</guid> 
<description>
<![CDATA[ 
	<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Docker的生态日趋成熟，开源社区也不断孵化出优秀的周边项目，覆盖网络、监控、维护、部署、开发等方面。帮助开发、运维人员快速构建、运营Docker服务环境，其中也不乏有大公司的影子，如Google、IBM、Redhat，甚至微软也宣称后续将提供Docker在Windows平台的支持。Docker的发展前景一片大好。但在企业当中，如何选择适合自己的Docker构建方案？可选的方案有kubernetes与CoreOS（都已整合各类组件），另外一种方案为Haproxy+etcd+confd，采用松散式的组织结构，但各个组件之间的通讯是非常严密的，且扩展性更强，定制也更加灵活。下面详细介绍如何使用Haproxy+etcd+confd构建一个高可用及自动发现的Docker基础架构。<br/><strong>一、架构优势</strong><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;约定由Haproxy+etcd+confd+Docker构建的基础服务平台简称“HECD” 架构，整合了多种开源组件，看似松散的结构，事实上已经是一个有机的整体，它们互相联系、互相作用，是Docker生态圈中最理想的组合之一，具有以下优势：<br/>&nbsp;&nbsp;&nbsp;&nbsp;  自动、实时发现及无感知服务刷新；<br/>&nbsp;&nbsp;&nbsp;&nbsp;  支持任意多台Docker主宿机；<br/>&nbsp;&nbsp;&nbsp;&nbsp;  支持多种APP接入且打散至不分主宿机；<br/>&nbsp;&nbsp;&nbsp;&nbsp;  采用Etcd存储信息，集群支持可靠性高；<br/>&nbsp;&nbsp;&nbsp;&nbsp;  采用Confd配置引擎，支持各类接入层，如Nginx；<br/>&nbsp;&nbsp;&nbsp;&nbsp;  支持负载均衡、故障迁移；<br/>&nbsp;&nbsp;&nbsp;&nbsp;  具备资源弹性，伸缩自如（通过生成、销毁容器实现）；<br/><br/><strong>二、架构说明</strong><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在HECD架构中，首先管理员操作Docker Client，除了提交容器（Container）启动与停止指令外，还通过REST-API方式向Etcd（K/V）存储组件注册容器信息，包括容器名称、主宿机IP、映射端口等。Confd配置组件会定时查询Etcd组件获取最新的容器信息，根据定义好的配置模板生成Haproxy配置文件Haproxy.cfg，并且自动reload haproxy服务。用户在访问业务服务时，完全没有感知后端APP的上线、下线、切换及迁移，达到了自动发现、高可用的目的。详细架构图见图1-1。<br/><a href="https://blog.liuts.com/attachment.php?fid=356" target="_blank"><img src="https://blog.liuts.com/attachment.php?fid=356" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><p align="center">图1-1 平台架构图</p><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了方便大家理解各组件间的关系，通过图1-2进行架构流程梳理，首先管理员通过Shell或api操作容器，下一步将容器信息注册到Etcd组件，Confd组件会定时查询Etcd，获取已经注册到Etcd中容器信息，最后通过Confd的模板引擎生成Haproxy配置，整个流程结束。<br/><a href="https://blog.liuts.com/attachment.php?fid=357" target="_blank"><img src="https://blog.liuts.com/attachment.php?fid=357" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><p align="center">图1-2架构流程图</p><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;了解架构流程后，我们逐一对流程中各组件进行详细介绍。<br/>1、Etcd介绍<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Etcd是一个高可用的 Key/Value 存储系统，主要用于分享配置和服务发现。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  简单：支持 curl 方式的用户 API (HTTP+JSON)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  安全：可选 SSL 客户端证书认证<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  快速：单实例可达每秒 1000 次写操作<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  可靠：使用 Raft 实现分布式<br/>2、Confd介绍<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Confd是一个轻量级的配置管理工具。通过查询Etcd，结合配置模板引擎，保持本地配置最新，同时具备定期探测机制，配置变更自动reload。<br/>3、Haproxy介绍<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HAProxy是提供高可用性、负载均衡以及基于TCP和HTTP应用的代理，支持虚拟主机，它是免费、快速并且可靠的一种解决方案。（来源百科）<br/><br/><strong>三、架构部署</strong><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;平台环境基于Centos6.5+Docker1.2构建，其中Etcd的版本为etcd version 0.5.0-alpha，Confd版本为confd 0.6.2，Haproxy版本为HA-Proxy version 1.4.24。下面对平台的运行环境、安装部署、组件说明等进行详细说明，环境设备角色表如下：<br/><a href="https://blog.liuts.com/attachment.php?fid=360" target="_blank"><img src="https://blog.liuts.com/attachment.php?fid=360" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/>1、组件安装<br/>&nbsp;&nbsp;&nbsp;&nbsp;1.1 Docker安装<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SSH终端登录192.168.1.22服务器，执行以下命令：<br/><textarea name="code" class="c" rows="15" cols="100">
 # yum -y install docker-io
 # service docker start
 # chkconfig docker on
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;1.2 Haproxy、confd安装<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SSH终端登录192.168.1.20服务器，执行以下命令：<br/><textarea name="code" class="c" rows="15" cols="100">
1、haproxy
# yum –y install haproxy

2、confd
# wget https://github.com/kelseyhightower/confd/releases/download/v0.6.3/confd-0.6.3-linux-amd64
# mv confd /usr/local/bin/confd
# chmod +x /usr/local/bin/confd
# /usr/local/bin/confd -version
confd 0.6.2
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;1.3 Etcd(v0.4.6)安装<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SSH终端登录192.168.1.21服务器，执行以下命令：<br/><textarea name="code" class="c" rows="15" cols="100">
# mkdir -p /home/install && cd /home/install
# wget https://github.com/coreos/etcd/releases/download/v0.4.6/etcd-v0.4.6-linux-amd64.tar.gz
# tar -zxvf etcd-v0.4.6-linux-amd64.tar.gz
# cd etcd-v0.4.6-linux-amd64
# cp etcd* /bin/
# /bin/etcd -version
etcd version 0.4.6
</textarea><br/>2、组件配置<br/>&nbsp;&nbsp;&nbsp;&nbsp;2.1 Etcd配置<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于etcd是一个轻量级的K/V存储平台，启动时指定相关参数即可，无需配置。<br/><textarea name="code" class="c" rows="15" cols="100">
# mkdir /data/etcd
# /bin/etcd -name etcdserver -peer-addr 192.168.1.21:7001 -addr 192.168.1.21:4001 -data-dir /data/etcd -peer-bind-addr 0.0.0.0:7001 -bind-addr 0.0.0.0:4001 &
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于etcd具备多机支持，参数“-peer-addr”指定与其它节点通讯的地址；参数“-addr”指定服务监听地址；参数“-data-dir”为指定数据存储目录。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于etcd是通过REST-API方式进行交互，常见操作如下：<br/>&nbsp;&nbsp;&nbsp;&nbsp;1) 设置(set) key操作<br/><textarea name="code" class="c" rows="15" cols="100">
# curl -L http://192.168.1.21:4001/v2/keys/mykey -XPUT -d value="this is awesome"
&#123;"action":"set","node":&#123;"key":"/mykey","value":"this is awesome","modifiedIndex":28,"createdIndex":28&#125;&#125;
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;2) 获取(get) key信息<br/><textarea name="code" class="c" rows="15" cols="100">
# curl -L http://192.168.1.21:4001/v2/keys/mykey
&#123;"action":"get","node":&#123;"key":"/mykey","value":"this is awesome","modifiedIndex":28,"createdIndex":28&#125;&#125;
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;3) 删除key信息<br/><textarea name="code" class="c" rows="15" cols="100">
# curl -L http://192.168.1.21:4001/v2/keys/mykey -XDELETE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;"action":"delete","node":&#123;"key":"/mykey","modifiedIndex":29,"createdIndex":28&#125;,"prevNode":&#123;"key":"/mykey","value":"this is awesome","modifiedIndex":28,"createdIndex":28&#125;&#125;
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;更多操作API见https://github.com/coreos/etcd/blob/master/Documentation/api.md。<br/><br/> 2.2 Confd+Haproxy配置<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于Haproxy的配置文件是由Confd组件生成，要求Confd务必要与haproxy安装在同一台主机上，Confd的配置有两种，一种为Confd资源配置文件，默认路径为“/etc/confd/conf.d”目录，另一种为配置模板文件，默认路径为“/etc/confd/templates”。具体配置如下：<br/>创建配置文件目录<br/># mkdir -p /etc/confd/&#123;conf.d,templates&#125;<br/>（1）配置资源文件<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;详细见以下配置文件，其中“src”为指定模板文件名称（默认到路径/etc/confd/templates中查找）；“dest”指定生成的Haproxy配置文件路径；“keys”指定关联Etcd中key的URI列表；“reload_cmd”指定服务重载的命令，本例中配置成haproxy的reload命令。<br/>【/etc/confd/conf.d/ haproxy.toml】<br/><textarea name="code" class="c" rows="15" cols="100">
[template]
src = "haproxy.cfg.tmpl"
dest = "/etc/haproxy/haproxy.cfg"
keys = [
&nbsp;&nbsp;"/app/servers",
]
reload_cmd = "/etc/init.d/haproxy reload"
</textarea><br/>（2）配置模板文件<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Confd模板引擎采用了Go语言的文本模板，更多见http://golang.org/pkg/text/template/，具备简单的逻辑语法，包括循环体、处理函数等，本示例的模板文件如下，通过range循环输出Key及Value信息。<br/>【/etc/confd/templates/haproxy.cfg.tmpl】<br/><textarea name="code" class="c" rows="15" cols="100">
global
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log 127.0.0.1 local3
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxconn 5000
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uid 99
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gid 99
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;daemon

defaults
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log 127.0.0.1 local3
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode http
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;option dontlognull
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;retries 3
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;option redispatch
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxconn 2000
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;contimeout&nbsp;&nbsp;5000
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;clitimeout&nbsp;&nbsp;50000
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;srvtimeout&nbsp;&nbsp;50000

listen frontend 0.0.0.0:80
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode http
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;balance roundrobin
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxconn 2000
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;option forwardfor
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&#123;range gets "/app/servers/*"&#125;&#125;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server &#123;&#123;base .Key&#125;&#125; &#123;&#123;.Value&#125;&#125; check inter 5000 fall 1 rise 2
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&#123;end&#125;&#125;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stats enable
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stats uri /admin-status
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stats auth admin:123456
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stats admin if TRUE
</textarea><br/>（3）模板引擎说明<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;本小节详细说明Confd模板引擎基础语法与示例，下面为示例用到的KEY信息。<br/><textarea name="code" class="c" rows="15" cols="100">
# curl -XPUT http://192.168.1.21:4001/v2/keys/app/servers/backstabbing_rosalind -d value="192.168.1.22:49156"
# curl -XPUT http://192.168.1.21:4001/v2/keys/app/servers/cocky_morse -d value="192.168.1.22:49158"
# curl -XPUT http://192.168.1.21:4001/v2/keys/app/servers/goofy_goldstine -d value="192.168.1.22:49160"
# curl -XPUT http://192.168.1.21:4001/v2/keys/app/servers/prickly_blackwell -d value="192.168.1.22:49162"
</textarea><br/> 1、base<br/>&nbsp;&nbsp;&nbsp;&nbsp;作为path.Base函数的别名，获取路径最后一段。<br/>&#123;&#123; with get "/app/servers/prickly_blackwell"&#125;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;server &#123;&#123;base .Key&#125;&#125; &#123;&#123;.Value&#125;&#125; check<br/>&#123;&#123;end&#125;&#125;<br/><textarea name="code" class="c" rows="15" cols="100">
prickly_blackwell 192.168.1.22:49162
</textarea><br/> 2、get<br/>&nbsp;&nbsp;&nbsp;&nbsp;返回一对匹配的KV，找不到则返回错误。<br/>&#123;&#123;with get "/app/servers/prickly_blackwell"&#125;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;key: &#123;&#123;.Key&#125;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;value: &#123;&#123;.Value&#125;&#125;<br/>&#123;&#123;end&#125;&#125;<br/><textarea name="code" class="c" rows="15" cols="100">
/app/servers/prickly_blackwell 192.168.1.22:49162
</textarea><br/>3、gets<br/>&nbsp;&nbsp;&nbsp;&nbsp;返回所有匹配的KV，找不到则返回错误。<br/> &#123;&#123;range gets "/app/servers/*"&#125;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;&#123;.Key&#125;&#125; &#123;&#123;.Value&#125;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp; &#123;&#123;end&#125;&#125;<br/><textarea name="code" class="c" rows="15" cols="100">
&nbsp;&nbsp;&nbsp;&nbsp;/app/servers/backstabbing_rosalind 192.168.1.22:49156
&nbsp;&nbsp;&nbsp;&nbsp;/app/servers/cocky_morse 192.168.1.22:49158
&nbsp;&nbsp;&nbsp;&nbsp;/app/servers/goofy_goldstine 192.168.1.22:49160
&nbsp;&nbsp; /app/servers/prickly_blackwell 192.168.1.22:49162
</textarea><br/>4、getv<br/>&nbsp;&nbsp;&nbsp;&nbsp;返回一个匹配key的字符串型Value，找不到则返回错误。<br/>&#123;&#123;getv "/app/servers/cocky_morse"&#125;&#125;<br/><textarea name="code" class="c" rows="15" cols="100">
192.168.1.22:49158
</textarea><br/>5、getvs <br/>&nbsp;&nbsp;&nbsp;&nbsp;返回所有匹配key的字符串型Value，找不到则返回错误。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&#123;range getvs "/app/servers/*"&#125;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value: &#123;&#123;.&#125;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&#123;end&#125;&#125;<br/><textarea name="code" class="c" rows="15" cols="100">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value: 192.168.1.22:49156
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value: 192.168.1.22:49158
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value: 192.168.1.22:49160
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value: 192.168.1.22:49162
</textarea><br/>6、split<br/>&nbsp;&nbsp;&nbsp;&nbsp;对输入的字符串做split处理，即将字符串按指定分隔符拆分成数组。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&#123; $url := split (getv "/app/servers/cocky_morse") ":" &#125;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;host: &#123;&#123;index $url 0&#125;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;port: &#123;&#123;index $url 1&#125;&#125;<br/><textarea name="code" class="c" rows="15" cols="100">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;host: 192.168.1.22
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;port: 49158
</textarea><br/>7、ls<br/>&nbsp;&nbsp;&nbsp;&nbsp;返回所有的字符串型子key，找不到则返回错误。<br/>&#123;&#123;range ls "/app/servers/"&#125;&#125;<br/>&nbsp;&nbsp; subkey: &#123;&#123;.&#125;&#125;<br/>&#123;&#123;end&#125;&#125;<br/><textarea name="code" class="c" rows="15" cols="100">
&nbsp;&nbsp; subkey: backstabbing_rosalind
&nbsp;&nbsp; subkey: cocky_morse
&nbsp;&nbsp; subkey: goofy_goldstine
&nbsp;&nbsp; subkey: prickly_blackwell
</textarea><br/>8、lsdir<br/>&nbsp;&nbsp;&nbsp;&nbsp;返回所有的字符串型子目录，找不到则返回一个空列表。<br/>&#123;&#123;range lsdir "/app/"&#125;&#125;<br/>&nbsp;&nbsp; subdir: &#123;&#123;.&#125;&#125;<br/>&#123;&#123;end&#125;&#125;<br/><textarea name="code" class="c" rows="15" cols="100">
subdir: servers
</textarea><br/>（4）启动confd及haproxy服务<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面为启动Confd服务命令行，参数“interval”为指定探测etcd的频率，单位为秒，参数“-node”为指定etcd监听服务主地址，以便获取容器信息。<br/><textarea name="code" class="c" rows="15" cols="100">
# /usr/local/bin/confd -verbose -interval 10 -node '192.168.1.21:4001' -confdir /etc/confd > /var/log/confd.log &
# /etc/init.d/haproxy start
</textarea><br/>3、容器配置<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;前面HECD架构说明内容，有讲到容器的操作会即时注册到etcd组件中，是通过curl命令进行REST-API方式提交的，下面详细介绍通过SHELL及Python-api两种方式的实现方法，支持容器启动、停止的联动。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;3.1、SHELL实现方法<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;实现的原理是通过获取“Docker run ***”命令输出的Container ID，通过“docker inspect Container ID”得到详细的容器信息，分析出容器服务映射的外部端口及容器名称，将以“/app/servers/容器名称”作为Key，“主宿机: 映射端口”作为Value注册到etcd中。其中Key信息前缀(/app/servers)与“/etc/confd/conf.d/haproxy.toml”中的keys参数是保持一致的。<br/>【docker.sh】<br/><textarea name="code" class="c" rows="15" cols="100">
#!/bin/bash

if [ -z $1 ]; then
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo "Usage: c run <image name>:<version>"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c stop <container name>"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit 1
fi

if [ -z $ETCD_HOST ]; then
&nbsp;&nbsp;ETCD_HOST="192.168.1.21:4001"
fi

if [ -z $ETCD_PREFIX ]; then
&nbsp;&nbsp;ETCD_PREFIX="app/servers"
fi

if [ -z $CPORT ]; then
&nbsp;&nbsp;CPORT="80"
fi

if [ -z $FORREST_IP ]; then
&nbsp;&nbsp;FORREST_IP=`ifconfig eth0&#124; grep "inet addr" &#124; head -1 &#124; cut -d : -f2 &#124; awk '&#123;print $1&#125;'`
fi

function launch_container &#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo "Launching $1 on $FORREST_IP ..."

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CONTAINER_ID=`docker run -d --dns 172.17.42.1 -P -v /data:/data -v /etc/httpd/conf:/etc/httpd/conf -v /etc/httpd/conf.d:/etc/httpd/conf.d -v /etc/localtime:/etc/localtime:ro $1 /bin/sh -c "/usr/bin/supervisord -c /etc/supervisord.conf"`
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PORT=`docker inspect $CONTAINER_ID&#124;grep "&#92;"Ports&#92;"" -A 50&#124;grep "&#92;"$CPORT/tcp&#92;"" -A 3&#124; grep HostPort&#124;cut -d '"' -f4&#124;head -1`
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NAME=`docker inspect $CONTAINER_ID &#124; grep Name &#124; cut -d '"' -f4 &#124; sed "s/&#92;///g"&#124;sed -n 2p`

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo "Announcing to $ETCD_HOST..."
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl -XPUT "http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$NAME" -d value="$FORREST_IP:$PORT"

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo "$1 running on Port $PORT with name $NAME"
&#125;

function stop_container &#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo "Stopping $1..."
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CONTAINER_ID=`docker ps -a&#124; grep $1 &#124; awk '&#123;print $1&#125;'`
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo "Found container $CONTAINER_ID"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;docker stop $CONTAINER_ID
&nbsp;&nbsp;echo http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$1
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl -XDELETE http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$1 &> /dev/null
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo "Stopped."
&#125;


if [ $1 = "run" ]; then
&nbsp;&nbsp;launch_container $2
else
&nbsp;&nbsp;stop_container $2
fi
</textarea><br/>docker.sh使用方法:<br/>&nbsp;&nbsp;&nbsp;&nbsp;1) 启动一个容器<br/>&nbsp;&nbsp;&nbsp;&nbsp;# ./docker.sh run yorko/webserver:v3(镜像)<br/>&nbsp;&nbsp;&nbsp;&nbsp;2) 停止一个容器<br/>&nbsp;&nbsp;&nbsp;&nbsp;# ./docker.sh stop berserk_hopper(容器名)<br/><br/> 3.2、Docker-py API实现方法<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通过Python语言调用Docker-py的API实现容器的远程操作（创建、运行、停止），并结合python-etcd模块对etcd进行操作(set、delete)，达到与SHELL方式一样的效果，很明显，Docker-py方式更加容易扩展，可以无缝与现有运营平台对接。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为兼顾到远程API支持，需对docker启动文件“exec”处进行修改，详细见如下：<br/># vi /etc/init.d/docker<br/><textarea name="code" class="c" rows="15" cols="100">
$exec -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock -d &>> $logfile &
</textarea><br/>启动容器的程序如下：<br/>【docker_run.py】<br/><textarea name="code" class="python" rows="15" cols="100">
#!/usr/local/Python/bin/python
import docker
import etcd
import sys

Etcd_ip="192.168.1.21"
Server_ip="192.168.1.22"
App_port="80"
App_protocol="tcp"
Image="yorko/webserver:v3"

Port=""
Name=""

idict=&#123;&#125;
rinfo=&#123;&#125;
try:
&nbsp;&nbsp;&nbsp;&nbsp;c = docker.Client(base_url='tcp://'+Server_ip+':2375',version='1.14',timeout=15)
except Exception,e:
&nbsp;&nbsp;&nbsp;&nbsp;print "Connection docker server error:"+str(e)
&nbsp;&nbsp;&nbsp;&nbsp;sys.exit()

try:
&nbsp;&nbsp;&nbsp;&nbsp;rinfo=c.create_container(image=Image,stdin_open=True,tty=True,command="/usr/bin/supervisord -c /etc/supervisord.conf",volumes=['/data','/etc/httpd/conf','/etc/httpd/conf.d
','/etc/localtime'],ports=[80,22],name=None)
&nbsp;&nbsp;&nbsp;&nbsp;containerId=rinfo['Id']
except Exception,e:
&nbsp;&nbsp;&nbsp;&nbsp;print "Create docker container error:"+str(e)
&nbsp;&nbsp;&nbsp;&nbsp;sys.exit()

try:
&nbsp;&nbsp;&nbsp;&nbsp;c.start(container=containerId, binds=&#123;'/data':&#123;'bind': '/data','ro': False&#125;,'/etc/httpd/conf':&#123;'bind': '/etc/httpd/conf','ro': True&#125;,'/etc/httpd/conf.d':&#123;'bind': '/etc/htt
pd/conf.d','ro': True&#125;,'/etc/localtime':&#123;'bind': '/etc/localtime','ro': True&#125;&#125;, port_bindings=&#123;80:None,22:None&#125;, lxc_conf=None,publish_all_ports=True, links=None, privileged=F
alse,dns='172.17.42.1', dns_search=None, volumes_from=None, network_mode=None,restart_policy=None, cap_add=None, cap_drop=None)
except Exception,e:
&nbsp;&nbsp;&nbsp;&nbsp;print "Start docker container error:"+str(e)
&nbsp;&nbsp;&nbsp;&nbsp;sys.exit()

try:
&nbsp;&nbsp;&nbsp;&nbsp;idict=c.inspect_container(containerId)
&nbsp;&nbsp;&nbsp;&nbsp;Name=idict["Name"][1:]
&nbsp;&nbsp;&nbsp;&nbsp;skey=App_port+'/'+App_protocol
&nbsp;&nbsp;&nbsp;&nbsp;for _key in idict["NetworkSettings"]["Ports"].keys():
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if _key==skey:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Port=idict["NetworkSettings"]["Ports"][skey][0]["HostPort"]
except Exception,e:
&nbsp;&nbsp;&nbsp;&nbsp;print "Get docker container inspect error:"+str(e)
&nbsp;&nbsp;&nbsp;&nbsp;sys.exit()

if Name!="" and Port!="":
&nbsp;&nbsp;&nbsp;&nbsp;try:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client = etcd.Client(host=Etcd_ip, port=4001)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.write('/app/servers/'+Name, Server_ip+":"+str(Port))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print Name+" container run success!"
&nbsp;&nbsp;&nbsp;&nbsp;except Exception,e:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print "set etcd key error:"+str(e)
else:
&nbsp;&nbsp;&nbsp;&nbsp;print "Get container name or port error."
</textarea><br/>停止容器的程序如下：<br/>【docker_stop.py】<br/><textarea name="code" class="python" rows="15" cols="100">
#!/usr/local/Python/bin/python
import docker
import etcd
import sys

Etcd_ip="192.168.1.21"
Server_ip="192.168.1.22"
containerName="grave_franklin" #指定需要停止容器的名称

try:
&nbsp;&nbsp;&nbsp;&nbsp;c = docker.Client(base_url='tcp://'+Server_ip+':2375',version='1.14',timeout=10)
&nbsp;&nbsp;&nbsp;&nbsp;c.stop('furious_heisenberg')
except Exception,e:
&nbsp;&nbsp;&nbsp;&nbsp;print str(e)
&nbsp;&nbsp;&nbsp;&nbsp;sys.exit()

try:
&nbsp;&nbsp;&nbsp;&nbsp;client = etcd.Client(host=Etcd_ip, port=4001)
&nbsp;&nbsp;&nbsp;&nbsp;client.delete('/app/servers/'+containerName)
&nbsp;&nbsp;&nbsp;&nbsp;print containerName+" container stop success!"
except Exception,e:
print str(e)
</textarea><br/>注意：<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于容器是无状态的，尽量让其以松散的形式存在，映射端口选项要求使用“-P”参数，即使用随机端口的模式，减少人手干预。<br/><br/><strong>四、业务上线</strong><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HECD架构已部署完毕，接下来就是让其为我们服务，案例中使用的镜像“yorko/webserver:v3”为已经构建好的LAMP平台。类似的镜像也可以在docker-pub中下载到，开始跑起,运行dockery.sh创建两个容器：<br/><textarea name="code" class="c" rows="15" cols="100">
# ./docker.sh run yorko/webserver:v3
Launching yorko/webserver:v3 on 192.168.1.22 ...
Announcing to 192.168.1.21:4001...
&#123;"action":"set","node":&#123;"key":"/app/servers/berserk_hopper","value":"192.168.1.22:49170","modifiedIndex":33,"createdIndex":33&#125;&#125;
yorko/webserver:v3 running on Port 49170 with name berserk_hopper

# ./docker.sh run yorko/webserver:v3
Launching yorko/webserver:v3 on 192.168.1.22 ...
Announcing to 192.168.1.21:4001...
&#123;"action":"set","node":&#123;"key":"/app/servers/lonely_meitner","value":"192.168.1.22:49172","modifiedIndex":34,"createdIndex":34&#125;&#125;
yorko/webserver:v3 running on Port 49172 with name lonely_meitner
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;访问Haproxy监控地址：http://192.168.1.20/admin-status，刚创建的容器已经添加到haproxy中，见图1-3。<br/><a href="https://blog.liuts.com/attachment.php?fid=358" target="_blank"><img src="https://blog.liuts.com/attachment.php?fid=358" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a> <br/><p align="center"> 图1-3 Haproxy监控后台截图</p><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1）观察Haproxy的配置文件(更新部分)：<br/><textarea name="code" class="c" rows="15" cols="100">
# vi /etc/haproxy/haproxy.cfg
… …
listen frontend 0.0.0.0:80
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode http
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;balance roundrobin
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxconn 2000
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;option forwardfor
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server berserk_hopper 192.168.1.22:49170 check inter 5000 fall 1 rise 2
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server lonely_meitner 192.168.1.22:49172 check inter 5000 fall 1 rise 2
… …
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2）访问php测试文件http://192.168.1.20/info.php<br/> <a href="https://blog.liuts.com/attachment.php?fid=359" target="_blank"><img src="https://blog.liuts.com/attachment.php?fid=359" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><p align="center"> 图1-4 php测试文件截图</p><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从图1-4可以看出，获取的服务器端IP为容器本身的IP地址（172.17.0.11），在System环境变量处输出容器名为“598cf10a50a2”的信息。<br/><br/>参考：<br/>http://ox86.tumblr.com/post/90554410668/easy-scaling-with-docker-haproxy-and-confd<br/>https://github.com/AVGP/forrest/blob/master/forrest.sh<br/>Tags - <a href="https://blog.liuts.com/tags/docker/" rel="tag">docker</a> , <a href="https://blog.liuts.com/tags/confd/" rel="tag">confd</a> , <a href="https://blog.liuts.com/tags/etcd/" rel="tag">etcd</a> , <a href="https://blog.liuts.com/tags/haproxy/" rel="tag">haproxy</a> , <a href="https://blog.liuts.com/tags/hecd%25E6%259E%25B6%25E6%259E%2584/" rel="tag">hecd架构</a>
]]>
</description>
</item><item>
<link>https://blog.liuts.com/post/242/#blogcomment2481</link>
<title><![CDATA[[评论] 构建一个高可用及自动发现的Docker基础架构-HECD[原创]]]></title> 
<author>云飞 &lt;m114@m114.org&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Tue, 21 Oct 2014 05:43:32 +0000</pubDate> 
<guid>https://blog.liuts.com/post/242/#blogcomment2481</guid> 
<description>
<![CDATA[ 
	请问下，这个配置中etcd有做跨机房部署吗？跨机房的效果如何呢？
]]>
</description>
</item><item>
<link>https://blog.liuts.com/post/242/#blogcomment2482</link>
<title><![CDATA[[评论] 构建一个高可用及自动发现的Docker基础架构-HECD[原创]]]></title> 
<author>sany &lt;huangyishan@coocaa.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Tue, 21 Oct 2014 08:47:17 +0000</pubDate> 
<guid>https://blog.liuts.com/post/242/#blogcomment2482</guid> 
<description>
<![CDATA[ 
	刘大师，你好<br/>请问你有没有组织关于Docker的培训课程，我想参加
]]>
</description>
</item><item>
<link>https://blog.liuts.com/post/242/#blogcomment2483</link>
<title><![CDATA[[评论] 构建一个高可用及自动发现的Docker基础架构-HECD[原创]]]></title> 
<author>LeoLiuYan &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Tue, 21 Oct 2014 09:45:05 +0000</pubDate> 
<guid>https://blog.liuts.com/post/242/#blogcomment2483</guid> 
<description>
<![CDATA[ 
	安装etcd 0.5.0-alpha的时候，build不成功，下载etcd-0.4.6可以搞定；另外在etcd-0.4.6启动的时候要加上-name参数。最后文章写的非常棒！多谢博主的无私分享
]]>
</description>
</item><item>
<link>https://blog.liuts.com/post/242/#blogcomment2484</link>
<title><![CDATA[[评论] 构建一个高可用及自动发现的Docker基础架构-HECD[原创]]]></title> 
<author>tiger &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 23 Oct 2014 08:59:28 +0000</pubDate> 
<guid>https://blog.liuts.com/post/242/#blogcomment2484</guid> 
<description>
<![CDATA[ 
	你好,非常感谢那么好的文章啊,请问下我如何添加多个docker宿主机呢?谢谢
]]>
</description>
</item><item>
<link>https://blog.liuts.com/post/242/#blogcomment2485</link>
<title><![CDATA[[评论] 构建一个高可用及自动发现的Docker基础架构-HECD[原创]]]></title> 
<author>fredwu &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 23 Oct 2014 14:52:55 +0000</pubDate> 
<guid>https://blog.liuts.com/post/242/#blogcomment2485</guid> 
<description>
<![CDATA[ 
	NB，按天斯哥的文章一次部署成功，谢谢！
]]>
</description>
</item><item>
<link>https://blog.liuts.com/post/242/#blogcomment2486</link>
<title><![CDATA[[评论] 构建一个高可用及自动发现的Docker基础架构-HECD[原创]]]></title> 
<author>飞天虎 &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 23 Oct 2014 15:32:24 +0000</pubDate> 
<guid>https://blog.liuts.com/post/242/#blogcomment2486</guid> 
<description>
<![CDATA[ 
	大牛，你好 我在部署的时候启动confd出现这个问题？2014-10-23T23:25:28+08:00 docker-test-vm-1.novalocal /usr/local/bin/confd[1267]: ERROR Cannot process template resource /etc/confd/conf.d/haproxy.toml - Near line 4, key &#039;template.keys&#039;: Near line 5: Expected value but found &#039;\u00a0&#039; instead.导致我创建的容器都没有出现 请问这是什么问题呢。我仔细检查了配置文件 没发现什么异常的[root@docker-test-vm-1 conf.d]# more haproxy.toml [template]src = &quot;haproxy.cfg.tmpl&quot;dest = &quot;/etc/haproxy/haproxy.cfg&quot;keys = [  &quot;/app/servers&quot;,]reload_cmd = &quot;/etc/init.d/haproxy reload&quot;# more haproxy.cfg.tmpl ………………&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{range gets &quot;/app/servers/*&quot;}}        server {{base .Key}} {{.Value}} check inter 5000 fall 1 rise 2        {{end}}………………
]]>
</description>
</item><item>
<link>https://blog.liuts.com/post/242/#blogcomment2487</link>
<title><![CDATA[[评论] 构建一个高可用及自动发现的Docker基础架构-HECD[原创]]]></title> 
<author>docker_f &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Sun, 26 Oct 2014 04:52:58 +0000</pubDate> 
<guid>https://blog.liuts.com/post/242/#blogcomment2487</guid> 
<description>
<![CDATA[ 
	大师的精品文章，收藏学习。
]]>
</description>
</item><item>
<link>https://blog.liuts.com/post/242/#blogcomment2488</link>
<title><![CDATA[[评论] 构建一个高可用及自动发现的Docker基础架构-HECD[原创]]]></title> 
<author>huangyishan &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Wed, 29 Oct 2014 03:55:33 +0000</pubDate> 
<guid>https://blog.liuts.com/post/242/#blogcomment2488</guid> 
<description>
<![CDATA[ 
	./docker.sh run yorko/webserver:v3&nbsp;&nbsp;报错，怎么解决呢<br/>Unable to find image &#039;yorko/webserver:v3&#039; locally
]]>
</description>
</item><item>
<link>https://blog.liuts.com/post/242/#blogcomment2489</link>
<title><![CDATA[[评论] 构建一个高可用及自动发现的Docker基础架构-HECD[原创]]]></title> 
<author>lanxera &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Wed, 29 Oct 2014 13:24:22 +0000</pubDate> 
<guid>https://blog.liuts.com/post/242/#blogcomment2489</guid> 
<description>
<![CDATA[ 
	感觉confd+Etcd在这应用场景有点鸡肋哦。要同步更新haproxy配置文件方法太多了吧。除非经常大量上线业务，或用在产品开发调试环境，小批量的也没必要这样折腾了。
]]>
</description>
</item><item>
<link>https://blog.liuts.com/post/242/#blogcomment2492</link>
<title><![CDATA[[评论] 构建一个高可用及自动发现的Docker基础架构-HECD[原创]]]></title> 
<author>小林子 &lt;weilaihui@126.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Mon, 03 Nov 2014 03:23:26 +0000</pubDate> 
<guid>https://blog.liuts.com/post/242/#blogcomment2492</guid> 
<description>
<![CDATA[ 
	两个脚本都只是local化的，不能集群，加上Shipyard可以解决这个问题。我最近也在研究这个。非常感谢提供的架构图
]]>
</description>
</item>
</channel>
</rss>