当前位置: 首页 > Linux

Fabric源码学习:如何实现远程服务器的批量管理?

时间:2023-04-06 19:38:44 Linux

不久前,我写了一篇文章《Fabric教程》。简单的说,它是一个用Python开发的轻量级远程系统管理工具。它可以远程登录服务器,执行Shell命令,批量管理服务器,部署等场景,非常好用。Fabric2是其最新的主要版本。和早前的Fabric1有很大的不同,用起来也比较简单,但是还有相当多的坑没有填上……本文继续讲Fabric,不想面面俱到相反,关注这个话题:它是如何实现批处理服务器的串行/并发管理的?(友情提示:为了有更好的阅读体验,如果不了解Fabric的基本用法,建议先阅读前面的教程。)Fabric通过Group将多个服务器组合在一起。区别在于fabric.group.Group的基类(父类)派生的两个子类:并发方式的操作我们先来看看这个基类:我折叠了一些无用的信息。比较值得注意的内容是:Group继承list,所以可以extend(),而establishconnection核心run()方法没有实现,本意是留给子类去实现最后的__enter__()和__exit__()来实现上下文管理器。有了这个基类,接下来就是看SerialGroup和ThreadingGroup的具体实现了。SerialGroup类非常简单,只实现了一个run()方法。因为该类在初始化的时候已经为所有主机建立了连接并保存了,所以这里只需要使用for循环将它们一一取出,然后执行连接的run()方法即可。在这里可以看到一个很实用的开发技巧:在创建类的时候,让它继承内置的数据结构(比如list、dict),这样就可以直接使用self.append()、self.extend()、self.update()等方法将关键信息存储在“self”中,取出时再“forxxxinself”,这样就避免了创建临时list或dict,避免了在里面来回传递参数。GroupResult和GroupException是执行结果和异常的处理,不是我们关注的重点,这里略过。接下来看ThreadingGroup,它也只有一个run()方法:ExceptionHandlingThread是一个继承threading.Thread的类,这是一种创建多线程的方式。每个线程执行的方法主要做了两件事:执行连接的run()方法,将执行成功的结果存入队列。接下来将执行成功的结果和异常的结果分别存储在results中。所以Fabric采用threading多线程来实现并发。网络请求是IO密集型的,使用多线程是个好主意。至此,我们一开始提出的问题有了初步的答案:Fabric封装了两种Group来批量管理服务器。串行方式使用简单的for循环,而并发方式使用threading多线程。但是,通过分析这两个Groups的实现代码(和做法),我们也可以发现Fabric的缺陷:Group只实现了run()方法,却没有实现Connection的put()、get()、sudo()等方法。没有方法,就是说用这种方式管理服务器集群时,只能在上面执行shell命令……每次调用run()方法,都要等到所有主机都执行完才会返回结果,这意思是先执行完的主机会被阻塞。更致命的是,如果其中一台主机在执行过程中出现了异常,那么整个run()方法都会抛出异常,也就是说每次使用run()方法时,都需要进行异常捕获。run()方法支持执行单个shell命令,但不传递命令的状态。假设先在一个run()方法中运行cd命令切换到A目录(不是根目录),然后在下一个run()方法中创建一个文件。最后的结果是文件不在A目录下,而是在default目录下。解决方法是用“&&”连接多个命令,有点麻烦。这些问题在Fabric的Githubissue中被不同的人反复提出,但都没有得到很好的回应……言归正传,本文主要分析Fabric批量管理服务器的实现方案,阅读其源码,你就能明白串行/并发典型场景的使用,以及类定义、类继承、多线程、异常处理等。最后,我们也揭露了它的几个特性缺陷。谢谢阅读。最后附上Fabric教程:https://mp.weixin.qq.com/s/UHtPaxO2ojql5ps4hTn3Vg