【问题标题】:Unable to set environment variable inside docker container when calling sh file from Dockerfile CMD从 Dockerfile CMD 调用 sh 文件时无法在 docker 容器内设置环境变量
【发布时间】:2021-08-16 11:34:09
【问题描述】:

我正在关注this 链接以创建火花集群。我能够运行火花集群。但是,我必须给出启动spark-shell 的绝对路径。我正在尝试设置环境变量,即PATHstart-shell.sh 中的其他一些变量。但是,它没有在容器内设置。我尝试在容器内使用printenv 打印它。但这些变量永远不会反映。

我是否试图错误地设置环境变量? Spark 集群运行成功。

我正在使用 docker-compose.yml 来构建和重新创建图像和容器。

docker-compose up --build

Dockerfile

# builder step used to download and configure spark environment
FROM openjdk:11.0.11-jre-slim-buster as builder

# Add Dependencies for PySpark
RUN apt-get update && apt-get install -y curl vim wget software-properties-common ssh net-tools ca-certificates python3 python3-pip python3-numpy python3-matplotlib python3-scipy python3-pandas python3-simpy

# JDBC driver download and install
ADD https://go.microsoft.com/fwlink/?linkid=2168494 /usr/share/java

RUN update-alternatives --install "/usr/bin/python" "python" "$(which python3)" 1

# Fix the value of PYTHONHASHSEED
# Note: this is needed when you use Python 3.3 or greater
ENV SPARK_VERSION=3.1.2 \
HADOOP_VERSION=3.2 \
SPARK_HOME=/opt/spark \
PYTHONHASHSEED=1

# Download and uncompress spark from the apache archive
RUN wget --no-verbose -O apache-spark.tgz "https://archive.apache.org/dist/spark/spark-${SPARK_VERSION}/spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz" \
&& mkdir -p ${SPARK_HOME} \
&& tar -xf apache-spark.tgz -C ${SPARK_HOME} --strip-components=1 \
&& rm apache-spark.tgz

我的 Dockerfile-spark

在 Dockerfile 的 ENV 下使用 SPARK_BIN="${SPARK_HOME}/bin/ 时,设置了环境变量。使用printenv在docker容器内可见

FROM apache-spark

WORKDIR ${SPARK_HOME}

ENV SPARK_MASTER_PORT=7077 \
SPARK_MASTER_WEBUI_PORT=8080 \
SPARK_LOG_DIR=${SPARK_HOME}/logs \
SPARK_MASTER_LOG=${SPARK_HOME}/logs/spark-master.out \
SPARK_WORKER_LOG=${SPARK_HOME}/logs/spark-worker.out \
SPARK_WORKER_WEBUI_PORT=8080 \
SPARK_MASTER="spark://spark-master:7077" \
SPARK_WORKLOAD="master"        

COPY start-spark.sh /

CMD ["/bin/bash", "/start-spark.sh"]

start-spark.sh

#!/bin/bash
. "$SPARK_HOME/bin/load-spark-env.sh"

export SPARK_BIN="${SPARK_HOME}/bin/" # This doesn't work here
export PATH="${SPARK_HOME}/bin/:${PATH}" # This doesn't work here      

# When the spark work_load is master run class org.apache.spark.deploy.master.Master
if [ "$SPARK_WORKLOAD" == "master" ];
then

export SPARK_MASTER_HOST=`hostname`           # This works here

cd $SPARK_BIN && ./spark-class org.apache.spark.deploy.master.Master --ip $SPARK_MASTER_HOST --port $SPARK_MASTER_PORT --webui-port $SPARK_MASTER_WEBUI_PORT >> $SPARK_MASTER_LOG. 

我的文件结构是

  • 码头文件
  • dockerfile-spark # 使用 dockerfile 创建的预构建镜像
  • start-spark.sh # 调用购买 dockerfile-spark
  • docker-compose.yml # 使用 build 参数从 dockerfile-spark 构建镜像

从主容器内部

root@3abbd4508121:/opt/spark# export
declare -x HADOOP_VERSION="3.2"
declare -x HOME="/root"
declare -x HOSTNAME="3abbd4508121"
declare -x JAVA_HOME="/usr/local/openjdk-11"
declare -x JAVA_VERSION="11.0.11+9"
declare -x LANG="C.UTF-8"
declare -x OLDPWD
declare -x PATH="/usr/local/openjdk-11/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
declare -x PWD="/opt/spark"
declare -x PYTHONHASHSEED="1"
declare -x SHLVL="1"
declare -x SPARK_HOME="/opt/spark"
declare -x SPARK_LOCAL_IP="spark-master"
declare -x SPARK_LOG_DIR="/opt/spark/logs"
declare -x SPARK_MASTER="spark://spark-master:7077"
declare -x SPARK_MASTER_LOG="/opt/spark/logs/spark-master.out"
declare -x SPARK_MASTER_PORT="7077"
declare -x SPARK_MASTER_WEBUI_PORT="8080"
declare -x SPARK_VERSION="3.1.2"
declare -x SPARK_WORKER_LOG="/opt/spark/logs/spark-worker.out"
declare -x SPARK_WORKER_WEBUI_PORT="8080"
declare -x SPARK_WORKLOAD="master"
declare -x TERM="xterm"
root@3abbd4508121:/opt/spark# 

