Configuring Init Script
使用init脚本,无非就是实现软件服务的开机启动。但是用Debian或Ubuntu时,都是用apt-get来安装软件,而且服务类的软件都自带init脚本,对init脚本变得生疏了。工作中在CentOS上配置了两个开机启动脚本,并发现一个问题。
开机守护进程启动Tomcat6
用了Tomcat这么久,以前居然一直没做个这个配置。Tomcat官方网站推荐用jsvc守护进程来启动Tomcat。
关于jsvc,Apache官网上说得非常清楚了:
http://commons.apache.org/proper/commons-daemon/jsvc.html
关于Unix(包括Linux)系统上Tomcat6守护进程配置:
https://tomcat.apache.org/tomcat-6.0-doc/setup.html#Unix_daemon
这里的步骤简单为:
1)找到jsvc的源码包:tomcat6/bin/commons-daemon-native.tar.gz
2)解压、配置并编译
cd $CATALINA_HOME/bin
tar xvfz commons-daemon-native.tar.gz
cd commons-daemon-1.0.x-native-src/unix
./configure
make
cp jsvc $CATALINA_HOME/bin/
3)配置init脚本。jsvc源码包里有Tomcat5的init脚本,复制过来,按需要进行修改,并放到 /etc/init.d/ 目录下。例如文件名为 tomcat 。jsvc启动Tomcat后,可能根据Tomcat的启动情况来设置返回值(即通过$?获取的值),导致Tomcat正常启动后,jsvc仍会返回1,而不是0。因此,这里改为Tomcat启动后,通过检测相关端口来判断Tomcat是否启动成功并显示启动后的提示信息。脚本示例如下:
#!/bin/sh
#
# Startup Script for Tomcat6
#
# chkconfig: 345 98 02
# description: Tomcat Daemon
# processname: tomcat
# pidfile: /var/run/tomcat.pid
# config:
#
# Source function library.
. /etc/rc.d/init.d/functions
#
prog=tomcat
JAVA_HOME=/usr/local/jdk1.6.0_45
CATALINA_HOME=/usr/local/apache-tomcat-6.0.35
DAEMON_HOME=$CATALINA_HOME/bin
TOMCAT_USER=tomcat
HTTP_PORT=8080
# for multi instances adapt those lines.
TMP_DIR=$CATALINA_HOME/temp
PID_FILE=/var/run/tomcat.pid
CATALINA_BASE=$CATALINA_HOME
CATALINA_OPTS="-server -Xss512k -Xms64M -Xmx512M"
CLASSPATH=\
$JAVA_HOME/lib/tools.jar:\
$CATALINA_HOME/bin/commons-daemon.jar:\
$CATALINA_HOME/bin/bootstrap.jar
start() {
echo -n $"Starting $prog: "
#
# Start Tomcat
#
$DAEMON_HOME/jsvc $JSVC_OPTS \
-user $TOMCAT_USER \
-home $JAVA_HOME \
-Dcatalina.home=$CATALINA_HOME \
-Dcatalina.base=$CATALINA_BASE \
-Djava.io.tmpdir=$TMP_DIR \
-wait 10 \
-pidfile $PID_FILE \
-outfile $CATALINA_HOME/logs/catalina.out \
-errfile '&1' \
$CATALINA_OPTS \
-cp $CLASSPATH \
org.apache.catalina.startup.Bootstrap
#
# To get a verbose JVM
#-verbose \
# To get a debug of jsvc.
#-debug \
# Check if Tomcat is running on HTTP_PORT
lsof -i:$HTTP_PORT > /dev/null
ret=$?
if [ $ret -eq 0 ]; then
echo_success
else
echo_failure
fi
echo
return $ret
}
stop() {
echo -n $"Stopping $prog: "
#
# Stop Tomcat
#
$DAEMON_HOME/jsvc $JSVC_OPTS \
-stop \
-pidfile $PID_FILE \
org.apache.catalina.startup.Bootstrap
ret=$?
if [ $ret -eq 0 ]; then
echo_success
else
echo_failure
fi
echo
return $ret
}
restart() {
stop
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo "Usage $prog start/stop/restart"
exit 1;;
esac
4)加入开机启动。利用chkconfig命令:
chkconfig --add tomcat
chkconfig tomcat on
所有Java程序都可以用jsvc来启动,但是,启动的类需要实现init(String[] args)、start()、stop()、destroy()这4个方法。服务在启动时会先调用init(String[] args)方法,然后调用start()方法,在服务停止是会首先调用stop()方法,然后调用destroy()方法。
直接启动Tomcat6
其实init脚本只是个含有特定格式(例如要求实现带start和stop参数的功能)的shell脚本。就是说,可以直接执行Tomcat6自带的启动和停止脚本($CATALINA_HOME/bin目录下的startup.sh和shutdown.sh)。
具体配置参考这个:
http://www.cnblogs.com/lianche/p/3154096.html
init脚本的模板如下:(以memcache为例)
#!/bin/sh
#
# Startup Script for memcached
#
# chkconfig: 345 65 35
# description: memcached
# processname: memcached
# pidfile: /var/run/memcached.pid
# config:
#
# Source function library.
. /etc/rc.d/init.d/functions
#
prog=memcached
FILE_HOME=/usr/local/memcache
PID_FILE=/var/run/memcached.pid
start() {
echo -n $"Starting $prog: "
$FILE_HOME/bin/memcached -d -m 128 -u root -l 0.0.0.0 -p 11211 -c 512 -P $PID_FILE
retval=$?
if [ $retval -eq 0 ]; then
echo_success
else
echo_failure
fi
echo
return $retval
}
stop() {
echo -n $"Stopping $prog: "
cat $PID_FILE | xargs kill -9
ret=$?
if [ $ret -eq 0 ]; then
echo_success
else
echo_failure
fi
echo
return $ret
}
restart() {
stop
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
*)
echo "Usage $prog start/stop/restart"
exit 1;;
esac
关于没有生成PID文件的程序
有些程序不像memcached那样把进程id记录到PID_FILE,于是就不能使用kill命令来关闭该进程了。虽然直接调用startup.sh和shutdown.sh来启动和关闭Tomcat也是没有PID_FILE,但是Tomcat会使用端口来关闭自己进程。遇到既没有PID_FILE,也没有像Tomcat那样有特殊的关闭方法的话,就自能自己用ps命令找到对应的进程id,然后kill掉。
以下以“花生壳(phddns)”为例。其中get_pid函数是获取进程号的。本来ps -C phddns -o pid=
就可以获取到进程id了。但是由于启动脚本的文件名也是phddns,ps -C phddns
会找到脚本所在的进程id,所以要再加上grep来找到/usr/bin/phddns命令的进程。
#!/bin/sh
#
# Startup Script for phddns
#
# chkconfig: 345 56 44
# description: phddns Daemon
# processname: phddns
# pidfile:
# config:
#
# Source function library.
. /etc/rc.d/init.d/functions
#
prog=phddns
get_pid() {
#get the process id using ps
echo $(ps -C phddns -o pid,command | grep '/usr/bin/phddns' | awk '{print $1}')
}
start() {
echo -n $"Starting $prog: "
PID_NUM=$(get_pid)
if [ -n "$PID_NUM" ]; then
echo_failure
echo
echo $prog is already running.
else
/usr/bin/phddns -c /etc/phlinux.conf -d > /dev/null
ret=$?
if [ $ret -eq 0 ]; then
echo_success
else
echo_failure
fi
echo
fi
return $ret
}
stop() {
echo -n $"Stopping $prog: "
PID_NUM=$(get_pid)
if [ -n "$PID_NUM" ]; then
kill -9 $PID_NUM
ret=$?
if [ $ret -eq 0 ]; then
echo_success
else
echo_failure
fi
echo
else
echo_failure
echo
echo $prog is not running.
fi
return $ret
}
restart() {
stop
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo "Usage $prog start/stop/restart"
exit 1;;
esac
关于启动顺序
开机启动时,如果服务A依赖服务B来运行时,必须要先启动服务B,再启动服务A。通常在init脚本头(注释)中,用 chkconfig 及其后面3个参数来标记。例如:
chkconfig 345 65 35
345表示在init状态为3、4、5时启动,65表示启动顺序(只支持两位数字,数字越小,启动越早),35表示中止顺序(也只支持两位数字,数字越小,中止越早)。
那么,服务A的chkconfig第2个参数要大于服务B的。
生成的启动顺序,可以参照 /etc/rc.d 文件夹(号代表0到6)内的文件名称。其中K开头的表示中止,S开头的表示启动。
PS. 由于工作上用到了CentOS,相关的配置也用得越来越多。这是N年前想不到的事情。