Utiliser des conteneurs personnalisés avec Dataproc sans serveur pour Spark

Dataproc sans serveur pour Spark exécute des charges de travail dans des conteneurs Docker. Le conteneur fournit l'environnement d'exécution des processus du pilote et de l'exécuteur de la charge de travail. Par défaut, Dataproc sans serveur pour Spark utilise une image de conteneur qui inclut les packages Spark, Java, Python et R par défaut associés à une version d'exécution. L'API Dataproc sans serveur pour Spark batches vous permet d'utiliser une image de conteneur personnalisée au lieu de l'image par défaut. En règle générale, une image de conteneur personnalisé ajoute des dépendances de charge de travail Spark Java ou Python non fournies par l'image de conteneur par défaut. Important:N'incluez pas Spark dans votre image de conteneur personnalisée. Dataproc sans serveur pour Spark installera Spark dans le conteneur au moment de l'exécution.

Envoyer une charge de travail par lot Spark à l'aide d'une image de conteneur personnalisée

gcloud

Exécutez la commande gcloud dataproc batch submit spark avec l'option --container-image pour spécifier votre image de conteneur personnalisé lorsque vous envoyez une charge de travail par lot Spark.

gcloud dataproc batches submit spark \
    --container-image=custom-image, for example, "gcr.io/my-project-id/my-image:1.0.1" \
    --region=region \
    --jars=path to user workload jar located in Cloud Storage or included in the custom container \
    --class=The fully qualified name of a class in the jar file, such as org.apache.spark.examples.SparkPi \
    -- add any workload arguments here

Remarques :

  • Custom-image:spécifiez l'image de conteneur personnalisé en utilisant le format de nom d'image Container Registry suivant: {hostname}/{project-id}/{image}:{tag}, par exemple, "gcr.io/my-project-id/my-image:1.0.1". Remarque:Vous devez héberger votre image de conteneur personnalisé sur Container Registry ou Artifact Registry. Notez que Dataproc sans serveur ne peut pas extraire de conteneurs à partir d'autres registres.
  • --jars:spécifiez le chemin d'accès d'une charge de travail utilisateur incluse dans votre image de conteneur personnalisé ou située dans Cloud Storage, par exemple /opt/spark/jars/spark-examples.jar ou gs://my-bucket/spark/jars/spark-examples.jar.
  • Autres options de commande pour les lots:vous pouvez ajouter d'autres indicateurs de commande de lots facultatifs, par exemple pour utiliser un serveur d'historique persistant (PHS). Remarque: Le PHS doit être situé dans la région où vous exécutez des charges de travail par lot.
  • Arguments de charge de travail : vous pouvez ajouter n'importe quel argument de charge de travail en ajoutant "--" à la fin de la commande, suivi des arguments de la charge de travail.

REST

L'image de conteneur personnalisé est fournie via le champ RuntimeConfig.containerImage dans une requête API batches.create.

L'exemple suivant montre comment utiliser un conteneur personnalisé pour envoyer une charge de travail par lot à l'aide de l'API batches.create de Dataproc sans serveur pour Spark.

Avant d'utiliser les données de requête, effectuez les remplacements suivants:

  • project-id : ID de projet Google Cloud
  • region: région
  • custom-container-image: spécifiez l'image de conteneur personnalisé en utilisant le format de nom d'image Container Registry suivant: {hostname}/{project-id}/{image}:{tag}, par exemple, "gcr.io/my-project-id/my-image:1.0.1". Remarque: Vous devez héberger votre conteneur personnalisé sur Container Registry ou Artifact Registry . Notez que Dataproc sans serveur ne peut pas extraire de conteneurs à partir d'autres registres.
  • jar-uri:spécifiez le chemin d'accès à un fichier JAR de charge de travail inclus dans votre image de conteneur personnalisé ou situé dans Cloud Storage, par exemple, "/opt/spark/jars/spark-examples.jar" ou "gs:///spark/jars/spark-examples.jar".
  • class:nom complet d'une classe dans le fichier JAR, par exemple "org.apache.spark.examples.SparkPi".
  • Autres options:vous pouvez utiliser d'autres champs de ressources de charge de travail par lot. Par exemple, vous pouvez utiliser le champ sparkBatch.args pour transmettre des arguments à votre charge de travail (pour en savoir plus, consultez la documentation sur les ressources Batch). Pour utiliser un serveur d'historique persistant (PHS), consultez la page Configurer un serveur d'historique persistant. Remarque: Le PHS doit être situé dans la région où vous exécutez des charges de travail par lot.

