Rhedeg Apache Spark ar Kubernetes

Annwyl ddarllenwyr, prynhawn da. Heddiw, byddwn yn siarad ychydig am Apache Spark a'i ragolygon datblygu.

Rhedeg Apache Spark ar Kubernetes

Ym myd modern Data Mawr, Apache Spark yw'r safon de facto ar gyfer datblygu tasgau prosesu data swp. Yn ogystal, fe'i defnyddir hefyd i greu cymwysiadau ffrydio sy'n gweithio yn y cysyniad swp micro, prosesu a chludo data mewn dognau bach (Ffrydio Strwythuredig Spark). Ac yn draddodiadol mae wedi bod yn rhan o stac cyffredinol Hadoop, gan ddefnyddio YARN (neu mewn rhai achosion Apache Mesos) fel y rheolwr adnoddau. Erbyn 2020, mae ei ddefnydd yn ei ffurf draddodiadol dan amheuaeth ar gyfer y rhan fwyaf o gwmnïau oherwydd diffyg dosbarthiadau Hadoop gweddus - mae datblygiad HDP a CDH wedi dod i ben, nid yw CDH wedi'i ddatblygu'n dda ac mae ganddo gost uchel, ac mae'r cyflenwyr Hadoop sy'n weddill wedi dod i ben. naill ai wedi peidio â bodoli neu â dyfodol gwan. Felly, mae lansiad Apache Spark gan ddefnyddio Kubernetes o ddiddordeb cynyddol ymhlith y gymuned a chwmnïau mawr - gan ddod yn safon mewn cerddorfa cynhwysydd a rheoli adnoddau mewn cymylau preifat a chyhoeddus, mae'n datrys y broblem gydag amserlennu adnoddau anghyfleus o dasgau Spark ar YARN ac yn darparu llwyfan sy'n datblygu'n raddol gyda llawer o ddosbarthiadau masnachol ac agored ar gyfer cwmnïau o bob maint a streipiau. Yn ogystal, yn sgil poblogrwydd, mae'r mwyafrif eisoes wedi llwyddo i gaffael cwpl o osodiadau eu hunain ac wedi cynyddu eu harbenigedd wrth ei ddefnyddio, sy'n symleiddio'r symudiad.

Gan ddechrau gyda fersiwn 2.3.0, cafodd Apache Spark gefnogaeth swyddogol ar gyfer rhedeg tasgau mewn clwstwr Kubernetes a heddiw, byddwn yn siarad am aeddfedrwydd presennol y dull hwn, amrywiol opsiynau ar gyfer ei ddefnyddio a'r peryglon a ddaw ar eu traws yn ystod y gweithredu.

Yn gyntaf oll, gadewch i ni edrych ar y broses o ddatblygu tasgau a chymwysiadau yn seiliedig ar Apache Spark a thynnu sylw at achosion nodweddiadol lle mae angen i chi redeg tasg ar glwstwr Kubernetes. Wrth baratoi'r swydd hon, defnyddir OpenShift fel dosbarthiad a rhoddir gorchmynion sy'n berthnasol i'w ddefnyddioldeb llinell orchymyn (oc). Ar gyfer dosbarthiadau Kubernetes eraill, gellir defnyddio'r gorchmynion cyfatebol o gyfleustodau llinell orchymyn safonol Kubernetes (kubectl) neu eu analogau (er enghraifft, ar gyfer polisi oc adm).

Achos defnydd cyntaf - gwreichionen-cyflwyno

Yn ystod datblygiad tasgau a chymwysiadau, mae angen i'r datblygwr redeg tasgau i ddadfygio trawsnewid data. Yn ddamcaniaethol, gellir defnyddio bonion at y dibenion hyn, ond mae datblygiad gyda chyfranogiad achosion gwirioneddol (er prawf) o systemau diwedd wedi profi i fod yn gyflymach ac yn well yn y dosbarth hwn o dasgau. Yn yr achos pan fyddwn yn dadfygio ar achosion gwirioneddol o systemau diwedd, mae dwy senario yn bosibl:

  • mae'r datblygwr yn rhedeg tasg Spark yn lleol mewn modd annibynnol;

    Rhedeg Apache Spark ar Kubernetes

  • mae datblygwr yn rhedeg tasg Spark ar glwstwr Kubernetes mewn dolen brawf.

    Rhedeg Apache Spark ar Kubernetes

