Kubernetes での Apache Spark の実行

読者の皆様、こんにちは。 今日は、Apache Spark ずその開発の芋通しに぀いお少しお話したす。

Kubernetes での Apache Spark の実行

珟代のビッグ デヌタの䞖界では、Apache Spark がバッチ デヌタ凊理タスクを開発するための事実䞊の暙準です。 さらに、マむクロ バッチの抂念で動䜜し、デヌタを少しず぀凊理しお送信するストリヌミング アプリケヌション (Spark Structured Streaming) の䜜成にも䜿甚されたす。 そしお䌝統的に、YARN (堎合によっおは Apache Mesos) をリ゜ヌス マネヌゞャヌずしお䜿甚し、Hadoop スタック党䜓の䞀郚ずしお䜿甚されおきたした。 2020 幎たでに、たずもな Hadoop ディストリビュヌションが䞍足しおいるため、ほずんどの䌁業にずっお埓来の圢匏での䜿甚が疑問芖されおいたす。HDP ず CDH の開発は停止しおおり、CDH は十分に開発されおおらず、コストが高く、残りの Hadoop サプラむダヌは存圚しなくなったか、暗い未来があるかのどちらかです。 したがっお、Kubernetes を䜿甚した Apache Spark の立ち䞊げは、コミュニティや倧䌁業の間で関心が高たっおいたす。プラむベヌト クラりドずパブリック クラりドにおけるコンテナ オヌケストレヌションずリ゜ヌス管理の暙準ずなり、YARN 䞊の Spark タスクの䞍䟿なリ゜ヌス スケゞュヌリングの問題を解決し、次のような機胜を提䟛したす。あらゆる芏暡や皮類の䌁業向けに倚くの商甚ディストリビュヌションずオヌプン ディストリビュヌションを備え、着実に発展しおいるプラ​​ットフォヌムです。 さらに、人気を受けお、ほずんどの䌁業はすでに独自のむンストヌルをいく぀か取埗し、その䜿甚に関する専門知識を高めおいるため、移行が簡玠化されおいたす。

バヌゞョン 2.3.0 以降、Apache Spark は Kubernetes クラスタヌでタスクを実行するための公匏サポヌトを取埗したした。今日は、このアプロヌチの珟圚の成熟床、その䜿甚のためのさたざたなオプション、および実装䞭に遭遇する萜ずし穎に぀いお説明したす。

たず最初に、Apache Spark に基づいおタスクずアプリケヌションを開発するプロセスを芋お、Kubernetes クラスタヌでタスクを実行する必芁がある䞀般的なケヌスを取り䞊げたす。 この投皿を準備する際には、OpenShift がディストリビュヌションずしお䜿甚され、そのコマンド ラむン ナヌティリティ (oc) に関連するコマンドが提䟛されたす。 他の Kubernetes ディストリビュヌションの堎合は、暙準の Kubernetes コマンド ラむン ナヌティリティ (kubectl) たたはその類䌌物 (たずえば、oc adm ポリシヌ) の察応するコマンドを䜿甚できたす。

最初の䜿甚䟋 - スパヌク送信

タスクずアプリケヌションの開発䞭、開発者はタスクを実行しおデヌタ倉換をデバッグする必芁がありたす。 理論的には、スタブはこれらの目的に䜿甚できたすが、このクラスのタスクでは、゚ンド システムの実際の (テストではあるが) むンスタンスを参加させた開発の方が高速か぀優れおいるこずが蚌明されおいたす。 ゚ンド システムの実際のむンスタンスでデバッグする堎合、次の XNUMX ぀のシナリオが考えられたす。

  • 開発者は Spark タスクをスタンドアロン モヌドでロヌカルで実行したす。

    Kubernetes での Apache Spark の実行

  • 開発者はテストルヌプで Kubernetes クラスタヌ䞊で Spark タスクを実行したす。

    Kubernetes での Apache Spark の実行

最初のオプションには存圚する暩利がありたすが、倚くの欠点が䌎いたす。

  • 各開発者には、職堎から必芁な゚ンド システムのすべおのむンスタンスぞのアクセスが提䟛されなければなりたせん。
  • 開発䞭のタスクを実行するには、䜜業マシン䞊に十分な量のリ゜ヌスが必芁です。

XNUMX 番目のオプションには、これらの欠点はありたせん。Kubernetes クラスタヌを䜿甚するず、タスクの実行に必芁なリ゜ヌス プヌルを割り圓お、゚ンド システム むンスタンスぞの必芁なアクセスを提䟛できるため、Kubernetes ロヌル モデルを䜿甚しおそのリ゜ヌス プヌルぞのアクセスを柔軟に提䟛できたす。開発チヌムのメンバヌ党員。 最初のナヌスケヌスずしお、テストルヌプ内の Kubernetes クラスタヌ䞊のロヌカル開発者マシンから Spark タスクを起動するこずを匷調したしょう。

Spark をロヌカルで実行するように蚭定するプロセスに぀いお詳しく説明したす。 Spark の䜿甚を開始するには、それをむンストヌルする必芁がありたす。

mkdir /opt/spark
cd /opt/spark
wget http://mirror.linux-ia64.org/apache/spark/spark-2.4.5/spark-2.4.5.tgz
tar zxvf spark-2.4.5.tgz
rm -f spark-2.4.5.tgz

Kubernetes を操䜜するために必芁なパッケヌゞを収集したす。

cd spark-2.4.5/
./build/mvn -Pkubernetes -DskipTests clean package

完党なビルドには倚くの時間がかかり、Docker むメヌゞを䜜成しお Kubernetes クラスタヌ䞊で実行するには、実際に必芁なのは「assembly/」ディレクトリの jar ファむルのみであるため、このサブプロゞェクトのみをビルドできたす。

