不苟且的运维之路

导言
最近比较关注大数据、云计算、Docker、DevOps等几个方向,一会也简单围绕这几点跟大家做个交流。
聊运维人生这个主题有点大,^_^就先从个人怎么入运维这行说起吧。

人在天涯
2003年毕业后的第一份工作是当php、java程序员,人力紧张时还要兼顾美工设计的工作。
工作中一次偶然的机会看到导师在黑压压的界面中敲入不同指令,第一感觉非常震撼,很COOL,联想到《黑客帝国》电影中的画面,与之前接触到的Windows系统完全不一样,后来才晓得是Redhat9(红帽9)。此时还是一名普通码农。
2005年的10月,进入第二个东家-天涯社区,人生的第一个转折点在此酝酿。
由于赶上了公司快速发展的阶段,接触到了很多开源技术,包括LVS、Squid、Haproxy、MongoDB、Mysql、Cfengine等等,也不断应用在生产环境,取得了非常不错的效果,重点业务的高可用持续保持在99.99%。

如何高效运营?
随之新的问题也陆续出现,包括如何更好整合各类开源组件,发挥其最大效能,另一个是如何高效运营。不可否认,具有开发背景的运维人员有着先天性的优势,可以在不同角色之间进行思考,视野被放大。
事实上在天涯6年就做一件事,即主导并实施天涯社区从Windows技术路线往开源架构改造,驱动这个事情有两原因,一个是运营管理成本、另一个是正版化给企业带来的高额成本(视窗+Sqlserver License)。
改造最大难点是没有可参考及借签的对象,文档资料不全。完全依靠原有人员的技术储备,不断摸索。经过“试错 -> 失败 -> 回滚 -> 再尝试”的过程,个人能力在此期间也得到了一次升华。
对Linux环境下的C++做了深入的研究学习,具备这样的知识对开源软件的架构、分层理解及故障定位有很大的帮助。改造后的天涯新架构在当时比较具有代表性且通用。

架构图如下:
点击在新窗口中浏览此图片

2010年天涯IT管理架构
点击在新窗口中浏览此图片

天涯期间的开源项目
点击在新窗口中浏览此图片
取之开源,同样也回馈开源,以下为个人在天涯期间的开源项目,其中开源后的“LVS管理平台”第一时间被国内某证卷公司采用,当时感觉很有成就感。
比较有意思的“服务器机柜模拟图平台”项目,不少公司在此基础上陆续出升级版本,平台截图如下:
点击在新窗口中浏览此图片
可以通过下面网址,访问在线版本
http://blog.liuts.com/idc/

后面也将内部的运维管理平台原型做了开源,同时也在《Python自动化运维实践》书籍中做了介绍,下面为OMServer平台截图
点击在新窗口中浏览此图片
源码托管在:
http://github.com/yorkoliu/pyauto

小经验:
05年开始写博客,当时目的比较单纯,即当笔记本来用,后来慢慢演变成交流沟通的平台,收到很多同行的评论、邮件,也认识了很多业界大牛,很多好友到现在还保持联系。

坚持写博客是一件非常困难的事。但在荣获了2010年度《十大杰出IT博客》的同时,也让我领悟到每写一篇博客其实就是个人对工作、生活的一次总结。不但可以锻炼你的语言组织、逻辑思维、表达等能力,还可以给你增加人气,提升个人影响力 ^_^。

腾讯CDN运维之旅
2011年加盟腾讯,主要负责了腾讯静态、下载类的CDN运维,截止当前已有400+加速节点(包括静态内容平台、游戏下载平台、UGC加速平台、流媒体平台、动态加速平台等),总带宽突破10Tb,覆盖了腾讯QQ、微信、QQ空间、腾讯视频等业务。
流量调度主要通过GSLB来实现,部分业务基于httpDNS来实现。
腾讯自建CDN除了强大的资源部署外,软实力方面做得非常不错,比如:协议栈优化TCP传输、DiskTank解决小文件读写瓶颈与碎片、链路实时调整等,其中链路实时调整依赖全网拓扑的实时测速的数据作为依据,调整事件是通过调用公司的GSLB接口进行刷新。

调度示意图如下:
点击在新窗口中浏览此图片

What happened?

运维期间碰到最头疼的问题还是小运营商的域名、内容劫持,表现形式为TTL、目标指向、内容劫持等几个方面。

Why?:运营这样做的初衷是什么?

减少运营商LDNS设备的性能负载;
减少运营商跨网结算成本;
嵌入捆绑广告营销策略等。
How?:解决的一些思路,发现问题:

主动:遍历域名在所有运营商LDNS的解析结果,与公司的威权GSLB记录做比较,存在异常的解析可以立马被定位;
被动:产品投拆、网友论坛反馈等渠道。目前还没有彻底解决问题的方案,原因是站在运营商的角度,适当的劫持是合理的,不过可以通过商务去推动解决(局部),关于内容劫持比较好的解决方案就是上HTTPS。

腾讯数据运营大舞台

2013 年至今负责腾讯游戏大数据运营体系的建设,支撑百余款游戏的数据接入、传输、ETL分析、大数据基础平台运维等工作,确保日7000亿条(50T)日志流水传输,以及日均10万次计算任务调度质量。
在IT基础服务方面,利用Docker技术及DevOps理念,对游戏数据应用实施持续交付流程、服务弹性调度的落地,以及开发团队与运维团队融合制度的定制。

数据运营简介

当前腾讯游戏数据传输、存储规模:
点击在新窗口中浏览此图片

游戏大数据服务架构图:
点击在新窗口中浏览此图片

其中存储与计算都采用公司级的数据服务平台-TDW(基于Hadoop+Hive构建),传输采用自研的TDBank平台,类似于开源Flume+Kafka的技术方案。
数据采集采用自研的Tglog方案,更轻量、传输效率更高,支持UDP及TCP版本,两个特点:
耦和度低、标准开放接口、接入成本低;
统一化数据运营标准协议XML、数据本地化容灾。
Tglog简介:

也简单介绍下Tglog日志格式的定义吧,分两部分,一为日志描述,二为实体日志。

下图为日志格式描述文件(XML格式):
点击在新窗口中浏览此图片

下图为采集原始日志格式(表名|字段值1|字段值2|… …):
点击在新窗口中浏览此图片

支撑数据应用-iData(腾讯智能化游戏数据分析平台),提供了游戏生命周期管理,且每个环节都结合了数据挖掘算法,做到精准玩家触达,精细化运营的目的。
点击在新窗口中浏览此图片

运维支撑难点:

如何保障海量游戏日志的采集、传输质量达到99.999% 的SLA标准?
需要从几个维度入手,包括元数据管理、数据地图、数据字典、血缘关系、数据对账、安全审计等。同时要确保与各类数据变更的强联动,感兴趣今后可以展开来讨论哈。
二级数据分析集群我们采用Flume+Spark,调度使用Mesos来实现,同时也结合kubernetes来实现长服务组件的调度,有兴趣今后可展开讨论。

我的DevOps观
Why DevOps?
DevOps是开发者和运维之间的高度协同与融合,这个过程贯穿整个软件开发生命周期,从业务规划到创建、交付再到反馈。
需要注意一点是,不仅仅包括是开发和运维团队,真正的 DevOps 方法需要业务部门、测试人员、企业高管、合作伙伴和供应商等配合完成。
主流观点
为什么要DevOps,国内认同度比较高的几个观点,更多可搜索老王分享过的一些文章。

改善团队协作;
帮助控管风险、成本、减少浪费;
提升软件品质;
提高软件迭代速度。
个人观点
个人更趋向于IBM的诠释,即增强客户体验、提高创新能力、更快实现价值。

建立一种机制,从所交付软件应用的所有利益相关方快速获取反馈,利益相关方包括客户、业务部门、用户、供应商、合作伙伴等等;
目标是减少浪费和返工,并将资源转移给价值更高的活动;
将软件快速、高效和可靠地交付于生产的文化、实践和自动化,快速达成目标,实现价值。
How?
怎么去做DevOps?
调整考核和激励机制;
绑定开发、测试、运维,共同输出价值;
全面自动化,减少人工干预;
开展培训和组织开发活动;
制定新体系结构标准。
持续集成、交付、部署是一个非常好的切入点,DevOps全景图:
点击在新窗口中浏览此图片
So What is it?
Devops就是:

CALMS - Culture(文化)
Automation(自动化)
Lean (Lean[精益]理论,其思想源于消除浪费)
Measurement(量化)包括监控、指标、分析
Sharing(分享)
补充概念
很多人会混淆持续交付(Continuous Delivery)、持续部署(Continuous Deployment)两个概念。
简单说明下,持续交付并不是指软件每一个改动都要尽快的部署到产品环境中,它指的是任何的修改都已证明可以在任何时候实施部署,而持续部署是将所有通过了自动化测试的改动都自动的部署到产品环境里。

DEVOPS最终目标及原则方向:

运维要成为一等公民;
让开发人员完成一切;
谁构建,谁运行。
经验点:

前期的沟通铺垫很重要,一定要了解公司内部利益相关方,包括开发负责人、运维主管、项目主管、产品负责人等的诉求及关注点,尽可能共同捆绑目标及输出价值,不同角色区别只在于分工。
其次是让大老板了解DevOps的收益,且要得到老板的支持,因为DevOps落地是一项长期工作,不可能在短期内看到收益。
量化的指标一定要清晰,且直观易懂,包括业务监控平台、分析报告,同时需要提供至少一种产品或用户反馈的途径,可以是产品官网、在线客服,这对提升平台的品质,直接影响产品口碑。这里直接使用了公司现有的非常完善的基础组件及渠道。
持续集成、交付、部署,我们使用了SVN+Jenkins+Docker+应用商店(镜像)。
第一期自动化部署我们采用了HECD架构实现,第二期计划是与蓝鲸平台打通,通过APP形式来实现从集成至部署的封环,很大程度提高了软件迭代的速度及频率,对提升软件品质及运维服务水平至关重要。
此项工作一定要做好,这也是让相关利益方切身感受变化的关键一环。

小知识
什么是HECD架构:构建一个高可用及自动发现的Docker基础架构-HECD
http://blog.liuts.com/post/242/

从业经验

最后再简单分享个人在运维领域从业的两个小经验:

1、一步一个脚印,切忌一步到位。
关于运维自动化这件事情,几乎所有的IT企业都在做,看似是一件非常好的事情,忽略了前提条件,往往付出更大的代价及运营成本。
之前所提到的前提条件便是运维体系“标准化”、“流程化”、“规范化”的建设,覆盖企业中资源、版本、业务发布、监控、事件管理等环节。有了这些作为基础铺垫,运维自动化的建设才会很顺利实施,达成预期。
自动化程度的高低很大程度是随公司所处不同发展阶段,不断去演变而来,不要想着去复制BAT的架构,一步到位是一个很大的误区。

2、运筹帷幄,主动耦合。
对业务的生命周期进行管理,是运维扮演的角色。
一个产品在规划之初运维人员须第一时间介入参与,根据产品特点,提供业务平台前期架构设计、资源评估等数据。
当产品进入开发阶段,须与开发人员保持密切沟通与互动,提供业务接入、缓存、存储、监控、安全等方面规范,以便在编码阶段更好磨合与对接,避免上线后反复做不必要的版本迭代,也使得开发出来的产品具备更高的可运维性。
待业务上线后,务必定期同步相关运营数据给产品与开发人员侧,为后续优化、改进的工作提供数据支持,这也恰恰能体现运维人员的专业性及团队合作意识。

一个感悟

运维体系中各个环节的工作犹如散落在地上的珠子,每个珠子分别代表事件、资源、监控、安全、自动化、日常工作等。
看似是七零八落的,我们需要利用“流程”这条线将所有的珠子串起来。珠子的前后顺序及间隔由“标准规范”来控制。
这样就形成了一条完整的链子,是一个有机的整体,最后会促使运维工作开展得井井有条。这条链子扣在三个点子上,就是“质量”、“效率”、“成本”。

如何保持竞争力?

很喜欢乔希·维茨金在《学习之道》书中一句名言:追求卓越的关键在于,要坚持充满活力,长期的学习过程,不再满足于原地踏步、平平庸庸。
作为一名优秀的运维工程师,应该将学习成果与日常工作相结合,独立及深入思考发现的问题,善于发现不同问题之间的联系,并把它们升华为方法论,最后再做总结与传承。
我的感悟:不断学习才是保持竞争力的唯一途径,不断思考和总结会成为你潜意识的行为;
我的行动:坚持每天1~2小时的学习时间,从未停止。

那么问题来了:
如学习的动力不源于兴趣或好奇心怎么办?
这里跟大家分享我与团队小伙伴探讨的一个观点:
首先应该静下心好好思考,自己5、10年后想过什么样的生活,可以预见的状态是上有老下有小、背着沉重的房贷、车贷。
如此时再去与一个毕业生竞争同一工作岗位,必然会丧失了所有的优势与竞争力,被行业所淘汰只是时间问题。
因此,想过衣食无忧生活,在职业发展过程中处于不败之地,那么,从现在开始就应该怀着空怀心态,不断打怪升级,使自己变得更加强大。
引用萧帮主一分享主题:“危机前的自我拯救”,没有这个勇气或行动是否考虑该转行了?

如何挖掘专利?

再聊聊运维人员如何写专利,在很多人眼里,写专利是一件非常复杂且很难做到的事情,甚至会认为这是开发人员更擅长的领域。
其实写专利并不像大家想象的那么遥远,只要是一个可以实现的方法或思路就能写成专利。比如运维平台当中的一个功能点、一个快速安全扫描的方法、一项容灾传输的技术、自动化测试案例思路、一种有效服务监控的手段等。这些点子都可以写成一个发明专利。
当然,创新及新颖性非常重要,可以先对比现有的技术实现,突出该发明的优势及亮点,就可以开始写交底书了。
如真的没有一点头绪,大家也可以参考个人写过的一些专利,访问中国专利局网,检索发明人:“刘天斯”
http://www.pss-system.gov.cn/sipopublicsearch/search/searchHome-searchIndex.shtml

