Usar contenedores personalizados con Google Cloud Serverless para Apache Spark

Google Cloud Serverless para Apache Spark ejecuta cargas de trabajo en contenedores Docker. El contenedor proporciona el entorno de tiempo de ejecución para los procesos del controlador y del ejecutor de la carga de trabajo. De forma predeterminada, Google Cloud Serverless para Apache Spark usa una imagen de contenedor que incluye los paquetes predeterminados de Spark, Java, Python y R asociados a una versión de lanzamiento del entorno de ejecución. La API Google Cloud Serverless para Apache Sparkpor lotes te permite usar una imagen de contenedor personalizada en lugar de la imagen predeterminada. Normalmente, una imagen de contenedor personalizada añade dependencias de Java o Python de la carga de trabajo de Spark que no proporciona la imagen de contenedor predeterminada. Importante: No incluyas Spark en tu imagen de contenedor personalizada. Google Cloud Serverless para Apache Spark montará Spark en el contenedor en el tiempo de ejecución.

Enviar una carga de trabajo por lotes de Spark con una imagen de contenedor personalizada

gcloud

Usa el comando gcloud dataproc batches submit spark con la marca --container-image para especificar tu imagen de contenedor personalizada al enviar una carga de trabajo por lotes de 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

Notas:

  • Custom-image: especifica la imagen de contenedor personalizada con el siguiente formato de nombre de imagen de Container Registry: {hostname}/{project-id}/{image}:{tag}. Por ejemplo, "gcr.io/my-project-id/my-image:1.0.1". Nota: Debes alojar tu imagen de contenedor personalizada en Container Registry o en Artifact Registry. Google Cloud Serverless para Apache Spark no puede obtener contenedores de otros registros.
  • --jars: especifique una ruta a una carga de trabajo de usuario incluida en su imagen de contenedor personalizada o ubicada en Cloud Storage. Por ejemplo, file:///opt/spark/jars/spark-examples.jar o gs://my-bucket/spark/jars/spark-examples.jar.
  • Otras opciones de comandos de lotes: puedes añadir otras marcas de comandos de lotes opcionales, por ejemplo, para usar un servidor de historial persistente (PHS). Nota: El PHS debe estar ubicado en la región en la que ejecutas cargas de trabajo por lotes.
  • Argumentos de carga de trabajo: puedes añadir cualquier argumento de carga de trabajo añadiendo "--" al final del comando, seguido de los argumentos de carga de trabajo.

REST

La imagen de contenedor personalizada se proporciona a través del campo RuntimeConfig.containerImage como parte de una solicitud a la API batches.create.

En el siguiente ejemplo se muestra cómo usar un contenedor personalizado para enviar una carga de trabajo por lotes mediante la API Google Cloud Serverless para Apache Spark batches.create.

Antes de usar los datos de la solicitud, haz las siguientes sustituciones:

  • project-id: ID de proyecto de Google Cloud
  • region: región
  • custom-container-image: especifica la imagen de contenedor personalizada con el siguiente formato de nombre de imagen de Container Registry: {hostname}/{project-id}/{image}:{tag}. Por ejemplo, "gcr.io/my-project-id/my-image:1.0.1". Nota: Debes alojar tu contenedor personalizado en Container Registry o en Artifact Registry . Google Cloud Serverless para Apache Spark no puede obtener contenedores de otros registros.
  • jar-uri: Especifica la ruta a un archivo JAR de carga de trabajo incluido en tu imagen de contenedor personalizada o ubicado en Cloud Storage. Por ejemplo, "/opt/spark/jars/spark-examples.jar" o "gs:///spark/jars/spark-examples.jar".
  • class: nombre completo de una clase del archivo JAR, como "org.apache.spark.examples.SparkPi".
  • Otras opciones: puedes usar otros campos de recursos de carga de trabajo por lotes, por ejemplo, usa el campo sparkBatch.args para transferir argumentos a tu carga de trabajo (consulta la documentación de recursos Batch para obtener más información). Para usar un servidor de historial persistente (PHS), consulta Configurar un servidor de historial persistente. Nota: El PHS debe estar ubicado en la región en la que ejecutas cargas de trabajo por lotes.

Método HTTP y URL:

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

Cuerpo JSON de la solicitud:

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

