Apache Iceberg, Hadoop e Hive: Abra seu Datalake (Lakehouse) -> Parte I
- Claude Paugh

- 16 de jun.
- 12 min de leitura
Atualizado: 22 de jun.
Em um post anterior, fiz um breve resumo dos critérios que diferenciam um datalake de um lakehouse. O gerenciamento e a organização de dados foram os principais pontos a serem considerados, assim como o que caracteriza um lakehouse, e a falta de referências a um datalake, além da maior velocidade de entrada de dados. Agora, passei a abordar como tornar qualquer um desses padrões, datalake ou lakhouse, utilizável para acesso dos usuários. Dividirei este assunto em duas partes: a primeira com foco na infraestrutura e a segunda no conteúdo e acesso aos dados.

Apesar das diferenças, você pode dar alguns passos largos em direção a uma arquitetura de lakehouse ou datalake ao implantar sua infraestrutura. Obviamente, ela não lida com nenhum dos processos adicionais que compõem uma prática de gerenciamento de dados, mas você pode se posicionar para seguir nessa direção.
Minhas postagens anteriores sobre o Apache Iceberg destacaram os recursos de gerenciamento de dados do Iceberg, especialmente no gerenciamento de esquemas. Então, comecei minha arquitetura de minimodelo utilizando o Iceberg.
Para minha plataforma de processamento de dados, estou optando por código aberto. Mas também não acho que existam produtos comerciais melhores. A decisão sobre a plataforma foi um pouco mais complexa. Usei e implantei DASK e Apache Spark como mecanismos de processamento capazes de processamento paralelo em CPU (altamente preferível devido ao custo-benefício) e GPU. Obviamente, é necessário obter bibliotecas de fornecedores de GPU para estender qualquer um deles, mas o processamento de dados é um desperdício de GPUs – elas não são adequadas para a tarefa.
Decidi usar o Apache Spark devido à sua grande comunidade e ao suporte dos fornecedores. É um processo de aceleração de implantação e operações muito rápido, quando os nós estão na casa de um dígito — a menos que você esteja usando um serviço de provedor de nuvem que possa escalar rapidamente. O Spark também não requer a codificação de funções Python para habilitar o paralelismo. Como eu estava usando o Iceberg (e o Hadoop), há integração interna com o Spark, o que deve tornar a integração mais suave (eu acho).
Por fim, para permitir o acesso de consulta do usuário aos dados, optei pelo Apache Hive e pelo Drill devido à sua integração. Existem muitas outras ferramentas de código aberto e produtos comerciais que podem fazer mais sentido, dependendo dos seus requisitos específicos, mas este guia permitirá que você comece sem custos de licenciamento.
A seguir está uma representação dessas escolhas com setas representando os fluxos de dados (idealmente):

