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 objectos 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

Utilize Ingress apenas quando o consumidor estiver fora do cluster ou quando a arquitectura 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 o expor ou configurar consumidores, garanta autenticação activada, TLS para tráfego fora do cluster e controlo de rede adequado. Consulte Segurança - Apache Ozone.

Credenciais e endpoint

Os exemplos abaixo usam AWS Signature v4 em modo simples. As credenciais devem provir 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 ficheiros Git.

Para o Ozone, mantenha o path-style access activado nos consumidores S3, pois o bucket faz normalmente parte do caminho do URL.

Para detalhes sobre modelo de credenciais, rotação, TLS e controlo 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 elevados 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 os 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 utilizar 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 partilham o mesmo warehouse, mantenha o endpoint, a região, o estilo de acesso e as 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 directamente 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 a ser 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"
dica

Em produção, prefira referenciar as credenciais a partir de um Secret Kubernetes em vez de valores literais no values.yaml.

Exemplos em notebooks

Utilizando 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")

Utilizando 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 o Ozone como backend de XCom para passagem de dados volumosos
  • Armazenar artefactos de DAGs

Configuração

Configure os 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

Utilize 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: activado

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 objectos compatível com S3, um bucket é a unidade básica de organização: funciona como um contentor de nível superior onde os objectos (ficheiros, dados) são armazenados, cada um identificado por uma chave única dentro do bucket.

Cada consumidor deve ter o seu próprio bucket (ou prefixo dedicado) e credenciais independentes. Isto simplifica o controlo de acesso e permite revogar credenciais por serviço sem afectar os restantes.

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, utilize o script disponível no chart Ozone (consulte 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 ficheiro 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 ficheiro 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 (utilização 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ávelCorrecçã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 a tentar usar bucket no hostnameActive 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