Mae gan yr opsiwn cyntaf yr hawl i fodoli, ond mae'n golygu nifer o anfanteision:

  • Rhaid darparu mynediad o'r gweithle i bob datblygwr i bob achos o'r systemau terfynol sydd eu hangen arno;
  • mae angen swm digonol o adnoddau ar y peiriant gweithio i redeg y dasg sy'n cael ei datblygu.

Nid oes gan yr ail opsiwn yr anfanteision hyn, gan fod defnyddio clwstwr Kubernetes yn caniatáu ichi ddyrannu'r gronfa adnoddau angenrheidiol ar gyfer rhedeg tasgau a rhoi'r mynediad angenrheidiol iddo i achosion diwedd system, gan ddarparu mynediad hyblyg iddo gan ddefnyddio model rôl Kubernetes ar gyfer holl aelodau'r tîm datblygu. Gadewch i ni ei amlygu fel yr achos defnydd cyntaf - lansio tasgau Spark o beiriant datblygwr lleol ar glwstwr Kubernetes mewn cylched prawf.

Gadewch i ni siarad mwy am y broses o sefydlu Spark i redeg yn lleol. I ddechrau defnyddio Spark mae angen i chi ei osod:

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

Rydym yn casglu'r pecynnau angenrheidiol ar gyfer gweithio gyda Kubernetes:

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

Mae adeiladwaith llawn yn cymryd llawer o amser, ac i greu delweddau Docker a'u rhedeg ar glwstwr Kubernetes, dim ond ffeiliau jar o'r cyfeiriadur “cynulliad /” sydd eu hangen arnoch chi mewn gwirionedd, felly dim ond yr is-brosiect hwn y gallwch chi ei adeiladu:

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

I redeg swyddi Spark ar Kubernetes, mae angen i chi greu delwedd Docker i'w ddefnyddio fel delwedd sylfaen. Mae 2 ddull posibl yma:

  • Mae'r ddelwedd Docker a gynhyrchir yn cynnwys y cod tasg Spark gweithredadwy;
  • Mae'r ddelwedd a grëwyd yn cynnwys Spark yn unig a'r dibyniaethau angenrheidiol, mae'r cod gweithredadwy yn cael ei gynnal o bell (er enghraifft, yn HDFS).

Yn gyntaf, gadewch i ni adeiladu delwedd Docker sy'n cynnwys enghraifft brawf o dasg Spark. I greu delweddau Docker, mae gan Spark gyfleustodau o'r enw "docker-image-tool". Gadewch i ni astudio'r help arno:

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

Gyda'i help, gallwch greu delweddau Docker a'u llwytho i fyny i gofrestrfeydd anghysbell, ond yn ddiofyn mae ganddo nifer o anfanteision:

  • yn ddi-ffael yn creu 3 delwedd Docker ar unwaith - ar gyfer Spark, PySpark ac R;
  • nid yw'n caniatáu ichi nodi enw delwedd.

Felly, byddwn yn defnyddio fersiwn wedi'i addasu o'r cyfleustodau hwn a roddir isod:

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

Gyda'i help, rydym yn cydosod delwedd Spark sylfaenol sy'n cynnwys tasg prawf ar gyfer cyfrifo Pi gan ddefnyddio Spark (dyma {docker-registry-url} yw URL eich cofrestrfa ddelweddau Docker, {repo} yw enw'r ystorfa y tu mewn i'r gofrestrfa, sy'n cyfateb i'r prosiect yn OpenShift , {image-name} - enw'r ddelwedd (os defnyddir gwahaniad tair lefel o ddelweddau, er enghraifft, fel yn y gofrestr integredig o ddelweddau Red Hat OpenShift), {tag} - tag o hwn fersiwn o'r ddelwedd):

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