Méthode HTTP et URL :

POST https://dataproc.googleapis.com/v1/projects/project-id/locations/region/batches

Corps JSON de la requête :

{
  "runtimeConfig":{
    "containerImage":"custom-container-image
  },
  "sparkBatch":{
    "jarFileUris":[
      "jar-uri"
    ],
    "mainClass":"class"
  }
}

Pour envoyer votre requête, développez l'une des options suivantes :

Vous devriez recevoir une réponse JSON de ce type :

{
"name":"projects/project-id/locations/region/batches/batch-id",
  "uuid":",uuid",
  "createTime":"2021-07-22T17:03:46.393957Z",
  "runtimeConfig":{
    "containerImage":"gcr.io/my-project/my-image:1.0.1"
  },
  "sparkBatch":{
    "mainClass":"org.apache.spark.examples.SparkPi",
    "jarFileUris":[
      "/opt/spark/jars/spark-examples.jar"
    ]
  },
  "runtimeInfo":{
    "outputUri":"gs://dataproc-.../driveroutput"
  },
  "state":"SUCCEEDED",
  "stateTime":"2021-07-22T17:06:30.301789Z",
  "creator":"account-email-address",
  "runtimeConfig":{
    "properties":{
      "spark:spark.executor.instances":"2",
      "spark:spark.driver.cores":"2",
      "spark:spark.executor.cores":"2",
      "spark:spark.app.name":"projects/project-id/locations/region/batches/batch-id"
    }
  },
  "environmentConfig":{
    "peripheralsConfig":{
      "sparkHistoryServerConfig":{
      }
    }
  },
  "operation":"projects/project-id/regions/region/operation-id"
}

Créer une image de conteneur personnalisée

Les images de conteneurs personnalisés Dataproc sans serveur pour Spark sont des images Docker. Vous pouvez utiliser les outils de création d'images Docker pour créer des images de conteneurs personnalisées, mais certaines conditions doivent être remplies pour que les images soient compatibles avec Dataproc sans serveur pour Spark. Les sections suivantes décrivent ces conditions.

Système d'exploitation

Vous pouvez choisir n'importe quelle image de système d'exploitation comme image de base pour votre image de conteneur personnalisé. Recommandation: Les images Debian 11 par défaut sont préférées (par exemple, debian:11-slim), car elles ont fait l'objet de tests pour éviter les problèmes de compatibilité.

Services publics

Vous devez inclure les packages utilitaires suivants, nécessaires à l'exécution de Spark, dans votre image de conteneur personnalisée:

  • procps
  • tini

Pour exécuter XGBoost à partir de Spark (Java ou Scala), vous devez inclure libgomp1.

Utilisateur du conteneur

Dataproc sans serveur pour Spark exécute les conteneurs en tant qu'utilisateur Linux spark avec un UID 1099 et un GID 1099. Les instructions USER définies dans les fichiers Dockerfile d'images de conteneurs personnalisées sont ignorées au moment de l'exécution. Utilisez l'UID et le GID pour les autorisations du système de fichiers. Par exemple, si vous ajoutez un fichier JAR à l'emplacement /opt/spark/jars/my-lib.jar de l'image en tant que dépendance de charge de travail, vous devez accorder à l'utilisateur spark une autorisation de lecture sur le fichier.

Streaming d'images

Dataproc sans serveur pour Spark démarre normalement une charge de travail nécessitant une image de conteneur personnalisée en téléchargeant l'image entière sur le disque. Cela peut entraîner un délai d'initialisation, en particulier pour les clients avec des images volumineuses.

À la place, vous pouvez utiliser le streaming d'image, qui permet d'extraire des données d'image selon vos besoins. Cela permet à la charge de travail de démarrer sans attendre le téléchargement de l'image entière, ce qui peut améliorer le temps d'initialisation. Pour activer le streaming d'images, vous devez activer l'API Container Filesystem. Vous devez également stocker vos images de conteneurs dans Artifact Registry, et le dépôt Artifact Registry doit se trouver dans la même région que votre charge de travail Dataproc ou dans un emplacement multirégional correspondant à la région dans laquelle votre charge de travail s'exécute. Si Dataproc n'est pas compatible avec l'image ou si le service de streaming d'images n'est pas disponible, notre mise en œuvre de streaming télécharge l'image entière. Notez que les éléments suivants ne sont pas compatibles avec le streaming d'images:

Dans ce cas, Dataproc extrait l'intégralité de l'image avant de démarrer la charge de travail.

Spark

N'incluez pas Spark dans votre image de conteneur personnalisé. Au moment de l'exécution, Dataproc sans serveur pour Spark installe les fichiers binaires et les configurations Spark de l'hôte dans le conteneur: les binaires sont installés dans le répertoire /usr/lib/spark et les configurations sont installés dans le répertoire /etc/spark/conf. Les fichiers existants dans ces répertoires sont remplacés par Dataproc sans serveur pour Spark au moment de l'exécution.

Environnement d'exécution Java

N'incluez pas votre propre environnement d'exécution Java (JRE) dans votre image de conteneur personnalisé. Au moment de l'exécution, Dataproc sans serveur pour Spark installe OpenJDK de l'hôte dans le conteneur. Si vous incluez un JRE dans votre image de conteneur personnalisé, il sera ignoré.

Packages Java

Vous pouvez inclure des fichiers JAR en tant que dépendances de charge de travail Spark dans votre image de conteneur personnalisé. Vous pouvez également définir la variable d'environnement SPARK_EXTRA_CLASSPATH pour inclure les fichiers JAR. Dataproc sans serveur pour Spark ajoutera la valeur de la variable d'environnement dans le chemin de classe des processus JVM Spark. Recommandation : Placez les fichiers JAR dans le répertoire /opt/spark/jars et définissez SPARK_EXTRA_CLASSPATH sur /opt/spark/jars/*.

Vous pouvez inclure le fichier JAR de la charge de travail dans votre image de conteneur personnalisé, puis le référencer avec un chemin d'accès local lors de l'envoi de la charge de travail, par exemple file:///opt/spark/jars/my-spark-job.jar (voir l'article Envoyer une charge de travail par lot Spark à l'aide d'une image de conteneur personnalisée).

Packages Python

Par défaut, Dataproc sans serveur pour Spark installe Conda de l'hôte au répertoire /opt/dataproc/conda du conteneur au moment de l'exécution. PYSPARK_PYTHON est défini sur /opt/dataproc/conda/bin/python. Son répertoire de base, /opt/dataproc/conda/bin, est inclus dans PATH.

Vous pouvez inclure votre environnement Python avec des packages situés dans un autre répertoire de votre image de conteneur personnalisé, par exemple dans /opt/conda, et définir la variable d'environnement PYSPARK_PYTHON sur /opt/conda/bin/python.

Votre image de conteneur personnalisé peut inclure d'autres modules Python qui ne font pas partie de l'environnement Python, par exemple des scripts Python avec des fonctions utilitaires. Définissez la variable d'environnement PYTHONPATH pour inclure les répertoires dans lesquels se trouvent les modules.

Environnement R

Vous pouvez personnaliser l'environnement R dans votre image de conteneur personnalisée à l'aide de l'une des options suivantes : - Utilisez Conda pour gérer et installer les packages R à partir du canal conda-forge. - Ajoutez un dépôt R pour votre image de conteneur Linux OS, et installez les packages R via le gestionnaire de packages R Linux (voir l'index des packages R Software).

Lorsque vous utilisez l'une de ces options, vous devez définir la variable d'environnement R_HOME pour qu'elle pointe vers votre environnement R personnalisé. Exception: Si vous utilisez Conda à la fois pour gérer votre environnement R et personnaliser votre environnement Python, vous n'avez pas besoin de définir la variable d'environnement R_HOME. Elle est automatiquement définie en fonction de la variable d'environnement PYSPARK_PYTHON.

Exemple de création d'image de conteneur personnalisé

Dockerfile

# Debian 11 is recommended.
FROM debian:11-slim

# Suppress interactive prompts
ENV DEBIAN_FRONTEND=noninteractive

# (Required) Install utilities required by Spark scripts.
RUN apt update && apt install -y procps tini libjemalloc2

# (Optiona) Install utilities required by XGBoost for Spark.
RUN apt install -y procps libgomp1

# Enable jemalloc2 as default memory allocator
ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2

# (Optional) Add extra jars.
ENV SPARK_EXTRA_JARS_DIR=/opt/spark/jars/
ENV SPARK_EXTRA_CLASSPATH='/opt/spark/jars/*'
RUN mkdir -p "${SPARK_EXTRA_JARS_DIR}"
COPY spark-bigquery-with-dependencies_2.12-0.22.2.jar "${SPARK_EXTRA_JARS_DIR}"

# (Optional) Install and configure Miniconda3.
ENV CONDA_HOME=/opt/miniconda3
ENV PYSPARK_PYTHON=${CONDA_HOME}/bin/python
ENV PATH=${CONDA_HOME}/bin:${PATH}
COPY Miniconda3-py39_4.10.3-Linux-x86_64.sh .
RUN bash Miniconda3-py39_4.10.3-Linux-x86_64.sh -b -p /opt/miniconda3 \
  && ${CONDA_HOME}/bin/conda config --system --set always_yes True \
  && ${CONDA_HOME}/bin/conda config --system --set auto_update_conda False \
  && ${CONDA_HOME}/bin/conda config --system --prepend channels conda-forge \
  && ${CONDA_HOME}/bin/conda config --system --set channel_priority strict
# Packages ipython and ipykernel are required if using custom conda and want to
# use this container for running notebooks.
RUN ${CONDA_HOME}/bin/conda install ipython ipykernel

# (Optional) Install Conda packages.
#
# The following packages are installed in the default image, it is strongly
# recommended to include all of them.
#
# Use mamba to install packages quickly.
RUN ${CONDA_HOME}/bin/conda install mamba -n base -c conda-forge \
    && ${CONDA_HOME}/bin/mamba install \
      conda \
      cython \
      fastavro \
      fastparquet \
      gcsfs \
      google-cloud-bigquery-storage \
      google-cloud-bigquery[pandas] \
      google-cloud-bigtable \
      google-cloud-container \
      google-cloud-datacatalog \
      google-cloud-dataproc \
      google-cloud-datastore \
      google-cloud-language \
      google-cloud-logging \
      google-cloud-monitoring \
      google-cloud-pubsub \
      google-cloud-redis \
      google-cloud-spanner \
      google-cloud-speech \
      google-cloud-storage \
      google-cloud-texttospeech \
      google-cloud-translate \
      google-cloud-vision \
      koalas \
      matplotlib \
      nltk \
      numba \
      numpy \
      openblas \
      orc \
      pandas \
      pyarrow \
      pysal \
      pytables \
      python \
      regex \
      requests \
      rtree \
      scikit-image \
      scikit-learn \
      scipy \
      seaborn \
      sqlalchemy \
      sympy \
      virtualenv

# (Optional) Add extra Python modules.
ENV PYTHONPATH=/opt/python/packages
RUN mkdir -p "${PYTHONPATH}"
COPY test_util.py "${PYTHONPATH}"

# (Optional) Install R and R libraries.
RUN apt update \
  && apt install -y gnupg \
  && apt-key adv --no-tty \
      --keyserver "hkp://keyserver.ubuntu.com:80" \
      --recv-keys E19F5F87128899B192B1A2C2AD5F960A256A04AF \
  && echo "deb http://cloud.r-project.org/bin/linux/debian bullseye-cran40/" \
      >/etc/apt/sources.list.d/cran-r.list \
  && apt update \
  && apt install -y \
      libopenblas-base \
      libssl-dev \
      r-base \
      r-base-dev \
      r-recommended \
      r-cran-blob

ENV R_HOME=/usr/lib/R

# (Required) Create the 'spark' group/user.
# The GID and UID must be 1099. Home directory is required.
RUN groupadd -g 1099 spark
RUN useradd -u 1099 -g 1099 -d /home/spark -m spark
USER spark

Commandes de compilation

Exécutez-le dans le répertoire Dockerfile.

IMAGE=gcr.io/my-project/my-image:1.0.1

# Download the BigQuery connector.
gsutil cp \
  gs://spark-lib/bigquery/spark-bigquery-with-dependencies_2.12-0.22.2.jar .

# Download the Miniconda3 installer.
wget https://repo.anaconda.com/miniconda/Miniconda3-py39_4.10.3-Linux-x86_64.sh

# Python module example
cat >test_util.py <<EOF
def hello(name):
  print("hello {}".format(name))

def read_lines(path):
  with open(path) as f:
    return f.readlines()
EOF

# Build and push the image.
docker build -t "${IMAGE}" .
docker push "${IMAGE}"