Linux Jenkins Shell 启动脚本分析

14 篇文章 0 订阅
7 篇文章 0 订阅

学习Shell的时候,顺带找了几个大厂的shell启动脚本来练练手,发现了很多彩蛋,学习到不少技巧~~~~

1:源码分析

#!/bin/sh
#
#     RedHat system statup script for Jenkins
#     Based on SUSE system statup script for Jenkins
#     Copyright (C) 2007  Pascal Bleser
#
#    #此处是一堆版权申明#
#
###############################################################################
#
# chkconfig: 35 99 01
# description: Jenkins Automation Server
#
###############################################################################
### BEGIN INIT INFO
# Provides:          jenkins
# Required-Start:    $local_fs $remote_fs $network $time $named
# Should-Start: $time sendmail
# Required-Stop:     $local_fs $remote_fs $network $time $named
# Should-Stop: $time sendmail
# Default-Start:     3 5
# Default-Stop:      0 1 2 6
# Short-Description: Jenkins Automation Server
# Description:       Jenkins Automation Server
### END INIT INFO

# Check for missing binaries (stale symlinks should not happen)
##先定义了jenkins默认的安装包的位置(采取yum等方式安装会下载war包到这个位置)
JENKINS_WAR="/usr/lib/jenkins/jenkins.war"
##判断jenkins的安装包是否存在,如果不存在并且输入命令的第一个参数是stop,将直接退出程序
test -r "$JENKINS_WAR" || { echo "$JENKINS_WAR not installed";
    if [ "$1" = "stop" ]; then exit 0;
    else exit 5; fi; }

##判断配置文件是否存在
JENKINS_CONFIG=/etc/sysconfig/jenkins
test -e "$JENKINS_CONFIG" || { echo "$JENKINS_CONFIG not existing";
    if [ "$1" = "stop" ]; then exit 0;
    else exit 6; fi; }

##判断配置文件是否有权限读取
test -r "$JENKINS_CONFIG" || { echo "$JENKINS_CONFIG not readable. Perhaps you forgot 'sudo'?";
    if [ "$1" = "stop" ]; then exit 0;
    else exit 6; fi; }

##定义jenkins运行时的pid(防止jenkins被启动多份,因为jenkins在运行时可以指定多个端口,但是其他的目录并没有额外指定,这会维护软件设计的初衷,所以除非有特殊设置,否则只允许启动一份实例)

JENKINS_PID_FILE="/var/run/jenkins.pid"

#

JENKINS_LOCKFILE="/var/lock/subsys/jenkins"

#加载系统的functions库,functions库的详见本博客的functions库
. /etc/init.d/functions

# 利用 -f 判断:文件是否存在且为普通文件则为真. file 可以把文件里的配置加载到该shell里
[ -f "$JENKINS_CONFIG" ] && . "$JENKINS_CONFIG"

# 根据配置文件来设置环境变量(这是一种不错的设置思路,软件开发过程中可以借鉴)

##判断JENKINS_HOME的位置是否是合法的
[ -n "$JENKINS_HOME" ] || { echo "JENKINS_HOME not configured in $JENKINS_CONFIG";
    if [ "$1" = "stop" ]; then exit 0;
    else exit 6; fi; }

## -d 用于文件存在且为目录则为真
[ -d "$JENKINS_HOME" ] || { echo "JENKINS_HOME directory does not exist: $JENKINS_HOME";
    if [ "$1" = "stop" ]; then exit 0;
    else exit 1; fi; }

# Search usable Java as /usr/bin/java might not point to minimal version required by Jenkins.
# see http://www.nabble.com/guinea-pigs-wanted-----Hudson-RPM-for-RedHat-Linux-td25673707.html

##usr/bin/java可能不是jenkins运行支持的jdk,candidate(候选)的jdk

##此处蕴含了可以把字符串当做数组来迭代!!!!!
candidates="
/etc/alternatives/java
/usr/lib/jvm/java-1.8.0/bin/java
/usr/lib/jvm/jre-1.8.0/bin/java
/usr/lib/jvm/java-1.7.0/bin/java
/usr/lib/jvm/jre-1.7.0/bin/java
/usr/lib/jvm/java-11.0/bin/java
/usr/lib/jvm/jre-11.0/bin/java
/usr/lib/jvm/java-11-openjdk-amd64
/usr/bin/java
"

## -x 用于判断文件存在且可执行则为真 ,如果配置文件里制定了jdk则用配置文件里的jdk 
for candidate in $candidates
do
  [ -x "$JENKINS_JAVA_CMD" ] && break
  JENKINS_JAVA_CMD="$candidate"
done

JAVA_CMD="$JENKINS_JAVA_CMD

#JENKINS_JAVA_OPTIONS="-Djava.awt.headless=true" 默认开始了绘图无显示设备模式

$JENKINS_JAVA_OPTIONS -DJENKINS_HOME=$JENKINS_HOME -jar $JENKINS_WAR"

