Hadoop啓動腳本分析

1 基本概述

Hadoop的命令位於${HADOOP_HOME}/bin、${HADOOP_HOME}/sbin、${HADOOP_HOME}/libexec下面。包含了Linux的shell腳本和windows的批處理文件。本文主要解析linux下的shell腳本。

 

2 腳本詳解

2.1 start-all.sh

要啓動Hadoop的各節點和其他服務,這是一個繞不開的啓動腳本,該腳本位於${HADOOP_HOME}/sbin下。不過在Hadoop的2.x版本中,Hadoop官方已經宣佈被棄用了。接下來,就詳細地分析一下該腳本是如何工作的:

1、首先腳本開頭有一段註釋:# Start all hadoop daemons.  Run this on master node.中文意思是:啓動所有的進程(也就是各節點),在管理節點(也就是namenode-名稱節點)上運行該腳本。

 

2、如果是2.x版本會有echo "This script is Deprecated. Instead usestart-dfs.sh and start-yarn.sh"的提示,意思該腳本已經過時,該腳本已經被start-dfs.sh和 start-yarn.sh替代使用。

 

3、bin=`dirname"${BASH_SOURCE-$0}"`,提取start-all.sh的所在的絕對路徑。

 

4、bin=`cd"$bin"; pwd`,切換到start-all.sh的所在目錄下,並將路徑賦值給bin。

 

5、DEFAULT_LIBEXEC_DIR="$bin"/../libexec,獲取${HADOOP_HOME}/libexec的絕對路徑以備後用。

 

6、HADOOP_LIBEXEC_DIR=${HADOOP_LIBEXEC_DIR:-$DEFAULT_LIBEXEC_DIR},爲HADOOP_LIBEXEC_DIR變量三元賦值。如果HADOOP_LIBEXEC_DIR爲空或者環境變量沒有配置,就賦值默認的絕對路徑,爲下一步執行該目錄下面的腳本做準備。

 

7、.$HADOOP_LIBEXEC_DIR/hadoop-config.sh。執行${HADOOP_HOME}/libexec/hadoop-config.sh腳本。爲後面執行啓動各節點和啓動YARN做預處理。

 

8、執行節點啓動腳本

if [ -f"${HADOOP_HDFS_HOME}"/sbin/start-dfs.sh ]; then

"${HADOOP_HDFS_HOME}"/sbin/start-dfs.sh--config $HADOOP_CONF_DIR

fi

這段腳本的意圖是:如果${HADOOP_HDFS_HOME}/sbin/start-dfs.sh爲文件,則聯合--config參數及其後面的參數值執行start-dfs.sh。start-dfs.sh後面做詳細分析。

另外看註釋:# start hdfsdaemons if hdfs is present,中文意思是:啓動hdfs進程

 

9、執行YRAN調度服務

if [ -f"${HADOOP_YARN_HOME}"/sbin/start-yarn.sh ]; then

 "${HADOOP_YARN_HOME}"/sbin/start-yarn.sh --config$HADOOP_CONF_DIR

fi

這段腳本的意圖是:如果${HADOOP_HDFS_HOME}/sbin/start-yarn.sh爲文件,則聯合--config參數及其後面的參數值執行start-yarn.sh。start-yarn.sh後面做詳細分析。

  另外看註釋:# start yarndaemons if yarn is present,中文意思是:啓動yarn進程

 

備註${HADOOP_HDFS_HOME}已經在hadoop-config.sh做了預處理。hadoop-config.sh後面做詳細的分析。

 

2.2 hadoop-config.sh

該腳本位於${HADOOP_HOME}/libexec下。該腳本是在啓動節點和其他服務之前必須要執行的一個腳本。它的主要目的使用啓動Hadoop之前做一些環境變量預處理。接下來,就詳細地分析一下該腳本是如何工作的:

      1、文件最前面的註釋:Resolvelinks ($0 may be a softlink) and convert a relative path to an absolute path。中文意思是:解析路徑並將一些相對路徑轉成絕對路徑。

 

      2、執行hadoop-layout.sh腳本