一则小广告^_^:
点击在新窗口中浏览此图片
在个人著作《Python自动化运维:技术与最佳实践》中也分享一些运维自动化的实现方法与实践案例,供大家参考。
另一著作《Docker深入详解》预计8月上市,敬请关注!谢谢大家的聆听。

Q&A

Q1:自行研发tglog对于海量日志传输是否主要走的udp协议?如果是走的udp协议,怎么去解决一些数据包传输中数据乱序以及数据反序列化问题,或者做了哪些协议层面的优化?
A: 是的,主要走的是UDP协议。tglog同时也是一套数据日志的规范,约束开发人员打日志的标准。
目前未碰到数据乱序以及数据反序列化问题,以前面临一个比较大的问题是丢包情况,尤其在流量高峰期时段更为明显。
后面在内核、IO优化得到缓解,但无法规避,所以我们对比较重要的日志采用TCP传输。比如玩家消费流水。

Q2:规范化、标准化遇到最大问题是什么?我们遇到就是无法行政干涉开发如何写代码?有什么好的方式去引导规范?尤其是开发有很繁重的开发任务.
A: 这已经不是运维层面推动的事情,必须升级到运维及开发的上层领导,开发任务繁重不是理由,上线后出问题一样得不偿失,提前抛出风险,让开发人员认真做好上线前的评估。

Q3:Docker化应用,是否面临胖容器还是瘦容器问题,即每个容器单进程还是多进程,你们这块是如何使用的?
A: 这块没有统一的标准,看实际业务场景来决定,我们遵循一个原则是:最大化解耦。
另外一个需要考虑的点:是否为原子调度单元。
        在运营系统中经常用到异步方式来处理我们的任务,比如将业务上线流程串成任务再写入队列,通过后台作业节点去调度执行。比较典型的案例为腾讯的蓝鲸、织云、云智慧等平台。本译文结合Django+Celery+Redis实现一个定期从Flickr 获取图片并展示的简单案例,方便大家理解实现异步对列任务的过程。
        刚接触django的时候,我经历过的最让人沮丧的事情是需要定期运行一段代码。我写了一个需要每天上午12点执行一个动作的不错的函数。很简单是不是?错了。事实证明,这对我来说是一个巨大的困难点,因为,那时我使用Cpane类型的虚拟主机管理系统,它能专门提供一个很友好,很方便的图形用户界面来设置cron作业。
       经过反复研究,我发现了一个很好的解决方案 - Celery,一个用于在后台运行任务的强大的异步作业队列。但是,这也导致了其它的问题,因为我无法找到一系列简单的指令将celery集成到Django项目中。
       当然,我最终还是设法成功搞定了它 - 这正是本文将介绍的内容:如何将celery集成到一个Django项目,创建周期性任务。
       该项目利用Python3.4,Django的1.8.2,celery3.1.18和Redis3.0.2.

一、概述
       由于大篇幅的文字,为了您的方便,请参阅下表中的每一步的简要信息,并获取相关的代码。
步骤      概要           Git标签
样板      样板下载           V1
建立      集成Celery和Django       V2
Celery任务    添加基本的Celery任务     V3
周期性任务    添加周期性任务         V4
本地运行    本地运行我们的应用程序     V5
远程运行    远程运行我们的应用程序     V5

二、什么是Celery
       “Celery是一个异步任务队列/基于分布式消息传递的作业队列。它侧重于实时操作,但对调度的支持也很好。”本文,我们将重点讲解周期性执行任务的调度特点。
       为什么这一点有用呢?
      •回想一下你不得不在将来运行某一特定任务的经历。也许你需要每隔一小时访问一个API。或者,也许你需要在这一天结束时发送一批电子邮件。不论任务大小,Celery都可以使得调度周期性任务变的很容易。
      •你永远不希望终端用户等待那些不必要的页面加载或动作执行完成。如果你的应用程序工作流的一部分是一个需要很长时间的程序,当资源可用时,你就可以使用Celery在后台执行这段程序,从而使你的应用程序可以继续响应客户端的请求。这样可以使任务在应用程序的环境之外运行。

三、构建项目
       在深入了解Celery之前,先从Github库中获取开始项目。确保激活一个虚拟的环境,安装必要的软件,并运行迁移。然后启动服务器,通过你的浏览器导航到http://localhost:8000/。你应当能看到‘恭喜你的第一个Django页面’。完成后,关闭服务器。
       接下来,我们开始安装celery。
  1. $ pip install celery==3.1.18  
  2. $ pip freeze > requirements.txt  

       现在,我们通过简单的三步将celery集成到django项目中。
步骤一:创建celery.py
在“picha“目录下,创建celery.py,代码如下:
  1. from __future__ import absolute_import  
  2. import os  
  3. from celery import Celery  
  4. from django.conf import settings  
  5.   
  6. # set the default Django settings module for the 'celery' program.  
  7. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'picha.settings')  
  8. app = Celery('picha')  
  9.   
  10. # Using a string here means the worker will not have to  
  11. # pickle the object when using Windows.  
  12. app.config_from_object('django.conf:settings')  
  13. app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)  
  14.   
  15.   
  16. @app.task(bind=True)  
  17. def debug_task(self):  
  18.     print('Request: {0!r}'.format(self.request))  

       请注意代码中的注释。
步骤二:引入celery应用
为了确保在django启动时加载了celery应用,在settings.py旁边新建__init__.py,并添加以下代码到__init__.py中。
  1. from __future__ import absolute_import  
  2.   
  3. # This will make sure the app is always imported when  
  4. # Django starts so that shared_task will use this app.  
  5. from .celery import app as celery_app  

完成以上步骤后,你的项目目录应该是这样的:
  1. ├── manage.py  
  2. ├── picha  
  3. │   ├── __init__.py  
  4. │   ├── celery.py  
  5. │   ├── settings.py  
  6. │   ├── urls.py  
  7. │   └── wsgi.py  
  8. └── requirements.txt  

步骤三:安装 Redis作为Celery的“中间件”
    Celery使用中间件在django项目与celery监控者之间传递消息。在本教程中,我们使用redis作为消息中间代理。
首先,从官方下载页面或通过brew(BREW安装Redis)安装Redis,然后打开你的终端上,在一个新的终端窗口,启动服务器:
  1. $ redis-server  

你可以通过在终端中输入如下命令测试Redis是否正常工作。
  1. $ redis-cli ping  

       Redis应该回复PONG - 试试吧!
       一旦Redis正常启动了,把下面的代码添加到你的settings.py文件中:
  1. # CELERY STUFF  
  2. BROKER_URL = 'redis://localhost:6379'  
  3. CELERY_RESULT_BACKEND = 'redis://localhost:6379'  
  4. CELERY_ACCEPT_CONTENT = ['application/json']  
  5. CELERY_TASK_SERIALIZER = 'json'  
  6. CELERY_RESULT_SERIALIZER = 'json'  
  7. CELERY_TIMEZONE = 'Africa/Nairobi'  

       你还需要添加Redis的作为Django项目的依赖:
  1. $ pip install redis==2.10.3  
  2. $ pip freeze > requirements.txt  

       就是这样了!你现在应该能够在Django中使用Celery。有关设置Celery与Django的更多信息,请查看官方Celery文档。
在继续下面步骤之前,让我们进行一些完整性检查,以确保一切都是正常的。
测试Celery worker已准备好接收任务:
  1. $ celery -A picha worker -l info  
  2. ...  
  3. [2015-07-07 14:07:07,398: INFO/MainProcess] Connected to redis://localhost:6379//  
  4. [2015-07-07 14:07:07,410: INFO/MainProcess] mingle: searching for neighbors  
  5. [2015-07-07 14:07:08,419: INFO/MainProcess] mingle: all alone  

       使用CTRL-C杀死该段程序。现在,测试Celery任务调度程序是否已经准备好:
  1. $ celery -A picha beat -l info  
  2. ...  
  3. [2015-07-07 14:08:23,054: INFO/MainProcess] beat: Starting...  

       在上述完成时再次终止该进程。

1、Celery任务
       Celery利用celery调用的常规Python函数作为任务。
       例如,让我们把这个基本函数变为celery的任务:
  1. def add(x, y):  
  2.     return x + y  

       首先,添加一个装饰器。
  1. from celery.decorators import task  
  2.   
  3. @task(name="sum_two_numbers")  
  4. def add(x, y):  
  5.     return x + y  

       然后你可以通过以下方式利用celery异步运行该任务:
  1. add.delay(7, 8)  

       很简单,对不对?
所以,这对于解决类似你要加载一个网页,而不需要用户等待一些后台程序的完成这些类型的任务来说是非常完美的。
      让我们来看一个例子...
让我们再回到Django项目的版本3,它包括一个接受来自用户的反馈的应用程序,人们形象地称之为反馈:
  1. ├── feedback  
  2. │   ├── __init__.py  
  3. │   ├── admin.py  
  4. │   ├── emails.py  
  5. │   ├── forms.py  
  6. │   ├── models.py  
  7. │   ├── tests.py  
  8. │   └── views.py  
  9. ├── manage.py  
  10. ├── picha  
  11. │   ├── __init__.py  
  12. │   ├── celery.py  
  13. │   ├── settings.py  
  14. │   ├── urls.py  
  15. │   └── wsgi.py  
  16. ├── requirements.txt  
  17. └── templates  
  18.     ├── base.html  
  19.     └── feedback  
  20.         ├── contact.html  
  21.         └── email  
  22.             ├── feedback_email_body.txt  
  23.             └── feedback_email_subject.txt  

       安装新的必要软件,启动应用程序,并导航到http://localhost:8000/feedback/。你应该看到如下结果:
点击在新窗口中浏览此图片
让我们连接celery任务。

2、添加任务
       基本上,用户提交反馈表后,我们希望让他继续以他舒服的方式往下进行,而我们在后台进行处理反馈,发送电子邮件等等。
要做到这一点,首先添加一个叫tasks.py的文件到“feedback”目录:
  1. from celery.decorators import task  
  2. from celery.utils.log import get_task_logger  
  3.   
  4. from feedback.emails import send_feedback_email  
  5.   
  6. logger = get_task_logger(__name__)  
  7.   
  8.   
  9. @task(name="send_feedback_email_task")  
  10. def send_feedback_email_task(email, message):  
  11.     """sends an email when feedback form is filled successfully"""  
  12.     logger.info("Sent feedback email")  
  13.     return send_feedback_email(email, message)  

       然后按照如下内容更新forms.py:
  1. from django import forms  
  2. from feedback.tasks import send_feedback_email_task  
  3.   
  4.   
  5. class FeedbackForm(forms.Form):  
  6.     email = forms.EmailField(label="Email Address")  
  7.     message = forms.CharField(  
  8.         label="Message", widget=forms.Textarea(attrs={'rows': 5}))  
  9.     honeypot = forms.CharField(widget=forms.HiddenInput(), required=False)  
  10.   
  11.     def send_email(self):  
  12.         # try to trick spammers by checking whether the honeypot field is  
  13.         # filled in; not super complicated/effective but it works  
  14.         if self.cleaned_data['honeypot']:  
  15.             return False  
  16.         send_feedback_email_task.delay(  
  17.             self.cleaned_data['email'], self.cleaned_data['message'])  

       大体上,send_feedback_email_task.delay(email, message)的函数过程,并发送反馈电子邮件等都是在用户继续使用该网站的同时作为后台进程运行。
注:在views.py中的success_url被设置为将用户重定向到/ 目录,这个目录还不存在。我们会在下一节设置这个终点启动。

3、周期任务
       通常情况下,你经常需要安排一个任务在特定的时间运行 - 例如,一个web scraper 可能需要每天都运行。这样的任务,被称为周期性任务,很容易建立利用celery启动。
       celery使用“celery beat”来安排定期任务。celery beat定期运行任务,然后由celery worker执行任务。
例如,下面的任务计划每15分钟运行一次:
  1. from celery.task.schedules import crontab  
  2. from celery.decorators import periodic_task  
  3.   
  4.   
  5. @periodic_task(run_every=(crontab(minute='*/15')), name="some_task", ignore_result=True)  
  6. def some_task():  
  7.     # do something  

       让我们通过往Django项目中添加功能来看一个更强大的例子。
回到Django项目版本4,它包括另一个新的应用程序,叫做photos,这个应用程序使用 Flickr API获取新照片用来显示在网站:
  1. ├── feedback  
  2. │   ├── __init__.py  
  3. │   ├── admin.py  
  4. │   ├── emails.py  
  5. │   ├── forms.py  
  6. │   ├── models.py  
  7. │   ├── tasks.py  
  8. │   ├── tests.py  
  9. │   └── views.py  
  10. ├── manage.py  
  11. ├── photos  
  12. │   ├── __init__.py  
  13. │   ├── admin.py  
  14. │   ├── models.py  
  15. │   ├── settings.py  
  16. │   ├── tests.py  
  17. │   ├── utils.py  
  18. │   └── views.py  
  19. ├── picha  
  20. │   ├── __init__.py  
  21. │   ├── celery.py  
  22. │   ├── settings.py  
  23. │   ├── urls.py  
  24. │   └── wsgi.py  
  25. ├── requirements.txt  
  26. └── templates  
  27.     ├── base.html  
  28.     ├── feedback  
  29.     │   ├── contact.html  
  30.     │   └── email  
  31.     │       ├── feedback_email_body.txt  
  32.     │       └── feedback_email_subject.txt  
  33.     └── photos  
  34.         └── photo_list.html  

       安装新的必要软件,运行迁移,然后启动服务器,以确保一切都是好的。重新测试反馈表。这次,它应该重定向好了。
       下一步是什么?
       既然我们需要周期性的调用Flickr API,以获取更多的照片添加到我们的网站,我们可以添加一个celery任务。

