Saltar para o conteúdo principal
Versão 3.0

Integrações - Apache Ozone

ChartVersion3.0.1TypeapplicationAppVersion2.0.0
CompatibilidadeKubernetes1.32+OpenShift4.19+Rancher2.10.x+

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:

ComponenteTipo de integraçãoPrioridadeComplexidade
SparkArmazenamento primário para jobs⭐⭐⭐ AltaBaixa
TrinoBackend de catálogo Hive/Iceberg⭐⭐⭐ AltaBaixa
Hive MetastoreWarehouse de metadados⭐⭐⭐ AltaBaixa
Delta LakeArmazenamento e manutenção de tabelas⭐⭐⭐ AltaBaixa
IcebergArmazenamento de formato de tabela⭐⭐⭐ AltaBaixa
AirflowDAGs, logs e XCom backend⭐⭐ MédiaMédia
NiFiFluxos de dados e repositório de conteúdo⭐⭐ MédiaMédia
JupyterAcesso a dados e notebooks⭐⭐ MédiaBaixa
SupersetCache de resultados de consulta⭐ BaixaBaixa
RangerArmazenamento de logs de auditoria⭐ BaixaMé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>.

Segurança do endpoint S3

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:

spark-ozone-config.yaml
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:

Terminal input
kubectl apply -f spark-ozone-config.yaml -n <NAMESPACE>
values.yaml (trecho)
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

Terminal input
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:

values.yaml (trecho)
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:

values.yaml (trecho)
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:

values.yaml (trecho)
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:

Terminal input
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:

values.yaml (trecho)
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"
tip

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:

Terminal input
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:

values.yaml (trecho)
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:

BucketUso principal
warehouseTabelas Spark, Hive Metastore, Iceberg e Delta Lake
airflow-logsLogs remotos do Airflow
airflow-dataArtefatos e dados de DAGs
nifi-dataDados ingeridos ou processados pelo NiFi
notebooksNotebooks e dados do JupyterHub

Crie os buckets com o AWS CLI apontado para o endpoint interno do Ozone:

Terminal input
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):

Terminal input
./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:

Terminal input
# 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
Segurança

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:

  1. Spark + Hive Metastore — base do data lake
  2. Trino — motor de consulta analítica
  3. Airflow — orquestração
  4. Ferramentas especializadas (NiFi, Jupyter) conforme necessidade

Solução de problemas

Para verificar a conectividade ao endpoint S3 a partir de um pod consumidor:

Terminal input
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
SintomaCausa provávelCorreção
Name or service not knownDNS interno ou namespace incorretoVerifique http://<RELEASE_NAME>-s3g-rest.<NAMESPACE>.svc.cluster.local:9878 e o namespace do release
Conexão recusada ou timeoutService sem endpoints, NetworkPolicy ou porta erradaVerifique pods S3 Gateway, endpoints do Service e políticas de rede
Erro de bucket virtual-hosted-styleCliente tentando usar bucket no hostnameHabilite path-style access no consumidor
SignatureDoesNotMatch ou 403Credenciais, região, endpoint ou relógio inconsistentesConfira Secret, região us-east-1, URL exata e sincronização de horário
Erro TLSCliente não confia no certificado ou usa HTTP/HTTPS divergenteAlinhe URL, certificado, truststore e configuração TLS do consumidor