看完的老铁,可以赏个赞啊!
1 Overview
Spark 容器化的前提是需要 Spark 的镜像文件,那么怎么 build 呢?Spark 官方是提供了 Dockerfile 的,并且也提供了脚本工具,可以自行 build 并发布到自己的 Restry 里。
2 Spark里的Kubernetes
2.1 Dockerfile
Spark 提供的 Dockerfile 可以在类似目录找到。
# tmp 是我放 Spark 的目录
/tmp/spark-2.4.2-bin-hadoop2.7/kubernetes/dockerfiles/spark
查看 Dockerfile 的内容,需要注意一下注释的内容,关键点在于构建命令 docker build...
,需要在哪个目录去执行,注释写的很清楚,这里就不赘述了。然后一句句看下来再分析。
首先看第一部分是基础镜像。
FROM openjdk:8-alpine
第二部分是一些内部的临时变量 ARG。
ARG spark_jars=jars
ARG img_path=kubernetes/dockerfiles
ARG k8s_tests=kubernetes/tests
第三部分是一些命令。
# -e: 若指令传回值不等于0,则立即退出shell
# -x: 执行指令后,会先显示该指令及所下的参数
set -ex
# apk 是 alpine 提供的软件包管理工具
# upgrade --no-cache 重新更新已安装的软件包
# add 命令从仓库中安装最新软件包,并自动安装必须的依赖包,这里注意一下 tini,在 entrypoint.sh 中是很重要的角色
apk upgrade --no-cache
apk add --no-cache bash tini libc6-compat linux-pam nss
# 创建 Spark 相关的一些文件夹用来存放文件
mkdir -p /opt/spark
mkdir -p /opt/spark/work-dir
touch /opt/spark/RELEASE
# 这个是 linux 系统经常存在的配置,详细可以自行找找 /bin/sh 和 /bin/bash 的区别
rm /bin/sh
ln -sv /bin/bash /bin/sh
echo "auth required pam_wheel.so use_uid" >> /etc/pam.d/su
chgrp root /etc/passwd
chmod ug+rw /etc/passwd
第四部分是将构建 Docker 镜像文件的时候,将本地文件 Copy 到镜像中。
COPY ${spark_jars} /opt/spark/jars
COPY bin /opt/spark/bin
COPY sbin /opt/spark/sbin
COPY ${img_path}/spark/entrypoint.sh /opt/
COPY examples /opt/spark/examples
COPY ${k8s_tests} /opt/spark/tests
COPY data /opt/spark/data
第五部分是设置环境变量和工作目录以及容器启动的脚本(后面也会分析这个脚本)。
ENV SPARK_HOME /opt/spark
WORKDIR /opt/spark/work-dir
ENTRYPOINT [ "/opt/entrypoint.sh" ]
2.2 Entrypoint
这个是容器的入口点,执行的是 COPY 进去镜像的 entrypoint.sh 文件。而这个脚本最后的命令是这样的。
exec /sbin/tini -s -- "${CMD[$]}"
打开内容可以看到,首先会根据容器启动的类型来初始化一些环节变量,并且根据启动的参数,来执行镜像里的脚本,并启动 Driver 或者 Executor,具体可以参看脚本内容。
# 篇幅有限,有删减
case "$SPARK_K8S_CMD" in
driver)
CMD=(
"$SPARK_HOME/bin/spark-submit"
--conf "spark.driver.bindAddress=$SPARK_DRIVER_BIND_ADDRESS"
--deploy-mode client
"$@"
)
;;
# 删减开始
...
...
...
# 删减结束
executor)
CMD=(
${JAVA_HOME}/bin/java
"${SPARK_EXECUTOR_JAVA_OPTS[@]}"
-Xms$SPARK_EXECUTOR_MEMORY
-Xmx$SPARK_EXECUTOR_MEMORY
-cp "$SPARK_CLASSPATH"
org.apache.spark.executor.CoarseGrainedExecutorBackend
--driver-url $SPARK_DRIVER_URL
--executor-id $SPARK_EXECUTOR_ID
--cores $SPARK_EXECUTOR_CORES
--app-id $SPARK_APPLICATION_ID
--hostname $SPARK_EXECUTOR_POD_IP
)
;;
*)
echo "Unknown command: $SPARK_K8S_CMD" 1>&2
exit 1
esac
关于 tini 的使用,我也是最近才了解的,给两个参考链接。
http://yunke.science/2018/04/09/Tini-command/
https://github.com/krallin/tini/issues/8
3 Summary
我们可以根据 Spark 提供的镜像构建的方式,来构建自己的 Spark 镜像,适合有定制化需求的团队。
看完文章,大家可能会好奇,Spark 如何通过 K8S 的 Java 客户端来拉起镜像创建 Pod 的,后面会通过源码来继续分析。