Darbojas Apache Spark vietnē Kubernetes

Cienījamie lasītāji, laba pēcpusdiena. Šodien mēs nedaudz runāsim par Apache Spark un tās attīstības perspektīvām.

Darbojas Apache Spark vietnē Kubernetes

MÅ«sdienu lielo datu pasaulē Apache Spark ir de facto standarts pakeÅ”u datu apstrādes uzdevumu izstrādei. Turklāt to izmanto arÄ«, lai izveidotu straumÄ“Å”anas lietojumprogrammas, kas darbojas mikropakeÅ”u koncepcijā, apstrādājot un nosÅ«tot datus nelielās porcijās (Spark Structured Streaming). Un tradicionāli tā ir bijusi daļa no kopējās Hadoop steka, izmantojot YARN (vai dažos gadÄ«jumos Apache Mesos) kā resursu pārvaldnieku. LÄ«dz 2020. gadam tā izmantoÅ”ana tradicionālajā formā ir apÅ”aubāma lielākajai daļai uzņēmumu, jo trÅ«kst pienācÄ«gas Hadoop izplatÄ«Å”anas ā€” HDP un CDH attÄ«stÄ«ba ir apstājusies, CDH nav labi attÄ«stÄ«ts un tam ir augstas izmaksas, un pārējiem Hadoop piegādātājiem ir vai nu beidza pastāvēt, vai arÄ« tiem ir blāva nākotne. Tāpēc Apache Spark palaiÅ”ana, izmantojot Kubernetes, rada arvien lielāku interesi sabiedrÄ«bā un lielos uzņēmumos - kļūstot par standartu konteineru orÄ·estrÄ“Å”anā un resursu pārvaldÄ«bā privātajos un publiskajos mākoņos, tas atrisina problēmu ar neērto resursu plānoÅ”anu Spark uzdevumiem YARN un nodroÅ”ina stabili attÄ«stoÅ”a platforma ar daudzām komerciālām un atvērtām izplatÄ«bām visu izmēru un joslu uzņēmumiem. Turklāt, ņemot vērā popularitāti, vairums jau ir paspējuÅ”i iegādāties pāris savas instalācijas un ir palielinājuÅ”i savas zināŔanas tās lietoÅ”anā, kas vienkārÅ”o pārvietoÅ”anos.

Sākot ar versiju 2.3.0, Apache Spark ieguva oficiālu atbalstu uzdevumu izpildei Kubernetes klasterÄ«, un Å”odien mēs runāsim par Ŕīs pieejas paÅ”reizējo briedumu, dažādām tās izmantoÅ”anas iespējām un kļūmēm, ar kurām saskarsies ievieÅ”anas laikā.

Vispirms apskatÄ«sim uzdevumu un lietojumprogrammu izstrādes procesu, pamatojoties uz Apache Spark, un izcelsim tipiskus gadÄ«jumus, kad jums ir jāpalaiž uzdevums Kubernetes klasterÄ«. Sagatavojot Å”o ziņu, OpenShift tiek izmantots kā izplatÄ«Å”ana, un tiks dotas komandas, kas attiecas uz tā komandrindas utilÄ«tu (oc). Citiem Kubernetes izplatÄ«jumiem var izmantot atbilstoŔās komandas no standarta Kubernetes komandrindas utilÄ«ta (kubectl) vai to analogiem (piemēram, oc adm politikai).

Pirmais lietoÅ”anas gadÄ«jums ā€“ spark-submit

Uzdevumu un lietojumprogrammu izstrādes laikā izstrādātājam ir jāpalaiž uzdevumi, lai atkļūdotu datu transformāciju. Teorētiski Å”iem mērÄ·iem var izmantot stubus, taču Å”ajā uzdevumu klasē ātrāka un labāka ir izrādÄ«jusies izstrāde ar reālu (kaut arÄ« testa) gala sistēmu eksemplāru piedalÄ«Å”anos. GadÄ«jumā, ja mēs atkļūdojam reālos gala sistēmu gadÄ«jumos, ir iespējami divi scenāriji:

  • izstrādātājs palaiž Spark uzdevumu lokāli savrupajā režīmā;

    Darbojas Apache Spark vietnē Kubernetes

  • izstrādātājs testa cilpā izpilda Spark uzdevumu Kubernetes klasterÄ«.

    Darbojas Apache Spark vietnē Kubernetes