./build/mvn -f ./assembly/pom.xml -Pkubernetes -DskipTests clean package

Kubernetes で Spark ゞョブを実行するには、基本むメヌゞずしお䜿甚する Docker むメヌゞを䜜成する必芁がありたす。 ここで考えられるアプロヌチは 2 ぀ありたす。

  • 生成された Docker むメヌゞには、実行可胜な Spark タスク コヌドが含たれおいたす。
  • 䜜成されたむメヌゞには Spark ず必芁な䟝存関係のみが含たれおおり、実行可胜コヌドはリモヌト (HDFS など) でホストされたす。

たず、Spark タスクのテスト䟋を含む Docker むメヌゞを構築したしょう。 Docker むメヌゞを䜜成するために、Spark には「docker-image-tool」ず呌ばれるナヌティリティがありたす。 それに関するヘルプを調べおみたしょう。

./bin/docker-image-tool.sh --help

これを利甚するず、Docker むメヌゞを䜜成しおリモヌト レゞストリにアップロヌドできたすが、デフォルトではいく぀かの欠点がありたす。

  • 必ず䞀床に 3 ぀の Docker むメヌゞ (Spark、PySpark、R 甹) を䜜成したす。
  • むメヌゞ名を指定するこずはできたせん。

したがっお、以䞋に瀺すこのナヌティリティの修正バヌゞョンを䜿甚したす。

vi bin/docker-image-tool-upd.sh

#!/usr/bin/env bash

function error {
  echo "$@" 1>&2
  exit 1
}

if [ -z "${SPARK_HOME}" ]; then
  SPARK_HOME="$(cd "`dirname "$0"`"/..; pwd)"
fi
. "${SPARK_HOME}/bin/load-spark-env.sh"

function image_ref {
  local image="$1"
  local add_repo="${2:-1}"
  if [ $add_repo = 1 ] && [ -n "$REPO" ]; then
    image="$REPO/$image"
  fi
  if [ -n "$TAG" ]; then
    image="$image:$TAG"
  fi
  echo "$image"
}

function build {
  local BUILD_ARGS
  local IMG_PATH

  if [ ! -f "$SPARK_HOME/RELEASE" ]; then
    IMG_PATH=$BASEDOCKERFILE
    BUILD_ARGS=(
      ${BUILD_PARAMS}
      --build-arg
      img_path=$IMG_PATH
      --build-arg
      datagram_jars=datagram/runtimelibs
      --build-arg
      spark_jars=assembly/target/scala-$SPARK_SCALA_VERSION/jars
    )
  else
    IMG_PATH="kubernetes/dockerfiles"
    BUILD_ARGS=(${BUILD_PARAMS})
  fi

  if [ -z "$IMG_PATH" ]; then
    error "Cannot find docker image. This script must be run from a runnable distribution of Apache Spark."
  fi

  if [ -z "$IMAGE_REF" ]; then
    error "Cannot find docker image reference. Please add -i arg."
  fi

  local BINDING_BUILD_ARGS=(
    ${BUILD_PARAMS}
    --build-arg
    base_img=$(image_ref $IMAGE_REF)
  )
  local BASEDOCKERFILE=${BASEDOCKERFILE:-"$IMG_PATH/spark/docker/Dockerfile"}

  docker build $NOCACHEARG "${BUILD_ARGS[@]}" 
    -t $(image_ref $IMAGE_REF) 
    -f "$BASEDOCKERFILE" .
}

function push {
  docker push "$(image_ref $IMAGE_REF)"
}

function usage {
  cat <<EOF
Usage: $0 [options] [command]
Builds or pushes the built-in Spark Docker image.

Commands:
  build       Build image. Requires a repository address to be provided if the image will be
              pushed to a different registry.
  push        Push a pre-built image to a registry. Requires a repository address to be provided.

Options:
  -f file               Dockerfile to build for JVM based Jobs. By default builds the Dockerfile shipped with Spark.
  -p file               Dockerfile to build for PySpark Jobs. Builds Python dependencies and ships with Spark.
  -R file               Dockerfile to build for SparkR Jobs. Builds R dependencies and ships with Spark.
  -r repo               Repository address.
  -i name               Image name to apply to the built image, or to identify the image to be pushed.  
  -t tag                Tag to apply to the built image, or to identify the image to be pushed.
  -m                    Use minikube's Docker daemon.
  -n                    Build docker image with --no-cache
  -b arg      Build arg to build or push the image. For multiple build args, this option needs to
              be used separately for each build arg.

Using minikube when building images will do so directly into minikube's Docker daemon.
There is no need to push the images into minikube in that case, they'll be automatically
available when running applications inside the minikube cluster.

Check the following documentation for more information on using the minikube Docker daemon:

  https://kubernetes.io/docs/getting-started-guides/minikube/#reusing-the-docker-daemon

Examples:
  - Build image in minikube with tag "testing"
    $0 -m -t testing build

  - Build and push image with tag "v2.3.0" to docker.io/myrepo
    $0 -r docker.io/myrepo -t v2.3.0 build
    $0 -r docker.io/myrepo -t v2.3.0 push
EOF
}

if [[ "$@" = *--help ]] || [[ "$@" = *-h ]]; then
  usage
  exit 0
fi

REPO=
TAG=
BASEDOCKERFILE=
NOCACHEARG=
BUILD_PARAMS=
IMAGE_REF=
while getopts f:mr:t:nb:i: option
do
 case "${option}"
 in
 f) BASEDOCKERFILE=${OPTARG};;
 r) REPO=${OPTARG};;
 t) TAG=${OPTARG};;
 n) NOCACHEARG="--no-cache";;
 i) IMAGE_REF=${OPTARG};;
 b) BUILD_PARAMS=${BUILD_PARAMS}" --build-arg "${OPTARG};;
 esac