4、添加任务
往photos应用中添加一个tasks.py。
  1. from celery.task.schedules import crontab  
  2. from celery.decorators import periodic_task  
  3. from celery.utils.log import get_task_logger  
  4.   
  5. from photos.utils import save_latest_flickr_image  
  6.   
  7. logger = get_task_logger(__name__)  
  8.   
  9.   
  10. @periodic_task(  
  11.     run_every=(crontab(minute='*/15')),  
  12.     name="task_save_latest_flickr_image",  
  13.     ignore_result=True  
  14. )  
  15. def task_save_latest_flickr_image():  
  16.     """ 
  17.     Saves latest image from Flickr 
  18.     """  
  19.     save_latest_flickr_image()  
  20.     logger.info("Saved image from Flickr")  

       在这里,我们通过在一个task中包装这个函数,来实现每15分钟运行一次save_latest_flickr_image()函数。该@periodic_task装饰器抽象出代码来运行celery任务,使得tasks.py干净,易于阅读!

5、本地运行
       准备开始运行了?
       在Django应用程序和Redis运行的前提下,打开两个新的终端窗口/标签。在每一个新的窗口中,导航到你的项目目录,激活你的虚拟环境,然后运行下面的命令(每个窗口一个):
  1. $ celery -A picha worker -l info  
  2. $ celery -A picha beat -l info  

       当你访问http://127.0.0.1:8000/ 网址的时候,你现在应该能看到一个图片。我们的应用程序每15分钟从Flickr 获取一张图片。
点击在新窗口中浏览此图片
点击在新窗口中浏览此图片
通过photos/tasks.py查看代码。点击“Feedback”按钮发送一些反馈意见:
点击在新窗口中浏览此图片
点击在新窗口中浏览此图片
       以上是通过celery任务运行的。更多的请查看 feedback/tasks.py
       就这样,你成功的启动并运行了 Picha项目!
       当你本地开发Django项目时,这是一个很好的测试,但是当你需要部署到生产环境- 就像 DigitalOcean时,就不那么合适了。为此,建议你通过使用Supervisor在后台作为一个守护进程运行celery worker和调度器。

6、远程运行
       安装很简单。从版本库中获取版本5(如果你还没有的话)。然后,SSH到远程服务器,并运行:
  1. $ sudo apt-get install supervisor  

       然后,通过在远程服务器上“/etc/supervisor/conf.d/” 目录下添加配置文件来告知Supervisor celery的workers。在我们的例子中,我们需要两个这样的配置文件 - 一个用于Celery worker,一个是Celery scheduler。
在本地,在项目的根目录下创建一个“supervisor”的文件夹,然后添加下面的文件。
Celery Worker: picha_celery.conf
  1. ; ==================================  
  2. ;  celery worker supervisor example  
  3. ; ==================================  
  4.   
  5. ; the name of your supervisord program  
  6. [program:pichacelery]  
  7.   
  8. ; Set full path to celery program if using virtualenv  
  9. command=/home/mosh/.virtualenvs/picha/bin/celery worker -A picha --loglevel=INFO  
  10.   
  11. ; The directory to your Django project  
  12. directory=/home/mosh/sites/picha  
  13.   
  14. ; If supervisord is run as the root user, switch users to this UNIX user account  
  15. ; before doing any processing.  
  16. user=mosh  
  17.   
  18. ; Supervisor will start as many instances of this program as named by numprocs  
  19. numprocs=1  
  20.   
  21. ; Put process stdout output in this file  
  22. stdout_logfile=/var/log/celery/picha_worker.log  
  23.   
  24. ; Put process stderr output in this file  
  25. stderr_logfile=/var/log/celery/picha_worker.log  
  26.   
  27. ; If true, this program will start automatically when supervisord is started  
  28. autostart=true  
  29.   
  30. ; May be one of false, unexpected, or true. If false, the process will never  
  31. ; be autorestarted. If unexpected, the process will be restart when the program  
  32. ; exits with an exit code that is not one of the exit codes associated with this  
  33. ; process’ configuration (see exitcodes). If true, the process will be  
  34. ; unconditionally restarted when it exits, without regard to its exit code.  
  35. autorestart=true  
  36.   
  37. ; The total number of seconds which the program needs to stay running after  
  38. ; a startup to consider the start successful.  
  39. startsecs=10  
  40.   
  41. ; Need to wait for currently executing tasks to finish at shutdown.  
  42. ; Increase this if you have very long running tasks.  
  43. stopwaitsecs = 600  
  44.   
  45. ; When resorting to send SIGKILL to the program to terminate it  
  46. ; send SIGKILL to its whole process group instead,  
  47. ; taking care of its children as well.  
  48. killasgroup=true  
  49.   
  50. if your broker is supervised, set its priority higher  
  51. ; so it starts first  
  52. priority=998  

Celery Scheduler: picha_celerybeat.conf
  1. ; ================================  
  2. ;  celery beat supervisor example  
  3. ; ================================  
  4.   
  5. ; the name of your supervisord program  
  6. [program:pichacelerybeat]  
  7.   
  8. ; Set full path to celery program if using virtualenv  
  9. command=/home/mosh/.virtualenvs/picha/bin/celerybeat -A picha --loglevel=INFO  
  10.   
  11. ; The directory to your Django project  
  12. directory=/home/mosh/sites/picha  
  13.   
  14. ; If supervisord is run as the root user, switch users to this UNIX user account  
  15. ; before doing any processing.  
  16. user=mosh  
  17.   
  18. ; Supervisor will start as many instances of this program as named by numprocs  
  19. numprocs=1  
  20.   
  21. ; Put process stdout output in this file  
  22. stdout_logfile=/var/log/celery/picha_beat.log  
  23.   
  24. ; Put process stderr output in this file  
  25. stderr_logfile=/var/log/celery/picha_beat.log  
  26.   
  27. ; If true, this program will start automatically when supervisord is started  
  28. autostart=true  
  29.   
  30. ; May be one of false, unexpected, or true. If false, the process will never  
  31. ; be autorestarted. If unexpected, the process will be restart when the program  
  32. ; exits with an exit code that is not one of the exit codes associated with this  
  33. ; process’ configuration (see exitcodes). If true, the process will be  
  34. ; unconditionally restarted when it exits, without regard to its exit code.  
  35. autorestart=true  
  36.   
  37. ; The total number of seconds which the program needs to stay running after  
  38. ; a startup to consider the start successful.  
  39. startsecs=10  
  40.   
  41. if your broker is supervised, set its priority higher  
  42. ; so it starts first  
  43. priority=999  

       注:确保更新这些文件的路径,以匹配你的远程服务器的文件系统。
基本上,这些supervisor 配置文件告诉supervisord如何运行并管理我们的'programs'(因为它们是由supervisord调用)。
       在上面的例子中,我们已经创建了两个名为“pichacelery”和“pichacelerybeat”的supervisord程序。
现在,只需将这些文件拷贝到远程服务器的/etc/supervisor/conf.d/目录下。
       我们还需要在远程服务器上创建上面脚本中提到的日志文件:
  1. $ touch /var/log/celery/picha_worker.log  
  2. $ touch /var/log/celery/picha_beat.log  

       最后,运行以下命令,使 Supervisor 知道它所管理的程序的存在 - 例如,pichacelery和pichacelerybeat:
  1. $ sudo supervisorctl reread  
  2. $ sudo supervisorctl update  

       运行以下命令停止,启动,和/或检查pichacelery程序的状态:
  1. $ sudo supervisorctl stop pichacelery  
  2. $ sudo supervisorctl start pichacelery  
  3. $ sudo supervisorctl status pichacelery  

       你可以通过阅读官方文档获取Supervisor的更多信息。

7、最后提示
       1. 千万不要传递Django模型对象到celery任务。为了避免模型对象在传递给celery任务之前已经改变了,传递celery的主键给celery。然后,在运行之前使用主键从数据库中获取对象。
       2. 默认celery调度会在本地创建一些文件存储它的调度表。这些文件是“celerybeat-schedule.db”和“celerybeat.pid”。如果你在使用版本控制系统,比如Git(你应该使用!),请忽略这个文件,不要将它们添加到你的代码库中,因为它们是为本地运行的进程服务的。

8、下一步
       以上就是将celery集成到一个django项目的基本介绍。
想要更多?
1. 深入研究官方celery用户指南,以了解更多信息。
2. 创建一个Fabfile来设置Supervisor和配置文件。确保添加命令到reread和 update Supervisor。
3. 从repo中获取这个项目,并打开一个Pull 请求来添加一个新的celery任务。
编码快乐!

原文: https://realpython.com/blog/python/asynchronous-tasks-with-django-and-celery/

一、前言
        Kubernetes 是Google开源的容器集群管理系统,基于Docker构建一个容器的调度服务,提供资源调度、均衡容灾、服务注册、动态扩缩容等功能套件,目前最新版本为0.6.2。本文介绍如何基于Centos7.0构建Kubernetes平台,在正式介绍之前,大家有必要先理解Kubernetes几个核心概念及其承担的功能。以下为Kubernetes的架构设计图:
点击在新窗口中浏览此图片
1. Pods
        在Kubernetes系统中,调度的最小颗粒不是单纯的容器,而是抽象成一个Pod,Pod是一个可以被创建、销毁、调度、管理的最小的部署单元。比如一个或一组容器。
2. Replication Controllers
        Replication Controller是Kubernetes系统中最有用的功能,实现复制多个Pod副本,往往一个应用需要多个Pod来支撑,并且可以保证其复制的副本数,即使副本所调度分配的主宿机出现异常,通过Replication Controller可以保证在其它主宿机启用同等数量的Pod。Replication Controller可以通过repcon模板来创建多个Pod副本,同样也可以直接复制已存在Pod,需要通过Label selector来关联。
3、Services
        Services是Kubernetes最外围的单元,通过虚拟一个访问IP及服务端口,可以访问我们定义好的Pod资源,目前的版本是通过iptables的nat转发来实现,转发的目标端口为Kube_proxy生成的随机端口,目前只提供GOOGLE云上的访问调度,如GCE。如果与我们自建的平台进行整合?请关注下篇《kubernetes与HECD架构的整合》文章。
4、Labels
        Labels是用于区分Pod、Service、Replication Controller的key/value键值对,仅使用在Pod、Service、 Replication Controller之间的关系识别,但对这些单元本身进行操作时得使用name标签。
5、Proxy
        Proxy不但解决了同一主宿机相同服务端口冲突的问题,还提供了Service转发服务端口对外提供服务的能力,Proxy后端使用了随机、轮循负载均衡算法。

        说说个人一点看法,目前Kubernetes 保持一周一小版本、一个月一大版本的节奏,迭代速度极快,同时也带来了不同版本操作方法的差异,另外官网文档更新速度相对滞后及欠缺,给初学者带来一定挑战。在上游接入层官方侧重点还放在GCE(Google Compute Engine)的对接优化,针对个人私有云还未推出一套可行的接入解决方案。在v0.5版本中才引用service代理转发的机制,且是通过iptables来实现,在高并发下性能令人担忧。但作者依然看好Kubernetes未来的发展,至少目前还未看到另外一个成体系、具备良好生态圈的平台,相信在V1.0时就会具备生产环境的服务支撑能力。

一、环境部署
1、平台版本说明
    1)Centos7.0 OS
    2)Kubernetes V0.6.2
    3)etcd version 0.4.6
    4)Docker version 1.3.2

2、平台环境说明
点击在新窗口中浏览此图片

3、环境安装
    1)系统初始化工作(所有主机)
    系统安装-选择[最小化安装]
    
引用

    # yum -y install wget ntpdate bind-utils
    # wget http://mirror.centos.org/centos/7/extras/x86_64/Packages/epel-release-7-2.noarch.rpm
    # yum update
    

    CentOS 7.0默认使用的是firewall作为防火墙,这里改为iptables防火墙(熟悉度更高,非必须)。
    1.1、关闭firewall:
    
引用

    # systemctl stop firewalld.service #停止firewall
    # systemctl disable firewalld.service #禁止firewall开机启动
    

    1.2、安装iptables防火墙
    
引用

    # yum install iptables-services #安装
    # systemctl start iptables.service #最后重启防火墙使配置生效
    # systemctl enable iptables.service #设置防火墙开机启动
    

    2)安装Etcd(192.168.1.10主机)
    
引用

    # 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  
    

    启动服务etcd服务,如有提供第三方管理需求,另需在启动参数中添加“-cors='*'”参数。
    
引用

    # mkdir /data/etcd  
    # /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 &
    

    配置etcd服务防火墙,其中4001为服务端口,7001为集群数据交互端口。
  
引用

    # iptables -I INPUT -s 192.168.1.0/24 -p tcp --dport 4001 -j ACCEPT
    # iptables -I INPUT -s 192.168.1.0/24 -p tcp --dport 7001 -j ACCEPT
  


    3)安装Kubernetes(涉及所有Master、Minion主机)
    通过yum源方式安装,默认将安装etcd, docker, and cadvisor相关包。
    
引用

    # 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
    #yum -y install kubernetes
    

    升级至v0.6.2,覆盖bin文件即可,方法如下:
    
引用

    # mkdir -p /home/install && cd /home/install
    # wget https://github.com/GoogleCloudPlatform/kubernetes/releases/download/v0.6.2/kubernetes.tar.gz
    # tar -zxvf kubernetes.tar.gz
    # tar -zxvf kubernetes/server/kubernetes-server-linux-amd64.tar.gz
    # cp kubernetes/server/bin/kube* /usr/bin
    

    校验安装结果,出版以下信息说明安装正常。
    
引用

    [root@SN2014-12-200 bin]# /usr/bin/kubectl version
    Client Version: version.Info{Major:"0", Minor:"6+", GitVersion:"v0.6.2", GitCommit:"729fde276613eedcd99ecf5b93f095b8deb64eb4", GitTreeState:"clean"}
    Server Version: &version.Info{Major:"0", Minor:"6+", GitVersion:"v0.6.2", GitCommit:"729fde276613eedcd99ecf5b93f095b8deb64eb4", GitTreeState:"clean"}
    

    4)Kubernetes配置(仅Master主机)
    master运行三个组件,包括apiserver、scheduler、controller-manager,相关配置项也只涉及这三块。