Pirmajai iespējai ir tiesības pastāvēt, taču tai ir vairāki trūkumi:

  • Katram izstrādātājam ir jānodroÅ”ina piekļuve no darba vietas visiem viņam nepiecieÅ”amajiem gala sistēmu gadÄ«jumiem;
  • darba maŔīnai ir nepiecieÅ”ams pietiekams resursu apjoms, lai izpildÄ«tu izstrādājamo uzdevumu.

Otrajai iespējai nav Å”o trÅ«kumu, jo Kubernetes klastera izmantoÅ”ana ļauj pieŔķirt nepiecieÅ”amo resursu kopu palaiÅ”anas uzdevumiem un nodroÅ”ināt tai nepiecieÅ”amo piekļuvi gala sistēmas gadÄ«jumiem, elastÄ«gi nodroÅ”inot piekļuvi tai, izmantojot Kubernetes lomu modeli. visi izstrādes komandas locekļi. Izcelsim to kā pirmo lietoÅ”anas gadÄ«jumu ā€” Spark uzdevumu palaiÅ”ana no vietējā izstrādātāja maŔīnas Kubernetes klasterÄ« testa ķēdē.

Parunāsim vairāk par Spark iestatÄ«Å”anas procesu vietējai darbÄ«bai. Lai sāktu lietot Spark, tas jāinstalē:

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

Savācam nepiecieŔamās paketes darbam ar Kubernetes:

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

Pilna bÅ«vÄ“Å”ana aizņem daudz laika, un, lai izveidotu Docker attēlus un palaistu tos Kubernetes klasterÄ«, jums patieŔām ir nepiecieÅ”ami tikai jar faili no direktorija ā€œassembly/ā€, lai jÅ«s varētu izveidot tikai Å”o apakÅ”projektu:

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

Lai palaistu Spark darbus vietnē Kubernetes, jums ir jāizveido Docker attēls, ko izmantot kā pamata attēlu. Šeit ir 2 iespējamās pieejas:

  • Ä¢enerētajā Docker attēlā ir iekļauts izpildāmais Spark uzdevuma kods;
  • Izveidotajā attēlā ir iekļauts tikai Spark un nepiecieÅ”amās atkarÄ«bas, izpildāmais kods tiek mitināts attālināti (piemēram, HDFS).

Vispirms izveidosim Docker attēlu, kurā ir Spark uzdevuma testa piemērs. Lai izveidotu Docker attēlus, Spark ir utilīta ar nosaukumu "docker-image-tool". Izpētīsim palīdzību par to:

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

Ar tās palÄ«dzÄ«bu jÅ«s varat izveidot Docker attēlus un augÅ”upielādēt tos attālos reÄ£istros, taču pēc noklusējuma tam ir vairāki trÅ«kumi:

  • bez kļūmēm izveido 3 Docker attēlus vienlaikus - Spark, PySpark un R;
  • neļauj norādÄ«t attēla nosaukumu.

Tāpēc mēs izmantosim Ŕīs utilÄ«tas modificēto versiju, kas norādÄ«ta tālāk:

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

Ar tā palÄ«dzÄ«bu mēs apkopojam pamata Spark attēlu, kurā ir testa uzdevums Pi aprēķināŔanai, izmantojot Spark (Å”eit {docker-registry-url} ir jÅ«su Docker attēlu reÄ£istra URL, {repo} ir repozitorija nosaukums reÄ£istrā, kas atbilst projektam OpenShift , {image-name} ā€” attēla nosaukums (ja tiek izmantota trÄ«s lÄ«meņu attēlu atdalÄ«Å”ana, piemēram, kā integrētajā Red Hat OpenShift attēlu reÄ£istrā), {tag} ā€” Ŕī atzÄ«me attēla versija):

