Keyrir Apache Spark á Kubernetes

Kæru lesendur, góðan daginn. Í dag munum við tala aðeins um Apache Spark og þróunarhorfur þess.

Keyrir Apache Spark á Kubernetes

Í nútíma heimi Big Data er Apache Spark í raun staðallinn til að þróa hópgagnavinnsluverkefni. Að auki er það einnig notað til að búa til streymisforrit sem vinna í örlotuhugmyndinni, vinnslu og sendingu gagna í litlum skömmtum (Spark Structured Streaming). Og venjulega hefur það verið hluti af heildar Hadoop staflanum, með því að nota YARN (eða í sumum tilfellum Apache Mesos) sem auðlindastjóra. Árið 2020 er notkun þess í sinni hefðbundnu mynd í vafa hjá flestum fyrirtækjum vegna skorts á almennilegum Hadoop dreifingum - þróun HDP og CDH hefur stöðvast, CDH er ekki vel þróað og hefur mikinn kostnað í för með sér og þeir Hadoop birgjar sem eftir eru hafa annaðhvort hætt að vera til eða eiga sér litla framtíð. Þess vegna hefur kynning á Apache Spark með Kubernetes vaxandi áhuga meðal samfélagsins og stórra fyrirtækja - þar sem hún er orðin staðall í gámaskipan og auðlindastjórnun í einkaskýjum og opinberum skýjum, það leysir vandamálið með óþægilegri auðlindaáætlun Spark verkefna á YARN og veitir vettvangur í stöðugri þróun með mörgum viðskiptalegum og opnum dreifingum fyrir fyrirtæki af öllum stærðum og röndum. Að auki, í kjölfar vinsælda, hefur flestum þegar tekist að eignast nokkrar eigin uppsetningar og aukið sérþekkingu sína á notkun þess, sem einfaldar flutninginn.

Frá og með útgáfu 2.3.0 fékk Apache Spark opinberan stuðning við að keyra verkefni í Kubernetes klasa og í dag munum við tala um núverandi þroska þessarar nálgunar, ýmsa möguleika til notkunar hennar og gildrur sem munu koma upp við innleiðingu.

Í fyrsta lagi skulum við skoða ferlið við að þróa verkefni og forrit byggð á Apache Spark og draga fram dæmigerð tilvik þar sem þú þarft að keyra verkefni á Kubernetes klasa. Við undirbúning þessarar færslu er OpenShift notað sem dreifing og skipanir sem tengjast skipanalínubúnaði þess (oc) verða gefnar. Fyrir aðrar Kubernetes dreifingar er hægt að nota samsvarandi skipanir frá venjulegu Kubernetes skipanalínuforritinu (kubectl) eða hliðstæður þeirra (til dæmis fyrir oc adm stefnu).

Fyrsta notkunartilfelli - neisti-senda

Við þróun verkefna og forrita þarf verktaki að keyra verkefni til að kemba gagnaumbreytingu. Fræðilega séð er hægt að nota stubba í þessum tilgangi, en þróun með þátttöku raunverulegra (að vísu prófunar) tilvika endakerfa hefur reynst hraðari og betri í þessum flokki verkefna. Í því tilviki þegar við kemba í raunverulegum tilvikum lokakerfa eru tvær aðstæður mögulegar:

  • verktaki keyrir Spark verkefni á staðnum í sjálfstæðum ham;

    Keyrir Apache Spark á Kubernetes

  • þróunaraðili keyrir Spark verkefni á Kubernetes klasa í prófunarlykkju.

    Keyrir Apache Spark á Kubernetes

Fyrsti kosturinn hefur tilverurétt en hefur í för með sér ýmsa ókosti:

  • Sérhver þróunaraðili verður að fá aðgang frá vinnustað að öllum tilfellum þeirra endakerfa sem hann þarfnast;
  • nægilegt magn af fjármagni þarf á vinnuvélinni til að keyra verkefnið sem verið er að þróa.

Annar valmöguleikinn hefur ekki þessa ókosti, þar sem notkun Kubernetes klasa gerir þér kleift að úthluta nauðsynlegum tilföngum til að keyra verkefni og veita honum nauðsynlegan aðgang að lokakerfistilvikum, með sveigjanlegan aðgang að honum með því að nota Kubernetes fyrirmyndina fyrir allir meðlimir þróunarteymisins. Við skulum varpa ljósi á það sem fyrsta notkunartilvikið - að ræsa Spark verkefni frá staðbundinni þróunarvél á Kubernetes þyrping í prófunarlykkju.