this="${BASH_SOURCE-$0}"

common_bin=$(cd-P -- "$(dirname -- "$this")" && pwd -P)

script="$(basename-- "$this")"

this="$common_bin/$script"

[ -f"$common_bin/hadoop-layout.sh" ] && ."$common_bin/hadoop-layout.sh"

      這段腳本的意圖就是找出hadoop-layout.sh並執行,如果沒有找出來就不執行。同時有些變量爲後面的程序執行做準備。

 

      3、準備HDFS、YRAN、Mapreduce的jar包路徑並設置路徑

HADOOP_COMMON_DIR=${HADOOP_COMMON_DIR:-"share/hadoop/common"}

HADOOP_COMMON_LIB_JARS_DIR=${HADOOP_COMMON_LIB_JARS_DIR:-"share/hadoop/common/lib"}

HADOOP_COMMON_LIB_NATIVE_DIR=${HADOOP_COMMON_LIB_NATIVE_DIR:-"lib/native"}

HDFS_DIR=${HDFS_DIR:-"share/hadoop/hdfs"}

HDFS_LIB_JARS_DIR=${HDFS_LIB_JARS_DIR:-"share/hadoop/hdfs/lib"}

YARN_DIR=${YARN_DIR:-"share/hadoop/yarn"}

YARN_LIB_JARS_DIR=${YARN_LIB_JARS_DIR:-"share/hadoop/yarn/lib"}

MAPRED_DIR=${MAPRED_DIR:-"share/hadoop/mapreduce"}

MAPRED_LIB_JARS_DIR=${MAPRED_LIB_JARS_DIR:-"share/hadoop/mapreduce/lib"}

     

      4、設置${Hadoop_Home}的根目錄的環境變量

      HADOOP_DEFAULT_PREFIX=$(cd -P --"$common_bin"/.. && pwd -P)

HADOOP_PREFIX=${HADOOP_PREFIX:-$HADOOP_DEFAULT_PREFIX}

export HADOOP_PREFIX

 

5、判斷輸出參數

