在 Linux 运维和系统管理中,systemd 是最常用的服务管理工具之一。然而,在实际使用过程中,我们经常会遇到服务启动失败的情况,而日志信息往往不够直观。本文将以一个真实的 Kafka 服务启动失败案例为例,详细分析 systemd 服务失败的排查思路、解决方案,并提供优化建议。
用户尝试启动 ad-kafka-s 服务,但发现服务在启动后迅速失败。执行 systemctl status 后,关键日志如下:
● ad-kafka-s.service
Loaded: loaded (/etc/systemd/system/ad-kafka-s.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Thu 2025-04-24 17:31:28 CST; 791ms ago
Process: 4091343 ExecStop=/bin/kill -s TERM $MAINPID (code=exited, status=0/SUCCESS)
Process: 4098297 ExecStart=/opt/ad_kafka_s/deployer.sh start (code=exited, status=0/SUCCESS)
Main PID: 4098300 (code=exited, status=1/FAILURE)
Apr 24 17:31:22 kafka-s-index systemd[1]: Starting ad-kafka-s.service...
Apr 24 17:31:22 kafka-s-index systemd[1]: Started ad-kafka-s.service.
Apr 24 17:31:28 kafka-s-index systemd[1]: ad-kafka-s.service: main process exited, code=exited, status=1/FAILURE
Apr 24 17:31:28 kafka-s-index systemd[1]: Unit ad-kafka-s.service entered failed state.
Apr 24 17:31:28 kafka-s-index systemd[1]: ad-kafka-s.service failed.从日志可以看出:
ExecStart 脚本 /opt/ad_kafka_s/deployer.sh start 返回 0(成功),但主进程(PID 4098300)却以 status=1 失败。deployer.sh 可能启动了 Kafka 或其他后台进程,但该进程因配置错误、资源不足或依赖问题而崩溃。Type 设置不正确: deployer.sh 启动的是守护进程(如 Kafka),Type 应该设为 forking,否则 systemd 可能误判主进程状态。SuccessExitStatus: 0 退出码表示成功,需在 systemd 单元文件中声明。使用 journalctl 查看详细日志:
journalctl -u ad-kafka-s -n 100 --no-pager如果 Kafka 有独立日志,检查:
tail -n 100 /opt/ad_kafka_s/logs/server.log/opt/ad_kafka_s/deployer.sh start观察输出,并检查进程是否存活:
ps aux | grep kafka
jps # 查看 Java 进程cat /etc/systemd/system/ad-kafka-s.service典型 Kafka systemd 配置示例:
[Unit]
Description=Apache Kafka Server
After=network.target zookeeper.service
[Service]
Type=forking
User=kafka
Group=kafka
ExecStart=/opt/ad_kafka_s/deployer.sh start
ExecStop=/opt/ad_kafka_s/deployer.sh stop
Restart=on-failure
RestartSec=10
SuccessExitStatus=0 143
LimitNOFILE=65536
Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk"
[Install]
WantedBy=multi-user.target关键点:
Type=forking(如果 Kafka 以守护进程运行)。SuccessExitStatus 确保 systemd 正确处理退出码。LimitNOFILE 提高文件描述符限制。# 检查内存
free -h
# 检查磁盘
df -h
# 检查 FD 限制
ulimit -n如果 ulimit 过低,可在 systemd 单元文件中增加:
LimitNOFILE=65536Kafka 默认使用 9092,检查是否被占用:
netstat -tlnp | grep 9092
lsof -i :9092确保 Type 和 ExecStart 正确:
[Service]
Type=forking
ExecStart=/opt/ad_kafka_s/deployer.sh start
ExecStop=/opt/ad_kafka_s/deployer.sh stop
Restart=on-failuredeployer.sh 示例(确保正确等待子进程):
#!/bin/bash
set -e # 出错时退出
case "$1" in
start)
echo "Starting Kafka..."
/opt/kafka/bin/kafka-server-start.sh -daemon /opt/kafka/config/server.properties
sleep 5 # 等待 Kafka 启动
;;
stop)
echo "Stopping Kafka..."
/opt/kafka/bin/kafka-server-stop.sh
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
exit 0server.properties 关键配置:
broker.id=1
listeners=PLAINTEXT://:9092
zookeeper.connect=localhost:2181
log.dirs=/var/lib/kafkasystemctl daemon-reload
systemctl start ad-kafka-s
systemctl status ad-kafka-s日志监控:使用 journalctl 或 logrotate 管理日志。
资源限制:调整 systemd 的 LimitNOFILE 和 LimitMEMLOCK。
健康检查:在脚本中添加进程检查逻辑:
if ! ps -p $KAFKA_PID > /dev/null; then
echo "Kafka process died!"
exit 1
fi通过本案例,我们学习了:
systemd 服务失败日志。systemd 单元文件和启动脚本。systemd 服务管理虽然强大,但必须正确配置才能稳定运行。希望本文能帮助你在遇到类似问题时快速定位和解决!🚀
附录:相关命令速查
命令 | 用途 |
|---|---|
systemctl status <service> | 查看服务状态 |
journalctl -u <service> | 查看服务日志 |
ps aux | grep <process> | 查找进程 |
netstat -tlnp | 检查端口占用 |
ulimit -n | 查看 FD 限制 |
参考资料