A execução de jobs do Spark em um cluster do Container Service for Kubernetes (ACK) gera um grande volume de logs distribuídos por diferentes pods, o que dificulta o gerenciamento. O Simple Log Service (SLS) oferece uma solução completa para coleta, processamento, consulta, análise, visualização e alertas de logs. Este tópico explica como usar o Simple Log Service (SLS) para gerenciar com eficiência os logs de jobs do Spark executados em um cluster ACK.
Pré-requisitos
O add-on ack-spark-operator está implantado.
Um Project do Simple Log Service (SLS) foi criado. Para mais informações, consulte Gerenciar um Project.
O componente Logtail foi instalado.
Visão geral
Este tópico orienta você nas etapas necessárias para configurar o SLS e gerenciar os logs de sistema e de aplicação gerados pelos seus jobs do Spark.
Crie uma imagem de contêiner do Spark que inclua a dependência do layout de modelo JSON do log4j e envie a imagem para o seu registro de imagens de contêiner.
Crie um ConfigMap para configurar o Log4j2, definindo o nível de log como INFO e o formato de saída como JSONL.
Crie um recurso AliyunLogConfig. O Simple Log Service criará automaticamente a configuração do Logtail correspondente no Logstore especificado para coletar logs dos jobs do Spark enviados pelo operador Spark.
Crie e execute um job de exemplo do Spark, verifique se os logs do pod estão no formato JSONL e revise as definições dos campos principais.
Faça login no console do Simple Log Service para consultar e analisar os logs do job do Spark dentro de um intervalo de tempo específico.
(Opcional) Limpeza: após concluir os testes, exclua os jobs do Spark e outros recursos criados para evitar custos adicionais.
Etapa 1: Criar uma imagem de contêiner do Spark
Crie o seguinte Dockerfile (este exemplo usa o Spark 3.5.3) e adicione a dependência necessária ao classpath do Spark. Após concluir a compilação, envie a imagem para o seu registro de imagens de contêiner. Para simplificar a coleta e a análise de logs, utilizaremos o formato JSONL para a saída.
ARG SPARK_IMAGE=<SPARK_IMAGE> # Replace <SPARK_IMAGE> with your Spark base image.
FROM ${SPARK_IMAGE}
# Add dependency for log4j-layout-template-json
ADD --chown=spark:spark --chmod=644 https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-layout-template-json/2.24.1/log4j-layout-template-json-2.24.1.jar ${SPARK_HOME}/jars
Etapa 2: Configurar logs do Log4j2
Crie um arquivo chamado spark-log-conf.yaml com o conteúdo abaixo. Esta configuração define o nível de log como INFO e o formato de saída como JSONL. Ela utiliza o Elastic Common Schema (ECS), um formato de log padronizado, como modelo. Para mais detalhes sobre a configuração, consulte Coletar logs do Log4j.
apiVersion: v1
kind: ConfigMap
metadata:
name: spark-log-conf
namespace: default
data:
log4j2.properties: |
# Set everything to be logged to the console and file
rootLogger.level = info
rootLogger.appenderRefs = console, file
rootLogger.appenderRef.console.ref = STDOUT
rootLogger.appenderRef.file.ref = FileAppender
appender.console.name = STDOUT
appender.console.type = Console
appender.console.layout.type = JsonTemplateLayout
appender.console.layout.eventTemplateUri = classpath:EcsLayout.json
appender.file.name = FileAppender
appender.file.type = File
appender.file.fileName = /opt/spark/logs/spark.log
appender.file.layout.type = JsonTemplateLayout
appender.file.layout.eventTemplateUri = classpath:EcsLayout.json
Execute o comando a seguir para criar o recurso ConfigMap.
kubectl apply -f spark-log-conf.yaml
Saída esperada:
configmap/spark-log-conf created
Etapa 3: Criar uma configuração do Logtail
Crie um arquivo de manifesto AliyunLogConfig chamado aliyun-log-config.yaml com o conteúdo a seguir. Substitua <SLS_PROJECT> pelo nome do seu Project do SLS e <SLS_LOGSTORE> pelo nome do seu Logstore. Para mais informações sobre as opções de configuração, consulte Gerenciar configurações do Logtail usando AliyunLogConfig.
apiVersion: log.alibabacloud.com/v1alpha1
kind: AliyunLogConfig
metadata:
name: spark
namespace: default
spec:
# (Optional) The name of the destination Project. Default value: k8s-log-<Your_Cluster_ID>.
project: <SLS_PROJECT>
# The name of the Logstore. If the specified Logstore does not exist, Simple Log Service automatically creates it.
logstore: <SLS_LOGSTORE>
# The Logtail configuration.
logtailConfig:
# The name of the Logtail configuration.
configName: spark
# The type of the data source. `file` indicates text logs.
inputType: file
# The configurations for log input.
inputDetail:
# The directory where the log files are located.
logPath: /opt/spark/logs
# The name of the log file. Wildcard characters are supported.
filePattern: '*.log'
# The encoding of the log file.
fileEncoding: utf8
# The log type.
logType: json_log
localStorage: true
key:
- content
logBeginRegex: .*
logTimezone: ''
discardNonUtf8: false
discardUnmatch: true
preserve: true
preserveDepth: 0
regex: (.*)
outputType: LogService
topicFormat: none
adjustTimezone: false
enableRawLog: false
# Collect text logs from containers.
dockerFile: true
# Advanced configurations.
advanced:
# Preview container metadata.
collect_containers_flag: true
# Kubernetes collection configuration.
k8s:
# Filter pods by label.
IncludeK8sLabel:
sparkoperator.k8s.io/launched-by-spark-operator: "true"
# Filter containers by name.
K8sContainerRegex: "^spark-kubernetes-(driver|executor)$"
# Additional log tag configurations.
ExternalK8sLabelTag:
spark-app-name: spark-app-name
spark-version: spark-version
spark-role: spark-role
spark-app-selector: spark-app-selector
sparkoperator.k8s.io/submission-id: sparkoperator.k8s.io/submission-id
# Log processing plug-ins.
plugin:
processors:
# Log splitting.
- type: processor_split_log_string
detail:
SplitKey: content
SplitSep: ''
# JSON field parsing.
- type: processor_json
detail:
ExpandArray: false
ExpandConnector: ''
ExpandDepth: 0
IgnoreFirstConnector: false
SourceKey: content
KeepSource: false
KeepSourceIfParseError: true
NoKeyError: false
UseSourceKeyAsPrefix: false
# Log timestamp extraction.
- type: processor_strptime
detail:
SourceKey: '@timestamp'
Format: '%Y-%m-%dT%H:%M:%S.%fZ'
KeepSource: false
AdjustUTCOffset: true
UTCOffset: 0
AlarmIfFail: false
Execute o comando a seguir para criar a configuração do Logtail.
kubectl apply -f aliyun-log-config.yaml
Siga estas etapas para visualizar o novo Logstore e a configuração do Logtail:
Faça login no console do Simple Log Service.
-
Na seção Projects, clique em no projeto desejado.

