书接上篇文章: ansbile中文指南 ,实验最后那个playbook时始终难以完成启动django项目的操作,于是就去跟随着ansible执行的过程,看了下源码。
不得不说的这个runner接口,这是ansible下层用来执行远程命令的一个接口,无论是上一篇说的Ad-Hoc命令的执行: ansible -i ~/hosts all -m command -a 'who' ,还是最后一个执行playbook的命令: ansible-playbook playbook.yml ,下面掉得都是这个接口。
在ansible官网文档的Python API处也是介绍的这个接口: Python API 。比如要执行上面那个Ad-Hoc的命令,直接调用这个接口的话得这么写:
import ansible.runner
runner = ansible.runner.Runner(
module_name='command',
module_args='who',
pattern='local',
forks=10
)
datastructure = runner.run()
print datastructure
打印出来的结果是这样的:
{
'dark': {
},
'contacted': {
'127.0.0.1': {
u'changed': True,
u'end': u'2014-02-25 22:11:16.566084',
u'stdout': u'Guest console Feb 1 16:29 \nthe5fire console Jan 20 19:50 \nthe5fire ttys023 Feb 25 22:11 \t(localhost)\nthe5fire ttys024 Feb 24 22:20 \t(localhost)',
u'cmd': [
u'who'
],
u'rc': 0,
u'start': u'2014-02-25 22:11:16.556888',
u'stderr': u'',
u'delta': u'0:00:00.009196',
'invocation': {
'module_name': 'command',
'module_args': 'who'
}
}
}
}
从这个地方我们可以了解到为什么对于受控制服务器需要安装simplejson,因为控制服务器和受控服务器都需要通过json格式进行数据传递的。
有了上面的认识,runner之上的东西就比较好理解了,都是业务相关的东西,比如:收集CLI(命令行界面)的数据、获取playbook.yml中的数据,以及对应的解析等等。
根据我的理解,画了一个大概的图出来,可以用来参考:
图上是一个大概的流程,只是针对playbook来说(Ad-Hoc的执行是直接掉得runner)。
首先ansbile-playbook接受到参数: playbook.yml,然后读取这个yml文件,根据这个yml文件生成Playbook对象,代码: class Playbook 。
在这个Playbook中加载yml文件,在执行时生成Play对象,在Play对象中又包含了Task对象,一个Task对象可以算是一个最小的执行单元。
到了Task这一步之后就应该调用runner接口了,这个接口的调用还是在Playbook这个类中: Playbook._run_task_internal 。而这个runner接口,上面已经介绍了,到此也就大体了解上层的执行过程了。
上面已经探索了ansible-playbook在执行时的流程,这里再继续深入了解一下,想看看ansible到底是如何执行的。
在runner这个类中,具体执行某一个task时是通过一个Action Module来完成的,这个action module是根据参数确定的,比如咱配置的是异步操作还是同步操作,这个不深究,默认是加载normal中的ActionModule,位置: normal 。在这个Action中对要执行的命令做了处理,对shell和command进行了处理,然后调用runner中的 self.runner._execute_module 来执行对应模块的操作,也就是playbook上写的git或者shell这样的模块。
到这整个过程其实还是挺无趣的,不过这个_execute_module里面的过程还是有点意思的。这个过程做了什么事呢?
首先,根据对应模块加载了library下面的对应的模块代码比如shell加载的是:library/commands/command这个代码,(这里要注意,上面加载的是normal的action模块,在那个模块里对shell进行了处理,变为command,因此这里就是command了)。找到这个具体的模块文件之后,ansible会加载一个module_common.py,对其进行渲染(把咱们定义的命令,比如:virtualenv ~demo,渲染到这个文件中)。
渲染完毕之后,会把这个文件copy到远程服务器的用户家目录下的.ansible/tmp/ansible-xxxxxx 这样的文件夹下(那个ansible-xxxx中xxx表示不知道是以什么方式生成的字符序列,可能是时间戳)。如果咱们定义的是一个shell,这里会多一个command的py文件,并且是可执行的。如果是git,这个文件名就是git。
传输完毕之后,就是执行了。ansible默认是以兼容的ssh来进行远程命令执行的,执行的方法就是,通过subprocess,来执行ssh和已经传输到远程服务器的可执行的python文件,通过PIPE的方式把执行结果输出回来,输出的CLI上。
大概就是这么个过程,只是大致的看了下整个的执行过程,很多地方复杂的逻辑都忽略了,最后的通过subprocess的方式执行ssh远程操作,并把结果通过PIPE输出回来不是特别理解其原理。
有兴趣的同学,如果耐心看了本文,并且也看了源码的话,不妨交流交流。