提供:ZStack云计算
本教程为如何配置一项在崩溃或重启后自动启用的Linux服务系列两篇中的第二篇。
在本文中,我们将更为具体地解释上篇教程中提到的各init进程,帮助大家更好地理解其如何控制守护程序的start-up行为。
在第一部分中,我们探讨了如何利用MySQL在崩溃或重启情况下实现Linux服务的自动启用。
在前篇文章内,我们谈到三种不同的init模式,即System V、Upstart与systemd。参阅第一部分回顾各发行版中使用的默认init系统。
在这里,我们将更为详尽地说明流程中所使用的命令与需要编辑的配置文件。首先从System V init守护程序入手,了解其为何逐步被新型init模式所替代。
要完成本篇教程,大家首先需要根据上一篇内容构建三套DigitalOcean Droplets。
具体包括:
另外,我们还需要root用户或者拥有sudo权限的非root用户。要了解sudo权限相关内容,请参阅此篇教程。
请注意,大家不应将本篇教程中提及的任何命令、查询或者配置应用于生产Lnux服务器。
一条runlevel代表的是一套Linux系统的当前状态。
其概念源自System V init,具体步骤为Linux系统引导、初始化内核、进入某种(惟一一种)runlevel。
例如,runlevel可以代表Linux服务器的停机状态、单用户模式以及重启模式等等。每种模式都决定了能够运行于该状态下的具体服务。
部分服务能够运行在多种runlevel之下,但部分服务只能匹配一种runlevel。
Runlevels由单一数字表现,取值范围为0到6,具体含义如下:
Runlevels 2, 3与4在不同发行版中有所区别。例如,某些Linux版本不提供runlevel 4,而有些则提供。部分发行版在这三种级别之间拥有区分。总体来讲,runlevel 2,3与4代表Linux在多用户、网络功能启用的文本模式下运行。
当将服务设定为auto-start时,我们需要将其添加至runlevel当中。在System V中,系统会以特定runlevel启动,并同时启动全部与该runlevel相关联的服务。
在systemd中,runlevel则被替换为target,我们将在后文中进行说明。
init为Linux系统在完成设备引导及内核加载后的首个进程。
它的作用是决定用户进程或者系统服务如何加载、采用怎样的顺序以及是否自动启动。
Linux中的每个进程都拥有一个进程ID(PID),而init的PID为1。它是其它全部进程的父进程。
Init的历史
随着Linux的逐步演进,init守护程序也同时发生着变化。最初,Linux使用的是System V init,与Unix保持一致。在此之后,Linux开始使用Upstart init守护程序(由Ubuntu创建),如今则转换为systemd init守护程序(最初来自Fedora)。
大部分Linux发行版都已经移除了System V或者以自己的方式将其淘汰,只保留向下兼容能力。作为Unix的变种,FreeBSD采用的就是BSD init。Debian的早期版本使用的正是SysV init。
不同init版本拥有着不同的服务管理方式。这些变化背后的原因在于对强大服务管理工具的需求,用户需要其在服务之外,同时管理设备、端口及其它资源,意味着init必须并行加载资源且能够从崩溃中顺利实现恢复。
System V使用inittab文件,而Upstart等后续init方案继续保留向下兼容能力。
下面来看看System V的启动流程:
在读取runlevel相关init脚本时,init守护程序实际上是在搜索需要启动的各项服务。这些init脚本负责指定各服务的配置启动行为。
下面具体谈谈init脚本。
init脚本负责控制System V中的特定服务,例如MySQL Server。
Init脚本可由应用供应方或者Linux发行版(针对原生服务)负责提供。我们也可以为定制化服务创建自己的init脚本。
当某一进程或者服务启动时,其二进制程序文件需要被加载至内存中。
根据实际服务配置,此程序可能需要持续存在于后台(并接收客户端连接)。而二进制应用的启动、停止或者重载则由该服务的init脚本实现,而之所以称其为init脚本,是因为其负责对该服务进行初始化(initialize)。
在System V中,init脚本属于shell脚本。
Init脚本亦被称为rc(即run命令)脚本。
/etc目录为init脚本的主目录。各init shell脚本的实际位置保存在/etc/init.d。这些脚本被符号链接至rc目录处。
在/etc目录中存在着多个rc目录,其名称中皆包含一个数字。这些数字代表着不同runlevels,因此实际名称可能为/etc/rc0.d、rc1.d等等。
在每个rcn.d目录内,我们都能够找到以K或者S作为文件名开头的文件,字母之后则是两位数字。这些符号链接文件指向回具体init shell脚本。为什么用K与S?K代表Kill(即停止),而S代表Start。
两位数字则为该脚本的执行顺序。因此,如果我们的文件名为K25,那么其执行优先级高于K99脚本。
下面再来看启动序列。这些init脚本如何调用?又由谁来调用?
K与S脚本并非直接由init守护程序调用,而是接受另一脚本的调用:/etc/init.d/rcscript。
大家可能还记得,/etc/inittab文件告知init守护程序当前系统应默认使用怎样的runlevel。对于每种runlevel,/etc/inittab文件内都有一行负责调用/etc/init.d/rc脚本,并将该runlevel作为参数进行传递。以该参数为基础,脚本随后调用对应/etc/rcn.d目录下的文件。因此,如果服务器以runlevel 2引导,那么/etc/rc2.d下的脚本就会得到调用。
而在rc目录内,首先全部K脚本按次序配合“stop”参数运行,而后是全部S脚本配合“start”参数以同样的方式运行。这意味着各init shell脚本将分别以stop与start作为参数接受调用。
由于/etc/rcn.d目录下的各文件(Knn与Snn文件)仅属于符号链接,因此调用它们意味着实际调用的是对应init shell脚本。
总体来讲,当Linux服务器进入某runlevel时,特定脚本将运行以停止某些服务,其它一些脚本则运行以启动某些服务。
这种init脚本调用方式同样存在于新runlevel的系统切换过程中,具体流程同上。
整个流程能够确保任何不应运行在当前runlevel中的服务被停止,而全部应运行在该runlevel下的服务得到启动。
在引导时将某项服务设定为auto-start,则意味着对其init行为加以修改。
例如,当我们将某服务在runlevel 3中进行自动启动,则相当于在/etc/rc3.d目录下创建对应的链接。
听起来有点复杂,不过别担心,下面我们通过示例进行理解。
下面来看我们的MySQL服务示例,这一次我们着重讲解其中的理论内容。
出于教程需要,我们使用第一部分中创建的Debian 6 Droplet。使用SSH命令接入该服务器(Windows用户亦可使用PuTTy等工具接入)。
运行以下命令查看inittab文件内容:
输出结果如下:
Output
id:2:initdefault:
id字段中的2代表系统配置为runlevel 2,即其默认runlevel。在本示例中,Debian的runlevel 2代表多用户文本模式。如果大家执行以下命令:
则输出结果如下:
Output
# Runlevel 0 is halt.
# Runlevel 1 is single-user.
# Runlevels 2-5 are multi-user.
# Runlevel 6 is reboot.
运行以下命令列出全部rc目录:
ls -ld /etc/rc*.d
Output
drwxr-xr-x 2 root root 4096 Jul 31 07:09 /etc/rc0.d
drwxr-xr-x 2 root root 4096 Jul 31 07:09 /etc/rc1.d
drwxr-xr-x 2 root root 4096 Jul 31 07:21 /etc/rc2.d
drwxr-xr-x 2 root root 4096 Jul 31 07:21 /etc/rc3.d
drwxr-xr-x 2 root root 4096 Jul 31 07:21 /etc/rc4.d
drwxr-xr-x 2 root root 4096 Jul 31 07:21 /etc/rc5.d
drwxr-xr-x 2 root root 4096 Jul 31 07:09 /etc/rc6.d
drwxr-xr-x 2 root root 4096 Jul 23 2012 /etc/rcS.d
由于系统引导为runlevel 2(inittab文件中的默认init),因此系统启动时直接使用/etc/rc2.d目录下的脚本。
列出此目录内容:
此目录中只包含符号链接,各自指向/etc/init.d目录下的对应脚本文件:
Output
. . .
lrwxrwxrwx 1 root root 17 Jul 23 2012 S01rsyslog -> ../init.d/rsyslog
lrwxrwxrwx 1 root root 22 Jul 23 2012 S02acpi-support -> ../init.d/acpi-support
lrwxrwxrwx 1 root root 15 Jul 23 2012 S02acpid -> ../init.d/acpid
lrwxrwxrwx 1 root root 17 Jul 23 2012 S02anacron -> ../init.d/anacron
lrwxrwxrwx 1 root root 13 Jul 23 2012 S02atd -> ../init.d/atd
lrwxrwxrwx 1 root root 14 Jul 23 2012 S02cron -> ../init.d/cron
lrwxrwxrwx 1 root root 15 Jul 31 07:09 S02mysql -> ../init.d/mysql
lrwxrwxrwx 1 root root 13 Jul 23 2012 S02ssh -> ../init.d/ssh
. . .
可以看到,其中不包含K脚本,只有S(即start)脚本。这些脚本负责启动rsyslog、cron以及ssh等服务。
请注意,S之后的两位数字代表启动顺序:例如,rsyslog启动优先级高于cron守护程序。
现在我们已经理解了System V下各服务的安装方式,其会在/etc/init.d目录下创建shell脚本。下面查看MySQL的shell脚本:
输出结果
-rwxr-xr-x 1 root root 5437 Jan 14 2014 /etc/init.d/mysql
读取该文件以查看start-up脚本的具体内容:
在结果中,可以看到这是一套规模可观的bash脚本。
在CentOS这类基于RHEL的发行版中,我们可以使用chkconfig以启用或禁用System V中的服务。它还能够列出已经安装的服务及其runlevel。
检查CentOS系统下全部runlevels的服务状态,可使用以下语法:
Debian自身并不提供这样的工具(update-rc.d只能从runlevel处安装或移除服务),因此我们需要安装sysv-rc-conf工具进行服务管理。
安装sysv-rc-conf:
工具安装完成后,执行该命令以查看各服务的runlevel行为:
输出结果如下所示。在这里,我们可以看到哪些服务适用哪些runlevels(以X标记)。
使用箭头键与空格键,我们可以禁用或启用各项服务的runlevels.
现在按下Q退出此屏幕。
可以看到,MySQL目前可在runlevel 2到5中启用。
运行以下命令以禁用MySQL服务:
sudo update-rc.d mysql disable
Output
update-rc.d: using dependency based boot sequencing
insserv: warning: current start runlevel(s) (empty) of script mysql' overwrites defaults (2 3 4 5).
mysql’ overwrites defaults (0 1 6).
insserv: warning: current stop runlevel(s) (0 1 2 3 4 5 6) of script
现在运行:
输出结果应显示由/etc/rc2.d到/etc/init.d/mysql下的符号链接被变更为K:
Output
. . .
lrwxrwxrwx 1 root root 15 Jul 31 07:09 K02mysql -> ../init.d/mysql
. . .
换言之,现在MySQL将不会在默认runlevel(2)下启动。
这就是我们在System V中启用及禁用服务时,背后的整个调整过程。只要目标服务在默认runlevel目录下拥有S脚本,init就会在引导时对其加以启动。
再次启动服务:
第八步——测试崩溃时的MySQL Start-up行为
下面来看System V如何处理服务崩溃。
我们曾在第一部分教程中对/etc/inittab文件进行过变更,从而使MySQL在发生崩溃时自动恢复。现在添加以下行:
/etc/inittab
ms:2345:respawn:/bin/sh /usr/bin/mysqld_safe
这能帮助MySQL服务在崩溃后自动恢复。为检查其效果,我们重启服务器:
当服务器恢复运行后,SSH会介入并检查MySQL进程ID:
请注意mysqld_safe与mysqld的进程ID。在本示例中,二者分别为895与1019:
Output
root 907 1 0 07:30 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql 1031 907 0 07:30 ? 00:00:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --user=mysql --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=3306
root 1032 907 0 07:30 ? 00:00:00 logger -t mysqld -p daemon.error
root 2550 2532 0 07:31 pts/0 00:00:00 grep mysql
使用-9开关再次关闭该进程:
sudo kill -9 1031
等待五分钟左右,再次执行该命令:
输出结果显示MySQL已经开始运行:
Output
/usr/bin/mysqladmin Ver 8.42 Distrib 5.1.73, for debian-linux-gnu on x86_64
如果再次运行ps -ef | grep mysql命令,则可看到mysqld_safe与mysqldprocessess已经恢复运行。
多试几次,结果应该始终一致。
这种在/etc/inittab中添加额外行的方式,正是配置System V服务实现崩溃后恢复的手段。大家可以参阅第一部分内容了解具体语法构成。
另外需要注意的是,如果某项服务在2分钟之内崩溃并尝试恢复的次数超过十次,则Linux会在接下来的五分钟中禁用其恢复尝试。如果大家在控制台中收到了这样的事件,请立即检查对应应用——其已经开始发生持续崩溃。
经典SysV init长久以来一直存在于主流Linux发行版本当中,直到Upstart的出现。
随着Linux市场的不断发展,人们发现序列化加载任务及服务开始占用大量时间且变得愈发复杂。与此同时,越来越多现代服务(例如可插拔存储介质)开始出现,意味着SysV init已经很难对其进行快速处理。
在这样的情况下,Ubuntu率先迈出了尝试的脚步,Upstart作为另一种初始化守护程序正式出现。
Upstart init在以下几个方面优于System V:
Upstart允许多个事件与单一服务相关联。这套基于事件的架构允许Upstart更灵活地处理服务管理工作。
每个事件都能关闭负责管理该事件的shell脚本。
Upstart事件包括:
在这些事件中,单一服务可还可处于其它状态,例如:
Upstart也能够执行这些事件,从而实现极为灵活的架构。
与System V类似,Upstart同样会在启动时运行/etc/init.d/rc脚本。该脚本能够正常执行System V init脚本。
Upstart还会查看/etc/init目录并执行各服务配置文件中的shell命令。
Upstart利用配置文件实现服务控制。
Upstart并不像System V那样使用Bash脚本,而使用服务配置文件,其标准名称为service_name.conf。
该文本由纯文本构成,其中包含多个部分,名为stanzas。每个stanza负责描述服务的一个方面及其行为方式。
不同的stanzes控制服务中的不同事件,例如pre-start、start、pre-stop或者post-stop。
这些Stanzas本身包含有shell命令。因此,我们可以为每项服务中的每个事件调用多种操作。
每个配置文件还指定以下两项内容:
Upstart配置文件位于/etc/init目录内。
下面看看Upstart如何处理MySQL Server服务。
返回我们在第一部分中创建的Ubuntu 14.04 Droplet,使用SSH命令接入该服务器。
大部分Upstart配置文件位于/etc/init目录。我们需要利用它创建新服务。
登录至服务器后执行以下命令:
结果显示大量服务配置文件,每次一屏。以下为Upstart下的原生运行服务:
Output
total 356
. . .
-rw-r--r-- 1 root root 297 Feb 9 2013 cron.conf
-rw-r--r-- 1 root root 489 Nov 11 2013 dbus.conf
-rw-r--r-- 1 root root 273 Nov 19 2010 dmesg.conf
. . .
-rw-r--r-- 1 root root 1770 Feb 19 2014 mysql.conf
-rw-r--r-- 1 root root 2493 Mar 20 2014 networking.conf
按Q退出。
将其与系统中的原生System V init服务进行比对:
结果更为直观:
Output
-rw-r--r-- 1 root root 677 Jun 14 23:31 /etc/rc3.d/README
lrwxrwxrwx 1 root root 15 Apr 17 2014 /etc/rc3.d/S20rsync -> ../init.d/rsync
lrwxrwxrwx 1 root root 24 Apr 17 2014 /etc/rc3.d/S20screen-cleanup -> ../init.d/screen-cleanup
lrwxrwxrwx 1 root root 19 Apr 17 2014 /etc/rc3.d/S70dns-clean -> ../init.d/dns-clean
lrwxrwxrwx 1 root root 18 Apr 17 2014 /etc/rc3.d/S70pppd-dns -> ../init.d/pppd-dns
lrwxrwxrwx 1 root root 26 Apr 17 2014 /etc/rc3.d/S99digitalocean -> ../init.d//rc.digitalocean
lrwxrwxrwx 1 root root 21 Apr 17 2014 /etc/rc3.d/S99grub-common -> ../init.d/grub-common
lrwxrwxrwx 1 root root 18 Apr 17 2014 /etc/rc3.d/S99ondemand -> ../init.d/ondemand
lrwxrwxrwx 1 root root 18 Apr 17 2014 /etc/rc3.d/S99rc.local -> ../init.d/rc.local
我们已经在第一部分教程中查看了mysql.conf文件,现在打开另一个配置文件——cron守护程序配置文件:
可以看到,cron的配置文件相当简单:
/etc/init/cron.conf
# cron - regular background program processing daemon
#
# cron is a standard UNIX program that runs user-specified programs at
# periodic scheduled times
description "regular background program processing daemon"
start on runlevel [2345]
stop on runlevel [!2345]
expect fork
respawn
exec cron
这里最重要的字段为start on、stop on与respawn。
指令中的start告知Ubuntu在系统处于runlevel 2、3、4或5时启动crond守护程序。2、3与4为多用户文本模式且启用网络功能,而5则为多用户图形模式。该服务无法运行在其它runlevel之下(例如0、1或6)。
而fork指令告知Upstart那些需要从控制台中分离并在后台运行的进程。
下面是respawn指令,它负责告知系统cron应当在崩溃后自动恢复。
不要变更文件内容,直接退出。
显然,cron配置文件非常简单,而MySQL配置文件虽然结构类似但更为复杂。除了start、stop与respawn之外,它还包含两套脚本块用于处理pre-start与post-start事件。这些代码块告知系统应在mysqld进程启动或者已经处于运行时执行怎样的操作。
关于如何构建自己的Upstart文件,请参阅本篇Upstart教程。
我们都知道,MySQL实例在Ubuntu 14.04服务器上被默认为引导时自动启动。下面看看如何禁用自动启动。
在Upstart中,禁用服务需要使用/etc/init/下的service_name.override文件。该文件的内容非常简单:
manual
要了解如何利用此文件禁用MySQL,执行以下命令为MySQL创建override文件:
添加以下一行:
/etc/init/mysql.override
manual
保存变更。
而后启动服务器:
服务器重新运行后,检查该服务状态:
输出结果应为:
Output
mysql stop/waiting
这意味着MySQL并未运行。
检查MySQL服务配置文件内的start指令是否已经变更:
结果应该相同:
Output
start on runlevel [2345]
意味着在init目录下检查.conf文件并不是惟一影响服务是否自动启动的因素,因此需要确保不存在.override文件。
要重新启用MySQL,需要删除override文件并重启服务器:
sudo rm -f /etc/init/mysql.override
sudo reboot
服务器重启后,进行远程登录。
运行sudo initctl status mysql命令,可以看到该服务已经自动启动。
在默认情况下,MySQL会在崩溃后自动重启。要停止其自动重启,打开/etc/init/mysql.conf配置文件:
将两条respawn指令修改为注释:
/etc/init/mysql.conf
# respawn
# respawn limit 2 5
运行以下命令重启服务:
sudo initctl stop mysql
sudo initctl start mysql
我们需要明确关闭及启动该服务,因为测试显示initctl restart或initctl reload命令在这里并不起效。
第二条命令用于显示MySQL的PID:
Output
mysql start/running, process 1274
注意MySQL实例的PID。如果在这里崩溃掉mysql进程,那么其无法自动恢复。关闭进程ID:
检查其状态
sudo initctl status mysql
Output
mysql stop/waiting
多试几次,大家会发现MySQL确实无法恢复运行,这是因为该服务的配置文件中已经不存在respawn指令。
第一部分教程中包含有关于respawn指令的详尽解释。
那么我们什么时候会希望Upstart服务在重启或崩溃后不再自动恢复?
假定我们已经对Linux内核进行了更新或者安装了最新补丁,这时需要不受任何额外干扰地单纯重启服务器。通过禁用auto-start项目,我们能够显著降低升级风险。
如果大家的服务能够恢复但很快再次崩溃,也需要变更其respawn指令以检查问题原因。
最后一款Linux init守护程序为systemd。事实上,它并不仅仅属于init守护程序:systemd是一套新型框架,综合了大量现代Linux系统中的组件。
其主要功能之一就是作为Linux的系统与服务管理器存在。systemd能够控制服务在崩溃或者设备重启后的操作。具体请参阅systemd的systemctl工具一文。
systemd向下支持System V命令及初始化脚本。这意味着任意System V服务都能够运行在systemd之下。这是因为大多数Upstart与System V管理命令都已经针对systemd进行了修改。
事实上,如果我们在系统上运行ps -ef | grep systemd命令,其不会返回任何结果。因为systemd在引导时将自身重命名为init。/sbin/init文件作为符号链接指向/bin/systemd。
systemd的核心在于unit文件。每个unit文件代表着一种系统资源。systemd与另外两种init方案的主要区别在于,systemd不仅负责初始化服务守护程序,同时也面向嵌套、设备操作系统路径、安装点等资源类型。
每个unit文件都代表一种特定系统资源,其名称为service name.unit。
因此我们会看到dbus.service、sshd.socket或者home.mount这样的文件名。
在CentOs等基于红帽的系统中,unit文件存在于两个位置。其主位置为/lib/systemd/system/。
定制化unit文件或者现有unit文件都可由系统管理员修改,其保存于/etc/systemd/system目录下。
如果两个目录中存在同名文件,systemd会使用位于/etc下的那个。如果某服务可在引导时或其它target/runlevel条件下启动,则创建符号链接以指向/etc/systemd/system内对应目录中的服务unit文件。/etc/systemd/system下的unit文件实际上属于指向/lib/systemd/system下同名文件的符号链接。
这里要提一种特殊的unit文件类型,即target unit。
这类target unit的后缀名为.target。Target unit与其它unit文件不同,因为它们并不代表某种特定资源。相反,它们代表的是系统在任意时间内所处的状态。
Target unit会将多个unit文件进行分组与启动,并将其作为该状态的组成部分。systemd target因此能够在一定程度上与System V runlevel进行比较,当然二者并不尽相同。
每个target都拥有一个名称而非数字。例如,我们使用multi-user.target来代替runlevel 3。
当Linux服务器启动时,multi-user.target可能负责将服务器引导至runlevel 2、3或4,即启用网络功能的多用户文本模式。
具体实现方式正是二者的差异所在。与System V不同,systemd并不会按顺序启动服务。在此过程中,它会检查其它现有服务或资源,并决定其加载顺序。这意味着系统能够并行加载服务。
另一点不同在于,一套Linux系统只能存在于一种runlevel内,而target unit却并非惟一,意味着单一target unit活动时还可将其它target作为其组成部分进行加载。
例如,Linux系统在graphical.target激活时会引导一套图形用户界面,而其反过来又会自动确保multi-user.target加载并处于激活。
以下为runlevel与target的对比表格:
Runlevel (System V init)Target Units (Systemd)
runlevel 0 poweroff.target
runlevel 1 resuce.target
runlevel 2, 3, 4 multi-user.target
runlevel 5 graphical.target
runlevel 6 reboot.target
default.target相当于默认runlevel。
在System V中,默认runlevel由inittab文件定义。在systemd中,该文件则被替换为default.target。该默认target unit文件存在于/etc/systemd/system目录内。其属于符号链接,指向/lib/systemd/system目录下的target unit文件。
在变更该默认target时,我们实际上重新创建了该符号链接并变更了系统的runlevel。
System V中的inittab文件还指定Linux执行init脚本的来源目录。而在systemd中,默认target unit则会检测引导时将要加载的各资源units。
全部units都会被激活,但并非全部以并行或者序列方式激活。资源unit的具体加载方式取决于它所需要的其它资源unit。
之所以对unit文件与target文件进行探讨,是为了强调systemd如何处理不同守护程序之间的依赖性。
如之前所提到,Upstart利用配置文件确保服务的并行加载。在System V中,服务可以在特定runlevel下启动,但同时亦可等待其它服务或资源可用后再行启动。同样的,systemd服务也可加载至一个或者多个targets当中,或者等待其它服务或资源开始活动后再行启动。
在systemd中,要求另一unit方可正常运行的unit无法早于前者进行加载。另外,如果运行所需要的unit发生故障,但与之关联的unit仍处于活动,则后者亦同时停止。
现在让我们看systemd中的MySQL启动行为。
为了理解各概念及服务自动启动机制,我们再次使用第一部分中创建的CentOS 7 Droplet。
使用SSH命令接入该服务器。
这部分内容很长,因为我们需要尽可能多地深入查看.target设计方式。systemd的启动序列包含一套极长的依赖性链。
defaul.target
default.target文件控制服务器常规引导时,哪些服务会一同启动。
执行以下命令显示该默认target unit文件:
输出结果如下:
Output
lrwxrwxrwx. 1 root root 37 Jul 8 2014 /etc/systemd/system/default.target -> /lib/systemd/system/multi-user.target
可以看到,默认的target属于指向/lib/systemd/system/下多用户target文件的符号链接。因此,该系统应当在multi-user.target之下引导,类似于runlevel 3。
multi-user.target.wants
下面,执行命令以检查multi-user.target文件想要的服务:
输出结果如下:
Output
. . .
lrwxrwxrwx. 1 root root 37 Jul 8 2014 /etc/systemd/system/multi-user.target.wants/crond.service -> /usr/lib/systemd/system/crond.service
. . .
lrwxrwxrwx 1 root root 38 Jul 31 22:02 /etc/systemd/system/multi-user.target.wants/mysqld.service -> /usr/lib/systemd/system/mysqld.service
lrwxrwxrwx. 1 root root 46 Jul 8 2014 /etc/systemd/system/multi-user.target.wants/NetworkManager.service -> /usr/lib/systemd/system/NetworkManager.service
lrwxrwxrwx. 1 root root 39 Jul 8 2014 /etc/systemd/system/multi-user.target.wants/postfix.service -> /usr/lib/systemd/system/postfix.service
lrwxrwxrwx. 1 root root 39 Jul 8 2014 /etc/systemd/system/multi-user.target.wants/rsyslog.service -> /usr/lib/systemd/system/rsyslog.service
lrwxrwxrwx. 1 root root 36 Jul 8 2014 /etc/systemd/system/multi-user.target.wants/sshd.service -> /usr/lib/systemd/system/sshd.service
. . .
其中全部为符号链接文件,指回/lib/systemd/system/下的实际unit文件。我们可以看到,mysqld.service属于multi-user.target的组成部分。
执行以下命令过滤输出结果可找到同样的信息:
sudo systemctl show –property “Wants” multi-user.target | fmt -10 | grep mysql
Output
mysqld.service
除了multi-user.target,target还拥有多种类型,例如system-update.target以及basic.target。
要查看multi-user target所依赖的各targets,执行以下命令:
sudo systemctl show –property “Requires” multi-user.target | fmt -10
Output
Requires=basic.target
这样系统在启动为multi-user.target模式前,会首先加载basic.target。
basic.target
要查看basic.target的其它依赖性,执行以下命令:
输出结果为:
Output
Requires=sysinit.target
sysinit.target
下面来看sysinit.target所需要的units。虽然结果显示没有必要的unit,但我们可以进一步查看其想要的服务:
其会显示sysinit想要的服务数量。
Output
Wants=local-fs.target
swap.target
cryptsetup.target
systemd-udevd.service
systemd-update-utmp.service
systemd-journal-flush.service
plymouth-read-write.service
. . .
可以看到,系统无法只依赖于单一target运行。其会根据不同targets之间的事务决定需要加载的服务。
再进一步,我们看看服务的unit文件。这里以sshd的unit文件为例:
内容如下:
Output
[Unit]
Description=OpenSSH server daemon
After=syslog.target network.target auditd.service
[Service]
EnvironmentFile=/etc/sysconfig/sshd
ExecStartPre=/usr/sbin/sshd-keygen
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target
与Upstart守护程序的配置文件类似,此服务unit文件明确且易于理解。
首先需要理解的是After子句。其表达的是SSHD服务需要在系统与网络targets外加审计登录服务加载完成后,才能进行加载。
该文件还显示此服务属于multi-user.target想要的条目,意味着此target会加载该服务,但其不会导致sshd发生故障。
由于multi-user.target属于默认target,sshd守护程序会在引导时启动。
退出编辑器。
在第一部分教程中,我们已经开始运行MySQL服务,下面来看如何进行调整。
在上一章节里,我们运行了一条命令并确认mysqld.service属于multi-user.target_想要_的服务。而后,我们列出了/etc/systemd/system/multi-user.target.wants/目录下的内容,发现其中一条符号链接指回/usr/lib/systemd/system/下的原始服务unit。
运行以下命令禁用该服务,确保其不会在引导时自动运行:
现在运行以下命令,检查MySQL是否仍然为multi-user.target“想要”的服务:
没有返回任何结果。运行以下命令检查符号链接是否仍然存在:
链接已经不存在:
Output
ls: cannot access /etc/systemd/system/multi-user.target.wants/mysql*: No such file or directory
现在重新启用该服务:
链接也会正常恢复:
sudo ls -l /etc/systemd/system/multi-user.target.wants/mysql*
Output
lrwxrwxrwx 1 root root 38 Aug 1 04:43 /etc/systemd/system/multi-user.target.wants/mysqld.service -> /usr/lib/systemd/system/mysqld.service
可以看到,我们能够在默认target的wants目录下启用或禁用systemd服务,同时创建或删除符号链接。
MySQL会在崩溃后自动恢复,下面看看如何禁用这一机制。
打开MySQL服务的unit文件:
在标题信息之后,文件内容如下:
/etc/systemd/system/multi-user.target.wants/mysqld.service
[Unit]
Description=MySQL Community Server
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
Alias=mysql.service
[Service]
User=mysql
Group=mysql
# Execute pre and post scripts as root
PermissionsStartOnly=true
# Needed to create system tables etc.
ExecStartPre=/usr/bin/mysql-systemd-start pre
# Start main service
ExecStart=/usr/bin/mysqld_safe
# Don't signal startup success before a ping works
ExecStartPost=/usr/bin/mysql-systemd-start post
# Give up if ping don't get an answer
TimeoutSec=600
Restart=always
PrivateTmp=false
如第一部分所见,Restart参数的值被设定为always(对于sshd,其被设定为on-failure only)。这意味着MySQL服务会在完全、不完全退出以及超时情况下进行重启。
Restart的各项参数请参阅systemd服务指南页面:
Restart settings/Exit causesnoalwayson-successon-failureon-abnormalon-aborton-watchdog
Clean exit code or signal X X
Unclean exit code X X
Unclean signal X X X X
Timeout X X X
Watchdog X X X X
在systemd服务unit文件内,- Restart与- RestartSec两项参数负责控制崩溃行为。第一项参数指定服务在崩溃后重启,第二项则定义重启前的等待时长。
将Restart指令设为注释,保存文件后退出。
/etc/systemd/system/multi-user.target.wants/mysqld.service
# Restart=always
下面重载该systemd守护程序,而后重启mysqld服务:
sudo systemctl daemon-reload
sudo systemctl restart mysqld.service
接下来,找到该服务的主PID:
sudo systemctl status mysqld.service
Output
…
Main PID: 11217 (mysqld_safe)
使用-9命令关闭该主PID。
运行sudo systemctl status mysqld.service,结果显示该服务发生故障:
sudo systemctl status mysqld.service
Output
mysqld.service - MySQL Community Server
Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled)
Active: failed (Result: signal) since Sun 2015-06-21 02:28:17 EDT; 1min 33s ago
Process: 2566 ExecStartPost=/usr/bin/mysql-systemd-start post (code=exited, status=0/SUCCESS)
Process: 2565 ExecStart=/usr/bin/mysqld_safe (code=killed, signal=KILL)
Process: 2554 ExecStartPre=/usr/bin/mysql-systemd-start pre (code=exited, status=0/SUCCESS)
Main PID: 2565 (code=killed, signal=KILL)
Jun 21 02:20:09 test-centos7 systemd[1]: Starting MySQL Community Server…
Jun 21 02:20:09 test-centos7 mysqld_safe[2565]: 150621 02:20:09 mysqld_safe Logging to ‘/var/log/mysqld.log’.
Jun 21 02:20:09 test-centos7 mysqld_safe[2565]: 150621 02:20:09 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
Jun 21 02:20:10 test-centos7 systemd[1]: Started MySQL Community Server.
Jun 21 02:28:16 test-centos7 systemd[1]: mysqld.service: main process exited, code=killed, status=9/KILL
Jun 21 02:28:17 test-centos7 systemd[1]: Unit mysqld.service entered failed state.
多试几次,确认服务不再处于运行状态。
这样,我们就成功模拟了崩溃后服务不再重启的情况。
现在再次编辑mysqld.service unit文件,取消Restart参数注释,保存并重载该systemctl守护程序,最终启动服务。现在一切应该恢复至改动前的状态了。
通过这部分讲解,我们已经了解了如何在服务unit文件的[Service]部分中添加Restart(或者RestartSec)控制服务在崩溃后的重启行为。
这就是Linux处理服务启动的全部方式。我们在文章中探讨了System V、Upstart以及systemd几类init流程的起效方式,外加三者在重启或崩溃后实现服务重启的具体原理。
Upstart配置文件以及systemd unit文件中的声明语法较System V init脚本显然更加简单易用。
在操作自己的Linux环境时,大家应当检查具体发行版版本并查看其所支持的init守护程序。
另外,大家也应当认真考虑何时启用及禁用某些服务。在大多数情况下,我们不需要对第三方应用或者原生Linux守护程序进行修改。换方之,startup与respawn操作的对象一般仅限于我们自己定制的服务应用。
本文来源自DigitalOcean Community。英文原文:How To Configure a Linux Service to Start Automatically After a Crash or Reboot – Part 2: Reference By Sadequl Hussain
翻译:diradw