Kjære lesere, god ettermiddag. I dag skal vi snakke litt om Apache Spark og dens utviklingsmuligheter.
I den moderne verden av Big Data er Apache Spark de facto-standarden for utvikling av batchdatabehandlingsoppgaver. I tillegg brukes den også til å lage strømmeapplikasjoner som fungerer i mikrobatch-konseptet, behandle og sende data i små porsjoner (Spark Structured Streaming). Og tradisjonelt har det vært en del av den totale Hadoop-stabelen, ved å bruke YARN (eller i noen tilfeller Apache Mesos) som ressursansvarlig. Innen 2020 er bruken i sin tradisjonelle form et spørsmål for de fleste selskaper på grunn av mangelen på anstendige Hadoop-distribusjoner - utviklingen av HDP og CDH har stoppet, CDH er ikke godt utviklet og har høye kostnader, og de gjenværende Hadoop-leverandørene har enten sluttet å eksistere eller har en svak fremtid. Derfor er lanseringen av Apache Spark ved bruk av Kubernetes av økende interesse blant fellesskapet og store selskaper - ved å bli en standard innen containerorkestrering og ressursstyring i private og offentlige skyer, løser den problemet med upraktisk ressursplanlegging av Spark-oppgaver på YARN og gir en plattform i stadig utvikling med mange kommersielle og åpne distribusjoner for selskaper i alle størrelser og striper. I tillegg, i kjølvannet av populariteten, har de fleste allerede klart å skaffe seg et par egne installasjoner og har økt sin ekspertise på bruken, noe som forenkler flyttingen.
Fra og med versjon 2.3.0 skaffet Apache Spark offisiell støtte for å kjøre oppgaver i en Kubernetes-klynge, og i dag vil vi snakke om den nåværende modenheten til denne tilnærmingen, ulike alternativer for bruken og fallgruvene som vil bli møtt under implementeringen.
Først av alt, la oss se på prosessen med å utvikle oppgaver og applikasjoner basert på Apache Spark og fremheve typiske tilfeller der du trenger å kjøre en oppgave på en Kubernetes-klynge. Når du forbereder dette innlegget, brukes OpenShift som en distribusjon og kommandoer som er relevante for kommandolinjeverktøyet (oc) vil bli gitt. For andre Kubernetes-distribusjoner kan de tilsvarende kommandoene fra standard Kubernetes kommandolinjeverktøy (kubectl) eller deres analoger (for eksempel for oc adm policy) brukes.
Førstegangsbruk - gnist-send
Under utviklingen av oppgaver og applikasjoner må utvikleren kjøre oppgaver for å feilsøke datatransformasjon. Teoretisk sett kan stubber brukes til disse formålene, men utvikling med deltagelse av reelle (riktignok test) forekomster av sluttsystemer har vist seg å være raskere og bedre i denne klassen av oppgaver. I tilfellet når vi feilsøker på reelle forekomster av sluttsystemer, er to scenarier mulig:
utvikleren kjører en Spark-oppgave lokalt i frittstående modus;
en utvikler kjører en Spark-oppgave på en Kubernetes-klynge i en testsløyfe.
Det første alternativet har rett til å eksistere, men medfører en rekke ulemper:
Hver utvikler må gis tilgang fra arbeidsplassen til alle forekomster av sluttsystemene han trenger;
en tilstrekkelig mengde ressurser kreves på arbeidsmaskinen for å kjøre oppgaven som utvikles.
Det andre alternativet har ikke disse ulempene, siden bruken av en Kubernetes-klynge lar deg tildele den nødvendige ressurspoolen for å kjøre oppgaver og gi den den nødvendige tilgangen til sluttsystemforekomster, og gir fleksibel tilgang til den ved å bruke Kubernetes-rollemodellen for alle medlemmer av utviklingsteamet. La oss fremheve det som den første brukssaken – lansering av Spark-oppgaver fra en lokal utviklermaskin på en Kubernetes-klynge i en testkrets.
La oss snakke mer om prosessen med å sette opp Spark til å kjøre lokalt. For å begynne å bruke Spark må du installere det:
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 samler inn de nødvendige pakkene for å jobbe med Kubernetes:
cd spark-2.4.5/
./build/mvn -Pkubernetes -DskipTests clean package
En full build tar mye tid, og for å lage Docker-bilder og kjøre dem på en Kubernetes-klynge trenger du egentlig bare jar-filer fra "assembly/"-katalogen, så du kan bare bygge dette underprosjektet:
For å kjøre Spark-jobber på Kubernetes, må du opprette et Docker-bilde som skal brukes som et basisbilde. Det er 2 mulige tilnærminger her:
Det genererte Docker-bildet inkluderer den kjørbare Spark-oppgavekoden;
Det opprettede bildet inkluderer bare Spark og de nødvendige avhengighetene, den kjørbare koden vert eksternt (for eksempel i HDFS).
Først, la oss bygge et Docker-bilde som inneholder et testeksempel på en Spark-oppgave. For å lage Docker-bilder har Spark et verktøy som heter "docker-image-tool". La oss studere hjelpen på det:
./bin/docker-image-tool.sh --help
Med dens hjelp kan du lage Docker-bilder og laste dem opp til eksterne registre, men som standard har det en rekke ulemper:
skaper uten feil 3 Docker-bilder samtidig - for Spark, PySpark og R;
tillater deg ikke å angi et bildenavn.
Derfor vil vi bruke en modifisert versjon av dette verktøyet gitt nedenfor:
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
Med dens hjelp setter vi sammen et grunnleggende Spark-bilde som inneholder en testoppgave for å beregne Pi ved hjelp av Spark (her er {docker-registry-url} URL-en til Docker-bilderegisteret ditt, {repo} er navnet på depotet inne i registeret, som samsvarer med prosjektet i OpenShift , {image-name} — navn på bildet (hvis tre-nivå separasjon av bilder brukes, for eksempel som i det integrerte registret for Red Hat OpenShift-bilder), {tag} — tag for dette versjon av bildet):
La oss sjekke at det sammensatte bildet er tilgjengelig i OKD. For å gjøre dette, åpne URL-en i nettleseren med en liste over bilder av det tilsvarende prosjektet (her er {project} navnet på prosjektet i OpenShift-klyngen, {OKD-WEBUI-URL} er URL-en til OpenShift-nettkonsollen ) - https://{OKD-WEBUI-URL}/console /project/{project}/browse/images/{image-name}.
For å kjøre oppgaver må det opprettes en tjenestekonto med rettighetene til å kjøre pods som root (vi vil diskutere dette punktet senere):
—navn — navnet på oppgaven som vil delta i dannelsen av navnet på Kubernetes-bekkene;
—klasse — klasse til den kjørbare filen, kalt når oppgaven starter;
—conf — Spark-konfigurasjonsparametere;
spark.executor.instances — antall Spark-utøvere som skal lanseres;
spark.kubernetes.authenticate.driver.serviceAccountName - navnet på Kubernetes-tjenestekontoen som brukes ved oppstart av pods (for å definere sikkerhetskonteksten og funksjonene når du samhandler med Kubernetes API);
spark.kubernetes.namespace — Kubernetes navneområde der driver- og eksekveringsmoduler vil bli lansert;
spark.submit.deployMode — metode for å starte Spark (for standard gnist-send brukes "cluster", for Spark Operator og senere versjoner av Spark "klient");
spark.kubernetes.container.image - Docker-bilde som brukes til å starte pods;
spark.master — Kubernetes API URL (ekstern er spesifisert slik at tilgang skjer fra den lokale maskinen);
local:// er banen til den kjørbare Spark-filen inne i Docker-bildet.
Vi går til det tilsvarende OKD-prosjektet og studerer de opprettede podene - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods.
For å forenkle utviklingsprosessen, kan et annet alternativ brukes, der et felles basisbilde av Spark opprettes, som brukes av alle oppgavene til å kjøre, og øyeblikksbilder av kjørbare filer publiseres til ekstern lagring (for eksempel Hadoop) og spesifiseres når du ringer spark-send inn som en lenke. I dette tilfellet kan du kjøre forskjellige versjoner av Spark-oppgaver uten å gjenoppbygge Docker-bilder, ved å bruke for eksempel WebHDFS for å publisere bilder. Vi sender en forespørsel om å opprette en fil (her er {host} verten for WebHDFS-tjenesten, {port} er porten til WebHDFS-tjenesten, {path-to-file-on-hdfs} er den ønskede banen til filen på HDFS):
curl -i -X PUT "http://{host}:{port}/webhdfs/v1/{path-to-file-on-hdfs}?op=CREATE
Du vil motta et svar som dette (her {location} er nettadressen som må brukes for å laste ned filen):
Last inn den kjørbare Spark-filen til HDFS (her er {path-to-local-file} banen til den kjørbare Spark-filen på den gjeldende verten):
curl -i -X PUT -T {path-to-local-file} "{location}"
Etter dette kan vi gjøre spark-submit ved å bruke Spark-filen lastet opp til HDFS (her {class-name} er navnet på klassen som må startes for å fullføre oppgaven):
Det skal bemerkes at for å få tilgang til HDFS og sikre at oppgaven fungerer, kan det hende du må endre Dockerfile og entrypoint.sh-skriptet - legg til et direktiv til Dockerfile for å kopiere avhengige biblioteker til /opt/spark/jars-katalogen og inkludere HDFS-konfigurasjonsfilen i SPARK_CLASSPATH i inngangspunkt. sh.
Andre bruksboks - Apache Livy
Videre, når en oppgave er utviklet og resultatet må testes, oppstår spørsmålet om å starte den som en del av CI/CD-prosessen og spore statusen for utføringen av den. Selvfølgelig kan du kjøre det ved å bruke et lokalt spark-submit-kall, men dette kompliserer CI/CD-infrastrukturen siden det krever installasjon og konfigurering av Spark på CI-serveragentene/løperne og konfigurering av tilgang til Kubernetes API. For dette tilfellet har målimplementeringen valgt å bruke Apache Livy som et REST API for å kjøre Spark-oppgaver som er vert inne i en Kubernetes-klynge. Med dens hjelp kan du kjøre Spark-oppgaver på en Kubernetes-klynge ved å bruke vanlige cURL-forespørsler, som enkelt implementeres basert på enhver CI-løsning, og plasseringen i Kubernetes-klyngen løser problemet med autentisering når du samhandler med Kubernetes API.
La oss fremheve det som en andre brukssak – å kjøre Spark-oppgaver som en del av en CI/CD-prosess på en Kubernetes-klynge i en testsløyfe.
Litt om Apache Livy - den fungerer som en HTTP-server som gir et webgrensesnitt og en RESTful API som lar deg fjernstarte spark-submit ved å sende de nødvendige parameterne. Tradisjonelt har den blitt sendt som en del av en HDP-distribusjon, men kan også distribueres til OKD eller en hvilken som helst annen Kubernetes-installasjon ved å bruke det riktige manifestet og et sett med Docker-bilder, for eksempel denne - github.com/ttauveron/k8s-big-data-experiments/tree/master/livy-spark-2.3. For vårt tilfelle ble et lignende Docker-bilde bygget, inkludert Spark versjon 2.4.5 fra følgende Dockerfile:
Det genererte bildet kan bygges og lastes opp til ditt eksisterende Docker-depot, for eksempel det interne OKD-depotet. For å distribuere det, bruk følgende manifest ({registry-url} - URL til Docker-bilderegisteret, {image-name} - Docker-bildenavn, {tag} - Docker-bildetag, {livy-url} - ønsket nettadresse der serveren vil være tilgjengelig Livy; "Rute"-manifestet brukes hvis Red Hat OpenShift brukes som Kubernetes-distribusjon, ellers brukes det tilsvarende Ingress- eller Service-manifestet av typen NodePort):
Etter å ha brukt den og vellykket lansering av poden, er Livy grafiske grensesnitt tilgjengelig på lenken: http://{livy-url}/ui. Med Livy kan vi publisere Spark-oppgaven vår ved å bruke en REST-forespørsel fra for eksempel Postman. Et eksempel på en samling med forespørsler er presentert nedenfor (konfigurasjonsargumenter med variabler som er nødvendige for driften av den lanserte oppgaven kan sendes i "args"-matrisen):
La oss utføre den første forespørselen fra samlingen, gå til OKD-grensesnittet og sjekke at oppgaven har blitt lansert - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods. Samtidig vil en økt vises i Livy-grensesnittet (http://{livy-url}/ui), der du ved å bruke Livy API eller grafisk grensesnitt kan spore fremdriften til oppgaven og studere økten tømmerstokker.
La oss nå vise hvordan Livy fungerer. For å gjøre dette, la oss undersøke loggene til Livy-beholderen inne i poden med Livy-serveren - https://{OKD-WEBUI-URL}/console/project/{project}/browse/pods/{livy-pod-name }?tab=logger. Fra dem kan vi se at når du kaller Livy REST API i en beholder kalt "livy", blir en spark-submit utført, lik den vi brukte ovenfor (her er {livy-pod-name} navnet på den opprettede poden med Livy-serveren). Samlingen introduserer også en andre spørring som lar deg kjøre oppgaver som eksternt er vert for en Spark-kjørbar fil ved hjelp av en Livy-server.
Tredje brukstilfelle - Spark Operator
Nå som oppgaven er testet, oppstår spørsmålet om å kjøre den regelmessig. Den opprinnelige måten å regelmessig kjøre oppgaver i en Kubernetes-klynge er CronJob-enheten, og du kan bruke den, men for øyeblikket er bruken av operatører til å administrere applikasjoner i Kubernetes veldig populær, og for Spark er det en ganske moden operatør, som også er brukes i løsninger på bedriftsnivå (for eksempel Lightbend FastData Platform). Vi anbefaler å bruke den - den nåværende stabile versjonen av Spark (2.4.5) har ganske begrensede konfigurasjonsmuligheter for å kjøre Spark-oppgaver i Kubernetes, mens den neste hovedversjonen (3.0.0) erklærer full støtte for Kubernetes, men utgivelsesdatoen forblir ukjent . Spark Operator kompenserer for denne mangelen ved å legge til viktige konfigurasjonsalternativer (for eksempel å montere en ConfigMap med Hadoop-tilgangskonfigurasjon til Spark-pods) og muligheten til å kjøre en regelmessig planlagt oppgave.
La oss fremheve det som et tredje bruksområde – regelmessig kjører Spark-oppgaver på en Kubernetes-klynge i en produksjonssløyfe.
Bruke manifester fra det offisielle depotet (https://github.com/GoogleCloudPlatform/spark-on-k8s-operator/tree/master/manifest). Det er verdt å merke seg følgende - Cloudflow inkluderer en operatør med API-versjon v1beta1. Hvis denne typen installasjon brukes, bør Spark-applikasjonsmanifestbeskrivelser være basert på eksempelkoder i Git med riktig API-versjon, for eksempel "v1beta1-0.9.0-2.4.0". Versjonen av operatøren kan finnes i beskrivelsen av CRD inkludert i operatøren i "versjoner"-ordboken:
oc get crd sparkapplications.sparkoperator.k8s.io -o yaml
Hvis operatøren er riktig installert, vil en aktiv pod med Spark-operatøren vises i det tilsvarende prosjektet (for eksempel cloudflow-fdp-sparkoperator i Cloudflow-området for Cloudflow-installasjonen) og en tilsvarende Kubernetes-ressurstype kalt "sparkapplications" vises . Du kan utforske tilgjengelige Spark-applikasjoner med følgende kommando:
oc get sparkapplications -n {project}
For å kjøre oppgaver med Spark Operator må du gjøre 3 ting:
lage et Docker-bilde som inkluderer alle nødvendige biblioteker, samt konfigurasjons- og kjørbare filer. I målbildet er dette et bilde laget på CI/CD-stadiet og testet på en testklynge;
publisere et Docker-bilde til et register tilgjengelig fra Kubernetes-klyngen;
"apiVersion"-ordboken må angi API-versjonen som tilsvarer operatørversjonen;
«metadata.namespace»-ordboken må angi navneområdet der applikasjonen skal startes;
"spec.image"-ordboken må inneholde adressen til det opprettede Docker-bildet i et tilgjengelig register;
"spec.mainClass"-ordboken må inneholde Spark-oppgaveklassen som må kjøres når prosessen starter;
"spec.mainApplicationFile"-ordboken må inneholde banen til den kjørbare jar-filen;
"spec.sparkVersion"-ordboken må angi hvilken versjon av Spark som brukes;
«spec.driver.serviceAccount»-ordboken må spesifisere tjenestekontoen innenfor det tilsvarende Kubernetes-navneområdet som skal brukes til å kjøre programmet;
«spec.executor»-ordboken må angi antall ressurser som er tildelt applikasjonen;
"spec.volumeMounts"-ordboken må spesifisere den lokale katalogen der de lokale Spark-oppgavefilene skal opprettes.
Et eksempel på generering av et manifest (her er {spark-service-account} en tjenestekonto inne i Kubernetes-klyngen for å kjøre Spark-oppgaver):
Dette manifestet spesifiserer en tjenestekonto som du, før du publiserer manifestet, må opprette de nødvendige rollebindingene som gir de nødvendige tilgangsrettighetene for at Spark-applikasjonen kan samhandle med Kubernetes API (om nødvendig). I vårt tilfelle trenger applikasjonen rettigheter for å lage Pods. La oss lage den nødvendige rollebindingen:
Det er også verdt å merke seg at denne manifestspesifikasjonen kan inneholde en "hadoopConfigMap"-parameter, som lar deg spesifisere et ConfigMap med Hadoop-konfigurasjonen uten å først plassere den tilsvarende filen i Docker-bildet. Den er også egnet for å kjøre oppgaver regelmessig - ved å bruke parameteren "planlegging", kan en tidsplan for å kjøre en gitt oppgave spesifiseres.
Etter det lagrer vi manifestet vårt i spark-pi.yaml-filen og bruker det på Kubernetes-klyngen vår:
oc apply -f spark-pi.yaml
Dette vil lage et objekt av typen "sparkapplications":
oc get sparkapplications -n {project}
> NAME AGE
> spark-pi 22h
I dette tilfellet vil det bli opprettet en pod med en applikasjon, hvis status vil vises i de opprettede "sparkapplications". Du kan se den med følgende kommando:
oc get sparkapplications spark-pi -o yaml -n {project}
Når oppgaven er fullført, vil POD flytte til "Fullført"-statusen, som også vil oppdateres i "sparkapplications". Applikasjonslogger kan vises i nettleseren eller ved å bruke følgende kommando (her er {sparkapplications-pod-name} navnet på poden til den kjørende oppgaven):
oc logs {sparkapplications-pod-name} -n {project}
Spark-oppgaver kan også administreres ved å bruke det spesialiserte sparkctl-verktøyet. For å installere det, klone depotet med kildekoden, installer Go og bygg dette verktøyet:
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
La oss undersøke listen over kjørende Spark-oppgaver:
La oss kjøre den beskrevne oppgaven ved å bruke sparkctl:
sparkctl create spark-app.yaml -n {project}
La oss undersøke listen over kjørende Spark-oppgaver:
sparkctl list -n {project}
La oss undersøke listen over hendelser for en lansert Spark-oppgave:
sparkctl event spark-pi -n {project} -f
La oss undersøke statusen til den løpende Spark-oppgaven:
sparkctl status spark-pi -n {project}
Avslutningsvis vil jeg vurdere de oppdagede ulempene ved å bruke den nåværende stabile versjonen av Spark (2.4.5) i Kubernetes:
Den første og kanskje største ulempen er mangelen på datalokalitet. Til tross for alle manglene til YARN, var det også fordeler ved å bruke det, for eksempel prinsippet om å levere kode til data (i stedet for data til kode). Takket være det ble Spark-oppgaver utført på nodene der dataene som var involvert i beregningene var lokalisert, og tiden det tok å levere data over nettverket ble betydelig redusert. Når vi bruker Kubernetes, står vi overfor behovet for å flytte data involvert i en oppgave over nettverket. Hvis de er store nok, kan oppgaveutførelsestiden øke betydelig, og også kreve en ganske stor mengde diskplass tildelt Spark-oppgaveforekomster for deres midlertidige lagring. Denne ulempen kan reduseres ved å bruke spesialisert programvare som sikrer datalokalitet i Kubernetes (for eksempel Alluxio), men dette betyr faktisk behovet for å lagre en fullstendig kopi av dataene på nodene til Kubernetes-klyngen.
Den andre viktige ulempen er sikkerhet. Som standard er sikkerhetsrelaterte funksjoner for å kjøre Spark-oppgaver deaktivert, bruken av Kerberos er ikke dekket i den offisielle dokumentasjonen (selv om de tilsvarende alternativene ble introdusert i versjon 3.0.0, som vil kreve ekstra arbeid), og sikkerhetsdokumentasjonen for ved bruk av Spark (https ://spark.apache.org/docs/2.4.5/security.html) vises bare YARN, Mesos og Standalone Cluster som nøkkelbutikker. Samtidig kan brukeren som Spark-oppgaver startes under ikke spesifiseres direkte - vi spesifiserer kun tjenestekontoen den skal fungere under, og brukeren velges basert på de konfigurerte sikkerhetspolicyene. I denne forbindelse brukes enten rotbrukeren, som ikke er trygg i et produktivt miljø, eller en bruker med en tilfeldig UID, noe som er upraktisk ved distribusjon av tilgangsrettigheter til data (dette kan løses ved å lage PodSecurityPolicies og koble dem til tilsvarende tjenestekontoer). Foreløpig er løsningen enten å plassere alle nødvendige filer direkte inn i Docker-bildet, eller modifisere Spark-lanseringsskriptet for å bruke mekanismen for å lagre og hente hemmeligheter som er tatt i bruk i organisasjonen din.
Å kjøre Spark-jobber med Kubernetes er offisielt fortsatt i eksperimentell modus, og det kan være betydelige endringer i artefaktene som brukes (konfigurasjonsfiler, Docker-basebilder og lanseringsskript) i fremtiden. Og faktisk, når du utarbeidet materialet, ble versjoner 2.3.0 og 2.4.5 testet, oppførselen var betydelig forskjellig.
La oss vente på oppdateringer - en ny versjon av Spark (3.0.0) ble nylig utgitt, som brakte betydelige endringer i arbeidet til Spark på Kubernetes, men beholdt den eksperimentelle statusen for støtte for denne ressursbehandleren. Kanskje vil de neste oppdateringene virkelig gjøre det mulig å fullt ut anbefale å forlate YARN og kjøre Spark-oppgaver på Kubernetes uten frykt for sikkerheten til systemet ditt og uten å måtte endre funksjonelle komponenter uavhengig.