Saltar para o conteúdo principal
Versão 3.0

Integrações — JupyterLab

ChartVersion3.0.1TypeapplicationAppVersion5.3.0
CompatibilidadeKubernetes1.32+OpenShift4.19+Rancher2.10.x+

Visão geral das integrações

O JupyterHub usa SQLite internamente por padrão; um PostgreSQL externo (tdp-postgresql) pode substituí-lo — consulte Configuração do Jupyter.

Integração com Spark

O chart tdp-jupyter integra-se com o Apache Spark via o mecanismo tdpSparkIntegration. Ao ativar a integração, um ConfigMap (tdp-jupyter-spark-integration) é criado com as configurações de spark-defaults.conf e um script auxiliar jupyter-spark-env.sh.

Os pods de notebooks montam esse ConfigMap em /opt/bitnami/spark/conf e executam o script no postStart, de modo que cada sessão Spark encontra o master correto automaticamente.

Para o usuário final, a decisão principal é simples:

  • usar PySpark local para testes rápidos e desenvolvimento;
  • usar cluster Spark externo quando quiser distribuir processamento;
  • usar Iceberg a partir do notebook apenas depois de a integração Spark e o catálogo Iceberg já estarem configurados no ambiente.

Modos de operação

ModotdpSparkIntegration.enabledValor resolvido para spark.masterUso típico
PySpark localfalselocal[*]Executa Spark dentro do próprio pod do notebook (padrão para desenvolvimento)
Cluster externotruespark://<RELEASE_NAME>-spark-master-svc.<NAMESPACE>.svc.cluster.local:7077Liga a um deployment Spark já existente
tip

O parâmetro spark.master no values.yaml fica vazio por padrão. O template escolhe o valor correto em tempo de renderização com base em tdpSparkIntegration.enabled. É possível fornecer uma URL personalizada se necessário.

Componentes envolvidos

ComponenteFunção
templates/spark-integration-configmap.yamlRenderiza as configurações Spark e o script auxiliar de ambiente
singleuser.extraEnvDefine variáveis de ambiente Spark para cada pod de notebook
singleuser.lifecycleHooks.postStartExecuta jupyter-spark-env.sh antes do JupyterLab iniciar
singleuser.networkPolicy.egressPermite que os pods de notebook alcancem o Spark master e serviços auxiliares

Variáveis de ambiente injetadas nos pods de notebook

SPARK_HOME=/opt/bitnami/spark
PYTHONPATH=/opt/bitnami/spark/python:/opt/bitnami/spark/python/lib/py4j-0.10.9.7-src.zip
SPARK_CONF_DIR=/opt/bitnami/spark/conf
PYSPARK_PYTHON=/opt/conda/envs/py312/bin/python
PYSPARK_DRIVER_PYTHON=/opt/conda/envs/py312/bin/python
SPARK_MASTER_URL=<automático> # local[*] ou spark://... conforme tdpSparkIntegration.enabled
SPARK_DRIVER_PORT=2222
SPARK_BLOCKMANAGER_PORT=7777

Volumes montados nos pods de notebook

CaminhoTipoConteúdo
/opt/bitnami/spark/confConfigMapspark-defaults.conf e scripts auxiliares
/tmp/spark-localemptyDirDados temporários e shuffle do Spark
/tmp/spark-logsemptyDirLogs do driver Spark

Como configurar

Modo 1 — PySpark local (padrão)

Não requer um cluster Spark externo. O Spark é executado dentro do pod do notebook com local[*]:

tdpSparkIntegration:
enabled: false
deploySparkCluster: false
configMap:
sparkConfig:
"spark.master": "" # resolve para local[*]

Teste num notebook:

from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("Local PySpark").getOrCreate()
print(spark.sparkContext.master) # local[*]

Modo 2 — Cluster Spark externo

Liga os notebooks a um deployment Spark já existente no cluster Kubernetes:

tdpSparkIntegration:
enabled: true
deploySparkCluster: false # false = apontar para deployment existente
configMap:
sparkConfig:
"spark.kubernetes.namespace": "<NAMESPACE>" # opcional
"spark.master": "" # resolve para spark://<RELEASE_NAME>-spark-master-svc.<NAMESPACE>:7077
"spark.driver.host": "" # deixe vazio para usar o serviço admin do notebook
"spark.executor.instances": "2"
"spark.executor.memory": "4g"
"spark.executor.cores": "3"

tdp-spark:
spark:
worker:
replicaCount: 2
resources:
limits:
cpu: 4
memory: 6Gi

Certifique-se de que o serviço Spark master é acessível a partir do namespace dos notebooks (ex.: tdp-spark-master-svc.tdp-project.svc.cluster.local:7077).

Considerações sobre NetworkPolicy

  • Os pods de notebook adicionam uma regra de egress que corresponde a qualquer Spark master (app.kubernetes.io/component: master, app.kubernetes.io/name: spark).
  • Se o chart Spark tiver a sua própria NetworkPolicy, permita conexões de entrada a partir do namespace dos notebooks.
  • Para que os Workers Spark consigam se conectar de volta ao driver do notebook, configure também a regra de ingress descrita em Segurança — JupyterLab.

Limitações conhecidas

Cada notebook pode iniciar uma sessão Spark própria, com portas fixas pré-configuradas para o driver (2222) e para o BlockManager (7777).
Se múltiplas sessões Spark forem abertas no mesmo pod — por exemplo, vários kernels ativos — ou se uma sessão anterior não liberou os recursos corretamente, o Spark pode encontrar essas portas já em uso e gerar BindException.
Quando o ambiente usa Spark Connect, definir SPARK_CONNECT_PORT: "0" faz o endpoint escolher uma porta livre aleatória, evitando conflito especificamente na porta padrão 15002; essa configuração não substitui o tratamento das portas do driver e do BlockManager.

tdp-jupyter:
singleuser:
extraEnv:
SPARK_CONNECT_PORT: "0" # porta aleatória, evita conflitos quando vários notebooks rodam ao mesmo tempo

Modo 3 — Cluster Spark integrado (opcional)

Define tdpSparkIntegration.deploySparkCluster: true para instalar o subchart tdp-spark junto com o JupyterHub:

tdpSparkIntegration:
enabled: true
deploySparkCluster: true

Ajuste os valores do subchart tdp-spark conforme necessário.

Uso de Iceberg a partir do Jupyter

O suporte a Iceberg no Jupyter não é uma integração separada do chart tdp-jupyter. Na prática, ele acontece via Spark:

  • o notebook liga-se ao Spark;
  • o Spark precisa conhecer o catálogo Iceberg;
  • o catálogo Iceberg precisa de acesso ao Hive Metastore e ao storage S3/MinIO.

Por isso:

note

Não trate Iceberg como obrigatório para o Jupyter. Ele é apenas um cenário adicional para notebooks que precisem consultar ou manter tabelas Iceberg via Spark.

Uso de Delta Lake a partir do Jupyter

O suporte a Delta Lake no Jupyter também não é uma integração separada do chart tdp-jupyter — assim como o Iceberg, acontece via Spark:

  • o notebook liga-se ao Spark;
  • o bloco deltaLake do chart tdp-spark habilita o suporte, mas não configura sozinho o spark.sparkConf — as propriedades Spark necessárias são fornecidas via customSparkConfig.properties ou spark.sparkConf;
  • diferente do Iceberg, as tabelas Delta Lake não passam pelo Hive Metastore — o acesso é direto aos caminhos no storage S3/MinIO.

Por isso:


Instalar ou atualizar o JupyterHub

Terminal input
helm upgrade --install <RELEASE_NAME> \
oci://registry.tecnisys.com.br/tdp/charts/tdp-jupyter \
-n <NAMESPACE> \
-f values.yaml
Após atualizações

Sempre que modificar ConfigMaps ou variáveis de ambiente, reinicie os pods de usuário (Stop Server → Start Server no JupyterHub) para que as novas configurações entrem em vigor.