Para enviar tu solicitud, despliega una de estas opciones:

Deberías recibir una respuesta JSON similar a la siguiente:

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

Crear una imagen de contenedor personalizada

Google Cloud Las imágenes de contenedor personalizadas de Serverless para Apache Spark son imágenes de Docker. Puedes usar las herramientas para crear imágenes Docker y crear imágenes de contenedor personalizadas, pero las imágenes deben cumplir ciertos requisitos para ser compatibles con Google Cloud Serverless para Apache Spark. En las siguientes secciones se explican estas condiciones.

Sistema operativo

Puedes elegir cualquier imagen base del sistema operativo para tu imagen de contenedor personalizada.

Recomendación: Usa las imágenes predeterminadas de Debian 12, como debian:12-slim, ya que se han probado para evitar problemas de compatibilidad.

Servicios públicos

Debes incluir los siguientes paquetes de utilidades, que son necesarios para ejecutar Spark, en tu imagen de contenedor personalizada:

  • procps
  • tini

Para ejecutar XGBoost desde Spark (Java o Scala), debes incluir libgomp1

Usuario de contenedor

Google Cloud Serverless para Apache Spark ejecuta contenedores como el usuario de Linux spark con un 1099 UID y un 1099 GID. Las directivas USER definidas en los archivos Dockerfile de las imágenes de contenedor personalizadas se ignoran en el tiempo de ejecución. Usa el UID y el GID para los permisos del sistema de archivos. Por ejemplo, si añade un archivo JAR en /opt/spark/jars/my-lib.jar en la imagen como dependencia de una carga de trabajo, debe dar al usuario spark permiso de lectura para el archivo.

Streaming de imágenes

Normalmente, Serverless para Apache Spark inicia una carga de trabajo que requiere una imagen de contenedor personalizada descargando toda la imagen en el disco. Esto puede provocar un retraso en el tiempo de inicialización, sobre todo en el caso de los clientes que tienen imágenes de gran tamaño.

En su lugar, puedes usar la transmisión de imágenes, que es un método para extraer datos de imágenes según sea necesario. De esta forma, la carga de trabajo puede iniciarse sin esperar a que se descargue toda la imagen, lo que puede mejorar el tiempo de inicialización. Para habilitar la transmisión de imágenes, debes habilitar la API Container File System. También debe almacenar sus imágenes de contenedor en Artifact Registry, y el repositorio de Artifact Registry debe estar en la misma región que su carga de trabajo de Dataproc o en una multirregión que corresponda a la región en la que se ejecuta su carga de trabajo. Si Dataproc no admite la imagen o el servicio de streaming de imágenes no está disponible, nuestra implementación de streaming descarga la imagen completa.

Ten en cuenta que no admitimos lo siguiente para el streaming de imágenes:

En estos casos, Dataproc extrae toda la imagen antes de iniciar la carga de trabajo.

Spark

No incluyas Spark en tu imagen de contenedor personalizada. En el tiempo de ejecución, Google Cloud Serverless para Apache Spark monta los archivos binarios y las configuraciones de Spark del host en el contenedor: los archivos binarios se montan en el directorio /usr/lib/spark y las configuraciones se montan en el directorio /etc/spark/conf. Los archivos que ya estén en estos directorios se sobrescribirán con Google Cloud Serverless para Apache Spark en el tiempo de ejecución.

Java Runtime Environment

No incluyas tu propio Java Runtime Environment (JRE) en tu imagen de contenedor personalizado. En el tiempo de ejecución, Google Cloud Serverless para Apache Spark monta OpenJDK desde el host en el contenedor. Si incluye un JRE en su imagen de contenedor personalizada, se ignorará.

Paquetes de Java

