OpenStack中如何获取程序运行时状态

2014-07-30 Lingxian Kong 更多博文 » 博客 » GitHub »

原文链接 https://lingxiankong.github.io/2014-07-30-eventlet-backdoor.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


eventlet中的backdoor

OpenStack中编程模型中大量使用了GreenThread,使用eventlet库实现,关于eventlet可以参考官方文档。这里单独说一说backdoor的使用。

OpenStack每一个服务在创建时,都会根据backdoor_port配置项决定是否创建backdoor server,允许telnet到服务所在的节点进行调试,主要用于获取某个长时间运行的进程的状态。其原理是这样的:在程序的代码中,专门运行一个协程,这个协程一般不会被调度到,所以不会影响程序的正常运行。这个协程中跑了一个backdoor_server,比如下面的这行代码:

eventlet.spawn(backdoor.backdoor_server,eventlet.listen(('localhost', 3000)), locals=backdoor_locals)

这里的backdoor_locals是一个字典,key是某个字符串,而value就是对应的方法,在OpenStack中(nova-compute服务)定义如下:

backdoor_locals = {
    'exit': _dont_use_this,      # So we don't exit the entire process
    'quit': _dont_use_this,      # So we don't exit the entire process
    'fo': _find_objects,
    'pgt': _print_greenthreads,
    'pnt': _print_nativethreads,
}

由于这个是协程,所以我们的backdoor_server完全有能力修改程序中的内存变量。当这个程序运行起来后,通过telnet的方法可以连上这个程序,然后可以通过执行backdoor_locals中指定的方法来控制我们程序的运行行为(比如修改某个内存中变量的值)。

来看一个简单的例子:

from eventlet import backdoor
import eventlet

def _funca():
    print "abc"
    return "123"

backdoor_locals = {'funca': _funca}

eventlet.spawn(backdoor.backdoor_server, eventlet.listen(('localhost', 3000)),locals=backdoor_locals)

while True:
    print "aaa"
    eventlet.sleep(1)

当这个程序运行后,在另一个终端上执行下面的命令就可以看到对应的结果:

[root@ubuntu ~]# telnet 127.0.0.1 3000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Python 2.6.6 (r266:84292, Sep  4 2013, 07:46:00)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> funca()
abc
'123'
>>>

再看一个修改内存变量,改变程序行为的例子,下面这个例子在backdoor命令中修改全局变量,从而改变主程序的判断条件:

from eventlet import backdoor
import eventlet

_VAR = True

def _change_var():
    global _VAR
    _VAR = False
    print "OK"

backdoor_locals = {'func': _change_var}

eventlet.spawn(backdoor.backdoor_server, eventlet.listen(('localhost', 12223)),locals=backdoor_locals)

while _VAR:
    print "aaa"
    eventlet.sleep(1)    

运行程序:

root@ubuntu:~# python /home/kong/backdoor_test.py
aaa
backdoor server listening on 127.0.0.1:12223
aaa
aaa 
... 

在另一个console内登录主机,telnet连接,并运行func函数:

root@ubuntu:~# telnet 127.0.0.1 12223
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Python 2.7.3 (default, Feb 27 2014, 19:58:35) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> func()
OK
>>> Connection closed by foreign host.    

你会发现,运行完后,telnet自动断开了,是因为主程序退出,协程也就不存在了。

Guru Meditation Reports

简称GMR,关于Guru Meditaion,wikipedia上有详细的解释,大致意思就是系统宕机后的提示,比如windows系统出现致命问题后,大家看到的蓝屏。

为什么还需要GMR上呢?上面提到的backdoor有一些使用上的限制:

  • 每个需要backdoor的服务都需要预留一个TCP端口号,管理员需要一一记录;
  • 一旦服务重启,那么关键信息就会遗失;
  • 最后,也是最重要的,每个服务开一个端口,对系统安全性是一种极大的考验,所以backdoor在生产环境基本不会被采用;

关于GMR,我还没有上手测试过,没有过多的发言权。可以参见官方文档和一些老外写的博客:
https://wiki.openstack.org/wiki/GuruMeditationReport
http://docs.openstack.org/developer/nova/devref/gmr.html
https://www.berrange.com/posts/2015/02/19/nova-and-its-use-of-olso-incubator-guru-meditation-reports/