Mewngofnodwch i'r clwstwr OKD gan ddefnyddio'r cyfleustodau consol (yma {OKD-API-URL} yw URL API clwstwr OKD):

oc login {OKD-API-URL}

Gadewch i ni gael tocyn y defnyddiwr presennol ar gyfer awdurdodiad yng Nghofrestrfa'r Docwyr:

oc whoami -t

Mewngofnodwch i Gofrestrfa Docker fewnol y clwstwr OKD (rydym yn defnyddio'r tocyn a gafwyd gan ddefnyddio'r gorchymyn blaenorol fel y cyfrinair):

docker login {docker-registry-url}

Gadewch i ni uwchlwytho'r ddelwedd Docker sydd wedi'i ymgynnull i'r Gofrestrfa Dociwr OKD:

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

Gadewch i ni wirio bod y ddelwedd sydd wedi'i ymgynnull ar gael yn OKD. I wneud hyn, agorwch yr URL yn y porwr gyda rhestr o ddelweddau o'r prosiect cyfatebol (yma {project} yw enw'r prosiect y tu mewn i'r clwstwr OpenShift, {OKD-WEBUI-URL} yw URL y consol OpenShift Web ) - https://{OKD-WEBUI-URL}/console /project/{project}/browse/images/{image-name}.

I redeg tasgau, rhaid creu cyfrif gwasanaeth gyda'r breintiau i redeg codennau fel gwraidd (byddwn yn trafod y pwynt hwn yn nes ymlaen):

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

Gadewch i ni redeg y gorchymyn spark-submit i gyhoeddi tasg Spark i'r clwstwr OKD, gan nodi'r cyfrif gwasanaeth a grëwyd a delwedd Docker:

 /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

Yma:

-enw - enw'r dasg a fydd yn cymryd rhan wrth ffurfio enw codennau Kubernetes;

—dosbarth — dosbarth y ffeil gweithredadwy, a elwir pan fydd y dasg yn cychwyn;

—conf — Paramedrau cyfluniad Spark;

spark.executor.instances — nifer yr ysgutorion Spark i'w lansio;

spark.kubernetes.authenticate.driver.serviceAccountName - enw'r cyfrif gwasanaeth Kubernetes a ddefnyddir wrth lansio codennau (i ddiffinio'r cyd-destun diogelwch a galluoedd wrth ryngweithio ag API Kubernetes);

spark.kubernetes.namespace — gofod enwau Kubernetes lle bydd codennau gyrrwr ac ysgutor yn cael eu lansio;

spark.submit.deployMode — dull o lansio Spark (ar gyfer “clwstwr” cyflwyno gwreichionen safonol, ar gyfer Spark Operator a fersiynau diweddarach o Spark “cleient”);

spark.kubernetes.container.image - Delwedd docwr a ddefnyddir i lansio codennau;

spark.master - Kubernetes API URL (nodir allanol fel bod mynediad yn digwydd o'r peiriant lleol);

local:// yw'r llwybr i'r Spark gweithredadwy y tu mewn i ddelwedd y Docker.

Rydym yn mynd i'r prosiect OKD cyfatebol ac yn astudio'r codennau a grëwyd - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods.

I symleiddio'r broses ddatblygu, gellir defnyddio opsiwn arall, lle mae delwedd sylfaenol gyffredin o Spark yn cael ei chreu, ei defnyddio gan bob tasg i'w rhedeg, a cipluniau o ffeiliau gweithredadwy yn cael eu cyhoeddi i storfa allanol (er enghraifft, Hadoop) a'u nodi wrth alw spark-cyflwyno fel dolen. Yn yr achos hwn, gallwch redeg gwahanol fersiynau o dasgau Spark heb ailadeiladu delweddau Docker, gan ddefnyddio, er enghraifft, WebHDFS i gyhoeddi delweddau. Rydym yn anfon cais i greu ffeil (yma {host} yw gwesteiwr y gwasanaeth WebHDFS, {port} yw porthladd y gwasanaeth WebHDFS, {path-to-file-on-hdfs} yw'r llwybr dymunol i'r ffeil ar HDFS):

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

Byddwch yn derbyn ymateb fel hyn (dyma {location} yw'r URL sydd angen ei ddefnyddio i lawrlwytho'r ffeil):

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

Llwythwch y ffeil gweithredadwy Spark i HDFS (yma {path-to-local-file} yw'r llwybr i'r ffeil gweithredadwy Spark ar y gwesteiwr presennol):

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

Ar ôl hyn, gallwn ni wneud spark-submit gan ddefnyddio'r ffeil Spark a uwchlwythwyd i HDFS (yma {enw'r dosbarth} yw enw'r dosbarth sydd angen ei lansio i gwblhau'r dasg):

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