./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

Piesakieties OKD klasterī, izmantojot konsoles utilītu (Ŕeit {OKD-API-URL} ir OKD klastera API URL):

oc login {OKD-API-URL}

IegÅ«sim paÅ”reizējā lietotāja pilnvaru autorizācijai Docker reÄ£istrā:

oc whoami -t

Piesakieties OKD klastera iekŔējā Docker reÄ£istrā (kā paroli mēs izmantojam marÄ·ieri, kas iegÅ«ta, izmantojot iepriekŔējo komandu):

docker login {docker-registry-url}

AugÅ”upielādēsim salikto Docker attēlu Docker reÄ£istra OKD:

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

PārbaudÄ«sim, vai samontētais attēls ir pieejams OKD. Lai to izdarÄ«tu, pārlÅ«kprogrammā atveriet URL ar atbilstoŔā projekta attēlu sarakstu (Å”eit {project} ir projekta nosaukums OpenShift klasterÄ«, {OKD-WEBUI-URL} ir OpenShift Web konsoles URL. ) - https://{OKD-WEBUI-URL}/console /project/{project}/browse/images/{image-name}.

Lai palaistu uzdevumus, ir jāizveido pakalpojuma konts ar privilēģijām palaist pods kā root (Å”o punktu apspriedÄ«sim vēlāk):

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

Palaidīsim komandu spark-submit, lai publicētu Spark uzdevumu OKD klasterī, norādot izveidoto pakalpojuma kontu un Docker attēlu:

 /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

Š—Š“ŠµŃŃŒ:

ā€”nosaukums ā€” uzdevuma nosaukums, kas piedalÄ«sies Kubernetes pākstu nosaukuma veidoÅ”anā;

ā€”class ā€” izpildāmā faila klase, kas tiek izsaukta, kad tiek palaists uzdevums;

ā€”conf ā€” Spark konfigurācijas parametri;

spark.executor.instances ā€” palaižamo Spark izpildÄ«tāju skaits;

spark.kubernetes.authenticate.driver.serviceAccountName ā€” Kubernetes pakalpojuma konta nosaukums, kas tiek izmantots, palaižot apvidus (lai definētu droŔības kontekstu un iespējas, mijiedarbojoties ar Kubernetes API);

spark.kubernetes.namespace ā€” Kubernetes nosaukumtelpa, kurā tiks palaisti draiveru un izpildÄ«tāju podi;

spark.submit.deployMode ā€” Spark palaiÅ”anas metode (standarta spark-submit tiek izmantots ā€œklasterisā€, Spark Operator un jaunākām Spark versijām ā€œklientsā€);

spark.kubernetes.container.image ā€” Docker attēls, ko izmanto, lai palaistu pākstis;

spark.master ā€” Kubernetes API URL (ir norādÄ«ts ārējais, tāpēc piekļuve notiek no vietējās maŔīnas);

local:// ir ceļŔ uz Spark izpildāmo failu Docker attēlā.

Mēs ejam uz atbilstoÅ”o OKD projektu un pētām izveidotos podi - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods.

Lai vienkārÅ”otu izstrādes procesu, var izmantot citu opciju, kurā tiek izveidots kopÄ«gs Spark bāzes attēls, kuru izmanto visi uzdevumi, lai palaistu, un izpildāmo failu momentuzņēmumi tiek publicēti ārējā krātuvē (piemēram, Hadoop) un norādÄ«ti zvanot. spark-iesniegt kā saiti. Šādā gadÄ«jumā varat palaist dažādas Spark uzdevumu versijas, nepārbÅ«vējot Docker attēlus, izmantojot, piemēram, WebHDFS, lai publicētu attēlus. Mēs nosÅ«tām pieprasÄ«jumu izveidot failu (Å”eit {host} ir WebHDFS pakalpojuma resursdators, {port} ir WebHDFS pakalpojuma ports, {path-to-file-on-hdfs} ir vēlamais ceļŔ uz failu uz HDFS):

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