-
Na aba , clique em no ícone > à frente do Logstore de destino e escolha .
Na página Logtail configuration, uma lista das configurações existentes é exibida. A lista inclui uma configuração chamada spark, associada a um grupo de máquinas.
Clique em na configuração do Logtail desejada para visualizar seus detalhes.
Etapa 4: Enviar um job de exemplo do Spark
Crie um arquivo de manifesto SparkApplication chamado spark-pi.yaml com o seguinte conteúdo.
apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
name: spark-pi
namespace: default
spec:
type: Scala
mode: cluster
image: <SPARK_IMAGE>
mainClass: org.apache.spark.examples.SparkPi
mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.12-3.5.3.jar
arguments:
- "5000"
sparkVersion: 3.5.3
sparkConfigMap: spark-log-conf
driver:
cores: 1
memory: 512m
serviceAccount: spark-operator-spark
executor:
instances: 1
cores: 1
memory: 4g
Execute o comando a seguir para enviar o job.
kubectl apply -f spark-pi.yaml
Após a conclusão do job, visualize as últimas 10 linhas do log do pod driver.
kubectl logs --tail=10 spark-pi-driver
Saída esperada:
{"@timestamp":"2024-11-20T11:45:48.487Z","ecs.version":"1.2.0","log.level":"WARN","message":"Kubernetes client has been closed.","process.thread.name":"-937428334-pool-19-thread-1","log.logger":"org.apache.spark.scheduler.cluster.k8s.ExecutorPodsWatchSnapshotSource"}
{"@timestamp":"2024-11-20T11:45:48.585Z","ecs.version":"1.2.0","log.level":"INFO","message":"MapOutputTrackerMasterEndpoint stopped!","process.thread.name":"dispatcher-event-loop-7","log.logger":"org.apache.spark.MapOutputTrackerMasterEndpoint"}
{"@timestamp":"2024-11-20T11:45:48.592Z","ecs.version":"1.2.0","log.level":"INFO","message":"MemoryStore cleared","process.thread.name":"main","log.logger":"org.apache.spark.storage.memory.MemoryStore"}
{"@timestamp":"2024-11-20T11:45:48.592Z","ecs.version":"1.2.0","log.level":"INFO","message":"BlockManager stopped","process.thread.name":"main","log.logger":"org.apache.spark.storage.BlockManager"}
{"@timestamp":"2024-11-20T11:45:48.596Z","ecs.version":"1.2.0","log.level":"INFO","message":"BlockManagerMaster stopped","process.thread.name":"main","log.logger":"org.apache.spark.storage.BlockManagerMaster"}
{"@timestamp":"2024-11-20T11:45:48.598Z","ecs.version":"1.2.0","log.level":"INFO","message":"OutputCommitCoordinator stopped!","process.thread.name":"dispatcher-event-loop-1","log.logger":"org.apache.spark.scheduler.OutputCommitCoordinator$OutputCommitCoordinatorEndpoint"}
{"@timestamp":"2024-11-20T11:45:48.602Z","ecs.version":"1.2.0","log.level":"INFO","message":"Successfully stopped SparkContext","process.thread.name":"main","log.logger":"org.apache.spark.SparkContext"}
{"@timestamp":"2024-11-20T11:45:48.604Z","ecs.version":"1.2.0","log.level":"INFO","message":"Shutdown hook called","process.thread.name":"shutdown-hook-0","log.logger":"org.apache.spark.util.ShutdownHookManager"}
{"@timestamp":"2024-11-20T11:45:48.604Z","ecs.version":"1.2.0","log.level":"INFO","message":"Deleting directory /var/data/spark-f783cf2e-44db-452c-83c9-738f9c894ef9/spark-2caa5814-bd32-431c-a9f9-a32208b34fbb","process.thread.name":"shutdown-hook-0","log.logger":"org.apache.spark.util.ShutdownHookManager"}
{"@timestamp":"2024-11-20T11:45:48.606Z","ecs.version":"1.2.0","log.level":"INFO","message":"Deleting directory /tmp/spark-dacdfd95-f166-4b23-9312-af9052730417","process.thread.name":"shutdown-hook-0","log.logger":"org.apache.spark.util.ShutdownHookManager"}
Os logs são impressos no formato JSONL. Os campos são descritos da seguinte forma:
@timestamp: momento em que a entrada de log foi gerada.ecs.version: versão do Elastic Common Schema (ECS). O ECS é um esquema padronizado para dados de log.log.level: nível de severidade do log.message: mensagem registrada no log.process.thread.name: nome da thread que originou a entrada de log.log.logger: nome do logger responsável por registrar a entrada.
Etapa 5: Consultar e analisar logs do Spark
Consulte o guia de início rápido para consulta e análise de logs para especificar o intervalo de execução do job e confirmar se a coleta ocorreu com sucesso.
Na página de consulta do console do Simple Log Service, selecione o Logstore spark, defina um intervalo de tempo e execute a consulta. Na aba Raw Logs, é possível visualizar os logs coletados da aplicação Spark. Entradas como o aviso de nível WARN Unable to load native-hadoop library e registros de nível INFO sobre a versão do Spark (3.5.3), sistema operacional e versão do Java durante a fase de inicialização confirmam que a coleta de logs está funcionando corretamente.
(Opcional) Etapa 6: Limpeza
Caso tenha concluído este tutorial e não precise mais dos recursos, execute os comandos abaixo para excluí-los.
Exclua o job do Spark com o seguinte comando:
kubectl delete -f spark-pi.yaml
Remova a configuração do Logtail executando:
kubectl delete -f aliyun-log-config.yaml
Elimine a configuração de log do Log4j2 através do comando:
kubectl delete -f spark-log-conf.yaml