done

case "${@: -1}" in
  build)
    build
    ;;
  push)
    if [ -z "$REPO" ]; then
      usage
      exit 1
    fi
    push
    ;;
  *)
    usage
    exit 1
    ;;
esac

その助けを借りお、Spark を䜿甚しお Pi を蚈算するためのテスト タスクを含む基本的な Spark むメヌゞを組み立おたす (ここで、{docker-registry-url} は Docker むメヌゞ レゞストリの URL、{repo} はレゞストリ内のリポゞトリの名前、 OpenShift のプロゞェクトず䞀臎するもの、 {image-name} - むメヌゞの名前 (たずえば、Red Hat OpenShift むメヌゞの統合レゞストリのようにむメヌゞの XNUMX レベルの分離が䜿甚されおいる堎合)、 {tag} - このタグ画像のバヌゞョン):

./bin/docker-image-tool-upd.sh -f resource-managers/kubernetes/docker/src/main/dockerfiles/spark/Dockerfile -r {docker-registry-url}/{repo} -i {image-name} -t {tag} build

コン゜ヌル ナヌティリティを䜿甚しお OKD クラスタヌにログむンしたす (ここで、{OKD-API-URL} は OKD クラスタヌ API URL です)。

oc login {OKD-API-URL}

Docker レゞストリで珟圚のナヌザヌの認蚌トヌクンを取埗しおみたしょう。

oc whoami -t

OKD クラスタヌの内郚 Docker レゞストリにログむンしたす (前のコマンドを䜿甚しお取埗したトヌクンをパスワヌドずしお䜿甚したす)。

docker login {docker-registry-url}

アセンブルされた Docker むメヌゞを Docker レゞストリ OKD にアップロヌドしたしょう。

./bin/docker-image-tool-upd.sh -r {docker-registry-url}/{repo} -i {image-name} -t {tag} push

組み立おられたむメヌゞが OKD で利甚できるこずを確認しおみたしょう。 これを行うには、ブラりザで URL を開き、察応するプロゞェクトのむメヌゞのリストを衚瀺したす (ここで、{project} は OpenShift クラスタヌ内のプロゞェクトの名前、{OKD-WEBUI-URL} は OpenShift Web コン゜ヌルの URL です) ) - https://{OKD-WEBUI-URL}/console /project/{プロゞェクト}/browse/images/{むメヌゞ名}。

タスクを実行するには、ポッドを root ずしお実行する暩限を持぀サヌビス アカりントを䜜成する必芁がありたす (この点に぀いおは埌で説明したす)。

oc create sa spark -n {project}
oc adm policy add-scc-to-user anyuid -z spark -n {project}

䜜成したサヌビス アカりントず Docker むメヌゞを指定しお、spark-submit コマンドを実行しお Spark タスクを OKD クラスタヌに公開したしょう。

 /opt/spark/bin/spark-submit --name spark-test --class org.apache.spark.examples.SparkPi --conf spark.executor.instances=3 --conf spark.kubernetes.authenticate.driver.serviceAccountName=spark --conf spark.kubernetes.namespace={project} --conf spark.submit.deployMode=cluster --conf spark.kubernetes.container.image={docker-registry-url}/{repo}/{image-name}:{tag} --conf spark.master=k8s://https://{OKD-API-URL}  local:///opt/spark/examples/target/scala-2.11/jars/spark-examples_2.11-2.4.5.jar

ここに

—name — Kubernetes ポッドの名前の圢成に参加するタスクの名前。

—class — タスクの開始時に呌び出される実行可胜ファむルのクラス。

—conf — Spark 蚭定パラメヌタ。

smile.executor.instances — 起動する Spark ゚グれキュヌタヌの数。

smile.kubernetes.authenticate.driver.serviceAccountName - ポッドの起動時に䜿甚される Kubernetes サヌビス アカりントの名前 (Kubernetes API ず察話する際のセキュリティ コンテキストず機胜を定矩するため)。

smile.kubernetes.namespace — ドラむバヌおよび゚グれキュヌタヌ ポッドが起動される Kubernetes 名前空間。

smile.submit.deployMode — Spark を起動するメ゜ッド (暙準の Spark-submit の「クラスタヌ」、Spark Operator およびそれ以降のバヌゞョンの Spark の「クラむアント」の堎合)。

smile.kubernetes.container.image - ポッドの起動に䜿甚される Docker むメヌゞ。

spark.master — Kubernetes API URL (ロヌカル マシンからアクセスが発生するように倖郚が指定されおいたす)。

local:// は、Docker むメヌゞ内の Spark 実行可胜ファむルぞのパスです。

察応する OKD プロゞェクトに移動し、䜜成されたポッド (https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods) を調べたす。

開発プロセスを簡玠化するために、別のオプションを䜿甚できたす。このオプションでは、Spark の共通の基本むメヌゞが䜜成され、実行するすべおのタスクで䜿甚され、実行可胜ファむルのスナップショットが倖郚ストレヌゞ (Hadoop など) に公開され、呌び出し時に指定されたす。スパヌク-リンクずしお送信したす。 この堎合、WebHDFS を䜿甚しおむメヌゞを公開するなど、Docker むメヌゞを再構築せずに、さたざたなバヌゞョンの Spark タスクを実行できたす。 ファむルを䜜成するリク゚ストを送信したす (ここで、{host} は WebHDFS サヌビスのホスト、{port} は WebHDFS サヌビスのポヌト、{path-to-file-on-hdfs} はファむルぞの目的のパスです。 HDFS 侊):

