<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[运维进行时]]></title> 
<link>https://www.liuts.com/index.php</link> 
<description><![CDATA[互联网运维与架构]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[运维进行时]]></copyright>
<item>
<link>https://www.liuts.com/post/252/</link>
<title><![CDATA[腾讯游戏大数据解密：DevOps持续改进之道-布道【原创】]]></title> 
<author>刘天斯 &lt;liutiansi@gmail.com&gt;</author>
<category><![CDATA[Docker]]></category>
<pubDate>Mon, 19 Dec 2016 13:45:08 +0000</pubDate> 
<guid>https://www.liuts.com/post/252/</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2016年12月3日，在深圳圣淘沙酒店，运维帮、云技术、Linux中国三大社区联合主办主办了运维世界大会OpsWorld，演讲内容全程无广告，只谈技术，受到了广大观众的一直好评。本文根据刘天斯老师演讲内容整理而成。<br/><a href="https://www.liuts.com/attachment.php?fid=398" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=398" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我分享的主题是“DevOps持续改进之道——布道”。我刚才已经做了简单的介绍，这里再罗嗦一下，我叫刘天斯，现在是在腾讯互动娱乐负责大数据的运营。在互联网行业已经从业了13年，也算是一个老鸟，之前在天涯，工作了6年多。PPT右侧是我的两本著作，第二本是关于Docker的，如果大家关注Docker，可以在网上购买这本书来看看。我个人最近关注的方向有自动化运维、云计算、大数据、Docker&nbsp;&nbsp;DevOps等。<br/><a href="https://www.liuts.com/attachment.php?fid=399" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=399" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是我今天分享的大纲，总共分四大块，第一块是跟大家简单介绍一下我的DevOps观，第二点我会介绍我们布道平台在持续集成与交付这块是怎么做的。第三点会介绍布道在线服务运营能力。最后给大家做一个总结。<br/><a href="https://www.liuts.com/attachment.php?fid=400" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=400" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;可能大部分人都认为DevOps无非是基于文化+技术这两个点在企业里面落地，这是大部分人之前的思维，在2015年底举行的全球DevOps峰会上，Gartner把DevOps做了进一步的划分，在文化和技术后面又细化出了过程和人。DevOps并非目标，但它可帮助您实现目标，企业的目标是更快地交付价值，DevOps是辅助我们完成这样一个目标的手段。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DevOps产生的价值，我比较认同IBM给出的解释，总共有三点，第一，它认为这个会增强客户的体验。这是比较容易理解的，第二点是提高创新能力，这个不好理解，创新和它有什么关系呢？它的说法是这样的，通过DevOps去实践，通过精益的方法论去减少我们的浪费、返工，将我们现有的价值及精力投入到更有价值的事情，创新是其中的一个非常重要的点。第三个就是更快的实现价值。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DevOps应该是永无止境的，它是一个持续改进的过程，它没有终点，它的目的是对影响交付质量的对象进行持续的改进，包括我们的技术、流程、团队，甚至企业的文化等。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;那我们怎么去检验我们的软件交付过程是不是高效的？我个人总结了一个“短”五个“快”。短就是我们交付软件的开发周期要短，快就是排错、解决问题、测试、部署、反馈这个闭环一定要快，不知道大家认不认可这样的观点，一会儿我们可以讨论一下。<br/><a href="https://www.liuts.com/attachment.php?fid=401" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=401" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Gartner在去年底提出DevOps的模型，它分为四个点，除了文化、技术，它还延伸出了过程和人这两个角色。我们首先从【技术】来看，这里面加亮的部分是重点要关注的，一个是【基础设施即代码】，第二个是【一键安装程序构建，测试、部署】，第三个是【监控一切】，第四个是【连续监控】。在【过程】里面，我认为应该关注的是【持续集成】、【测序】、【交付】这几个方面，另外还要关注到【自动化的构建】，包括【自动化发布】。我觉得【失败总结】这一点是比较重要的，当我们过程失败之后，必须要知道失败在哪里，要进行总结。另外一个是一切都要工具化，一切都要版本化。除了代码以外，我们的脚本或者是配置文件都要通过版本化进行管理。在文化这部分，我认为比较重要的就是协作的文化，还有持续的改进，还有学习的文化，这三个都不难理解，但是我认为，这里面是不是还遗漏了一点？我觉得少了信息的共享、透明，我认为这是非常关键。为什么这个很重要？在我们内部定期都会组织相关的讨论会，去讨论我们项目中碰到的问题以及风险，这些都是需要关注的，另外一点就是IBM提出的一个概念，运维要“左移”，意思是运维要在开发的生命周期阶段提前介入。这样做有什么好处呢？好处是有助于运维的问题提前在开发阶段提前暴露，我认为这是非常关键的一点。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面简单介绍一下我们服务的背景，我们为谁服务，服务什么样的平台。大家看一下我们的规模，当前的数据量每天入库7600亿条日志，大概80T，占公司总存储的26%。<br/><a href="https://www.liuts.com/attachment.php?fid=402" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=402" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是我们的服务精细化的场景，这个背景是怎么来的？因为我们已经具备了海量的游戏数据，我们也在思考，怎么通过数据去驱动我们做精细化的运营。这也是我们目前不断在思考的。DataMore是我们思考出来的一个解决方案，它目前包括三个层次，第一个是大数据H5应用，比如说我们经常看到朋友圈大家分享的自己玩游戏的对局信息，这些数据都是由我们这边提供的。第二个是场景化的标签服务，我们会针对不同玩家打标签，打完标签就通过我们的精细化触达平台，针对性的做一些触达和营销活动。第三个是应用产品，比如说我们的游戏助手、官网，甚至是游戏里面的个人中心，大家会看到自己在游戏中的状态、成长轨迹、对战信息等，这些都可以看到，这就是游戏业务典型的一个服务化的场景。<br/><a href="https://www.liuts.com/attachment.php?fid=403" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=403" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面再介绍一下我们的数据服务的架构，这个其实也没有太多的技术含量，基本上都是业界主流的能力，包括最左侧的游戏的数据源，它有两类，一类是游戏内的，一类是游戏外的。平台组件有三大块，一是数据接入，数据接入包括实时传输、离线传输，第二个是数据处理，包括实时计算引擎、离线引擎，第三个是数据储存，包括KV存储、DB存储、位图索引、HDFS等。我们的服务引擎这一块包括数据分析引擎、数据接口中心、运营规则引擎、用户触达中心，业务应用就是报表统计、数据分析、触达运营，总体来讲就是包括这样三块：数据采集、存储计算、应用落地。<br/><a href="https://www.liuts.com/attachment.php?fid=404" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=404" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是我们的DataMore体系的技术架构，整个架构层次也比较简单，最上层是STGW，这是腾讯非常强大的一个负载均衡平台，再下面是二级代理。逻辑层我们现在有GoServer&nbsp;&nbsp;PHP Server。实时数据层用的都是业界主流数据库存储技术，比如TRedis、Hbase、Postgresql等。最低层是数据计算层，包括实时计算引擎、离线计算引擎。当前整个服务的PV大概是6.35亿，日发布变更15次，单次发布时间是10秒，也就是一个容器起启的时间。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面介绍一下今天的主角“布道”。布道是我们数据服务全流程解决方案，采用了DevOps的思想实现软件的快速迭代、快速试错，最终提升软件交付的质量和达到持续改进的目的。它有几个特点，第一个是基于腾讯蓝鲸PaaS平台快速构建。蓝鲸本身就是一个PaaS平台，我们基于蓝鲸之上构建服务层的SaaS，本身布道就是基于最上层的SaaS服务。第二个是建立在DevOps的思想实践。我们这个实践思路来源于我刚才分享的Gartner发布的DevOps模型。各关键节点间之间的联系都是我们参考的样本。第三是面向运维、开发、测试、项目PM。第四个是具备持续集成、交付、部署等快速迭代的能力。第五是具备服务质量跟踪、用户舆情、持续反馈能力。<br/><a href="https://www.liuts.com/attachment.php?fid=405" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=405" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是布道的能力体系，大家可以看一下，实际上包括两大块，一个是持续集成交付，另外一个是在线运营。其中服务质量和持续改进是贯穿整个服务生命周期。<a href="https://www.liuts.com/attachment.php?fid=406" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=406" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是布道服务层的架构，刚才提到了这一块，第一块持续集成&交付是采用业界主流的架构，且比较通用，我就不详细再讲了。我们的在线运营是基于HECD的架构，实现Docker的发现注册与发现，最上层就是我们的接入层，比较简单高效，因为没有太多其它逻辑关联耦合的节点。这里的TDocker是部门内部基于Docker构建的容器服务解决方案。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;以DevOps形成的运营服务闭环，它的内涵及核心就是实现持续改进。首先通过我们的CICD的快速通道，实现软件平台的快速迭代，在质量管理方面，我们通过监控、安全、故障自愈、BUG修复等方式进行质量管理。在用户反馈方面，我们建立一个通道，会收集并响应用户的反馈，包括来源于客服和对外网舆情的采集，会将这些信息反馈给开发人员，开发人员收到反馈之后，会在功能或者bug这一块做功能修复，最终就会形成一个持续改进的闭环，而且是一个良性的闭环。<br/><a href="https://www.liuts.com/attachment.php?fid=407" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=407" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;再简单介绍一下我们持续集成&交付的架构，刚才已经有提到，这里就简单说一下里面的几点细节。首先采用Docker架构具有一些优势，它可以保持跨环境一致性，天然易移植性，还有易于版本控制。然后CI&CD在流程方面做到编译并构建版本镜像，推送镜像仓库，触发交付作业并快速实现预览。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;以上介绍的是行业的主流做法，各家的方案都是大同小异的，我们在这个过程中加入了一些独特的东西，一个是代码扫描的工具coverity，它能够发现比较深层次代码逻辑问题，比如内存泄露、溢出、数组越界、未初始化等等，且目前支持的语言也比较多。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;另外一个是通过引入外网的流量，帮助我们实现压测，甚至做到一定BUG定位，它能达到百分之百的仿真的效果，具体的细节大家简单看一下PPT，整个步骤也非常简单，不是很复杂，它的特点是可以达到真实的模拟测试验证，有助于提前发现问题，有效降低发布风险。<br/><a href="https://www.liuts.com/attachment.php?fid=408" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=408" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是一个案例，开发人员提交他的版本，然后进行预发布，在预发布里面有预览校验软件功能，也可引入外部的流量来进行预览，出现BUG会这个过程中被发现。我们在实践过程中碰到几点问题，在这里也跟大家一起分享。第一点是在容器中做源码的编译合不合理，不知道大家是怎么做的，我们给的结论是不合理。每个项目它用的第三方包或者功能组件是不一样的，可能A项目用的是A、B、C组件，B项目用的是1、2、3组件，不同的组件你在编译的时候去部署它的编译环境，这样会使我们的容器越来越臃肿，所以我们把这个环境迁移到另外一种角色（jenkinsslave），让它做源码的编译，编译完了就打包成镜像，保证镜像中没有太多无用的数据文件。第二点是容器性能的监控最优方案。我们使用Docker的时候有没发现一个问题?在容器中无论我们执行free、top、uptime、vmstat等命令，看到的都是主宿机的性能信息，原因是由于Docker的隔离性做得不够彻底导致。我们可以通过LXCFS这个组件帮助我们增强Docker的隔离性，它可以提供一个虚拟的PROC文件系统，另外也提供了容器自身的Cgroup的目录树，跟容器中的PORC目录是一一对应的。如何使用？我们Dockerrun的时候，通过“-v”参数，实现容器proc文件与Lxcfsproc的映射，效果是容器中看到的只是容器本身的内存、CPU等信息。比如说我们看CPU核数，看到的只是分配好的0、1、2、4的核数。第三点是容器在CI阶段的网络选择。这里只是给个建议，我们生产环境Tdocker使用的网络模型SRIOV，是通过硬件虚拟网卡的方式实现，然后Docker再通过IPlink做映射，但是我们在CI阶段，它是一个隔离的开发专区，它跟普通的IDC是不一样的，由于硬性相对比较老久，硬件虚拟化兼容性差，因此我们使用了Docker Macvlan方案，原理是通过创建网卡子接口与容器接口之间做映射，缺点是要求Linux Kernel 3.10.0及Docker>=1.12.0的环境。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是我们的线上服务。项目上线的时候，每个PM都有一个习惯，都会最大化去的申请资源，在极端情况下还会远远超过他的预估值，这个时候怎么办？只需要评估扩容的需求量为多少，什么样的机型配置，提交Tdocker在线扩容就可以了，后面的事情就交给布道去处理。<br/><a href="https://www.liuts.com/attachment.php?fid=409" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=409" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在告警方面我也分享一些思路，在腾讯已经有一套非常成熟的告警系统，我们在这里只是提供一些跟我们业务逻辑相关的策略，我们的思路首先是统一日志规范，每个人的开发水平和习惯都不一样，我们给他们统一一个规范，这个规范叫Tlog，另外引入基础+特性这两个指标。看到的这个DEMO，它通过XML的模式描述日志的结构。特性是跟业务绑定相关的，我这里有一些服务，这个服务我发了一些金币，发到某个区间可能出现问题了，我们就需要开发人员把这个发放的数值打印出来，我们好做一个曲线的跟踪。采集完之后就会进入一个日志采集环节，这个架构也很简单，就是LVS+Keepalive，采集完之后，这里就分两条线，一个是实时，一个是离线的。我们采用的是蓝鲸的实时数据平台，能够实现特性指标多维度的配置。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是蓝鲸的智能监控的界面。它是实时流，内部用的是Storm做实时分析，然后支持定义灵活的配置，有支持SQL的函数，比如支持某个字段的累加、求和，这个频率可以控制为10分钟、15分钟。<br/><a href="https://www.liuts.com/attachment.php?fid=410" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=410" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面是我们安全这一块做的东西，首先是安全防御，在腾讯内部，安全这一块已经做得非常强悍，为什么我还讲安全呢？因为在公司层面做的安全防护更多是通用性或者基础类的防护，WAF更侧重在业务逻辑层，它的架构非常简单，通过布道做一些规则的下发到WAF，例如XSS、CC、UA、URI的规则等等，然后开启日志追踪，通过传输、实时分析防护日志，最终做到防护报告实时展示。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;规则校验步骤与大家简单介绍一下，目前采用Nginx+LUA结合的方式，它的防护流程是在检查端口及域名是否匹配规则，匹配就下发规则，然后检查黑白IP名单，后面再开启CC防护，以及开启userAgent检查，最后是开启URI检查注入、XSS、SSRF等。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面是舆情监控这一块，我们在这里特点就是关键词的实时监控、定时报送推送。从下面这个图可以看到，某一款游戏在某此活动期间的口碑展示情况，有多少是正面的，有多少是负面的报告。<br/><a href="https://www.liuts.com/attachment.php?fid=411" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=411" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后做一个总结，在线运营的运营经验，第一个是资源评估，我们要定一个标准，根据实际服务场景来做出评估，而不是拍脑袋做。这个评估有一个公式：设计数量=（PV/86400）×2/单机承载最大QPS/0.8。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第二个是灰度+热力度对蓝绿发布。蓝绿发布的目的是让我们的用户没有感知，我们做蓝绿发布，操作的步骤很多，一旦人工接触多了，就容易出现误操作。我们采用的是热更新方式，原理是服务A进程收到关闭信号量之后，启动B进程来接收已经创建的连接或新连接，当A进程连接完全释放之后就会自动关闭，整个过程用户无感知。灰度实际就是放量检验这个效果及功能，如中间没有出现问题，只需两步就完成变更。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第三个是修复了Supervisor一个bug。Supervisor没办法对进程fork子进程进行管理，所以我们修复了这个bug，如大家碰到同样的问题可以私下找我，我会给大家具体的解决方法。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后是一个数据流向监控的实现思路。首先简单介绍一下为什么要做这个流向监控。数据的流向监控在业界目前没有较好的方案，当我们的数据链、处理链、服务链拉得很长的时候，如何感知当数据源头有发生变更或异常，影响到我们的最终服务，这是非常难度的，而且它跟我们的业务特点关系非常大。同样我们逆向推导也是成立的，我发现玩家的某个对局数据不对，本来是胜局结果变成了负局，怎么确认数据是从哪个节点负责计算或存储的？这个非常有挑战性，这需要我们的数据服务能力一步一步做叠加，需要不断做扩展与关联，才能够达到某一个层次的服务水平。目前我们做的还没那么完美。首先我们要具备元数据管理，还有元信息的管理，这是非常重要的两层。第二个是要有数据字典管理，我们给数据表名称、字段说明，结构性的定义及标签。第三个就是血缘关系，能够定义到任务之间的关系。第四个是数据服务的注册，这是比较核心的一点，怎么注册，我们怎么知道你用了哪个数据源呢？这很关键，我们内部开发了一个通用组件，比如说你要引用后端的数据层，你必须使用这个组件接入，这样关系才能够建立起来，所以应用配置文件也是单独生成的。而最上游我们会提供一个数据流向查询，我们用了A数据，查下来就知道它在这个过程经过了哪些关键点，有可能是存储、计算、分析、接口等。<a href="https://www.liuts.com/attachment.php?fid=412" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=412" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后快速做一个总结，这是我们的持续服务的体系，在持续运营状态下，我们具备质量监控、舆情监控、容量管理、安全管理、故障治愈、故障修复等能力，同时收集用户服务体验、产品、BUG、新功能、吐槽点，将这些信息反馈给产品人员、开发人员和运维人员。再通过持续集成交付、发布变更管理，做到快速迭代及部署，最后就形成了良性的、持续改进的闭环。 <br/><br/>1、【提问】：你刚才提到了资源分配的公式，你们那个公式是怎么总结出来的？那个公式的基础是以什么样的结果作为标准的？<br/>【刘天斯】：这个公式也不是我们发明的，也是借鉴了很多前辈思路，我们只是在这个基础上做改进，结合我们自己的业务场景，包括我们服务是什么样的类型，因为服务类型不同，它的要求不一样，配置不一样，它的生命周期也不一样，它能够反映出来的效果和能力、访问量都不一样，比如服务到底是CPU型还是内存或IO型，完全不一样。这就带来一个问题，我们怎么定一个标准？我们目前聚焦在接入这块的逻辑层，供参考的是8核、4G内存，硬盘100到150G的配置。<br/><br/>2、【提问】：我是来自创维公司负责运维的。今天讲的是DevOps，DevOps要快速迭代到什么样的程度才行？像您现在做的一个平台一天发布15次，我不知道你在前面需要进行多长时间的准备？比如说线上的实时导流导多长时间是合适的？这个度我们一直拿捏不准，能帮我们解答一下，提点好的建议吗？【刘天斯】：在互联网行业我们大部分都是采用敏捷开发的模式，这样会产生技术的债务，这是必然的。你是传统行业吗？<br/>【提问】：我们前身是传统行业，现在我们是互联网电视，相当于做电视上的服务，我们是非常快捷的，每天都发布，每天改bug，不停地迭代，而且需求也特别多，开发也忙不过来，我们运维也感觉到准备不足。<br/>【刘天斯】：互联网这种快速发展的模式，势必会带来很多技术的债务，我觉得这是正常的。我们的互联网发展很快，必须要先要有一个雏形产品出来，然后再不断地迭代，这是大部分互联网公司或者是刚刚转型公司采用的模式，这种模式出现的问题我认为是很正常的问题，包括在腾讯也一样有这样的问题，他评估不到我在某个阶段要花多长时间去检验我们是否够快、是否高效。我们在布道这个环节下一步就是预发布，预发布追求的并不是快，而是能不能在临门一脚之前发现一些致命的问题，因此，可以在持续交付阶段追求快，而不应在预发布阶段。<br/>Tags - <a href="https://www.liuts.com/tags/%25E5%25A4%25A7%25E6%2595%25B0%25E6%258D%25AE/" rel="tag">大数据</a> , <a href="https://www.liuts.com/tags/%25E5%25B8%2583%25E9%2581%2593/" rel="tag">布道</a> , <a href="https://www.liuts.com/tags/%25E5%2588%2598%25E5%25A4%25A9%25E6%2596%25AF/" rel="tag">刘天斯</a>
]]>
</description>
</item><item>
<link>https://www.liuts.com/post/247/</link>
<title><![CDATA[基于kubernetes构建Docker集群管理详解]]></title> 
<author>刘天斯 &lt;liutiansi@gmail.com&gt;</author>
<category><![CDATA[Docker]]></category>
<pubDate>Mon, 22 Dec 2014 13:41:54 +0000</pubDate> 
<guid>https://www.liuts.com/post/247/</guid> 
<description>
<![CDATA[ 
	<br/><strong>一、前言</strong><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Kubernetes 是Google开源的容器集群管理系统，基于Docker构建一个容器的调度服务，提供资源调度、均衡容灾、服务注册、动态扩缩容等功能套件，目前最新版本为0.6.2。本文介绍如何基于Centos7.0构建Kubernetes平台，在正式介绍之前，大家有必要先理解Kubernetes几个核心概念及其承担的功能。以下为Kubernetes的架构设计图：<br/><a href="https://www.liuts.com/attachment.php?fid=368" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=368" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>1. Pods<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在Kubernetes系统中，调度的最小颗粒不是单纯的容器，而是抽象成一个Pod，Pod是一个可以被创建、销毁、调度、管理的最小的部署单元。比如一个或一组容器。<br/>2. Replication Controllers<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Replication Controller是Kubernetes系统中最有用的功能，实现复制多个Pod副本，往往一个应用需要多个Pod来支撑，并且可以保证其复制的副本数，即使副本所调度分配的主宿机出现异常，通过Replication Controller可以保证在其它主宿机启用同等数量的Pod。Replication Controller可以通过repcon模板来创建多个Pod副本，同样也可以直接复制已存在Pod，需要通过Label selector来关联。<br/>3、Services<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Services是Kubernetes最外围的单元，通过虚拟一个访问IP及服务端口，可以访问我们定义好的Pod资源，目前的版本是通过iptables的nat转发来实现，转发的目标端口为Kube_proxy生成的随机端口，目前只提供GOOGLE云上的访问调度，如GCE。如果与我们自建的平台进行整合？请关注下篇《kubernetes与HECD架构的整合》文章。<br/>4、Labels<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Labels是用于区分Pod、Service、Replication Controller的key/value键值对，仅使用在Pod、Service、 Replication Controller之间的关系识别，但对这些单元本身进行操作时得使用name标签。<br/>5、Proxy<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Proxy不但解决了同一主宿机相同服务端口冲突的问题，还提供了Service转发服务端口对外提供服务的能力，Proxy后端使用了随机、轮循负载均衡算法。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;说说个人一点看法，目前Kubernetes 保持一周一小版本、一个月一大版本的节奏，迭代速度极快，同时也带来了不同版本操作方法的差异，另外官网文档更新速度相对滞后及欠缺，给初学者带来一定挑战。在上游接入层官方侧重点还放在GCE（Google Compute Engine）的对接优化，针对个人私有云还未推出一套可行的接入解决方案。在v0.5版本中才引用service代理转发的机制，且是通过iptables来实现，在高并发下性能令人担忧。但作者依然看好Kubernetes未来的发展，至少目前还未看到另外一个成体系、具备良好生态圈的平台，相信在V1.0时就会具备生产环境的服务支撑能力。<br/><br/><strong>一、环境部署</strong><br/>1、平台版本说明<br/>&nbsp;&nbsp;&nbsp;&nbsp;1）Centos7.0 OS<br/>&nbsp;&nbsp;&nbsp;&nbsp;2）Kubernetes V0.6.2<br/>&nbsp;&nbsp;&nbsp;&nbsp;3）etcd version 0.4.6<br/>&nbsp;&nbsp;&nbsp;&nbsp;4）Docker version 1.3.2<br/><br/>2、平台环境说明<br/><a href="https://www.liuts.com/attachment.php?fid=369" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=369" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/>3、环境安装<br/>&nbsp;&nbsp;&nbsp;&nbsp;1）系统初始化工作（所有主机）<br/>&nbsp;&nbsp;&nbsp;&nbsp;系统安装-选择[最小化安装]<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>&nbsp;&nbsp;&nbsp;&nbsp;# yum -y install wget ntpdate bind-utils<br/>&nbsp;&nbsp;&nbsp;&nbsp;# wget http://mirror.centos.org/centos/7/extras/x86_64/Packages/epel-release-7-2.noarch.rpm<br/>&nbsp;&nbsp;&nbsp;&nbsp;# yum update<br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;CentOS 7.0默认使用的是firewall作为防火墙，这里改为iptables防火墙（熟悉度更高，非必须）。<br/>&nbsp;&nbsp;&nbsp;&nbsp;1.1、关闭firewall：<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>&nbsp;&nbsp;&nbsp;&nbsp;# systemctl stop firewalld.service #停止firewall<br/>&nbsp;&nbsp;&nbsp;&nbsp;# systemctl disable firewalld.service #禁止firewall开机启动<br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;1.2、安装iptables防火墙<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>&nbsp;&nbsp;&nbsp;&nbsp;# yum install iptables-services #安装<br/>&nbsp;&nbsp;&nbsp;&nbsp;# systemctl start iptables.service #最后重启防火墙使配置生效<br/>&nbsp;&nbsp;&nbsp;&nbsp;# systemctl enable iptables.service #设置防火墙开机启动<br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;2）安装Etcd（192.168.1.10主机）<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>&nbsp;&nbsp;&nbsp;&nbsp;# mkdir -p /home/install && cd /home/install&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;# wget https://github.com/coreos/etcd/releases/download/v0.4.6/etcd-v0.4.6-linux-amd64.tar.gz&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;# tar -zxvf etcd-v0.4.6-linux-amd64.tar.gz&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;# cd etcd-v0.4.6-linux-amd64&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;# cp etcd* /bin/&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;# /bin/etcd -version&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;etcd version 0.4.6&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;启动服务etcd服务，如有提供第三方管理需求，另需在启动参数中添加“-cors='*'”参数。<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>&nbsp;&nbsp;&nbsp;&nbsp;# mkdir /data/etcd&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;# /bin/etcd -name etcdserver -peer-addr 192.168.1.10:7001 -addr 192.168.1.10:4001 -data-dir /data/etcd -peer-bind-addr 0.0.0.0:7001 -bind-addr 0.0.0.0:4001 &<br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;配置etcd服务防火墙，其中4001为服务端口，7001为集群数据交互端口。<br/>&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>&nbsp;&nbsp;&nbsp;&nbsp;# iptables -I INPUT -s 192.168.1.0/24 -p tcp --dport 4001 -j ACCEPT<br/>&nbsp;&nbsp;&nbsp;&nbsp;# iptables -I INPUT -s 192.168.1.0/24 -p tcp --dport 7001 -j ACCEPT<br/>&nbsp;&nbsp; </div></div><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;3）安装Kubernetes（涉及所有Master、Minion主机）<br/>&nbsp;&nbsp;&nbsp;&nbsp;通过yum源方式安装，默认将安装etcd, docker, and cadvisor相关包。<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>&nbsp;&nbsp;&nbsp;&nbsp;# curl https://copr.fedoraproject.org/coprs/eparis/kubernetes-epel-7/repo/epel-7/eparis-kubernetes-epel-7-epel-7.repo -o /etc/yum.repos.d/eparis-kubernetes-epel-7-epel-7.repo<br/>&nbsp;&nbsp;&nbsp;&nbsp;#yum -y install kubernetes<br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;升级至v0.6.2，覆盖bin文件即可，方法如下：<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>&nbsp;&nbsp;&nbsp;&nbsp;# mkdir -p /home/install && cd /home/install<br/>&nbsp;&nbsp;&nbsp;&nbsp;# wget https://github.com/GoogleCloudPlatform/kubernetes/releases/download/v0.6.2/kubernetes.tar.gz<br/>&nbsp;&nbsp;&nbsp;&nbsp;# tar -zxvf kubernetes.tar.gz<br/>&nbsp;&nbsp;&nbsp;&nbsp;# tar -zxvf kubernetes/server/kubernetes-server-linux-amd64.tar.gz<br/>&nbsp;&nbsp;&nbsp;&nbsp;# cp kubernetes/server/bin/kube* /usr/bin<br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;校验安装结果，出版以下信息说明安装正常。<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>&nbsp;&nbsp;&nbsp;&nbsp;[root@SN2014-12-200 bin]# /usr/bin/kubectl version<br/>&nbsp;&nbsp;&nbsp;&nbsp;Client Version: version.Info&#123;Major:"0", Minor:"6+", GitVersion:"v0.6.2", GitCommit:"729fde276613eedcd99ecf5b93f095b8deb64eb4", GitTreeState:"clean"&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;Server Version: &version.Info&#123;Major:"0", Minor:"6+", GitVersion:"v0.6.2", GitCommit:"729fde276613eedcd99ecf5b93f095b8deb64eb4", GitTreeState:"clean"&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;4）Kubernetes配置（仅Master主机）<br/>&nbsp;&nbsp;&nbsp;&nbsp;master运行三个组件,包括apiserver、scheduler、controller-manager，相关配置项也只涉及这三块。<br/>4.1、【/etc/kubernetes/config】<br/><textarea name="code" class="c" rows="15" cols="100">
# Comma seperated list of nodes in the etcd cluster
KUBE_ETCD_SERVERS="--etcd_servers=http://192.168.1.10:4001"

# logging to stderr means we get it in the systemd journal
KUBE_LOGTOSTDERR="--logtostderr=true"

# journal message level, 0 is debug
KUBE_LOG_LEVEL="--v=0"

# Should this cluster be allowed to run privleged docker containers
KUBE_ALLOW_PRIV="--allow_privileged=false"
</textarea><br/>4.2、【/etc/kubernetes/apiserver】<br/><textarea name="code" class="c" rows="15" cols="100">
# The address on the local server to listen to.
KUBE_API_ADDRESS="--address=0.0.0.0"

# The port on the local server to listen on.
KUBE_API_PORT="--port=8080"

# How the replication controller and scheduler find the kube-apiserver
KUBE_MASTER="--master=192.168.1.200:8080"

# Port minions listen on
KUBELET_PORT="--kubelet_port=10250"

# Address range to use for services
KUBE_SERVICE_ADDRESSES="--portal_net=10.254.0.0/16"

# Add you own!
KUBE_API_ARGS=""
</textarea><br/>4.3、【/etc/kubernetes/controller-manager】<br/><textarea name="code" class="c" rows="15" cols="100">
# Comma seperated list of minions
KUBELET_ADDRESSES="--machines= 192.168.1.201,192.168.1.202"

# Add you own!
KUBE_CONTROLLER_MANAGER_ARGS=""
</textarea><br/>4.4、【/etc/kubernetes/scheduler】<br/><textarea name="code" class="c" rows="15" cols="100">
# Add your own!
KUBE_SCHEDULER_ARGS=""
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;启动master侧相关服务<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>&nbsp;&nbsp;&nbsp;&nbsp;# systemctl daemon-reload<br/>&nbsp;&nbsp;&nbsp;&nbsp;# systemctl start kube-apiserver.service kube-controller-manager.service kube-scheduler.service<br/>&nbsp;&nbsp;&nbsp;&nbsp;# systemctl enable kube-apiserver.service kube-controller-manager.service kube-scheduler.service<br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;5）Kubernetes配置（仅minion主机）<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;minion运行两个组件,包括kubelet、proxy，相关配置项也只涉及这两块。<br/>&nbsp;&nbsp;&nbsp;&nbsp;Docker启动脚本更新<br/>&nbsp;&nbsp;&nbsp;&nbsp;# vi /etc/sysconfig/docker<br/>&nbsp;&nbsp;&nbsp;&nbsp;添加：-H tcp://0.0.0.0:2375，最终配置如下，以便以后提供远程API维护。<br/>&nbsp;&nbsp;&nbsp;&nbsp;OPTIONS=--selinux-enabled -H tcp://0.0.0.0:2375 -H fd://<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;修改minion防火墙配置，通常master找不到minion主机多半是由于端口没有连通。<br/>&nbsp;&nbsp;&nbsp;&nbsp;iptables -I INPUT -s 192.168.1.200 -p tcp --dport 10250 -j ACCEPT<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;修改kubernetes minion端配置，以192.168.1.201主机为例，其它minion主机同理。<br/>5.1、【/etc/kubernetes/config】<br/><textarea name="code" class="c" rows="15" cols="100">
# Comma seperated list of nodes in the etcd cluster
KUBE_ETCD_SERVERS="--etcd_servers=http://192.168.1.10:4001"

# logging to stderr means we get it in the systemd journal
KUBE_LOGTOSTDERR="--logtostderr=true"

# journal message level, 0 is debug
KUBE_LOG_LEVEL="--v=0"

# Should this cluster be allowed to run privleged docker containers
KUBE_ALLOW_PRIV="--allow_privileged=false"
</textarea><br/>5.2、【/etc/kubernetes/kubelet】<br/><textarea name="code" class="c" rows="15" cols="100">
###
# kubernetes kubelet (minion) config

# The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)
KUBELET_ADDRESS="--address=0.0.0.0"

# The port for the info server to serve on
KUBELET_PORT="--port=10250"

# You may leave this blank to use the actual hostname
KUBELET_HOSTNAME="--hostname_override=192.168.1.201"

# Add your own!
KUBELET_ARGS=""
</textarea><br/>5.3、【/etc/kubernetes/proxy】<br/><textarea name="code" class="c" rows="15" cols="100">
KUBE_PROXY_ARGS=""
</textarea><br/>启动kubernetes服务<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/># systemctl daemon-reload<br/># systemctl enable docker.service kubelet.service kube-proxy.service<br/># systemctl start docker.service kubelet.service kube-proxy.service<br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>3、校验安装(在master主机操作，或可访问master主机8080端口的client api主机)<br/>&nbsp;&nbsp;1) kubernetes常用命令<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/># kubectl get minions&nbsp;&nbsp;&nbsp;&nbsp;#查查看minion主机<br/># kubectl get pods&nbsp;&nbsp;&nbsp;&nbsp;#查看pods清单<br/># kubectl get services 或 kubectl get services -o json&nbsp;&nbsp;&nbsp;&nbsp;#查看service清单<br/># kubectl get replicationControllers&nbsp;&nbsp;&nbsp;&nbsp;#查看replicationControllers清单<br/># for i in `kubectl get pod&#124;tail -n +2&#124;awk '&#123;print $1&#125;'`; do kubectl delete pod $i; done&nbsp;&nbsp;&nbsp;&nbsp;#删除所有pods<br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;或者通过Server api for REST方式（推荐，及时性更高）：<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/># curl -s -L http://192.168.1.200:8080/api/v1beta1/version &#124; python -mjson.tool&nbsp;&nbsp;&nbsp;&nbsp;#查看kubernetes版本<br/># curl -s -L http://192.168.1.200:8080/api/v1beta1/pods &#124; python -mjson.tool&nbsp;&nbsp;&nbsp;&nbsp;#查看pods清单<br/># curl -s -L http://192.168.1.200:8080/api/v1beta1/replicationControllers &#124; python -mjson.tool&nbsp;&nbsp;&nbsp;&nbsp;#查看replicationControllers清单<br/># curl -s -L http://192.168.1.200:8080/api/v1beta1/minions &#124; python -m json.tool&nbsp;&nbsp;&nbsp;&nbsp;#查查看minion主机<br/># curl -s -L http://192.168.1.200:8080/api/v1beta1/services &#124; python -m json.tool&nbsp;&nbsp;&nbsp;&nbsp;#查看service清单<br/>&nbsp;&nbsp;&nbsp;&nbsp; </div></div><br/><span style="color: #DC143C;">注：在新版kubernetes中，所有的操作命令都整合至kubectl，包括kubecfg、kubectl.sh、kubecfg.sh等</span><br/><br/>&nbsp;&nbsp;2）创建测试pod单元<br/>&nbsp;&nbsp; # /home/kubermange/pods && cd /home/kubermange/pods<br/>&nbsp;&nbsp; # vi apache-pod.json<br/><textarea name="code" class="python" rows="15" cols="100">
&#123;
&nbsp;&nbsp;"id": "fedoraapache",
&nbsp;&nbsp;"kind": "Pod",
&nbsp;&nbsp;"apiVersion": "v1beta1",
&nbsp;&nbsp;"desiredState": &#123;
&nbsp;&nbsp;&nbsp;&nbsp;"manifest": &#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"version": "v1beta1",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"id": "fedoraapache",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"containers": [&#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"name": "fedoraapache",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"image": "fedora/apache",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"ports": [&#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"containerPort": 80,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"hostPort": 8080
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;]
&nbsp;&nbsp;&nbsp;&nbsp;&#125;
&nbsp;&nbsp;&#125;,
&nbsp;&nbsp;"labels": &#123;
&nbsp;&nbsp;&nbsp;&nbsp;"name": "fedoraapache"
&nbsp;&nbsp;&#125;
&#125;
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;# kubectl create -f apache-pod.json<br/>&nbsp;&nbsp;&nbsp;&nbsp;# kubectl get pod<br/><div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IMAGE(S)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HOST&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LABELS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;STATUS<br/>fedoraapache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.202/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/></div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;启动浏览器访问http://192.168.1.202:8080/，对应的服务端口切记在iptables中已添加。效果图如下：<br/><a href="https://www.liuts.com/attachment.php?fid=370" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=370" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>&nbsp;&nbsp;&nbsp;&nbsp;观察kubernetes在etcd中的数据存储结构<br/><a href="https://www.liuts.com/attachment.php?fid=373" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=373" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;观察单个pods的数据存储结构，以json的格式存储。<br/><a href="https://www.liuts.com/attachment.php?fid=374" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=374" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/><strong>二、实战操作</strong><br/>&nbsp;&nbsp;&nbsp;&nbsp;任务：通过Kubernetes创建一个LNMP架构的服务集群，以及观察其负载均衡，涉及镜像“yorko/webserver”已经push至registry.hub.docker.com，大家可以通过“docker pull yorko/webserver”下载。<br/>&nbsp;&nbsp;&nbsp;&nbsp;<div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>&nbsp;&nbsp;&nbsp;&nbsp;# mkdir -p /home/kubermange/replication && mkdir -p /home/kubermange/service<br/>&nbsp;&nbsp;&nbsp;&nbsp;# cd /home/kubermange/replication <br/>&nbsp;&nbsp;&nbsp;&nbsp;</div></div><br/>1、 创建一个replication ，本例直接在replication模板中创建pod并复制，也可独立创建pod再通过replication来复制。<br/>【replication/lnmp-replication.json】<br/><textarea name="code" class="python" rows="15" cols="100">
&#123;
&nbsp;&nbsp;"id": "webserverController",
&nbsp;&nbsp;"kind": "ReplicationController",
&nbsp;&nbsp;"apiVersion": "v1beta1",
&nbsp;&nbsp;"labels": &#123;"name": "webserver"&#125;,
&nbsp;&nbsp;"desiredState": &#123;
&nbsp;&nbsp;&nbsp;&nbsp;"replicas": 2,
&nbsp;&nbsp;&nbsp;&nbsp;"replicaSelector": &#123;"name": "webserver_pod"&#125;,
&nbsp;&nbsp;&nbsp;&nbsp;"podTemplate": &#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"desiredState": &#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "manifest": &#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "version": "v1beta1",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "id": "webserver",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "volumes": [
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;"name":"httpconf", "source":&#123;"hostDir":&#123;"path":"/etc/httpd/conf"&#125;&#125;&#125;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;"name":"httpconfd", "source":&#123;"hostDir":&#123;"path":"/etc/httpd/conf.d"&#125;&#125;&#125;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;"name":"httproot", "source":&#123;"hostDir":&#123;"path":"/data"&#125;&#125;&#125;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "containers": [&#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "name": "webserver",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "image": "yorko/webserver",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "command": ["/bin/sh", "-c", "/usr/bin/supervisord -c /etc/supervisord.conf"],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "volumeMounts": [
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;"name":"httpconf", "mountPath":"/etc/httpd/conf"&#125;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;"name":"httpconfd", "mountPath":"/etc/httpd/conf.d"&#125;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;"name":"httproot", "mountPath":"/data"&#125;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "cpu": 100,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "memory": 50000000,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "ports": [&#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "containerPort": 80,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;,&#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "containerPort": 22,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "labels": &#123;"name": "webserver_pod"&#125;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;,
&nbsp;&nbsp;&#125;
&#125;
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;执行创建命令<br/>&nbsp;&nbsp;&nbsp;&nbsp;#kubectl create -f lnmp-replication.json<br/>&nbsp;&nbsp;&nbsp;&nbsp;观察生成的pod副本清单：<br/>[root@SN2014-12-200 replication]# kubectl get pod<br/><div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMAGE(S)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HOST&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LABELS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS<br/>84150ab7-89f8-11e4-970d-000c292f1620&nbsp;&nbsp; yorko/webserver&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.202/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=webserver_pod&nbsp;&nbsp; Running<br/>84154ed5-89f8-11e4-970d-000c292f1620&nbsp;&nbsp; yorko/webserver&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.201/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=webserver_pod&nbsp;&nbsp; Running<br/>840beb1b-89f8-11e4-970d-000c292f1620&nbsp;&nbsp; yorko/webserver&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.202/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=webserver_pod&nbsp;&nbsp; Running<br/>84152d93-89f8-11e4-970d-000c292f1620&nbsp;&nbsp; yorko/webserver&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.202/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=webserver_pod&nbsp;&nbsp; Running<br/>840db120-89f8-11e4-970d-000c292f1620&nbsp;&nbsp; yorko/webserver&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.201/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=webserver_pod&nbsp;&nbsp; Running<br/>8413b4f3-89f8-11e4-970d-000c292f1620&nbsp;&nbsp; yorko/webserver&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.201/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=webserver_pod&nbsp;&nbsp; Running<br/></div></div><br/>2、创建一个service，通过selector指定 "name": "webserver_pod"与pods关联。<br/>【service/lnmp-service.json】<br/><textarea name="code" class="python" rows="15" cols="100">
&#123;
&nbsp;&nbsp;"id": "webserver",
&nbsp;&nbsp;"kind": "Service",
&nbsp;&nbsp;"apiVersion": "v1beta1",
&nbsp;&nbsp;"selector": &#123;
&nbsp;&nbsp;&nbsp;&nbsp;"name": "webserver_pod",
&nbsp;&nbsp;&#125;,
&nbsp;&nbsp;"protocol": "TCP",
&nbsp;&nbsp;"containerPort": 80,
&nbsp;&nbsp;"port": 8080
&#125;
</textarea><br/>&nbsp;&nbsp;&nbsp;&nbsp;执行创建命令：<br/>&nbsp;&nbsp;&nbsp;&nbsp;# kubectl create -f lnmp-service.json<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;登录minion主机（192.168.1.201），查询主宿机生成的iptables转发规则（最后一行）<br/>&nbsp;&nbsp;&nbsp;&nbsp;# iptables -nvL -t nat<br/><div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>Chain KUBE-PROXY (2 references)<br/> pkts bytes target&nbsp;&nbsp;&nbsp;&nbsp; prot opt in&nbsp;&nbsp;&nbsp;&nbsp; out&nbsp;&nbsp;&nbsp;&nbsp; source&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; destination&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp; 120 REDIRECT&nbsp;&nbsp; tcp&nbsp;&nbsp;--&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.0.0.0/0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10.254.102.162&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* kubernetes */ tcp dpt:443 redir ports 47700<br/>&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;60 REDIRECT&nbsp;&nbsp; tcp&nbsp;&nbsp;--&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.0.0.0/0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10.254.28.74&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* kubernetes-ro */ tcp dpt:80 redir ports 60099<br/>&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp; 0 REDIRECT&nbsp;&nbsp; tcp&nbsp;&nbsp;--&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.0.0.0/0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10.254.216.51&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* webserver */ tcp dpt:8080 redir ports 40689<br/></div></div><br/>&nbsp;&nbsp;&nbsp;&nbsp;访问测试，http://192.168.1.201:40689/info.php，刷新浏览器发现proxy后端的变化，默认为随机轮循算法。<br/><a href="https://www.liuts.com/attachment.php?fid=371" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=371" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><a href="https://www.liuts.com/attachment.php?fid=372" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=372" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/><strong>三、测试过程</strong><br/>&nbsp;&nbsp;&nbsp;&nbsp;1、pods自动复制、销毁测试，观察kubernetes自动保持副本数（6份）<br/>删除replicationcontrollers中一个副本fedoraapache<br/>[root@SN2014-12-200 pods]# kubectl delete pods fedoraapache<br/>I1219 23:59:39.305730&nbsp;&nbsp;&nbsp;&nbsp;9516 restclient.go:133] Waiting for completion of operation 142530<br/>fedoraapache<br/><div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>[root@SN2014-12-200 pods]# kubectl get pods<br/>NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMAGE(S)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HOST&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LABELS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;STATUS<br/>5d70892e-8794-11e4-970d-000c292f1620&nbsp;&nbsp; fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.201/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/>5d715e56-8794-11e4-970d-000c292f1620&nbsp;&nbsp; fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.202/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/>5d717f8d-8794-11e4-970d-000c292f1620&nbsp;&nbsp; fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.202/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/>5d71c584-8794-11e4-970d-000c292f1620&nbsp;&nbsp; fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.201/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/>5d71a494-8794-11e4-970d-000c292f1620&nbsp;&nbsp; fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.202/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/></div></div><br/>#自动生成出一个副本，保持6份的效果<br/><div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>[root@SN2014-12-200 pods]# kubectl get pods<br/>NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMAGE(S)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HOST&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LABELS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;STATUS<br/>5d717f8d-8794-11e4-970d-000c292f1620&nbsp;&nbsp; fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.202/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/>5d71c584-8794-11e4-970d-000c292f1620&nbsp;&nbsp; fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.201/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/>5d71a494-8794-11e4-970d-000c292f1620&nbsp;&nbsp; fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.202/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/>2a8fb993-8798-11e4-970d-000c292f1620&nbsp;&nbsp; fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.201/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/>5d70892e-8794-11e4-970d-000c292f1620&nbsp;&nbsp; fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.201/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/>5d715e56-8794-11e4-970d-000c292f1620&nbsp;&nbsp; fedora/apache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.202/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=fedoraapache&nbsp;&nbsp; Running<br/></div></div><br/>2、测试不同角色模块中的hostPort<br/>&nbsp;&nbsp;&nbsp;&nbsp;1）pod中hostPort为空，而replicationcontrollers为指定端口，则异常；两侧都指定端口，相同或不同时都异常；pod的hostport为指定，另replicationcon为空，则正常；pod的hostport为空，另replicationcon为空，则正常；结论是在replicationcontrollers场景不能指定hostport，否则异常，待持续测试。<br/>&nbsp;&nbsp;&nbsp;&nbsp;2）结论：在replicationcontronllers.json中，"replicaSelector": &#123;"name": "webserver_pod"&#125;要与"labels": &#123;"name": "webserver_pod"&#125;以及service中的"selector": &#123;"name": "webserver_pod"｝保持一致；<br/><br/>请关注下篇《kubernetes与HECD架构的整合》，近期推出。<br/><br/>参考文献：<br/>https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/getting-started-guides/fedora/fedora_manual_config.md<br/>https://github.com/GoogleCloudPlatform/kubernetes/blob/master/DESIGN.md<br/>http://www.infoq.com/cn/articles/Kubernetes-system-architecture-introduction<br/><br/>转载请注明来源 http://blog.liuts.com/post/247/<br/>Tags - <a href="https://www.liuts.com/tags/docker/" rel="tag">docker</a> , <a href="https://www.liuts.com/tags/kubernetes/" rel="tag">kubernetes</a> , <a href="https://www.liuts.com/tags/%25E5%25AE%25B9%25E5%2599%25A8%25E7%25AE%25A1%25E7%2590%2586/" rel="tag">容器管理</a> , <a href="https://www.liuts.com/tags/%25E5%2588%2598%25E5%25A4%25A9%25E6%2596%25AF/" rel="tag">刘天斯</a>
]]>
</description>
</item><item>
<link>https://www.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://www.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://www.liuts.com/attachment.php?fid=356" target="_blank"><img src="https://www.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://www.liuts.com/attachment.php?fid=357" target="_blank"><img src="https://www.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://www.liuts.com/attachment.php?fid=360" target="_blank"><img src="https://www.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://www.liuts.com/attachment.php?fid=358" target="_blank"><img src="https://www.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://www.liuts.com/attachment.php?fid=359" target="_blank"><img src="https://www.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://www.liuts.com/tags/docker/" rel="tag">docker</a> , <a href="https://www.liuts.com/tags/confd/" rel="tag">confd</a> , <a href="https://www.liuts.com/tags/etcd/" rel="tag">etcd</a> , <a href="https://www.liuts.com/tags/haproxy/" rel="tag">haproxy</a> , <a href="https://www.liuts.com/tags/hecd%25E6%259E%25B6%25E6%259E%2584/" rel="tag">hecd架构</a>
]]>
</description>
</item><item>
<link>https://www.liuts.com/post/241/</link>
<title><![CDATA[Docker远程python API操作容器一例[原创]]]></title> 
<author>刘天斯 &lt;liutiansi@gmail.com&gt;</author>
<category><![CDATA[Docker]]></category>
<pubDate>Mon, 08 Sep 2014 15:48:23 +0000</pubDate> 
<guid>https://www.liuts.com/post/241/</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://github.com/docker/docker-py" target="_blank">Docker-py</a>作为官方推出的客户端API，功能可以满足我们大部分操作需求，API涉及镜像(images)及容器(CONTAINER)的功能操作，利用docker-py可以轻松开发出Docker的管理平台，以便维护大规模的Docker集群，本文介绍如何通过DockerFile创建一个WEB服务的镜像，再通过远程API对容器进行管理。<br/><br/><strong>一、环境准备</strong><br/>1、环境说明<br/>192.168.1.20 #Docker python API主机<br/>192.168.1.22 #Docker服务主机<br/>2、Docker环境部署(略)<br/>3、修改自启动服务文件，支持远程TCP接口与本地SOCK连接；<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>#service docker restart<br/><br/><strong>二、创建镜像</strong><br/>1、获取最新的centos镜像<br/># docker pull centos:latest<br/>2、编写Dockerfile（支持apache+ssh服务）<br/># mkdir /home/Dockerfile/webserver<br/># cd /home/Dockerfile/webserver<br/># vi Dockerfile<br/><textarea name="code" class="python" rows="15" cols="100">
# This is a base comment
FROM centos:latest
MAINTAINER yorko Liu <liutiansi@gmail.com>

