NoOps

Ops make no ops | Ops的目标是没有Ops,嗯!

ansible动手篇-如何书写自己的hosts脚本

作者: |   11,840 浏览  | 

写在前面

         最近Ansible很火,作为一个后起之秀,在github上他的commitor人数已经超过鼻祖puppet两倍多了,相信很多人也尝试过使用这款工具,如果你所管理的环境足够简单(模块没上50个?),足够变化足够慢(半年不用动?),那么也许你并不需要阅读本文,因为简单的静态配置就可以搞定你的需求了,请参考官方文档
         更多的人遇到的是这样的情形:机器和分组信息需要从服务A中动态获取;资产信息需要从服务B中动态获取…诸如此类,但是我们运行时,往往需要以这些信息作为分组依据来管理。这时候,用静态hosts文件来管理就不现实了。幸运的是,ansible支持动态inventory script,不过官方对这部分的描述甚为模糊,甚至连返回的结构体应该是什么样子都没有提,造成很多同学只能望洋兴叹。本文就以我们自己写一个inventory script的过程,给大家分享下inventory 脚本的写法和要求。

一、背景

         在我米,机器的属性是以tag来定义的,通过组合tag就可以唯一的确定一批具有相同属性的机器,这些属性包括但不限于产品划分,服务架构,机器硬件,流程状态,机器归属,控制部署等任何方面,这些tag由一个专门的系统(xbox)来维护和变更。
         举例来说,我如何找出一台属于小米公司米聊部门A产品线X服务fe(前端)子系统中m机房正在服务的nginx机器列表呢?我会通过类似如下一个tag串来查询它(仅作示例,司内莫要对号入座):
         com.xiaomi_dept.miliao_pdl.A_service.X.sbs.fe_idc.m_job.nginx_status.service
         其中com,dept,pdl,service,sbs,job,status,分别是属性中公司,部门产品线,服务,子系统,原子服务,在线状态的tag标签。当我把这个串+token发给系统后,系统会找出在我权限范围内,包含所有这些标签,并且其对应属性值与tag串指明值相等的机器集合,于是我们得到了想要的机器列表。

二、与ansible的结合

         在考虑与ansible结合之前,搞清楚ansible本身如何获取、筛选机器列表并考虑与我们自己系统合适的对接是很有必要的,在开始时,我们只是以最粗暴直接的方式修改ansible源码,虽然快速的满足了需求,但也造成一个很严重的后果——给升级带来很大代价,因此最终我们放弃了这个版本。

1. 前期调研和规划

         首先我们来看下ansible是怎么调用到inventory脚本的,详细的列出追踪过程无论对作者还是读者都是一个极其痛苦的事情,因此我决定在这里只列出关键代码段:


         从中可以得出如下结论:
  1.    ansible config,HOSTLIST的位置可以调整(ansible/constents.py)。
  2.    HOSTLIST可以是文件,目录,可执行程序,并且目录可有无数层子目录,下面可以有无数个脚本或配置文件(ansible/inventory/__init__.py:70-107)。
  3.    inventory Script不限定脚本语言,但至少需要实现–list 选项,并返回一个符合规则的json str,列出全量的机器列表,执行inventory Script阶段不支持传入自定义参数;另外可选的是,实现一个—host方法,返回机器的变量信息(ansible/inventory/script.py),具体的来说,—list返回格式要求如下:

等效的hosts配置文件:

         因为python程序对于python环境十分敏感,所以我们强烈建议大家使用virtualenv将ansible及其环境部署到一个独立的目录,并且得益于结论1,我们可以将所有ansible相关的目录也收敛到此目录下,这样不但方便开发部署,也能避免对其它程序造成影响,我将我的环境统一放到了/usr/local/ansible下,并在activate脚本中export出了所有需要的变量,然后为用户alias了ago/back两个命令来进入和退出ansible环境。
         基于2,我们可以将hosts搞成一个目录,然后如果你乐意的话,下面还可以再创建分类目录,方便归类管理。我的目录结构如下:
         结论3中的那个“全量”再加上“不支持传入自定义参数”让我们有些头疼,实际上因为公司的机器成千上万,显然OP并不需要也不能每次运行ansible命令就拉全量机器列表,我们care的可能只有那么百十来台,可人家又不支持传入参数,怎么办?答案是:使用配置+cache,实际上官方提供的两个例子中也都使用了配置,不过配置这事,也需要安全的环境支撑,我们在第一版本中之所以没有用cache,是因为我们运行ansible的机器没有个人账号,呐,你敢不敢把token写文件里面放到上面?恐怕你连cache都不愿意放。说道这里我也特别提醒下,仅在足够安全的环境下使用我们最后提供的脚本。
         好了,现在我们脑海中就有个大体的轮廓了:只要写一个脚本,读取自定义配置,然后查询机器信息管理系统获得机器列表,实现到—list中方法,然后吐出一个合理的json,扔到ansible hosts目录下,机器列表的事情就搞定了,如果愿意,甚至可以连分组信息以及机器变量也搞定了。
         下面我们就开始动手写一个来试试了。

2. 动手

         首先我们来实现一个最简单的脚本,官方的两个示例都是很好的例子,cobber是获取机器列表(—list)的例子,ec2是获取机器信息(—host)的例子。我们下载cobber.py改起,我也画了下这个程序的流程图(这里),也可以到这里查看带附注的版本(翻墙please…),看完后相信你会觉得这事太简单了,总的来说,我们只需要改动他read_settings,update_cache这两个方法。
         先来个简化的,不做任何可配置、容错检查以及排错支持,但骨架是全了:获取机器列表,并设置分组和变量信息,要做到这一点只需要添加两个函数,然后再盖一下update_cache就OK了:
         上面的代码可读性好到屌爆,也许你替换url后还真能用,不过大多数情况下,你得到了一堆exception,恩没错,你的代码需要更多的裹脚布,然后,你需要尽可能的把配置抽取出来,像我还顺手加点小功能,于是到最后我的脚本达到了400多行。。
         恩,你一定会说,累死我了,给我脚本我自己看吧,好吧,请摆驾这里
         简单的说明下,这个脚本除了上面的主体功能外,还包括以下酱油功能:
          ·   管理员可配置:API URL , 用户配置模板获取位置,合理超时区间,封禁用户,用户缓存路径
          ·   用户可配置:token,关心的机器tags(就是all怎么获得),超时间隔
enjoy!

5 Comments

  1. wilbur
    2014/03/18 at 6:37 下午

    我去,量产啊,可以用MD格式写,记着加下 more

  2. iambocai
    2014/03/18 at 8:04 下午

    哈哈,我加下

    • loneavon
      2014/04/20 at 9:16 下午

      楼主给力!哈哈

  3. 松涛
    2014/11/24 at 5:38 下午

    你的公司邮箱暴露在git上面了

    • wilbur
      2014/12/12 at 3:02 下午

      嗯,谢谢提醒

loneavon 进行回复 取消回复