前置条件
1. 三台主机名已配置为 node11/node12/node13,且能互相 ping 通;
2. root 用户节点间 ssh/scp 免密可用(部署 Hadoop 时已配置);
3. JDK 17、Hadoop 3.5.0 已部署完成;
4. scala-2.13.16.tgz 与 spark-4.1.3-bin-hadoop3.tgz 已上传到 /usr/local/soft
一键部署脚本
cd /usr/local/soft
touch spark_deploy.sh
touch spark_uninstall.sh
chmod +x spark_deploy.sh
chmod +x spark_uninstall.sh
spark_deploy.sh内容如下
#!/bin/bash
# =============================================================================
# deploy_spark.sh —— Spark 4.1.3 集群一键部署脚本
#
# 在 node11(主节点)上以 root 执行:
# 步骤1 前置检查 步骤2 node11 安装 Scala 2.13.16
# 步骤3 node11 部署 Spark 步骤4 分发 Scala/Spark/环境变量到工作节点
# 步骤5 启动 Hadoop、Spark 步骤6 jps 验证集群进程
#
# 前置条件:
# 1. 三台主机名已配置为 node11/node12/node13,且能互相 ping 通;
# 2. root 用户节点间 ssh/scp 免密可用(部署 Hadoop 时已配置);
# 3. JDK 17、Hadoop 3.5.0 已部署完成;
# 4. scala-2.13.16.tgz 与 spark-4.1.3-bin-hadoop3.tgz 已上传到 /usr/local/soft。
#
# 用法:bash deploy_spark.sh
# 脚本可重复执行:已完成的步骤自动跳过,配置块带标记、不会重复追加。
# =============================================================================
set -eo pipefail
# 注意:不启用 -u(nounset)。/etc/profile 等外部环境文件中可能存在未定义变量,
# set -u 会让 source 它们时以 "unbound variable" 致命错误无提示退出整个脚本
# ------------------------------ 可按需修改的参数 -----------------------------
SOFT_DIR="/usr/local/soft" # 软件统一安装目录
SCALA_PKG="scala-2.13.16.tgz"
SCALA_DIR="scala-2.13.16"
SPARK_PKG="spark-4.1.3-bin-hadoop3.tgz"
SPARK_DIR="spark-4.1.3-bin-hadoop3"
JAVA_HOME_DIR="$SOFT_DIR/jdk-17.0.1" # 已部署的 JDK(讲义表3-2)
HADOOP_HOME_DIR="$SOFT_DIR/hadoop-3.5.0" # 已部署的 Hadoop
HADOOP_CONF_DIR="$HADOOP_HOME_DIR/etc/hadoop"
MASTER="node11" # 主节点(讲义表3-1)
WORKERS=(node12 node13) # 工作节点
SPARK_WORKER_MEMORY="1024m" # Worker 可分配内存(讲义表3-3)
AUTO_START="yes" # 部署后是否自动启动集群 yes/no
PROFILE="/etc/profile"
MARKER_BEGIN="# >>> spark-auto-deploy >>>" # 配置块标记,保证可重复执行
MARKER_END="# <<< spark-auto-deploy <<<"
log() { echo -e "\033[32m[INFO]\033[0m $*"; }
warn() { echo -e "\033[33m[WARN]\033[0m $*"; }
die() { echo -e "\033[31m[FAIL]\033[0m $*" >&2; exit 1; }
trap 'die "部署在第 $LINENO 行出错,可用 bash -x $0 复查;处理后可重新执行本脚本"' ERR
# 向文件追加带标记的配置块;若已有旧块先整体删除,重复执行不会叠加
append_block() { # $1=目标文件,配置块内容从标准输入读入
local file="$1" tmp
touch "$file"
tmp=$(mktemp)
awk -v b="$MARKER_BEGIN" -v e="$MARKER_END" \
'$0 == b {skip=1} !skip {print} $0 == e {skip=0}' "$file" > "$tmp"
cat "$tmp" > "$file" && rm -f "$tmp"
cat >> "$file"
}
# ------------------------------ 步骤 1:前置检查 -----------------------------
log "步骤 1/6:前置检查"
[ "$(id -u)" -eq 0 ] || die "请使用 root 用户执行本脚本"
[ "$(hostname -s)" = "$MASTER" ] || die "请在 $MASTER(主节点)上执行本脚本,当前主机:$(hostname -s)"
[ -f "$SOFT_DIR/$SCALA_PKG" ] || die "缺少 $SOFT_DIR/$SCALA_PKG,请先上传"
[ -f "$SOFT_DIR/$SPARK_PKG" ] || die "缺少 $SOFT_DIR/$SPARK_PKG,请先上传"
[ -d "$JAVA_HOME_DIR" ] || die "未找到 JDK 目录 $JAVA_HOME_DIR,请先安装 JDK 17"
[ -d "$HADOOP_CONF_DIR" ] || die "未找到 Hadoop 配置目录 $HADOOP_CONF_DIR,请先部署 Hadoop"
for node in "${WORKERS[@]}"; do
ping -c 1 -W 2 "$node" >/dev/null 2>&1 || die "无法 ping 通 $node,请检查网络与 /etc/hosts"
done
# 首次 ssh 连接不进入交互确认,避免 start-all.sh 远程启动进程时卡住
export SPARK_SSH_OPTS="-o StrictHostKeyChecking=no"
export HADOOP_SSH_OPTS="-o StrictHostKeyChecking=no"
JPS="$JAVA_HOME_DIR/bin/jps"
[ -x "$JPS" ] || JPS=jps
log "前置检查通过"
# ------------------------- 步骤 2:node11 安装 Scala -------------------------
log "步骤 2/6:安装 Scala 2.13.16(node11,讲义 3.3)"
if [ ! -d "$SOFT_DIR/$SCALA_DIR" ]; then
tar -zxvf "$SOFT_DIR/$SCALA_PKG" -C "$SOFT_DIR"
log "已解压 $SCALA_PKG 到 $SOFT_DIR/$SCALA_DIR"
else
log "$SOFT_DIR/$SCALA_DIR 已存在,跳过解压"
fi
# 写入 /etc/profile:SCALA_HOME 与 SPARK_HOME 一次性配好(对应讲义两次 vi /etc/profile)
log "写入 SCALA_HOME / SPARK_HOME 环境变量到 $PROFILE"
append_block "$PROFILE" <<EOF
$MARKER_BEGIN
export SCALA_HOME=$SOFT_DIR/$SCALA_DIR
export SPARK_HOME=$SOFT_DIR/$SPARK_DIR
export PATH=\$PATH:\$SCALA_HOME/bin:\$SPARK_HOME/bin
$MARKER_END
EOF
# 不在脚本内 source /etc/profile:脚本全部使用绝对路径,无需立即生效;
# 且 source 外部环境文件可能引入不可控的报错导致脚本中断
"$SOFT_DIR/$SCALA_DIR/bin/scala" -version
log "Scala 安装完成"
# ------------------------- 步骤 3:node11 部署 Spark -------------------------
log "步骤 3/6:部署 Spark 4.1.3(node11,讲义 3.4)"
if [ ! -d "$SOFT_DIR/$SPARK_DIR" ]; then
tar -zxvf "$SOFT_DIR/$SPARK_PKG" -C "$SOFT_DIR"
log "已解压 $SPARK_PKG 到 $SOFT_DIR/$SPARK_DIR"
else
log "$SOFT_DIR/$SPARK_DIR 已存在,跳过解压"
fi
SPARK_CONF_DIR="$SOFT_DIR/$SPARK_DIR/conf"
[ -d "$SPARK_CONF_DIR" ] || die "未找到 Spark 配置目录 $SPARK_CONF_DIR,解压可能不完整"
# 3.1 spark-env.sh:由模板复制后追加配置(讲义表3-3)
if [ ! -f "$SPARK_CONF_DIR/spark-env.sh" ] && [ -f "$SPARK_CONF_DIR/spark-env.sh.template" ]; then
cp "$SPARK_CONF_DIR/spark-env.sh.template" "$SPARK_CONF_DIR/spark-env.sh"
fi
log "写入 spark-env.sh 配置"
append_block "$SPARK_CONF_DIR/spark-env.sh" <<EOF
$MARKER_BEGIN
# JDK目录:所有 Spark 进程都以它启动 JVM
export JAVA_HOME=$JAVA_HOME_DIR
# Scala目录
export SCALA_HOME=$SOFT_DIR/$SCALA_DIR
# Master 所在节点(集群入口地址);SPARK_MASTER_HOST 是 Spark 3+ 的官方变量名
export SPARK_MASTER_IP=$MASTER
export SPARK_MASTER_HOST=$MASTER
# 每个 Worker 进程可分配给 Executor 的总内存
export SPARK_WORKER_MEMORY=$SPARK_WORKER_MEMORY
# Hadoop 配置目录:Spark 通过它找到 HDFS 并接入 YARN
export HADOOP_CONF_DIR=$HADOOP_CONF_DIR
$MARKER_END
EOF
# 3.2 workers 文件:只写工作节点,绝不能保留 localhost(讲义 3.4 注意)
log "写入 workers 文件"
printf '%s\n' "${WORKERS[@]}" > "$SPARK_CONF_DIR/workers"
log "spark-env.sh 与 workers 配置完成(workers:${WORKERS[*]})"
# ---------------- 步骤 4:分发到 node12 / node13(讲义 3.3/3.4) --------------
log "步骤 4/6:分发 Scala、Spark 与环境变量到工作节点"
for node in "${WORKERS[@]}"; do
log "向 $node 分发中(如未配置免密,请按提示输入 root 密码)..."
# 先删除目标节点旧目录,避免 scp -r 把目录拷进同名旧目录里形成嵌套
ssh "root@$node" "rm -rf '$SOFT_DIR/$SCALA_DIR' '$SOFT_DIR/$SPARK_DIR'"
scp -q -r "$SOFT_DIR/$SCALA_DIR" "root@$node:$SOFT_DIR/"
scp -q -r "$SOFT_DIR/$SPARK_DIR" "root@$node:$SOFT_DIR/"
scp -q "$PROFILE" "root@$node:$PROFILE"
# 讲义约定:分发完成后在目标节点执行验证
ssh "root@$node" ". /etc/profile >/dev/null 2>&1; '$SOFT_DIR/$SCALA_DIR/bin/scala' -version"
log "$node 分发并验证完成"
done
# ------------------------- 步骤 5:启动集群(讲义 3.5) -----------------------
if [ "$AUTO_START" = "yes" ]; then
log "步骤 5/6:启动集群(先检测 Hadoop 是否已启动,已启动的不再重复启动)"
# HDFS:NameNode 已在运行则跳过
if "$JPS" -l 2>/dev/null | grep -qw NameNode; then
log "Hadoop 的 HDFS 已在运行(NameNode 正常),跳过启动"
else
[ -x "$HADOOP_HOME_DIR/sbin/start-dfs.sh" ] || die "未找到 $HADOOP_HOME_DIR/sbin/start-dfs.sh,请检查 Hadoop 安装目录"
"$HADOOP_HOME_DIR/sbin/start-dfs.sh"
log "HDFS 启动完成"
fi
# YARN:ResourceManager 已在运行则跳过
if "$JPS" -l 2>/dev/null | grep -qw ResourceManager; then
log "Hadoop 的 YARN 已在运行(ResourceManager 正常),跳过启动"
else
[ -x "$HADOOP_HOME_DIR/sbin/start-yarn.sh" ] || die "未找到 $HADOOP_HOME_DIR/sbin/start-yarn.sh,请检查 Hadoop 安装目录"
"$HADOOP_HOME_DIR/sbin/start-yarn.sh"
log "YARN 启动完成"
fi
if "$JPS" -l 2>/dev/null | grep -qw Master; then
log "Spark Master 已在运行,跳过 Spark start-all.sh"
else
# 讲义 3.5 注意:Hadoop 与 Spark 都有 start-all.sh,必须用完整路径
"$SOFT_DIR/$SPARK_DIR/sbin/start-all.sh"
log "Spark 集群启动完成"
fi
sleep 5
else
log "步骤 5/6:跳过启动与验证(AUTO_START=$AUTO_START)"
fi
# ------------------------- 步骤 6:验证集群(讲义表3-4) ----------------------
if [ "$AUTO_START" = "yes" ]; then
log "步骤 6/6:验证集群进程(讲义表3-4)"
check_proc() { # $1=节点 $2=应存在的进程
local node="$1" proc="$2"
if ssh "root@$node" ". /etc/profile >/dev/null 2>&1; jps" 2>/dev/null | grep -qw "$proc"; then
log " $node $proc 进程正常"
else
warn " $node 未发现 $proc,请登录该节点执行 jps 排查"
fi
}
check_proc "$MASTER" NameNode
check_proc "$MASTER" Master
for node in "${WORKERS[@]}"; do
check_proc "$node" DataNode
check_proc "$node" Worker
done
fi
echo
log "=============================================================="
log " Spark 集群部署完成!"
log " 版本 : Spark 4.1.3 / Scala 2.13.16 / Java 17"
log " Master : $MASTER(spark-shell / spark-submit 在此节点运行)"
log " Worker : ${WORKERS[*]}"
log " Web UI : http://$MASTER:8080(Master),Worker UI 端口 8081"
log ""
log " 当前会话请先执行 source /etc/profile,再使用 spark-shell 等命令"
log " 停止集群:$SOFT_DIR/$SPARK_DIR/sbin/stop-all.sh"
log " $HADOOP_HOME_DIR/sbin/stop-all.sh"
log "=============================================================="
一键卸载脚本
spark_uninstall.sh内容如下
#!/bin/bash
# =============================================================================
# spark_uninstall.sh —— 一键卸载 Spark 4.1.3 与 Scala 2.13.16(保留 JDK/Hadoop)
#
# 与 spark_deploy.sh 配套,按部署的逆序在 node11(主节点)上以 root 执行:
# 步骤1 前置检查 步骤2 停止 Spark 集群(不动 Hadoop)
# 步骤3 清除 /etc/profile 配置块 步骤4 删除 scala、spark 安装目录
# 步骤5 验证:spark/scala 已清除,java/hadoop 完好
#
# 说明:
# 1. 只删除 spark_deploy.sh 装的 scala-2.13.16 与 spark-4.1.3-bin-hadoop3
# 两个目录;
# 2. /etc/profile 中与 Scala/Spark 相关的环境变量全部清除,包括两类:
# a) spark_deploy.sh 写入的标记配置块;
# b) 手动 vi 添加的 SCALA_HOME、SPARK_HOME 及其 PATH 行;
# 3. JDK、Hadoop 及其在 /etc/profile 中的配置一概不动;
# 4. /usr/local/soft 下的两个 .tgz 安装包保留,需要时请自行删除;
# 5. 脚本可重复执行:已卸载的部分自动跳过。
#
# 用法:bash spark_uninstall.sh (直接卸载,无需用户确认)
# =============================================================================
set -u
# ------------------------------ 可按需修改的参数 -----------------------------
# 与 spark_deploy.sh 保持一致
SOFT_DIR="/usr/local/soft" # 软件统一安装目录
SCALA_DIR="scala-2.13.16"
SPARK_DIR="spark-4.1.3-bin-hadoop3"
JAVA_HOME_DIR="$SOFT_DIR/jdk-17.0.1" # 保留:已部署的 JDK
HADOOP_HOME_DIR="$SOFT_DIR/hadoop-3.5.0" # 保留:已部署的 Hadoop
HADOOP_CONF_DIR="$HADOOP_HOME_DIR/etc/hadoop"
MASTER="node11"
WORKERS=(node12 node13)
PROFILE="/etc/profile"
MARKER_BEGIN="# >>> spark-auto-deploy >>>" # 与 spark_deploy.sh 相同的标记
MARKER_END="# <<< spark-auto-deploy <<<"
log() { echo -e "\033[32m[INFO]\033[0m $*"; }
warn() { echo -e "\033[33m[WARN]\033[0m $*"; }
die() { echo -e "\033[31m[FAIL]\033[0m $*" >&2; exit 1; }
JPS="$JAVA_HOME_DIR/bin/jps"
[ -x "$JPS" ] || JPS=jps
# 清除文件中的 Scala/Spark 环境变量:标记配置块 + 手动添加的相关行
remove_block() { # $1=目标文件
local file="$1" tmp leftover
[ -f "$file" ] || return 0
if ! grep -qF "$MARKER_BEGIN" "$file" \
&& ! grep -q 'SCALA_HOME=' "$file" \
&& ! grep -q 'SPARK_HOME=' "$file"; then
log "$file 中未发现 Scala/Spark 环境变量,跳过"
return 0
fi
# 逐行过滤,删除两类内容:
# 1) spark_deploy.sh 写入的标记配置块(含标记行本身);
# 2) 按讲义手动 vi 添加的四行:
# export SCALA_HOME=/usr/local/soft/scala-2.13.16
# export PATH=$PATH:$SCALA_HOME/bin
# export SPARK_HOME=/usr/local/soft/spark-4.1.3-bin-hadoop3
# export PATH=$PATH:$SPARK_HOME/bin
tmp=$(mktemp)
awk -v b="$MARKER_BEGIN" -v e="$MARKER_END" '
$0 == b {inb=1; next}
$0 == e {inb=0; next}
inb {next}
index($0, "SCALA_HOME=") && index($0, "/usr/local/soft/scala") {next}
index($0, "SPARK_HOME=") && index($0, "/usr/local/soft/spark") {next}
index($0, "$SCALA_HOME/bin") {next}
index($0, "$SPARK_HOME/bin") {next}
{print}
' "$file" > "$tmp"
cat "$tmp" > "$file" && rm -f "$tmp"
log "$file:标记配置块与手动添加的 Scala/Spark 环境变量已全部删除"
# 极少数情况:仍存在指向其他目录的 SCALA_HOME/SPARK_HOME,不误删,仅提示
leftover=$(grep -nE 'SCALA_HOME=|SPARK_HOME=' "$file" 2>/dev/null || true)
if [ -n "$leftover" ]; then
warn "$file 中仍存在其他 Scala/Spark 环境变量(未动),请自行确认:"
echo "$leftover" | sed 's/^/ /'
fi
}
# ------------------------------ 步骤 1:前置检查 -----------------------------
log "步骤 1/5:前置检查"
[ "$(id -u)" -eq 0 ] || die "请使用 root 用户执行本脚本"
[ "$(hostname -s)" = "$MASTER" ] || die "请在 $MASTER(主节点)上执行本脚本,当前主机:$(hostname -s)"
for node in "${WORKERS[@]}"; do
ping -c 1 -W 2 "$node" >/dev/null 2>&1 || die "无法 ping 通 $node,请检查网络与 /etc/hosts"
done
export SPARK_SSH_OPTS="-o StrictHostKeyChecking=no"
if [ ! -d "$SOFT_DIR/$SCALA_DIR" ] && [ ! -d "$SOFT_DIR/$SPARK_DIR" ] \
&& ! grep -qE 'SCALA_HOME=|SPARK_HOME=' "$PROFILE" 2>/dev/null; then
log "本机未发现 Scala/Spark 安装目录和环境变量,无需卸载"
exit 0
fi
log "前置检查通过"
# 不询问确认,直接卸载(仅提示卸载范围)
log "将在 $MASTER、${WORKERS[*]} 上卸载 Scala 与 Spark,并清除 /etc/profile 环境变量"
log "JDK($JAVA_HOME_DIR)与 Hadoop($HADOOP_HOME_DIR)不受影响"
# ------------------- 步骤 2:停止 Spark 集群(不动 Hadoop) -------------------
log "步骤 2/5:停止 Spark 集群"
if [ -x "$SOFT_DIR/$SPARK_DIR/sbin/stop-all.sh" ]; then
"$SOFT_DIR/$SPARK_DIR/sbin/stop-all.sh" || warn "stop-all.sh 返回非零,继续执行"
sleep 5
else
log "未找到 $SPARK_DIR/sbin/stop-all.sh,跳过(安装目录可能已删除)"
fi
if "$JPS" 2>/dev/null | grep -qw Master; then
warn "node11 上 Master 进程仍在运行,请手动处理:$JPS 查看 PID 后 kill"
else
log "node11 上 Master 进程已停止"
fi
for node in "${WORKERS[@]}"; do
if ssh "root@$node" ". /etc/profile >/dev/null 2>&1; jps" 2>/dev/null | grep -qw Worker; then
warn "$node 上 Worker 进程仍在运行,请登录该节点手动 kill"
else
log "$node 上 Worker 进程已停止"
fi
done
# ---------------- 步骤 3:清除 /etc/profile 环境变量(三个节点) ----------------
log "步骤 3/5:清除 /etc/profile 中的 Scala/Spark 环境变量"
remove_block "$PROFILE"
for node in "${WORKERS[@]}"; do
scp -q "$PROFILE" "root@$node:$PROFILE"
ssh "root@$node" "grep -qE 'SCALA_HOME=|SPARK_HOME=' '$PROFILE' && echo '$node: 环境变量清除异常' || echo '$node: 环境变量已清除'"
done
# ---------------- 步骤 4:删除 scala、spark 安装目录(三个节点) ---------------
log "步骤 4/5:删除 Scala 与 Spark 安装目录"
rm -rf "$SOFT_DIR/$SCALA_DIR" "$SOFT_DIR/$SPARK_DIR"
log "$MASTER:安装目录已删除"
for node in "${WORKERS[@]}"; do
ssh "root@$node" "rm -rf '$SOFT_DIR/$SCALA_DIR' '$SOFT_DIR/$SPARK_DIR'"
log "$node:安装目录已删除"
done
# --------------- 步骤 5:验证——spark/scala 已清除,java/hadoop 完好 ------------
log "步骤 5/5:验证卸载结果"
# 5.1 安装目录与环境变量应已清除,JDK/Hadoop 目录应完好
check_node() { # $1=节点
local node="$1"
if ssh "root@$node" \
"[ ! -d '$SOFT_DIR/$SCALA_DIR' ] && [ ! -d '$SOFT_DIR/$SPARK_DIR' ] \
&& ! grep -qE 'SCALA_HOME=|SPARK_HOME=' '$PROFILE' \
&& [ -d '$JAVA_HOME_DIR' ] && [ -d '$HADOOP_CONF_DIR' ]"; then
log " $node scala/spark 已删除,环境变量已清除,JDK/Hadoop 完好"
else
warn " $node 卸载不彻底或 JDK/Hadoop 目录异常,请登录该节点检查 $SOFT_DIR 与 $PROFILE"
fi
return 0
}
check_node "$MASTER"
for node in "${WORKERS[@]}"; do check_node "$node"; done
# 5.2 JDK 与 Hadoop 功能应不受影响(仅检查主节点)
if "$JAVA_HOME_DIR/bin/java" -version >/dev/null 2>&1; then
log "JDK 正常:$("$JAVA_HOME_DIR/bin/java" -version 2>&1 | head -n 1)"
else
warn "JDK 验证失败,请检查 $JAVA_HOME_DIR"
fi
[ -d "$HADOOP_CONF_DIR" ] && log "Hadoop 配置目录正常:$HADOOP_CONF_DIR"
if "$JPS" 2>/dev/null | grep -qw NameNode; then
log "Hadoop NameNode 仍在运行(未受影响)"
else
warn "NameNode 未运行。若卸载前 Hadoop 在运行,可用 $HADOOP_HOME_DIR/sbin/start-all.sh 重新启动"
fi
echo
log "=============================================================="
log " Spark 与 Scala 卸载完成!"
log " 已删除:$SOFT_DIR/$SCALA_DIR"
log " $SOFT_DIR/$SPARK_DIR"
log " 已清除:/etc/profile 中的 SCALA_HOME、SPARK_HOME 及其 PATH 行"
log " 未动 :JDK($JAVA_HOME_DIR)、Hadoop($HADOOP_HOME_DIR)"
log " 保留 :/usr/local/soft 下的两个 .tgz 安装包"
log ""
log " 当前终端 PATH 可能仍残留 scala/spark 路径,执行 source /etc/profile"
log " 或重新登录后即恢复干净环境(hash -r 可清空命令缓存)"
log "=============================================================="
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/taogumo/article/details/169848449