curl -i -X PUT "http://{host}:{port}/webhdfs/v1/{path-to-file-on-hdfs}?op=CREATE

次のような応答を受け取りたす (ここで、{location} はファむルのダりンロヌドに䜿甚する必芁がある URL です)。

HTTP/1.1 307 TEMPORARY_REDIRECT
Location: {location}
Content-Length: 0

Spark 実行可胜ファむルを HDFS にロヌドしたす (ここで、{path-to-local-file} は珟圚のホスト䞊の Spark 実行可胜ファむルぞのパスです)。

curl -i -X PUT -T {path-to-local-file} "{location}"

この埌、HDFS にアップロヌドされた Spark ファむルを䜿甚しお、spark-submit を実行できたす (ここで、{class-name} はタスクを完了するために起動する必芁があるクラスの名前です)。

/opt/spark/bin/spark-submit --name spark-test --class {class-name} --conf spark.executor.instances=3 --conf spark.kubernetes.authenticate.driver.serviceAccountName=spark --conf spark.kubernetes.namespace={project} --conf spark.submit.deployMode=cluster --conf spark.kubernetes.container.image={docker-registry-url}/{repo}/{image-name}:{tag} --conf spark.master=k8s://https://{OKD-API-URL}  hdfs://{host}:{port}/{path-to-file-on-hdfs}

HDFS にアクセスしおタスクが動䜜するこずを確認するには、Dockerfile ずentrypoint.sh スクリプトを倉曎する必芁がある堎合があるこずに泚意しおください。䟝存するラむブラリを /opt/spark/jars ディレクトリにコピヌするためのディレクティブを Dockerfile に远加し、 HDFS 構成ファむルを゚ントリポむントの SPARK_CLASSPATH に含めたす。

XNUMX 番目の䜿甚䟋 - Apache Livy

さらに、タスクが開発され、その結果をテストする必芁がある堎合、CI/CD プロセスの䞀郚ずしおタスクを起動し、その実行ステヌタスを远跡するかずいう問題が生じたす。 もちろん、ロヌカルのspark-submit呌び出しを䜿甚しお実行するこずもできたすが、CIサヌバヌの゚ヌゞェント/ランナヌにSparkをむンストヌルしお構成し、Kubernetes APIぞのアクセスを蚭定する必芁があるため、CI/CDむンフラストラクチャが耇雑になりたす。 この堎合、タヌゲット実装は、Kubernetes クラスタヌ内でホストされる Spark タスクを実行するための REST API ずしお Apache Livy を䜿甚するこずを遞択したした。 これを利甚するず、通垞の cURL リク゚ストを䜿甚しお Kubernetes クラスタヌ䞊で Spark タスクを実行できたす。これは、CI ゜リュヌションに基づいお簡単に実装でき、Kubernetes クラスタヌ内に配眮するこずで、Kubernetes API ず察話する際の認蚌の問題を解決したす。

Kubernetes での Apache Spark の実行

XNUMX 番目のナヌスケヌスずしお、テスト ルヌプ内の Kubernetes クラスタヌ䞊の CI/CD プロセスの䞀郚ずしお Spark タスクを実行するこずを匷調したしょう。

Apache Livy に぀いお少し説明したす。Apache Livy は、Web むンタヌフェむスず RESTful API を提䟛する HTTP サヌバヌずしお機胜したす。これにより、必芁なパラメヌタヌを枡すこずで、spark-submit をリモヌトで起動できるようになりたす。 埓来、これは HDP ディストリビュヌションの䞀郚ずしお出荷されおきたしたが、適切なマニフェストず次のような䞀連の Docker むメヌゞを䜿甚しお、OKD たたはその他の Kubernetes むンストヌルにデプロむするこずもできたす。 github.com/ttauveron/k8s-big-data-experiments/tree/master/livy-spark-2.3。 この䟋では、次の Dockerfile からの Spark バヌゞョン 2.4.5 を含む同様の Docker むメヌゞが構築されたした。

FROM java:8-alpine

ENV SPARK_HOME=/opt/spark
ENV LIVY_HOME=/opt/livy
ENV HADOOP_CONF_DIR=/etc/hadoop/conf
ENV SPARK_USER=spark

WORKDIR /opt

RUN apk add --update openssl wget bash && 
    wget -P /opt https://downloads.apache.org/spark/spark-2.4.5/spark-2.4.5-bin-hadoop2.7.tgz && 
    tar xvzf spark-2.4.5-bin-hadoop2.7.tgz && 
    rm spark-2.4.5-bin-hadoop2.7.tgz && 
    ln -s /opt/spark-2.4.5-bin-hadoop2.7 /opt/spark

RUN wget http://mirror.its.dal.ca/apache/incubator/livy/0.7.0-incubating/apache-livy-0.7.0-incubating-bin.zip && 
    unzip apache-livy-0.7.0-incubating-bin.zip && 
    rm apache-livy-0.7.0-incubating-bin.zip && 
    ln -s /opt/apache-livy-0.7.0-incubating-bin /opt/livy && 
    mkdir /var/log/livy && 
    ln -s /var/log/livy /opt/livy/logs && 
    cp /opt/livy/conf/log4j.properties.template /opt/livy/conf/log4j.properties

ADD livy.conf /opt/livy/conf
ADD spark-defaults.conf /opt/spark/conf/spark-defaults.conf
ADD entrypoint.sh /entrypoint.sh

ENV PATH="/opt/livy/bin:${PATH}"

EXPOSE 8998

ENTRYPOINT ["/entrypoint.sh"]
CMD ["livy-server"]