【问题讨论】:

  • 是什么让您认为没有设置环境变量?您显示的任何内容实际上都没有包含printenv 电话。
  • @DavidMaze 在 DockerFile 中使用 SPARK_BIN="${SPARK_HOME}/bin/" and PATH="${SPARK_HOME}/bin/:${PATH}"` 时,它可以在 start-spark.sh 中使用它们。它不起作用有人告诉我使用 . /start-spark.sh 由于 shell 全局与本地上下文。但是它抛出了不同的错误。
  • 你说的“容器内”的几个地方,你是怎么到那里的? (docker exec 调试 shell 不会看到您在入口点包装脚本中设置的环境变量。)
  • 我使用docker exec -it sparkdocs_spark-master_1 /bin/bash 进入容器外壳

标签: docker apache-spark docker-compose environment-variables sh


【解决方案1】:

在 Docker 中设置环境变量有几种不同的方法,运行进程也有几种不同的方法。一个容器通常运行一个进程,该进程由镜像的ENTRYPOINTCMD 设置控制。如果你docker exec容器中的第二个进程,它不会作为主进程的子进程运行,并且不会看到由该主进程设置的环境变量。

在您在此处显示的设置中,start-spark.sh 脚本是主容器进程(它是图像的CMD)。如果你docker exec your-container printenv,它将看到Dockerfile 中设置的内容,但看不到此脚本中设置的内容。

文件系统路径之类的东西通常会在你每次运行容器时被修复,无论你在那里运行什么命令,所以你可以在 Dockerfile 中指定这些

ENV SPARK_BIN=${SPARK_HOME}/bin PATH=${SPARK_BIN}:${PATH}

您可以在 Dockerfile 中同时指定 ENTRYPOINTCMD;如果这样做,CMD 将作为参数传递给ENTRYPOINT。这导致了一个有用的模式,其中CMD 是一个标准的shell 命令,ENTRYPOINT 是一个包装器,它进行首次设置然后运行它。您可以将脚本分成两部分:

#!/bin/sh
# spark-env.sh
. "${SPARK_BIN}/load-spark-env.snh"
exec "$@"
#!/bin/sh
# start-spark.sh
spark-class org.apache.spark.deploy.master.Master \
  --ip "$SPARK_MASTER_HOST" \
  --port "$SPARK_MASTER_PORT" \
  --webui-port "$SPARK_MASTER_WEBUI_PORT"

然后在你的 Dockerfile 中指定这两个部分

COPY spark-env.sh start-spark.sh /
ENTRYPOINT ["/spark-env.sh"] # must be JSON-array syntax
CMD ["/start-spark.sh"] # or any other valid CMD

这对您的调试很有用,因为在 docker rundocker-compose run 指令中覆盖 CMD 很简单,而将 ENTRYPOINT 留在原处。

docker-compose run spark \
  printenv

这会基于所有相同的 Dockerfile 设置启动一个新容器。当它运行时,它运行printenv 而不是图像中的CMD。这将在ENTRYPOINT 脚本中进行首次设置,然后最后的exec "$@" 行将运行printenv 而不是启动Spark 应用程序。这将向您显示应用程序启动时的环境。

【讨论】:

  • 相当详尽的答案。 +1。我想知道是否仍然可以从 start-spark.sh 设置一个环境变量,该变量在其他会话中也可见,而无需在 dockerfile 中指定 env。只是想知道这是否可能。我知道这将是一个糟糕的设计。
  • 如果你在一个进程中设置了一个环境变量,它在另一个进程中永远不可见,一个特定的例外是子进程在调用 fork(2) 时继承其父进程的环境(但在那之后两个进程的环境是分开的)。这也适用于docker exec;额外的调试外壳或其他进程不是主容器进程的子进程,并且看不到它的环境。 (谨慎使用docker exec,作为调试工具,而不是作为获取“会话”的方式。)
猜你喜欢
  • 2019-01-09
  • 2018-07-02
  • 1970-01-01
  • 2019-09-11
  • 1970-01-01
  • 2018-07-20
  • 1970-01-01
  • 2017-11-15
  • 2020-02-13
相关资源
最近更新 更多