我试图在CentOS7中以系统服务的方式运行一个java应用程序。这个jar应该以一个特定的用户:appuser来运行。我有一个shell脚本,用下面的命令来运行这个jar。整个脚本要大得多,因为它还处理停止、重启和状态,但这是启动部分。
servicename="myservice"
user="appuser"
pid_file="/var/run/$servicename.pid"
get_pid_from_file() {
cat "$pid_file"
}
get_pids() {
(ps -ef | grep myjar | grep $user | grep -v grep | awk '{print $2}')
}
is_running() {
! [ -z "`get_pids`" ] || ([ -f "$pid_file" ] && ps `get_pid_from_file` > /dev/null 2>&1)
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
case "$2" in
*)
su -s /bin/sh $user -c "cd /app/myworkingdir ; java -jar myjar.jar >> /var/log/systemout.log 2>> /var/log/systemerr.log" &
pid=`ps -ef | grep myjar | grep $user | grep -v grep | awk '{print $2}' | tail -1`
echo $pid
echo $pid > $pid_file
当我从命令行运行这个脚本时 它会启动jar并写入pidfile。我使用命令中的尾巴来获取PID,因为我实际上有3个进程:su,binsh和实际的java -jar命令。
现在我还有一个systemctl脚本,它在 /etc/systemd/system/multi-user.target.wants/myservice.service
,它看起来像这样。
[Unit]
Description=myservice
After=syslog.target
After=network.target
[Service]
Type=simple
WorkingDirectory=/app/myworkingdir/run
PIDFile=/var/run/myservice.pid
ExecStart=/app/myworkingdir/run/myscript.sh start
ExecStop=/app/myworkingdir/run/myscript.sh stop
User=appuser
Group=appgrp
Restart=always
RestartSec=30
StartLimitInterval=60
StartLimitBurst=5
TimeoutStartSec=30
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
问题是... systemctl start myservice.service
将无法正确启动服务。
当我运行 journeyctl -xe
:
May 21 13:03:23 myserver.com systemd[1]: Starting my service...
-- Subject: Unit myservice.service has begun start-up
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit myservice.service has begun starting up.
May 21 13:03:23 myserver.com polkitd[619]: Unregistered Authentication Agent for unix-process:19329:16
May 21 13:03:23 myserver.com myscript.sh[19335]: Already started
May 21 13:03:24 myserver.com myscript.sh[19345]: Stopping myscript.sh..
这里是varlogmessages中的记录。
[root@myserver run]# systemctl start myservice.service
May 21 13:34:00 myserver systemd: myservice.service holdoff time over, scheduling restart.
May 21 13:34:00 myserver systemd: Started myservice.
May 21 13:34:00 myserver systemd: Starting myservice...
[root@myserver run]# May 21 13:34:00 ctor-app52 myscript.sh: Already started
May 21 13:34:00 myserver denver.sh: Stopping myscript.sh..
May 21 13:34:31 myserver systemd: myservice.service holdoff time over, scheduling restart.
May 21 13:34:31 myserver systemd: Started myservice.
May 21 13:34:31 myserver systemd: Starting myservice...
May 21 13:34:31 myserver myscript.sh: Already started
May 21 13:34:31 myserver myscript.sh: Stopping myscript.sh..
May 21 13:35:01 myserver systemd: Started Session 122559 of user root.
May 21 13:35:01 myserver systemd: Starting Session 122559 of user root.
May 21 13:35:01 myserver su: (to appuser) root on none
May 21 13:35:01 myserver systemd: LPdenver.service holdoff time over, scheduling restart.
May 21 13:35:01 myserver systemd: Started myservice.
May 21 13:35:01 myserver systemd: Starting myservice...
May 21 13:35:01 myserver myscript.sh: Already started
May 21 13:35:01 myserver myscript.sh: Stopping myscript.sh..
May 21 13:35:31 myserver systemd: myservice.service holdoff time over, scheduling restart.
May 21 13:35:31 myserver systemd: Started myservice.
May 21 13:35:31 myserver systemd: Starting myservice...
May 21 13:35:31 myserver myscript.sh: Already started
May 21 13:35:31 myserver myscript.sh: Stopping myscript.sh..
May 21 13:36:01 myserver systemd: myservice.service holdoff time over, scheduling restart.
May 21 13:36:01 myserver systemd: Started myservice.
May 21 13:36:01 myserver systemd: Starting myservice...
May 21 13:36:01 myserver myscript.sh: Already started
May 21 13:36:02 myserver myscript.sh: Stopping myscript.sh..
我到底做错了什么?
你正在指定一个用户在你的 .service
文件,因此,您不需要做任何的 su
脚本中的魔法。
替换这个。
su -s /bin/sh $user -c "cd /app/myworkingdir ; java -jar myjar.jar >> /var/log/systemout.log 2>> /var/log/systemerr.log"
替换成这个
cd /app/myworkingdir
java -jar myjar.jar >> /var/log/systemout.log 2>> /var/log/systemerr.log"
此外,我在你的脚本中还看到两个问题。
你是通过读取进程表来寻找服务的PID. 如果有另一个正在运行的进程的命令行中包含相同的字符,这可能会失败(系统可能最终选择错误的进程来终止)。echo $!>$pid_file
因此,在启动java后立即终止进程是一个比较安全的方法。
服务类型是 simple
但你的脚本却将它作为一个独立的进程分叉,然后返回。这将使systemd感到困惑,你的服务将无法启动。
这两个问题可能都很容易解决。我想你只需要PID就可以停止服务,而要做到这一点,只需要发送一个 SIGINT
. 在这种情况下,你可以利用这样一个事实,即在没有任何的 ExecStop
,systemd会简单地发送一个 SIGINT
来停止服务进程。
去掉java调用后的安括号,并在前面加上 exec
来代替。这样一来,java进程就会被systemd视为守护进程。
完全放弃java调用后的PID魔法。
在 .service
文件,删除 ExecStop
的条目。
相反,还在 [Service]
加 SuccessExitStatus=143
. (当以信号结束时,JVM将以一个非零的退出状态退出,即128加上信号。默认情况下,systemd将退出状态为143视为失败,该条目将告诉systemd 143代表优雅退出)。)
你可以再进一步,完全放弃这个脚本。
ExecStart
which java
).service
文件也是)。)