美文网首页
[nginx]句读nginx服务启动脚本

[nginx]句读nginx服务启动脚本

作者: geoeee | 来源:发表于2017-06-04 21:18 被阅读246次

    起因

    之前需要设定一个服务的开机启动脚本,但是遗憾的是我没有很好的 Linux 系统使用和配置经验,操作系统不熟,Shell 脚本不熟,网上的教程和例子不少但是限于自身的基础,其实参考性不大;基于现有经验和使用状况,其实nginx的服务启动和周期管理不就是很好的例子?
    通过句读 nginx 的 init 脚本,扩展shell编程的知识点和编程结构;

    回顾

    首先我需要什么?
    有一个本地的可执行文件,启动之后可以提供有限的http服务.我需要他想nginx的服务一样,开机启动,不受会话启动关闭的影响;守护进程,前后台的概念在过程中熟悉,先去看看怎么实践;

    nginx 服务操作

    启动命令
    sudo /etc/init.d/nginx start
    关闭命令
    sudo /etc/init.d/nginx stop
    查看状态
    sudo /etc/init.d/nginx status
    重启
    sudo /etc/init.d/nginx restart
    逻辑上看,是使用root权限来执行一段存在绝对路径下的,脚本,并且传入了一个参数,start,stop,status restart等等来执行对应的操作,交互层面模仿这样就可以

    sudo /etc/init.d/myservice {start|stop|status|restart}
    

    开始句读

    开头部分

    #!/bin/sh
    
    ### BEGIN INIT INFO
    # Provides:   nginx
    # Required-Start:    $local_fs $remote_fs $network $syslog $named
    # Required-Stop:     $local_fs $remote_fs $network $syslog $named
    # Default-Start:     2 3 4 5
    # Default-Stop:      0 1 6
    # Short-Description: starts the nginx web server
    # Description:       starts nginx using start-stop-daemon
    ### END INIT INFO
    

    第一行是 shell 脚本的魔法字符串,指定解释器,好像所有的脚本都这样子;而后是一大段注释,init info 启动信息,不是很明白,0 到 6 的数字貌似是运行级别,暂时先不管,被注释的不会运行;

    20170605 These comments are definitely not useless.
    They are used to define LSB info, why I found it? because it has warning when I add it to init daemon use this

    sudo update-rc.d myservice defaults
    

    变量定义

    PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
    DAEMON=/usr/sbin/nginx
    NAME=nginx
    DESC=nginx
    

    接下的部分是变量设定部分,先看下本机系统中的PATH变量原来是什么,然后这里是直接指定了PATH
    DAEMON的意思本身就是守护进程吧,这里指定的nginx的二进制程序?看看对应目录下的文件是什么意思?文件是真实存在的,而且应该就是nginx的执行文件,那我自己的可执行文件也要配置到这个变量中吧
    NAME 和 DESC 应该只是下面脚本中的变量替换,作为服务的名字和描述,也要改成自己服务的名字

    默认配置运行

    # Include nginx defaults if available
    if [ -r /etc/default/nginx ]; then
        . /etc/default/nginx
    fi
    

    先要去看看 shell 当中的 if 结构是怎么写的了,看不懂呀这个.....

    shell 中的 if 结构

    分支结构的基本形式是 if 后面跟上中括号,中括号里面放的是条件表达式,代表真假的布尔值,之后是一个分号和 then,真个分支代码块以 fi 结束

    if [ ... ]; then
    ...
    fi
    

    方括号里的条件表达式的写法有固定的格式,上文中的表达式具体的意思是检测文件是不是可读的-r /etc/default/nginx;
    再来看条件成立的时候执行的代码块中的命令,一个点,空格,加上文件,表示执行这个文件, source file ; ./file ; . file 这几个命令貌似都是执行文件,但是还是略有不同,具体的区别暂时不去深究吧

    回到句读中,这一段的意思大概就是,查看这个配置文件是不是可读的,如果是的话那就执行这个配置文件,来看看这配置文件的内容
    /etc/defaults/nginx

     # Note: You may want to look at the following page before setting the ULIMIT.
    #  http://wiki.nginx.org/CoreModule#worker_rlimit_nofile
    # Set the ulimit variable if you need defaults to change.
    #  Example: ULIMIT="-n 4096"
    #ULIMIT="-n 4096"
    

    内容都被注释掉了,就说没有执行任何东西

    测试执行文件

    test -x $DAEMON || exit 0
    

    这行代码里面有几点需要看,test命令是什么? -x 参数的意思是? 一个竖线是管道,两个竖线又是什么? exit 0 是表示退出?

    • test 命令 : test 本身的作用是检查文件类型并比较值,结合 -x 参数的作用就是检查文件是否存在并且是不是有执行的权限
    • 短路逻辑或 || : exit 0 左边的两个竖线 || 跟一般含义一样是逻辑运算符,表示短路的逻辑或,当 || 左边的 test 命令返回假的时候,右边的 exit 0 退出命令才会执行,表示脚本退出;
      结合上面的两点来看,这个语句的意思就是,测试可执行文件 DAEMON
      是否存在兵器可以执行,如果不是的话就退出程序;

    环境初始化

     . /lib/init/vars.sh
     . /lib/lsb/init-functions
    

    这是执行了两个文件,看名字,一个是初始化变量定义,一个是初始化函数,具体内容中的确是有执行一些变量和函数的定义,下面用到的时候在回头看吧

    尝试获取 PID

    # Try to extract nginx pidfile
     PID=$(cat /etc/nginx/nginx.conf | grep -Ev '^\s*#' | awk 'BEGIN { RS="[;{}]" } { if ($1 == "pid") print $2 }' | head -n1)
    if [ -z "$PID" ]
    then
        PID=/run/nginx.pid
    fi
    

    首先肯定和这个文件 /etc/nginx/nginx.conf 有关,cat 命令打印内容,之后是用 grep 筛选了什么,然后交给 awk 命令 来执行,最后是 head 命令;一个个来看

    • cat 命令很熟悉,就是吧文件的内容打印到控制台,也就是说,管道第一步是吧这个配置文件的内容打印到控制台
    • grep 只知道是筛选,会返回筛选的内容所在的行这样子,参数 -Ev 的具体含义?后面跟的明显是正则表达式,应该是有关系的~~~
      • -E 参数的意思是后面跟随的是正则表达式,而 -v 是表示反选,输出的不是跟表达式匹配的,而是不匹配的;来看正则,意思会任意的空格开头,而后是井号,意思是所有的注释行?反选的话就是去掉所有的注释
      • 到这里也就明显,第一步打印配置文件,第二步,删除所有的注释行,接下来来到了第三步 awk
    • awk 命令没有怎么接触过,知道很强大;awk 是模式扫描和文本处理语言,这是一个语言?恩......好吧,看了一下, 本机的awk 实际上是一个指向 mawk 的链接,大概看了下语法,首先是指定要分割的分隔符集合,也就是 RS 等于的东西,然后针对分割之后的每行文本进行处理,这里的意思就是,每一行都会被 RS 分割成两部分应该,第一个部分如果等于 PID 的话,那么就输出第二部分; 这里就明了了,最后得到的就是存储pid的文件;
    • head 获取文件的前几行, -n1 就是获取第一行

    在解析完配置文件之后,得到的是储存pid的文件,所以之后在检测一下,变量 pid 的长度是不是0, 如果没有找到配置的文件,那么就是用默认的 /run/nginx.pid

    检查ulimit

    # Check if the ULIMIT is set in /etc/default/nginx
    if [ -n "$ULIMIT" ]; then
        # Set the ulimits
        ulimit $ULIMIT
    fi
    

    因为这个文件中的ulimit配置选项被注释掉了, -n 参数表示后面参数字符串的长度部位 0 的时候为真;所以这篇代码不执行, ulimit表示什么暂时不管

    函数定义部分

    #
    # Function that starts the daemon/service
    #
    do_start()
    {
        # Return
        #   0 if daemon has been started
        #   1 if daemon was already running
        #   2 if daemon could not be started
        start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null \
            || return 1
        start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- \
            $DAEMON_OPTS 2>/dev/null \
            || return 2
    }
    

    从函数的名字可以看出这是服务启动的时候执行的函数,从注释中也看出了返回值的具体含义,0-服务已经开启,1-服务已经运行,2-服务起不起来
    start-stop-daemon 是一个系统的命令,专门用来启动关闭服务的,那么剩下的就是配置参数了,唯一的问题就是一直没有找到那个 DAEMON_OPTS 变量定义在哪里
    接下来的函数们就实现各自的功能了 start stop 等等

    脚本参数解析

    最后的一部分就是一个多路分支的结构了,根据输入的第一个参数来决定执行什么函数

    总结

    看来一个 daemon 的维护需要的东西还是蛮多的,如果没有配置需要载入的话,就需要两个文件,一个是 /etc/init/myservice ,还有一个是必须的 pid 文件, /run/myservice.pid 照着写应该就是OK的,具体的执行可以交给系统内部的命令

    文件原本- nginx服务脚本 /etc/init.d/nginx

    #!/bin/sh
    
    ### BEGIN INIT INFO
    # Provides:   nginx
    # Required-Start:    $local_fs $remote_fs $network $syslog $named
    # Required-Stop:     $local_fs $remote_fs $network $syslog $named
    # Default-Start:     2 3 4 5
    # Default-Stop:      0 1 6
    # Short-Description: starts the nginx web server
    # Description:       starts nginx using start-stop-daemon
    ### END INIT INFO
    
    PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
    DAEMON=/usr/sbin/nginx
    NAME=nginx
    DESC=nginx
    
    # Include nginx defaults if available
    if [ -r /etc/default/nginx ]; then
        . /etc/default/nginx
    fi
    
    test -x $DAEMON || exit 0
    
    . /lib/init/vars.sh
    . /lib/lsb/init-functions
    
    # Try to extract nginx pidfile
    PID=$(cat /etc/nginx/nginx.conf | grep -Ev '^\s*#' | awk 'BEGIN { RS="[;{}]" } { if ($1 == "pid") print $2 }' | head -n1)
    if [ -z "$PID" ]
    then
        PID=/run/nginx.pid
    fi
    
    # Check if the ULIMIT is set in /etc/default/nginx
    if [ -n "$ULIMIT" ]; then
        # Set the ulimits
        ulimit $ULIMIT
    fi
    
    #
    # Function that starts the daemon/service
    #
    do_start()
    {
        # Return
        #   0 if daemon has been started
        #   1 if daemon was already running
        #   2 if daemon could not be started
        start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null \
            || return 1
        start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- \
            $DAEMON_OPTS 2>/dev/null \
            || return 2
    }
    
    test_nginx_config() {
        $DAEMON -t $DAEMON_OPTS >/dev/null 2>&1
    }
    
    #
    # Function that stops the daemon/service
    #
    do_stop()
    {
        # Return
        #   0 if daemon has been stopped
        #   1 if daemon was already stopped
        #   2 if daemon could not be stopped
        #   other if a failure occurred
        start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PID --name $NAME
        RETVAL="$?"
    
        sleep 1
        return "$RETVAL"
    }
    
    #
    # Function that sends a SIGHUP to the daemon/service
    #
    do_reload() {
        start-stop-daemon --stop --signal HUP --quiet --pidfile $PID --name $NAME
        return 0
    }
    
    #
    # Rotate log files
    #
    do_rotate() {
        start-stop-daemon --stop --signal USR1 --quiet --pidfile $PID --name $NAME
        return 0
    }
    
    #
    # Online upgrade nginx executable
    #
    # "Upgrading Executable on the Fly"
    # http://nginx.org/en/docs/control.html
    #
    do_upgrade() {
        # Return
        #   0 if nginx has been successfully upgraded
        #   1 if nginx is not running
        #   2 if the pid files were not created on time
        #   3 if the old master could not be killed
        if start-stop-daemon --stop --signal USR2 --quiet --pidfile $PID --name $NAME; then
            # Wait for both old and new master to write their pid file
            while [ ! -s "${PID}.oldbin" ] || [ ! -s "${PID}" ]; do
                cnt=`expr $cnt + 1`
                if [ $cnt -gt 10 ]; then
                    return 2
                fi
                sleep 1
            done
            # Everything is ready, gracefully stop the old master
            if start-stop-daemon --stop --signal QUIT --quiet --pidfile "${PID}.oldbin" --name $NAME; then
                return 0
            else
                return 3
            fi
        else
            return 1
        fi
    }
    
    case "$1" in
        start)
            [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
            do_start
            case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
            esac
            ;;
        stop)
            [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
            do_stop
            case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
            esac
            ;;
        restart)
            log_daemon_msg "Restarting $DESC" "$NAME"
    
            # Check configuration before stopping nginx
            if ! test_nginx_config; then
                log_end_msg 1 # Configuration error
                exit 0
            fi
    
            do_stop
            case "$?" in
                0|1)
                    do_start
                    case "$?" in
                        0) log_end_msg 0 ;;
                        1) log_end_msg 1 ;; # Old process is still running
                        *) log_end_msg 1 ;; # Failed to start
                    esac
                    ;;
                *)
                    # Failed to stop
                    log_end_msg 1
                    ;;
            esac
            ;;
        reload|force-reload)
            log_daemon_msg "Reloading $DESC configuration" "$NAME"
    
            # Check configuration before reload nginx
            #
            # This is not entirely correct since the on-disk nginx binary
            # may differ from the in-memory one, but that's not common.
            # We prefer to check the configuration and return an error
            # to the administrator.
            if ! test_nginx_config; then
                log_end_msg 1 # Configuration error
                exit 0
            fi
    
            do_reload
            log_end_msg $?
            ;;
        configtest|testconfig)
            log_daemon_msg "Testing $DESC configuration"
            test_nginx_config
            log_end_msg $?
            ;;
        status)
            status_of_proc -p $PID "$DAEMON" "$NAME" && exit 0 || exit $?
            ;;
        upgrade)
            log_daemon_msg "Upgrading binary" "$NAME"
            do_upgrade
            log_end_msg 0
            ;;
        rotate)
            log_daemon_msg "Re-opening $DESC log files" "$NAME"
            do_rotate
            log_end_msg $?
            ;;
        *)
            echo "Usage: $NAME {start|stop|restart|reload|force-reload|status|configtest|rotate|upgrade}" >&2
            exit 3
            ;;
    esac
    

    相关文章

      网友评论

          本文标题:[nginx]句读nginx服务启动脚本

          本文链接:https://www.haomeiwen.com/subject/uiuvfxtx.html