写给linux系统管理员看的systemd 三如何把SysV Init脚本转换成一个systmed的service文件 (systemd作者blog翻译过来的)

来源:互联网 发布:js展开收起多个div 编辑:程序博客网 时间:2024/05/16 23:47

原文地址:http://0pointer.de/blog/projects/systemd-for-admins-3.html


如何把SysV Init脚本转换成一个systmed的service文件?


传统方式,Unix和Linux服务(daemons)由SysV的init脚本启动.这些服务写成Bourne Shell脚本,通常驻留在像/etc/rc.d/init.d/这样的目录里,调用时有一个或几个参数,比如start, stop或restart, 用来控制服务的开始,停止和重启. 开始服务包括调用守护程序的代码, 守护程序再fork一个后台进程.Shell脚本很慢,难以阅读,冗长而脆弱. 尽管shell脚本极其灵活(毕竟,脚本本身就是代码)有些事儿很难拿脚本来完成,比如:安排并发执行的顺序,正确的监督进程或只是详尽的配置执行上下文. systmed可以与脚本兼容, 但下面我们马上就会提到,在安装所有守护进程的时候推荐安装原生的systemd服务文件. SysV 的启动脚本需要根据发行版的不同做出调整,systemd的服务文件则兼容任何发行版中运行的systemd. 接下来是一个简洁的入门手册:如何把SysV的shell脚本转换成一个systemd服务文件。理想情况下,上位的软件应该在tar包里发行和安装systemd的服务文件. 如果你已经根据入门手册成功的把某个上位软件SysV脚本转换成功了,一个不错的做法是把转换成功的文件作为补丁提交给那个上位软件. 后续的文章将会讨论如何准备这样一个补丁, 现在可以告诉你的是systemd的daemon(7)用户手册中有与之相关的很多有用信息.

开始吧,作为例子,我们将把ABRT守护进程的init脚本转换成systemd的服务文件。ABRT是每个Fedora安装版的标准组件,它是自动Bug报告工具(Automatic Bug Reporting Tool)的缩写,比如,它提供收据crash dumps服务。它的SysV脚本上传到这里了。

