Systemd 简介
Systemd是什么?以前linux系统启动机制是init,由于init对于进程的管理是串行化的,容易出现阻塞情况,另一方面init也仅仅是执行启动脚本,并不能对服务本身进行更多的管理。Systemd是为了更好解决这些问题而诞生的。它的设计目标是,为系统的启动和管理提供一套完整的解决方案,根据Linux惯例,字母d是守护进程(daemon)的缩写,Systemd这个名字的含义,就是它要守护整个系统。使用了Systemd,就不需要再用init了。Systemd取代了initd,成为系统的第一个进程(PID 等于 1),其他进程都是它的子进程。
Systemd作为进程管理工具优点
1、支持并行化任务;
2、同时采用socket式与D-Bus总线式激活服务;
3、按需启动守护进程(daemon);
4、利用Linux的cgroups监视进程;
5、支持快照和系统恢复;
6、维护挂载点和自动挂载点;
7、各服务间基于依赖关系进行精密控制。
Systemd管理系统资源
不同的系统资源统称为 Unit(单元),Unit一共分成以下12种,如下Unit类型:
1.Service:装守护进程的启动、停止、重启和重载操作,是最常见的一种 Unit 文件
2.Target:多个Unit构成的一个逻辑组,用于对 Unit 文件进行逻辑分组,引导其它 Unit 的执行。它替代了 SysV-init 运行级别的作用,并提供更灵活的基于特定设备事件的启动方式
3.Device:硬件设备,主要用于定义设备之间的依赖关系
4.Mount:文件系统的挂载点,可以替代过去的 /etc/fstab 配置文件
5.Automount:自动挂载点,相当于 SysV-init 的 autofs 服务
6.Path:用于监控指定文件或路径的变化,并触发其它 Unit 运行
7.Scope:不是用户创建的,而是 Systemd 运行时产生的,描述一些系统服务的分组信息
8.Slice:进程组,用于表示一个 CGroup 的树,通常也不是用户创建的
9.Snapshot:Systemd快照,可以切回某个快照
10. Socket:监控来自于系统或网络的数据消息,用于实现基于数据自动触发服务启动
11. Swap:虚拟内存的交换分区
12. Timer Unit:定时器,用于配置在特定时间触发的任务,替代了 Crontab 的功能
Systemd Service配置文件
每一个被管理单元(Unit)都需要有一个配置文件用于告知systemd对于该单元(Unit)的管理方式。Systemd默认从目录/etc/systemd/system/读取配置文件,但是里面存放的大部分文件都是符号链接,指向目录/lib/systemd/system,配置文件存放于/lib/systemd/system/,开机启动后会在/etc/systemd/system目录建立软链接文件,systemctl enable命令用于在/etc/systemd/system/与/lib/systemd/system/两个目录之间建立符号链接关系。systemctl disable命令用于在两个目录之间撤销符号链接关系,相当于撤销开机启动。配置文件的后缀名,就是该Unit的种类,比如sshd.socket;如果命令行中省略后缀名,Systemd默认后缀名为.service,所以当systemctl enable sshd会被理解成systemctl enable sshd.service。
查看nginxsystemd配置文件
systemctl cat sshd.service # /lib/systemd/system/nginx.service [Unit] Description=nginx - high performance web server Documentation=http://nginx.org/en/docs/ After=network.target [Service] Type=forking PIDFile=/var/run/nginx.pid ExecStartPost=/bin/sleep 0.1 ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s QUIT $MAINPID LimitNOFILE=1000000 LimitNPROC=1000000 LimitCORE=1000000 [Install] WantedBy=multi-user.target
通常一个service服务单元的配置包含3个区块:Unit,Service和Install
Unit区块
[Unit]区块通常是配置文件的第一个区块,用来定义 Unit 的元数据,以及配置与其他 Unit 的关系。它的主要字段如下:
Description:简短描述 Documentation:文档地址 Requires:当前Unit依赖的其他Unit,如果它们没有运行,当前Unit会启动失败 Wants:与当前Unit配合的其他Unit,如果它们没有运行,当前Unit不会启动失败 BindsTo:与Requires类似,它指定的 Unit 如果退出,会导致当前Unit停止运行 Before:如果该字段指定的Unit也要启动,那么必须在当前Unit之后启动 After:如果该字段指定的Unit也要启动,那么必须在当前Unit之前启动 Conflicts:这里指定的Unit 不能与当前Unit同时运行 Condition...:当前Unit运行必须满足的条件,否则不会运行 Assert...:当前Unit运行必须满足的条件,否则会报启动失败
Service区块
[Service]区块用来Service的配置,只有Service类型的Unit才有这个区块。它的主要字段如下:
Type:定义启动时的进程行为。它有以下几种值。 Type=simple:默认值,执行ExecStart指定的命令,启动主进程 Type=forking:以fork方式从父进程创建子进程,创建后父进程会立即退出 Type=oneshot:一次性进程,Systemd会等当前服务退出,再继续往下执行 Type=dbus:当前服务通过D-Bus启动 Type=notify:当前服务启动完毕,会通知Systemd,再继续往下执行 Type=idle:若有其他任务执行完毕,当前服务才会运行 ExecStart:启动当前服务的命令 ExecStartPre:启动当前服务之前执行的命令 ExecStartPost:启动当前服务之后执行的命令 ExecReload:重启当前服务时执行的命令 ExecStop:停止当前服务时执行的命令 ExecStopPost:停止当其服务之后执行的命令 RestartSec:自动重启当前服务间隔的秒数 Restart:定义何种情况Systemd会自动重启当前服务,可能的值包括always(总是重启)、on-success、on-failure、on-abnormal、on-abort、on-watchdog TimeoutSec:定义Systemd停止当前服务之前等待的秒数 Environment:指定环境变量 EnvironmentFile 指定加载一个包含服务所需的环境变量列表的文件,文件中的每一行都是一个环境变量的定义。
Install区块
[Install]通常是配置文件的最后一个区块,用来定义如何启动,以及是否开机启动。它的主要字段如下:
WantedBy:它的值是一个或多个Target,当前Unit激活时(enable)符号链接会放入/etc/systemd/system目录下面以Target名+.wants后缀构成的子目录中 RequiredBy:它的值是一个或多个Target,当前Unit激活时,符号链接会放入/etc/systemd/system目录下面以Target 名 + .required后缀构成的子目录中 Alias:当前Unit 可用于启动的别名 Also:当前Unit激活(enable)时,会被同时激活的其他Unit
占位符
在 Unit 文件中,有时会需要使用到一些与运行环境有关的信息,例如节点 ID、运行服务的用户等。这些信息可以使用占位符来表示,然后在实际运行中动态地替换为实际的值。
%n:完整的 Unit 文件名字,包括 .service 后缀名 %p:Unit 模板文件名中 @ 符号之前的部分,不包括 @ 符号 %i:Unit 模板文件名中 @ 符号之后的部分,不包括 @ 符号和 .service 后缀名 %t:存放系统运行文件的目录,通常是 “run” %u:运行服务的用户,如果 Unit 文件中没有指定,则默认为 root %U:运行服务的用户 ID %h:运行服务的用户 Home 目录,即 %{HOME} 环境变量的值 %s:运行服务的用户默认 Shell 类型,即 %{SHELL} 环境变量的值 %m:实际运行节点的 Machine ID,对于运行位置每个的服务比较有用 %b:Boot ID,这是一个随机数,每个节点各不相同,并且每次节点重启时都会改变 %H:实际运行节点的主机名 %v:内核版本,即 “uname -r” 命令输出的内容 %%:在 Unit 模板文件中表示一个普通的百分号
服务监控启动
socket 触发的服务
涉及网络的服务,可以通过socket来触发启动。也就是说服务本身在没连接业务时不用一直空跑着,可以让systemd帮忙监听一个socket ,以减少资源消耗。当真正有业务连接进来时,才唤醒目标服务。要达成这样的配置,目标服务程序在实现上也有一定要求。
开发一个常规的网络服务,一般有以下几个关键步骤:
创建一个socket 调用bind将该 socket 绑定一个端口 调用listen监听端口,将该socket变成监听文件描叙符fd 调用accept接收一个客户端连接,得到一个新的连接文件描叙符fd 读写连接socket的fd,完成业务逻辑
借助systemd强大且通用的服务功能,它可以帮忙完成前两步,并且将socket的fd传给被激活的程序,后者就只要从第3步开始实现工作。
由socker触发的服务对应于systemd的配置文件要有两个,后缀分别是.socket与.service ,除后缀外的文件名要相同,这样就能自动关联。
例如名为nginx-socket的服务:
nginx-socket.socket
[Unit]Description=Hello World Socket [Socket]ListenStream=0.0.0.0:4321
nginx-socket.service
[Unit]Description=Hello World Socket Service [Service]ExecStart=/absolute/path/to/nginx-socket.exe
如上,.socket的配置,需要有[Socket]段,ListenStream字段表示了要监听的地址与端口。相应的 .service配置,与之前例子一样,描叙了如何启动服务。因为这是想由socket激活的service ,故没有配置重启字段。
在systemctl的大多数子命令中,如start ,其参数默认是假定 .service 单元 配置的。例如systemctl start nginx-socket等效于systemctl start nginx-socket.service 。但在这个例子中,有两种同名单元配置, 且按要求先只启动nginx-socket.socket ,所以要写完整的单元名:
systemctl start nginx-socket.socket
定时器触发的服务
对于定时器触发的服务首先要配置一个 .timer单元文件,例如:
nginx.timer
[Unit]Description=The nginx Timer [Timer]OnCalendar=*-*-* *:*:00
其中,OnCalendar 的配置格式同 crontab ,上例表示每分钟触发。
然后需要一个同名的 .service 单元文件。本文开头编译的 nginx.exe 正好 可作为该定时器启动的程序,例如:
nginx.service
[Unit]Description=The nginx Timer [Service]Type=oneshotExecStart=/absolute/path/to/nginx.exeStandardOutput=file:/absolute/path/to/stdout-file
然后启动定时器,并查看状态:
systemctl start nginx.timersystemctl status nginx.timer
服务异常重运行
为了确保服务在遭遇故障时能够自动重启。在Systemd的服务单元文件中,Restart指令是控制服务重启行为的核心设置。本文章将探讨Restart=on-failure与Restart=always这两个选项的区别,帮助开发人员对系统服务做出更适合的选择。Restart指令定义了当服务停止时Systemd的行为。它可以精细控制服务在遇到不同退出情况时是否应该重启。这是确保关键服务可靠性的重要机制,尤其是在生产环境中,服务的持续运行对业务至关重要。
智能重启
当服务单元文件中设置了Restart=on-failure时,Systemd会在服务因错误退出时尝试重启服务。”错误退出”通常是指服务以非零状态码结束运行,这可能是由于程序崩溃、遇到未处理的异常或其他非正常情况导致的。例如,如果你的服务由于内存不足而崩溃,on-failure将确保服务尝试重新启动。但如果服务是由于正常的系统维护任务而被停止,或者开发人员故意停止服务进行调试,那么它将不会被重启。
其应用场景如下:
生产环境:在不希望因为维护或更新操作而自动重启服务的生产环境中使用。
故障排除:当服务可能需要在出现问题时停止,以便进行故障排除时。
有条件的重启:当你只想在服务因特定问题而停止时重启。
无条件重启
与on-failure相对的是Restart=always选项。不管服务是如何终止的,系统都会尝试将其重启。这意味着即使服务被管理员有意关闭,或者服务正常结束,Systemd也会立即尝试将其重启。
这种策略适用于那些必须始终运行的服务,无论它们是因为何种原因停止的。这确保了即使在进行系统更新或维护时,服务也能尽可能快地恢复运行。
其应用场景如下:
关键服务:对于那些系统的核心功能,如数据库服务或Web服务器,这些服务的任何停机时间都是不可接受的。
高可用性要求:在需要最大程度减少服务停机时间的环境中。
简化管理:在希望无论服务如何停止都能立即重启的情况下。
实战演练
java应用使用systemd管理
查看配置文件:systemctl cat java.service
[Unit] Description=springboot webapp After=springboot-webapp.service [Service] WorkingDirectory=/home/app/springboot-webapp Type=forking Environment="JAVA_HOME=/home/app/jdk1.8.0_202" Environment="PATH=/home/app/jdk1.8.0_202/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/.local/bin:/home/bin" Environment="CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar" User=java Group=java PIDFile=/home/app/springboot-webapp/upp.pid ExecStart=/home/app/springboot-webapp/start.sh ExecStop=/home/app/springboot-webapp/stop.sh PrivateTmp=true [Install] WantedBy=multi-user.target
java服务生命周期管理命令
启动服务:systemctl start java.service 关闭服务:systemctl stop java.service 重启服务:systemctl restart java.service 显示服务的状态:systemctl status java.service 在开机时启用服务:systemctl enable java.service 在开机时禁用服务:systemctl disable java.service 查看服务是否开机启动:systemctl is-enabled java.service 查看已启动的服务列表:systemctl list-unit-files|grep enabled 查看启动失败的服务列表:systemctl --failed
命令
systemctl是Systemd 的主命令,用于管理系统。
# 重启系统 $ sudo systemctl reboot # 关闭系统,切断电源 $ sudo systemctl poweroff # CPU停止工作 $ sudo systemctl halt # 暂停系统 $ sudo systemctl suspend # 让系统进入冬眠状态 $ sudo systemctl hibernate # 让系统进入交互式休眠状态 $ sudo systemctl hybrid-sleep # 启动进入救援状态(单用户状态) $ sudo systemctl rescue
systemd-analyze命令用于查看启动耗时。
# 查看启动耗时 $ systemd-analyze # 查看每个服务的启动耗时 $ systemd-analyze blame # 显示瀑布状的启动过程流 $ systemd-analyze critical-chain # 显示指定服务的启动流 $ systemd-analyze critical-chain atd.service
查看配置文件
# 列出所有配置文件 # 这个列表显示每个配置文件的状态,一共有四种。 # enabled:已建立启动链接 # disabled:没建立启动链接 # static:该配置文件没有[Install]部分(无法执行),只能作为其他配置文件的依赖 # masked:该配置文件被禁止建立启动链接 $ systemctl list-unit-files # 列出指定类型的配置文件 $ systemctl list-unit-files --type=service # 查看当前系统的所有 Target $ systemctl list-unit-files --type=target
查看系统Unit
# 列出正在运行的 Unit $ systemctl list-units # 列出所有Unit,包括没有找到配置文件的或者启动失败的 $ systemctl list-units --all # 列出所有没有运行的 Unit $ systemctl list-units --all --state=inactive # 列出所有加载失败的 Unit $ systemctl list-units --failed # 列出所有正在运行的、类型为 service 的 Unit $ systemctl list-units --type=service # 查看 Unit 配置文件的内容 $ systemctl cat docker.service
查看 Unit 的状态
# 显示系统状态 $ systemctl status # 显示单个 Unit 的状态 $ systemctl status bluetooth.service # 显示远程主机的某个 Unit 的状态 $ systemctl -H root@levonfly.example.com status httpd.service # 显示某个 Unit 是否正在运行 $ systemctl is-active application.service # 显示某个 Unit 是否处于启动失败状态 $ systemctl is-failed application.service # 显示某个 Unit 服务是否建立了启动链接 $ systemctl is-enabled application.service
其它
systemd-analyze 显示此次系统启动时运行每个服务所消耗的时间,可以用于分析系统启动过程中的性能瓶颈 systemd-ask-password:辅助性工具,用星号屏蔽用户的任意输入,然后返回实际输入的内容 systemd-cat:用于将其他命令的输出重定向到系统日志 systemd-cgls:递归地显示指定 CGroup 的继承链 systemd-cgtop:显示系统当前最耗资源的 CGroup 单元 systemd-escape:辅助性工具,用于去除指定字符串中不能作为 Unit 文件名的字符 systemd-hwdb:Systemd 的内部工具,用于更新硬件数据库 systemd-delta:对比当前系统配置与默认系统配置的差异 systemd-detect-virt:显示主机的虚拟化类型 systemd-inhibit:用于强制延迟或禁止系统的关闭、睡眠和待机事件 systemd-machine-id-setup:Systemd 的内部工具,用于给 Systemd 容器生成 ID systemd-notify:Systemd 的内部工具,用于通知服务的状态变化 systemd-nspawn:用于创建 Systemd 容器 systemd-path:Systemd 的内部工具,用于显示系统上下文中的各种路径配置 systemd-run:用于将任意指定的命令包装成一个临时的后台服务运行 systemd-stdio- bridge:Systemd 的内部 工具,用于将程序的标准输入输出重定向到系统总线 systemd-tmpfiles:Systemd 的内部工具,用于创建和管理临时文件目录 systemd-tty-ask-password-agent:用于响应后台服务进程发出的输入密码请求