使用python封装sd_notify到systemd

        我们知道Centos7之后搁浅Sysvinit,改用复杂功能又及其强大,又特别惹人烦恼的systemd.  这个名字取得很大气,直接用system + d .   Sysvint从unix时期就已经早早的存在了,在各大国外的社区都很不少大牛针对init 和 systemd进行讨论,目前来看支持Sysvinit的人多点。  


       周边人对于systemd的看法.  有意思的是,我周边的开发人员都觉得无所谓,看了systemd的高级功能更是蠢蠢欲动。也确实systemd的功能对比init来说,复杂强大的多,比如并行化,unit, target ,socket , timer等等。   我以前的运维朋友,包括bat那帮人都不喜欢systemd,为什么?  又要学一个新东西,madan的。   咱们也不说什么高端的说法,不说systemd不符合unix的设计哲学,不符合简单美。    我个人也讨厌新东西,虽然他的新功能很是吸引人。   


该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新. http://xiaorui.cc/?p=3462


       前面虽然说的很直爽,但现实中因为各种各样的环境会导致你去用systemd。  不是因为systemd而去用centos7 ,而是因为centos7用systemd。  跟朋友一起开发一个开源的OpenApi,这与做什么这里先不说,保密。 这哥们蛋疼的想做一个systemd sd_notify的外部支持,用golang扩展一个模块。 文章的后面会告诉你,实现代码是怎么个简单粗暴。 


       systemd sd_notify是什么?  传统的服务化进程管理,我们只是能知道他活着,他挂了.  sd_notify可以做到一个运行程序跟systemd交互通信。 我主动告诉systemd我现在的状态,不仅仅是让systemd通过event pid粗浅的认为我的状态。   另外sd_notify也是可以发出类似进度的指令。 

里说下使用systemd遇到的问题

  1. 其实官方文档有说明 。 READY 只是有1 ,  一开始是stackoverload看了个sd_notify的说明有 0 2的值。   后来….    
  2. systemd是不会给程序传递信号,让他去reload,只能是你自己加信号解决。 
  3. 不要轻易让systemd把自己标注为ERROR的状态,原本只是想做个提示,结果发现systemd把你给干掉了。
  4. watchdog还是有点意思的,你可以申请一个watchdog dev设备,去定期喂数据,保持这个keep alive,他会改变定时器时间。  


更加详细的systemd文档,可以调到下面的连接。  再次感叹systemd真心是个复杂的工程。 

https://www.freedesktop.org/wiki/Software/systemd/ 

http://www.freedesktop.org/software/systemd/man/sd_notify.html

官方 systemd sd_notify 的参数, 我就不给你一一翻译了。


sd_notify() may be called by a service to notify the service manager about state changes. It can be used to send arbitrary information, encoded in an environment-block-like string. Most importantly, it can be used for start-up completion notification.

If the unset_environment parameter is non-zero, sd_notify() will unset the $NOTIFY_SOCKET environment variable before returning (regardless of whether the function call itself succeeded or not). Further calls to sd_notify() will then fail, but the variable is no longer inherited by child processes.

The state parameter should contain a newline-separated list of variable assignments, similar in style to an environment block. A trailing newline is implied if none is specified. The string may contain any kind of variable assignments, but the following shall be considered well-known:

READY=1
Tells the service manager that service startup is finished. This is only used by systemd if the service definition file has Type=notify set. Since there is little value in signaling non-readiness, the only value services should send is “READY=1” (i.e. “READY=0” is not defined).

RELOADING=1
Tells the service manager that the service is reloading its configuration. This is useful to allow the service manager to track the service’s internal state, and present it to the user. Note that a service that sends this notification must also send a “READY=1” notification when it completed reloading its configuration.

STOPPING=1
Tells the service manager that the service is beginning its shutdown. This is useful to allow the service manager to track the service’s internal state, and present it to the user.

STATUS=…
Passes a single-line UTF-8 status string back to the service manager that describes the service state. This is free-form and can be used for various purposes: general state feedback, fsck-like programs could pass completion percentages and failing programs could pass a human-readable error message. Example: “STATUS=Completed 66% of file system check…”

ERRNO=…
If a service fails, the errno-style error code, formatted as string. Example: “ERRNO=2” for ENOENT.

BUSERROR=…
If a service fails, the D-Bus error-style error code. Example: “BUSERROR=org.freedesktop.DBus.Error.TimedOut”

MAINPID=…
The main process ID (PID) of the service, in case the service manager did not fork off the process itself. Example: “MAINPID=4711”

WATCHDOG=1
Tells the service manager to update the watchdog timestamp. This is the keep-alive ping that services need to issue in regular intervals if WatchdogSec= is enabled for it. See systemd.service(5) for information how to enable this functionality and sd_watchdog_enabled(3) for the details of how the service can check whether the watchdog is enabled.

systemd官方也提供了systemd的一些使用样例:


启动就绪通知

sd_notify(0, "READY=1");

这个接口也有扩展模式的,你可以把ststus,mainPid等信息都告诉systemd

#xiaorui.cc
sd_notifyf(0, "READY=1\n"
        "STATUS=Processing requests from xiaorui.cc   ...\n"
        "MAINPID=%lu",
        (unsigned long) getpid());

Error信号通知,  systemd收到该请求后,会重置该单元的状态,后面你可以通过systemctl查询他们的状态。

sd_notifyf(0, "STATUS=Failed to start up: %s\n"
        "ERRNO=%i",
        strerror(errno),
        errno);

记录通知 文件描述符

sd_pid_notify_with_fds(0, 0, "FDSTORE=1\nFDNAME=xiaorui.cc", &fd, 1);

到此为止我们已经把systemd sd_notify信号描述完了。 那么说说python是怎么跟systemd发送sd_notify信号数据的。   最一开始没有绕过弯来, 寻思使用python ctypes来调用systemd的c接口。 虽然也实现了信号的发送,但是通过打印字节和strace发现,Systemd 暴露出的接口居然也是socket套接字, 有点奇怪。 既然都是socket,还不如干脆直接调用python socket来解决sd_notify的发送。 

下面是python的逻辑代码,实现相当的简单,就是创建socket连接对象,然后send数据,然后就没了。  

#coding:utf-8
import socket
import os
import sys

if sys.version_info < (3,):
    def _b(x):
        return x
else:
    import codecs
    def _b(x):
        return codecs.latin_1_encode(x)[0]


class SystemdSend(object):

    def __init__(self, debug=False):
        self.debug = debug
        try:
            self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
            addr = os.getenv('NOTIFY_SOCKET')
            if addr[0] == '@':
                addr = '\0' + addr[1:]
            self.socket.connect(addr)
        except:
            self.socket = None
            if self.debug:
                raise

    def notify(self, state):
        try:
            self.socket.sendall(_b(state))
        except:
            if self.debug:
                raise

python的调用方法很简单,创建连接对象后,根据不同的状态send不同的参数就可以了。 

#xiaorui.cc
import time

from systemd import SystemSend


print("Test starting up...")

time.sleep(10)
print("Test startup finished")

n = SystemSend()
n.notify("READY=1")

count = 1
while True:
    print("Running... {}".format(count))
    n.notify("STATUS=Count is {}".format(count))
    count += 1
    time.sleep(2)

END.


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc

发表评论

邮箱地址不会被公开。 必填项已用*标注