Dylid nodi, er mwyn cyrchu HDFS a sicrhau bod y dasg yn gweithio, efallai y bydd angen i chi newid y Dockerfile a'r sgript entrypoint.sh - ychwanegu cyfarwyddeb i'r Dockerfile i gopïo llyfrgelloedd dibynnol i'r cyfeiriadur /opt/spark/jars a cynnwys y ffeil ffurfweddu HDFS yn SPARK_CLASSPATH yn y pwynt mynediad. sh.

Achos ail ddefnydd - Apache Livy

Ymhellach, pan fydd tasg yn cael ei datblygu a bod angen profi'r canlyniad, mae'r cwestiwn yn codi o'i lansio fel rhan o'r broses CI/CD ac olrhain statws ei chyflawni. Wrth gwrs, gallwch ei redeg gan ddefnyddio galwad gwreichionen-gyflwyno lleol, ond mae hyn yn cymhlethu'r seilwaith CI / CD gan ei fod yn gofyn am osod a ffurfweddu Spark ar asiantau / rhedwyr gweinydd CI a sefydlu mynediad i Kubernetes API. Ar gyfer yr achos hwn, mae'r gweithrediad targed wedi dewis defnyddio Apache Livy fel API REST ar gyfer rhedeg tasgau Spark a gynhelir y tu mewn i glwstwr Kubernetes. Gyda'i help, gallwch chi redeg tasgau Spark ar glwstwr Kubernetes gan ddefnyddio ceisiadau cURL rheolaidd, sy'n cael eu gweithredu'n hawdd yn seiliedig ar unrhyw ddatrysiad CI, ac mae ei leoliad y tu mewn i glwstwr Kubernetes yn datrys mater dilysu wrth ryngweithio ag API Kubernetes.

Rhedeg Apache Spark ar Kubernetes

Gadewch i ni ei amlygu fel achos ail ddefnydd - rhedeg tasgau Spark fel rhan o broses CI / CD ar glwstwr Kubernetes mewn dolen brawf.

Ychydig am Apache Livy - mae'n gweithio fel gweinydd HTTP sy'n darparu rhyngwyneb Gwe ac API RESTful sy'n eich galluogi i lansio spark-submit o bell trwy basio'r paramedrau angenrheidiol. Yn draddodiadol mae wedi'i gludo fel rhan o ddosbarthiad HDP, ond gellir ei ddefnyddio hefyd i OKD neu unrhyw osodiad Kubernetes arall gan ddefnyddio'r maniffest priodol a set o ddelweddau Docker, fel yr un hwn - github.com/ttauveron/k8s-big-data-experiments/tree/master/livy-spark-2.3. Yn ein hachos ni, adeiladwyd delwedd Docker debyg, gan gynnwys fersiwn Spark 2.4.5 o'r Dockerfile canlynol:

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

