Integrações — JupyterLab
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
| Modo | tdpSparkIntegration.enabled | Valor resolvido para spark.master | Uso típico |
|---|---|---|---|
| PySpark local | false | local[*] | Executa Spark dentro do próprio pod do notebook (padrão para desenvolvimento) |
| Cluster externo | true | spark://<RELEASE_NAME>-spark-master-svc.<NAMESPACE>.svc.cluster.local:7077 | Liga a um deployment Spark já existente |
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
| Componente | Função |
|---|---|
templates/spark-integration-configmap.yaml | Renderiza as configurações Spark e o script auxiliar de ambiente |
singleuser.extraEnv | Define variáveis de ambiente Spark para cada pod de notebook |
singleuser.lifecycleHooks.postStart | Executa jupyter-spark-env.sh antes do JupyterLab iniciar |
singleuser.networkPolicy.egress | Permite 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
| Caminho | Tipo | Conteúdo |
|---|---|---|
/opt/bitnami/spark/conf | ConfigMap | spark-defaults.conf e scripts auxiliares |
/tmp/spark-local | emptyDir | Dados temporários e shuffle do Spark |
/tmp/spark-logs | emptyDir | Logs 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:
- a configuração do Jupyter fica nesta página;
- a configuração do catálogo Iceberg fica em Integrações — Iceberg;
- a configuração do Spark fica em Integrações — Spark.
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
deltaLakedo charttdp-sparkhabilita o suporte, mas não configura sozinho ospark.sparkConf— as propriedades Spark necessárias são fornecidas viacustomSparkConfig.propertiesouspark.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:
- a configuração do Jupyter fica nesta página;
- a configuração do bloco Delta Lake fica em Integrações — Delta Lake;
- a configuração do Spark fica em Integrações — Spark.
Instalar ou atualizar o JupyterHub
helm upgrade --install <RELEASE_NAME> \
oci://registry.tecnisys.com.br/tdp/charts/tdp-jupyter \
-n <NAMESPACE> \
-f values.yaml
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
-
Pods em execução
Terminal inputkubectl get pods -n <NAMESPACE> | grep jupyter -
ConfigMap Spark criado
Terminal inputkubectl get configmap tdp-jupyter-spark-integration -n <NAMESPACE> -o yaml -
Conectividade de rede (a partir de um pod de notebook)
Terminal inputkubectl 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:
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
| Sintoma | Causa provável | Ação sugerida |
|---|---|---|
JAVA_GATEWAY_EXITED ou erros Py4J | SPARK_HOME/PYTHONPATH mal configurados | Verifique se singleuser.extraEnv usa os caminhos /opt/bitnami/spark |
IllegalStateException: Cannot call methods on a stopped SparkContext | Spark master inacessível ou NetworkPolicy bloqueando egress/ingress | Confirme 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 PySpark | Não adicione /opt/conda/envs/py312/lib/python3.12/site-packages ao PYTHONPATH |
| Driver Spark não consegue conectar/comunicar | SPARK_DRIVER_HOST não resolvível | Deixe 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 Spark | Adicione 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 IP | Garanta que spark.driver.host esteja vazio em sparkConfig e que SPARK_DRIVER_HOST seja injetado via Downward API (fieldPath: status.podIP) |
Avisos CANNOT_MODIFY_CONFIG | Configuração Spark aplicada via SparkSession.builder.config() após o import do PySpark | Passe spark.driver.host e JARs via PYSPARK_SUBMIT_ARGS antes de importar o PySpark, não via SparkSession.builder.config() |
Comandos de diagnóstico
# 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.profileListe ajuste variáveis de ambiente Spark por perfil. - Quando usar múltiplos clusters Spark, sobreponha
spark.masterpor perfil ou via ambiente de usuário.
Limpeza
helm uninstall <RELEASE_NAME> -n <NAMESPACE>
kubectl delete configmap tdp-jupyter-spark-integration -n <NAMESPACE>