4.1、【/etc/kubernetes/config】
  1. # Comma seperated list of nodes in the etcd cluster  
  2. KUBE_ETCD_SERVERS="--etcd_servers=http://192.168.1.10:4001"  
  3.   
  4. # logging to stderr means we get it in the systemd journal  
  5. KUBE_LOGTOSTDERR="--logtostderr=true"  
  6.   
  7. # journal message level, 0 is debug  
  8. KUBE_LOG_LEVEL="--v=0"  
  9.   
  10. # Should this cluster be allowed to run privleged docker containers  
  11. KUBE_ALLOW_PRIV="--allow_privileged=false"  

4.2、【/etc/kubernetes/apiserver】
  1. # The address on the local server to listen to.  
  2. KUBE_API_ADDRESS="--address=0.0.0.0"  
  3.   
  4. # The port on the local server to listen on.  
  5. KUBE_API_PORT="--port=8080"  
  6.   
  7. # How the replication controller and scheduler find the kube-apiserver  
  8. KUBE_MASTER="--master=192.168.1.200:8080"  
  9.   
  10. # Port minions listen on  
  11. KUBELET_PORT="--kubelet_port=10250"  
  12.   
  13. # Address range to use for services  
  14. KUBE_SERVICE_ADDRESSES="--portal_net=10.254.0.0/16"  
  15.   
  16. # Add you own!  
  17. KUBE_API_ARGS=""  

4.3、【/etc/kubernetes/controller-manager】
  1. # Comma seperated list of minions  
  2. KUBELET_ADDRESSES="--machines= 192.168.1.201,192.168.1.202"  
  3.   
  4. # Add you own!  
  5. KUBE_CONTROLLER_MANAGER_ARGS=""  

4.4、【/etc/kubernetes/scheduler】
  1. # Add your own!  
  2. KUBE_SCHEDULER_ARGS=""  

    启动master侧相关服务
    
引用

    # systemctl daemon-reload
    # systemctl start kube-apiserver.service kube-controller-manager.service kube-scheduler.service
    # systemctl enable kube-apiserver.service kube-controller-manager.service kube-scheduler.service
    

    5)Kubernetes配置(仅minion主机)
        minion运行两个组件,包括kubelet、proxy,相关配置项也只涉及这两块。
    Docker启动脚本更新
    # vi /etc/sysconfig/docker
    添加:-H tcp://0.0.0.0:2375,最终配置如下,以便以后提供远程API维护。
    OPTIONS=--selinux-enabled -H tcp://0.0.0.0:2375 -H fd://

    修改minion防火墙配置,通常master找不到minion主机多半是由于端口没有连通。
    iptables -I INPUT -s 192.168.1.200 -p tcp --dport 10250 -j ACCEPT

    修改kubernetes minion端配置,以192.168.1.201主机为例,其它minion主机同理。
5.1、【/etc/kubernetes/config】
  1. # Comma seperated list of nodes in the etcd cluster  
  2. KUBE_ETCD_SERVERS="--etcd_servers=http://192.168.1.10:4001"  
  3.   
  4. # logging to stderr means we get it in the systemd journal  
  5. KUBE_LOGTOSTDERR="--logtostderr=true"  
  6.   
  7. # journal message level, 0 is debug  
  8. KUBE_LOG_LEVEL="--v=0"  
  9.   
  10. # Should this cluster be allowed to run privleged docker containers  
  11. KUBE_ALLOW_PRIV="--allow_privileged=false"  

5.2、【/etc/kubernetes/kubelet】
  1. ###  
  2. # kubernetes kubelet (minion) config  
  3.   
  4. # The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)  
  5. KUBELET_ADDRESS="--address=0.0.0.0"  
  6.   
  7. # The port for the info server to serve on  
  8. KUBELET_PORT="--port=10250"  
  9.   
  10. # You may leave this blank to use the actual hostname  
  11. KUBELET_HOSTNAME="--hostname_override=192.168.1.201"  
  12.   
  13. # Add your own!  
  14. KUBELET_ARGS=""  

5.3、【/etc/kubernetes/proxy】
  1. KUBE_PROXY_ARGS=""  

启动kubernetes服务
    
引用

# systemctl daemon-reload
# systemctl enable docker.service kubelet.service kube-proxy.service
# systemctl start docker.service kubelet.service kube-proxy.service
    

3、校验安装(在master主机操作,或可访问master主机8080端口的client api主机)
  1) kubernetes常用命令
    
引用

# kubectl get minions    #查查看minion主机
# kubectl get pods    #查看pods清单
# kubectl get services 或 kubectl get services -o json    #查看service清单
# kubectl get replicationControllers    #查看replicationControllers清单
# for i in `kubectl get pod|tail -n +2|awk '{print $1}'`; do kubectl delete pod $i; done    #删除所有pods
    

    或者通过Server api for REST方式(推荐,及时性更高):
    
引用

# curl -s -L http://192.168.1.200:8080/api/v1beta1/version | python -mjson.tool    #查看kubernetes版本
# curl -s -L http://192.168.1.200:8080/api/v1beta1/pods | python -mjson.tool    #查看pods清单
# curl -s -L http://192.168.1.200:8080/api/v1beta1/replicationControllers | python -mjson.tool    #查看replicationControllers清单
# curl -s -L http://192.168.1.200:8080/api/v1beta1/minions | python -m json.tool    #查查看minion主机
# curl -s -L http://192.168.1.200:8080/api/v1beta1/services | python -m json.tool    #查看service清单
    

注:在新版kubernetes中,所有的操作命令都整合至kubectl,包括kubecfg、kubectl.sh、kubecfg.sh等

  2)创建测试pod单元
   # /home/kubermange/pods && cd /home/kubermange/pods
   # vi apache-pod.json
  1. {  
  2.   "id": "fedoraapache",  
  3.   "kind": "Pod",  
  4.   "apiVersion": "v1beta1",  
  5.   "desiredState": {  
  6.     "manifest": {  
  7.       "version": "v1beta1",  
  8.       "id": "fedoraapache",  
  9.       "containers": [{  
  10.         "name": "fedoraapache",  
  11.         "image": "fedora/apache",  
  12.         "ports": [{  
  13.           "containerPort": 80,  
  14.           "hostPort": 8080  
  15.         }]  
  16.       }]  
  17.     }  
  18.   },  
  19.   "labels": {  
  20.     "name": "fedoraapache"  
  21.   }  
  22. }  

    # kubectl create -f apache-pod.json
    # kubectl get pod
引用

NAME                IMAGE(S)            HOST                LABELS              STATUS
fedoraapache        fedora/apache       192.168.1.202/      name=fedoraapache   Running

    启动浏览器访问http://192.168.1.202:8080/,对应的服务端口切记在iptables中已添加。效果图如下:
点击在新窗口中浏览此图片
    观察kubernetes在etcd中的数据存储结构
点击在新窗口中浏览此图片

    观察单个pods的数据存储结构,以json的格式存储。
点击在新窗口中浏览此图片

二、实战操作
    任务:通过Kubernetes创建一个LNMP架构的服务集群,以及观察其负载均衡,涉及镜像“yorko/webserver”已经push至registry.hub.docker.com,大家可以通过“docker pull yorko/webserver”下载。
    
引用

    # mkdir -p /home/kubermange/replication && mkdir -p /home/kubermange/service
    # cd /home/kubermange/replication
    

1、 创建一个replication ,本例直接在replication模板中创建pod并复制,也可独立创建pod再通过replication来复制。
【replication/lnmp-replication.json】
  1. {  
  2.   "id": "webserverController",  
  3.   "kind": "ReplicationController",  
  4.   "apiVersion": "v1beta1",  
  5.   "labels": {"name": "webserver"},  
  6.   "desiredState": {  
  7.     "replicas": 2,  
  8.     "replicaSelector": {"name": "webserver_pod"},  
  9.     "podTemplate": {  
  10.       "desiredState": {  
  11.          "manifest": {  
  12.            "version": "v1beta1",  
  13.            "id": "webserver",  
  14.            "volumes": [  
  15.              {"name":"httpconf", "source":{"hostDir":{"path":"/etc/httpd/conf"}}},  
  16.              {"name":"httpconfd", "source":{"hostDir":{"path":"/etc/httpd/conf.d"}}},  
  17.              {"name":"httproot", "source":{"hostDir":{"path":"/data"}}}  
  18.             ],  
  19.            "containers": [{  
  20.              "name": "webserver",  
  21.              "image": "yorko/webserver",  
  22.              "command": ["/bin/sh", "-c", "/usr/bin/supervisord -c /etc/supervisord.conf"],  
  23.              "volumeMounts": [  
  24.                {"name":"httpconf", "mountPath":"/etc/httpd/conf"},  
  25.                {"name":"httpconfd", "mountPath":"/etc/httpd/conf.d"},  
  26.                {"name":"httproot", "mountPath":"/data"}  
  27.               ],  
  28.              "cpu": 100,  
  29.              "memory": 50000000,  
  30.              "ports": [{  
  31.                "containerPort": 80,  
  32.              },{  
  33.                "containerPort": 22,  
  34.             }]  
  35.            }]  
  36.          }  
  37.        },  
  38.        "labels": {"name": "webserver_pod"},  
  39.       },  
  40.   }  
  41. }  

    执行创建命令
    #kubectl create -f lnmp-replication.json
    观察生成的pod副本清单:
[root@SN2014-12-200 replication]# kubectl get pod
引用

NAME                                   IMAGE(S)            HOST                LABELS               STATUS
84150ab7-89f8-11e4-970d-000c292f1620   yorko/webserver     192.168.1.202/      name=webserver_pod   Running
84154ed5-89f8-11e4-970d-000c292f1620   yorko/webserver     192.168.1.201/      name=webserver_pod   Running
840beb1b-89f8-11e4-970d-000c292f1620   yorko/webserver     192.168.1.202/      name=webserver_pod   Running
84152d93-89f8-11e4-970d-000c292f1620   yorko/webserver     192.168.1.202/      name=webserver_pod   Running
840db120-89f8-11e4-970d-000c292f1620   yorko/webserver     192.168.1.201/      name=webserver_pod   Running
8413b4f3-89f8-11e4-970d-000c292f1620   yorko/webserver     192.168.1.201/      name=webserver_pod   Running

2、创建一个service,通过selector指定 "name": "webserver_pod"与pods关联。
【service/lnmp-service.json】
  1. {  
  2.   "id": "webserver",  
  3.   "kind": "Service",  
  4.   "apiVersion": "v1beta1",  
  5.   "selector": {  
  6.     "name": "webserver_pod",  
  7.   },  
  8.   "protocol": "TCP",  
  9.   "containerPort": 80,  
  10.   "port": 8080  
  11. }  

    执行创建命令:
    # kubectl create -f lnmp-service.json

    登录minion主机(192.168.1.201),查询主宿机生成的iptables转发规则(最后一行)
    # iptables -nvL -t nat
引用

Chain KUBE-PROXY (2 references)
pkts bytes target     prot opt in     out     source               destination        
    2   120 REDIRECT   tcp  --  *      *       0.0.0.0/0            10.254.102.162       /* kubernetes */ tcp dpt:443 redir ports 47700
    1    60 REDIRECT   tcp  --  *      *       0.0.0.0/0            10.254.28.74         /* kubernetes-ro */ tcp dpt:80 redir ports 60099
    0     0 REDIRECT   tcp  --  *      *       0.0.0.0/0            10.254.216.51        /* webserver */ tcp dpt:8080 redir ports 40689

    访问测试,http://192.168.1.201:40689/info.php,刷新浏览器发现proxy后端的变化,默认为随机轮循算法。
点击在新窗口中浏览此图片
点击在新窗口中浏览此图片

三、测试过程
    1、pods自动复制、销毁测试,观察kubernetes自动保持副本数(6份)
删除replicationcontrollers中一个副本fedoraapache
[root@SN2014-12-200 pods]# kubectl delete pods fedoraapache
I1219 23:59:39.305730    9516 restclient.go:133] Waiting for completion of operation 142530
fedoraapache
引用

[root@SN2014-12-200 pods]# kubectl get pods
NAME                                   IMAGE(S)            HOST                LABELS              STATUS
5d70892e-8794-11e4-970d-000c292f1620   fedora/apache       192.168.1.201/      name=fedoraapache   Running
5d715e56-8794-11e4-970d-000c292f1620   fedora/apache       192.168.1.202/      name=fedoraapache   Running
5d717f8d-8794-11e4-970d-000c292f1620   fedora/apache       192.168.1.202/      name=fedoraapache   Running
5d71c584-8794-11e4-970d-000c292f1620   fedora/apache       192.168.1.201/      name=fedoraapache   Running
5d71a494-8794-11e4-970d-000c292f1620   fedora/apache       192.168.1.202/      name=fedoraapache   Running

#自动生成出一个副本,保持6份的效果
引用

[root@SN2014-12-200 pods]# kubectl get pods
NAME                                   IMAGE(S)            HOST                LABELS              STATUS
5d717f8d-8794-11e4-970d-000c292f1620   fedora/apache       192.168.1.202/      name=fedoraapache   Running
5d71c584-8794-11e4-970d-000c292f1620   fedora/apache       192.168.1.201/      name=fedoraapache   Running
5d71a494-8794-11e4-970d-000c292f1620   fedora/apache       192.168.1.202/      name=fedoraapache   Running
2a8fb993-8798-11e4-970d-000c292f1620   fedora/apache       192.168.1.201/      name=fedoraapache   Running
5d70892e-8794-11e4-970d-000c292f1620   fedora/apache       192.168.1.201/      name=fedoraapache   Running
5d715e56-8794-11e4-970d-000c292f1620   fedora/apache       192.168.1.202/      name=fedoraapache   Running