Gellir adeiladu'r ddelwedd a gynhyrchir a'i huwchlwytho i'ch storfa Docker bresennol, megis ystorfa fewnol OKD. I'w ddefnyddio, defnyddiwch y maniffest canlynol ({registry-url} - URL cofrestrfa ddelweddau'r Docker, {delwedd-enw} - Enw delwedd y Docker, {tag} - Tag delwedd Docker, {livy-url} - URL dymunol lle mae'r bydd y gweinydd yn hygyrch Livy; defnyddir y maniffest “Route” os defnyddir Red Hat OpenShift fel dosbarthiad Kubernetes, fel arall defnyddir y maniffest Ingress neu Service cyfatebol o'r math NodePort):

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

Ar ôl ei gymhwyso a lansio'r pod yn llwyddiannus, mae rhyngwyneb graffigol Livy ar gael yn y ddolen: http://{livy-url}/ui. Gyda Livy, gallwn gyhoeddi ein tasg Spark gan ddefnyddio cais REST gan, er enghraifft, Postman. Cyflwynir enghraifft o gasgliad gyda cheisiadau isod (gellir pasio dadleuon ffurfweddu gyda newidynnau sy'n angenrheidiol ar gyfer gweithrediad y dasg a lansiwyd yn yr arae “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": {}
}

Gadewch i ni weithredu'r cais cyntaf o'r casgliad, ewch i'r rhyngwyneb OKD a gwirio bod y dasg wedi'i lansio'n llwyddiannus - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods. Ar yr un pryd, bydd sesiwn yn ymddangos yn y rhyngwyneb Livy (http://{livy-url}/ui), ac o fewn y rhain, gan ddefnyddio'r API Livy neu'r rhyngwyneb graffigol, gallwch olrhain cynnydd y dasg ac astudio'r sesiwn boncyffion.

