整理一下逻辑思路,要编写一个nginx启动脚本,都需要什么?
nginx服务的操作需要nginx服务的支持,即服务器上必须编译安装了nginx服务
nginx启动脚本说白了就是方便对nginx服务启动、关闭、状态查询、热修改的一个脚本文件
依赖的几个文件
nginx脚本依赖于nginx的二进制系统程序文件:/usr/sbin/nginx(这个在源码编译的时候自定义位置,不过得被Bash找到)
nginx属于网络服务,所以还依赖于网卡信息总文件:/etc/sysconfig/network
nginx服务启动脚本使用了一些Linux内核函数,需要:/etc/rc.d/init.d/functions函数文件
在进行第五步之前,我们还得定义几个变量。
nginx=/usr/sbin/nginx:定义nginx二进制系统文件
prog=`basename $nainx`:定义nginx名
NGINX_CONFIG_FILE:定义nginx主配置文件,用于检测文件是否有语法错误
LOCK_FILE:nginx锁文件
首先得明白,几乎所有得service启动脚本,都时一个case语句+若干函数,用户/管理员在使用service服务时,都时通过传递$1参数,进行选择对服务得不同操作,即:start|stop|restart|reload|status等,所以,nginx也不例外,编辑这些需要使用得函数,然后在case语句里面添加
start函数:启动nginx服务,其实质是从nginx的二进制系统文件启动nginx
stop函数:停止nginx服务,其实质是functions中的killproc函数(这里时不是也可以用killall命令???测试是可以的,不知道有什么问题)
restart函数:重启服务,其实质是stop+start
reload函数:不stop服务的前提下重新加载,其实质是functions中的killproc函数
status函数:查看nginx运行状态,其实质是使用了functions的函数
force_reload函数:强制重新加载,其实质就是stop+start
case语句:接受命令行参数$1,并根据$1值的不同,进行不同的操作
configtest:检测nginx主配置文件是否有语法错误,没有才能进行下一步
话不多说直接上脚本
[root@rs2 mnt]# cat nginx_.sh # chkconfig: - 85 15 # description: nginx is a World Wide Web server. It is used to serve #加载函数库 . /etc/rc.d/init.d/functions #加载网络配置文件 . /etc/sysconfig/network #检查网络是否启动 [[ "$NETWORKING" = "no" ]] && exit 0 #定义变量 nginx=/usr/sbin/nginx prog=$(basename $nginx) NGINX_CONFIG_NAME="/etc/nginx/nginx.conf" LOCKFILE="/var/lock/nginx/nginx.lock" #测试nginx主配置文件是否有语法错误 configtest() { $nginx -t } #启动函数 start() { configtest #-x:检测nginx的二进制系统文件是否存在,如果不存在直接退出 test -x $nginx || exit 5 #-f:检测nginx的主配置文件是否存在,如果不存在直接退出 test -f $NGINX_CONFIG_NAME || exit 6 #如果不存在pid目录、lock目录,则创建 mkdir -p /var/run/nginx mkdir -p /var/lock/nginx #输出提示语句,表明nginx服务即将启动 echo -n $"Starting $prog :" #使用nginx二进制系统文件启动nginx服务 daemon $nginx -c $NGINX_CONFIG_NAME #获取nginx启动的返回状态值,存入变量retval retval=$? echo #如果返回状态值为0.表示启动成功,并创建锁文件 test $retval -eq 0 && touch $LOCKFILE return $retval } #停止函数 stop() { #输出提示语句,表示nginx服务即将关闭 echo "Stoping $prog :" #使用functions文件中定义的killproc函数,杀死nginx对应的进程 killproc $prog -QUIT #获取nginx关闭的返回状态值,存入变量retval(就是上一条命令执行是否成功的值) retval=$? echo #如果返回状态值为0表示关闭成功,删除锁文件 [ $retval -eq 0 ] && rm -f $LOCKFILE return $retval } #重启函数 restart() { configtest || return $? stop sleep 3 start } #热加载 reload() { configtest || return $? echo -n $"Reloading $prog :" #同stop,参数不同,这个表示重启进程 killproc $nginx -HUP retval=$? echo } #强制重启 force_reload() { restart } #状态查询 rt_status() { #functions中的status函数,获取对应进程的状态 status $prog #如果获取状态为runing,则显示配置文件检测结果,更加细化 [ $? -eq 0 ] && echo -n `configtest` } case $1 in status) rt_status ;; start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; force_reload) force_reload ;; *) #如果输入的$1不是上面的,则输出提示信息 echo "Usage:$prog {start|stop|status|reload|force_reload|restart}" exit 1 ;; esac
执行shell脚本的时候,添加-x选项,可以看到具体内核执行步骤
[root@rs2 mnt]# bash -x nginx_.sh start + . /etc/rc.d/init.d/functions #加载了functions函数 ++ TEXTDOMAIN=initscripts ++ umask 022 ++ PATH=/sbin:/usr/sbin:/bin:/usr/bin ++ export PATH ++ '[' 2020 -ne 1 -a -z '' ']' ++ /bin/mountpoint -q /cgroup/systemd ++ /bin/mountpoint -q /sys/fs/cgroup/systemd ++ case "$0" in ++ '[' -z '' ']' ++ COLUMNS=80 ++ '[' -z '' ']' ++ '[' -c /dev/stderr -a -r /dev/stderr ']' +++ /sbin/consoletype ++ CONSOLETYPE=pty ++ '[' -z '' ']' ++ '[' -z '' ']' ++ '[' -f /etc/sysconfig/i18n -o -f /etc/locale.conf ']' ++ . /etc/profile.d/lang.sh ++ unset LANGSH_SOURCED ++ '[' -z '' ']' ++ '[' -f /etc/sysconfig/init ']' ++ . /etc/sysconfig/init +++ BOOTUP=color +++ RES_COL=60 +++ MOVE_TO_COL='echo -en \033[60G' +++ SETCOLOR_SUCCESS='echo -en \033[0;32m' +++ SETCOLOR_FAILURE='echo -en \033[0;31m' +++ SETCOLOR_WARNING='echo -en \033[0;33m' +++ SETCOLOR_NORMAL='echo -en \033[0;39m' ++ '[' pty = serial ']' ++ __sed_discard_ignored_files='/\(~\|\.bak\|\.orig\|\.rpmnew\|\.rpmorig\|\.rpmsave\)$/d' ++ '[' '' = 1 ']' + . /etc/sysconfig/network #加载network主文件 ++ HOSTNAME=rs2 + [[ '' = \n\o ]] #判断网络是否启动,这里network主文件并没有定义NETWORKING + nginx=/usr/sbin/nginx #定义二进制系统文件变量nginx ++ basename /usr/sbin/nginx #获取nginx名 + prog=nginx #定义prog变量 + NGINX_CONFIG_NAME=/etc/nginx/nginx.conf #定义nginx主配置文件路径 + LOCKFILE=/var/lock/nginx/nginx.lock #定义lock文件 + case $1 in #执行case语句 + start #发现是start选项,调用start函数 + configtest #start函数中,首先调用了configtest函数 + /usr/sbin/nginx -t #configtest函数调用nginx二进制系统文件检测主配置文件是否有语法错误 #可以看到没有语法错误 nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful + test -x /usr/sbin/nginx #检查是否由二进制系统文件 + test -f /etc/nginx/nginx.conf #检查是否由nginx主配置文件 + mkdir -p /var/run/nginx #创建/var/run/nginx目录 + echo -n 'Starting nginx :' #输出提示 #调用nginx二进制系统文件启动nginx Starting nginx :+ daemon /usr/sbin/nginx -c /etc/nginx/nginx.conf + local gotbase= force= nicelevel corelimit + local pid base= user= nice= bg= pid_file= + local cgroup= + nicelevel=0 + '[' /usr/sbin/nginx '!=' /usr/sbin/nginx ']' + '[' -z '' ']' + base=nginx + __pids_var_run nginx '' + local base=nginx + local pid_file=/var/run/nginx.pid + pid= + '[' -f /var/run/nginx.pid ']' + return 3 + '[' -n '' -a -z '' ']' + corelimit='ulimit -S -c 0' + '[' -n '' ']' + '[' -n '' ']' + '[' color = verbose -a -z '' ']' + '[' -z '' ']' + /bin/bash -c 'ulimit -S -c 0 >/dev/null 2>&1 ; /usr/sbin/nginx -c /etc/nginx/nginx.conf' + '[' 0 -eq 0 ']' + success 'nginx startup' + '[' color '!=' verbose -a -z '' ']' + echo_success + '[' color = color ']' + echo -en '\033[60G' + echo -n '[' [+ '[' color = color ']' + echo -en '\033[0;32m' + echo -n ' OK ' //启动成功 OK + '[' color = color ']' + echo -en '\033[0;39m' + echo -n ']' ]+ echo -ne '\r' + return 0 + return 0 #返回状态码值0 + retval=0 #将返回的状态码值给retval变量 + echo #为了美观,输出一个空白行 + test 0 -eq 0 #测试返回码值是否为0,这里的确为0 + touch /var/lock/nginx/nginx.lock #创建lock文件 + return 0 #返回0,脚本执行结束
[root@rs2 mnt]# bash -x nginx_.sh stop + . /etc/rc.d/init.d/functions ++ TEXTDOMAIN=initscripts ++ umask 022 ++ PATH=/sbin:/usr/sbin:/bin:/usr/bin ++ export PATH ++ '[' 2020 -ne 1 -a -z '' ']' ++ /bin/mountpoint -q /cgroup/systemd ++ /bin/mountpoint -q /sys/fs/cgroup/systemd ++ case "$0" in ++ '[' -z '' ']' ++ COLUMNS=80 ++ '[' -z '' ']' ++ '[' -c /dev/stderr -a -r /dev/stderr ']' +++ /sbin/consoletype ++ CONSOLETYPE=pty ++ '[' -z '' ']' ++ '[' -z '' ']' ++ '[' -f /etc/sysconfig/i18n -o -f /etc/locale.conf ']' ++ . /etc/profile.d/lang.sh ++ unset LANGSH_SOURCED ++ '[' -z '' ']' ++ '[' -f /etc/sysconfig/init ']' ++ . /etc/sysconfig/init +++ BOOTUP=color +++ RES_COL=60 +++ MOVE_TO_COL='echo -en \033[60G' +++ SETCOLOR_SUCCESS='echo -en \033[0;32m' +++ SETCOLOR_FAILURE='echo -en \033[0;31m' +++ SETCOLOR_WARNING='echo -en \033[0;33m' +++ SETCOLOR_NORMAL='echo -en \033[0;39m' ++ '[' pty = serial ']' ++ __sed_discard_ignored_files='/\(~\|\.bak\|\.orig\|\.rpmnew\|\.rpmorig\|\.rpmsave\)$/d' ++ '[' '' = 1 ']' + . /etc/sysconfig/network ++ HOSTNAME=rs2 + [[ '' = \n\o ]] + nginx=/usr/sbin/nginx ++ basename /usr/sbin/nginx + prog=nginx + NGINX_CONFIG_NAME=/etc/nginx/nginx.conf + LOCKFILE=/var/lock/nginx/nginx.lock + case $1 in + stop + echo 'Stoping nginx :' Stoping nginx : + killproc nginx -QUIT #执行killproc函数,传递的参数是nginx + local RC killlevel= base pid pid_file= delay try + RC=0 + delay=3 + try=0 + '[' 2 -eq 0 ']' + '[' nginx = -p ']' + '[' nginx = -d ']' + '[' -n -QUIT ']' + killlevel=-QUIT + base=nginx + __pids_var_run nginx '' + local base=nginx + local pid_file=/var/run/nginx.pid + pid= + '[' -f /var/run/nginx.pid ']' + return 3 + RC=3 + '[' -z '' ']' + '[' -z '' ']' ++ __pids_pidof nginx ++ pidof -c -m -o 2827 -o 2020 -o %PPID -x nginx + pid='2826 2824' + '[' -n '2826 2824' ']' + '[' color = verbose -a -z '' ']' + '[' -z -QUIT ']' + checkpid 2826 2824 + local i + for i in '$*' + '[' -d /proc/2826 ']' + return 0 + kill -QUIT 2826 2824 + RC=0 + '[' 0 -eq 0 ']' + success 'nginx -QUIT' + '[' color '!=' verbose -a -z '' ']' + echo_success + '[' color = color ']' + echo -en '\033[60G' + echo -n '[' [+ '[' color = color ']' + echo -en '\033[0;32m' + echo -n ' OK ' OK + '[' color = color ']' + echo -en '\033[0;39m' + echo -n ']' ]+ echo -ne '\r' + return 0 + return 0 + '[' -z -QUIT ']' + return 0 + retval=0 + echo + '[' 0 -eq 0 ']' + rm -f /var/lock/nginx/nginx.lock + return 0
这里重点是killproc函数:
# 该函数的作用是关闭进程的. # 用法: # killproc [-p pidfile] [-d delay][-signal] # -p: 指定进程的pid文件,一般在/var/run/xxx.pid # -d: 指定延迟多长时间就强度关闭进程 # -signal: 关闭进程的信号 # A function to stop a program. killproc() { local RC killlevel= base pid pid_file= delay # 定义局部变量,只在函数体内有效。 RC=0; delay=3 # 默认延迟3秒,不能正常关闭就强制关闭 # Test syntax. # 判断用户调用该函数时,给的参数格式。并使用变量记录下来 if [ "$#" -eq 0 ]; then echo $"Usage: killproc [-p pidfile] [ -d delay] {program} [-signal]“ # 如果调用该函数时,不使用任何参数的话将给用户输出使用该函数的使用方法。 return 1 # 退出该函数。 fi if [ "$1" = "-p" ]; then # 如果参数1,是字符 -p 的话,那么用户输入的第二个参数就是: # 程序的pid_file文件。通常是/var/run/XXX.pid pid_file=$2 shift 2 fi if [ "$1" = "-d" ]; then delay=$2 # 记录的参数的作用是用来。如果进程在正常情况下不能关闭, # 延迟多长时间就向该进程发送SIGKILL信号强制关闭。 shift 2 fi # check for second arg to be kill level # 检查用户是否指定函数关闭进程时,所使用的信号。 [ -n "${2:-}" ] && killlevel=$2 # Save basename. # 保存进程的二进制运行文件的。 base=${1##*/} # 参数替换,把程序的二进制文件的路径名***掉。 #################################################################### # 查找可运行二进制程序的进程号PID 部分 # Find pid. __pids_var_run "$1" "$pid_file" #调用 函数:__pids_var_run 来检查用户指定的程序的进程号。 # 该函数会使用一个变量:PID记录程序的进程号的。 if [ -z "$pid_file" -a -z "$pid" ]; then # 如果pidfile文件不存在,并且找不到进程的pid号。 # 就使用该方法查找该程序$base的PID号。 pid="$(__pids_pidof "$1")" fi ##################################################################### # Kill it. # 找到二进制可运行程序的进程号,是如果关闭进程的 if [ -n "$pid" ] ; then # 如果$PID 不为空,证明进程运行的。就执行 then 后的内容 [ "$BOOTUP" = "verbose" -a -z "${LSB:-}" ] && echo -n "$base " if [ -z "$killlevel" ] ; then #如果用户没有指定函数要使用关闭进程的信号的话,就执行 then后的内容 if checkpid $pid 2>&1; then # 调用函数:checkpid 来判断进程号为:$PID的进程是否 #运行。其实 checkpid是判断$PID,如果伪目录/proc #下是否有一个目录名和$PID相同 # TERM first, then KILL if not dead kill -TERM $pid >/dev/null 2>&1 # 给进程$PID发送 -15信号。等待进程关闭资源后, # 再杀死该进程。 usleep 100000 #等待100000 微秒 if checkpid $pid && sleep 1 && #再使用函数checkpid检查,如果进程还运行的话, #再等待1秒, checkpid $pid && sleep $delay && #再使用函数checkpid检查,如果进程还运行的话, #就使用用户给的延迟时间,再等待 checkpid $pid ; then #在用户给的延时时间到了,再次使用函数 #checkpid检查,如果进程还是运行的 # 话,就强制关闭。 kill -KILL $pid >/dev/null 2>&1 # 给进程$PID发送 -9 信号,强制关闭该进程。 usleep 100000 fi fi checkpid $pid # 再次确认下,进程$PID是否是关闭的。 RC=$? # 记录函数checkpid的返回结果。 # 如果是:0 的话就代表关闭进程$PID成功。 [ "$RC" -eq 0 ] && failure $"$base shutdown" || success $"$base shutdown" # 判断函数checkpid的返回值,给用户输出进程是否关闭成功的信 RC=$((! $RC)) # 对变量RC进行,非操作,避免下面使用该变量时,判断有误。 # use specified level only #调用函数:killproc时传递有关闭进程的信号就执行else 后的程序 else # 如果用户给函数killproc 传递了关闭进程所使用的信号, #就执行else后的代码 # 作用想要通知进程重读配置文件时, 可以传递一个 -HUP 信号给进程。 if checkpid $pid; then #使用函数checkpid检查,进程$PID是否运行的, kill $killlevel $pid >/dev/null 2>&1 #使用用户传递关闭进程的信号来关闭进程$PID RC=$? #记录函数checkpid的返回结果。 [ "$RC" eq 0 ] && success $"$base $killlevel" || failure $"$base $killlevel" # 根据$RC判断进程$PID是否关闭成功 elif [ -n "${LSB:-}" ]; then RC=7 # Program is not running fi fi ###########################找不到二进制可运行程序的进程号 else if [ -n "${LSB:-}" -a -n "$killlevel" ]; then RC=7 # Program is not running else failure $"$base shutdown" RC=0 fi fi # Remove pid file if any. if [ -z "$killlevel" ]; then rm -f "${pid_file:-/var/run/$base.pid}" fi return $RC }
[root@rs2 mnt]# bash -x nginx_.sh status + . /etc/rc.d/init.d/functions ++ TEXTDOMAIN=initscripts ++ umask 022 ++ PATH=/sbin:/usr/sbin:/bin:/usr/bin ++ export PATH ++ '[' 2020 -ne 1 -a -z '' ']' ++ /bin/mountpoint -q /cgroup/systemd ++ /bin/mountpoint -q /sys/fs/cgroup/systemd ++ case "$0" in ++ '[' -z '' ']' ++ COLUMNS=80 ++ '[' -z '' ']' ++ '[' -c /dev/stderr -a -r /dev/stderr ']' +++ /sbin/consoletype ++ CONSOLETYPE=pty ++ '[' -z '' ']' ++ '[' -z '' ']' ++ '[' -f /etc/sysconfig/i18n -o -f /etc/locale.conf ']' ++ . /etc/profile.d/lang.sh ++ unset LANGSH_SOURCED ++ '[' -z '' ']' ++ '[' -f /etc/sysconfig/init ']' ++ . /etc/sysconfig/init +++ BOOTUP=color +++ RES_COL=60 +++ MOVE_TO_COL='echo -en \033[60G' +++ SETCOLOR_SUCCESS='echo -en \033[0;32m' +++ SETCOLOR_FAILURE='echo -en \033[0;31m' +++ SETCOLOR_WARNING='echo -en \033[0;33m' +++ SETCOLOR_NORMAL='echo -en \033[0;39m' ++ '[' pty = serial ']' ++ __sed_discard_ignored_files='/\(~\|\.bak\|\.orig\|\.rpmnew\|\.rpmorig\|\.rpmsave\)$/d' ++ '[' '' = 1 ']' + . /etc/sysconfig/network ++ HOSTNAME=rs2 + [[ '' = \n\o ]] + nginx=/usr/sbin/nginx ++ basename /usr/sbin/nginx + prog=nginx + NGINX_CONFIG_NAME=/etc/nginx/nginx.conf + LOCKFILE=/var/lock/nginx/nginx.lock + case $1 in + rt_status + status nginx + local base pid lock_file= pid_file= + '[' 1 = 0 ']' + '[' nginx = -p ']' + '[' nginx = -l ']' + base=nginx + '[' '' = 1 ']' + __pids_var_run nginx '' + local base=nginx + local pid_file=/var/run/nginx.pid + pid= + '[' -f /var/run/nginx.pid ']' + return 3 + RC=3 + '[' -z '' -a -z '' ']' ++ __pids_pidof nginx ++ pidof -c -m -o 2984 -o 2020 -o %PPID -x nginx + pid='2983 2981' + '[' -n '2983 2981' ']' + echo 'nginx (pid 2983 2981) is running...' nginx (pid 2983 2981) is running... + return 0 + '[' 0 -eq 0 ']' ++ configtest ++ /usr/sbin/nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful + echo -n
这里重点是status函数:
# 检测一个二进制可运行程序是否运行: # 使用方法: status [-p pidfile] {program} status() { local base pid pid_file= # Test syntax. # 测试调用该函数时的参数格式。 if [ "$#" = 0 ] ; then echo $"Usage: status [-p pidfile] {program}" # 如果调用该函数没有给一个参数的话就显示:该函数的使用方法,然后直 #接退出。退出状态码:1 return 1 fi if [ "$1" = "-p" ]; then pid_file=$2 shift 2 fi base=${1##*/} # 使用$base 记录二进制可运行程序的文件名。 # First try "pidof" __pids_var_run "$1" "$pid_file" # 调用函数:__pids_var_run 来检测二进制程序的运行状态。 RC=$? if [ -z "$pid_file" -a -z "$pid" ]; then # 如果调用 status时,没有传递pidfile文件,并且没有检测到PID号 pid="$(__pids_pidof "$1")" # 因为有些二进制可运行程序,是不会把PID写到 # /var/run/$base.pid文件当中的。就调用函数__pids_pidof来查找PID号 fi #################################################################### if [ -n "$pid" ]; then # 如果查到二进制运行程序的PID号,就代表该该二进制程序是运行的。 echo $"${base} (pid $pid) is running..." return 0 fi case "$RC" in # 根据函数:__pids_var_run返回的状态码,来判断二进制程序的运行状态。 0) echo $"${base} (pid $pid) is running..." return 0 ;; 1) echo $"${base} dead but pid file exists" # 如果函数:__pids_var_run返回的状态码是:1 ,的话.... return 1 ;; esac ##################################################################### # See if /var/lock/subsys/${base} exists # ***锁文件。 if [ -f /var/lock/subsys/${base} ]; then echo $"${base} dead but subsys locked" return 2 fi echo $"${base} is stopped" return 3
重点使用了killproc函数
如果要使用添加到service里面,则需要:
chkconfig命令添加
脚本需要放到指定位置:/etc/rc.d/init.d目录下,并且名字为nginx(注意执行权限的赋予)
脚本里面由service提示信息(必须有!!!)
在脚本中添加如下两行数据:
# chkconfig: - 85 15 # description: nginx is a World Wide Web server. It is used to serve
执行chkconfig命令,添加service服务
[root@rs2 mnt]# cp nginx_.sh /etc/rc.d/init.d/nginx [root@rs2 mnt]# chmod +x /etc/rc.d/init.d/nginx [root@rs2 mnt]# chkconfig --add /etc/rc.d/init.d/nginx
测试一下:
[root@rs2 ~]# service nginx status nginx.service - SYSV: nginx is a World Wide Web server. It is used to serve Loaded: loaded (/etc/rc.d/init.d/nginx) Active: active (running) since Sat 2018-07-14 02:11:24 CST; 17s ago Process: 3217 ExecStart=/etc/rc.d/init.d/nginx start (code=exited, status=0/SUCCESS) CGroup: /system.slice/nginx.service ├─3223 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.c... └─3225 nginx: worker process Jul 14 02:11:24 rs2 systemd[1]: Starting SYSV: nginx is a World Wide Web se..... Jul 14 02:11:24 rs2 nginx[3217]: nginx: the configuration file /etc/nginx/n...ok Jul 14 02:11:24 rs2 nginx[3217]: nginx: configuration file /etc/nginx/nginx...ul Jul 14 02:11:24 rs2 nginx[3217]: Starting nginx :[ OK ] Jul 14 02:11:24 rs2 systemd[1]: Started SYSV: nginx is a World Wide Web ser...e. Jul 14 02:11:35 rs2 systemd[1]: Started SYSV: nginx is a World Wide Web ser...e. Hint: Some lines were ellipsized, use -l to show in full. nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
满满的自己写得脚本的风格。。。