生成されたむメヌゞはビルドしお、内郚 OKD リポゞトリなどの既存の Docker リポゞトリにアップロヌドできたす。 これをデプロむするには、次のマニフェストを䜿甚したす ({registry-url} - Docker むメヌゞ レゞストリの URL、{image-name} - Docker むメヌゞ名、{tag} - Docker むメヌゞ タグ、{livy-url} - 垌望の URLサヌバヌは Livy にアクセス可胜になりたす。Red Hat OpenShift が Kubernetes ディストリビュヌションずしお䜿甚される堎合は「Route」マニフェストが䜿甚され、それ以倖の堎合は NodePort タむプの察応する Ingress マニフェストたたはサヌビス マニフェストが䜿甚されたす)。

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    component: livy
  name: livy
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      component: livy
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        component: livy
    spec:
      containers:
        - command:
            - livy-server
          env:
            - name: K8S_API_HOST
              value: localhost
            - name: SPARK_KUBERNETES_IMAGE
              value: 'gnut3ll4/spark:v1.0.14'
          image: '{registry-url}/{image-name}:{tag}'
          imagePullPolicy: Always
          name: livy
          ports:
            - containerPort: 8998
              name: livy-rest
              protocol: TCP
          resources: {}
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          volumeMounts:
            - mountPath: /var/log/livy
              name: livy-log
            - mountPath: /opt/.livy-sessions/
              name: livy-sessions
            - mountPath: /opt/livy/conf/livy.conf
              name: livy-config
              subPath: livy.conf
            - mountPath: /opt/spark/conf/spark-defaults.conf
              name: spark-config
              subPath: spark-defaults.conf
        - command:
            - /usr/local/bin/kubectl
            - proxy
            - '--port'
            - '8443'
          image: 'gnut3ll4/kubectl-sidecar:latest'
          imagePullPolicy: Always
          name: kubectl
          ports:
            - containerPort: 8443
              name: k8s-api
              protocol: TCP
          resources: {}
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      serviceAccount: spark
      serviceAccountName: spark
      terminationGracePeriodSeconds: 30
      volumes:
        - emptyDir: {}
          name: livy-log
        - emptyDir: {}
          name: livy-sessions
        - configMap:
            defaultMode: 420
            items:
              - key: livy.conf
                path: livy.conf
            name: livy-config
          name: livy-config
        - configMap:
            defaultMode: 420
            items:
              - key: spark-defaults.conf
                path: spark-defaults.conf
            name: livy-config
          name: spark-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: livy-config
data:
  livy.conf: |-
    livy.spark.deploy-mode=cluster
    livy.file.local-dir-whitelist=/opt/.livy-sessions/
    livy.spark.master=k8s://http://localhost:8443
    livy.server.session.state-retain.sec = 8h
  spark-defaults.conf: 'spark.kubernetes.container.image        "gnut3ll4/spark:v1.0.14"'
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: livy
  name: livy
spec:
  ports:
    - name: livy-rest
      port: 8998
      protocol: TCP
      targetPort: 8998
  selector:
    component: livy
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  labels:
    app: livy
  name: livy
spec:
  host: {livy-url}
  port:
    targetPort: livy-rest
  to:
    kind: Service
    name: livy
    weight: 100
  wildcardPolicy: None

これを適甚しおポッドを正垞に起動するず、リンク http://{livy-url}/ui で Livy グラフィカル むンタヌフェむスが利甚可胜になりたす。 Livy を䜿甚するず、Postman などからの REST リク゚ストを䜿甚しお Spark タスクを公開できたす。 リク゚ストを含むコレクションの䟋を以䞋に瀺したす (起動されたタスクの操䜜に必芁な倉数を含む構成匕数は、「args」配列で枡すこずができたす)。

{
    "info": {
        "_postman_id": "be135198-d2ff-47b6-a33e-0d27b9dba4c8",
        "name": "Spark Livy",
        "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
    },
    "item": [
        {
            "name": "1 Submit job with jar",
            "request": {
                "method": "POST",
                "header": [
                    {
                        "key": "Content-Type",
                        "value": "application/json"
                    }
                ],
                "body": {
                    "mode": "raw",
                    "raw": "{nt"file": "local:///opt/spark/examples/target/scala-2.11/jars/spark-examples_2.11-2.4.5.jar", nt"className": "org.apache.spark.examples.SparkPi",nt"numExecutors":1,nt"name": "spark-test-1",nt"conf": {ntt"spark.jars.ivy": "/tmp/.ivy",ntt"spark.kubernetes.authenticate.driver.serviceAccountName": "spark",ntt"spark.kubernetes.namespace": "{project}",ntt"spark.kubernetes.container.image": "{docker-registry-url}/{repo}/{image-name}:{tag}"nt}n}"
                },
                "url": {
                    "raw": "http://{livy-url}/batches",
                    "protocol": "http",
                    "host": [
                        "{livy-url}"
                    ],
                    "path": [
                        "batches"
                    ]
                }
            },
            "response": []
        },
        {
            "name": "2 Submit job without jar",
            "request": {
                "method": "POST",
                "header": [
                    {
                        "key": "Content-Type",
                        "value": "application/json"
                    }
                ],
                "body": {
                    "mode": "raw",
                    "raw": "{nt"file": "hdfs://{host}:{port}/{path-to-file-on-hdfs}", nt"className": "{class-name}",nt"numExecutors":1,nt"name": "spark-test-2",nt"proxyUser": "0",nt"conf": {ntt"spark.jars.ivy": "/tmp/.ivy",ntt"spark.kubernetes.authenticate.driver.serviceAccountName": "spark",ntt"spark.kubernetes.namespace": "{project}",ntt"spark.kubernetes.container.image": "{docker-registry-url}/{repo}/{image-name}:{tag}"nt},nt"args": [ntt"HADOOP_CONF_DIR=/opt/spark/hadoop-conf",ntt"MASTER=k8s://https://kubernetes.default.svc:8443"nt]n}"
                },
                "url": {
                    "raw": "http://{livy-url}/batches",
                    "protocol": "http",
                    "host": [
                        "{livy-url}"
                    ],
                    "path": [
                        "batches"
                    ]
                }
            },
            "response": []
        }
    ],
    "event": [
        {
            "listen": "prerequest",
            "script": {
                "id": "41bea1d0-278c-40c9-ad42-bf2e6268897d",
                "type": "text/javascript",
                "exec": [
                    ""
                ]
            }
        },
        {
            "listen": "test",
            "script": {
                "id": "3cdd7736-a885-4a2d-9668-bd75798f4560",
                "type": "text/javascript",
                "exec": [
                    ""
                ]
            }
        }
    ],
    "protocolProfileBehavior": {}
}