JÅ«s saņemsit Ŕādu atbildi (Å”eit {location} ir URL, kas jāizmanto faila lejupielādei):

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

Ielādējiet Spark izpildāmo failu HDFS (Å”eit {path-to-local-file} ir ceļŔ uz Spark izpildāmo failu paÅ”reizējā resursdatorā):

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

Pēc tam mēs varam veikt spark-iesniegÅ”anu, izmantojot Spark failu, kas augÅ”upielādēts HDFS (Å”eit {class-name} ir tās klases nosaukums, kas jāpalaiž, lai pabeigtu uzdevumu):

/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}

Jāņem vērā, ka, lai piekļūtu HDFS un nodroÅ”inātu uzdevuma darbÄ«bu, iespējams, bÅ«s jāmaina Dockerfile un enterpoint.sh skripts - Dockerfile jāpievieno direktÄ«va, lai kopētu atkarÄ«gās bibliotēkas direktorijā /opt/spark/jars un iekļaut HDFS konfigurācijas failu ievades punktā SPARK_CLASSPATH. sh.

Otrais lietoŔanas gadījums - Apache Livy

Turklāt, kad uzdevums ir izstrādāts un rezultāts ir jāpārbauda, ā€‹ā€‹rodas jautājums par tā palaiÅ”anu kā daļu no CI/CD procesa un izsekot tā izpildes statusam. Protams, varat to palaist, izmantojot lokālo spark-submit izsaukumu, taču tas sarežģī CI/CD infrastruktÅ«ru, jo ir jāinstalē un jākonfigurē Spark CI servera aÄ£entos/skrējējiem un jāiestata piekļuve Kubernetes API. Å ajā gadÄ«jumā mērÄ·a ievieÅ”ana ir izvēlējusies izmantot Apache Livy kā REST API, lai palaistu Spark uzdevumus, kas mitināti Kubernetes klasterÄ«. Ar tās palÄ«dzÄ«bu jÅ«s varat palaist Spark uzdevumus Kubernetes klasterÄ«, izmantojot parastos cURL pieprasÄ«jumus, kas ir viegli ievieÅ”ami, pamatojoties uz jebkuru CI risinājumu, un tā ievietoÅ”ana Kubernetes klasterÄ« atrisina autentifikācijas problēmu, mijiedarbojoties ar Kubernetes API.

Darbojas Apache Spark vietnē Kubernetes

Izcelsim to kā otro lietoÅ”anas gadÄ«jumu ā€” Spark uzdevumu izpilde kā daļa no CI/CD procesa Kubernetes klasterÄ« testa cilpā.

Nedaudz par Apache Livy - tas darbojas kā HTTP serveris, kas nodroÅ”ina tÄ«mekļa saskarni un RESTful API, kas ļauj attālināti palaist spark-submit, nododot nepiecieÅ”amos parametrus. Tradicionāli tas tiek piegādāts kā daļa no HDP izplatÄ«Å”anas, taču to var izvietot arÄ« OKD vai jebkurā citā Kubernetes instalācijā, izmantojot atbilstoÅ”o manifestu un Docker attēlu kopu, piemēram, Å”o - github.com/ttauveron/k8s-big-data-experiments/tree/master/livy-spark-2.3. MÅ«su gadÄ«jumā tika izveidots lÄ«dzÄ«gs Docker attēls, tostarp Spark versija 2.4.5 no Ŕāda Dockerfile:

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"]