Við skulum tala meira um ferlið við að setja upp Spark til að keyra á staðnum. Til að byrja að nota Spark þarftu að setja það upp:

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

Við söfnum nauðsynlegum pakka til að vinna með Kubernetes:

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

Full bygging tekur mikinn tíma og til að búa til Docker myndir og keyra þær á Kubernetes klasa þarftu í raun aðeins jar skrár úr „assembly/“ möppunni, svo þú getur aðeins smíðað þetta undirverkefni:

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

Til að keyra Spark störf á Kubernetes þarftu að búa til Docker mynd til að nota sem grunnmynd. Það eru 2 mögulegar aðferðir hér:

  • Mynduð Docker myndin inniheldur keyranlega Spark verkefnakóðann;
  • Myndin sem er búin til inniheldur aðeins Spark og nauðsynlegar ósjálfstæði, keyranlega kóðinn er hýst lítillega (til dæmis í HDFS).

Fyrst skulum við byggja Docker mynd sem inniheldur prófunardæmi um Spark verkefni. Til að búa til Docker myndir hefur Spark tól sem kallast "docker-image-tool". Við skulum rannsaka hjálpina á því:

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

Með hjálp þess geturðu búið til Docker myndir og hlaðið þeim upp í fjarskrár, en sjálfgefið hefur það ýmsa ókosti:

  • án þess að mistakast býr til 3 Docker myndir í einu - fyrir Spark, PySpark og R;
  • leyfir þér ekki að tilgreina nafn myndar.

Þess vegna munum við nota breytta útgáfu af þessu tóli sem gefið er upp hér að neðan:

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

Með hjálp þess setjum við saman grunn Spark mynd sem inniheldur prófunarverkefni til að reikna út Pi með því að nota Spark (hér er {docker-registry-url} slóð Docker myndaskrárinnar þinnar, {repo} er heiti geymslunnar inni í skránni, sem passar við verkefnið í OpenShift , {image-name} - nafn myndarinnar (ef þriggja stiga aðskilnaður mynda er notaður, t.d. eins og í samþættri skráningu Red Hat OpenShift mynda), {tag} - merki þessa útgáfa af myndinni):

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

Skráðu þig inn á OKD þyrpinguna með því að nota stjórnborðsforritið (hér er {OKD-API-URL} OKD þyrping API URL):

oc login {OKD-API-URL}

Við skulum fá auðkenni núverandi notanda fyrir heimild í Docker Registry:

oc whoami -t

Skráðu þig inn á innri Docker Registry OKD klasans (við notum táknið sem fæst með fyrri skipuninni sem lykilorð):

docker login {docker-registry-url}

Við skulum hlaða upp samsettu Docker myndinni í Docker Registry OKD:

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

Við skulum athuga hvort samsetta myndin sé fáanleg í OKD. Til að gera þetta, opnaðu slóðina í vafranum með lista yfir myndir af samsvarandi verkefni (hér er {project} heiti verkefnisins inni í OpenShift klasanum, {OKD-WEBUI-URL} er slóð OpenShift vefborðsins ) - https://{OKD-WEBUI-URL}/console /project/{project}/browse/images/{image-name}.

Til að keyra verkefni verður að búa til þjónustureikning með réttindi til að keyra belg sem rót (við munum ræða þetta atriði síðar):

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

Við skulum keyra spark-submit skipunina til að birta Spark verkefni í OKD klasanum, tilgreina stofnaðan þjónustureikning og Docker mynd:

 /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

Hér:

—nafn — heiti verkefnisins sem mun taka þátt í myndun nafns Kubernetes fræbelganna;

—class — class of the executable file, kallaður þegar verkefnið byrjar;

—conf — Spark stillingarfæribreytur;

spark.executor.instances — fjöldi Spark framkvæmdastjóra til að ræsa;

spark.kubernetes.authenticate.driver.serviceAccountName - heiti Kubernetes þjónustureikningsins sem notaður er þegar hleðslur eru ræstar (til að skilgreina öryggissamhengi og möguleika þegar samskipti eru við Kubernetes API);

spark.kubernetes.namespace — Kubernetes nafnrými þar sem ökumanns- og framkvæmdahópar verða ræstir;

spark.submit.deployMode — aðferð til að ræsa Spark (fyrir venjulegan neistasending er „þyrping“ notuð, fyrir Spark Operator og síðari útgáfur af „viðskiptavini“ Spark);