Puedes incluir archivos JAR como dependencias de cargas de trabajo de Spark en tu imagen de contenedor personalizada y definir la variable de entorno SPARK_EXTRA_CLASSPATH para incluir los archivos JAR. Google Cloud Serverless para Apache Spark añadirá el valor de la variable de entorno a la ruta de clases de los procesos de JVM de Spark. Recomendación: Coloca los archivos JAR en el directorio /opt/spark/jars y asigna el valor SPARK_EXTRA_CLASSPATH a /opt/spark/jars/*.

Puedes incluir el archivo JAR de la carga de trabajo en tu imagen de contenedor personalizada y, a continuación, hacer referencia a él con una ruta local al enviar la carga de trabajo. Por ejemplo, file:///opt/spark/jars/my-spark-job.jar (consulta Enviar una carga de trabajo por lotes de Spark con una imagen de contenedor personalizada para ver un ejemplo).

Paquetes de Python

De forma predeterminada, Google Cloud Serverless para Apache Spark monta el entorno de compilación de Conda mediante un repositorio de Conda-Forge de software libre del host en el directorio /opt/dataproc/conda del contenedor en el tiempo de ejecución. PYSPARK_PYTHON está configurado en /opt/dataproc/conda/bin/python. Su directorio base, /opt/dataproc/conda/bin, se incluye en PATH.

Puedes incluir tu entorno de Python con paquetes en otro directorio de tu imagen de contenedor personalizada, por ejemplo, en /opt/conda, y definir la variable de entorno PYSPARK_PYTHON en /opt/conda/bin/python.

Tu imagen de contenedor personalizada puede incluir otros módulos de Python que no formen parte del entorno de Python, como secuencias de comandos de Python con funciones de utilidad. Define la variable de entorno PYTHONPATH para que incluya los directorios en los que se encuentran los módulos.

Entorno de R

Puedes personalizar el entorno de R en tu imagen de contenedor personalizada con una de las siguientes opciones:

  • Usa Conda para gestionar e instalar paquetes de R desde el canal conda-forge.
  • Añade un repositorio de R para el SO Linux de tu imagen de contenedor e instala paquetes de R con el gestor de paquetes del SO Linux (consulta el índice de paquetes de software de R).

Cuando uses cualquiera de las dos opciones, debes definir la variable de entorno R_HOME para que apunte a tu entorno de R personalizado. Excepción: Si usas Conda para gestionar tu entorno de R y personalizar tu entorno de Python, no tienes que definir la variable de entorno R_HOME. Se define automáticamente en función de la variable de entorno PYSPARK_PYTHON.

Ejemplo de compilación de imagen de contenedor personalizado

En esta sección se incluyen ejemplos de compilación de imágenes de contenedor personalizadas, que incluyen Dockerfiles de muestra, seguidos de un comando de compilación. Un ejemplo incluye la configuración mínima necesaria para crear una imagen. La otra muestra incluye ejemplos de configuración adicional, como bibliotecas de Python y R.

Configuración mínima

# Recommendation: Use Debian 12.
FROM debian:12-slim

# Suppress interactive prompts
ENV DEBIAN_FRONTEND=noninteractive

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

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

# 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
    

Configuración adicional

# Recommendation: Use Debian 12.
FROM debian:12-slim

# Suppress interactive prompts
ENV DEBIAN_FRONTEND=noninteractive

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

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

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

# Install and configure Miniconda3.
ENV CONDA_HOME=/opt/miniforge3
ENV PYSPARK_PYTHON=${CONDA_HOME}/bin/python
ENV PATH=${CONDA_HOME}/bin:${PATH}
ADD https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-x86_64.sh .
RUN bash Miniforge3-Linux-x86_64.sh -b -p /opt/miniforge3 \
  && ${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 --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/mamba install ipython ipykernel

#Install Google Cloud SDK.
RUN ${CONDA_HOME}/bin/mamba install -n base google-cloud-sdk

# Install Conda packages.
#
# The following packages are installed in the default image.
# Recommendation: include all packages.
#
# Use mamba to quickly install packages.
RUN ${CONDA_HOME}/bin/mamba install -n base \
    accelerate \
    bigframes \
    cython \
    deepspeed \
    evaluate \
    fastavro \
    fastparquet \
    gcsfs \
    google-cloud-aiplatform \
    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 \
    langchain \
    lightgbm \
    koalas \
    matplotlib \
    mlflow \
    nltk \
    numba \
    numpy \
    openblas \
    orc \
    pandas \
    pyarrow \
    pynvml \
    pysal \
    pytables \
    python \
    pytorch-cpu \
    regex \
    requests \
    rtree \
    scikit-image \
    scikit-learn \
    scipy \
    seaborn \
    sentence-transformers \
    sqlalchemy \
    sympy \
    tokenizers \
    transformers \
    virtualenv \
    xgboost

# Install pip packages.
RUN ${PYSPARK_PYTHON} -m pip install \
    spark-tensorflow-distributor \
    torcheval

# Install R and R libraries.
RUN ${CONDA_HOME}/bin/mamba install -n base \ 
    r-askpass \
    r-assertthat \
    r-backports \
    r-bit \
    r-bit64 \
    r-blob \
    r-boot \
    r-brew \
    r-broom \
    r-callr \
    r-caret \
    r-cellranger \
    r-chron \
    r-class \
    r-cli \
    r-clipr \
    r-cluster \
    r-codetools \
    r-colorspace \
    r-commonmark \
    r-cpp11 \
    r-crayon \
    r-curl \
    r-data.table \
    r-dbi \
    r-dbplyr \
    r-desc \
    r-devtools \
    r-digest \
    r-dplyr \
    r-ellipsis \
    r-evaluate \
    r-fansi \
    r-fastmap \
    r-forcats \
    r-foreach \
    r-foreign \
    r-fs \
    r-future \
    r-generics \
    r-ggplot2 \
    r-gh \
    r-glmnet \
    r-globals \
    r-glue \
    r-gower \
    r-gtable \
    r-haven \
    r-highr \
    r-hms \
    r-htmltools \
    r-htmlwidgets \
    r-httpuv \
    r-httr \
    r-hwriter \
    r-ini \
    r-ipred \
    r-isoband \
    r-iterators \
    r-jsonlite \
    r-kernsmooth \
    r-knitr \
    r-labeling \
    r-later \
    r-lattice \
    r-lava \
    r-lifecycle \
    r-listenv \
    r-lubridate \
    r-magrittr \
    r-markdown \
    r-mass \
    r-matrix \
    r-memoise \
    r-mgcv \
    r-mime \
    r-modelmetrics \
    r-modelr \
    r-munsell \
    r-nlme \
    r-nnet \
    r-numderiv \
    r-openssl \
    r-pillar \
    r-pkgbuild \
    r-pkgconfig \
    r-pkgload \
    r-plogr \
    r-plyr \
    r-praise \
    r-prettyunits \
    r-processx \
    r-prodlim \
    r-progress \
    r-promises \
    r-proto \
    r-ps \
    r-purrr \
    r-r6 \
    r-randomforest \
    r-rappdirs \
    r-rcmdcheck \
    r-rcolorbrewer \
    r-rcpp \
    r-rcurl \
    r-readr \
    r-readxl \
    r-recipes \
    r-recommended \
    r-rematch \
    r-remotes \
    r-reprex \
    r-reshape2 \
    r-rlang \
    r-rmarkdown \
    r-rodbc \
    r-roxygen2 \
    r-rpart \
    r-rprojroot \
    r-rserve \
    r-rsqlite \
    r-rstudioapi \
    r-rvest \
    r-scales \
    r-selectr \
    r-sessioninfo \
    r-shape \
    r-shiny \
    r-sourcetools \
    r-spatial \
    r-squarem \
    r-stringi \
    r-stringr \
    r-survival \
    r-sys \
    r-teachingdemos \
    r-testthat \
    r-tibble \
    r-tidyr \
    r-tidyselect \
    r-tidyverse \
    r-timedate \
    r-tinytex \
    r-usethis \
    r-utf8 \
    r-uuid \
    r-vctrs \
    r-whisker \
    r-withr \
    r-xfun \
    r-xml2 \
    r-xopen \
    r-xtable \
    r-yaml \
    r-zip

ENV R_HOME=/usr/lib/R

# Add extra Python modules.
ENV PYTHONPATH=/opt/python/packages
RUN mkdir -p "${PYTHONPATH}"

# 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}"

#Uncomment below and replace EXTRA_JAR_NAME with the jar file name.
#COPY "EXTRA_JAR_NAME" "${SPARK_EXTRA_JARS_DIR}"

# 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
      

Comando de compilación

Ejecuta el siguiente comando en el directorio de Dockerfile para crear y enviar la imagen personalizada a Artifact Registry.

# Build and push the image.
gcloud builds submit --region=REGION \
    --tag REGION-docker.pkg.dev/PROJECT/REPOSITORY/IMAGE_NAME:IMAGE_VERSION