#yum install Package
RUN yum -y install net-tools
RUN yum -y install iputils&nbsp;&nbsp;iproute&nbsp;&nbsp;man&nbsp;&nbsp;vim-minimal&nbsp;&nbsp;openssh-server&nbsp;&nbsp;openssh-clients
RUN yum -y install httpd
RUN yum -y install python-setuptools
RUN easy_install supervisor

#set sshd
RUN ssh-keygen -q -N "" -t dsa -f /etc/ssh/ssh_host_dsa_key
RUN ssh-keygen -q -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key
RUN ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -N ""
RUN sed -ri 's/session&nbsp;&nbsp;&nbsp;&nbsp;required&nbsp;&nbsp;&nbsp;&nbsp; pam_loginuid.so/#session&nbsp;&nbsp;&nbsp;&nbsp;required&nbsp;&nbsp;&nbsp;&nbsp; pam_loginuid.so/g' /etc/pam.d/sshd
RUN mkdir -p /root/.ssh && chown root.root /root && chmod 700 /root/.ssh
RUN echo 'root:Ksjhg34TDju' &#124; chpasswd

#set supervisor
RUN mkdir -p /var/log/supervisor
ADD supervisord.conf /etc/supervisord.conf

#set port
EXPOSE 22
EXPOSE 80