##指定了jenkins自身的运行日志的目录
PARAMS="--logfile=/var/log/jenkins/jenkins.log

##项目运行时会把${JENKINS_WAR}解压放到cache目录里,jenkins其实是一个web架构项目

##  是否会每一次运行都去把这个执行解压,还是解压一次后,就不再管了,这个需要看源码

--webroot=/var/cache/jenkins/war --daemon"

## -n用于判断:字符串的长度不为零,判断是否指定JENKINS_PORT(jenkins监听的端口号)
[ -n "$JENKINS_PORT" ] && PARAMS="$PARAMS --httpPort=$JENKINS_PORT"

## -n用于判断:字符串的长度不为零,判断是否指定jenkins响应指定IP的请求,默认监听所有IP (0.0.0.0),若安全性有要求,可只监听指定的IP
[ -n "$JENKINS_LISTEN_ADDRESS" ] && PARAMS="$PARAMS --httpListenAddress=$JENKINS_LISTEN_ADDRESS"

## -n用于判断:字符串的长度不为零,判断是否指定HTTPS_PORT(默认是关闭的,也没有配置)

如果判断表达式为真,会把当前当前参数拼接在一起


[ -n "$JENKINS_HTTPS_PORT" ] && PARAMS="$PARAMS --httpsPort=$JENKINS_HTTPS_PORT"

 

## -n用于判断:字符串的长度不为零,判断是否指定HTTPS_KEYSTORE(默认是没有启用https,也没有配置,HTTPS_KEYSTORE与HTTPS应该同时出现才行)如果判断表达式为真,会把当前当前参数拼接在一起

[ -n "$JENKINS_HTTPS_KEYSTORE" ] && PARAMS="$PARAMS --httpsKeyStore=$JENKINS_HTTPS_KEYSTORE"

## -n用于判断:字符串的长度不为零,判断是否指定HTTPS_KEYSTORE_PASSWORD(默认是没有启用https,也没有配置,HTTPS_KEYSTORE_PASSWORD与HTTPS_KEYSTORE应该同时出现才行)如果判断表达式为真,会把当前当前参数拼接在一起
[ -n "$JENKINS_HTTPS_KEYSTORE_PASSWORD" ] && PARAMS="$PARAMS --httpsKeyStorePassword='$JENKINS_HTTPS_KEYSTORE_PASSWORD'"

## -n用于判断:字符串的长度不为零,判断是否指定jenkins响应指定IP的请求,默认监听所有IP (0.0.0.0),若安全性有要求,可只监听指定的IP
[ -n "$JENKINS_HTTPS_LISTEN_ADDRESS" ] && PARAMS="$PARAMS --httpsListenAddress=$JENKINS_HTTPS_LISTEN_ADDRESS"

## -n用于判断:字符串的长度不为零,判断是否启用了要监听Http2协议的监听自定义端口
[ -n "$JENKINS_HTTP2_PORT" ] && PARAMS="$PARAMS --http2Port=$JENKINS_HTTP2_PORT"

## -n用于判断:字符串的长度不为零,判断是否指定jenkins响应指定IP的请求HTTP2,默认监听所有IP (0.0.0.0),若安全性有要求,可只监听指定的IP
[ -n "$JENKINS_HTTP2_LISTEN_ADDRESS" ] && PARAMS="$PARAMS --http2ListenAddress=$JENKINS_HTTP2_LISTEN_ADDRESS"

## -n用于判断:字符串的长度不为零,判断是否指定了Jenkins的日志级别(默认=5=INFO
[ -n "$JENKINS_DEBUG_LEVEL" ] && PARAMS="$PARAMS --debug=$JENKINS_DEBUG_LEVEL"


[ -n "$JENKINS_HANDLER_STARTUP" ] && PARAMS="$PARAMS --handlerCountStartup=$JENKINS_HANDLER_STARTUP"

##JENKINS_HANDLER_MAX http连接池的MAX数量(默认是100,超过这个数量会被拒绝访问)
[ -n "$JENKINS_HANDLER_MAX" ] && PARAMS="$PARAMS --handlerCountMax=$JENKINS_HANDLER_MAX"

##JENKINS_HANDLER_IDLE http连接池的IDEL数量
[ -n "$JENKINS_HANDLER_IDLE" ] && PARAMS="$PARAMS --handlerCountMaxIdle=$JENKINS_HANDLER_IDLE"

##jenkins默认选择winstone作为servlet容器,如果有必要刻意选择jetty-runner.jar作为servlet容器
[ -n "$JENKINS_EXTRA_LIB_FOLDER" ] && PARAMS="$PARAMS --extraLibFolder=$JENKINS_EXTRA_LIB_FOLDER"

##jenkins支持的其他的额外的参数 可以通过java -jar jenkins.war --help 查看详见本博客
[ -n "$JENKINS_ARGS" ] && PARAMS="$PARAMS $JENKINS_ARGS"

