运维排查 | Systemd 之服务停止后状态为 failed
哈喽大家好,我是咸鱼。
我们知道 CentOS 7 之后,Systemd 代替了原来的 SystemV 来管理服务,相比 SystemV ,Systemd 能够很好地解决各个服务间的依赖关系,还能让所有的服务同时启动,而不是串行启动。
通常情况下,yum 安装的软件会由系统的包管理器(如 RPM)安装,并且会配置相应的 systemd 服务,因此由 systemd 来管理。然而,在一些情况下,特别是当采用源码编译安装软件或者软件本身并没有提供 systemd 管理的解决方案时,就需要手动编写 systemd 服务文件(service 文件)来管理这些软件。
那今天我们就来看看手动编写 systemd 服务文件来管理软件时发现的一些问题。
问题出现
我们的 zookeeper 是通过源码编译来安装,为了方便管理,决定改成通过 systemd 来管理。
下面是 zookeeper 的 service 文件
# zookeeper
[Unit]
Description=Zookeeper
After=network.target
[Service]
Type=forking
ExecStart=/opt/zookeeper/bin/zkServer.sh start
ExecStop=/opt/zookeeper/bin/zkServer.sh stop
PIDFile=/var/lib/zookeeper/zookeeper_server.pid
[Install]
WantedBy=multi-user.target
可以看到,配置文件分成几个区块,每个区块包含若干条键值对。
[Unit]
Unit
部分的 Description
字段给出当前服务的简单描述接下来的设置是启动顺序:
After
字段:表示zookeeper.service
应该在哪些服务之后启动。Before
字段,表示zookeeper.service
应该在哪些服务之前启动。
注意,After
和Before
字段只涉及启动顺序,不涉及依赖关系。
[Service]
Service
部分定义如何启动当前服务。
ExecStart
:启动进程时执行的命令。ExecStop
:停止服务时执行的命令。Type
:定义启动类型- simple(默认值):
ExecStart
字段启动的进程为主进程 - forking:
ExecStart
字段将以fork()
方式启动,此时父进程将会退出,子进程将成为主进程 - oneshot:类似于
simple
,但只执行一次,Systemd 会等它执行完,才启动其他服务 - dbus:类似于
simple
,但会等待 D-Bus 信号后启动 - notify:类似于
simple
,启动结束后会发出通知信号,然后 Systemd 再启动其他服务 - idle:类似于
simple
,但是要等到其他任务都执行完,才会启动该服务。一种使用场合是为让该服务的输出,不与其他服务的输出相混合
- simple(默认值):
[Install]
Install
部分定义如何安装这个配置文件,即怎样做到开机启动
WantedBy
:表示该服务所在的 Target。
Target
的含义是服务组,表示一组服务。WantedBy=multi-user.target
指的是,kafka 和 zookeeper 所在的 Target 是 multi-user.target
。
这个设置非常重要,因为执行systemctl enable
命令时,zookeeper .service
的一个符号链接,就会放在/etc/systemd/system
目录下面的multi-user.target.wants
子目录之中。
Systemd 有默认的启动 Target。
[root@localhost ~]# systemctl get-default
multi-user.target
上面的结果表示,默认的启动 Target 是 multi-user.target
。在这个组里的所有服务,都将开机启动。这就是为什么 systemctl enable
命令能设置开机启动的原因。
编写好 service 文件之后,我们执行下面的命令来启动 zookeeper:
[root@localhost ~]# systemctl start zookeeper.service
接着看下 zookeeper 的运行状态
[root@localhost ~]# systemctl status zookeeper.service
● zookeeper.service - Zookeeper
Loaded: loaded (/usr/lib/systemd/system/zookeeper.service; enabled; vendor preset: disabled)
Active: active (running) since 一 2024-04-01 09:10:23 CST; 2s ago
Process: 60955 ExecStop=/opt/zookeeper/bin/zkServer.sh stop (code=exited, status=0/SUCCESS)
Process: 61116 ExecStart=/opt/zookeeper/bin/zkServer.sh start (code=exited, status=0/SUCCESS)
Main PID: 61132 (java)
CGroup: /system.slice/zookeeper.service
└─61132 java -Dzookeeper.log.dir=/opt/zookeeper/bin/../logs -Dzookeeper.log.file=zookeeper--server-localhost.localdomain.log -Dzookeepe...
4月 01 09:10:22 localhost.localdomain systemd[1]: Starting Zookeeper...
4月 01 09:10:22 localhost.localdomain zkServer.sh[61116]: /usr/sbin/java
4月 01 09:10:22 localhost.localdomain zkServer.sh[61116]: ZooKeeper JMX enabled by default
4月 01 09:10:22 localhost.localdomain zkServer.sh[61116]: Using config: /opt/zookeeper/bin/../conf/zoo.cfg
4月 01 09:10:23 localhost.localdomain zkServer.sh[61116]: Starting zookeeper ... STARTED
4月 01 09:10:23 localhost.localdomain systemd[1]: Started Zookeeper.
active (running)
表示运行正常
当我们执行 systemctl stop zookeeper.service
命令停止 zookeeper 的时候,问题出现了
[root@localhost ~]# systemctl status zookeeper.service
● zookeeper.service - Zookeeper
Loaded: loaded (/usr/lib/systemd/system/zookeeper.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since 一 2024-04-01 09:10:30 CST; 906ms ago
Process: 61183 ExecStop=/opt/zookeeper/bin/zkServer.sh stop (code=exited, status=0/SUCCESS)
Process: 61116 ExecStart=/opt/zookeeper/bin/zkServer.sh start (code=exited, status=0/SUCCESS)
Main PID: 61132 (code=exited, status=143)
4月 01 09:10:23 localhost.localdomain systemd[1]: Started Zookeeper.
4月 01 09:10:29 localhost.localdomain systemd[1]: Stopping Zookeeper...
4月 01 09:10:29 localhost.localdomain zkServer.sh[61183]: /usr/sbin/java
4月 01 09:10:29 localhost.localdomain zkServer.sh[61183]: ZooKeeper JMX enabled by default
4月 01 09:10:29 localhost.localdomain zkServer.sh[61183]: Using config: /opt/zookeeper/bin/../conf/zoo.cfg
4月 01 09:10:29 localhost.localdomain systemd[1]: zookeeper.service: main process exited, code=exited, status=143/n/a
4月 01 09:10:30 localhost.localdomain zkServer.sh[61183]: Stopping zookeeper ... STOPPED
4月 01 09:10:30 localhost.localdomain systemd[1]: Stopped Zookeeper.
4月 01 09:10:30 localhost.localdomain systemd[1]: Unit zookeeper.service entered failed state.
4月 01 09:10:30 localhost.localdomain systemd[1]: zookeeper.service failed.
可以看到,zookeeper 服务在停止后并不是 inactive
,而是 failed
状态,最后两行输出里有 Unit zookeeper.service entered failed state./zookeeper.service failed
字段
问题定位
我们接着看上面的输出,可以看到在设置了 Type=forking
后,服务在启动或关闭时执行对应的脚本会开启一个进程,并且两个进程都成功执行了(返回状态码为 0 )。
Process: 61183 ExecStop=/opt/zookeeper/bin/zkServer.sh stop (code=exited, status=0/SUCCESS)
Process: 61116 ExecStart=/opt/zookeeper/bin/zkServer.sh start (code=exited, status=0/SUCCESS)
Main PID: 61132 (code=exited, status=143)
但是主进程退出时返回的状态码却是 143,而不是状态码 0。
接着看下 zookeeper 进程还在不在:
[root@localhost ~]# jps -l
61287 sun.tools.jps.Jps
[root@localhost ~]# ps -ef | grep zookeeper
root 61300 61250 0 09:49 pts/0 00:00:00 grep --color=auto zookeeper
奇怪,明明 zookeeper 进程已经成功退出了,但是 systemd 却说它退出失败
此时我注意到尽管在停止服务时,状态码为 0,但也只是表明执行 /opt/zookeeper/bin/zkServer.sh stop
命令本身成功完成,这个状态码并不代表脚本内部的执行逻辑一定是成功的。
我们看下 zkServer.sh
脚本中关于 stop 的逻辑
stop)
echo -n "Stopping zookeeper ... "
if [ ! -f "$ZOOPIDFILE" ]
then
echo "no zookeeper to stop (could not find file $ZOOPIDFILE)"
else
$KILL $(cat "$ZOOPIDFILE")
rm "$ZOOPIDFILE"
sleep 1
echo STOPPED
fi
exit 0
;;
没有发现有什么不妥,接着我们注释掉 ExecStop
字段,采用 systemd 默认的方式来停止服务。
默认情况下,systemd 将向进程发送
SIGTERM
信号(相当于kill
命令发送的终止信号),等待一段时间后,如果服务进程未正常退出,则发送SIGKILL
信号(相当于kill -9
命令发送的强制终止信号)强制终止服务进程。
然后重新启停一下 zookeeper ,看下状态:
[root@localhost ~]# systemctl status zookeeper.service
● zookeeper.service - Zookeeper
Loaded: loaded (/usr/lib/systemd/system/zookeeper.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since 一 2024-04-01 10:03:04 CST; 1s ago
Process: 61453 ExecStart=/opt/zookeeper/bin/zkServer.sh start (code=exited, status=0/SUCCESS)
Main PID: 61469 (code=exited, status=143)
4月 01 10:02:55 localhost.localdomain systemd[1]: Started Zookeeper.
4月 01 10:02:55 localhost.localdomain zkServer.sh[61453]: /usr/sbin/java
4月 01 10:02:55 localhost.localdomain zkServer.sh[61453]: ZooKeeper JMX enabled by default
4月 01 10:02:55 localhost.localdomain zkServer.sh[61453]: Using config: /opt/zookeeper/bin/../conf/zoo.cfg
4月 01 10:02:56 localhost.localdomain zkServer.sh[61453]: Starting zookeeper ... STARTED
4月 01 10:03:04 localhost.localdomain systemd[1]: Stopping Zookeeper...
4月 01 10:03:04 localhost.localdomain systemd[1]: zookeeper.service: main process exited, code=exited, status=143/n/a
4月 01 10:03:04 localhost.localdomain systemd[1]: Stopped Zookeeper.
4月 01 10:03:04 localhost.localdomain systemd[1]: Unit zookeeper.service entered failed state.
4月 01 10:03:04 localhost.localdomain systemd[1]: zookeeper.service failed.
[root@localhost ~]# jps -l
61524 sun.tools.jps.Jps
[root@localhost ~]# ps -ef | grep zookeeper
root 61537 61250 0 10:04 pts/0 00:00:00 grep --color=auto zookeeper
还是一样的问题,zookeeper 已经成功退出但是却显示 failed 状态,状态码是 143。
从上面我们得知:无论是通过 zkserver.sh
还是 systemd 默认方式来关闭服务,本质上都是向 zookeeper 进程发送 SIGTERM
信号(数值为 15 ),虽然 zookeeper 进程成功退出,但是 systemd 将此解释为异常退出,因为预期的退出状态码为 0。
而根据 POSIX 规范:【因接收到信号而终止的命令的退出状态应报告为大于 128】,所以被信号中断的进程退出时会返回 128 加上信号数值作为退出状态码。
也就是说,当 zookeeper 进程收到 SIGTERM
信号时,会返回 128 + 15 也就是 143 作为退出状态码,这也就是为什么进程在成功退出后 systemctl 显示为 failed 状态。
解决问题
既然知道了进程在退出时的状态码是 143 但是 systemd 不会解释为成功,因为预期的退出状态码为 0,那么我们只需要让 systemd 把状态码 143 也解释为成功就行。
所以在 zookeeper 的 service 文件中添加下面的配置:
[Service]
...
SuccessExitStatus=143
...
表示当服务进程以状态码 143 正常退出时,systemd 将其视为成功退出而不是异常退出。
运维排查 | Systemd 之服务停止后状态为 failed的更多相关文章
- 监控windows服务,当服务停止后自动重启服务
近期花时间研究了一下windows和linux下某服务停了后自动重启的功能,在网上收集了些资料,并经过测试,在此整理一下.这里介绍的是windows服务的监控,是通过批处理来实现的.本例是监控wind ...
- Linux运维二:CentOS6.6系统安装后的基本配置与优化
CentOS6.6系统安装完成后还需要做一些配置与优化: 一:Linux内核版本号介绍 查看内核版本: [root@Gin scripts]# uname -r 2.6.32-504.el6.x86_ ...
- 运维排查篇 | Linux 连接跟踪表满了怎么处理
nf_conntrack (在老版本的 Linux 内核中叫 ip_conntrack )是一个内核模块,用于跟踪一个网络连接的状态 一旦内核 netfilter 模块 conntrack 相关参数配 ...
- python自动化运维二:业务服务监控
p { margin-bottom: 0.25cm; line-height: 120% } a:link { } p { margin-bottom: 0.25cm; line-height: 12 ...
- CentOS 6.5自动化运维之基于cobbler服务的自动化安装操作系统详解
一.Cobbler安装 前提:cobbler由epel源提供,故此需要事先配置指向epel的yum源方可进行类似下面的安装过程. # yum install -y epel-release # yum ...
- 监控和安全运维 1.8 zabbix服务端安装
1. Zabbix简介基于web的开源软件,开源监控系统状态也可以监控网络设备.和nagios不同的是zabbix会把获取的数据保存在数据库中,所以zabbix需要有数据库支持 Zabbix还可以自动 ...
- 运维笔记--ubuntu rm删除文件后 恢复
待补充 特别注意:umount分区,尝试恢复文件,文件夹(目录),全部文件 https://www.cnblogs.com/wangxiaoqiangs/p/5630288.html https:// ...
- Linux运维之每日小技巧-检测网站状态以及PV、UV等介绍
[root@ELK-chaofeng07 httpd]# curl -o /dev/null -w %{http_code}\\n -s www.baidu.com 状态码为200表示成功. PV.U ...
- 从谷歌CRE谈起,运维如何培养服务意识?
从谷歌CRE谈起,运维如何培养服务意识? 2016年10月,谷歌云平台博客(Google Cloud Platform Blog)上更新了一篇文章,谷歌宣布了一个新的专业岗位,CRE(Customer ...
- 看DLI服务4核心如何提升云服务自动化运维
摘要:今天我们来说说DLI是如何实现监控告警来提升整体运维能力,从而为客户更好的提供Serverless的DLI. DLI是支持多模引擎的Serverless大数据计算服务,免运维也是其作为Serve ...
随机推荐
- JSON排除指定字段的4种方法
转自:https://blog.csdn.net/Sn_Keys/article/details/122443407
- Flutter——安装依赖包时,出现Waiting for another flutter command to release the startup lock
问题描述 运行 flutter packages get 时 出现 Waiting for another flutter command to release the startup lock 解决 ...
- 对find命令结果进行操作
# find匹配到一些文件后,可能希望对其进行一些操作,这时就可以使用-exec选项,exec选项后面跟着所要执行的命令,然后是一对{},一个空格和一个\,最后是一个分号; find . -type ...
- django中如何处理事务
生成订单时,一次性生成多条数据记录或者一次性操作多个模型,都有可能产生中途报错的情况,所以需要在生成订单时保证多个数据操作的原子性. 事务 在完成一个整体功能时,操作到了多个表数据,或者同一个表的多条 ...
- C C++指针面试题零碎整理
最基础的指针如下: int a; int* p = &a; 答:p指向a的地址,&是取a的地址.*指的是指针中取内容的符号. 2.str[]和str*的区别: char str1[] ...
- 【Azure Redis 缓存】Redis的监控方式? 是否有API接口调用来获取监控值
问题描述 对于PaaS的Azure Cache for Redis,Azure中有哪些监控方式?是否能有api接口调用来获取监控值? 问题答案 1) 在Redis的门户中,使用Metrics查看Red ...
- ChatGPT用10秒画完一张UML流程图,而我用了。。。
不用AI的程序员,失业潮真的快来临了. 一张订单履约的流程图,我花了10分钟才完成,而ChatGPT绘图过程只用了10秒钟,基本可以达到同样的水平,通过ChatGPT可以显著提高画流程图的效率. 订单 ...
- ants - 目前开源最优的协程池
ants - 目前开源最优的协程池 目前我们的项目重度使用 ants 协程池,在开启一个 go 的时候并不是用 go 关键字,而是用一个封装的 go 函数来开启协程.框架底层,则是使用 ants 项目 ...
- Docker安装好后服务启动不了
安装 安装方式参考地址:https://www.docker.org.cn/book/install/install-docker-on-rhel-29.html 问题 安装好后启动不了服务器.查看状 ...
- api网关介绍
1.什么是网关 API网关是一个系统的唯一入口. 是众多分布式服务唯一的一个出口. 它做到了物理隔离,内网服务只有通过网关才能暴露到外网被别人访问. 简而言之:网关就是你家的大门 2.提供了哪些功能 ...