systemd 服务项注册
前言
systemd 是所有进程之母,它负责使 Linux 主机启动到可以做生产性任务的状态。systemd 设定的一些功能比老的初始化程序要广泛得多,它要管理正在运行的 Linux 主机的许多方面,包括挂载文件系统、管理硬件、处理定时器以及启动和管理生产性主机所需的系统服务。
CentOS7引导顺序:
- UEFi或BIOS初始化,运行POST开机自检
- 选择启动设备
- 引导装载程序, centos7是grub2
- 加载装载程序的配置文件:(/etc/grub.d/ 、etc/default/grub为模板配置文件 )/boot/grub2/grub.cfg为真正的配置文件,不建议直接修改
- 加载initramfs驱动模块
- 加载内核选项
- 内核初始化,centos7使用systemd代替init
- 执行initrd.target所有单元,包括挂载/etc/fstab
- 从initramfs根文件系统切换到磁盘根目录
- systemd执行默认target配置,配置文件/etc/systemd/default.target /etc/systemd/system/
- systemd执行sysinit.target初始化系统及basic.target准备操作系统
- systemd启动multi-user.target下的本机与服务器服务
- systemd执行multi-user.target下的/etc/rc.d/rc.local
- Systemd执行multi-user.target下的getty.target及登入服务
- systemd执行graphical需要的服务
X.配置文件路径
/usr/lib/systemd/system/
XX.结构示例
[Unit]
Description = some descriptions
Documentation = man:xxx(8) man:xxx_config(5)
Requires = xxx1.target xxx2.target
After = yyy1.target yyy2.target
[Service]
Type = <TYPE>
ExecStart = <CMD_for_START>
ExecStop = <CMD_for_STOP>
ExecReload = <CMD_for_RELOAD>
Restart = <WHEN_TO_RESTART>
RestartSec = <TIME>
[Install]
WantedBy = xxx.target yy.target
A.[unit] 编写
1.Description 描述
对于此启动单元的概述
[Unit]
Description=Daemon to start He.net IPv6
2.依赖关系
note: 依赖的项目一般是与本单元同时启动的、可以指定多个服务,以空格隔开
2-1.弱依赖
2-1-1.want
推荐使用。本单元启动了,它“想要”的单元也会被启动。但是启动不成功,对本单元没有影响。
大多数时候 Wants 的都是一个固定的系统状态而不是其它 systemd 服务
[Unit]
Description=Daemon to start He.net IPv6
Wants=network-online.target
2-2.强依赖
2-2-1.Requires
Systemd 不是先启动 Requires 再启动本单元,而是在本单元被激活时,并行启动两者。于是会产生争分夺秒的问题,如果 Requires 先启动成功,那么皆大欢喜; 如果 Requires 启动得慢,那本单元就会失败
[Unit]
Description=Daemon to start He.net IPv6
Requires=network-online.target
2-2-2. RequiresOverridable
跟 Requires 很像。但是如果这条服务是由用户手动启动的,那么 RequiresOverridable 后面的服务即使启动不成功也不报错;如果不由用户手动启动而是随系统开机启动,那么依然会有 Requires 面临的问题
[Unit]
Description=Daemon to start He.net IPv6
RequiresOverridable=network-online.target
2-2-3.Requisite
强势版本的 Requires。要是这里需要的服务启动不成功、本单元文件就会立即失败。
[Unit]
Description=Daemon to start He.net IPv6
Requisite=network-online.target
3.启动顺序
note:After和Before字段只涉及启动顺序,不涉及依赖关系。
3-1.Before
本服务会在预设的服务之前启动
[Unit]
Description=Daemon to start He.net IPv6
Wants=network-online.target
Before=mysqld.service
3-1.After
本服务会在预设的服务之后启动
[Unit]
Description=Daemon to start He.net IPv6
Wants=network-online.target
Before=mysqld.service
After=network.service
4.冲突检测
Conflicts
配置与当前服务互斥的服务。如果本unit启动了,则指定的服务就不能启动,相反亦是如此。
[Unit]
Description=Daemon to start He.net IPv6
Conflicts=network-online.target
B.[service] 编写
本服务的重点部分、指定启动行为。Type、EnvironmentFile、ExecStart、ExecReload、ExecStop、PrivateTmp。
1.声明服务类型
Type
Type指定服务的启动类型,必须为simple, exec, forking, oneshot, dbus, notify, idle 之一、常用simple和forking。
建议对长时间持续运行的服务尽可能使用Type=simple(这是最简单和速度最快的选择)。注意,因为simple类型的服务 无法报告启动失败、也无法在服务完成初始化后对其他单元进行排序,所以,当客户端需要通过仅由该服务本身创建的IPC通道(而非由systemd创建的套接字或D-bus之类)连接到该服务的时候,simple类型并不是最佳选择。在这种情况下, notify或dbus(该服务必须提供D-Bus接口)才是最佳选择, 因为这两种类型都允许服务进程精确的安排何时算是服务启动成功、何时可以继续启动后继单元。notify类型需要服务进程明确使用sd_notify()函数或类似的API,否则,可以使用forking作为替代(它支持传统的UNIX服务启动协议)。最后,如果能够确保服务进程调用成功、服务进程自身不做或只做很少的初始化工作(且不大可能初始化失败),那么exec将是最佳选择。注意,因为使用任何 simple 之外的类型都需要等待服务完成初始化,所以可能会减慢系统启动速度。 因此,应该尽可能避免使用 simple 之外的类型(除非必须)。另外,也不建议对长时间持续运行的服务使用 idle 或 oneshot 类型。
1-1.simple
默认类型,启动的程序就是主体程序,这个程序要是退出那么一切皆休。simple 类型不存在主进程退出的情况也就不存在有返回状态的情况,所以它一旦启动就认为是成功的,除非没起来。
[Service]
Type=simple
1-2.exec
与simple的不同之处在于,只有在该服务的主服务进程正常结束之后,systemd才会认为该服务启动完成。 其他后继单元必须一直阻塞到这个时间点之后才能继续启动。
[Service]
Type=exec
1-3.forking
forking 标准 Unix Daemon 使用的启动方式。启动程序后会调用 fork() 函数,把必要的通信频道都设置好之后父进程退出,留下守护精灵的子进程。你要是使用的这种方式,最好也指定下 PIDFILE= /pid路径/,不要让 Systemd 去猜,非要猜也可以,把 GuessMainPID 设为 yes。如果你的程序确实是 forking 类型,但就是没实现创建 PIDFILE 的功能,那么建议使用 ExecStartPost= 结合 shell 命令来手动抓取进程编号并写到 /var/run/xxx.pid。
[Service]
Type=forking
PIDFILE= /pid路径/
或
[Service]
Type=forking
GuessMainPID=yes
1-4.oneshot
字面意思,打一枪换一个地方。所以这种服务类型就是启动,完成就退出、没进程了。只有在该服务的主服务进程退出之后,systemd才会认为该服务启动完成,才会开始启动后继单元。 此种类型的服务通常会设定RemainAfterExit=yes;这条配置的意思是说,即使没进程了,我们也要 Systemd 认为该服务是存在并成功了的、状态一直都是running的;除非手动systemctl stop 掉,或者压根就没启动成功。
[Service]
Type=oneshot
RemainAfterExit=yes
1-5.dbus
dbus 这个程序启动时需要获取一块 DBus 空间,所以需要和 BusName= 一起用。只有它成功获得了 DBus 空间,依赖它的程序才会被启动。
BusName需要使用一个D-Bus的总线名称,作为该服务的可访问名称
[Service]
Type=dbus
BusName=org.freedesktop.NetworkManager
1-6.notify
比较少用的类型;notify 这个程序会在启动结束后会发出通知信号,然后 Systemd 再启动其他服务。所以还需要配合 NotifyAccess 来让 Systemd 接收消息,后者有三个级别:none,所有消息都忽略掉; main,只接受我们程序的主进程发过去的消息; all,我们程序的所有进程发过去的消息都算。NotifyAccess 要是不写的话默认是 main。
[Service]
Type=notify
NotifyAccess=main
1-7.idle
比较少用的类型;这个程序要等它里面调度的全部其它东西都跑完才会跑它自己。比如你 ExecStart 的是个 shell 脚本,里面可能跑了一些别的东西,如果不这样的话,那很可能别的东西的控制台输出里会多一个“启动成功”这样的 Systemd 消息。
[Service]
Type=idle
2.环境变量
Environment
可以多次使用Environment赋值,也可以在""或'' 内定义好各个键值、并加以空格分割不同变量。
也可以通过导入外部预设变量文件的方式设定、所有传参必须使用 $ 。
eg:
[Service]
Environment=“ONE=one” ‘TWO=two two’
ExecStart=echo $ONE $TWO ${TWO}
# 这将给 /bin/echo 命令依次传递如下四个参数: “one”, “two”, “two”, “two two”
eg:
cat >>/opt/envfile/network_env<<EOF
AAA_IPV4_ANCHOR_0=X.X.X.X
BBB_IPV4_PRIVATE_0=X.X.X.X
CCC_HOSTNAME=test.example.com
EOF
···
[Service]
EnvironmentFile=/opt/envfile/network_env
ExecStart=/xxx --abc=xx${AAA_IPV4_ANCHOR_0}yy
# 导入之后就可以直接使用预定义文件中的变量咯
3.启动指令
ExecStart
就是实际执行此服务的程序。接受 "命令 参数 参数..." 的格式,不接受 <, >, >>, |, & 等特殊字符,很多 bash 语法也不支持。所以,要使用这些特殊的字符时,最好直接写入到脚本里面去!
可以接受一个命令,参数不限
如果非bash不行、可以尝试ExecStart=sh -c ‘dmesg | tac’ 这样的格式变通。
- 如果你服务的类型不是 oneshot,那么它只可以接受一个命令,参数不限,比如你先 ip tunnel create 再 ip tunnel0 up,那是两个 ip 命令,如果你不是 oneshot 类型这样是不行的。
- 如果有多条命令(oneshot 类型),命令之间以分号 ; 分隔,跨行可用反斜杠 \。
- 除非你的服务类型是 forking,否则你在这里输入的命令都会被认为是主进程,不管它是不是。
[Service]
ExecStart=/usr/bin/redis-server /etc/redis/conf/6379.conf
4.启动前额外执行
ExecStartPre
在输入的命令是服务启动前执行的命令,要求同ExecStart.
5.启动后额外执行
ExecStartPost
在输入的命令是服务启动后执行的命令,要求同ExecStart.
6.停止指令
ExecStop
在输入的命令是stop时候执行的命令,要求同ExecStart.
[Service]
ExecStop=/usr/bin/kill -9 $MAINPID
7.重载指令
ExecReload
[Service]
ExecReload=/usr/bin/kill -HUP $MAINPID
8.自重启触发
Restart
当服务进程 正常退出、异常退出、被杀死、超时的时候, 是否重新启动该服务;;
所谓"服务进程" 是指 ExecStart=, ExecStartPre=, ExecStartPost=, ExecStop=, ExecStopPost=, ExecReload= 中设置的进程
取值范围:no(默认), on-success, on-failure, on-abnormal, on-watchdog, on-abort, always
no(默认值) 表示不会被重启
always 表示会被无条件的重启。
on-success 表示仅在服务进程正常退出时重启
on-failure 表示 仅在服务进程异常退出时重启; 推荐此项以增强可用性
on-abort 表示 服务被强制杀死时
[Service]
Restart=on-failure
9.自重启等待时间
RestartSec
一般与Restart同时出现、在服务终止多长时间之后才重新启动它。默认是 100ms
[Service]
RestartSec=60s
10.强制结束的耐心时间
TimeoutSec
无法顺利 "正常启动或正常结束" 的情况下,等多久才可以"强制结束"
[Service]
TimeoutSec=120s
11.stop 信号设定
KillMode
systemctl stop **.service 时发送的kill信号
四个类型选择
- control-group(默认):# 当前控制组里的所有子进程,都会被杀掉
- process: # 只杀主进程
- mixed: # 主进程将收到SIGTERM信号,子进程收到SIGKILL信号
- none: # 没有进程会被杀掉,只是执行服务的stop命令
[Service]
KillMode=process
C.[Install] 编写
主要说明如何安装这个配置文件,把该 unit 安装在哪个 target上,做到开机自启。其实就是在使用【systemctl enable】时,进行创建符号连接会识别到[Install]字段的内容进行安装,主要是创建软链到【/etc/systemd/system/】目录下
1.Target挂载选择
WantedBy
这个设置后面接的大部分是 *.target 。意思是,这个unit本身该附挂在哪个 target 下面
此项的值是一个或多个 Target,当前 Unit 激活时(enable)符号链接会放入/etc/systemd/system目录下面以 Target 名 + .wants后缀构成的子目录中
- multi-user.target: # 表示多用户命令行状态,这个设置很重要;一般写这个
- graphical.target: # 表示图形用户状体,它依赖于multi-user.target
[Install]
WantedBy=multi-user.target
2.触发其他unity自启
Also
当目前这个unit被设置开机自启时,Also 后面接的unit也要开机自启的意思
3.服务别名
Alias
当前 Unit 可用于启动的别名,systemctl enable相关的服务时,则此服务会进行链接文件的创建!默认开启!
D.关于Target
Target
的含义是服务组,表示一组服务。
WantedBy=multi-user.target指的是,sshd 所在的 Target 是
multi-user.target;这个设置非常重要,因为执行systemctl enable sshd.service
命令时,sshd.service
的一个符号链接,就会放在/etc/systemd/system
目录下面的multi-user.target.wants
子目录之中。启动计算机的时候,需要启动大量的 Unit。如果每一次启动,都要一一写明本次启动需要哪些 Unit,显然非常不方便。Systemd 的解决方案就是 Target。简单说,Target 就是一个 Unit 组,包含许多相关的 Unit 。启动某个 Target 的时候,Systemd 就会启动里面所有的 Unit。从这个意义上说,Target 这个概念类似于"状态点",启动某个 Target 就好比启动到某种状态。
note:默认Target 是 multi-user.target;是Systemd 默认的启动 Target,在这个组里的所有服务,都将开机自动启动
一般来说,常用的 Target 有两个:一个是multi-user.target
,表示多用户命令行状态;另一个是graphical.target
,表示图形用户状态,它依赖于multi-user.target
1.Target 编写
Target 也有自己的配置文件;但是没有启动命令。
示例
$ systemctl cat multi-user.target
[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes
E.示范
1.simple
systemd 执行守护进程之后, 即认为该单元已经启动成功;本例中的sshd服务 必须在启动后持续运行到服务被停止。 如果该进程只是为了派生守护进程,那么应该使用 Type=forking
[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.service
Wants=sshd-keygen.service
[Service]
EnvironmentFile=/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
Type=simple
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target
2.forking
多数传统的守护进程(服务)在启动时会转入后台运行。 systemd 通过 Type=forking 来支持这种工作方式。 对于这种类型的服务,如果最初启动的进程尚未退出, 那么该单元将依然处于"启动中"(activating)状态。当最初的进程成功退出, 并且至少有一个进程仍然在运行(并且 RemainAfterExit=no), 该服务才会被视为处于"活动"(active)状态。
对于单进程的传统服务,当最初的进程成功退出后, 将会只剩单独一个进程仍然在持续运行, systemd 将会把这个唯一剩余的进程视为该服务的主进程。 仅在这种情况下,才将可以在 ExecReload=, ExecStop= … 之类的选项中使用 $MAINPID 变量。
当最初的进程成功退出后,将会剩余多个进程在持续运行, 因此,systemd 无法确定哪一个进程才是该服务的主进程。 在这种情况下,不可以使用 $MAINPID 变量。 然而,如果主进程会创建传统的PID文件, 那么应该将 PIDFile= 设为此PID文件的绝对路径, 以帮助 systemd 从该PID文件中读取主进程的PID,从而帮助确定该服务的主进程。 注意,守护进程必须在完成初始化之前写入PID文件, 否则可能会导致 systemd 读取失败 (读取时文件不存在)。
[Unit] # 主要是服务说明
Description=test # 简单描述服务
After=network.target # 描述服务类别,表示本服务需要在network服务启动后在启动
Before=xxx.service # 表示需要在某些服务启动之前启动,After和Before字段只涉及启动顺序,不涉及依赖关系。
[Service] # 服务的关键
Type=forking # 表示后台运行模式。
PIDFile=/usr/local/test/test.pid # 存放PID的绝对路径
ExecStart=/usr/local/test/bin/startup.sh # 服务启动命令,命令需要绝对路径
ExecReload=xxx #为重启命令,
ExecStop=xxx #为停止命令
PrivateTmp=true # 表示给服务分配独立的临时空间
[Install]
WantedBy=multi-user.target # 多用户
3.oneshot
用于那些只需要执行一次性动作而不需要持久运行的单元, 例如文件系统检查或者清理临时文件。 此类单元, 将会在启动后一直等待指定的动作完成, 然后再回到停止状态。 下面是一个执行清理动作的单元:
[Unit]
After=network.target
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/bin/mount -t nfs -o defaults 192.168.1.120:/volume1/mydsm /mnt
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
4.dbus
对于需要在 D-Bus 系统总线上注册一个名字的服务, 应该使用 Type=dbus 并且设置相应的 BusName= 值。 该服务不可以派生任何子进程。 一旦从 D-Bus 系统总线成功获取所需的名字,该服务即被视为初始化成功。 下面是一个典型的 D-Bus 服务:
[Unit]
Description=一个简单的 DBus 服务
[Service]
Type=dbus
BusName=org.example.simple-dbus-service
ExecStart=/usr/sbin/simple-dbus-service
[Install]
WantedBy=multi-user.target
5.notify
Type=simple 类型的服务 非常容易编写, 但是, 无法向 systemd 及时通知 "启动成功"的消息, 是一个重大缺陷。 Type=notify 可以弥补该缺陷, 它支持将"启动成功"的消息及时通知给 systemd 。 下面是一个典型的例子;
该守护进程必须支持 systemd 通知协议, 否则 systemd 将会认为该服务一直处于"启动中"(activating)状态,并在超时后将其杀死。
[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.service
Wants=sshd-keygen.service
[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target
标签:systemd,服务,target,启动,注册,进程,Type
From: https://www.cnblogs.com/hukenis/p/17707307.html