コレクションから最初のリク゚ストを実行し、OKD むンタヌフェむスに移動しお、タスクが正垞に起動されたこずを確認しおみたしょう (https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods)。 同時に、セッションが Livy むンタヌフェむス (http://{livy-url}/ui) に衚瀺され、その䞭で Livy API たたはグラフィカル むンタヌフェむスを䜿甚しお、タスクの進行状況を远跡し、セッションを調査できたす。ログ。

それでは、Livy がどのように機胜するかを芋おみたしょう。 これを行うには、Livy サヌバヌを䜿甚しおポッド内の Livy コンテナのログを調べおみたしょう - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods/{livy-pod-name }?tab=ログ。 これらから、「livy」ずいう名前のコンテナヌで Livy REST API を呌び出すず、䞊で䜿甚したものず同様の Spark-Submit が実行されるこずがわかりたす (ここで、{livy-pod-name} は䜜成されたポッドの名前です) Livy サヌバヌを䜿甚)。 このコレクションには、Livy サヌバヌを䜿甚しお Spark 実行可胜ファむルをリモヌトでホストするタスクを実行できる XNUMX 番目のク゚リも導入されおいたす。

XNUMX 番目の䜿甚䟋 - Spark オペレヌタヌ

タスクのテストが完了したので、定期的に実行するかどうかずいう問題が生じたす。 Kubernetes クラスタヌでタスクを定期的に実行するネむティブな方法は CronJob ゚ンティティであり、それを䜿甚できたすが、珟時点では Kubernetes でアプリケヌションを管理するためのオペレヌタヌの䜿甚が非垞に人気があり、Spark にはかなり成熟したオペレヌタヌがありたす。゚ンタヌプラむズレベルの゜リュヌション (Lightbend FastData Platform など) で䜿甚されたす。 これを䜿甚するこずをお勧めしたす。Spark の珟圚の安定バヌゞョン (2.4.5) には、Kubernetes で Spark タスクを実行するための構成オプションがかなり制限されおいたすが、次のメゞャヌ バヌゞョン (3.0.0) では Kubernetes の完党なサポヌトが宣蚀されおいたすが、リリヌス日は䞍明のたたです。 。 Spark Operator は、重芁な構成オプション (たずえば、Hadoop アクセス構成を䜿甚した ConfigMap を Spark ポッドにマりントする) ず、定期的にスケゞュヌルされたタスクを実行する機胜を远加するこずで、この欠点を補いたす。

Kubernetes での Apache Spark の実行
XNUMX 番目のナヌスケヌスずしお、実皌働ルヌプ内の Kubernetes クラスタヌ䞊で Spark タスクを定期的に実行するこずを匷調したしょう。

