Uso de modelos de pod
A partir das versões 5.33.0 ou 6.3.0 do Amazon EMR, o Amazon EMR no EKS oferece suporte ao recurso de modelo de pod do Spark. Um pod corresponde a um grupo de um ou mais contêineres, com recursos de rede e armazenamento compartilhados, e uma especificação de como executar os contêineres. Os modelos de pod são especificações que determinam como ocorrerá a execução de cada pod. É possível usar arquivos de modelo de pod para definir as configurações de pods de drivers ou de executores para as quais as configurações do Spark não oferecem suporte. Para obter mais informações sobre o recurso de modelo de pod do Spark, consulte Pod Template
nota
O recurso de modelo de pod funciona somente com pods de drivers e de executores. Não é possível configurar pods de emissores de trabalhos usando o modelo de pods.
Cenários comuns
Você pode definir como executar trabalhos do Spark em clusters do EKS compartilhados ao usar modelos de pod com o Amazon EMR no EKS, e economizar custos e aprimorar a utilização e a performance dos recursos.
-
Para reduzir custos, você pode programar tarefas do driver do Spark para a execução em instâncias sob demanda do Amazon EC2 enquanto programa tarefas do executor do Spark para a execução em instâncias spot do Amazon EC2.
-
Para aumentar a utilização de recursos, você pode oferecer suporte a diversas equipes que executam as workloads no mesmo cluster do EKS. Cada equipe receberá um grupo de nós do Amazon EC2 designado para executar as workloads. Você pode usar os modelos de pod para aplicar uma tolerância correspondente à workload.
-
Para aprimorar o monitoramento, você pode executar um contêiner de registro em log separado para encaminhar os logs para a aplicação de monitoramento existente.
Por exemplo, o arquivo de modelo de pod a seguir demonstra um cenário de uso comum.
apiVersion: v1 kind: Pod spec: volumes: - name: source-data-volume emptyDir: {} - name: metrics-files-volume emptyDir: {} nodeSelector: eks.amazonaws.com/nodegroup: emr-containers-nodegroup containers: - name: spark-kubernetes-driver # This will be interpreted as driver Spark main container env: - name: RANDOM value: "random" volumeMounts: - name: shared-volume mountPath: /var/data - name: metrics-files-volume mountPath: /var/metrics/data - name: custom-side-car-container # Sidecar container image: <side_car_container_image> env: - name: RANDOM_SIDECAR value: random volumeMounts: - name: metrics-files-volume mountPath: /var/metrics/data command: - /bin/sh - '-c' - <command-to-upload-metrics-files> initContainers: - name: spark-init-container-driver # Init container image: <spark-pre-step-image> volumeMounts: - name: source-data-volume # Use EMR predefined volumes mountPath: /var/data command: - /bin/sh - '-c' - <command-to-download-dependency-jars>
O modelo de pod conclui as seguintes tarefas:
-
Adiciona um novo contêiner Init
que é executado antes do início do contêiner principal do Spark. O contêiner Init compartilha o volume EmptyDir chamado source-data-volume
com o contêiner principal do Spark. Você pode fazer com que seu contêiner Init execute as etapas de inicialização, como fazer download de dependências ou gerar dados de entrada. Em seguida, o contêiner principal do Spark consumirá os dados. -
Adiciona outro contêiner sidecar
que é executado junto com o contêiner principal do Spark. Os dois contêineres estão compartilhando outro volume EmptyDir
chamadometrics-files-volume
. Seu trabalho do Spark pode gerar métricas, como as métricas do Prometheus. Em seguida, o trabalho do Spark pode colocar as métricas em um arquivo e fazer com que o contêiner sidecar carregue os arquivos em seu próprio sistema de BI para análise futura. -
Adiciona uma nova variável de ambiente ao contêiner principal do Spark. Você pode fazer com que seu trabalho consuma a variável de ambiente.
-
Defina um seletor de nó
, para que o pod seja programado somente no grupo de nós emr-containers-nodegroup
. Isso ajuda a isolar os recursos de computação entre trabalhos e equipes.
Habilitação de modelos de pod com o Amazon EMR no EKS
Para habilitar o recurso de modelo de pod com o Amazon EMR no EKS, configure as propriedades do Spark spark.kubernetes.driver.podTemplateFile
e spark.kubernetes.executor.podTemplateFile
para direcionar aos arquivos de modelo de pod no Amazon S3. O Spark fará download do arquivo de modelo de pod e o usará para estruturar pods de drivers e de executores.
nota
O Spark usa o perfil de execução de trabalho para carregar o modelo de pod, portanto, o perfil de execução de trabalho deve ter permissões para acessar o Amazon S3 com a finalidade de carregar os modelos de pod. Para ter mais informações, consulte Criação de um perfil de execução de trabalho.
Você pode usar SparkSubmitParameters
para especificar o caminho do Amazon S3 para o modelo de pod, como demonstra o arquivo JSON de execução de trabalho a seguir.
{ "name": "myjob", "virtualClusterId": "123456", "executionRoleArn": "iam_role_name_for_job_execution", "releaseLabel": "
release_label
", "jobDriver": { "sparkSubmitJobDriver": { "entryPoint": "entryPoint_location", "entryPointArguments": ["argument1
", "argument2
", ...], "sparkSubmitParameters": "--class <main_class> \ --conf spark.kubernetes.driver.podTemplateFile=s3://path_to_driver_pod_template
\ --conf spark.kubernetes.executor.podTemplateFile=s3://path_to_executor_pod_template
\ --conf spark.executor.instances=2 \ --conf spark.executor.memory=2G \ --conf spark.executor.cores=2 \ --conf spark.driver.cores=1" } } }
Como alternativa, você pode usar configurationOverrides
para especificar o caminho do Amazon S3 para o modelo de pod, como demonstra o arquivo JSON de execução de trabalho a seguir.
{ "name": "myjob", "virtualClusterId": "123456", "executionRoleArn": "iam_role_name_for_job_execution", "releaseLabel": "
release_label
", "jobDriver": { "sparkSubmitJobDriver": { "entryPoint": "entryPoint_location", "entryPointArguments": ["argument1
", "argument2
", ...], "sparkSubmitParameters": "--class <main_class> \ --conf spark.executor.instances=2 \ --conf spark.executor.memory=2G \ --conf spark.executor.cores=2 \ --conf spark.driver.cores=1" } }, "configurationOverrides": { "applicationConfiguration": [ { "classification": "spark-defaults", "properties": { "spark.driver.memory":"2G", "spark.kubernetes.driver.podTemplateFile":"s3://path_to_driver_pod_template
", "spark.kubernetes.executor.podTemplateFile":"s3://path_to_executor_pod_template
" } } ] } }
nota
-
Você precisa seguir as diretrizes de segurança ao usar o recurso de modelo de pod com o Amazon EMR no EKS, por exemplo, como isolar códigos de aplicações não confiáveis. Para ter mais informações, consulte Práticas recomendadas de segurança para o Amazon EMR no EKS.
-
Não é possível alterar os nomes dos contêineres principais do Spark ao usar
spark.kubernetes.driver.podTemplateContainerName
espark.kubernetes.executor.podTemplateContainerName
, porque esses nomes são codificados comospark-kubernetes-driver
espark-kubernetes-executors
. Se desejar personalizar o contêiner principal do Spark, deverá especificar o contêiner em um modelo de pod com esses nomes codificados.
Campos do modelo de pod
Considere as restrições para campos apresentadas a seguir ao configurar um modelo de pod com o Amazon EMR no EKS.
-
O Amazon EMR no EKS permite somente os campos a seguir em um modelo de pod para habilitar a programação adequada de trabalhos.
Estes são os campos permitidos em nível de pod:
-
apiVersion
-
kind
-
metadata
-
spec.activeDeadlineSeconds
-
spec.affinity
-
spec.containers
-
spec.enableServiceLinks
-
spec.ephemeralContainers
-
spec.hostAliases
-
spec.hostname
-
spec.imagePullSecrets
-
spec.initContainers
-
spec.nodeName
-
spec.nodeSelector
-
spec.overhead
-
spec.preemptionPolicy
-
spec.priority
-
spec.priorityClassName
-
spec.readinessGates
-
spec.runtimeClassName
-
spec.schedulerName
-
spec.subdomain
-
spec.terminationGracePeriodSeconds
-
spec.tolerations
-
spec.topologySpreadConstraints
-
spec.volumes
Estes são os campos permitidos em nível de contêiner principal do Spark:
-
env
-
envFrom
-
name
-
lifecycle
-
livenessProbe
-
readinessProbe
-
resources
-
startupProbe
-
stdin
-
stdinOnce
-
terminationMessagePath
-
terminationMessagePolicy
-
tty
-
volumeDevices
-
volumeMounts
-
workingDir
Quando você usa qualquer campo não permitido no modelo de pod, o Spark gera uma exceção e o trabalho falha. O exemplo a seguir mostra uma mensagem de erro no log do controlador do Spark devido ao uso de campos não permitidos.
Executor pod template validation failed. Field container.command in Spark main container not allowed but specified.
-
-
O Amazon EMR no EKS define previamente os parâmetros apresentados a seguir em um modelo de pod. Os campos especificados em um modelo de pod não devem se sobrepor a esses campos.
Estes são os nomes de volumes definidos previamente:
-
emr-container-communicate
-
config-volume
-
emr-container-application-log-dir
-
emr-container-event-log-dir
-
temp-data-dir
-
mnt-dir
-
home-dir
-
emr-container-s3
Estas são as montagens de volume definidas previamente que se aplicam somente ao contêiner principal do Spark:
-
Nome:
emr-container-communicate
; MountPath:/var/log/fluentd
-
Nome:
emr-container-application-log-dir
; MountPath:/var/log/spark/user
-
Nome:
emr-container-event-log-dir
; MountPath:/var/log/spark/apps
-
Nome:
mnt-dir
; MountPath:/mnt
-
Nome:
temp-data-dir
; MountPath:/tmp
-
Nome:
home-dir
; MountPath:/home/hadoop
Estas são as variáveis de ambiente definidas previamente que se aplicam somente ao contêiner principal do Spark:
-
SPARK_CONTAINER_ID
-
K8S_SPARK_LOG_URL_STDERR
-
K8S_SPARK_LOG_URL_STDOUT
-
SIDECAR_SIGNAL_FILE
nota
Você ainda pode usar esses volumes definidos previamente e montá-los em seus contêineres sidecar adicionais. Por exemplo, é possível usar
emr-container-application-log-dir
e montá-lo em seu próprio contêiner sidecar definido no modelo de pod.Se os campos especificados entrarem em conflito com qualquer um dos campos definidos previamente no modelo de pod, o Spark gerará uma exceção e o trabalho falhará. O exemplo a seguir mostra uma mensagem de erro no log da aplicação do Spark devido a conflitos com os campos definidos previamente.
Defined volume mount path on main container must not overlap with reserved mount paths: [<reserved-paths>]
-
Considerações sobre contêineres sidecar
O Amazon EMR controla o ciclo de vida dos pods provisionados pelo Amazon EMR no EKS. Os contêineres sidecar devem seguir um ciclo de vida semelhante ao do contêiner principal do Spark. Se você injetar contêineres sidecar adicionais em seus pods, recomendamos a integração com o gerenciamento do ciclo de vida do pod definido pelo Amazon EMR para que o contêiner sidecar possa ser interrompido sozinho quando o contêiner principal do Spark sair.
Para reduzir custos, recomendamos implementar um processo que impeça que pods de drivers com contêineres sidecar continuem em execução após a conclusão do seu trabalho. O driver do Spark exclui os pods de executores quando o executor é encerrado. No entanto, quando um programa de driver é concluído, os contêineres sidecar adicionais continuam em execução. O pod é faturado até que o Amazon EMR no EKS limpe o pod do driver. Geralmente, isso ocorre menos de um minuto após a conclusão do contêiner principal do driver do Spark. Para reduzir custos, você pode integrar os contêineres sidecar adicionais ao mecanismo de gerenciamento do ciclo de vida que o Amazon EMR no EKS define para os pods de drivers e de executores, conforme descrito na seção a seguir.
O contêiner principal do Spark nos pods de drivers e de executores envia heartbeat
para um arquivo /var/log/fluentd/main-container-terminated
a cada dois segundos. Ao adicionar a montagem de volume emr-container-communicate
definida previamente do Amazon EMR ao contêiner sidecar, você pode definir um subprocesso do contêiner sidecar para monitorar periodicamente o horário da última modificação desse arquivo. O subprocesso é interrompido sozinho caso descubra que o contêiner principal do Spark interrompeu a heartbeat
por um período mais longo.
O exemplo apresentado a seguir demonstra um subprocesso que rastreia o arquivo de pulsação e é interrompido sozinho. Substitua your_volume_mount
pelo caminho no qual você monta o volume definido previamente. O script é empacotado dentro da imagem usada pelo contêiner sidecar. Em um arquivo de modelo de pod, você pode especificar um contêiner sidecar com os seguintes comandos: sub_process_script.sh
e main_command
.
MOUNT_PATH="
your_volume_mount
" FILE_TO_WATCH="$MOUNT_PATH/main-container-terminated" INITIAL_HEARTBEAT_TIMEOUT_THRESHOLD=60 HEARTBEAT_TIMEOUT_THRESHOLD=15 SLEEP_DURATION=10 function terminate_main_process() { # Stop main process } # Waiting for the first heartbeat sent by Spark main container echo "Waiting for file $FILE_TO_WATCH to appear..." start_wait=$(date +%s) while ! [[ -f "$FILE_TO_WATCH" ]]; do elapsed_wait=$(expr $(date +%s) - $start_wait) if [ "$elapsed_wait" -gt "$INITIAL_HEARTBEAT_TIMEOUT_THRESHOLD" ]; then echo "File $FILE_TO_WATCH not found after $INITIAL_HEARTBEAT_TIMEOUT_THRESHOLD seconds; aborting" terminate_main_process exit 1 fi sleep $SLEEP_DURATION; done; echo "Found file $FILE_TO_WATCH; watching for heartbeats..." while [[ -f "$FILE_TO_WATCH" ]]; do LAST_HEARTBEAT=$(stat -c %Y $FILE_TO_WATCH) ELAPSED_TIME_SINCE_AFTER_HEARTBEAT=$(expr $(date +%s) - $LAST_HEARTBEAT) if [ "$ELAPSED_TIME_SINCE_AFTER_HEARTBEAT" -gt "$HEARTBEAT_TIMEOUT_THRESHOLD" ]; then echo "Last heartbeat to file $FILE_TO_WATCH was more than $HEARTBEAT_TIMEOUT_THRESHOLD seconds ago at $LAST_HEARTBEAT; terminating" terminate_main_process exit 0 fi sleep $SLEEP_DURATION; done; echo "Outside of loop, main-container-terminated file no longer exists" # The file will be deleted once the fluentd container is terminated echo "The file $FILE_TO_WATCH doesn't exist any more;" terminate_main_process exit 0