2、测试不同角色模块中的hostPort
    1)pod中hostPort为空,而replicationcontrollers为指定端口,则异常;两侧都指定端口,相同或不同时都异常;pod的hostport为指定,另replicationcon为空,则正常;pod的hostport为空,另replicationcon为空,则正常;结论是在replicationcontrollers场景不能指定hostport,否则异常,待持续测试。
    2)结论:在replicationcontronllers.json中,"replicaSelector": {"name": "webserver_pod"}要与"labels": {"name": "webserver_pod"}以及service中的"selector": {"name": "webserver_pod"}保持一致;

请关注下篇《kubernetes与HECD架构的整合》,近期推出。

参考文献:
https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/getting-started-guides/fedora/fedora_manual_config.md
https://github.com/GoogleCloudPlatform/kubernetes/blob/master/DESIGN.md
http://www.infoq.com/cn/articles/Kubernetes-system-architecture-introduction

转载请注明来源 http://blog.liuts.com/post/247/

        主控端是OMServer的核心角色,负责接收加密的协议串且进行解密,解析成OMServer调用的任务模块,同时结合角色中的saltstack、ansible或func组件,向目标业务服务器集群(被控机)发送执行任务,执行完毕后,将返回的执行结果加解密处理,最后逐级返回给系统管理员,角色所在位置见以下架构图:
点击在新窗口中浏览此图片
一、环境部署
1、部署saltstack、ansible或func组件,详细见本书相关章节,此处省略;

2、安装rpyc模块
  1. # wget https://pypi.python.org/packages/source/r/rpyc/rpyc-3.2.3.tar.gz --no-check-certificate  
  2. # tar -zxvf rpyc-3.2.3.tar.gz  
  3. # cd rpyc-3.2.3  
  4. # python setup.py install  

3、下载主控端源码
#cd /home
download github地址:https://github.com/yorkoliu/pyauto/tree/master/第十三章/OMServer

修改OMServer/config.py主配置文件
  1. # -*- coding: utf-8 -*-  
  2. #!/usr/bin/env python  
  3. AUTO_PLATFORM = "saltstack"    #指定组件环境,支持saltstack、ansible、func  
  4.   
  5. #密钥,与项目中setting.py的SECRET_KEY变量保持一致  
  6. SECRET_KEY = "ctmj#&8hrgow_^sj$ejt@9fzsmh_o)-=(byt5jmg=e3#foya6u"  

4、编写任务模块
    1)在WEB前端点击【添加模块】,指定模块名称、描述、参数接口信息,提交后记录生成的模块ID(数字);
    2)在主控端OMServer/modules目录存放了各个组件的模块,以不同目录名作为区分,任务模块名称由“Mid_”+模块ID组成,与前端生成的模块ID进行关联,如Mid_1007.py,可参考现有示例进行修改。

5、启动服务
  1. # cd /home/OMServer  
  2. # python OMservermain.py &  
  3.   
  4. 检查11511端口是否已监听  
  5. # netstat -an|grep 11511  


二、校验环境
        最后,打开浏览器访问http://omserver.domain.com(自定义域名,可通过修改hosts实现),效果图如下。
点击在新窗口中浏览此图片

三、基于Python构建可扩展的自动化运维平台(WOT分享主题)

       《python自动化运维:技术与最佳实践》书籍发布已经1个月有余,根据读者反馈,在部署OMServer平台时遇到很多困难及问题,尤其是第一次部署Django环境的读者。因此,作者对书籍中OMServer环境部署章节的内容进行扩充,以便让每位读者都可以轻易完成平台搭建。OMServer平台涉及两个角色,其中一个为Web服务端,运行在Django及rpyc环境,另一角色为主控端,需要部署saltstack、ansible或func主控端环境,本文介绍Web服务端的部署详细步骤。

---环境版本说明---
* Python  版本  2.6.6
* Django  版本   1.4.9
* nginx  版本  1.5.9
* pcre  版本  8.34
* rpyc  版本  3.2.3
* uwsgi  版本  2.0.4
* django-debug-toolbar  版本  0.8.5

一、Django环境部署
  1. # yum install -y zlib-devel #Nginx环境初始化,安装gzip包  
  2. # yum -y install python-devel mysql-devel zlib-devel openssl-devel  #MySQL-python关联包  
  3.   
  4. # mkdir –p /data/logs/    #创建uwsgi日志目录  
  5. # mkdir –p /data/www/    #创建项目目录  
  6. # mkdir -p /home/install/Django && cd /home/install/Django    #创建安装包目录  

1、安装pcre,pcre是一个轻量级的正则表达式函数库,Nginx的HTTP Rewrite模块会用到,最新版本为8.34(对于OMServer平台环境来说是非必选项)。
  1. # wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.34.tar.gz  
  2. # tar -zxvf pcre-8.34.tar.gz  
  3. # cd pcre-8.34  
  4. #./configure  
  5. # make && make install  
  6. # cd ..  

2、安装Nginx,Nginx是最流行的高性能HTTP服务器,最新版本为1.5.9。
  1. # wget http://nginx.org/download/nginx-1.5.9.tar.gz  
  2. # tar -zxvf nginx-1.5.9.tar.gz  
  3. # cd nginx-1.5.9  
  4. #./configure --user=nobody --group=nobody --prefix=/usr/local/nginx --with-http_stub_status_module --with-cc-opt='-O3' --with-cpu-opt=opteron  
  5. # make && make install  
  6. # cd ..  

3、安装 MySQL-python,MySQL-python是Python访问MySQL数据库的第三方模块库,最新版本为1.2.3c1。
  1. # wget http://pypi.python.org/packages/source/M/MySQL-python/MySQL-python-1.2.5.zip --no-check-certificate  
  2. # unzip MySQL-python-1.2.5.zip  
  3. # cd MySQL-python-1.2.5  
  4. # python setup.py install  
  5. # cd ..  

4、rpyc模块安装,用于平台与主控端做数据通讯交互。
  1. # wget https://pypi.python.org/packages/source/r/rpyc/rpyc-3.2.3.tar.gz --no-check-certificate  
  2. # tar -zxvf rpyc-3.2.3.tar.gz  
  3. # cd rpyc-3.2.3  
  4. # python setup.py install  

5、安装uwsgi。uwsgi是一个快速的、纯C语言开发的、自维护、对开发者友好的WSGI服务器,旨在提供专业的Python web应用发布和开发,最新版本为2.0.4。
  1. # wget http://projects.unbit.it/downloads/uwsgi-2.0.4.tar.gz  
  2. # tar -zxvf uwsgi-2.0.4.tar.gz  
  3. # cd uwsgi-2.0.4  
  4. # make  

出现如下代码成功安装:
################# uWSGI configuration #################
pcre = True
kernel = Linux
malloc = libc
execinfo = False
ifaddrs = True
ssl = True
zlib = True
locking = pthread_mutex
plugin_dir = .
timer = timerfd
yaml = embedded
json = False
filemonitor = inotify
routing = True
debug = False
capabilities = False
xml = False
event = epoll
############## end of uWSGI configuration #############
total build time: 17 seconds
*** uWSGI is ready, launch it with ./uwsgi ***
  1. # vi /etc/ld.so.conf    #添加动态链接库目录/usr/local/lib  
  2. 添加行:/usr/local/lib  
  3. # ldconfig    #使之生效  
  4.   
  5. # cp uwsgi /usr/bin  
  6. # cd ..  

6、安装Django,Django是一个Python最流行的开源Web开发框架,最新版本为1.6.5。考虑到兼容与稳定性,本案例使用1.4.9版本进行开发。
  1. # wget https://www.djangoproject.com/m/releases/1.4/Django-1.4.9.tar.gz  
  2. # tar -zxvf Django-1.4.9.tar.gz  
  3. # cd Django-1.4.9  
  4. # python setup.py install  

创建一个demo项目,以便验证环境是否正确安装部署。
  1. # cd /data/www  
  2. # django-admin.py startproject demo  
  3.   
  4. # 由于Django1.4版本后对项目目录做了改动,即将manage.py管理任务命令单独放在项目根目录。为后续与OMServer平台目录结构相兼容,我们对目录结构进行调整。  
  5. # cd demo/demo && mv * ../ && cd .. && rm -rf demo  
  6.   
  7. # 最终文件目录结构如下:  
  8. # ls  
  9. __init__.py  manage.py  settings.py  urls.py  wsgi.py  

7、django-debug-toolbar的安装(Django调试利器)
  1. # wget http://blog.liuts.com/Django/django-debug-toolbar-master.tar.gz  
  2. # tar -zxvf django-debug-toolbar-master.tar.gz  
  3. # cd django-debug-toolbar-master/  
  4. # python setup.py install  

8、配置Nginx,修改/usr/local/nginx/conf/nginx.conf,最终完整配置如下:
  1. user  nobody;  
  2. worker_processes  2;  
  3.   
  4. #error_log  logs/error.log;  
  5. #error_log  logs/error.log  notice;  
  6. #error_log  logs/error.log  info;  
  7.   
  8. #pid        logs/nginx.pid;  
  9.   
  10.   
  11. events {  
  12.     worker_connections  10240;  
  13. }  
  14.   
  15.   
  16. http {  
  17.     include       mime.types;  
  18.     default_type  application/octet-stream;  
  19.   
  20.     #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '  
  21.     #                  '$status $body_bytes_sent "$http_referer" '  
  22.     #                  '"$http_user_agent" "$http_x_forwarded_for"';  
  23.   
  24.     #access_log  logs/access.log  main;  
  25.   
  26.     sendfile        on;  
  27.     tcp_nopush     on;  
  28.   
  29.     keepalive_timeout  65;  
  30.   
  31.     gzip  on;  
  32.     server {  
  33.         listen 80;  
  34.         server_name demo.domain.com;  
  35.   
  36.         location / {  
  37.             uwsgi_pass 127.0.0.1:9000;  
  38.             include uwsgi_params;  
  39.             uwsgi_param UWSGI_CHDIR  /data/www/demo;  
  40.             uwsgi_param UWSGI_SCRIPT wsgi;  
  41.             access_log off;  
  42.         }  
  43.   
  44.     }  
  45.   
  46. }  

* uwsgi_param UWSGI_SCRIPT wsgi;参数值wsgi对应项目目录中的wsgi.py,此处文件前缀与参数值要保持一致。

9、配置uwsgi,创建uwsgi配置文件/usr/local/nginx/conf/uwsgi.ini,详细内容如下:
  1. [uwsgi]  
  2. socket = 0.0.0.0:9000  
  3. master = true  
  4. pidfile = /usr/local/nginx/uwsgi.pid  
  5. processes = 8  
  6. workers = 2  
  7. chdir = /data/www/demo  
  8. pythonpath = /data/www  
  9. profiler=true  
  10. memory-report=true  
  11. enable-threads = true  
  12. logdate=true  
  13. limit-as=6048  
  14. daemonize=/data/logs/django.log  

---关键参数及说明---
1)chdir 指定项目目录;
2)pythonpath 指定项目目录上一级
3)processes 指定进程数
4)workers 分配CPU的核数
5)limit-as 子进程分配的内存大小
6)max-requests 分配最大的请求数

    启动uwsgi与nginx服务,建议配置成服务自启动脚本,便于后续的日常维护。详细启动脚本这里不展开说明,有兴趣的读者可参阅互联网上已经存在的相关资源。
    最后启动uwsgi与nginx服务
  1. # /usr/bin/uwsgi --ini /usr/local/nginx/conf/uwsgi.ini  
  2. # /usr/local/nginx/sbin/nginx  

        访问http://demo.domain.com,出现如图所示的页面说明Django+uwsgi环境部署成功!
点击在新窗口中浏览此图片

二、OMServer项目部署
1、修改Nginx配置
添加OMServer项目站点配置,[server]域具体内容如下:
  1. server {  
  2.     listen 80;  
  3.     server_name omserver.domain.com;  
  4.   
  5.     location / {  
  6.         uwsgi_pass 127.0.0.1:9001;  
  7.         include uwsgi_params;  
  8.         uwsgi_param UWSGI_CHDIR  /data/www/OMserverweb;  
  9.         uwsgi_param UWSGI_SCRIPT django_wsgi;  
  10.         access_log off;  
  11.     }  
  12.     location ^~ /static {  
  13.         root /data/www/OMserverweb;  
  14.     }  
  15.   
  16.     location ~* ^.+\.(mpg|avi|mp3|swf|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|txt|tar|mid|midi|wav|rtf|mpeg)$ {  
  17.         root /data/www/OMserverweb/static;  
  18.         access_log off;  
  19.     }  
  20. }  


  1)切记修改UWSGI_SCRIPT为django_wsgi;
  2)监听uwsgi端口修改成127.0.0.1:9001;    #多个站点使用不同端口区分


2、添加omserver项目uwsgi配置
  1. # cd /usr/local/nginx/conf  
  2. # vi uwsgi_omserver.ini  
  3. [uwsgi]  
  4. socket = 0.0.0.0:9001  
  5. master = true  
  6. pidfile = /usr/local/nginx/uwsgi_omserver.pid  
  7. processes = 8  
  8. chdir = /data/www/OMserverweb  
  9. pythonpath = /data/www  
  10. profiler=true  
  11. memory-report=true  
  12. enable-threads = true  
  13. logdate=true  
  14. limit-as=6048  
  15. daemonize=/data/logs/django_omserver.log  