#set ENV
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8

#run supervisor
CMD ["/usr/bin/supervisord -c /etc/supervisord.conf"]
</textarea><br/>通过supervisord来维护Docker容器中服务进程，编写supervisord.conf<br/># vi supervisord.conf<br/><textarea name="code" class="c" rows="15" cols="100">
[supervisord]
nodaemon=true

[program:sshd]
command=/usr/sbin/sshd -D

[program:httpd]
command=/usr/sbin/httpd -DFOREGROUND
</textarea><br/>创建镜像，运行：<br/># docker build -t yorko/webserver:v1 .<br/><span style="color: #DC143C;">注：最后有一个“.”，别遗漏。</span><br/><br/>镜像生成完毕后运行docker images查看，见下图：<br/><a href="https://www.liuts.com/attachment.php?fid=348" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=348" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/><strong>三、编写操作API</strong><br/>登录192.168.1.20服务器<br/># mkdir /home/test/docker-py<br/># cd /home/test/docker-py<br/>1、安装docker-py<br/># wget https://github.com/docker/docker-py/archive/master.zip<br/># unzip master<br/># cd docker-py-master/<br/># python setup.py install<br/>如正常导入模块（import docker）说明安装成功。<br/><br/>2、创建容器docker_create.py<br/><textarea name="code" class="python" rows="15" cols="100">
import docker