spark.kubernetes.container.image - Docker mynd notuð til að ræsa belg;

spark.master — Kubernetes API vefslóð (ytri er tilgreind svo aðgangur eigi sér stað frá staðbundinni vél);

local:// er slóðin að Spark executable inni í Docker myndinni.

Við förum í samsvarandi OKD verkefni og skoðum hólf sem búið var til - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods.

Til að einfalda þróunarferlið er hægt að nota annan valmöguleika, þar sem sameiginleg grunnmynd af Spark er búin til, notuð af öllum verkefnum til að keyra og skyndimyndir af keyranlegum skrám eru birtar á ytri geymslu (til dæmis Hadoop) og tilgreindar þegar hringt er í spark-submit sem tengil. Í þessu tilviki geturðu keyrt mismunandi útgáfur af Spark verkefnum án þess að endurbyggja Docker myndir, nota til dæmis WebHDFS til að birta myndir. Við sendum beiðni um að búa til skrá (hér er {host} gestgjafi WebHDFS þjónustunnar, {port} er höfn WebHDFS þjónustunnar, {path-to-file-on-hdfs} er æskileg slóð að skránni á HDFS):

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

Þú færð svar eins og þetta (hér er {location} vefslóðin sem þarf að nota til að hlaða niður skránni):

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

Hladdu Spark keyrsluskránni inn í HDFS (hér er {path-to-local-file} slóðin að Spark keyrsluskránni á núverandi hýsil):

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

Eftir þetta getum við gert spark-submit með því að nota Spark skrána sem hlaðið var upp á HDFS (hér er {class-name} nafnið á bekknum sem þarf að ræsa til að klára verkefnið):

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

Það skal tekið fram að til að fá aðgang að HDFS og tryggja að verkefnið virki gætirðu þurft að breyta Dockerfile og entrypoint.sh forskriftinni - bæta tilskipun við Dockerfile um að afrita háð söfn í /opt/spark/jars möppuna og innihalda HDFS stillingarskrána í SPARK_CLASSPATH í inngangspunkt. sh.

Önnur notkunarhylki - Apache Livy

Ennfremur, þegar verkefni er þróað og niðurstöðuna þarf að prófa, vaknar spurningin um að ræsa það sem hluta af CI/CD ferlinu og fylgjast með stöðu framkvæmdar þess. Auðvitað geturðu keyrt það með því að nota staðbundið neistakall, en þetta flækir CI/CD innviðina þar sem það krefst þess að Spark sé sett upp og stillt á CI miðlara/hlaupara og að setja upp aðgang að Kubernetes API. Í þessu tilviki hefur markútfærslan valið að nota Apache Livy sem REST API til að keyra Spark verkefni sem hýst eru inni í Kubernetes klasa. Með hjálp þess geturðu keyrt Spark verkefni á Kubernetes klasa með því að nota venjulegar cURL beiðnir, sem auðvelt er að útfæra út frá hvaða CI lausn sem er, og staðsetning hans inni í Kubernetes klasanum leysir vandamálið um auðkenningu þegar samskipti við Kubernetes API eru.

Keyrir Apache Spark á Kubernetes

Við skulum varpa ljósi á það sem annað notkunartilvik - keyra Spark verkefni sem hluta af CI/CD ferli á Kubernetes klasa í prófunarlykkju.

Smá um Apache Livy - það virkar sem HTTP þjónn sem veitir vefviðmót og RESTful API sem gerir þér kleift að ræsa spark-submit með því að senda nauðsynlegar breytur. Venjulega hefur það verið sent sem hluti af HDP dreifingu, en einnig er hægt að dreifa því á OKD eða hvaða aðra Kubernetes uppsetningu sem er með því að nota viðeigandi upplýsingaskrá og sett af Docker myndum, eins og þessari - github.com/ttauveron/k8s-big-data-experiments/tree/master/livy-spark-2.3. Fyrir okkar tilvik var svipuð Docker mynd byggð, þar á meðal Spark útgáfu 2.4.5 úr eftirfarandi 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"]

Hægt er að búa til myndina og hlaða henni upp í núverandi Docker geymslu, svo sem innri OKD geymslu. Til að dreifa því skaltu nota eftirfarandi upplýsingaskrá ({registry-url} - vefslóð Docker myndskrárinnar, {image-name} - Docker myndheiti, {tag} - Docker myndmerki, {livy-url} - viðkomandi vefslóð þar sem miðlarinn verður aðgengilegur Livy; „Route“ upplýsingaskráin er notuð ef Red Hat OpenShift er notað sem Kubernetes dreifing, annars er samsvarandi Ingress eða Service upplýsingaskrá af gerðinni NodePort notuð):

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