3、项目源码配置
  1)项目源码:
    # cd /data/www
    下载地址:https://github.com/yorkoliu/pyauto/tree/master/第十三章/OMserverweb
  2)导入数据库结构(Mysql)
    下载地址:https://github.com/yorkoliu/pyauto/blob/master/第十三章/SQL/OMServer.sql
  3)修改setting.py(数据库信息)
  1. DATABASES = {  
  2.     'default': {  
  3.         'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.  
  4.         'NAME': 'OMServer',                      # Or path to database file if using sqlite3.  
  5.         'USER': 'omserver_user',                      # Not used with sqlite3.  
  6.         'PASSWORD': 'J8w3jZXSG#y34',                  # Not used with sqlite3.  
  7.         'HOST': '192.168.1.10',                      # Set to empty string for localhost. Not used with sqlite3.  
  8.         'PORT': '3306',                      # Set to empty string for default. Not used with sqlite3.  
  9.     }  
  10. }  

   4)修改主控端rpyc主机IP
    OMserverweb/autoadmin/views.py
    
  1. ... ...  
  2. try:  
  3.     conn=rpyc.connect('192.168.1.20',11511)    #修改此处的rpyc主机地址;  
  4.     conn.root.login('OMuser','KJS23o4ij09gHF734iuhsdfhkGYSihoiwhj38u4h')  
  5. except Exception,e:  
  6.     logger.error('connect rpyc server error:'+str(e))  
  7.     return HttpResponse('connect rpyc server error:'+str(e))  
  8. ... ...  

启动项目uwsgi及Nginx服务
  1. # /usr/bin/uwsgi --ini /usr/local/nginx/conf/uwsgi_omserver.ini  
  2. # killall -9 nginx  
  3. # /usr/local/nginx/sbin/nginx  

4、访问http://omserver.domain.com,出现以下系统界面说明部署成功!
点击在新窗口中浏览此图片

下一步配置 《python自动化运维:技术与最佳实践》之OMServer平台环境部署详解【主控制端】

补:平台涉及开源组件包下载:
Django-1.4.9.tar.gz 下载
uwsgi-2.0.4.tar.gz 下载
rpyc-3.2.3.tar.gz 下载
pcre-8.34.tar.gz 下载
nginx-1.5.9.tar.gz 下载
MySQL-python-1.2.5.zip 下载
django-debug-toolbar-master.tar.gz 下载

【当当】 http://product.dangdang.com/23593858.html
【京东】 http://item.jd.com/11571426.html
【亚马逊】 http://www.amazon.cn/%E5%9B%BE%E4%B9%A6/dp/B00P5VKZWW
【天猫】 http://detail.tmall.com/item.htm?spm=a1z10.3.w4011-7555161747.28.SgDdii&id=42141530490&rn=3a7da8b28eea552fb6ebb6ed43ab024d&abbucket=18
【China-pub】 http://product.china-pub.com/3804188

《python自动化运维:技术与最佳实践》附带示例及案例源码
【国内镜像】(JD云汇)https://code.jd.com/yorkoliu/pyauto
【国外镜像】(Github)https://github.com/yorkoliu/pyauto
【源码打包下载】(zip)http://share.weiyun.com/9e4bfc70a9af8840927b92910ab80d8b

点击在新窗口中浏览此图片
一、内容简介
        本书在中国运维领域将有“划时代”的重要意义:一方面,这是国内第一本从纵、深和实践角度探讨Python在运维领域应用的著作;一方面本书的作者是中国运维领域的“偶像级”人物,本书是他在天涯社区和腾讯近10年工作经验的结晶。因为作者实战经验丰富,所以能高屋建瓴、直指痛处,围绕Python自动化运维这个主题,不仅详细介绍了系统基础信息、服务监控、数据报表、系统安全等基础模块,而且深入讲解了自动化操作、系统管理、配置管理、集群管理及大数据应用等高级功能。最重要的是,完整重现了4个来自实际生产环境的不同功能运维平台的综合案例,展示了完整的平台架构及开发流程。
        全书一共16章:基础篇(1-4章)详细介绍了系统基础信息、业务服务监控、定制业务质量报表、系统安全等基础和常用模块;高级篇(5-12章)深入讲解了批量运维管理器pexpect、paramiko、Fabric,集中化管理平台Ansible、Saltstack,统一网络控制器Func等高级功能,涵盖自动化操作、系统管理、配置管理、集群管理及大数据应用等主题;案例篇(13-16章)详细介绍了4个来自不同平台的运维案例,如何从零开始打造一个B/S自动化运维平台、如何打造Linux系统安全审计功能、如何构建分布式质量监控平台、如何构建桌面版C/S自动化运维平台,这4个案例均来自实际生产环境。

二、目录
前  言
第一部分 基础篇
第1章 系统基础信息模块详解 2
1.1 系统性能信息模块psutil 2
1.1.1 获取系统性能信息 3
1.1.2 系统进程管理方法 6
1.2 实用的IP地址处理模块IPy 7
1.2.1 IP地址、网段的基本处理 8
1.2.2 多网络计算方法详解 9
1.3 DNS处理模块dnspython 11
1.3.1 模块域名解析方法详解 11
1.3.2 常见解析类型示例说明 12
1.3.3 实践:DNS域名轮循业务监控 14
第2章 业务服务监控详解 17
2.1 文件内容差异对比方法 17
2.1.1 示例1:两个字符串的差异对比 17
2.1.2 生成美观的对比HTML格式文档 19
2.1.3 示例2:对比Nginx配置文件差异 19
2.2 文件与目录差异对比方法 21
2.2.1 模块常用方法说明 21
2.2.2 实践:校验源与备份目录差异 25
2.3 发送电子邮件模块smtplib 27
2.3.1 smtplib模块的常用类与方法 27
2.3.2 定制个性化的邮件格式方法 28
2.3.3 定制常用邮件格式示例详解 29
2.4 探测Web服务质量方法 34
2.4.1 模块常用方法说明 35
2.4.2 实践:实现探测Web服务质量 36
第3章 定制业务质量报表详解 39
3.1 数据报表之Excel操作模块 39
3.1.1 模块常用方法说明 41
3.1.2 实践:定制自动化业务流量报表周报 48
3.2 Python与rrdtool的结合模块 50
3.2.1 rrdtool模块常用方法说明 51
3.2.2 实践:实现网卡流量图表绘制 53
3.3 生成动态路由轨迹图 56
3.3.1 模块常用方法说明 56
3.3.2 实践:实现TCP探测目标服务路由轨迹 57
第4章 Python与系统安全 60
4.1 构建集中式的病毒扫描机制 60
4.1.1 模块常用方法说明 61
4.1.2 实践:实现集中式的病毒扫描 61
4.2 实现高效的端口扫描器 64
4.2.1 模块常用方法说明 64
4.2.2 实践:实现高效的端口扫描 66
第二部分 高级篇
第5章 系统批量运维管理器pexpect详解 70
5.1 pexpect的安装 70
5.2 pexpect的核心组件 71
5.2.1 spawn类 71
5.2.2 run函数 74
5.2.3 pxssh类 75
5.3 pexpect应用示例 76
5.3.1 实现一个自动化FTP操作 76
5.3.2 远程文件自动打包并下载 77
第6章 系统批量运维管理器paramiko详解 79
6.1 paramiko的安装 79
6.2 paramiko的核心组件 81
6.2.1 SSHClient类 81
6.2.2 SFTPClient类 82
6.3 paramiko应用示例 85
6.3.1 实现密钥方式登录远程主机 85
6.3.2 实现堡垒机模式下的远程命令执行 85
6.3.3 实现堡垒机模式下的远程文件上传 88
第7章 系统批量运维管理器Fabric详解 91
7.1 Fabric的安装 91
7.2 fab的常用参数 92
7.3 fabfile的编写 93
7.3.1 全局属性设定 93
7.3.2 常用API 94
7.3.3 示例1:查看本地与远程主机信息 95
7.3.4 示例2:动态获取远程目录列表 96
7.3.5 示例3:网关模式文件上传与执行 97
7.4 Fabric应用示例 98
7.4.1 示例1:文件打包、上传与校验 98
7.4.2 示例2:部署LNMP业务服务环境 99
7.4.3 示例3:生产环境代码包发布管理 101
第8章 从“零”开发一个轻量级WebServer 104
8.1 Yorserver介绍 104
8.1.1 功能特点 104
8.1.2 配置文件 105
8.2 功能实现方法 106
8.2.1 HTTP缓存功能 107
8.2.2 HTTP压缩功能 111
8.2.3 HTTP SSL功能 111
8.2.4 目录列表功能 114
8.2.5 动态CGI功能 114
第9章 集中化管理平台Ansible详解 118
9.1 YAML语言 119
9.1.1 块序列描述 120
9.1.2 块映射描述 120
9.2 Ansible的安装 121
9.2.1 业务环境说明 121
9.2.2 安装EPEL 122
9.2.3 安装Ansible 122
9.2.4 Ansible配置及测试 122
9.2.5 配置Linux主机SSH无密码访问 123
9.3 定义主机与组规则 124
9.3.1 定义主机与组 124
9.3.2 定义主机变量 125
9.3.3 定义组变量 125
9.3.4 分离主机与组特定数据 126
9.4 匹配目标 127
9.5 Ansible常用模块及API 127
9.6 playbook介绍 132
9.6.1 定义主机与用户 132
9.6.2 任务列表 133
9.6.3 执行playbook 134
9.7 playbook角色与包含声明 135
9.7.1 包含文件,鼓励复用 135
9.7.2 角色 136
9.8 获取远程主机系统信息:Facts 141
9.9 变量 142
9.9.1 Jinja2过滤器 143
9.9.2 本地Facts 143
9.9.3 注册变量 144
9.10 条件语句 145
9.11 循环 146
9.12 示例讲解 147
第10章 集中化管理平台Saltstack详解 155
10.1 Saltstack的安装 156
10.1.1 业务环境说明 156
10.1.2 安装EPEL 156
10.1.3 安装Saltstack 156
10.1.4 Saltstack防火墙配置 157
10.1.5 更新Saltstack配置及安装校验 157
10.2 利用Saltstack远程执行命令 158
10.3 Saltstack常用模块及API 161
10.4 grains组件 166
10.4.1 grains常用操作命令 167
10.4.2 定义grains数据 167
10.5 pillar组件 170
10.5.1 pillar的定义 171
10.5.2 pillar的使用 173
10.6 state介绍 174
10.6.1 state的定义 174
10.6.2 state的使用 175
10.7 示例:基于Saltstack实现的配置集中化管理 177
10.7.1 环境说明 177
10.7.2 主控端配置说明 177
10.7.3 配置pillar 179
10.7.4 配置state 180
10.7.5 校验结果 183
第11章 统一网络控制器Func详解 185
11.1 Func的安装 186
11.1.1 业务环境说明 186
11.1.2 安装Func 186
11.2 Func常用模块及API 189
11.2.1 选择目标主机 190
11.2.2 常用模块详解 190
11.3 自定义Func模块 194
11.4 非Python API接口支持 198
11.5 Func的Facts支持 199
第12章 Python大数据应用详解 202
12.1 环境说明 202
12.2 Hadoop部署 203
12.3 使用Python编写MapReduce 207
12.3.1 用原生Python编写MapReduce详解 208
12.3.2 用Mrjob框架编写MapReduce详解 212
12.4 实战分析 216
12.4.1 示例场景 216
12.4.2 网站访问流量统计 217
12.4.3 网站HTTP状态码统计 219
12.4.4 网站分钟级请求数统计 220
12.4.5 网站访问来源IP统计 221
12.4.6 网站文件访问统计 222
第三部分 案例篇
第13章 从零开始打造B/S自动化运维平台 226
13.1 平台功能介绍 226
13.2 系统构架设计 227
13.3 数据库结构设计 228
13.3.1 数据库分析 228
13.3.2 数据字典 228
13.3.3 数据库模型 229
13.4 系统环境部署 230
13.4.1 系统环境说明 230
13.4.2 系统平台搭建 230
13.4.3 开发环境优化 233
13.5 系统功能模块设计 235
13.5.1 前端数据加载模块 235
13.5.2 数据传输模块设计 237
13.5.3 平台功能模块扩展 240
第14章 打造Linux系统安全审计功能 245
14.1 平台功能介绍 245
14.2 系统构架设计 246
14.3 数据库结构设计 247
14.3.1 数据库分析 247
14.3.2 数据字典 247
14.4 系统环境部署 248
14.4.1 系统环境说明 248
14.4.2 上报主机配置 248
14.5 服务器端功能设计 252
14.5.1 Django配置 252
14.5.2 功能实现方法 253
第15章 构建分布式质量监控平台 256
15.1 平台功能介绍 256
15.2 系统构架设计 257
15.3 数据库结构设计 258
15.3.1 数据库分析 258
15.3.2 数据字典 258
15.3.3 数据库模型 259
15.4 系统环境部署 260
15.4.1 系统环境说明 260
15.4.2 数据采集角色 260
15.4.3 rrdtool作业 261
15.5 服务器端功能设计 263
15.5.1 Django配置 263
15.5.2 业务增加功能 264
15.5.3 业务报表功能 266
第16章 构建桌面版C/S自动化运维平台 269
16.1 平台功能介绍 269
16.2 系统构架设计 270
16.3 数据库结构设计 271
16.3.1 数据库分析 271
16.3.2 数据字典 272
16.3.3 数据库模型 272
16.4 系统环境部署 273
16.4.1 系统环境说明 273
16.4.2 系统环境搭建 273
16.5 系统功能模块设计 274
16.5.1 用户登录模块 274
16.5.2 系统配置功能 275
16.5.3 服务器分类模块 277
16.5.4 系统升级功能 280
16.5.5 客户端模块编写 284
16.5.6 执行功能模块 287
16.5.7 平台程序发布 289

三、书摘与插画
点击在新窗口中浏览此图片

点击在新窗口中浏览此图片

点击在新窗口中浏览此图片

点击在新窗口中浏览此图片

       2014年11月21日至22日,51CTO传媒主办的2014WOT全球软件技术峰会·深圳站将在深圳召开,技术人员和企业实践用户将齐聚深圳。WOT是由51CTO传媒主办的国内最具有影响的技术峰会,自2012年以来,秉承专注技术、服务技术人员的理念,获得了广大IT从业者和技术爱好者的一致认可,成为了业界重要的技术分享交流平台以及人脉拓展平台。
    本次2014 WOT·深圳站将邀请国内外顶级的互联网及创新企业技术负责人,首次对外公开其当下最in技术,分享涵盖六大主题,共有30+课程,移动游戏运营、运维开发、Web安全、数据挖掘、团队管理等以及未来两三年的技术趋势。 点击报名>>