Ä¢enerēto attēlu var izveidot un augÅ”upielādēt esoÅ”ajā Docker repozitorijā, piemēram, iekŔējā OKD repozitorijā. Lai to izvietotu, izmantojiet Å”o manifestu ({registry-url} ā€” Docker attēlu reÄ£istra URL, {image-name} ā€” Docker attēla nosaukums, {tag} ā€” Docker attēla tags, {livy-url} ā€” vēlamais URL, kur serveris bÅ«s pieejams Livy; manifests ā€œMarÅ”rutsā€ tiek izmantots, ja Red Hat OpenShift tiek izmantots kā Kubernetes izplatÄ«Å”ana, pretējā gadÄ«jumā tiek izmantots atbilstoÅ”ais NodePort tipa Ingress vai pakalpojuma manifests):

---
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

Pēc tā lietoÅ”anas un veiksmÄ«gas podziņas palaiÅ”anas Livy grafiskais interfeiss ir pieejams saitē: http://{livy-url}/ui. Izmantojot Livy, mēs varam publicēt savu Spark uzdevumu, izmantojot REST pieprasÄ«jumu no, piemēram, Pastnieka. Tālāk ir parādÄ«ts kolekcijas ar pieprasÄ«jumiem piemērs (konfigurācijas argumentus ar mainÄ«gajiem, kas nepiecieÅ”ami palaistā uzdevuma darbÄ«bai, var nodot ā€œargsā€ masÄ«vā):

{
    "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": {}
}