Nawr gadewch i ni ddangos sut mae Livy yn gweithio. I wneud hyn, gadewch i ni archwilio logiau'r cynhwysydd Livy y tu mewn i'r pod gyda'r gweinydd Livy - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods/{livy-pod-name }?tab=logiau. Oddyn nhw gallwn weld, wrth alw'r Livy REST API mewn cynhwysydd o'r enw “livy”, bod cyflwyniad gwreichionen yn cael ei weithredu, yn debyg i'r un a ddefnyddiwyd gennym uchod (yma {livy-pod-name} yw enw'r pod a grëwyd gyda gweinydd Livy). Mae'r casgliad hefyd yn cyflwyno ail ymholiad sy'n eich galluogi i redeg tasgau sy'n cynnal gweithred Spark o bell gan ddefnyddio gweinydd Livy.

Achos trydydd defnydd - Spark Operator

Nawr bod y dasg wedi'i phrofi, mae'r cwestiwn o'i redeg yn codi'n rheolaidd. Y ffordd frodorol i redeg tasgau yn rheolaidd mewn clwstwr Kubernetes yw endid CronJob a gallwch ei ddefnyddio, ond ar hyn o bryd mae'r defnydd o weithredwyr i reoli ceisiadau yn Kubernetes yn boblogaidd iawn ac ar gyfer Spark mae gweithredwr eithaf aeddfed, sydd hefyd yn a ddefnyddir mewn datrysiadau lefel Menter (er enghraifft, Lightbend FastData Platform). Rydym yn argymell ei ddefnyddio - mae gan y fersiwn sefydlog gyfredol o Spark (2.4.5) opsiynau cyfluniad eithaf cyfyngedig ar gyfer rhedeg tasgau Spark yn Kubernetes, tra bod y fersiwn fawr nesaf (3.0.0) yn datgan cefnogaeth lawn i Kubernetes, ond mae ei ddyddiad rhyddhau yn parhau i fod yn anhysbys . Mae Spark Operator yn gwneud iawn am y diffyg hwn trwy ychwanegu opsiynau ffurfweddu pwysig (er enghraifft, gosod ConfigMap gyda chyfluniad mynediad Hadoop i godiau Spark) a'r gallu i redeg tasg a drefnwyd yn rheolaidd.

Rhedeg Apache Spark ar Kubernetes
Gadewch i ni ei amlygu fel trydydd achos defnydd - yn rhedeg tasgau Spark yn rheolaidd ar glwstwr Kubernetes mewn dolen gynhyrchu.

Mae Spark Operator yn ffynhonnell agored ac wedi'i ddatblygu o fewn y Google Cloud Platform - github.com/GoogleCloudPlatform/spark-on-k8s-operator. Gellir ei osod mewn 3 ffordd:

  1. Fel rhan o'r gosodiad Lightbend FastData Platform/Cloudflow;
  2. Defnyddio Helm:
    helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
    helm install incubator/sparkoperator --namespace spark-operator
    	

  3. Gan ddefnyddio maniffestau o'r ystorfa swyddogol (https://github.com/GoogleCloudPlatform/spark-on-k8s-operator/tree/master/manifest). Mae'n werth nodi'r canlynol - mae Cloudflow yn cynnwys gweithredwr gyda fersiwn API v1beta1. Os defnyddir y math hwn o osodiad, dylai disgrifiadau maniffest cais Spark fod yn seiliedig ar dagiau enghreifftiol yn Git gyda'r fersiwn API priodol, er enghraifft, "v1beta1-0.9.0-2.4.0". Gellir dod o hyd i fersiwn y gweithredwr yn y disgrifiad o'r CRD sydd wedi'i gynnwys yn y gweithredwr yn y geiriadur “fersiynau”:
    oc get crd sparkapplications.sparkoperator.k8s.io -o yaml
    	

Os yw'r gweithredwr wedi'i osod yn gywir, bydd pod gweithredol gyda'r gweithredwr Spark yn ymddangos yn y prosiect cyfatebol (er enghraifft, cloudflow-fdp-sparkoperator yn y gofod Cloudflow ar gyfer gosodiad Cloudflow) a bydd math o adnodd Kubernetes cyfatebol o'r enw “sparkapplications” yn ymddangos . Gallwch archwilio cymwysiadau Spark sydd ar gael gyda'r gorchymyn canlynol:

oc get sparkapplications -n {project}

I redeg tasgau gan ddefnyddio Spark Operator mae angen i chi wneud 3 pheth:

  • creu delwedd Docker sy'n cynnwys yr holl lyfrgelloedd angenrheidiol, yn ogystal â ffurfweddu a ffeiliau gweithredadwy. Yn y llun targed, dyma ddelwedd a grëwyd ar y cam CI/CD ac a brofwyd ar glwstwr prawf;
  • cyhoeddi delwedd Docker i gofrestrfa sy'n hygyrch o glwstwr Kubernetes;
  • cynhyrchu maniffest gyda'r math “SparkApplication” a disgrifiad o'r dasg i'w lansio. Mae maniffestau enghreifftiol ar gael yn y gadwrfa swyddogol (e.e. github.com/GoogleCloudPlatform/spark-on-k8s-operator/blob/v1beta1-0.9.0-2.4.0/examples/spark-pi.yaml). Mae pwyntiau pwysig i’w nodi am y maniffesto:
    1. rhaid i'r geiriadur “apiVersion” nodi'r fersiwn API sy'n cyfateb i fersiwn y gweithredwr;
    2. rhaid i'r geiriadur “metadata.namespace” nodi'r gofod enwau y caiff y rhaglen ei lansio ynddo;
    3. rhaid i'r geiriadur “spec.image” gynnwys cyfeiriad y ddelwedd Docker a grëwyd mewn cofrestrfa hygyrch;
    4. rhaid i'r geiriadur “spec.mainClass” gynnwys y dosbarth tasg Spark sydd angen ei redeg pan fydd y broses yn dechrau;
    5. rhaid i'r geiriadur “spec.mainApplicationFile” gynnwys y llwybr i'r ffeil jar gweithredadwy;
    6. rhaid i'r geiriadur “spec.sparkVersion” nodi'r fersiwn o Spark a ddefnyddir;
    7. rhaid i'r geiriadur “spec.driver.serviceAccount” nodi'r cyfrif gwasanaeth o fewn gofod enw cyfatebol Kubernetes a ddefnyddir i redeg y rhaglen;
    8. rhaid i'r geiriadur “spec.executor” nodi nifer yr adnoddau a ddyrannwyd i'r cais;
    9. rhaid i'r geiriadur "spec.volumeMounts" nodi'r cyfeiriadur lleol y bydd y ffeiliau tasg Spark lleol yn cael eu creu ynddo.

Enghraifft o gynhyrchu maniffest (yma {spark-service-account} yw cyfrif gwasanaeth y tu mewn i glwstwr Kubernetes ar gyfer rhedeg tasgau Spark):

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"

Mae'r maniffest hwn yn nodi cyfrif gwasanaeth y mae'n rhaid i chi, cyn cyhoeddi'r maniffest, greu'r rhwymiadau rôl angenrheidiol sy'n darparu'r hawliau mynediad angenrheidiol i raglen Spark ryngweithio ag API Kubernetes (os oes angen). Yn ein hachos ni, mae angen hawliau ar y cais i greu Podiau. Gadewch i ni greu'r rhwymiad rôl angenrheidiol:

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

Mae'n werth nodi hefyd y gall y fanyleb maniffest hon gynnwys paramedr "hadoopConfigMap", sy'n eich galluogi i nodi ConfigMap gyda chyfluniad Hadoop heb orfod gosod y ffeil gyfatebol yn y ddelwedd Docker yn gyntaf. Mae hefyd yn addas ar gyfer rhedeg tasgau'n rheolaidd - gan ddefnyddio'r paramedr “amserlen”, gellir pennu amserlen ar gyfer rhedeg tasg benodol.

Ar ôl hynny, rydyn ni'n cadw ein maniffest i'r ffeil spark-pi.yaml a'i gymhwyso i'n clwstwr Kubernetes:

oc apply -f spark-pi.yaml

Bydd hyn yn creu gwrthrych o'r math “sparkapplications”:

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

Yn yr achos hwn, bydd pod gyda chais yn cael ei greu, a bydd ei statws yn cael ei arddangos yn y “sparkapplications” a grëwyd. Gallwch ei weld gyda'r gorchymyn canlynol:

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

Ar ôl cwblhau'r dasg, bydd y POD yn symud i'r statws “Wedi'i Gwblhau”, a fydd hefyd yn cael ei ddiweddaru yn “Sparkapplications”. Gellir gweld logiau cais yn y porwr neu ddefnyddio'r gorchymyn canlynol (dyma {sparkapplications-pod-name} yw enw pod y dasg redeg):

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

Gellir rheoli tasgau gwreichionen hefyd gan ddefnyddio'r cyfleustodau sparkctl arbenigol. Er mwyn ei osod, cloniwch yr ystorfa gyda'i chod ffynhonnell, gosodwch Go ac adeiladwch y cyfleustodau hwn:

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

Gadewch i ni archwilio'r rhestr o dasgau rhedeg Spark:

sparkctl list -n {project}

Gadewch i ni greu disgrifiad ar gyfer tasg 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"

Gadewch i ni redeg y dasg a ddisgrifir gan ddefnyddio sparkctl:

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

Gadewch i ni archwilio'r rhestr o dasgau rhedeg Spark:

sparkctl list -n {project}

Gadewch i ni archwilio'r rhestr o ddigwyddiadau tasg Spark a lansiwyd:

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

Gadewch i ni archwilio statws y dasg rhedeg Spark:

sparkctl status spark-pi -n {project}

I gloi, hoffwn ystyried yr anfanteision a ddarganfuwyd o ddefnyddio'r fersiwn sefydlog gyfredol o Spark (2.4.5) yn Kubernetes:

  1. Yr anfantais gyntaf, ac efallai, y brif anfantais yw diffyg Ardal Ddata. Er gwaethaf holl ddiffygion YARN, roedd manteision hefyd i'w ddefnyddio, er enghraifft, yr egwyddor o gyflwyno cod i ddata (yn hytrach na data i god). Diolch iddo, cyflawnwyd tasgau Spark ar y nodau lle lleolwyd y data a oedd yn gysylltiedig â'r cyfrifiadau, a gostyngwyd yr amser a gymerodd i gyflwyno data dros y rhwydwaith yn sylweddol. Wrth ddefnyddio Kubernetes, rydym yn wynebu'r angen i symud data sy'n ymwneud â thasg ar draws y rhwydwaith. Os ydyn nhw'n ddigon mawr, gall yr amser cyflawni tasgau gynyddu'n sylweddol, a hefyd angen llawer iawn o le ar y ddisg wedi'i ddyrannu i achosion tasg Spark ar gyfer eu storio dros dro. Gellir lliniaru'r anfantais hon trwy ddefnyddio meddalwedd arbenigol sy'n sicrhau lleoliad data yn Kubernetes (er enghraifft, Alluxio), ond mae hyn mewn gwirionedd yn golygu'r angen i storio copi cyflawn o'r data ar nodau clwstwr Kubernetes.
  2. Yr ail anfantais bwysig yw diogelwch. Yn ddiofyn, mae nodweddion diogelwch sy'n ymwneud â rhedeg tasgau Spark wedi'u hanalluogi, nid yw'r defnydd o Kerberos wedi'i gynnwys yn y ddogfennaeth swyddogol (er bod yr opsiynau cyfatebol wedi'u cyflwyno yn fersiwn 3.0.0, a fydd yn gofyn am waith ychwanegol), a'r ddogfennaeth diogelwch ar gyfer gan ddefnyddio Spark ( https://spark.apache.org/docs/2.4.5/security.html ) dim ond YARN, Mesos a Standalone Cluster sy'n ymddangos fel storfeydd allweddol. Ar yr un pryd, ni ellir nodi'r defnyddiwr y mae tasgau Spark yn cael ei lansio oddi tano yn uniongyrchol - dim ond y cyfrif gwasanaeth y bydd yn gweithio oddi tano y byddwn yn ei nodi, a dewisir y defnyddiwr yn seiliedig ar y polisïau diogelwch wedi'u ffurfweddu. Yn hyn o beth, naill ai defnyddir y defnyddiwr gwraidd, nad yw'n ddiogel mewn amgylchedd cynhyrchiol, neu ddefnyddiwr â UID ar hap, sy'n anghyfleus wrth ddosbarthu hawliau mynediad i ddata (gellir datrys hyn trwy greu PodSecurityPolicies a'u cysylltu â'r cyfrifon gwasanaeth cyfatebol). Ar hyn o bryd, yr ateb yw naill ai gosod yr holl ffeiliau angenrheidiol yn uniongyrchol i ddelwedd y Docker, neu addasu sgript lansio Spark i ddefnyddio'r mecanwaith ar gyfer storio ac adalw cyfrinachau a fabwysiadwyd yn eich sefydliad.
  3. Mae rhedeg swyddi Spark gan ddefnyddio Kubernetes yn dal yn swyddogol yn y modd arbrofol ac efallai y bydd newidiadau sylweddol yn yr arteffactau a ddefnyddir (ffeiliau ffurfweddu, delweddau sylfaen Docker, a sgriptiau lansio) yn y dyfodol. Ac yn wir, wrth baratoi'r deunydd, profwyd fersiynau 2.3.0 a 2.4.5, roedd yr ymddygiad yn sylweddol wahanol.

Gadewch i ni aros am ddiweddariadau - rhyddhawyd fersiwn newydd o Spark (3.0.0) yn ddiweddar, a ddaeth â newidiadau sylweddol i waith Spark on Kubernetes, ond cadwodd y statws arbrofol o gefnogaeth i'r rheolwr adnoddau hwn. Efallai y bydd y diweddariadau nesaf yn ei gwneud hi'n bosibl argymell yn llawn rhoi'r gorau i YARN a rhedeg tasgau Spark ar Kubernetes heb ofni diogelwch eich system a heb yr angen i addasu cydrannau swyddogaethol yn annibynnol.

Diwedd

Ffynhonnell: hab.com

Ychwanegu sylw