##jenkins 是否支持把访问日志记录下,如果支持会选择LoggerClassName ,日志记录格式,以及日志记录的位置

if [ "$JENKINS_ENABLE_ACCESS_LOG" = "yes" ]; then
    PARAMS="$PARAMS --accessLoggerClassName=winstone.accesslog.SimpleAccessLogger --simpleAccessLogger.format=combined --simpleAccessLogger.file=/var/log/jenkins/access_log"
fi

RETVAL=0

 

 

case "$1" in

##开始判断命令带过来的第一个参数,如果是start,就开始以daemon模式启动
    start)
    echo -n "Starting Jenkins "

##指定进程运行的属主+指定文件运行的pid文件(pid会被写进该文件里)
    daemon --user "$JENKINS_USER" --pidfile "$JENKINS_PID_FILE" $JAVA_CMD $PARAMS > /dev/null

##判断 代码有没有启动起来$?能得到命令执行后的结果,如果是0就是true
    RETVAL=$?
    if [ $RETVAL = 0 ]; then

##success是/etc/init.d/functions 定义的空壳函数
        success

 ## just in case we fail to find it(每次启动我们都要把jenkins_pid_file替换为一个新的空文件)
        echo > "$JENKINS_PID_FILE"

 ## $$能获取当前脚本进程号(pid),据此找到该进程的sessionId(-o 后面sess是定制的返回值)
            MY_SESSION_ID=`/bin/ps h -o sess -p $$`
            # get PID

## 通过ps -hu jenkins的运行属主 -o 定制显示输出格式sessionid,pid,cmd 

##把ps得到的结果里的command字段用来和jenkins指定位置的启动包进行匹配(防止一台机器上在不同的位置上部署了多个jenkins给误杀了)
            /bin/ps hww -u "$JENKINS_USER" -o sess,ppid,pid,cmd | \
            while read sess ppid pid cmd; do
        [ "$ppid" = 1 ] || continue
        # this test doesn't work because Jenkins sets a new Session ID
                # [ "$sess" = "$MY_SESSION_ID" ] || continue

##判断cmd 命令里是否是指定位置的jar包,$?返回的是上一个排在最后的命令(grep)执行的结果
               echo "$cmd" | grep $JENKINS_WAR > /dev/null

##中括号里面可以直接用于计算数学表达式(数字只能用=和!=号判断其他符号都不支持)
        [ $? = 0 ] || continue
        # found a PID 如果是启动jenkins会把当程序的pid写入到一个文件
        echo $pid > "$JENKINS_PID_FILE"
        done
        touch $JENKINS_LOCKFILE
    else

##failure 是/etc/init.d/functions 定义的空壳函数
        failure  
    fi
    echo
    ;;

## 如果是shell携带的参数是stop则执行停止jenkins已启动的进程
    stop)
    echo -n "Shutting down Jenkins "

## 当jenkins 注册为服务以后,killproc即可杀死由service jenkins start 启动的服务

##killproc 函数需要阅读/etc/init.d/functions functions里定义的killproc如果没有传递信号量只是尝试给进程发送stop信号量(需要程序里做出相应才行),并没有强制杀死进程
    killproc jenkins
    rm -f $JENKINS_LOCKFILE
    RETVAL=$?
    echo
    ;;
    try-restart|condrestart)
    if test "$1" = "condrestart"; then
        echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}"
    fi
    $0 status
    if test $? = 0; then
        $0 restart
    else
        : # Not running is not a failure.
    fi
    ;;
    restart)
    $0 stop
    $0 start
    ;;

    force-reload)
    echo -n "Reload service Jenkins "
    $0 try-restart
    ;;
    reload)
        $0 restart
    ;;
    status)
        status jenkins
    RETVAL=$?
    ;;
    probe)
    ## Optional: Probe for the necessity of a reload, print out the
    ## argument to this init script which is required for a reload.
    ## Note: probe is not (yet) part of LSB (as of 1.9)

    test "$JENKINS_CONFIG" -nt "$JENKINS_PID_FILE" && echo reload
    ;;
    *)
    echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}"
    exit 1
    ;;
esac
exit $RETVAL

 

2:分析总结

分析该文件花费了我20个小时左右:收获颇多

引用其他文件已定义好的函数/etc/init.d/functions
把一个服务加入到service可以通过service 服务名(文件名) stop|start|restart|status 定制操作
利用[数字变量=某个数值]||continue 结束流程对于不太想写过多if then fi的时候可以参考
killproc pragram杀死已启动的程序killproc是funtions里的函数,用信号量去杀死程序(程序里也需要配合信号量)

 

3:jenkins的脚本流程

先读取某个固定配置文件里的配置参数

如果某些项没有填写,就用默认的值

最后再解析命令的操作(stop|start|restart|status|...)

然后针对这些流程做出相应的处理


 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值