本次采访对象是本次2014WOT深圳站<自动化运维>论坛的演讲人刘天斯,目前为腾讯的高级运维工程师

点击在新窗口中浏览此图片

1、您目前在贵公司负责哪些事情?能否先简单谈谈您在运维领域的从业经验,和您对此运维的理解?

        从事运维方面的工作刚满10年,前6年负责天涯社区的系统架构及运维的工作,2011年入职腾讯,先后负责静态类、大游戏下载业务的CDN运营,以及负责公司所有游戏的大数据接入、分析、提取等工作。

         简单分享个人在运维领域从业的两个经验:

        1)关于运维自动化这件事情,几乎所有的IT企业都在做,看似是一件非常好的事情,忽略了前提条件,往往付出更大的代价及运营成本。所提到的前提条件便是运维体系“标准化”、“流程化”、“规范化”的建设,覆盖企业中资源、版本、业务发布、监控、事件管理等环节。有了这些作为基础铺垫,运维自动化的建设才会很顺利实施,达成预期。

        2)业务的生命周期管理,运维扮演的角色。当一个产品在规划之初运维人员须第一时间介入参与,根据产品特点,提供业务平台前期架构设计、资源评估等数据。当产品进入开发阶段,须与开发人员保持密切沟通与互动,提供业务接入、缓存、存储、监控、安全等方面规范,以便在编码阶段更好磨合与对接,避免上线后反复做不必要的版本迭代,也使得开发出来的产品具备更高的可运维性。待业务上线后,务必定期同步相关运营数据给产品与开发人员侧,为后续优化、改进的工作提供数据支持,这也恰恰能体现运维人员的专业性及团队合作意识。

        运维体系中各个环节的工作犹如散落在地上的珠子,每个珠子分别代表事件、资源、监控、安全、自动化、日常工作等,看似是七零八落的,我们需要利用“流程”这条线将所有的珠子串起来,珠子的前后顺序及间隔由“标准规范”来控制。这样就形成了一条完整的链子,是一个有机的整体,最后会促使运维工作开展得井井有条。这条链子扣在三个点子上,就是“质量”、“效率”、“成本”。

2、能否讲讲这么多年运维工作的变化与演进?

云计算给IT行业带来的巨大冲击,从最初的不信任逐渐到认可,到最后各类云计算应用的落地普及。当然,这也给运维人员带来非常大的挑战,尤其承担企业私有云的建设,运维人员除了具备传统运维的能力外,还需要深入理解业务资源使用的特点,例如区分是计算性、内存型、IO型还是存储型,同时需要对资源进行合理的规划及定义扩容规则。私有云作为资源的一个大池子,如何保持其弹性,需要具备一套精准的监控手段,配合自动化运维工具来保障,包括自动化安装部署、配置管理、存储管理、故障处理、备份容灾等。实现业务快速上线,资源快速扩容,同时具备高可用的能力。在这种大背景下,运维人员除了会用“云”,且要求用好“云”,才能给企业带来价值。另外基于容器实现的虚拟化(Docker)已经兴起,将给业务的打包、部署、迁移、测试等都会带来革命性的变革,运维准备好了吗?

3、随着如今大数据的爆发,这给运维工作带来了怎样的冲击与改变?

大数据在企业做精细运营方面发挥了巨大的作用,作为底层服务支撑的运维,需要掌握大数据生态圈中关键技术点,包括Hadoop、hive、hbase、spark、storm等平台的日常运营,需要解决包括资源调度、数据接入、快速扩容、节点故障处理、高可用、数据存储生命周期管理等问题,这给运维人员提出了更高的要求,同时也给运维工作带来了新的机遇,一典型案例是将所有告警接入storm实时计算分析,过滤出有效告警,同时将信息入库Hadoop,以便做历史档的离线分析,让运维人员更懂业务。

4、贵公司在监控上用了哪些技术?使用开源的还是自主研发?

公司内部使用了自研方式实现监控体系的构建,局部会使用开源工具作为补充。

5、您认为目前国内的自动化平台以及数据可视化平台建设如何?还需要加强哪方面发展?

        自动化运维是每个企业都在追求的终极目标,做到一键触发业务上线、故障自愈、资源自动调度、高质量数据报表及业务智能分析等,既然是目标,说明大部分都还在路上,即使国内一线的互联网企业也未能达到该理想的状态。自动化之路是一个复杂的系统工程,是一个长期积累、沉淀且不断优化的过程。由于互联网行业的特殊性,包括新技术不断涌入及快速迭代,另一方面是互联网业务日新月异,各种颠覆性的产品层出不穷。作为服务支撑,这也给自动化运维带来变数及挑战。

        在国内需要加强的部分还是资源与技术的共享,很多时候大家都在同一件事情,贡献一个成熟且通用的组件对业界的影响是深远的,阿里在这方面做得就非常好。在个人著作《Python自动化运维:技术与最佳实践》中也分享一些实现方法与实践案例,可作参考。

6、您认为一名合格的运维工程师是如何定义的?需要具备哪些因素?

        我认为一名合格的运维工程师需要具备高度的责任心,有一定的沟通及协调能力,同时需要具备发现问题及解决问题的能力,平时要多思考,多总结,多输出,以便将现有的沉淀更好传承下去,即使人员变动也不会出现断层。另外对资源、质量要非常敏感,有一定的规划及ITIL能力。对运营的业务要做到全面性的了解,包括提供的服务、总体架构、技术实现原理以及存在的问题等。在技能方面需要熟悉主流的运维相关技术,包括网络、设备、操作系统、负载均衡、缓存、数据库、云计算技术等方面,并关注最新技术发展动向,评估并思考如何运用到实际工作当中,解决工作中碰到的问题。同时,需要具有很好的开发能力,理由是没有人比我们更清楚我们需要什么的平台或工具,在与产品或开发沟通时,才有更多的发言权,甚至是主导权。

原文: 【2014WOT深圳站讲师专访】刘天斯:Docker的到来,运维准备好了么?

        Docker的生态日趋成熟,开源社区也不断孵化出优秀的周边项目,覆盖网络、监控、维护、部署、开发等方面。帮助开发、运维人员快速构建、运营Docker服务环境,其中也不乏有大公司的影子,如Google、IBM、Redhat,甚至微软也宣称后续将提供Docker在Windows平台的支持。Docker的发展前景一片大好。但在企业当中,如何选择适合自己的Docker构建方案?可选的方案有kubernetes与CoreOS(都已整合各类组件),另外一种方案为Haproxy+etcd+confd,采用松散式的组织结构,但各个组件之间的通讯是非常严密的,且扩展性更强,定制也更加灵活。下面详细介绍如何使用Haproxy+etcd+confd构建一个高可用及自动发现的Docker基础架构。
一、架构优势
        约定由Haproxy+etcd+confd+Docker构建的基础服务平台简称“HECD” 架构,整合了多种开源组件,看似松散的结构,事实上已经是一个有机的整体,它们互相联系、互相作用,是Docker生态圈中最理想的组合之一,具有以下优势:
      自动、实时发现及无感知服务刷新;
      支持任意多台Docker主宿机;
      支持多种APP接入且打散至不分主宿机;
      采用Etcd存储信息,集群支持可靠性高;
      采用Confd配置引擎,支持各类接入层,如Nginx;
      支持负载均衡、故障迁移;
      具备资源弹性,伸缩自如(通过生成、销毁容器实现);

二、架构说明
        在HECD架构中,首先管理员操作Docker Client,除了提交容器(Container)启动与停止指令外,还通过REST-API方式向Etcd(K/V)存储组件注册容器信息,包括容器名称、主宿机IP、映射端口等。Confd配置组件会定时查询Etcd组件获取最新的容器信息,根据定义好的配置模板生成Haproxy配置文件Haproxy.cfg,并且自动reload haproxy服务。用户在访问业务服务时,完全没有感知后端APP的上线、下线、切换及迁移,达到了自动发现、高可用的目的。详细架构图见图1-1。
点击在新窗口中浏览此图片

图1-1 平台架构图


        为了方便大家理解各组件间的关系,通过图1-2进行架构流程梳理,首先管理员通过Shell或api操作容器,下一步将容器信息注册到Etcd组件,Confd组件会定时查询Etcd,获取已经注册到Etcd中容器信息,最后通过Confd的模板引擎生成Haproxy配置,整个流程结束。
点击在新窗口中浏览此图片

图1-2架构流程图


        了解架构流程后,我们逐一对流程中各组件进行详细介绍。
1、Etcd介绍
        Etcd是一个高可用的 Key/Value 存储系统,主要用于分享配置和服务发现。
          简单:支持 curl 方式的用户 API (HTTP+JSON)
          安全:可选 SSL 客户端证书认证
          快速:单实例可达每秒 1000 次写操作
          可靠:使用 Raft 实现分布式
2、Confd介绍
        Confd是一个轻量级的配置管理工具。通过查询Etcd,结合配置模板引擎,保持本地配置最新,同时具备定期探测机制,配置变更自动reload。
3、Haproxy介绍
        HAProxy是提供高可用性、负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费、快速并且可靠的一种解决方案。(来源百科)

三、架构部署
        平台环境基于Centos6.5+Docker1.2构建,其中Etcd的版本为etcd version 0.5.0-alpha,Confd版本为confd 0.6.2,Haproxy版本为HA-Proxy version 1.4.24。下面对平台的运行环境、安装部署、组件说明等进行详细说明,环境设备角色表如下:
点击在新窗口中浏览此图片

1、组件安装
    1.1 Docker安装
        SSH终端登录192.168.1.22服务器,执行以下命令:
  1. # yum -y install docker-io  
  2. # service docker start  
  3. # chkconfig docker on  

    1.2 Haproxy、confd安装
        SSH终端登录192.168.1.20服务器,执行以下命令:
  1. 1、haproxy  
  2. # yum –y install haproxy  
  3.   
  4. 2、confd  
  5. # wget https://github.com/kelseyhightower/confd/releases/download/v0.6.3/confd-0.6.3-linux-amd64  
  6. # mv confd /usr/local/bin/confd  
  7. # chmod +x /usr/local/bin/confd  
  8. # /usr/local/bin/confd -version  
  9. confd 0.6.2  

    1.3 Etcd(v0.4.6)安装
        SSH终端登录192.168.1.21服务器,执行以下命令:
  1. # mkdir -p /home/install && cd /home/install  
  2. # wget https://github.com/coreos/etcd/releases/download/v0.4.6/etcd-v0.4.6-linux-amd64.tar.gz  
  3. # tar -zxvf etcd-v0.4.6-linux-amd64.tar.gz  
  4. # cd etcd-v0.4.6-linux-amd64  
  5. # cp etcd* /bin/  
  6. # /bin/etcd -version  
  7. etcd version 0.4.6  

2、组件配置
    2.1 Etcd配置
        由于etcd是一个轻量级的K/V存储平台,启动时指定相关参数即可,无需配置。
  1. # mkdir /data/etcd  
  2. # /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 &  

        由于etcd具备多机支持,参数“-peer-addr”指定与其它节点通讯的地址;参数“-addr”指定服务监听地址;参数“-data-dir”为指定数据存储目录。
        由于etcd是通过REST-API方式进行交互,常见操作如下:
    1) 设置(set) key操作
  1. # curl -L http://192.168.1.21:4001/v2/keys/mykey -XPUT -d value="this is awesome"  
  2. {"action":"set","node":{"key":"/mykey","value":"this is awesome","modifiedIndex":28,"createdIndex":28}}  

    2) 获取(get) key信息
  1. # curl -L http://192.168.1.21:4001/v2/keys/mykey  
  2. {"action":"get","node":{"key":"/mykey","value":"this is awesome","modifiedIndex":28,"createdIndex":28}}  

    3) 删除key信息
  1. # curl -L http://192.168.1.21:4001/v2/keys/mykey -XDELETE         {"action":"delete","node":{"key":"/mykey","modifiedIndex":29,"createdIndex":28},"prevNode":{"key":"/mykey","value":"this is awesome","modifiedIndex":28,"createdIndex":28}}  

    更多操作API见https://github.com/coreos/etcd/blob/master/Documentation/api.md。

2.2 Confd+Haproxy配置
        由于Haproxy的配置文件是由Confd组件生成,要求Confd务必要与haproxy安装在同一台主机上,Confd的配置有两种,一种为Confd资源配置文件,默认路径为“/etc/confd/conf.d”目录,另一种为配置模板文件,默认路径为“/etc/confd/templates”。具体配置如下:
创建配置文件目录
# mkdir -p /etc/confd/{conf.d,templates}
(1)配置资源文件
        详细见以下配置文件,其中“src”为指定模板文件名称(默认到路径/etc/confd/templates中查找);“dest”指定生成的Haproxy配置文件路径;“keys”指定关联Etcd中key的URI列表;“reload_cmd”指定服务重载的命令,本例中配置成haproxy的reload命令。
【/etc/confd/conf.d/ haproxy.toml】
  1. [template]  
  2. src = "haproxy.cfg.tmpl"  
  3. dest = "/etc/haproxy/haproxy.cfg"  
  4. keys = [  
  5.   "/app/servers",  
  6. ]  
  7. reload_cmd = "/etc/init.d/haproxy reload"  

(2)配置模板文件
        Confd模板引擎采用了Go语言的文本模板,更多见http://golang.org/pkg/text/template/,具备简单的逻辑语法,包括循环体、处理函数等,本示例的模板文件如下,通过range循环输出Key及Value信息。