Datalake: Configurar Apache Hadoop e Apache Iceberg
A instalação de um JDK v8 ou mais recente é necessária para o Hadoop. Usei o homebrew para instalar o openjdk@17, pois era a versão compatível com todos os produtos Apache que listei acima.
Dependendo da plataforma que você estiver usando, você pode escolher entre instalação manual via download ou usando um gerenciador de pacotes. Eu também usei o homebrew para instalar o Hadoop:
brew install hadoop
cd "/opt/homebrew/Cellar/hadoop/3.3.6/libexec/etc/hadoop"
vi core-site.xml Eu já havia feito isso como parte da instalação do Iceberg, conforme minha postagem sobre o assunto, mas basicamente seu core-site.xml deve ser parecido com o da lista abaixo. Destaquei os itens em negrito para dar mais atenção e, como tenho uma configuração local, habilitei a autenticação anônima. Na maioria dos casos, a autenticação deve ser habilitada/obrigatória. O principal pré-requisito é ter locais no sistema de arquivos e espaço disponíveis para instalar o Hadoop.
core-site.xml
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. See accompanying LICENSE file.
-->
<!-- Put site-specific property overrides in this file. -->
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/Volumes/ExtShield/opt/hdfs/tmp</value>
<description>A base for other temporary directories</description>
</property>
<property>
<name>hadoop.http.filter.initializers</name>
<value>org.apache.hadoop.security.AuthenticationFilterInitializer</value>
<description>
Authentication for Hadoop HTTP web-consoles
add to this property the org.apache.hadoop.security.AuthenticationFilterInitializer initializer class.
</description>
</property>
<property>
<name>hadoop.http.authentication.type</name>
<value>simple</value>
<description>
Defines authentication used for the HTTP web-consoles.
The supported values are: simple | kerberos | #AUTHENTICATION_HANDLER_CLASSNAME#.
The dfeault value is simple.
</description>
</property>
<property>
<name>hadoop.http.authentication.token.validity</name>
<value>36000</value>
<description>
Indicates how long (in seconds) an authentication token is valid before it has to be renewed.
The default value is 36000.
</description>
</property>
<property>
<name>hadoop.http.authentication.standard.config.path</name>
<value>/Users/claude_paugh/hadoop/auth.conf</value>
<description>
The signature secret file for signing the authentication tokens.
The same secret should be used for all nodes in the cluster, JobTracker, NameNode, DataNode and TastTracker.
The default value is $user.home/hadoop-http-auth-signature-secret.
IMPORTANT: This file should be readable only by the Unix user running the daemons.
</description>
</property>
<property>
<name>hadoop.http.authentication.simple.anonymous.allowed</name>
<value>true</value>
<description>
Indicates if anonymous requests are allowed when using ‘simple’ authentication.
The default value is true
</description>
</property>
<property>
<name>hadoop.http.authentication.signature.secret.file</name>
<value>/Users/claude_paugh/hadoop/hadoop-http-auth-signature-secret</value>
<description>
The signature secret file for signing the authentication tokens.
The same secret should be used for all nodes in the cluster, JobTracker, NameNode, DataNode and TastTracker.
The default value is $user.home/hadoop-http-auth-signature-secret.
IMPORTANT: This file should be readable only by the Unix user running the daemons.
</description>
</property>
<property>
<name>iceberg.engine.hive.enabled</name>
<value>true</value>
</property>
</configuration>hdfs-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. See accompanying LICENSE file.
-->
<!-- Put site-specific property overrides in this file. -->
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<property>
<name>dfs.permissions</name>
<value>false</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/Volumes/ExtShield/opt/hdfs/namenode/data</value>
</property>
<property>
<name>dfs.namenode.checkpoint.dir</name>
<value>file:/Volumes/ExtShield/opt/hdfs/namenode/checkpoint</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:/Volumes/ExtShield/opt/hdfs/datanode/data</value>
</property>
</configuration>mapred-site.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. See accompanying LICENSE file.
-->
<!-- Put site-specific property overrides in this file. -->
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.application.classpath</name>
<value>$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/lib/*</value>
</property>
</configuration>
yarn-site..xml
<?xml version="1.0"?>
<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. See accompanying LICENSE file.
-->
<configuration>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.env-whitelist</name>
<value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_HOME,PATH,LANG,TZ,HADOOP_MAPRED_HOME</value>
</property>
</configuration>A lista de arquivos de configuração acima reflete a configuração que tenho usado com o Apache Iceberg também. Em seguida, configurei o script .bashrc no meu diretório home para incluir as variáveis do Hadoop. Se o .bashrc não for executado quando a sessão do shell for aberta, você precisará usar " source ~/.bashrc " para executá-lo manualmente. As variáveis incluídas são necessárias para operar (inicialização/desligamento, tarefas) os produtos Iceberg/Hadoop, Hive e Spark:
.bashrc -- Somente Hadoop
export PYTHON_BIN="/usr/local/bin"
export JAVA_HOME="/opt/homebrew/opt/openjdk@21"
export PYTHONPATH=$PYTHON_BIN
eval "$(/opt/homebrew/bin/brew shellenv)"
export PYTHONPATH=$PYTHONPATH:/Users/claude_paugh/airflow/arf/site-packages/trading-0.1.0b0.dist-info
export HDFS_NAMENODE_USER="claude_paugh"
export HDFS_DATANODE_USER="claude_paugh"
export HDFS_SECONDARYNAMENODE_USER="claude_paugh"
export HDF5_USE_FILE_LOCKING="FALSE"
export HADOOP_HEAPSIZE=2048
export HADOOP_HOME="/opt/homebrew/Cellar/hadoop/3.4.1/libexec"
export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HADOOP_COMMON_HOME=$HADOOP_HOME
export HADOOP_HDFS_HOME=$HADOOP_HOME
export HADOOP_CLIENT_OPTS="-Di-Dorg.apache.logging.log4j.simplelog.StatusLogger.level=TRACE"
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export HADOOP_OPTS="-Djava.library.path=$HADOOP_HOME/lib"
export HADOOP_CLASSPATH=${JAVA_HOME}/lib/tools.jar
export PATH=$PATH:$PYTHONPATH:$JAVA_HOME:$HADOOP_HOME:$HADOOP_HOME/binVocê pode iniciar e desligar o Hadoop usando $HADOOP_HOME/sbin/start-all.sh e stop-all.sh no mesmo local. Se estiver usando as bibliotecas Python como eu, precisará instalar o pacote pyiceberg via pip ou conda. Nesse ponto, se " start-all.sh " for executado corretamente, acesse a URL http://localhost:9870/ no seu navegador e você deverá ver algo como abaixo:

Eu já tinha baixado o JAR para o Apache Iceberg, então agora posso adicionar o jar do Iceberg ao meu classpath (ou $HADOOP_HOME/lib ), e devo estar totalmente funcional com o Iceberg + Hadoop.
Configurar o Apache Spark
Meu próximo passo foi configurar o Spark de forma independente. Decidi testar a versão 4.0.0 e, novamente, usei o Homebrew para instalá-lo . Estava funcionando bem com minha instalação do Hadoop/Iceberg, mas ao instalar o Apache Hive 4.0.1, comecei a ter problemas intermitentes que já havia visto em outros aplicativos relacionados ao reconhecimento de catálogo, então optei pelo Spark 3.5.6, usando o Homebrew mais uma vez. Semelhante à instalação do Hadoop acima, existem start-all.sh e stop-all.sh no diretório $SPARK_HOME/sbin . Após a inicialização, você poderá acessar http://127.0.0.1:8080/ e ver isto:

Também criei e personalizei meu $SPARK_HOME/conf/ spark-defaults.co nf , que é lido quando o Spark é iniciado, e continha o seguinte:
spark.jars.packages org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.9.1
spark.sql.extensions org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
spark.sql.hive.metastore.uri thrift://localhost:9083
spark.sql.warehouse.dir hdfs://localhost:9000/user/hive/warehouse
spark.sql.catalogImplementation hive
spark.sql.catalog.spark_catalog org.apache.iceberg.spark.SparkCatalog
spark.sql.catalog.spark_catalog.type hive
spark.sql.catalog.spark_catalog.warehouse hdfs://localhost:9000/user/spark/warehouseOs valores são direcionados ao uso e integração do Hive. Também criei (a partir do modelo) e modifiquei o arquivo spark-env.sh. script no diretório $SPARK_HOME/conf para conter o seguinte:
export SPARK_MASTER_HOST=127.0.0.1
export SPARK_MASTER_PORT=7077
# Options read in any cluster manager using HDFS
export HADOOP_CONF_DIR="$HADOOP_HOME/etc/hadoop"
export HADOOP_HOME=$HADOOP_HOME
# Pyspark
PYSPARK_DRIVER_PYTHON=python
PYSPARK_PYTHON=pythonNesse ponto, eu tinha um ambiente Spark funcional e testei se os shells Scala e Pyspark seriam iniciados a partir da CLI, o que foi feito com sucesso, e executei algumas instruções. Em seguida, executei uma instalação do pip para o Pyspark, já que não planejava usar o shell durante os testes. Testei o restante da instalação do Spark executando um script simples que abriu uma SparkSession e leu um arquivo delimitado em um dataframe de um local HDFS, com o Pyspark importado para o script. Também adicionei as seguintes variáveis ao arquivo .bashrc :
export SPARK_REMOTE="sc://localhost"
export SPARK_HOME="/Volumes/ExtShield/opt/spark-3.5.6-bin-hadoop3/libexec"
export PYTHONPATH=$HADDOOP_HOME:$SPARK_HOME/jars:$PYTHONP$
export PATH=$PATH:$HADOOP_HOME:$SPARK_HOME:$HADOOP_HDFS_HOMEConfigurar o Apache Hive
Como mencionei anteriormente, comecei com o Apache Spark 4.0.0 e o Apache Hive 4.0.1, mas depois de alguns problemas, reduzi a versão do meu Spark para a 3.5.6 e instalei também a versão 4.0.0 do Apache Hive. Você pode encontrar as versões históricas do Hive aqui . Baixei o arquivo apache-hive-4.0.0-bin.tar.gz e o movi para o local de destino. Em seguida, em um shell no meu MacBook, executei:
gunzip apache-hive-4.0.0-bin.tar.gz
tar -xvf apache-hive-4.0.0-bin.tar Em seguida, adicionei as seguintes variáveis de ambiente ao meu .bashrc :
export HIVE_OPTS="-hiveconf mapreduce.map.memory.mb=8192 -hiveconf mapreduce.reduce.memory.mb=5120"
export HIVE_HOME="/Volumes/ExtShield/opt/apache-hive-4.0.0-bin"
export HIVE_CONF_DIR=$HIVE_HOME/conf
export HIVE_SERVER2_THRIFT_BIND_HOST="localhost"
export HIVE_SERVER2_THRIFT_PORT="10001"
export PYTHONPATH --> add $HIVE_HOME/lib
export PATH --> add $HIVE_HOMEOs limites de memória são para evitar erros de "falta de memória". O restante é autoexplicativo, com base na leitura da documentação do Hive. As variáveis que referenciam "HIVE_SERVER2" são necessárias para a instalação que está por vir.
Eu já havia usado um banco de dados PostgreSQL remoto para o catálogo Iceberg, então segui o mesmo caminho com o Hive. Reutilizando a instalação do Postgres da implantação, copiei o driver JDBC do Postgres para o diretório $HIVE_HOME/lib para que ele fosse adicionado ao classpath do Hive.
Comecei com a documentação mais recente do Hive e o Manual de Administração, depois baixei a versão 4.0.0 do Hive para a instalação do tarball. Segui as instruções e fiz as alterações no meu .bashrc e na configuração conforme indicado, e prossegui com a instalação do Hive Metastore .
Como indiquei acima, eu iria usar um Metastore remoto com PostgreSQL, então criei manualmente o banco de dados Postgres usando uma conexão JDBC usando DataGrip e o chamei de metacatalog . Em seguida, criei um hive-site.xml a partir do arquivo de modelo fornecido e comecei a atualizar a configuração, substituindo o MySQL pelo Postgres (na documentação do site). Você também notará que incluí alguns parâmetros usados para configurar o HiveServer2 . Os detalhes estão abaixo:
Parâmetros de configuração | Valor de configuração | Comentário |
javax.jdo.option.URL de conexão | jdbc:postgresql://10.0.0.30:5439/metacatalog?ssl=false;create=false | metadados são armazenados em um servidor PostgreSQL |
javax.jdo.option.NomeDoDriverDeConexão | Classe de driver JDBC | |
javax.jdo.option.NomeDoUsuárioDeConexão | <username> | nome de usuário para conexão ao Postgres |
javax.jdo.option.Senha de Conexão | <password> | senha para conexão ao Postgres |
colmeia.metastore.warehouse.dir | local padrão para tabelas Hive (Hadoop) | |
Nome do host ao qual o serviço do metastore será vinculado. Quando vazio, "localhost" é usado. Esta configuração está disponível a partir do Hive 4.0.0. | ||
colmeia.metastore.uris | economia:// localhost:9083 | Host e porta para o servidor metastore do Thrift. Se hive.metastore.thrift.bind.host for especificado, o host deverá ser o mesmo da configuração. Saiba mais sobre isso nos parâmetros de configuração de descoberta dinâmica de serviços. |
Tenho certeza de que todos perceberão que armazenar nomes de usuário e senhas no arquivo hive-site.xml em texto simples não é aceitável na maioria das configurações de segurança. Imagino que a maioria das organizações use SAML 2.0 e login único.
Em seguida, iniciei o metastore do Hive como um serviço em segundo plano após as alterações de configuração.
$HIVE_HOME/bin/hive --service metastore &Inicialmente, tentei iniciar o metastore com a opção de configuração " create=true " na URL JDBC, mas obtive sucesso parcial. Então, mudei para " create=false " e usei a ferramenta schema para realizar uma implantação manual. Consegui iniciar o serviço e implantar as tabelas do catálogo Hive com a ferramenta:

Na versão do catálogo que acompanha o Hive 4.0.0, você deveria ter 83 tabelas no banco de dados, supondo que ele estivesse vazio no início. Após iniciar o serviço do metastore com as alterações nos parâmetros após usar a ferramenta de esquema, tudo estava limpo nos logs de inicialização, então acho que passamos para a implantação do servidor Hive.
Eu já havia incluído alguns dos parâmetros acima para HiveServer2, mas ainda precisava fazer mais algumas alterações no arquivo hive-site.xml para concluir a tarefa. Primeiro, eu queria executar no modo "HTTP" em vez de binário, então atualizei o arquivo hive.server2.transport.mode no XML para " http ". Em seguida, adicionei os valores mínimos necessários ao arquivo hive-site.xml, incluindo os valores para execução no "modo http" (tabela no link), e também fiz o seguinte:
Desabilitando SSL
URL JDBC usada com parâmetros http: jdbc:hive2:// localhost:10001/default;transportMode=http;httpPath=cliservice
Configuração de registro
Autenticação Anônima
configuração do diretório scratch para Hive
Então iniciei o serviço:
$HIVE_HOME/bin/hive --service hiveserver2Visitando http://localhost:10002 , vejo o seguinte

Nesse ponto, parecia que eu estava pelo menos executando, então instalei e abri o DBeaver para validar a funcionalidade do Hive. Criei uma conexão JDBC com o Hive usando o DBeaver, adicionando uma fonte de dados do Hive sem autenticação, usando a URL acima:

Conectei e vi o seguinte:

URL aberta para HiveServer2:

Eu havia validado uma conexão com o banco de dados Hive. Em seguida, queria criar uma tabela para validar a integração Hive + Iceberg + Hadoop. Usei a instrução "create table" que usei nas postagens sobre o Apache Iceberg e fiz uma modificação simples para incluir o formato de linha serde para Hive/Iceberg:
Executei o script:


Saída da interface do usuário do Hive : clicar no link Drilldown fornece planos de execução e logs operacionais para a execução da instrução:

Pasta Hadoop contendo pasta de metadados para o Apache Iceberg:


Em seguida, também adicionei uma conexão com o Hive e o Hadoop ao meu IDE PyCharm, para que eu possa navegar e ver atributos estendidos para tabelas ao escrever scripts PySpark. Você precisa adicionar o plugin Spark se estiver executando uma versão mais antiga do IDE; as novas versões vêm com ele instalado se você estiver usando uma licença "Pro".
Abra "Big Data Tools" para criar as conexões:

Isso me dá uma visão detalhada das especificações da tabela clicando no ícone do botão " Hive Metastore " (à esquerda). Painéis devem aparecer à direita da tela após o clique do botão, apresentando informações sobre os atributos da tabela (Página 1 + 2):



Em seguida, carreguei um arquivo de dados que usei na série de artigos Iceberg , usando o Spark, e criei registros na tabela "test_company_stocks". Abordarei o uso do Spark na Parte II deste artigo, mas após o carregamento, vejo que os dados persistiram. Carreguei o arquivo em uma tabela de preparação separada e executei uma operação SQL no IDE: " INSERT INTO.....SELECT * FROM staging_table" , para poder demonstrar o que ocorre.
Após executar o comando " INSERT " no DBeaver, você pode acessar imediatamente o console do HiveServer2, clicar na consulta e, em seguida, acessar o "Registro de Operações" (supondo que você tenha configurado como eu fiz anteriormente). Você deverá ver uma saída como esta:

Se você copiar/colar o URL destacado em uma aba do navegador, verá a janela "Aplicativos Hadoop". É aqui que você pode monitorar o trabalho "MapReduce" que "traduz" os dados de uma tabela para outra dentro do Hadoop:

Se você clicar em " Ferramentas -> Logs " no menu à esquerda, poderá ver o progresso do trabalho nos arquivos de log. Para os realmente curiosos, se você definir "tmp" ou "scratchdir" para locais específicos do Hive, poderá ver os objetos temporários no sistema de arquivos do SO ou no sistema de arquivos do Hadoop, se tiver escolhido essa opção para os arquivos de rascunho.
Após a conclusão, voltei ao DBeaver e executei "select * from test_company_stock", e pude ver isto:

A pasta "test_company_stocks" no Hadoop agora deve ter uma subpasta chamada "data" com partições e arquivos parquet, já que está usando o Iceberg. No meu caso, tinha, como mostrado abaixo:


