Lugupeetud lugejad, tere pÀevast. TÀna rÀÀgime natuke Apache Sparkist ja selle arenguvÔimalustest.

Kaasaegses maailmas on Big Data jaoks Apache Spark de facto standard andmete partiide töötlemise ĂŒlesannete arendamisel. Lisaks kasutatakse seda ka voogedastusrakenduste loomiseks, mis töötavad mikropartiide kontseptsioonis, töötledes ja edastades andmeid vĂ€ikeste osade kaupa (Spark Structured Streaming). Traditsiooniliselt on see olnud osa Hadoopi ĂŒldisest tehnoloogiakogumist, kasutades ressursihaldurina YARNi (vĂ”i mĂ”nel juhul Apache Mesosit). Aastaks 2020 on selle traditsioonilise kasutuselevĂ”tt enamikus ettevĂ”tetes suure kĂŒsimĂ€rgi all, kuna puuduvad korralikud Hadoopi jaotised â HDP ja CDH arendamine on peatatud, CDH pole piisavalt vĂ€lja töötatud ja sellel on kĂ”rged kulud, ning teised Hadoopi pakkujad on kas lĂ”petanud oma tegevuse vĂ”i neil on ebamugav tulevik. SeetĂ”ttu on Apache Sparki kĂ€itamine Kuberneteses kogukonna ja suurte ettevĂ”tete seas ĂŒha suuremat huvi pĂ€lvinud â olles standard konteinerite orkestreerimisel ja ressursside haldamisel privaatsetes ja avalikes pilvedes, lahendab see probleemi YARNis Spark'i ĂŒlesannete ressursside haldamise ebamugavuste osas ning pakub stabiilselt arenevat platvormi paljude kommerts- ja avatud jaotistega igas suuruses ja mÀÀraga ettevĂ”tetele. HĂ€sti populaarseks muutumise laine tĂ”ukab enamikku juba omama mitut oma paigaldust ja suurendama oma ekspertteadmisi, mis lihtsustab ĂŒleviimist.
Alates versioonist 2.3.0 on Apache Spark saanud ametliku toe ĂŒlesannete kĂ€ivitamiseks Kubernetes klastris. TĂ€na rÀÀgime selle lĂ€henemise praegusest kĂŒpsusest, erinevatest kasutusvĂ”imalustest ning takistustest, millega tuleb silmitsi seista rakendamisel.
KĂ€esolevalt vaatame Apache Spark'i pĂ”hjal ĂŒlesannete ja rakenduste arendusprotsessi ning toome vĂ€lja tĂŒĂŒpilised juhtumid, kus on vaja kĂ€ivitada ĂŒlesanne Kubernetes klastris. Selles postituses kasutatakse jaotuseks OpenShift'i ja esitatakse kĂ€sklused, mis on kehtivad selle kĂ€surea utiliidi (oc) jaoks. Muude Kubernetes jaotiste puhul vĂ”ib kasutada vastavaid standardse Kubernetes'i kĂ€surea utiliidi (kubectl) kĂ€ske vĂ”i nende analooge (nt oc adm policy).
Esimene kasutusvariant â spark-submit
Arendustegevuste ja rakenduste arendamine nĂ”uab arendajalt andmete transformatsiooni silumise ĂŒlesannete kĂ€ivitamist. Teoreetiliselt vĂ”ivad selleks kasutada pealdisi, kuid reaalses (kuigi testversiooniga) lĂ”pp sĂŒsteemide puhul, on nĂ€idatud, et arendamine kulgeb selles klassis kiiremini ja kvaliteetsemalt. Kui teeme silumist reaalsetes lĂ”pp sĂŒsteemide eksemplarides, on tĂ”enĂ€olised kaks töö stsenaariumi:
- arendaja kĂ€ivitab Spark ĂŒlesande kohapeal standalone reĆŸiimis;

- arendaja kĂ€ivitab Spark ĂŒlesande Kubernetes klastris testkeskkonnas.

Esimene variant on olemas, aga sellega kaasnevad mitmed puudused:
- iga arendaja peab tagama juurdepÀÀsu kĂ”ikidele vajalikutele lĂ”pp sĂŒsteemide eksemplaridele oma töökohalt;
- töökohal peab olema piisavalt ressursse arendatava ĂŒlesande kĂ€ivitamiseks.
Teine variant on vaba andmete puudustest, kuna Kubernetes klastrite kasutamine vĂ”imaldab eraldada vajaliku ressursside basseini ĂŒlesannete kĂ€ivitamiseks ning tagada sellele vajalikud ligipÀÀsud lĂ”pp-sĂŒsteemide instantsidele, paindlikult andes sellele juurdepÀÀsu Kubernetes rollimudeli kaudu kĂ”igile arendustiimi liikmetele. Nimetame seda esimeseks kasutusjuhtumiks â Spark'i ĂŒlesannete kĂ€ivitamine arendaja kohalikult masinal Kubernetes klastris testkontekstis.
RÀÀgime lÀhemalt Spark'i kohaliku kÀivitamise seadistamise protsessist. Spark'i kasutamise alustamiseks tuleb see installida:
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
Kogume Kubernetesega töötamiseks vajalikud paketid:
cd spark-2.4.5/
./build/mvn -Pkubernetes -DskipTests clean package
TÀielik kogumine vÔtab palju aega, ning Docker'i piltide loomine ja nende kÀivitamine Kubernetes klastris vajab tegelikult ainult jar-faile kataloogist 'assembly/', seega saab koguda ainult selle alamprojekti:
./build/mvn -f ./assembly/pom.xml -Pkubernetes -DskipTests clean package
Spark'i kÀivitamiseks Kuberneteses tuleb luua Docker'i pilt, mida kasutatakse alusena. Siin on vÔimalikud kaks lÀhenemist:
- Loodud Docker'i pilt sisaldab Spark'i ĂŒlesande kĂ€ivitatavat koodi;
- Loodud pilt sisaldab ainult Spark'i ja vajalikke sÔltuvusi, kÀivitatav kood paikneb kaugel (nÀiteks HDFS-is).
Alustame Docker'i pildi koostamist, mis sisaldab testjuhtumit Spark'i ĂŒlesandest. Spark'il on Docker'i piltide loomiseks sobiv utiliit nimega «docker-image-tool». Vaadakem selle kohta abi:
./bin/docker-image-tool.sh --help
Selle abil saab luua Docker'i pilte ja laadida neid kaugele registrisse, kuid vaikimisi on sel mitmeid puudusi:
- see loob automaatselt kolm Docker'i pilti â Spark'i, PySpark'i ja R-i jaoks;
- ei luba mÀÀrata pildi nime.
SeetÔttu kasutame allpool esitatud muudetud versiooni sellest utiliidist:
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
Selle abil koostame Spark'i baaspildi, mis sisaldab testĂŒlesannet Pi arvestamiseks Spark'i abil (siin {docker-registry-url} â teie Docker'i piltide registri URL, {repo} â registri sisene nimekiri, mis vastab OpenShifti projektile, {image-name} â pildi nimi (kui kasutatakse kolmekihilist piltide jagamist, nĂ€iteks Red Hat OpenShift'i integreeritud registris), {tag} â selle pildi versiooni silt):
./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
Logime sisse OKD klastrisse konsooli utiliidi abil (siin {OKD-API-URL} â OKD klastri API URL):
oc login {OKD-API-URL}
Saame praeguse kasutaja tokeni Docker Registry autoriseerimiseks:
oc whoami -t
Logime sisse OKD klastri sisemisse Docker Registry'sse (paroolina kasutame eelneva kÀsu kaudu saadud tokenit):
docker login {docker-registry-url}
Laadime ĂŒles koostatud Docker'i pildi OKD Docker Registry'sse:
./bin/docker-image-tool-upd.sh -r {docker-registry-url}/{repo} -i {image-name} -t {tag} push
Kontrollime, et koostatud pilt on OKD-s saadaval. Selleks avame brauseris URL-i, kus on nimetatud vastava projekti pildid (siin {project} on projekti nimi OpenShifti klastris, {OKD-WEBUI-URL} on OpenShifti veebikonsoli URL) â https://{OKD-WEBUI-URL}/console/project/{project}/browse/images/{image-name}.
Ălesannete kĂ€ivitamiseks peab olema loodud teenuse konto, millel on podide root Ă”igused (arutame seda teemat hiljem):
oc create sa spark -n {project}
oc adm policy add-scc-to-user anyuid -z spark -n {project}
KĂ€ivitame kĂ€su spark-submit, et avaldada Spark-i ĂŒlesanne OKD klastris, mĂ€rkides loodud teenuse konto ja Docker pildi:
/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
Siin:
âname â ĂŒlesande nimi, mis osaleb Kubernetes-i podide nimede loomises;
âclass â kĂ€ivitatava faili klass, mis kutsutakse ĂŒlesande kĂ€ivitamisel;
âconf â Spark-i konfiguratsiooniparametrid;
spark.executor.instances â kĂ€ivitatavate Spark-i eksekutorite arv;
spark.kubernetes.authenticate.driver.serviceAccountName â Kubernetes-i teenuse konto nimi, mida kasutatakse podide kĂ€ivitamisel (turbe konteksti mÀÀratlemiseks ja API-ga suhtlemiseks);
spark.kubernetes.namespace â Kubernetes-i nimekiri, kus kĂ€ivitatakse draiveri ja eksekutorite podid;
spark.submit.deployMode â Spark'i kĂ€ivitamisviis (tava spark-submit jaoks kasutatakse "cluster", Spark Operator'i ja uuemate Spark'i versioonide jaoks "client");
spark.kubernetes.container.image â Docker'i pilt, mida kasutatakse podide kĂ€ivitamiseks;
spark.master â Kubernetes'i API URL (mĂ€rgitakse, et vĂ€line juurdepÀÀs toimub kohalikult masinalt);
local:// â tee Spark'i kĂ€ivitatavale failile Docker'i pildis.
Liigume vastavasse OKD projekti ja vaatame loodud podid â https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods.
Arendamisprotsessi lihtsustamiseks vĂ”ib kasutada veel ĂŒhte varianti, kus luuakse ĂŒhine Spark'i pĂ”hifail, mida kĂ”ik ĂŒlesanded kasutavad kĂ€ivitamiseks, ja kĂ€ivitatavate failide sĂŒnkroonid avaldatakse vĂ€lishoidlas (nĂ€iteks Hadoop) ning osutatakse spark-submit'i kutsumisel lingina. Sellisel juhul saab kĂ€ivitada erinevaid Spark'i ĂŒlesande versioone, ilma et oleks vaja Docker'i pilte uuesti koostada, kasutades nĂ€iteks WebHDFS'i piltide avaldamiseks. Saadame pĂ€ringu faili loomise jaoks (siin {host} â WebHDFS teenuse host, {port} â WebHDFS teenuse port, {path-to-file-on-hdfs} â soovitud tee faili HDFS-ile):
curl -i -X PUT "http://{host}:{port}/webhdfs/v1/{path-to-file-on-hdfs}?op=CREATE"
Sel juhul saadakse vastus jĂ€rgmist tĂŒĂŒpi (kus {location} on URL, mida tuleb kasutada faili ĂŒleslaadimiseks):
HTTP/1.1 307 TEMPORARY_REDIRECT
Location: {location}
Content-Length: 0
Laadime Spark'i kÀivitatava faili HDFS-i (kus {path-to-local-file} on kÀivitatava faili teekohane asukoht antud hostis):
curl -i -X PUT -T {path-to-local-file} "{location}"
PĂ€rast seda saame teha spark-submit Spark'i faili kasutades, mis on HDFS-ile laaditud (kus {class-name} on klassi nimi, mida tuleb ĂŒlesande tĂ€itmiseks kĂ€ivitada):
/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}
Siinkohal tuleb mĂ€rkida, et HDFS-i juurde pÀÀsemiseks ja ĂŒlesande tööks vĂ”ib olla vajalik muuta Dockerfile'i ja skripti entrypoint.sh â lisades Dockerfile'isse kĂ€su sĂ”ltuvate teekide kopeerimiseks kausta /opt/spark/jars ja sisaldades HDFS-i konfiguratsiooni faili SPARK_CLASSPATH entrypoint.sh-s.
Teine kasutusvariant â Apache Livy
Kui ĂŒlesanne on vĂ€lja töötatud ja saadud tulemust tuleb testida, tekib kĂŒsimus selle kĂ€itamisest CI/CD protsessis ning selle tĂ€itmise staatuse jĂ€lgimisest. Loomulikult vĂ”ib ĂŒlesande kĂ€ivitada ka kohaliku spark-submit'i kaudu, kuid see muudab CI/CD infrastruktuuri keerukamaks, kuna see nĂ”uab Spark'i installimist ja seadistamist CI serveri agentides/runnerites ning juurdepÀÀsu seadistamist Kubernetes API-le. Antud juhul on sihtrealiseerimises valitud Apache Livy kasutamine REST API-na Spark'i ĂŒlesannete kĂ€itamiseks, mis on paiknenud Kubernetes klastris. Selle abil saab kĂ€ivitada Spark'i ĂŒlesandeid Kubernetes klastris, kasutades tavalisi cURL pĂ€ringuid, mis on hĂ”lpsasti teostatavad iga CI lahenduse alusel, ja selle paiknemine Kubernetes klastri sees lahendab autentimise kĂŒsimuse Kubernetes API-ga suhtlemisel.

VÀÀrib mainimist kui teiseks kasutusvĂ”imaluseks â Spark'i ĂŒlesannete kĂ€itamine CI/CD protsessi raames Kubernetes klastris testimiskeskkonnas.
Veidi Apache Livy kohta â see töötab HTTP serverina, pakkudes veebiliidest ja RESTful API-d, mis vĂ”imaldab kaugel kĂ€ivitada spark-submit, edastades vajalikud parameetrid. Traditsiooniliselt oli see osa HDP jaotusest, kuid seda saab samuti juurutada OKD vĂ”i mis tahes muus Kubernetesi installatsioonis vastava manifesti ja Dockerite komplekti abil, nĂ€iteks seda â . Meie juhtumi jaoks oli loodud sarnane Dockeri image, mis sisaldas Spark 2.4.5 vastavalt jĂ€rgmisele Dockerfile'ile:
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"]
Loodud pilt saab olla kogutud ja ĂŒles laaditud teie olemasolevasse Dockerite registrisse, nĂ€iteks OKD siseregistrisse. Selle kasutamiseks rakendamiseks kasutatakse jĂ€rgmist manifesti ({registry-url} â Dockeri registri URL, {image-name} â Dockeri pildi nimi, {tag} â Dockeri pildi silt, {livy-url} â soovitud URL, mille kaudu Livy serverile juurde pÀÀseb; manifest "Route" rakendatakse juhul, kui Kubernetes'i distributsiooniks on Red Hat OpenShift, vastasel juhul kasutatakse vastavat Ingress'i vĂ”i NodePort tĂŒĂŒpi teenuse manifesti):
---
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Ă€rast selle rakendamist ja edukat poodi kĂ€ivitamist on Livy graafiline liides saadaval aadressil: http://{livy-url}/ui. Livy abil saame avaldada oma Spark'i ĂŒlesande REST-pĂ€ringu kaudu, nĂ€iteks Postman'ist. Allpool on esitatud pĂ€ringute kogum (massiivis "args" saab edastada konfigureerimisargumendid koos muutujatega, mis on vajalikud kĂ€ivitatava ĂŒlesande toimimiseks):
{
"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 Esita töö jar-iga",
"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 Esita töö ilma jar-ita",
"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": {}
}
Teeme esimese pĂ€ringu kogumist, lĂ€heme OKD liidese sisse ja kontrollime, et ĂŒlesanne kĂ€ivitatakse edukalt â https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods. Samuti ilmub Livy liideses (http://{livy-url}/ui) seanss, mille raames saab API Livy vĂ”i graafilise liidese abil jĂ€lgida ĂŒlesande tĂ€itmise edenemist ja uurida seansi logisid.
NĂŒĂŒd nĂ€itame Livy töömekanismi. Selleks uurime Livy konteineri logisid Livy serveri sees asuvas podis â https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods/{livy-pod-name}?tab=logs. Nendest on nĂ€ha, et kui kutsuda vĂ€lja Livy REST API, kĂ€ivitatakse konteineris nimega «livy» spark-submit, mis on sarnane meie eespool kasutatule (siin {livy-pod-name} on loodud Livy serveri podi nimi). Kogumis on esitatud ka teine pĂ€ring, mis vĂ”imaldab kĂ€ivitada ĂŒlesandeid kaugasukohtade kaudu Spark'i tĂ€itmisfaili abil Livy serveri kaudu.
Kolmas kasutusvĂ”imalus â Spark Operator
NĂŒĂŒd, kui ĂŒlesanne on testitud, kerkib kĂŒsimus selle regulaarse kĂ€ivitamise kohta. Kuberneteses ĂŒlesannete regulaarseks kĂ€ivitamiseks on natiivne viis CronJob'i kasutamine. Kuigi seda saab kasutada, on hetkel laialt levinud operaatorite kasutamine Kuberneteses rakenduste haldamiseks. Spark'i jaoks on olemas piisavalt kĂŒps operaator, mida kasutatakse ka ettevĂ”tte taseme lahendustes (nĂ€iteks Lightbend FastData Platform). Soovitame seda kasutada â praegune stabiilne versioon Spark (2.4.5) pakub ĂŒsna piiratud vĂ”imalusi Spark'i ĂŒlesannete kĂ€ivitamise konfiguratsiooniks Kuberneteses, samas kui jĂ€rgmises peaversioonis (3.0.0) on lubatud tĂ€ieĂ”iguslik Kubernetes'e tugi, kuid selle vĂ€ljalaskekuupĂ€ev jÀÀb teadmata. Spark Operator kompenseerib selle puuduse, lisades olulised seadistusparameetrid (nĂ€iteks ConfigMap'i monteerimise Hadoop'i juurdepÀÀsu konfiguratsiooniga Spark'i pod'desse) ja vĂ”imaluse ĂŒlesande regulaarseks kĂ€ivitamiseks ajakava alusel.

TĂ”stame selle esile kui kolmandat kasutusviisi â regulaarne Spark'i ĂŒlesannete kĂ€ivitamine Kuberneteses tootmiskeskkonnas.
Spark Operator on avatud lĂ€htekoodiga ja seda arendatakse Google Cloud Platformi raames â . Selle paigaldamine vĂ”ib toimuda 3 erineval viisil:
- Lightbend FastData Platformi/Cloudflow raames;
- Helmi abil:
helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator helm install incubator/sparkoperator --namespace spark-operator - Kasutades ametlikust hoidlast tulenevaid manifeste (https://github.com/GoogleCloudPlatform/spark-on-k8s-operator/tree/master/manifest). Siinkohal tasub mĂ€rkida, et Cloudflow'i koosseisus on operaator API versiooniga v1beta1. Kui kasutatakse seda paigaldusviisi, peavad Spark'i rakenduste manifeste olema koostatud vastavalt Git'i sildiga esitatud nĂ€idetele, millel on vastav API versioon, nĂ€iteks âv1beta1-0.9.0-2.4.0â. Operaatori versiooni saab vaadata operaatori CRD-i kirjelduses sĂ”nastikus âversionsâ:
oc get crd sparkapplications.sparkoperator.k8s.io -o yaml
Kui operaator on Ă”igesti paigaldatud, ilmub vastavasse projekti aktiivne Spark'i pod (nĂ€iteks cloudflow-fdp-sparkoperator Cloudflow'i ruumis Cloudflow' jaoks) ning ilmub vastav Kubernetes'i ressursside tĂŒĂŒp nimega âsparkapplicationsâ. Olemasolevate Spark'i rakenduste uurimiseks saab kasutada jĂ€rgmist kĂ€sku:
oc get sparkapplications -n {project}
Spark Operatoriga tööde kÀivitamiseks tuleb teha kolm asja:
- luua Docker'i pilt, mis sisaldab kÔiki vajalikke raamatukogusid, samuti konfiguratsiooni ja kÀivitatavaid faile. EesmÀrgi saavutamiseks on see pilt, mis on loodud CI/CD etapis ja testitud testklastris;
- avalikustada Docker'i pilt registrisse, mis on Kubernetes'i klastri jaoks ligipÀÀsetav;
- koostada âSparkApplicationâ tĂŒĂŒpi manifest koos kĂ€ivitatava ĂŒlesande kirjeldusega. Manifestide nĂ€iteid on saadaval ametlikus repositooriumis (nt, ). Oluline on mĂ€rkida mĂ”ningaid aspekte seoses manfestiga:
- sĂ”nastikus âapiVersionâ peab olema mÀÀratud API versioon, mis vastab operaatori versioonile;
- sĂ”nastikus âmetadata.namespaceâ peab olema mÀÀratud nimede ruum, kus rakendus kĂ€ivitatakse;
- sĂ”nastikus âspec.imageâ peab olema mÀÀratud loodud Docker'i pildi aadress ligipÀÀsetavas registris;
- sĂ”nastikus âspec.mainClassâ peab olema mÀÀratud Spark'i ĂŒlesande klass, mida tuleb protsessi kĂ€ivitamisel kĂ€ivitada;
- sĂ”nastikus âspec.mainApplicationFileâ peab olema mÀÀratud tee kĂ€ivitatava jar faili juurde;
- sÔnastikus «spec.sparkVersion» peab olema mÀrgitud kasutatav Spark'i versioon;
- sÔnastikus «spec.driver.serviceAccount» peab olema mÀrgitud teenuse konto, mis asub vastavas Kubernetes'i nimede ruumis ja mida kasutatakse rakenduse kÀitamiseks;
- sÔnastikus «spec.executor» peab olema mÀrgitud ressursside arv, mis on rakendusele eraldatud;
- sĂ”nastikus «spec.volumeMounts» peab olema mĂ€rgitud kohaliku kausta tee, kuhu Spark'i ĂŒlesannete kohalikud failid luuakse.
Maani festi loomise nĂ€ide (siin {spark-service-account} on teenuse konto, mis asub Kubernetes'i klastri sees Spark'i ĂŒlesannete kĂ€itamiseks):
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"
Selles manifestis on mÀrgitud teenusekonto, mille jaoks tuleb enne manifesti avaldamist luua vajalikud rolli sidumised, et anda Spark'i rakendusele vajalikud juurdepÀÀsuÔigused Kubernetes API-ga suhtlemiseks (kui vajalik). Meie puhul on rakendusele vajalikud Ôigused Pod'ide loomiseks. Loome vajaliku rolli sidumise:
oc adm policy add-role-to-user edit system:serviceaccount:{project}:{spark-service-account} -n {project}
Samuti tasub mainida, et selle manifesti spetsifikatsioonis vĂ”ib olla mÀÀratud parameeter âhadoopConfigMapâ, mis vĂ”imaldab mÀÀrata ConfigMap'i Hadoop konfiguratsiooniga ilma, et oleks vaja eelnevalt vastavat faili Docker'i pildisse lisada. See sobib ka regulaarsete ĂŒlesannete kĂ€ivitamiseks â parameetri âscheduleâ abil saab mÀÀrata selle ĂŒlesande kĂ€ivitamise ajakava.
PĂ€rast seda salvestame meie manifesti faili spark-pi.yaml ja rakendame selle meie Kubernetes klastrile:
oc apply -f spark-pi.yaml
Selle tulemusena luuakse objekt tĂŒĂŒpi âsparkapplicationsâ:
oc get sparkapplications -n {project}
> NAME AGE
> spark-pi 22h
Selle kĂ€igus luuakse rakenduse pod, mille olek kuvatakse loodud âsparkapplicationsâ töös. Selle ĂŒle saab vaadata jĂ€rgmise kĂ€suga:
oc get sparkapplications spark-pi -o yaml -n {project}
Töö lĂ”petamisel lĂ€heb POD staatuseks âCompletedâ, mis uuendatakse ka âsparkapplicationsâ osas. Rakenduse logisid saab vaadata brauseris vĂ”i kasutades jĂ€rgmist kĂ€sku (siin {sparkapplications-pod-name} on kĂ€ivitatud ĂŒlesande poda nimi):
oc logs {sparkapplications-pod-name} -n {project}
Spark'i ĂŒlesannete haldamist saab ka spetsialiseeritud tööriista sparkctl abil. Selle paigaldamiseks kloonime repositooriumi koos allikakoodiga, installime Go ja kogume selle tööriista:
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
Vaatame kĂ€ivitatud Spark'i ĂŒlesannete loendit:
sparkctl list -n {project}
Loome Spark'i ĂŒlesande kirjelduse:
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"
KĂ€ivitame kirjeldatud ĂŒlesande sparkctl'i abil:
sparkctl create spark-app.yaml -n {project}
Vaatame kĂ€ivitatud Spark'i ĂŒlesannete loendit:
sparkctl list -n {project}
Vaatame kĂ€ivitatud Spark-ĂŒlesande sĂŒndmuste loetelu:
sparkctl event spark-pi -n {project} -f
Uurime kĂ€ivitatud Spark-ĂŒlesande staatust:
sparkctl status spark-pi -n {project}
KokkuvÔtteks vaatame, milliseid puudusi on tuvastatud praeguse stabiilse Spark versiooni (2.4.5) kasutamisel Kuberneteses:
- Esimene ja vĂ”ib-olla peamine puudus on andmete lokaliseerimise puudumine. KĂ”ikide YARN-i puuduste juures oli selle kasutamisel ka eeliseid, nĂ€iteks koodi edastamise pĂ”himĂ”te andmetele (mitte andmete edastamine koodile). TĂ€nu sellele kĂ€ideldi Spark'i ĂŒlesandeid sĂ”lmedes, kus asusid arvutustes osalevad andmed, mis nĂ”udis mĂ€rgatavalt vĂ€hem aega andmete edastamiseks ĂŒle vĂ”rgu. Kubernetes'e kasutamisel peame silmitsi seisma vajadusega edastada andmeid, mis on tĂ¶Ă¶ĂŒlesande tĂ€itmisel vajalikud. Kui need on piisavalt suured, vĂ”ib tĂ¶Ă¶ĂŒlesande tĂ€itmise aeg oluliselt pikeneda ning vĂ”ib olla vajalik eraldada piisavalt suur kettaruum Spark'i ĂŒlesande eksemplaride ajutiseks salvestamiseks. Seda puudust saab vĂ€hendada spetsialiseeritud tarkvarade abil, mis tagavad andmete kohaloleku Kubernetes'es (nĂ€iteks Alluxio), kuid see tĂ€hendab sisuliselt vajadust sĂ€ilitada andmete tĂ€iskope klastrite sĂ”lmedes.
- Teine oluline miinus on turvalisus. Vaikimisi on Spark'i ĂŒlesannete kĂ€ivitamisega seotud turvafunktsioonid vĂ€ljalĂŒlitatud, Kerberose kasutamise vĂ”imalust ametlikus dokumentatsioonis ei kĂ€sitleta (kuigi vastavad seaded ilmusid versioonis 3.0.0, mis nĂ”uab tĂ€iendavat töötlemist), ning Spark'i turvalisuse dokumentatsioonis (https://spark.apache.org/docs/2.4.5/security.html) kajastuvad vĂ”tmehoidjatena ainult YARN, Mesos ja Standalone Cluster. Samuti ei saa Spark'i ĂŒlesandeid kĂ€itava kasutaja identiteeti otse mÀÀrata â mÀÀrame vaid teenuse kasutajakonto, mille all see kĂ€itub, ja kasutaja valitakse turvapoliitikate pĂ”hjal. Sellega seoses kas kasutatakse root kasutajat, mis ei ole tootmiskeskkonnas turvaline, vĂ”i juhuslikku UID'd, mis on ebamugav andmete juurdepÀÀsu Ă”iguste jaotamisel (lahendatav PodSecurityPolicies loomise ja nende seondumisega vastavate teenuse kasutajakontodega). Praegu lahendatakse see kas paigutades kĂ”ik vajalikud failid otse Docker'i pildile vĂ”i muutes Spark'i kĂ€ivitusskripti, et kasutada teie organisatsioonis aktsepteeritud saladuste hoidmise ja hankimise mehhanismi.
- Spark'i kĂ€ivitamine Kubernetesega on jĂ€tkuvalt eksperimentaalne ja tulevikus vĂ”ivad kasutada olevaid artefakte (konfiguratsioonifailid, Docker'i pĂ”hifailid ja kĂ€ivitusskriptid) olulisi muudatusi. TĂ”epoolest â materjali ettevalmistamise ajal testiti versioone 2.3.0 ja 2.4.5, mille kĂ€itumine oli oluliselt erinev.
Ootame uuendusi â hiljuti ilmus uus Spark'i versioon (3.0.0), mis toob olulisi muudatusi Spark'i toimimisse Kuberneteses, kuid sĂ€ilitab eksperimentaalse toe selle ressursihalduri jaoks. VĂ”imalik, et jĂ€rgmised uuendused tĂ”epoolest vĂ”imaldavad soovitada YARN'ist loobumist ja Spark'i ĂŒlesannete kĂ€ivitamist Kuberneteses, kartmata oma sĂŒsteemi turvalisuse pĂ€rast ning ilma vajaduseta iseseisva funktsionaalsete komponentide kohandamise jĂ€rele.
Fin.
Allikas: habr.com