if [ $# -gt 1 ]

then

    if ["--config" = "$1" ]

     then

         shift

         confdir=$1

         if [ ! -d "$confdir" ]; then

               echo "Error: Cannot find configuration directory: $confdir"

               exit 1

             fi

         shift

         HADOOP_CONF_DIR=$confdir

    fi

fi

 

這段代碼參數的意思:$#是表示輸入的參數個數,如果個數大於1,則判斷—config後面的參數是否是一個目錄。如果不是,則退出執行,如果是將傳入的參數賦值給HADOOP_CONF_DIR變量,即將HADOOP_CONF_DIR變量定位到hadoop的配置目錄。

     

6、設置hadoop的日誌級別

if [ $# -gt 1 ]

then

  if [ "--loglevel" = "$1"]

  then

    shift

    HADOOP_LOGLEVEL=$1

    shift

  fi

fi

HADOOP_LOGLEVEL="${HADOOP_LOGLEVEL:-INFO}"

      這段代碼的意思是:如果沒有傳入日誌參數,則默認爲INFO級別。

 

      7、設置HADOOP_CONF_DIR配置的環境變量,也是啓動Hadoop的工作目錄(--config後面的參數值)

if [ -e"${HADOOP_PREFIX}/conf/hadoop-env.sh" ]; then

  DEFAULT_CONF_DIR="conf"

else

  DEFAULT_CONF_DIR="etc/hadoop"

fi

 

exportHADOOP_CONF_DIR="${HADOOP_CONF_DIR:-$HADOOP_PREFIX/$DEFAULT_CONF_DIR}"

      這段的代碼的意圖是:如果參數值存在,輸出傳入的參數值;如果不存在,使用默認的目錄,一般位於${HADOOP_HOME}/etc/Hadoop.

 

      8、設置部署各個節點hostname配置的環境變量

if [ $# -gt 1 ]

then

    if [ "--hosts" = "$1" ]

    then

        shift

        exportHADOOP_SLAVES="${HADOOP_CONF_DIR}/$1"

        shift

    elif [ "--hostnames" ="$1" ]

    then

        shift

        export HADOOP_SLAVE_NAMES=$1

        shift

    fi

fi

      這段代碼:讀取${HADOOP_HOME}/${HADOOP_CONF_DIR}/slaves文件裏面的主機配置列表並輸出HADOOP_SLAVES或者HADOOP_SLAVE_NAMES環境變量。

 

      9、設置hadoop運行的其他環境變量

      類似於上面:如果在操作系統的設置過環境變量,則直接使用。如果沒有設置,則設置成默認的環境變量。這些環境變量有:JAVA_HOME、CLASSPATH、JVM啓動參數、JAVA_LIBRARY_PATH、MALLOC_ARENA_MAX等,這裏不再一一贅述。如果有不懂的地方,自行查詢java的相關環境變量配置資料

同時還設置了以下的環境變量:HADOOP_HOME、HADOOP_OPTS、HADOOP_COMMON_HOME、TOOL_PATH、HADOOP_HDFS_HOME、LD_LIBRARY_PATHHADOOP_YARN_HOME、HADOOP_MAPRED_HOME。這些環境變量的設置都引用了前面的變量,這裏也不再一一贅述。


  2.3 start-hdfs.sh

該腳本位於${HADOOP_HOME}/sbin下。該腳本是啓動集羣各節點(包括名稱主節點、各個名稱從節點、各個數據節點)的一個腳本。接下來,就詳細地分析一下該腳本是如何工作的:

      1、文件最前面的註釋:Starthadoop dfs daemons。中文意思是:啓動dfs的各個進程。

 

      2、提示信息變量:usage="Usage:start-dfs.sh [-upgrade|-rollback] [other options such as -clusterId]"。提示在執行這個腳本的時候需要主要哪些東西的一個提示信息。

 

      3、執行預處理腳本,這個腳本在前面已經分析過了,不再贅述。

      bin=`dirname"${BASH_SOURCE-$0}"`

bin=`cd "$bin"; pwd`

DEFAULT_LIBEXEC_DIR="$bin"/../libexec

HADOOP_LIBEXEC_DIR=${HADOOP_LIBEXEC_DIR:-$DEFAULT_LIBEXEC_DIR}

. $HADOOP_LIBEXEC_DIR/hdfs-config.sh

則也就是不推薦使用的start-all.sh這個腳本的原因所在。

 

      4、根據傳入參數進行判斷進行相應的變量設置

if [[ $# -ge 1]]; then

  startOpt="$1"

  shift

  case "$startOpt" in

    -upgrade)

      nameStartOpt="$startOpt"

    ;;

    -rollback)

      dataStartOpt="$startOpt"

    ;;

    *)

      echo $usage

      exit 1

    ;;

  esac

fi

      這段代碼的意思是:如果參數值的個數大於等於1的時候,如果參數是upgrade,則設置nameStartOpt變量以備後用;如果是rollback,則設置dataStartOpt變量以備後用。

 

      5、添加名稱節點可能參數值

      nameStartOpt="$nameStartOpt$@"

     

      6、啓動管理(主)名稱節點

NAMENODES=$($HADOOP_PREFIX/bin/hdfsgetconf -namenodes)

echo"Starting namenodes on [$NAMENODES]"

 

"$HADOOP_PREFIX/sbin/hadoop-daemons.sh"\

  --config"$HADOOP_CONF_DIR" \

  --hostnames"$NAMENODES" \

  --script"$bin/hdfs" start namenode $nameStartOpt

      這段的意思是:將config、hostnames、script的參數值傳給hadoop-daemons.sh執行,啓動名稱主節點。實際上是通過hdfs命令啓動的。

$($HADOOP_PREFIX/bin/hdfs getconf -namenodes)是在提取主名稱節點的主機名。

--script "$bin/hdfs" startnamenode $nameStartOpt實際啓動名稱節點。

      hadoop-daemons.sh腳本後面在做詳細分析

     

      7、啓動數據節點

if [ -n"$HADOOP_SECURE_DN_USER" ]; then

  echo \

    "Attempting to start secure cluster,skipping datanodes. " \

    "Run start-secure-dns.sh as root tocomplete startup."

else

  "$HADOOP_PREFIX/sbin/hadoop-daemons.sh"\

    --config "$HADOOP_CONF_DIR" \

    --script "$bin/hdfs" startdatanode $dataStartOpt

fi

 

這段的意思是:將config、script的參數值傳給hadoop-daemons.sh執行,啓動數據節點節點。實際上也是通過hdfs命令啓動的。

if [ -n "$HADOOP_SECURE_DN_USER"]; then這段代碼可以忽略,基本不用。

--script "$bin/hdfs" startdatanode $dataStartOpt實際啓動名稱節點。

 

      8、啓動從名稱節點

SECONDARY_NAMENODES=$($HADOOP_PREFIX/bin/hdfsgetconf -secondarynamenodes 2>/dev/null)

if [ -n"$SECONDARY_NAMENODES" ]; then

  echo "Starting secondary namenodes[$SECONDARY_NAMENODES]"

 

  "$HADOOP_PREFIX/sbin/hadoop-daemons.sh"\

      --config "$HADOOP_CONF_DIR" \

      --hostnames"$SECONDARY_NAMENODES" \

      --script "$bin/hdfs" startsecondarynamenode

Fi

      這段的意思是:將config、hostnames、script的參數值傳給hadoop-daemons.sh執行,啓動名稱主節點。實際上是通過hdfs命令啓動的。

SECONDARY_NAMENODES=$($HADOOP_PREFIX/bin/hdfsgetconf -secondarynamenodes 2>/dev/null)是在提取從名稱節點的主機名。

 

--script "$bin/hdfs" start namenode secondarynamenode實際啓動名稱節點。

 

      9、後面的執行

      後面的執行過程,後面有時間一一補充。

 

  2.4 hadoop-daemons.sh

該腳本位於${HADOOP_HOME}/sbin下。該腳本是具體執行啓動集羣各節點(包括名稱主節點、各個名稱從節點、各個數據節點)的一個腳本。接下來,就詳細地分析一下該腳本是如何工作的:

1、其它的代碼可以不做重點關注

2、exec"$bin/slaves.sh" --config $HADOOP_CONF_DIR cd"$HADOOP_PREFIX" \; "$bin/hadoop-daemon.sh" --config$HADOOP_CONF_DIR "$@"

      拆解這段代碼:

A、先執行slaves.sh腳本,這個腳本是根據slaves的主機列表迭代執行hadoop-daemon.sh腳本。

B、然後將所有參數傳入並執行hadoop-daemon.sh腳本。

 

   2.5 hadoop-daemon.sh

        該腳本位於${HADOOP_HOME}/sbin下。該腳本是具體執行啓動集羣各節點(包括名稱主節點、各個名稱從節點、各個數據節點)的一個腳本。接下來,就詳細地分析一下該腳本是如何工作的:

      1、先調用hadoop-config.sh腳本,不再贅述。

 

      2、提取hadoop命令並設置變量:

            hadoopScript="$HADOOP_PREFIX"/bin/Hadoop

 

      3、提取命令(或是啓動命令,或是停止命令),提取節點(或者名稱節點,或是數據節點)

        hadoopScript="$HADOOP_PREFIX"/bin/hadoop

        if[ "--script" = "$1" ]

          then

            shift

            hadoopScript=$1

            shift

        fi

        startStop=$1

        shift

        command=$1

        shift

 

      4、輸出日誌相關

        hadoop_rotate_log()

        {

            log=$1;

            num=5;

            if [ -n "$2" ]; then

              num=$2

            fi

            if [ -f "$log" ]; then # rotatelogs

              while [ $num -gt 1 ]; do

                 prev=`expr $num - 1`

                 [ -f "$log.$prev" ] && mv "$log.$prev""$log.$num"

                 num=$prev

              done

              mv "$log" "$log.$num";

            fi

        }

 

      5、執行環境變量設置腳本

        if[ -f "${HADOOP_CONF_DIR}/hadoop-env.sh" ]; then

          ."${HADOOP_CONF_DIR}/hadoop-env.sh"

        fi

 

      6、根據啓動節點的不同重新設置不同的變量值

        #Determine if we're starting a secure datanode, and if so, redefine appropriatevariables

        if[ "$command" == "datanode" ] && [ "$EUID"-eq 0 ] && [ -n "$HADOOP_SECURE_DN_USER" ]; then

          exportHADOOP_PID_DIR=$HADOOP_SECURE_DN_PID_DIR

          exportHADOOP_LOG_DIR=$HADOOP_SECURE_DN_LOG_DIR

          exportHADOOP_IDENT_STRING=$HADOOP_SECURE_DN_USER

          starting_secure_dn="true"

        fi

         

        #Determineif we're starting a privileged NFS, if so, redefine the appropriate variables

        if[ "$command" == "nfs3" ] && [ "$EUID" -eq0 ] && [ -n "$HADOOP_PRIVILEGED_NFS_USER" ]; then

            exportHADOOP_PID_DIR=$HADOOP_PRIVILEGED_NFS_PID_DIR

            exportHADOOP_LOG_DIR=$HADOOP_PRIVILEGED_NFS_LOG_DIR

            exportHADOOP_IDENT_STRING=$HADOOP_PRIVILEGED_NFS_USER

            starting_privileged_nfs="true"

        fi

 

      7、中間的都是輸出日誌和設置其他臨時變量和環境變量,不做重點介紹,請自行參考腳本

     

8、啓動或者停止節點

case$startStop in

 

  (start)

 

    [ -w "$HADOOP_PID_DIR" ] ||  mkdir -p "$HADOOP_PID_DIR"

 

    if [ -f $pid ]; then

      if kill -0 `cat $pid` > /dev/null2>&1; then

        echo $command running as process `cat$pid`.  Stop it first.

        exit 1

      fi

    fi

 

    if [ "$HADOOP_MASTER" !="" ]; then

      echo rsync from $HADOOP_MASTER

      rsync -a -e ssh --delete --exclude=.svn--exclude='logs/*' --exclude='contrib/hod/logs/*' $HADOOP_MASTER/"$HADOOP_PREFIX"

    fi

 

    hadoop_rotate_log $log

    echo starting $command, logging to $log

    cd "$HADOOP_PREFIX"

    case $command in

      namenode|secondarynamenode|datanode|journalnode|dfs|dfsadmin|fsck|balancer|zkfc)

        if [ -z "$HADOOP_HDFS_HOME"]; then

         hdfsScript="$HADOOP_PREFIX"/bin/hdfs

        else

         hdfsScript="$HADOOP_HDFS_HOME"/bin/hdfs

        fi

        nohup nice -n $HADOOP_NICENESS$hdfsScript --config $HADOOP_CONF_DIR $command "$@" >"$log" 2>&1 < /dev/null &

      ;;

      (*)

        nohup nice -n $HADOOP_NICENESS$hadoopScript --config $HADOOP_CONF_DIR $command "$@" >"$log" 2>&1 < /dev/null &

      ;;

    esac

    echo $! > $pid

    sleep 1

    head "$log"

    # capture the ulimit output

    if [ "true" ="$starting_secure_dn" ]; then

      echo "ulimit -a for secure datanodeuser $HADOOP_SECURE_DN_USER" >> $log

      # capture the ulimit info for theappropriate user

      su --shell=/bin/bash$HADOOP_SECURE_DN_USER -c 'ulimit -a' >> $log 2>&1

    elif [ "true" ="$starting_privileged_nfs" ]; then

        echo "ulimit -a for privileged nfsuser $HADOOP_PRIVILEGED_NFS_USER" >> $log

        su --shell=/bin/bash $HADOOP_PRIVILEGED_NFS_USER-c 'ulimit -a' >> $log 2>&1

    else

      echo "ulimit -a for user $USER">> $log

      ulimit -a >> $log 2>&1

    fi

    sleep 3;

    if ! ps -p $! > /dev/null ; then

      exit 1

    fi

    ;;

         

  (stop)

 

    if [ -f $pid ]; then

      TARGET_PID=`cat $pid`

      if kill -0 $TARGET_PID > /dev/null2>&1; then

        echo stopping $command

        kill $TARGET_PID

        sleep $HADOOP_STOP_TIMEOUT

        if kill -0 $TARGET_PID > /dev/null2>&1; then

          echo "$command did not stopgracefully after $HADOOP_STOP_TIMEOUT seconds: killing with kill -9"

          kill -9 $TARGET_PID

        fi

      else

        echo no $command to stop

      fi

      rm -f $pid

    else

      echo no $command to stop

    fi

    ;;

 

  (*)

    echo$usage

    exit 1

    ;;

 

esac

 

      這是一個分支執行流程,分爲啓動節點和停止節點:

            A、啓動節點分支:在該分支下,首先判斷節點主機上的PID(其實就是默認設置的端口)是否被佔用,如果被佔用則停止啓動。如果沒有被佔用,則開始打印日誌,也是我們在控制檯上看到的日誌(echo starting $command, logging to $log)。表明開始啓動節點上的hadoop進程,然後根據$command變量判斷啓動哪個節點,最後將所有的參數傳入hdfs命令啓動節點,至此,該節點就啓動起來了。

            B、停止節點分支:在該分支下,首先判斷節點主機上的PID(其實就是默認設置的端口)是否被佔用,如果被佔用則殺掉進程,如果沒有被殺掉,則使用kill-9強制殺掉。如果PID沒有被佔用,則會打印提示信息:“沒有XX節點需要停止”, 至此,該節點就停止服務了。

 

 2.6 hdfs

      該腳本首先判斷是否有參數傳入或者參數是否正確,如果沒有參數或者參數錯誤,就會提示用戶需要傳遞參數或者傳遞正確的參數。

      然後根據參數值判斷後調用jdk(JAVA_HOME)下相應的腳本,最後啓動相應的java進程。

      總而言之,這個腳本纔是啓動hadoop的最終歸宿,如果能掌握hadoop裏面的所有入口函數,也可以認爲這個腳本也不是最終歸宿,因爲這個腳本的最終目的就是通過調用jdk下面的java命令啓動Hadoop的必要入口函數,也就是main方法,最終達到啓動整個Hadoop集羣的目的。

 

 2.7 start-yarn.sh

      該腳本的執行流程與start-hdfs的執行流程類似,請自行參考該shell腳本,這裏不再贅述。

 

 2.8 yarn-daemons.sh

      該腳本的執行流程與hadoop-daemons.sh的執行流程類似,請自行參考該shell腳本,這裏不再贅述。

 

 2.9 yarn-daemon.sh

      該腳本的執行流程與hadoop-daemon.sh的執行流程類似,請自行參考該shell腳本,這裏不再贅述。

     

 2.10 各停止腳本

      從上面hadoop-daemon.sh可以看出,停止各節點的服務,無外乎也就是先提取相應的環境變量,然後殺掉相應的進程,將佔用端口釋放出來,沒什麼大書特書的。如有興趣,可以自行研究停止腳本,這裏不再做詳細分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章