Checklist de verificação

  1. Pods em execução

    Terminal input
    kubectl get pods -n <NAMESPACE> | grep jupyter
  2. ConfigMap Spark criado

    Terminal input
    kubectl get configmap tdp-jupyter-spark-integration -n <NAMESPACE> -o yaml
  3. Conectividade de rede (a partir de um pod de notebook)

    Terminal input
    kubectl exec -n <NAMESPACE> <POD_NAME> -- \
    curl -sv tdp-spark-master-svc.<NAMESPACE>.svc.cluster.local:7077

Testar a integração

Notebook de teste incluído no chart

O chart inclui um notebook de teste (tdp-jupyter-spark-test ConfigMap). Para o extrair:

Terminal input
kubectl get configmap tdp-jupyter-spark-test -n <NAMESPACE> \
-o jsonpath='{.data.spark-integration-test\.ipynb}' \
> spark-integration-test.ipynb

Faça upload do notebook pelo JupyterLab e execute cada célula.

Smoke test manual

Execute o seguinte código num notebook para validar a integração:

import os
from pyspark.sql import SparkSession

print("SPARK_HOME:", os.environ.get("SPARK_HOME"))
print("SPARK_MASTER_URL:", os.environ.get("SPARK_MASTER_URL"))

spark = SparkSession.builder.appName("TDP-Jupyter Smoke Test").getOrCreate()
print("Versão Spark:", spark.version)
print("Master activo:", spark.sparkContext.master)

spark.range(5).show()
spark.stop()

Solução de problemas

SintomaCausa provávelAção sugerida
JAVA_GATEWAY_EXITED ou erros Py4JSPARK_HOME/PYTHONPATH mal configuradosVerifique se singleuser.extraEnv usa os caminhos /opt/bitnami/spark
IllegalStateException: Cannot call methods on a stopped SparkContextSpark master inacessível ou NetworkPolicy bloqueando egress/ingressConfirme tdpSparkIntegration.enabled, verifique o serviço Spark, ajuste as NetworkPolicies
Pod do notebook falha na inicialização (ImportError para zmq)PYTHONPATH poluído com site-packages PySparkNão adicione /opt/conda/envs/py312/lib/python3.12/site-packages ao PYTHONPATH
Driver Spark não consegue conectar/comunicarSPARK_DRIVER_HOST não resolvívelDeixe em branco para usar o serviço admin do notebook ou forneça uma entrada DNS acessível
Workers não conseguem alcançar o driver (Connecting to /<ip>:2222 timed out)A NetworkPolicy do pod single-user está bloqueando o ingress dos pods Worker do SparkAdicione a regra de ingress descrita em Segurança — JupyterLab e atualize o release
java.net.UnknownHostException: <pod-name>spark.driver.host está resolvendo para o hostname do pod em vez do IPGaranta que spark.driver.host esteja vazio em sparkConfig e que SPARK_DRIVER_HOST seja injetado via Downward API (fieldPath: status.podIP)
Avisos CANNOT_MODIFY_CONFIGConfiguração Spark aplicada via SparkSession.builder.config() após o import do PySparkPasse spark.driver.host e JARs via PYSPARK_SUBMIT_ARGS antes de importar o PySpark, não via SparkSession.builder.config()

Comandos de diagnóstico

Terminal input
# Logs do pod do notebook
kubectl logs -n <NAMESPACE> <POD_NAME>

# Variáveis de ambiente Spark dentro do pod
kubectl exec -n <NAMESPACE> <POD_NAME> -- env | grep SPARK

# Listar arquivos montados
kubectl exec -n <NAMESPACE> <POD_NAME> -- ls -R /opt/bitnami/spark/conf

# Verificar endpoints do serviço Spark master
kubectl get svc -n <NAMESPACE> | grep spark-master

Personalização avançada

  • Adicione propriedades Spark extra em tdpSparkIntegration.configMap.sparkConfig.
  • Defina perfis de tamanho de notebook via singleuser.profileList e ajuste variáveis de ambiente Spark por perfil.
  • Quando usar múltiplos clusters Spark, sobreponha spark.master por perfil ou via ambiente de usuário.

Limpeza

Terminal input
helm uninstall <RELEASE_NAME> -n <NAMESPACE>
kubectl delete configmap tdp-jupyter-spark-integration -n <NAMESPACE>