Eftir að hafa notað það og ræst hólfið með góðum árangri, er Livy grafíska viðmótið fáanlegt á hlekknum: http://{livy-url}/ui. Með Livy getum við birt Spark verkefnið okkar með því að nota REST beiðni frá til dæmis Postman. Dæmi um söfnun með beiðnum er kynnt hér að neðan (stillingarrök með breytum sem nauðsynlegar eru fyrir rekstur verkefnisins sem var hleypt af stokkunum er hægt að senda í „args“ fylkinu):

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

Við skulum framkvæma fyrstu beiðnina úr safninu, fara í OKD viðmótið og athuga hvort verkefnið hafi verið ræst með góðum árangri - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods. Á sama tíma mun lota birtast í Livy viðmótinu (http://{livy-url}/ui), þar sem þú getur fylgst með framvindu verkefnisins með því að nota Livy API eða grafíska viðmótið og rannsakað lotuna logs.

Nú skulum við sýna hvernig Livy virkar. Til að gera þetta skulum við skoða annála Livy gámsins inni í hólfinu með Livy þjóninum - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods/{livy-pod-name }?tab=logs. Af þeim getum við séð að þegar hringt er í Livy REST API í íláti sem heitir „livy“ er keyrð neistasending, svipað og við notuðum hér að ofan (hér er {livy-pod-name} nafnið á hólfinu sem búið var til með Livy þjóninum). Safnið kynnir einnig aðra fyrirspurn sem gerir þér kleift að keyra verkefni sem fjarstýra Spark keyrslu með Livy netþjóni.

Þriðja notkunarmálið - Spark Operator

Nú þegar verkefnið hefur verið prófað, vaknar spurningin um að keyra það reglulega. Innfædda leiðin til að keyra verkefni reglulega í Kubernetes klasa er CronJob einingin og þú getur notað hann, en í augnablikinu er notkun rekstraraðila til að stjórna forritum í Kubernetes mjög vinsæl og fyrir Spark er nokkuð þroskaður rekstraraðili, sem er líka notað í lausnum á fyrirtækisstigi (til dæmis Lightbend FastData Platform). Við mælum með því að nota það - núverandi stöðuga útgáfa af Spark (2.4.5) hefur frekar takmarkaða stillingarvalkosti til að keyra Spark verkefni í Kubernetes, en næsta aðalútgáfa (3.0.0) lýsir yfir fullum stuðningi við Kubernetes, en útgáfudagur hennar er enn óþekktur . Spark Operator bætir upp fyrir þennan galla með því að bæta við mikilvægum stillingarvalkostum (til dæmis að setja upp ConfigMap með Hadoop aðgangsstillingum á Spark pods) og getu til að keyra reglulega skipulagt verkefni.

Keyrir Apache Spark á Kubernetes
Við skulum varpa ljósi á það sem þriðja notkunartilvik - keyra reglulega Spark verkefni á Kubernetes klasa í framleiðslulykkju.

Spark Operator er opinn uppspretta og þróaður innan Google Cloud Platform - github.com/GoogleCloudPlatform/spark-on-k8s-operator. Uppsetning þess er hægt að gera á 3 vegu:

  1. Sem hluti af Lightbend FastData Platform/Cloudflow uppsetningunni;
  2. Að nota Helm:
    helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
    helm install incubator/sparkoperator --namespace spark-operator
    	

  3. Notkun upplýsingaskráa frá opinberu geymslunni (https://github.com/GoogleCloudPlatform/spark-on-k8s-operator/tree/master/manifest). Þess má geta að eftirfarandi - Cloudflow inniheldur rekstraraðila með API útgáfu v1beta1. Ef þessi tegund af uppsetningu er notuð, ættu lýsingar Spark forritsins að byggjast á dæmum í Git með viðeigandi API útgáfu, til dæmis "v1beta1-0.9.0-2.4.0". Rekstrarútgáfuna er að finna í lýsingu á CRD sem fylgir símafyrirtækinu í orðabókinni „útgáfur“:
    oc get crd sparkapplications.sparkoperator.k8s.io -o yaml
    	

Ef stjórnandinn er rétt settur upp mun virkur hólf með Spark rekstraraðila birtast í samsvarandi verkefni (til dæmis cloudflow-fdp-sparkoperator í Cloudflow rýminu fyrir Cloudflow uppsetninguna) og samsvarandi Kubernetes tilfangategund sem heitir „sparkapplications“ mun birtast . Þú getur skoðað tiltæk Spark forrit með eftirfarandi skipun:

oc get sparkapplications -n {project}

Til að keyra verkefni með Spark Operator þarftu að gera 3 hluti:

  • búa til Docker mynd sem inniheldur öll nauðsynleg bókasöfn, svo og stillingar og keyranlegar skrár. Í markmyndinni er þetta mynd sem er búin til á CI/CD stigi og prófuð á prófþyrping;
  • birta Docker mynd í skrá sem er aðgengileg frá Kubernetes klasanum;
  • búa til upplýsingaskrá með gerðinni „SparkApplication“ og lýsingu á verkefninu sem á að ræsa. Dæmi um upplýsingaskrár eru fáanlegar í opinberu geymslunni (t.d. github.com/GoogleCloudPlatform/spark-on-k8s-operator/blob/v1beta1-0.9.0-2.4.0/examples/spark-pi.yaml). Það eru mikilvæg atriði sem þarf að hafa í huga varðandi stefnuskrána:
    1. „apiVersion“ orðabókin verður að tilgreina API útgáfuna sem samsvarar útgáfu rekstraraðila;
    2. Orðabókin „metadata.namespace“ verður að gefa til kynna nafnrýmið þar sem forritið verður ræst;
    3. „spec.image“ orðabókin verður að innihalda heimilisfangið á Docker myndinni sem búið var til í aðgengilegri skráningu;
    4. „spec.mainClass“ orðabókin verður að innihalda Spark verkefnaflokkinn sem þarf að keyra þegar ferlið hefst;
    5. "spec.mainApplicationFile" orðabókin verður að innihalda slóðina að keyrslu jar skránni;
    6. „spec.sparkVersion“ orðabókin verður að gefa til kynna hvaða útgáfu af Spark er verið að nota;
    7. „spec.driver.serviceAccount“ orðabókin verður að tilgreina þjónustureikninginn innan samsvarandi Kubernetes nafnrýmis sem verður notað til að keyra forritið;
    8. Orðabókin „spec.executor“ verður að gefa til kynna fjölda auðlinda sem umsókninni er úthlutað;
    9. "spec.volumeMounts" orðabókin verður að tilgreina staðbundna möppuna þar sem staðbundnar Spark verkefnaskrárnar verða búnar til.

Dæmi um að búa til upplýsingaskrá (hér er {spark-service-account} þjónustureikningur inni í Kubernetes klasanum til að keyra Spark verkefni):

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"

Þessi upplýsingaskrá tilgreinir þjónustureikning sem þú verður að búa til nauðsynlegar hlutverkabindingar fyrir áður en þú birtir upplýsingaskrána sem veita nauðsynlegan aðgangsrétt fyrir Spark forritið til að hafa samskipti við Kubernetes API (ef nauðsyn krefur). Í okkar tilviki þarf forritið réttindi til að búa til fræbelg. Við skulum búa til nauðsynlega hlutverkabindingu:

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

Það er líka athyglisvert að þessi upplýsingaskrá getur innihaldið „hadoopConfigMap“ færibreytu, sem gerir þér kleift að tilgreina ConfigMap með Hadoop stillingunni án þess að þurfa að setja samsvarandi skrá í Docker myndina. Það hentar líka til að keyra verkefni reglulega - með því að nota „áætlun“ færibreytuna er hægt að tilgreina áætlun um að keyra tiltekið verkefni.

Eftir það vistum við upplýsingaskrána okkar í spark-pi.yaml skrána og notum hana á Kubernetes þyrpinguna okkar:

oc apply -f spark-pi.yaml

Þetta mun búa til hlut af gerðinni „sparkapplications“:

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

Í þessu tilviki verður belg með forriti búinn til, staða þess birtist í stofnuðu „sparkapplications“. Þú getur skoðað það með eftirfarandi skipun:

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

Þegar verkefninu er lokið mun POD fara í stöðuna „Lokið“, sem mun einnig uppfæra í „sparkapplications“. Hægt er að skoða forritaskrár í vafranum eða með því að nota eftirfarandi skipun (hér er {sparkapplications-pod-name} nafnið á hólf verkefnisins sem er í gangi):

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

Einnig er hægt að stjórna neistaverkefnum með því að nota sérhæfða sparkctl tólið. Til að setja það upp, klónaðu geymsluna með frumkóðanum, settu upp Go og byggðu þetta tól:

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

Við skulum skoða listann yfir hlaupandi Spark verkefni:

sparkctl list -n {project}

Búum til lýsingu fyrir Spark verkefnið:

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"

Við skulum keyra verkefnið sem lýst er með því að nota sparkctl:

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

Við skulum skoða listann yfir hlaupandi Spark verkefni:

sparkctl list -n {project}

Við skulum skoða listann yfir atburði í hleypt af stokkunum Spark verkefni:

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

Við skulum skoða stöðu Spark verkefnisins sem er í gangi:

sparkctl status spark-pi -n {project}

Að lokum langar mig að íhuga galla þess að nota núverandi stöðugu útgáfu af Spark (2.4.5) í Kubernetes:

  1. Fyrsti og kannski helsti ókosturinn er skortur á gagnastaðsetningu. Þrátt fyrir alla galla YARN voru líka kostir við að nota það, til dæmis meginreglan um að koma kóða til gagna (frekar en gögn til kóða). Þökk sé því voru Spark verkefni framkvæmd á hnútunum þar sem gögnin sem tóku þátt í útreikningunum voru staðsett og tíminn sem það tók að afhenda gögn yfir netið minnkaði verulega. Þegar við notum Kubernetes stöndum við frammi fyrir því að þurfa að flytja gögn sem taka þátt í verkefni yfir netið. Ef þeir eru nógu stórir getur framkvæmdartími verkefna aukist verulega og einnig krafist nokkuð mikið af diskplássi sem úthlutað er til Spark verkefnatilvika fyrir tímabundna geymslu þeirra. Hægt er að draga úr þessum ókosti með því að nota sérhæfðan hugbúnað sem tryggir gagnastaðsetningu í Kubernetes (til dæmis Alluxio), en þetta þýðir í raun að geyma þarf fullkomið afrit af gögnunum á hnútum Kubernetes klasans.
  2. Annar mikilvægi ókosturinn er öryggi. Sjálfgefið er að öryggistengdir eiginleikar varðandi keyrslu Spark verkefni eru óvirkir, notkun Kerberos er ekki fjallað um í opinberu skjölunum (þó samsvarandi valkostir hafi verið kynntir í útgáfu 3.0.0, sem mun krefjast viðbótarvinnu), og öryggisskjölin fyrir með því að nota Spark (https://spark.apache.org/docs/2.4.5/security.html) birtast aðeins YARN, Mesos og Standalone Cluster sem lykilverslanir. Á sama tíma er ekki hægt að tilgreina notandann sem Spark verkefni eru sett undir beint - við tilgreinum aðeins þjónustureikninginn sem hann mun starfa undir og notandinn er valinn út frá stilltum öryggisreglum. Í þessu sambandi er annaðhvort notaður rótarnotandinn, sem er ekki öruggur í framleiðsluumhverfi, eða notandi með handahófskennt UID, sem er óþægilegt við að dreifa aðgangsrétti að gögnum (þetta er hægt að leysa með því að búa til PodSecurityPolicies og tengja þær við samsvarandi þjónustureikninga). Eins og er, er lausnin annaðhvort að setja allar nauðsynlegar skrár beint inn í Docker myndina eða breyta Spark ræsiforritinu til að nota vélbúnaðinn til að geyma og sækja leyndarmál sem eru samþykkt í fyrirtækinu þínu.
  3. Að keyra Spark störf með Kubernetes er opinberlega enn í tilraunaham og það gætu orðið verulegar breytingar á gripunum sem notaðir eru (stillingarskrár, Docker grunnmyndir og ræsiforskriftir) í framtíðinni. Og reyndar, þegar efnið var útbúið, voru útgáfur 2.3.0 og 2.4.5 prófaðar, hegðunin var verulega ólík.

Bíðum eftir uppfærslum - ný útgáfa af Spark (3.0.0) var nýlega gefin út, sem leiddi til umtalsverðar breytingar á starfi Spark á Kubernetes, en hélt tilraunastöðu stuðnings við þennan auðlindastjóra. Kannski munu næstu uppfærslur virkilega gera það mögulegt að mæla með því að yfirgefa YARN og keyra Spark verkefni á Kubernetes án þess að óttast um öryggi kerfisins þíns og án þess að þurfa að breyta virkum íhlutum sjálfstætt.

Endirinn.

Heimild: www.habr.com

Bæta við athugasemd