转换脚本的第一步是阅读它的代码并从中提取有用信息。几乎所有的init脚本中都有模板化的代码,通常这些代码就是从另一些脚本中拷贝过来的。下面开始抽取上面链接中脚本的有用信息。

  • 文件中的一个描述句子是“发现崩溃程序的守护进程”。其他很多注释是多余描述,它们不怎么描述服务本身,却描述启动服务的脚本文件。systmd服务也包含描述,描述的应该是服务内容而非服务文件。
  • LSB头[1] 包含服务的依赖关系信息,systemd是基于socket激活进行设计的,通常不需要(或者需要很少量的)手动配置依赖关系。(对socket激活的详细阐述请见这篇博客。本例所依赖的$syslog(表明abrtd需要用到syslog守护进程),只是个变量信息。头部列出的另一依赖关系($local_fs)对systemd来说是冗余项,因为系统服务开始时总是要求本地文件系统可用。
  • LSB头建议服务应该运行在运行级别3(多用户)或5(图形界面)
  • 守护进程的二进制文件是/usr/sbin/abrtd

有用的部分说完了。115行shell脚本的其他部分不是模板化的东西就是冗余代码:用来应付同步和顺序启动(比如,锁文件相关代码),或是输出状态信息(比如调用echo语句的代码),或者进行参数分析(比如case块儿)。

从上述抽取出的信息出发,我们现在可以写我们的systemd服务文件了。

[Unit]Description=Daemon to detect crashing appsAfter=syslog.target[Service]ExecStart=/usr/sbin/abrtdType=forking[Install]WantedBy=multi-user.target

这一文件的简单解释: [Unit] 节包含服务的通用信息. systemd 不止管理系统服务, 而且管理设备, mount 点, 计时器, 和其它系统组件. systemd中这些对象的通用名称是一个unit,[Unit]节中包含的信息不但可能用于服务,也可能用于systemd维护的其它unit类型。本例中我们进行如下的unit设置:设置描述项,并配置守护进程在Syslog后启动[2], 很像原init脚本中的LSB头。针对Syslog依赖关系,我们在systemd的Unit节创建了一个依赖类型After=systlog.target。syslog.target为systmed中专用的目标unit。它是syslog的标准化命名,对标准化命名的更多信息可见systemd.special(7).注意键入After=来创建的依赖关系只是一种建议顺序,它本身没有强制性:当abrtd启动时并不能引起syslog启动--这正是我们想要的,因为abrtd实际上在没有syslog时也能正常工作。但是,如果两者都启动了(通常情况如此),那么这一依赖关系决定了启动顺序。

接下来的一节是[Service]用来配置服务本身的信息。它包含只用于服务本身的设置,而非其它systemd所维护的units(mount点,设备,定时器等)。这里用了两个设置:ExecStart=定位服务启动时二进制文件的路径。Type=配置服务启动完成后如何通知init系统。由于传统的Unix守护进程fork完毕并完成后台守护进程初始化后返回父进程,我们就在这里设置forking类型。这告诉systmed等到启动的二进制文件返回后再考虑守护进程后面仍然运行的进程

最后一节是[Install]. 用于对如何安装给出建议,比如,服务在哪种环境下启动,如何被触发. 本例中服务将在muti-user.target unit激活后启动.muti-user.target是一个专用unit,基本上相当于SysV的运行级3[3].WantedBy= 设置项对运行时的守护进程没啥作用.systemd有个推荐的激活服务的命令,即systmectl enable,它会读取WantedBy=设置项. 此命令保证只要需要multi-user.target,我们制定的这个小服务就能自动启用. 正常的启动都需要multi-user.target[4].

就这些了.现在我们有了个最小的能工作的systmed服务文件.测试下吧, 把它拷贝到/etc/systemd/system/abrtd.service然后执行systemctl daemon-reload. 这会使systmed发现它, 现在我们可以启动它了:systemctl start abrtd.service.  我们能判断它现在是什么状态:systemctl status abrtd.service. 我们能停止它: systemctl stop abrtd.service. 最后,我们能启用它, 以后系统启动时它会自动激活:systemctl enable abrtd.service.

上面的服务文件,  是SysV init 脚本的1:1翻译, 仍有改进空间. 小改动下:

[Unit]Description=ABRT Automated Bug Reporting ToolAfter=syslog.target[Service]Type=dbusBusName=com.redhat.abrtExecStart=/usr/sbin/abrtd -d -s[Install]WantedBy=multi-user.target


看看我们修改了啥?两件事儿:描述项润色了一下.更重要的是,我们把服务类型改成了dbus,并给它配置了D-Bus总线名.为什么这么干?如前文所述,经典SysV服务启动后变成守护进程, 这一过程包括两次fork并与终端脱离. 当以脚本的方式启动守护进程的时候, 这样做是必要的(也是缓慢的),但在systemd这种要求精心照料其它进程的进程来看,这种做法就适得其反了(毕竟后台化,其全部思想就是移除systmed所需要管理的这种关系),fork完成后,区分哪个进程是服务进程的主进程, 哪个进程只是辅助进程, 对systemd来讲是十分困难的.但这些信息对于精心照料的要求来讲,是至关重要的, 比如监控某一进程,非正常退出时重启进程, 收集崩溃和退出码信息,诸如此类. 为了让systemd容易的区分出守护进程的主进程,我们更改service类型为dbus. 设置这一服务类型差不多就是说服务在初始化的最后一步以D-Bus系统总线命名[5]. 现在ABRT就是dbus类型了.这样设置后systemd将产生ABRT进程, ABRT进程将不再进行fork(配置成-d -s来进入守护进程),当com.redhat.abrt出现在总线上时,systemd就认为服务完全启动起来了.systemd用这种方式产生的进程就是守护进程的主进程, systemd就有了可靠的方法认定什么时候守护进程完全启动了并能容易的监视它.

全部设置就是这些了.我们有了一个10行的systemd服务文件,包含的信息超过了原SysV init脚本的115行代码.现在有更多的空间留给systemd将来提供更多的特性.比如,我们可以设置Restart=restart-always来告诉systemd在服务死掉时自动重启.或者,我们可以用 OOMScoreAdjust=-500来告诉内核当OOM killer(译者:Linux下有一种OOM KILLER 的机制,它会在系统内存耗尽的情况下,启用自己算法有选择性的kill 掉一些进程)施以暴行的时候,别动这个进程.或者,我们可以用CPUSchedulingPolicy=idle来保证abrtd进程只在后台进行crash dumps, 总准许内核去选择其他需要CPU时间的进程运行.

这里提到的配置选项的更多信息, 请见相应手册 systemd.unit(5), systemd.service(5), systemd.exec(5). 或者浏览 all of systemd's man pages.

当然,不是所有的SysV脚本都像这么容易就转换过来,但绝大多数都挺容易的.

就到这里了,下次见!

脚标说明:

[1] init脚本的LSB头是一种惯例:在SysV init脚本的顶部注释块中包含服务的元数据,这一惯例是在Linux Standard Base中规定的。意在规范不同发行版中的init脚本。尽管多数发行版都采用这样惯例,不同发行版对头部的处理千差万别,实际相同功能的脚本在每个发行版中的都需要进行调整。因而LSB规范从未信守诺言。

[2]严格讲,这一依赖关系其实不需要写在这里,它在systemd中是多余的,因为Syslog守护进程是socket可激活的。现代syslog系统(例如rsyslog v5)都已更新为socket可激活的。这样一个init系统用于After=syslog.target 依赖关系配置是多余的和暗含的。为了与那些未更新的syslog服务保持兼容,我们在这里加入这一依赖关系。

[3] 至少在Fedora中是这样

[4] systemd中的图型启动方式(graphical.target, 相当于SysV 中运行级 5)暗含字符节目启动(multi=user.target,比如运行级3).这意味着给后者增加一个服务相当于给前者也增加去了一个

[5]实际上现在的Fedora默认安装绝大多数服务都在启动完成后再以总线命名.


0 0