IzpildÄ«sim pirmo pieprasÄ«jumu no kolekcijas, ejam uz OKD saskarni un pārbaudÄ«sim, vai uzdevums ir veiksmÄ«gi palaists - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods. Tajā paŔā laikā Livy saskarnē (http://{livy-url}/ui) parādÄ«sies sesija, kurā, izmantojot Livy API vai grafisko interfeisu, varat izsekot uzdevuma gaitai un izpētÄ«t sesiju. baļķi.

Tagad parādÄ«sim, kā LÄ«vija strādā. Lai to izdarÄ«tu, izpētÄ«sim Livy konteinera žurnālus podā ar Livy serveri ā€” https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods/{livy-pod-name }?tab=logs. No tiem mēs varam redzēt, ka, izsaucot Livy REST API konteinerā ar nosaukumu ā€œlivyā€, tiek izpildÄ«ts spark-submit, lÄ«dzÄ«gs tam, ko izmantojām iepriekÅ” (Å”eit {livy-pod-name} ir izveidotā pod nosaukums ar Livy serveri). Kolekcijā ir iekļauts arÄ« otrs vaicājums, kas ļauj izpildÄ«t uzdevumus, kas attālināti mitina Spark izpildāmo failu, izmantojot Livy serveri.

TreŔais lietoŔanas gadījums - Spark Operator

Tagad, kad uzdevums ir pārbaudÄ«ts, rodas jautājums par tā regulāru izpildi. Vietējais veids, kā regulāri palaist uzdevumus Kubernetes klasterÄ«, ir entÄ«tija CronJob, un jÅ«s to varat izmantot, taču Å”obrÄ«d operatoru izmantoÅ”ana lietojumprogrammu pārvaldÄ«bai Kubernetes ir ļoti populāra, un Spark ir diezgan nobriedis operators, kas arÄ« ir izmanto uzņēmuma lÄ«meņa risinājumos (piemēram, Lightbend FastData Platform). Mēs iesakām to izmantot ā€” paÅ”reizējai stabilajai Spark versijai (2.4.5) ir diezgan ierobežotas konfigurācijas iespējas Spark uzdevumu izpildei Kubernetes, savukārt nākamā lielākā versija (3.0.0) deklarē pilnÄ«gu Kubernetes atbalstu, taču tās izlaiÅ”anas datums joprojām nav zināms. . Spark Operator kompensē Å”o trÅ«kumu, pievienojot svarÄ«gas konfigurācijas opcijas (piemēram, uzstādot ConfigMap ar Hadoop piekļuves konfigurāciju Spark podiem) un iespēju palaist regulāri ieplānotu uzdevumu.

Darbojas Apache Spark vietnē Kubernetes
Izcelsim to kā treÅ”o lietoÅ”anas gadÄ«jumu ā€” regulāra Spark uzdevumu izpilde Kubernetes klasterÄ« ražoÅ”anas cikla ietvaros.

Spark Operator ir atvērtā pirmkoda un izstrādāts Google Cloud Platform ietvaros. github.com/GoogleCloudPlatform/spark-on-k8s-operator. Tās uzstādÄ«Å”anu var veikt 3 veidos:

  1. Kā daļa no Lightbend FastData Platform/Cloudflow instalācijas;
  2. Izmantojot stūri:
    helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
    helm install incubator/sparkoperator --namespace spark-operator
    	

  3. Izmantojiet manifestus no oficiālā repozitorija (https://github.com/GoogleCloudPlatform/spark-on-k8s-operator/tree/master/manifest). Ir vērts atzÄ«mēt sekojoÅ”o - Cloudflow ietver operatoru ar API versiju v1beta1. Ja tiek izmantota Ŕāda veida instalÄ“Å”ana, Spark lietojumprogrammu manifestu aprakstiem ir jābÅ«t balstÄ«tiem uz Git tagu paraugiem ar atbilstoÅ”u API versiju, piemēram, "v1beta1-0.9.0-2.4.0". Operatora versiju var atrast CRD aprakstā, kas iekļauts operatora vārdnÄ«cā ā€œversijasā€:
    oc get crd sparkapplications.sparkoperator.k8s.io -o yaml
    	

Ja operators ir pareizi instalēts, attiecÄ«gajā projektā parādÄ«sies aktÄ«vs pods ar Spark operatoru (piemēram, cloudflow-fdp-sparkoperator Cloudflow instalācijas Cloudflow telpā) un tiks parādÄ«ts atbilstoÅ”s Kubernetes resursa veids ar nosaukumu ā€œsparkapplicationsā€. . Varat izpētÄ«t pieejamās Spark lietojumprogrammas, izmantojot Ŕādu komandu:

oc get sparkapplications -n {project}

Lai izpildītu uzdevumus, izmantojot Spark Operator, ir jāveic 3 darbības:

  • izveidot Docker attēlu, kas ietver visas nepiecieÅ”amās bibliotēkas, kā arÄ« konfigurācijas un izpildāmos failus. MērÄ·a attēlā Å”is ir attēls, kas izveidots CI/CD stadijā un pārbaudÄ«ts testa klasterÄ«;
  • publicēt Docker attēlu reÄ£istrā, kas pieejams no Kubernetes klastera;
  • Ä£enerējiet manifestu ar "SparkApplication" tipu un palaižamā uzdevuma aprakstu. Manifestu piemēri ir pieejami oficiālajā repozitorijā (piem. github.com/GoogleCloudPlatform/spark-on-k8s-operator/blob/v1beta1-0.9.0-2.4.0/examples/spark-pi.yaml). Ir svarÄ«gi atzÄ«mēt manifestu:
    1. vārdnÄ«cā ā€œapiVersionā€ jānorāda operatora versijai atbilstoŔā API versija;
    2. vārdnÄ«cā ā€œmetadata.namespaceā€ jānorāda nosaukumtelpa, kurā tiks palaists lietojumprogramma;
    3. vārdnÄ«cā ā€œspec.imageā€ ir jāietver izveidotā Docker attēla adrese pieejamā reÄ£istrā;
    4. vārdnÄ«cā ā€œspec.mainClassā€ ir jābÅ«t Spark uzdevumu klasei, kas jāpalaiž, kad process sākas;
    5. vārdnÄ«cā ā€œspec.mainApplicationFileā€ ir jāietver ceļŔ uz izpildāmo jar failu;
    6. vārdnÄ«cā ā€œspec.sparkVersionā€ jānorāda izmantotā Spark versija;
    7. vārdnÄ«cā ā€œspec.driver.serviceAccountā€ ir jānorāda pakalpojuma konts attiecÄ«gajā Kubernetes nosaukumvietā, kas tiks izmantots lietojumprogrammas palaiÅ”anai;
    8. vārdnÄ«cā ā€œspec.izpildÄ«tājsā€ jānorāda pieteikumam pieŔķirto resursu skaits;
    9. vārdnīcā "spec.volumeMounts" ir jānorāda lokālais direktorijs, kurā tiks izveidoti lokālie Spark uzdevumu faili.

Manifesta Ä£enerÄ“Å”anas piemērs (Å”eit {spark-service-account} ir pakalpojuma konts Kubernetes klasterÄ« Spark uzdevumu izpildei):

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"

Å ajā manifestā ir norādÄ«ts pakalpojuma konts, kuram pirms manifesta publicÄ“Å”anas ir jāizveido nepiecieÅ”amie lomu saistÄ«jumi, kas nodroÅ”ina nepiecieÅ”amās piekļuves tiesÄ«bas Spark lietojumprogrammai mijiedarbÄ«bai ar Kubernetes API (ja nepiecieÅ”ams). MÅ«su gadÄ«jumā lietojumprogrammai ir nepiecieÅ”amas tiesÄ«bas, lai izveidotu Pods. Izveidosim nepiecieÅ”amo lomu saiti:

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

Ir arÄ« vērts atzÄ«mēt, ka Å”ajā manifesta specifikācijā var bÅ«t ietverts parametrs "hadoopConfigMap", kas ļauj norādÄ«t ConfigMap ar Hadoop konfigurāciju, vispirms neievietojot attiecÄ«go failu Docker attēlā. Tas ir piemērots arÄ« regulārai uzdevumu izpildei - izmantojot parametru ā€œgrafiksā€, var norādÄ«t konkrētā uzdevuma izpildes grafiku.

Pēc tam mēs saglabājam manifestu failā spark-pi.yaml un lietojam to mūsu Kubernetes klasterī:

oc apply -f spark-pi.yaml

Tas izveidos ā€œsparkapplicationsā€ tipa objektu:

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

Å ajā gadÄ«jumā tiks izveidots pods ar lietojumprogrammu, kuras statuss tiks parādÄ«ts izveidotajās ā€œsparkaplikācijāsā€. To var apskatÄ«t ar Ŕādu komandu:

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

Pēc uzdevuma pabeigÅ”anas POD pāries uz statusu ā€œPabeigtsā€, kas tiks atjaunināts arÄ« sadaļā ā€œSparkapplicationsā€. Lietojumprogrammu žurnālus var skatÄ«t pārlÅ«kprogrammā vai izmantojot Ŕādu komandu (Å”eit {sparkapplications-pod-name} ir izpildāmā uzdevuma apgabala nosaukums):

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

Spark uzdevumus var pārvaldÄ«t arÄ«, izmantojot specializēto sparkctl utilÄ«tu. Lai to instalētu, klonējiet repozitoriju ar tā avota kodu, instalējiet Go un izveidojiet Å”o utilÄ«tu:

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

Apskatīsim esoŔo Spark uzdevumu sarakstu:

sparkctl list -n {project}

Izveidosim Spark uzdevuma aprakstu:

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"

Izpildīsim aprakstīto uzdevumu, izmantojot sparkctl:

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

Apskatīsim esoŔo Spark uzdevumu sarakstu:

sparkctl list -n {project}

Apskatīsim palaistā Spark uzdevuma notikumu sarakstu:

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

Apskatīsim palaistā Spark uzdevuma statusu:

sparkctl status spark-pi -n {project}

Noslēgumā es vēlos apsvērt atklātos trÅ«kumus, izmantojot paÅ”reizējo stabilo Spark versiju (2.4.5) Kubernetes:

  1. Pirmais un, iespējams, galvenais trÅ«kums ir datu lokācijas trÅ«kums. Neskatoties uz visiem YARN trÅ«kumiem, tā izmantoÅ”anai bija arÄ« priekÅ”rocÄ«bas, piemēram, koda piegādes princips datiem (nevis datiem kodā). Pateicoties tam, Spark uzdevumi tika izpildÄ«ti mezglos, kuros atradās aprēķinos iesaistÄ«tie dati, un ievērojami samazinājās laiks, kas bija nepiecieÅ”ams datu piegādei tÄ«klā. Lietojot Kubernetes, mēs saskaramies ar nepiecieÅ”amÄ«bu pārvietot ar uzdevumu saistÄ«tos datus tÄ«klā. Ja tie ir pietiekami lieli, uzdevumu izpildes laiks var ievērojami palielināties, kā arÄ« nepiecieÅ”ams diezgan liels diska vietas apjoms, kas tiek atvēlēts Spark uzdevumu gadÄ«jumiem to pagaidu glabāŔanai. Å o trÅ«kumu var mazināt, izmantojot specializētu programmatÅ«ru, kas nodroÅ”ina datu atraÅ”anās vietu Kubernetes (piemēram, Alluxio), taču patiesÄ«bā tas nozÄ«mē nepiecieÅ”amÄ«bu Kubernetes klastera mezglos saglabāt pilnÄ«gu datu kopiju.
  2. Otrs svarÄ«gais trÅ«kums ir droŔība. Pēc noklusējuma ir atspējoti ar droŔību saistÄ«tie lÄ«dzekļi saistÄ«bā ar Spark uzdevumu izpildi, Kerberos lietoÅ”ana nav ietverta oficiālajā dokumentācijā (lai gan atbilstoŔās opcijas tika ieviestas versijā 3.0.0, kas prasÄ«s papildu darbu), un droŔības dokumentācija izmantojot Spark (https://spark.apache.org/docs/2.4.5/security.html), tikai YARN, Mesos un Standalone Cluster tiek parādÄ«ti kā galvenie veikali. Tajā paŔā laikā nevar tieÅ”i norādÄ«t lietotāju, kuram tiek palaisti Spark uzdevumi - mēs norādām tikai pakalpojuma kontu, kurā tas darbosies, un lietotājs tiek atlasÄ«ts, pamatojoties uz konfigurētajām droŔības politikām. Å ajā sakarā tiek izmantots vai nu root lietotājs, kas nav droÅ”s produktÄ«vā vidē, vai arÄ« lietotājs ar nejauÅ”u UID, kas ir neērti, sadalot piekļuves tiesÄ«bas datiem (to var atrisināt, izveidojot PodSecurityPolicies un saistot tās ar atbilstoÅ”os pakalpojumu kontos). PaÅ”laik risinājums ir vai nu ievietot visus nepiecieÅ”amos failus tieÅ”i Docker attēlā, vai modificēt Spark palaiÅ”anas skriptu, lai izmantotu jÅ«su organizācijā pieņemto noslēpumu glabāŔanas un izguves mehānismu.
  3. Spark darbu izpilde, izmantojot Kubernetes, oficiāli joprojām ir eksperimentālā režīmā, un nākotnē var bÅ«t bÅ«tiskas izmaiņas izmantotajos artefaktos (konfigurācijas faili, Docker bāzes attēli un palaiÅ”anas skripti). Un patieŔām, sagatavojot materiālu, tika pārbaudÄ«tas versijas 2.3.0 un 2.4.5, uzvedÄ«ba bija ievērojami atŔķirÄ«ga.

GaidÄ«sim atjauninājumus - nesen tika izlaista jauna Spark versija (3.0.0), kas ienesa bÅ«tiskas izmaiņas Spark darbā uz Kubernetes, taču saglabāja Ŕī resursa pārvaldnieka atbalsta eksperimentālo statusu. Iespējams, ka nākamie atjauninājumi patieŔām ļaus pilnÄ«bā ieteikt atteikties no YARN un palaist Spark uzdevumus Kubernetes, nebaidoties par savas sistēmas droŔību un bez nepiecieÅ”amÄ«bas neatkarÄ«gi modificēt funkcionālos komponentus.

Beigas.

Avots: www.habr.com

Pievieno komentāru