Integrações - Apache Ozone
Visão geral das integrações
O Ozone fornece armazenamento de objetos compatível com S3 que pode substituir MinIO ou AWS S3 na stack TDP. A tabela abaixo resume os componentes que podem ser integrados, com indicação de prioridade e complexidade:
| Componente | Tipo de integração | Prioridade | Complexidade |
|---|---|---|---|
| Spark | Armazenamento primário para jobs | ⭐⭐⭐ Alta | Baixa |
| Trino | Backend de catálogo Hive/Iceberg | ⭐⭐⭐ Alta | Baixa |
| Hive Metastore | Warehouse de metadados | ⭐⭐⭐ Alta | Baixa |
| Delta Lake | Armazenamento e manutenção de tabelas | ⭐⭐⭐ Alta | Baixa |
| Iceberg | Armazenamento de formato de tabela | ⭐⭐⭐ Alta | Baixa |
| Airflow | DAGs, logs e XCom backend | ⭐⭐ Média | Média |
| NiFi | Fluxos de dados e repositório de conteúdo | ⭐⭐ Média | Média |
| Jupyter | Acesso a dados e notebooks | ⭐⭐ Média | Baixa |
| Superset | Cache de resultados de consulta | ⭐ Baixa | Baixa |
| Ranger | Armazenamento de logs de auditoria | ⭐ Baixa | Média |
Endpoint interno padrão do S3 Gateway REST:
http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
Use Ingress apenas quando o consumidor estiver fora do cluster ou quando a arquitetura exigir um hostname externo, como https://<OZONE_S3_REST_HOSTNAME>.
O S3 Gateway REST é o plano de dados do Ozone. Antes de expô-lo ou configurar consumidores, garanta autenticação habilitada, TLS para tráfego fora do cluster e controle de rede adequado. Consulte Segurança - Apache Ozone.
Credenciais e endpoint
Os exemplos abaixo usam AWS Signature v4 em modo simples. As credenciais devem vir de Secrets Kubernetes, variáveis de ambiente seguras ou mecanismo equivalente do componente consumidor. Não versionar <AWS_ACCESS_KEY_ID> nem <AWS_SECRET_ACCESS_KEY> em arquivos Git.
Para Ozone, mantenha path-style access habilitado nos consumidores S3, pois o bucket normalmente faz parte do caminho da URL.
Para detalhes sobre modelo de credenciais, rotação, TLS e controle de rede, consulte a página de segurança do Ozone.
Spark com S3A
Por que integrar
- Armazenar dados de entrada e saída de jobs Spark
- Persistir DataFrames e RDDs
- Compartilhar dados entre aplicações Spark
- Habilitar arquiteturas de data lake
Configuração
Propriedades mínimas de conexão:
spark.hadoop.fs.s3a.endpoint=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
spark.hadoop.fs.s3a.access.key=<AWS_ACCESS_KEY_ID>
spark.hadoop.fs.s3a.secret.key=<AWS_SECRET_ACCESS_KEY>
spark.hadoop.fs.s3a.path.style.access=true
spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem
spark.hadoop.fs.s3a.connection.ssl.enabled=false
Para volumes altos de dados, adicione as propriedades de tuning ao mesmo bloco:
spark.hadoop.fs.s3a.connection.maximum=100
spark.hadoop.fs.s3a.threads.max=64
spark.hadoop.fs.s3a.fast.upload=true
spark.hadoop.fs.s3a.block.size=128M
Em implantações Kubernetes, entregue as propriedades via ConfigMap. Crie o manifesto:
apiVersion: v1
kind: ConfigMap
metadata:
name: spark-custom-defaults
namespace: <NAMESPACE>
data:
spark-defaults.conf: |
spark.hadoop.fs.s3a.endpoint=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
spark.hadoop.fs.s3a.access.key=<AWS_ACCESS_KEY_ID>
spark.hadoop.fs.s3a.secret.key=<AWS_SECRET_ACCESS_KEY>
spark.hadoop.fs.s3a.path.style.access=true
spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem
spark.hadoop.fs.s3a.connection.ssl.enabled=false
spark.hadoop.fs.s3a.connection.maximum=100
spark.hadoop.fs.s3a.threads.max=64
spark.hadoop.fs.s3a.fast.upload=true
spark.hadoop.fs.s3a.block.size=128M
Aplique o ConfigMap e referencie-o no values.yaml do chart Spark:
kubectl apply -f spark-ozone-config.yaml -n <NAMESPACE>
spark:
master:
existingConfigmap: spark-custom-defaults
worker:
existingConfigmap: spark-custom-defaults
Exemplo de uso
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.appName("Ozone S3 Example") \
.getOrCreate()
# Ler do Ozone
df = spark.read.parquet("s3a://warehouse/data/input.parquet")
# Processar dados
result = df.groupBy("category").count()
# Escrever no Ozone
result.write.mode("overwrite").parquet("s3a://warehouse/data/output.parquet")
Criação do Secret Kubernetes
kubectl create secret generic spark-ozone-credentials \
--from-literal=access-key=<AWS_ACCESS_KEY_ID> \
--from-literal=secret-key=<AWS_SECRET_ACCESS_KEY> \
-n <NAMESPACE>
Trino com S3 nativo
Por que integrar
- Consultar dados armazenados no Ozone via catálogos Hive/Iceberg
- Queries federadas entre múltiplas fontes de dados
- Analytics de alta performance em armazenamento de objetos
Configuração
Configure os catálogos no values.yaml do chart Trino:
tdp-trino:
catalogs:
hive: |
connector.name=hive
hive.metastore.uri=thrift://tdp-hive-metastore.<NAMESPACE>.svc.cluster.local:9083
hive.non-managed-table-writes-enabled=true
hive.non-managed-table-creates-enabled=true
fs.native-s3.enabled=true
s3.endpoint=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
s3.region=us-east-1
s3.aws-access-key=<AWS_ACCESS_KEY_ID>
s3.aws-secret-key=<AWS_SECRET_ACCESS_KEY>
s3.path-style-access=true
iceberg: |
connector.name=iceberg
iceberg.catalog.type=hive_metastore
hive.metastore.uri=thrift://tdp-hive-metastore.<NAMESPACE>.svc.cluster.local:9083
fs.native-s3.enabled=true
s3.endpoint=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
s3.region=us-east-1
s3.aws-access-key=<AWS_ACCESS_KEY_ID>
s3.aws-secret-key=<AWS_SECRET_ACCESS_KEY>
s3.path-style-access=true
Ajuste nomes de Services, catálogos e Secrets conforme os releases reais do ambiente.
Exemplo de uso
-- Criar schema apontando para bucket Ozone
CREATE SCHEMA iceberg.warehouse WITH (location = 's3a://warehouse/');
-- Criar tabela Iceberg
CREATE TABLE iceberg.warehouse.vendas (
id BIGINT,
produto VARCHAR,
valor DECIMAL(10,2),
dt_venda DATE
) WITH (format = 'PARQUET', location = 's3a://warehouse/vendas');
-- Inserir dados
INSERT INTO iceberg.warehouse.vendas VALUES
(1, 'Laptop', 1200.00, DATE '2024-01-15'),
(2, 'Mouse', 25.50, DATE '2024-01-16');
-- Consultar
SELECT produto, SUM(valor) AS total FROM iceberg.warehouse.vendas GROUP BY produto;
Hive Metastore e warehouse
Por que integrar
- Armazenar metadados de tabelas e dados do warehouse no Ozone
- Repositório central de metadados para Spark, Trino e demais ferramentas
Configuração
Configure o Hive Metastore para usar o Ozone como warehouse no values.yaml:
tdp-hive-metastore:
metastore:
type: s3
s3:
endpoint: http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
accessKey: <AWS_ACCESS_KEY_ID>
secretKey: <AWS_SECRET_ACCESS_KEY>
bucket: warehouse
pathStyleAccess: true
warehouse:
dir: s3a://warehouse/hive
Quando Spark e Trino compartilham o mesmo warehouse, mantenha endpoint, região, estilo de acesso e credenciais alinhados nos dois lados.
Iceberg e Delta Lake
Por que integrar
- Armazenar tabelas Iceberg e Delta Lake no Ozone
- Transações ACID em armazenamento de objetos
- Time travel e versionamento de dados
Configuração
Exemplos de localizações:
s3a://warehouse/iceberg
s3a://warehouse/delta
Prefira buckets e prefixos separados por domínio ou ambiente.
Para jobs de manutenção Delta Lake (VACUUM, OPTIMIZE), configure o values.yaml do chart Delta Lake:
maintenance:
spark:
config:
"spark.sql.extensions": "io.delta.sql.DeltaSparkSessionExtension"
"spark.sql.catalog.spark_catalog": "org.apache.spark.sql.delta.catalog.DeltaCatalog"
"spark.hadoop.fs.s3a.endpoint": "http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878"
"spark.hadoop.fs.s3a.path.style.access": "true"
"spark.hadoop.fs.s3a.impl": "org.apache.hadoop.fs.s3a.S3AFileSystem"
"spark.hadoop.fs.s3a.connection.ssl.enabled": "false"
"spark.databricks.delta.retentionDurationCheck.enabled": "false"
defaultTablePath: "s3a://warehouse/delta"
Para executar VACUUM diretamente via spark-sql:
spark-sql \
--conf spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension \
--conf spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog \
--conf spark.hadoop.fs.s3a.endpoint=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878 \
--conf spark.hadoop.fs.s3a.path.style.access=true \
--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \
--conf spark.hadoop.fs.s3a.access.key=<AWS_ACCESS_KEY_ID> \
--conf spark.hadoop.fs.s3a.secret.key=<AWS_SECRET_ACCESS_KEY> \
--conf spark.databricks.delta.retentionDurationCheck.enabled=false \
-e "VACUUM delta.\`s3a://warehouse/delta/vendas\` RETAIN 168 HOURS;"
A política de retenção, limpeza e manutenção continua sendo definida no componente consumidor.
Jupyter e clientes S3
Por que integrar
- Acessar dados do Ozone em notebooks de análise
- Compartilhar datasets entre usuários
- Armazenar notebooks e resultados de análise
Configuração via values.yaml
Injete as credenciais como variáveis de ambiente no chart JupyterHub:
tdp-jupyter:
extraEnv:
- name: AWS_ACCESS_KEY_ID
value: "<AWS_ACCESS_KEY_ID>"
- name: AWS_SECRET_ACCESS_KEY
value: "<AWS_SECRET_ACCESS_KEY>"
- name: AWS_ENDPOINT_URL
value: "http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878"
- name: AWS_DEFAULT_REGION
value: "us-east-1"
Em produção, prefira referenciar as credenciais a partir de um Secret Kubernetes em vez de valores literais no values.yaml.
Exemplos em notebooks
Usando boto3 com pandas:
import boto3
import pandas as pd
s3 = boto3.client(
"s3",
endpoint_url="http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878",
aws_access_key_id="<AWS_ACCESS_KEY_ID>",
aws_secret_access_key="<AWS_SECRET_ACCESS_KEY>",
region_name="us-east-1",
)
# Ler dados do Ozone
obj = s3.get_object(Bucket="notebooks", Key="data/vendas.csv")
df = pd.read_csv(obj["Body"])
# Processar
resultado = df.groupby("categoria")["valor"].sum()
# Gravar resultado de volta no Ozone
resultado.to_csv("/tmp/resultado.csv")
s3.upload_file("/tmp/resultado.csv", "notebooks", "data/resultado.csv")
Usando AWS CLI:
aws configure set aws_access_key_id <AWS_ACCESS_KEY_ID>
aws configure set aws_secret_access_key <AWS_SECRET_ACCESS_KEY>
aws configure set region us-east-1
aws s3 ls --endpoint-url=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
Airflow e NiFi
Airflow
Por que integrar
- Armazenar logs remotos no Ozone
- Usar Ozone como backend de XCom para passagem de dados volumosos
- Armazenar artefatos de DAGs
Configuração
Configure logs remotos e a conexão AWS no values.yaml do chart Airflow:
tdp-airflow:
config:
logging:
remote_logging: "True"
remote_base_log_folder: "s3://airflow-logs/"
remote_log_conn_id: "ozone_s3"
connections:
- conn_id: ozone_s3
conn_type: aws
conn_extra: |
{
"aws_access_key_id": "<AWS_ACCESS_KEY_ID>",
"aws_secret_access_key": "<AWS_SECRET_ACCESS_KEY>",
"endpoint_url": "http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878",
"region_name": "us-east-1"
}
Exemplo em DAGs
from airflow import DAG
from airflow.operators.python import PythonOperator
from airflow.providers.amazon.aws.hooks.s3 import S3Hook
from datetime import datetime
with DAG("ozone_example", start_date=datetime(2024, 1, 1), schedule_interval=None) as dag:
def upload_to_ozone():
s3_hook = S3Hook(aws_conn_id="ozone_s3")
s3_hook.load_file(
filename="/tmp/dados.csv",
key="dados/entrada.csv",
bucket_name="airflow-data",
)
upload_task = PythonOperator(
task_id="upload_to_ozone",
python_callable=upload_to_ozone,
)
NiFi
Por que integrar
- Ingerir dados diretamente no Ozone
- Processar e transformar dados armazenados no Ozone
- Armazenar conteúdo de flowfiles
Configuração
Use os processadores PutS3Object e FetchS3Object com as seguintes configurações:
PutS3Object:
- Endpoint Override URL:
http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878 - Access Key e Secret Key: credenciais criadas para o serviço NiFi
- Region:
us-east-1 - Bucket:
nifi-data - Path Style Access: habilitado
FetchS3Object: mesmas configurações de endpoint, credenciais e região.
Exemplo de fluxo simples: GenerateFlowFile → PutS3Object (Ozone) → LogAttribute
Organização de buckets por componente
No armazenamento de objetos compatível com S3, um bucket é a unidade básica de organização: funciona como um contêiner de nível superior onde os objetos (arquivos, dados) são armazenados, cada um identificado por uma chave única dentro do bucket.
Cada consumidor deve ter seu próprio bucket (ou prefixo dedicado) e credenciais independentes. Isso simplifica o controle de acesso e permite revogar credenciais por serviço sem impactar os demais.
Sugestão de estrutura para um ambiente TDP completo:
| Bucket | Uso principal |
|---|---|
warehouse | Tabelas Spark, Hive Metastore, Iceberg e Delta Lake |
airflow-logs | Logs remotos do Airflow |
airflow-data | Artefatos e dados de DAGs |
nifi-data | Dados ingeridos ou processados pelo NiFi |
notebooks | Notebooks e dados do JupyterHub |
Crie os buckets com o AWS CLI apontado para o endpoint interno do Ozone:
aws s3 mb s3://warehouse --endpoint-url=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
aws s3 mb s3://airflow-logs --endpoint-url=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
aws s3 mb s3://nifi-data --endpoint-url=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
aws s3 mb s3://notebooks --endpoint-url=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
Sugestão de organização interna dos buckets:
warehouse/
├── hive/ # Tabelas Hive
├── spark/ # Saída de jobs Spark
├── delta/ # Tabelas Delta Lake
└── iceberg/ # Tabelas Iceberg
airflow-logs/
└── dag_id=<DAG_ID>/
└── run_id=<RUN_ID>/
nifi-data/
├── input/ # Dados brutos
├── processing/ # Dados em processamento
└── output/ # Dados processados
notebooks/
├── users/ # Notebooks por usuário
└── shared/ # Notebooks compartilhados
Para criar credenciais independentes por componente, use o script disponível no chart Ozone (veja a página de segurança):
./scripts/generate-s3-credentials.sh create spark-service
./scripts/generate-s3-credentials.sh create trino-service
./scripts/generate-s3-credentials.sh create airflow-service
./scripts/generate-s3-credentials.sh create jupyter-service
Teste rápido com dados de exemplo
Para validar a integração S3 logo após a instalação, crie buckets de teste e envie um arquivo de exemplo:
# Bucket para cargas de teste do Spark
aws s3 mb s3://spark-data --endpoint-url=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
# Bucket para testes de performance
aws s3 mb s3://performance-test --endpoint-url=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
# Envie um arquivo de exemplo para o bucket spark-data
aws s3 cp produtos.csv s3://spark-data/ --endpoint-url=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
Conteúdo de exemplo para produtos.csv:
id,nome,valor
1,produtoA,100
2,produtoB,250
3,produtoC,45
Para práticas de segurança (uso de Secrets, rotação de credenciais, TLS, NetworkPolicy e auditoria), consulte a página de segurança do Ozone.
Próximos passos
Recomendação de ordem de integração:
- Spark + Hive Metastore — base do data lake
- Trino — motor de consulta analítica
- Airflow — orquestração
- Ferramentas especializadas (NiFi, Jupyter) conforme necessidade
Solução de problemas
Para verificar a conectividade ao endpoint S3 a partir de um pod consumidor:
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- curl http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- aws s3 ls --endpoint-url=http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878
| Sintoma | Causa provável | Correção |
|---|---|---|
Name or service not known | DNS interno ou namespace incorreto | Verifique http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878 e o namespace do release |
| Conexão recusada ou timeout | Service sem endpoints, NetworkPolicy ou porta errada | Verifique pods S3 Gateway, endpoints do Service e políticas de rede |
| Erro de bucket virtual-hosted-style | Cliente tentando usar bucket no hostname | Habilite path-style access no consumidor |
SignatureDoesNotMatch ou 403 | Credenciais, região, endpoint ou relógio inconsistentes | Confira Secret, região us-east-1, URL exata e sincronização de horário |
| Erro TLS | Cliente não confia no certificado ou usa HTTP/HTTPS divergente | Alinhe URL, certificado, truststore e configuração TLS do consumidor |