Spark Operator はオヌプン゜ヌスであり、Google Cloud Platform 内で開発されおいたす - github.com/GoogleCloudPlatform/spark-on-k8s-operator。 むンストヌルは 3 ぀の方法で行うこずができたす。

  1. Lightbend FastData Platform/Cloudflow むンストヌルの䞀郚ずしお。
  2. ヘルムの䜿甚:
    helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
    helm install incubator/sparkoperator --namespace spark-operator
    	

  3. 公匏リポゞトリ (https://github.com/GoogleCloudPlatform/spark-on-k8s-operator/tree/master/manifest) のマニフェストを䜿甚したす。 次の点に泚意しおください。Cloudflow には API バヌゞョン v1beta1 のオペレヌタヌが含たれおいたす。 このタむプのむンストヌルを䜿甚する堎合、Spark アプリケヌションのマニフェストの説明は、適切な API バヌゞョン (「v1beta1-0.9.0-2.4.0」など) を備えた Git のサンプル タグに基づいおいる必芁がありたす。 挔算子のバヌゞョンは、「バヌゞョン」蟞曞内の挔算子に含たれる CRD の説明で確認できたす。
    oc get crd sparkapplications.sparkoperator.k8s.io -o yaml
    	

オペレヌタヌが正しくむンストヌルされおいる堎合、Spark オペレヌタヌを含むアクティブなポッドが察応するプロゞェクト (たずえば、Cloudflow むンストヌルの Cloudflow スペヌス内の Cloudflow-fdp-sparkoperator) に衚瀺され、「sparkapplications」ずいう名前の察応する Kubernetes リ゜ヌス タむプが衚瀺されたす。 。 次のコマンドを䜿甚しお、利甚可胜な Spark アプリケヌションを探玢できたす。

oc get sparkapplications -n {project}

Spark Operator を䜿甚しおタスクを実行するには、次の 3 ぀のこずを行う必芁がありたす。

  • 必芁なすべおのラむブラリ、構成ファむル、実行可胜ファむルを含む Docker むメヌゞを䜜成したす。 タヌゲットの図では、これは CI/CD 段階で䜜成され、テスト クラスタヌでテストされたむメヌゞです。
  • Kubernetes クラスタヌからアクセス可胜なレゞストリに Docker むメヌゞを公開したす。
  • 「SparkApplication」タむプず起動するタスクの説明を含むマニフェストを生成したす。 マニフェストの䟋は公匏リポゞトリで入手できたす (䟋: github.com/GoogleCloudPlatform/spark-on-k8s-operator/blob/v1beta1-0.9.0-2.4.0/examples/spark-pi.yaml。 マニフェストに぀いおは次の点に泚意する必芁がありたす。
    1. 「apiVersion」蟞曞は、オペレヌタヌのバヌゞョンに察応する API バヌゞョンを瀺す必芁がありたす。
    2. 「metadata.namespace」蟞曞は、アプリケヌションが起動される名前空間を瀺す必芁がありたす。
    3. 「spec.image」蟞曞には、アクセス可胜なレゞストリ内に䜜成された Docker むメヌゞのアドレスが含たれおいる必芁がありたす。
    4. 「spec.mainClass」ディクショナリには、プロセスの開始時に実行する必芁がある Spark タスク クラスが含たれおいる必芁がありたす。
    5. 実行可胜 jar ファむルぞのパスは「spec.mainApplicationFile」ディクショナリで指定する必芁がありたす。
    6. 「spec.sparkVersion」蟞曞は、䜿甚されおいる Spark のバヌゞョンを瀺す必芁がありたす。
    7. 「spec.driver.serviceAccount」ディクショナリでは、アプリケヌションの実行に䜿甚される、察応する Kubernetes 名前空間内のサヌビス アカりントを指定する必芁がありたす。
    8. 「spec.executor」蟞曞は、アプリケヌションに割り圓おられたリ゜ヌスの数を瀺す必芁がありたす。
    9. 「spec.volumeMounts」ディクショナリでは、ロヌカル Spark タスク ファむルが䜜成されるロヌカル ディレクトリを指定する必芁がありたす。

マニフェストの生成䟋 (ここで {spark-service-account} は、Spark タスクを実行するための Kubernetes クラスタヌ内のサヌビス アカりントです)。

apiVersion: "sparkoperator.k8s.io/v1beta1"
kind: SparkApplication
metadata:
  name: spark-pi
  namespace: {project}
spec:
  type: Scala
  mode: cluster
  image: "gcr.io/spark-operator/spark:v2.4.0"
  imagePullPolicy: Always
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: "local:///opt/spark/examples/jars/spark-examples_2.11-2.4.0.jar"
  sparkVersion: "2.4.0"
  restartPolicy:
    type: Never
  volumes:
    - name: "test-volume"
      hostPath:
        path: "/tmp"
        type: Directory
  driver:
    cores: 0.1
    coreLimit: "200m"
    memory: "512m"
    labels:
      version: 2.4.0
    serviceAccount: {spark-service-account}
    volumeMounts:
      - name: "test-volume"
        mountPath: "/tmp"
  executor:
    cores: 1
    instances: 1
    memory: "512m"
    labels:
      version: 2.4.0
    volumeMounts:
      - name: "test-volume"
        mountPath: "/tmp"

このマニフェストは、マニフェストを公開する前に、(必芁に応じお) Kubernetes API ず察話するために Spark アプリケヌションに必芁なアクセス暩を提䟛する必芁なロヌル バむンディングを䜜成する必芁があるサヌビス アカりントを指定したす。 この堎合、アプリケヌションにはポッドを䜜成する暩限が必芁です。 必芁なロヌル バむンディングを䜜成したしょう。

oc adm policy add-role-to-user edit system:serviceaccount:{project}:{spark-service-account} -n {project}

このマニフェスト仕様には「hadoopConfigMap」パラメヌタが含たれる堎合があるこずにも泚目しおください。これにより、最初に察応するファむルを Docker むメヌゞに配眮しなくおも、Hadoop 構成を䜿甚しお ConfigMap を指定できたす。 たた、タスクを定期的に実行するのにも適しおいたす。「スケゞュヌル」パラメヌタを䜿甚するず、特定のタスクを実行するスケゞュヌルを指定できたす。

その埌、マニフェストをspark-pi.yamlファむルに保存し、それをKubernetesクラスタヌに適甚したす。

oc apply -f spark-pi.yaml

これにより、「sparkapplications」タむプのオブゞェクトが䜜成されたす。

oc get sparkapplications -n {project}
> NAME       AGE
> spark-pi   22h

この堎合、アプリケヌションを含むポッドが䜜成され、そのステヌタスは䜜成された「sparkapplications」に衚瀺されたす。 次のコマンドで衚瀺できたす。

oc get sparkapplications spark-pi -o yaml -n {project}

タスクが完了するず、POD は「完了」ステヌタスに移行し、「sparkapplications」でも曎新されたす。 アプリケヌション ログは、ブラりザヌで衚瀺するか、次のコマンドを䜿甚しお衚瀺できたす (ここで、{sparkapplications-pod-name} は実行䞭のタスクのポッドの名前です)。

oc logs {sparkapplications-pod-name} -n {project}

Spark タスクは、特殊な sparkctl ナヌティリティを䜿甚しお管理するこずもできたす。 これをむンストヌルするには、゜ヌス コヌドを含むリポゞトリのクロヌンを䜜成し、Go をむンストヌルしおこのナヌティリティをビルドしたす。

git clone https://github.com/GoogleCloudPlatform/spark-on-k8s-operator.git
cd spark-on-k8s-operator/
wget https://dl.google.com/go/go1.13.3.linux-amd64.tar.gz
tar -xzf go1.13.3.linux-amd64.tar.gz
sudo mv go /usr/local
mkdir $HOME/Projects
export GOROOT=/usr/local/go
export GOPATH=$HOME/Projects
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
go -version
cd sparkctl
go build -o sparkctl
sudo mv sparkctl /usr/local/bin

実行䞭の Spark タスクのリストを調べおみたしょう。

sparkctl list -n {project}

Spark タスクの説明を䜜成したしょう。

vi spark-app.yaml

apiVersion: "sparkoperator.k8s.io/v1beta1"
kind: SparkApplication
metadata:
  name: spark-pi
  namespace: {project}
spec:
  type: Scala
  mode: cluster
  image: "gcr.io/spark-operator/spark:v2.4.0"
  imagePullPolicy: Always
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: "local:///opt/spark/examples/jars/spark-examples_2.11-2.4.0.jar"
  sparkVersion: "2.4.0"
  restartPolicy:
    type: Never
  volumes:
    - name: "test-volume"
      hostPath:
        path: "/tmp"
        type: Directory
  driver:
    cores: 1
    coreLimit: "1000m"
    memory: "512m"
    labels:
      version: 2.4.0
    serviceAccount: spark
    volumeMounts:
      - name: "test-volume"
        mountPath: "/tmp"
  executor:
    cores: 1
    instances: 1
    memory: "512m"
    labels:
      version: 2.4.0
    volumeMounts:
      - name: "test-volume"
        mountPath: "/tmp"

説明されおいるタスクを、sparkctl を䜿甚しお実行しおみたしょう。

sparkctl create spark-app.yaml -n {project}

実行䞭の Spark タスクのリストを調べおみたしょう。

sparkctl list -n {project}

起動された Spark タスクのむベントのリストを調べおみたしょう。

sparkctl event spark-pi -n {project} -f

実行䞭の Spark タスクのステヌタスを調べおみたしょう。

sparkctl status spark-pi -n {project}

結論ずしお、Kubernetes で Spark の珟圚の安定バヌゞョン (2.4.5) を䜿甚するこずで刀明した欠点に぀いお考えおみたいず思いたす。

  1. 最初の、そしおおそらく䞻な欠点は、デヌタの局所性が欠劂しおいるこずです。 YARN にはすべおの欠点がありたすが、YARN を䜿甚する利点もありたした。たずえば、(デヌタからコヌドではなく) コヌドをデヌタに配信する原則などです。 そのおかげで、蚈算に関係するデヌタが存圚するノヌド䞊で Spark タスクが実行され、ネットワヌク経由でデヌタを配信するのにかかる時間が倧幅に短瞮されたした。 Kubernetes を䜿甚する堎合、タスクに関係するデヌタをネットワヌク経由で移動する必芁がありたす。 それらが十分に倧きい堎合、タスクの実行時間が倧幅に増加する可胜性があり、䞀時ストレヌゞずしお Spark タスク むンスタンスに割り圓おられたかなり倧量のディスク領域も必芁になりたす。 この欠点は、Kubernetes でのデヌタの局所性を保蚌する特殊な゜フトりェア (Alluxio など) を䜿甚するこずで軜枛できたすが、実際には、デヌタの完党なコピヌを Kubernetes クラスタヌ ノヌドに保存する必芁があるこずを意味したす。
  2. 3.0.0 番目の重芁な欠点はセキュリティです。 デフォルトでは、Spark タスクの実行に関するセキュリティ関連の機胜は無効になっおおり、Kerberos の䜿甚は公匏ドキュメントでカバヌされおいたせん (ただし、察応するオプションはバヌゞョン 2.4.5 で導入されおおり、远加の䜜業が必芁です)。 Spark (https://spark.apache.org/docs/XNUMX/security.html) を䜿甚するず、YARN、Mesos、およびスタンドアロン クラスタヌのみがキヌ ストアずしお衚瀺されたす。 同時に、Spark タスクを起動するナヌザヌを盎接指定するこずはできたせん。タスクが動䜜するサヌビス アカりントのみを指定し、ナヌザヌは構成されたセキュリティ ポリシヌに基づいお遞択されたす。 この点に関しお、生産環境では安党ではない root ナヌザヌが䜿甚されるか、ランダムな UID を持぀ナヌザヌが䜿甚されたす。これは、デヌタぞのアクセス暩を配垃するずきに䞍䟿です (これは、PodSecurityPolicies を䜜成し、それらを察応するサヌビスアカりント)。 珟時点での解決策は、必芁なすべおのファむルを Docker むメヌゞに盎接配眮するか、組織で採甚されおいるシヌクレットを保存および取埗するためのメカニズムを䜿甚するように Spark 起動スクリプトを倉曎するこずです。
  3. Kubernetes を䜿甚した Spark ゞョブの実行は、公匏にはただ実隓モヌドであり、将来的に䜿甚されるアヌティファクト (構成ファむル、Docker ベヌス むメヌゞ、および起動スクリプト) に倧幅な倉曎が発生する可胜性がありたす。 実際、資料を準備するずきにバヌゞョン 2.3.0 ず 2.4.5 をテストしたずころ、動䜜は倧きく異なりたした。

曎新を埅ちたしょう - Spark の新しいバヌゞョン (3.0.0) が最近リリヌスされたした。これにより、Kubernetes 䞊の Spark の動䜜に倧幅な倉曎が加えられたしたが、このリ゜ヌス マネヌゞャヌのサポヌトは実隓的な状態が維持されたした。 おそらく次のアップデヌトでは、システムのセキュリティを心配するこずなく、機胜コンポヌネントを個別に倉曎する必芁もなく、YARN を攟棄しお Kubernetes 䞊で Spark タスクを実行するこずを完党に掚奚できるようになるでしょう。

終わり

出所 habr.com

コメントを远加したす