【/etc/confd/templates/haproxy.cfg.tmpl】
  1. global  
  2.         log 127.0.0.1 local3  
  3.         maxconn 5000  
  4.         uid 99  
  5.         gid 99  
  6.         daemon  
  7.   
  8. defaults  
  9.         log 127.0.0.1 local3  
  10.         mode http  
  11.         option dontlognull  
  12.         retries 3  
  13.         option redispatch  
  14.         maxconn 2000  
  15.         contimeout  5000  
  16.         clitimeout  50000  
  17.         srvtimeout  50000  
  18.   
  19. listen frontend 0.0.0.0:80  
  20.         mode http  
  21.         balance roundrobin  
  22.         maxconn 2000  
  23.         option forwardfor  
  24.         {{range gets "/app/servers/*"}}  
  25.         server {{base .Key}} {{.Value}} check inter 5000 fall 1 rise 2  
  26.         {{end}}  
  27.   
  28.         stats enable  
  29.         stats uri /admin-status  
  30.         stats auth admin:123456  
  31.         stats admin if TRUE  

(3)模板引擎说明
        本小节详细说明Confd模板引擎基础语法与示例,下面为示例用到的KEY信息。
  1. # curl -XPUT http://192.168.1.21:4001/v2/keys/app/servers/backstabbing_rosalind -d value="192.168.1.22:49156"  
  2. # curl -XPUT http://192.168.1.21:4001/v2/keys/app/servers/cocky_morse -d value="192.168.1.22:49158"  
  3. # curl -XPUT http://192.168.1.21:4001/v2/keys/app/servers/goofy_goldstine -d value="192.168.1.22:49160"  
  4. # curl -XPUT http://192.168.1.21:4001/v2/keys/app/servers/prickly_blackwell -d value="192.168.1.22:49162"  

1、base
    作为path.Base函数的别名,获取路径最后一段。
{{ with get "/app/servers/prickly_blackwell"}}
    server {{base .Key}} {{.Value}} check
{{end}}
  1. prickly_blackwell 192.168.1.22:49162  

2、get
    返回一对匹配的KV,找不到则返回错误。
{{with get "/app/servers/prickly_blackwell"}}
    key: {{.Key}}
    value: {{.Value}}
{{end}}
  1. /app/servers/prickly_blackwell 192.168.1.22:49162  

3、gets
    返回所有匹配的KV,找不到则返回错误。
{{range gets "/app/servers/*"}}
    {{.Key}} {{.Value}}
     {{end}}
  1. /app/servers/backstabbing_rosalind 192.168.1.22:49156  
  2. /app/servers/cocky_morse 192.168.1.22:49158  
  3. /app/servers/goofy_goldstine 192.168.1.22:49160  
  4. app/servers/prickly_blackwell 192.168.1.22:49162  

4、getv
    返回一个匹配key的字符串型Value,找不到则返回错误。
{{getv "/app/servers/cocky_morse"}}
  1. 192.168.1.22:49158  

5、getvs
    返回所有匹配key的字符串型Value,找不到则返回错误。
        {{range getvs "/app/servers/*"}}
           value: {{.}}
        {{end}}
  1. value: 192.168.1.22:49156  
  2. value: 192.168.1.22:49158  
  3. value: 192.168.1.22:49160  
  4. value: 192.168.1.22:49162  

6、split
    对输入的字符串做split处理,即将字符串按指定分隔符拆分成数组。
        {{ $url := split (getv "/app/servers/cocky_morse") ":" }}
          host: {{index $url 0}}
          port: {{index $url 1}}
  1. host: 192.168.1.22  
  2. port: 49158  

7、ls
    返回所有的字符串型子key,找不到则返回错误。
{{range ls "/app/servers/"}}
   subkey: {{.}}
{{end}}
  1. subkey: backstabbing_rosalind  
  2. subkey: cocky_morse  
  3. subkey: goofy_goldstine  
  4. subkey: prickly_blackwell  

8、lsdir
    返回所有的字符串型子目录,找不到则返回一个空列表。
{{range lsdir "/app/"}}
   subdir: {{.}}
{{end}}
  1. subdir: servers  

(4)启动confd及haproxy服务
        下面为启动Confd服务命令行,参数“interval”为指定探测etcd的频率,单位为秒,参数“-node”为指定etcd监听服务主地址,以便获取容器信息。
  1. # /usr/local/bin/confd -verbose -interval 10 -node '192.168.1.21:4001' -confdir /etc/confd > /var/log/confd.log &  
  2. # /etc/init.d/haproxy start  

3、容器配置
        前面HECD架构说明内容,有讲到容器的操作会即时注册到etcd组件中,是通过curl命令进行REST-API方式提交的,下面详细介绍通过SHELL及Python-api两种方式的实现方法,支持容器启动、停止的联动。

    3.1、SHELL实现方法
        实现的原理是通过获取“Docker run ***”命令输出的Container ID,通过“docker inspect Container ID”得到详细的容器信息,分析出容器服务映射的外部端口及容器名称,将以“/app/servers/容器名称”作为Key,“主宿机: 映射端口”作为Value注册到etcd中。其中Key信息前缀(/app/servers)与“/etc/confd/conf.d/haproxy.toml”中的keys参数是保持一致的。
【docker.sh】
  1. #!/bin/bash  
  2.   
  3. if [ -z $1 ]; then  
  4.         echo "Usage: c run <image name>:<version>"  
  5.         echo "       c stop <container name>"  
  6.         exit 1  
  7. fi  
  8.   
  9. if [ -z $ETCD_HOST ]; then  
  10.   ETCD_HOST="192.168.1.21:4001"  
  11. fi  
  12.   
  13. if [ -z $ETCD_PREFIX ]; then  
  14.   ETCD_PREFIX="app/servers"  
  15. fi  
  16.   
  17. if [ -z $CPORT ]; then  
  18.   CPORT="80"  
  19. fi  
  20.   
  21. if [ -z $FORREST_IP ]; then  
  22.   FORREST_IP=`ifconfig eth0| grep "inet addr" | head -1 | cut -d : -f2 | awk '{print $1}'`  
  23. fi  
  24.   
  25. function launch_container {  
  26.         echo "Launching $1 on $FORREST_IP ..."  
  27.   
  28.         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"`  
  29.         PORT=`docker inspect $CONTAINER_ID|grep "\"Ports\"" -A 50|grep "\"$CPORT/tcp\"" -A 3| grep HostPort|cut -d '"' -f4|head -1`  
  30.         NAME=`docker inspect $CONTAINER_ID | grep Name | cut -d '"' -f4 | sed "s/\///g"|sed -n 2p`  
  31.   
  32.         echo "Announcing to $ETCD_HOST..."  
  33.         curl -XPUT "http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$NAME" -d value="$FORREST_IP:$PORT"  
  34.   
  35.         echo "$1 running on Port $PORT with name $NAME"  
  36. }  
  37.   
  38. function stop_container {  
  39.         echo "Stopping $1..."  
  40.         CONTAINER_ID=`docker ps -a| grep $1 | awk '{print $1}'`  
  41.         echo "Found container $CONTAINER_ID"  
  42.         docker stop $CONTAINER_ID  
  43.   echo http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$1  
  44.         curl -XDELETE http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$1 &> /dev/null  
  45.         echo "Stopped."  
  46. }  
  47.   
  48.   
  49. if [ $1 = "run" ]; then  
  50.   launch_container $2  
  51. else  
  52.   stop_container $2  
  53. fi  

docker.sh使用方法:
    1) 启动一个容器
    # ./docker.sh run yorko/webserver:v3(镜像)
    2) 停止一个容器
    # ./docker.sh stop berserk_hopper(容器名)

3.2、Docker-py API实现方法
        通过Python语言调用Docker-py的API实现容器的远程操作(创建、运行、停止),并结合python-etcd模块对etcd进行操作(set、delete),达到与SHELL方式一样的效果,很明显,Docker-py方式更加容易扩展,可以无缝与现有运营平台对接。
        为兼顾到远程API支持,需对docker启动文件“exec”处进行修改,详细见如下:
# vi /etc/init.d/docker
  1. $exec -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock -d &>> $logfile &  

启动容器的程序如下:
【docker_run.py】
  1. #!/usr/local/Python/bin/python  
  2. import docker  
  3. import etcd  
  4. import sys  
  5.   
  6. Etcd_ip="192.168.1.21"  
  7. Server_ip="192.168.1.22"  
  8. App_port="80"  
  9. App_protocol="tcp"  
  10. Image="yorko/webserver:v3"  
  11.   
  12. Port=""  
  13. Name=""  
  14.   
  15. idict={}  
  16. rinfo={}  
  17. try:  
  18.     c = docker.Client(base_url='tcp://'+Server_ip+':2375',version='1.14',timeout=15)  
  19. except Exception,e:  
  20.     print "Connection docker server error:"+str(e)  
  21.     sys.exit()  
  22.   
  23. try:  
  24.     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  
  25. ','/etc/localtime'],ports=[80,22],name=None)  
  26.     containerId=rinfo['Id']  
  27. except Exception,e:  
  28.     print "Create docker container error:"+str(e)  
  29.     sys.exit()  
  30.   
  31. try:  
  32.     c.start(container=containerId, binds={'/data':{'bind': '/data','ro': False},'/etc/httpd/conf':{'bind': '/etc/httpd/conf','ro': True},'/etc/httpd/conf.d':{'bind': '/etc/htt  
  33. pd/conf.d','ro': True},'/etc/localtime':{'bind': '/etc/localtime','ro': True}}, port_bindings={80:None,22:None}, lxc_conf=None,publish_all_ports=True, links=None, privileged=F  
  34. alse,dns='172.17.42.1', dns_search=None, volumes_from=None, network_mode=None,restart_policy=None, cap_add=None, cap_drop=None)  
  35. except Exception,e:  
  36.     print "Start docker container error:"+str(e)  
  37.     sys.exit()  
  38.   
  39. try:  
  40.     idict=c.inspect_container(containerId)  
  41.     Name=idict["Name"][1:]  
  42.     skey=App_port+'/'+App_protocol  
  43.     for _key in idict["NetworkSettings"]["Ports"].keys():  
  44.         if _key==skey:  
  45.             Port=idict["NetworkSettings"]["Ports"][skey][0]["HostPort"]  
  46. except Exception,e:  
  47.     print "Get docker container inspect error:"+str(e)  
  48.     sys.exit()  
  49.   
  50. if Name!="" and Port!="":  
  51.     try:  
  52.         client = etcd.Client(host=Etcd_ip, port=4001)  
  53.         client.write('/app/servers/'+Name, Server_ip+":"+str(Port))  
  54.         print Name+" container run success!"  
  55.     except Exception,e:  
  56.         print "set etcd key error:"+str(e)  
  57. else:  
  58.     print "Get container name or port error."  

停止容器的程序如下:
【docker_stop.py】
  1. #!/usr/local/Python/bin/python  
  2. import docker  
  3. import etcd  
  4. import sys  
  5.   
  6. Etcd_ip="192.168.1.21"  
  7. Server_ip="192.168.1.22"  
  8. containerName="grave_franklin" #指定需要停止容器的名称  
  9.   
  10. try:  
  11.     c = docker.Client(base_url='tcp://'+Server_ip+':2375',version='1.14',timeout=10)  
  12.     c.stop('furious_heisenberg')  
  13. except Exception,e:  
  14.     print str(e)  
  15.     sys.exit()  
  16.   
  17. try:  
  18.     client = etcd.Client(host=Etcd_ip, port=4001)  
  19.     client.delete('/app/servers/'+containerName)  
  20.     print containerName+" container stop success!"  
  21. except Exception,e:  
  22. print str(e)  

注意:
        由于容器是无状态的,尽量让其以松散的形式存在,映射端口选项要求使用“-P”参数,即使用随机端口的模式,减少人手干预。

四、业务上线
        HECD架构已部署完毕,接下来就是让其为我们服务,案例中使用的镜像“yorko/webserver:v3”为已经构建好的LAMP平台。类似的镜像也可以在docker-pub中下载到,开始跑起,运行dockery.sh创建两个容器:
  1. # ./docker.sh run yorko/webserver:v3  
  2. Launching yorko/webserver:v3 on 192.168.1.22 ...  
  3. Announcing to 192.168.1.21:4001...  
  4. {"action":"set","node":{"key":"/app/servers/berserk_hopper","value":"192.168.1.22:49170","modifiedIndex":33,"createdIndex":33}}  
  5. yorko/webserver:v3 running on Port 49170 with name berserk_hopper  
  6.   
  7. # ./docker.sh run yorko/webserver:v3  
  8. Launching yorko/webserver:v3 on 192.168.1.22 ...  
  9. Announcing to 192.168.1.21:4001...  
  10. {"action":"set","node":{"key":"/app/servers/lonely_meitner","value":"192.168.1.22:49172","modifiedIndex":34,"createdIndex":34}}  
  11. yorko/webserver:v3 running on Port 49172 with name lonely_meitner  

        访问Haproxy监控地址:http://192.168.1.20/admin-status,刚创建的容器已经添加到haproxy中,见图1-3。
点击在新窗口中浏览此图片

图1-3 Haproxy监控后台截图


        1)观察Haproxy的配置文件(更新部分):
  1. # vi /etc/haproxy/haproxy.cfg  
  2. … …  
  3. listen frontend 0.0.0.0:80  
  4.         mode http  
  5.         balance roundrobin  
  6.         maxconn 2000  
  7.         option forwardfor  
  8.         server berserk_hopper 192.168.1.22:49170 check inter 5000 fall 1 rise 2  
  9.         server lonely_meitner 192.168.1.22:49172 check inter 5000 fall 1 rise 2  
  10. … …  

        2)访问php测试文件http://192.168.1.20/info.php
点击在新窗口中浏览此图片

图1-4 php测试文件截图


        从图1-4可以看出,获取的服务器端IP为容器本身的IP地址(172.17.0.11),在System环境变量处输出容器名为“598cf10a50a2”的信息。

参考:
http://ox86.tumblr.com/post/90554410668/easy-scaling-with-docker-haproxy-and-confd
https://github.com/AVGP/forrest/blob/master/forrest.sh
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值