c = docker.Client(base_url='tcp://192.168.1.22:2375',version='1.14',timeout=10)
c.create_container(image="yorko/webserver:v1",stdin_open=True,tty=True,command="/usr/bin/supervisord -c /etc/supervisord.conf",volumes=['/data'],ports=[80,22],name="webserver11")
#通过create_container方法创建容器，指定"yorko/webserver:v1"镜像名称，使用supervisord接管进程服务，挂载主宿机/data作为数据卷，容器监听80与22端口，容器的名称为webserver11
print str(r)
</textarea><br/>3、运行容器docker_start.py<br/><textarea name="code" class="python" rows="15" cols="100">
import docker

c = docker.Client(base_url='tcp://192.168.1.22:2375',version='1.14',timeout=10)
r=c.start(container='webserver11', binds=&#123;'/data':&#123;'bind': '/data','ro': False&#125;&#125;, port_bindings=&#123;80:80,22:2022&#125;, lxc_conf=None,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;publish_all_ports=True, links=None, privileged=False,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dns=None, dns_search=None, volumes_from=None, network_mode=None,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;restart_policy=None, cap_add=None, cap_drop=None)
#通过start方法启动容器，指定数据卷的挂载关系及权限，以及端口与主宿机的映射关系等
print str(r)
</textarea><br/>4、运行<br/># python docker_create.py<br/># python docker_start.py<br/>更多API参考<a href="https://github.com/docker/docker-py" target="_blank">https://github.com/docker/docker-py</a><br/><br/>5、在Docker主机观察结果，见下图：<br/><a href="https://www.liuts.com/attachment.php?fid=349" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=349" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/><strong>三、校验服务</strong><br/>1、校验SSH服务<br/><a href="https://www.liuts.com/attachment.php?fid=351" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=351" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/>2、校验WEB服务<br/><a href="https://www.liuts.com/attachment.php?fid=350" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=350" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/>3、检查数据卷<br/><a href="https://www.liuts.com/attachment.php?fid=352" target="_blank"><img src="https://www.liuts.com/attachment.php?fid=352" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>Tags - <a href="https://www.liuts.com/tags/docker/" rel="tag">docker</a> , <a href="https://www.liuts.com/tags/docker-py/" rel="tag">docker-py</a> , <a href="https://www.liuts.com/tags/dockerfile/" rel="tag">dockerfile</a> , <a href="https://www.liuts.com/tags/supervisord/" rel="tag">supervisord</a>
]]>
</description>
</item>
</channel>
</rss>