Crea un data lakehouse unificado para la IA con Apache Iceberg y BigLake

1. Introducción

En este codelab, explorarás las capacidades del Unified Data Lakehouse en Google Cloud. Interactuarás con conjuntos de datos públicos que se publican a través del catálogo de REST de Apache Iceberg en BigLake y, luego, aplicarás las capacidades de IA de Google Cloud a datos estructurados y no estructurados.

Consultarás el conjunto de datos clásico de NYC Taxi con Apache Iceberg, profundizarás en el viaje en el tiempo para auditar los cambios en los datos y, luego, usarás BigQuery ML y Gemini para ejecutar modelos de IA en tus datos.

Actividades

  • Usa Google Cloud Serverless for Apache Spark para consultar conjuntos de datos públicos de Apache Iceberg alojados en BigLake.
  • Consulta datos estructurados en formato Apache Iceberg.
  • Demuestra el viaje en el tiempo en Apache Iceberg.
  • Usa BigQuery ML para entrenar un modelo predictivo en datos estructurados.
  • Crea una tabla de objetos de BigLake (datos no estructurados) y usa Gemini para analizar imágenes.

Requisitos

  • Un navegador web, como Chrome.
  • Un proyecto de Google Cloud con facturación habilitada.

Costo y duración previstos

  • Tiempo para completar: ~45 minutos.
  • Costo estimado: < USD 2.00. Usamos conjuntos de datos públicos y consultas sin servidores para mantener los costos bajos.

2. Configuración y requisitos

En este paso, prepararás tu entorno y habilitarás las APIs necesarias.

Inicie Cloud Shell

Ejecutarás la mayoría de los comandos desde Google Cloud Shell.

  1. Haz clic en Activar Cloud Shell en la parte superior de la consola de Google Cloud.
  2. Verifica la autenticación:
    gcloud auth list
    
  3. Confirma tu proyecto:
    gcloud config get project
    
  4. Si el proyecto no está configurado, configúralo con tu ID del proyecto:
    gcloud config set project <YOUR_PROJECT_ID>
    

Habilita las APIs

Ejecuta el siguiente comando para habilitar las APIs necesarias para BigQuery, Cloud Resource Manager y Vertex AI:

gcloud services enable \
  bigquery.googleapis.com \
  aiplatform.googleapis.com \
  cloudresourcemanager.googleapis.com

Configura el entorno y crea el bucket de dependencias

  1. Configura las variables de entorno en tu terminal:
    export PROJECT_ID=$(gcloud config get project)
    export REGION=us-central1
    export DEPS_BUCKET=$PROJECT_ID-deps-bucket
    
  2. Crea el bucket de Cloud Storage de dependencias. Las secuencias de comandos de PySpark se suben aquí en el momento del envío del trabajo:
    gcloud storage buckets create gs://$DEPS_BUCKET --location=$REGION
    

3. Conéctate al catálogo público de Apache Iceberg

En este paso, te conectarás a un catálogo de Apache Iceberg activo y de nivel de producción alojado en BigLake de Google Cloud.

Ejecuta Spark SQL con la CLI de Serverless for Apache Spark Batch

Usaremos Google Cloud Serverless for Apache Spark para ejecutar trabajos de PySpark sin necesidad de administrar la infraestructura. Lo configuraremos para que apunte al catálogo público de REST de BigLake.

  1. Define las propiedades del catálogo de REST de BigLake para evitar repetirlas.Esta configuración le indica a Spark lo siguiente:
    • Usar las bibliotecas iceberg-spark-runtime y iceberg-gcp-bundle
    • Configurar un catálogo llamado my_catalog con el extremo del catálogo de REST de BigLake
    • Usar Google Cloud Storage (GCS) para leer archivos de datos en lugar del sistema de archivos local predeterminado
    • Configurar este catálogo my_catalog como el predeterminado para nuestra sesión
    • Usar credenciales vendidas para mejorar la seguridad y simplificar el acceso a los datos
    export METASTORE_PROPERTIES="^|^spark.jars.packages=org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.10.0,org.apache.iceberg:iceberg-gcp-bundle:1.10.0|\
    spark.sql.catalog.my_catalog=org.apache.iceberg.spark.SparkCatalog|\
    spark.sql.catalog.my_catalog.type=rest|\
    spark.sql.catalog.my_catalog.uri=https://biglake.googleapis.com/iceberg/v1/restcatalog|\
    spark.sql.catalog.my_catalog.warehouse=gs://biglake-public-nyc-taxi-iceberg|\
    spark.sql.catalog.my_catalog.io-impl=org.apache.iceberg.gcp.gcs.GCSFileIO|\
    spark.sql.catalog.my_catalog.header.x-goog-user-project=$PROJECT_ID|\
    spark.sql.catalog.my_catalog.header.X-Iceberg-Access-Delegation=vended-credentials|\
    spark.sql.catalog.my_catalog.rest.auth.type=org.apache.iceberg.gcp.auth.GoogleAuthManager|\
    spark.sql.defaultCatalog=my_catalog|\
    spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions|\
    spark.log.level=ERROR"
    
  2. Crea un archivo de consulta de prueba simple:
    cat <<EOF > test.py
    from pyspark.sql import SparkSession
    
    spark = SparkSession.builder.getOrCreate()
    
    spark.sql("SHOW TABLES IN public_data").show()
    EOF
    
  3. Envía el trabajo por lotes:
    gcloud dataproc batches submit pyspark \
      --project=$PROJECT_ID \
      --region=$REGION \
      --version=2.3 \
      --properties="$METASTORE_PROPERTIES" \
      --deps-bucket=gs://$DEPS_BUCKET \
      test.py
    
    Espera unos minutos para que finalice la ejecución del trabajo por lotes.Una vez que se complete el trabajo, deberías ver un resultado similar al siguiente:
    +-----------+----------------+-----------+
    |  namespace|       tableName|isTemporary|
    +-----------+----------------+-----------+
    |public_data|     nyc_taxicab|      false|
    |public_data|nyc_taxicab_2021|      false|
    +-----------+----------------+-----------+
    

4. Consulta datos estructurados de Iceberg

Una vez conectado, tienes acceso completo a SQL a los conjuntos de datos. Consultaremos el conjunto de datos de NYC Taxi modelado como una tabla de Iceberg.

Ejecuta una consulta de agregación estándar

Crea un archivo llamado query.py:

cat <<EOF > query.py
from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()

query = """
SELECT
  passenger_count,
  COUNT(1) AS num_trips,
  ROUND(AVG(total_amount), 2) AS avg_fare,
  ROUND(AVG(trip_distance), 2) AS avg_distance
FROM public_data.nyc_taxicab
WHERE data_file_year = 2021 AND passenger_count > 0
GROUP BY passenger_count
ORDER BY num_trips DESC
"""

spark.sql(query).show()
EOF

Y envíalo con Serverless for Apache Spark:

gcloud dataproc batches submit pyspark \
  --project=$PROJECT_ID \
  --region=$REGION \
  --version=2.3 \
  --properties="$METASTORE_PROPERTIES" \
  --deps-bucket=gs://$DEPS_BUCKET \
  query.py

Espera unos minutos para que finalice la ejecución del trabajo por lotes.

Una vez que se complete el trabajo, deberías ver un resultado similar al siguiente:

+---------------+---------+--------+------------+
|passenger_count|num_trips|avg_fare|avg_distance|
+---------------+---------+--------+------------+
|              1| 21508009|   18.82|        3.03|
|              2|  4424746|   20.22|        3.40|
|              3|  1164846|   19.84|        3.27|
|              5|   718282|   18.88|        3.07|
|              4|   466485|   20.61|        3.44|
|              6|   452467|   18.97|        3.11|
|              7|       78|   65.24|        3.71|
|              8|       49|   57.39|        5.88|
|              9|       35|   73.26|        6.20|
|             96|        1|   17.00|        2.00|
|            112|        1|   15.00|        2.00|
+---------------+---------+--------+------------+

¿Por qué usar Apache Iceberg aquí?

  • Eliminación de particiones: La consulta filtra en data_file_year = 2021. Iceberg permite que el motor omita por completo el análisis de datos de otros años.
  • Agilidad del motor: Puedes ejecutarlo en Spark, Trino o BigQuery sin copiar datos.

5. Viaje en el tiempo en Apache Iceberg

Una de las funciones más potentes de Iceberg es el viaje en el tiempo. Te permite consultar datos tal como existían en una versión o instantánea anterior.

Ver el historial de la tabla

Crea un archivo llamado history.py:

cat <<EOF > history.py
from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()

spark.sql("SELECT * FROM public_data.nyc_taxicab.history").show()
EOF

Y envíalo:

gcloud dataproc batches submit pyspark \
  --project=$PROJECT_ID \
  --region=$REGION \
  --version=2.3 \
  --properties="$METASTORE_PROPERTIES" \
  --deps-bucket=gs://$DEPS_BUCKET \
  history.py

Deberías ver un resultado similar al siguiente en tu consola:

+--------------------+-------------------+-------------------+-------------------+
|     made_current_at|        snapshot_id|          parent_id|is_current_ancestor|
+--------------------+-------------------+-------------------+-------------------+
|2026-01-07 21:32:...|6333415779680505547|               NULL|               true|
|2026-01-07 21:34:...|1840345522877675925|6333415779680505547|               true|
|2026-01-07 21:36:...|7203554539964460256|1840345522877675925|               true|
|2026-01-07 21:38:...|4573466015237516024|7203554539964460256|               true|
|2026-01-07 21:40:...|3353190952148867790|4573466015237516024|               true|
|2026-01-07 21:42:...|1335547378580631681|3353190952148867790|               true|
|2026-01-07 21:44:...|8203141258229894239|1335547378580631681|               true|
|2026-01-07 21:46:...|1597048231706307813|8203141258229894239|               true|
|2026-01-07 21:48:...|6247811509231462655|1597048231706307813|               true|
|2026-01-07 21:50:...|2527184310045633322|6247811509231462655|               true|
|2026-01-07 21:52:...|2512764101237223642|2527184310045633322|               true|
|2026-01-07 21:52:...|7045957533358062548|2512764101237223642|               true|
|2026-01-07 21:53:...| 531753237516076726|7045957533358062548|               true|
|2026-01-07 21:53:...|4184653573199718274| 531753237516076726|               true|
|2026-01-07 21:54:...|5125223829492177301|4184653573199718274|               true|
|2026-01-07 21:54:...|6844673237417600305|5125223829492177301|               true|
|2026-01-07 21:54:...|6634828203344518093|6844673237417600305|               true|
|2026-01-07 21:55:...|7637728273407236194|6634828203344518093|               true|
|2026-01-07 21:55:...|3424071684958740192|7637728273407236194|               true|
|2026-01-07 21:55:...|1743746294196424254|3424071684958740192|               true|
+--------------------+-------------------+-------------------+-------------------+

Verás filas que representan diferentes IDs de instantáneas y cuándo se confirmaron.

Compara el recuento de filas actual con el anterior

Crea un archivo llamado timetravel.py:

cat <<EOF > timetravel.py
from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()

query = """
SELECT 'Current State' AS version, COUNT(*) AS count FROM public_data.nyc_taxicab
UNION ALL
SELECT 'Past State' AS version, COUNT(*) AS count FROM public_data.nyc_taxicab VERSION AS OF 4573466015237516024
"""

spark.sql(query).show()
EOF

Y envíalo:

gcloud dataproc batches submit pyspark \
  --project=$PROJECT_ID \
  --region=$REGION \
  --version=2.3 \
  --properties="$METASTORE_PROPERTIES" \
  --deps-bucket=gs://$DEPS_BUCKET \
  timetravel.py

Deberías ver un resultado similar al siguiente en tu consola:

+-------------+----------+
|      version|     count|
+-------------+----------+
|Current State|1293069366|
|   Past State|  72878594|
+-------------+----------+

Esto garantiza que puedas auditar los cambios en los datos a lo largo del tiempo.

6. IA estructurada con BigQuery ML

Ahora que exploraste los datos de Iceberg, usemos las capacidades de IA de BigQuery. Dado que el catálogo público de Iceberg es de solo lectura, podemos usar BigQuery para entrenar un modelo en nuestro espacio de trabajo leyendo desde tablas públicas.

Crea un conjunto de datos local

Primero, crea un conjunto de datos en tu proyecto para almacenar el modelo de IA con la CLI de bq:

bq mk --location=$REGION --project_id=$PROJECT_ID iceberg_ai

Entrena un modelo de regresión lineal

Ahora entrenarás un modelo de regresión lineal con la tabla pública de BigLake Iceberg.

Crea un archivo de consulta y entrena el modelo con bq query:

cat <<'EOF' > train_model.sql
CREATE OR REPLACE MODEL `iceberg_ai.predict_fare`
OPTIONS(model_type='LINEAR_REG', input_label_cols=['fare_amount']) AS
SELECT fare_amount, passenger_count, CAST(trip_distance AS FLOAT64) AS trip_distance
FROM `bigquery-public-data`.`biglake-public-nyc-taxi-iceberg`.public_data.nyc_taxicab
WHERE fare_amount > 0 AND trip_distance > 0 AND RAND() < 0.01; -- Using 1% of data to downsample
EOF

bq query --location=$REGION --use_legacy_sql=false < train_model.sql

Predice con el modelo

Ahora que el modelo está entrenado, puedes usarlo para predecir los importes de las tarifas de los viajes nuevos con ML.PREDICT.

Crea un archivo de consulta y ejecuta la predicción con bq query:

cat <<'EOF' > predict_fare.sql
SELECT
  predicted_fare_amount, passenger_count, trip_distance
FROM
  ML.PREDICT(MODEL `iceberg_ai.predict_fare`,
    (
    SELECT 2 AS passenger_count, 5.0 AS trip_distance
    )
  );
EOF

bq query --location=$REGION --use_legacy_sql=false < predict_fare.sql

Debería ver un resultado similar al siguiente:

+-----------------------+-----------------+---------------+
| predicted_fare_amount | passenger_count | trip_distance |
+-----------------------+-----------------+---------------+
|     14.12252095150709 |               2 |           5.0 |
+-----------------------+-----------------+---------------+

7. IA no estructurada con BigLake

Los datos no son solo filas y columnas. Los Unified Data Lakehouses también controlan datos no estructurados (imágenes, PDFs). Usemos tablas de objetos y referencias de objetos para consultar datos no estructurados.

Las tablas de objetos son tablas externas de solo lectura en BigQuery que enumeran objetos en una ruta de Cloud Storage. Cada fila representa un archivo, con columnas para metadatos como uri, size y una columna ref especial que contiene el ObjectRef.

Las referencias de objetos (ObjectRef) apuntan a los datos reales de un solo archivo. Las funciones modernas de BigQuery ML (como AI.GENERATE o AI.AGG) consumen ObjectRef para leer el contenido del archivo (imágenes, audio o texto) para el análisis sin cargar los bytes en una tabla estándar.

Crea un conjunto de datos para la IA no estructurada

Primero, crea un segundo conjunto de datos en tu proyecto para almacenar las tablas de objetos con la CLI de bq en la multirregión US:

bq mk --location=US --project_id=$PROJECT_ID iceberg_object_ai

Crea una conexión externa

Para consultar datos almacenados en Cloud Storage (tablas de objetos y datos no estructurados) desde BigQuery, debes crear una conexión externa.

Ejecuta lo siguiente en Cloud Shell para crear una conexión de Cloud Resource:

bq mk --connection --project_id=$PROJECT_ID --location=US --connection_type=CLOUD_RESOURCE my-conn

Busca el ID de la cuenta de servicio creada para tu conexión:

CONNECTION_SA=$(bq show --format=json --project_id=$PROJECT_ID --connection $PROJECT_ID.us.my-conn | jq -r '.serviceAccountId // .cloudResource.serviceAccountId')

Otorga a la cuenta de servicio los roles de usuario de Vertex AI y visualizador de objetos de Storage para que pueda llamar a los modelos de Gemini y leer datos de GCS:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$CONNECTION_SA" \
  --role="roles/aiplatform.user"

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$CONNECTION_SA" \
  --role="roles/storage.objectViewer"

Crea una tabla de objetos

Usaremos la conexión externa my-conn creada en la sección anterior para acceder a datos no estructurados. Crea un archivo de consulta y crea la tabla de objetos con bq query:

cat <<'EOF' > create_object_table.sql
CREATE EXTERNAL TABLE `iceberg_object_ai.sample_images`
WITH CONNECTION `us.my-conn`
OPTIONS (
  object_metadata = 'SIMPLE',
  uris = ['gs://cloud-samples-data/vision/landmark/*']
);
EOF

bq query --use_legacy_sql=false < create_object_table.sql

Usa Gemini en datos de objetos

Ahora ejecuta una consulta con Gemini para evaluar las imágenes sin descargarlas.

Consulta las imágenes con SQL estándar a través de bq query:

cat <<EOF > query_images.sql
SELECT
  uri,
  image_analysis.description
FROM (
  SELECT
    uri,
    AI.GENERATE(
      (
        'Identify what is happening in the image.',
        ref
      ),
      connection_id => 'us.my-conn',
      endpoint => 'gemini-2.5-flash-lite',
      output_schema => 'event STRING, severity STRING, description STRING'
    ) AS image_analysis
  FROM
    iceberg_object_ai.sample_images
);
EOF

bq query --use_legacy_sql=false < query_images.sql

Resultado de muestra:

+----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                           uri                            |                                                                                                                                                                                                                             description                                                                                                                                                                                                                             |
+----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| gs://cloud-samples-data/vision/landmark/eiffel_tower.jpg | The Eiffel Tower stands tall against a cloudy sky, overlooking the Seine River in Paris. Boats are docked along the riverbank, and trees line the opposite shore, with bridges and buildings visible in the distance.                                                                                                                                                                                                                                               |
| gs://cloud-samples-data/vision/landmark/pofa.jpg         | A wide shot shows the Palace of Fine Arts, a monumental structure in San Francisco, California. The building features a large rotunda with a dome, surrounded by colonnades. In front of the rotunda is a lagoon. Several people are walking around the grounds. The sky is blue with a few scattered clouds.                                                                                                                                                       |
| gs://cloud-samples-data/vision/landmark/st_basils.jpeg   | A monument stands in front of Saint Basil's Cathedral in Moscow under a bright blue sky with scattered white clouds. The cathedral features distinctive onion domes in various colors and patterns, including red, blue and white stripes, green and beige stripes, and red and blue diamonds. A large green tree partially obscures the left side of the cathedral. People are visible in the foreground near the base of the monument and the cathedral entrance. |
+----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Explora ObjectRefs directamente: Análisis de opiniones

Si bien las tablas de objetos administran las referencias de archivos automáticamente, puedes interactuar con estos objetos directamente con las referencias de objetos de BigQuery para ejecutar análisis sobre la marcha en archivos individuales.

Por ejemplo, puedes usar un archivo de texto pequeño almacenado en tu propio bucket de GCS (con la variable $DEPS_BUCKET creada anteriormente) y analizarlo con OBJ.MAKE_REF con bq query.

Primero, crea un archivo de texto pequeño y súbelo a tu bucket:

cat <<'EOF' > review.txt
This product is fantastic! It exceeded my expectations. The quality is top-notch. I highly recommend it to everyone!
EOF

gcloud storage cp review.txt gs://${DEPS_BUCKET}/review.txt

Ahora consulta el archivo con OBJ.MAKE_REF dentro de SQL estándar:

cat <<EOF > sentiment_analysis.sql
SELECT
  AI.GENERATE(
    (
      'Analyze the sentiment of this text file. Is it positive, negative, or neutral? Explain why.',
      OBJ.MAKE_REF('gs://${DEPS_BUCKET}/review.txt', 'us.my-conn')
    ),
    connection_id => 'us.my-conn',
    endpoint => 'gemini-2.5-flash-lite'
  ).result AS ml_generate_text_result;
EOF

bq query --use_legacy_sql=false < sentiment_analysis.sql

Resultado de muestra:

+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                 ml_generate_text_result                                                                                  |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| This text file has a **strongly positive** sentiment.                                                                                                                                    |
|                                                                                                                                                                                          |
| Here's why:                                                                                                                                                                              |
|                                                                                                                                                                                          |
| *   **Positive Keywords:** The text is filled with unequivocally positive words and phrases:                                                                                             |
|     *   "fantastic"                                                                                                                                                                      |
|     *   "exceeded my expectations"                                                                                                                                                       |
|     *   "top-notch"                                                                                                                                                                      |
|     *   "highly recommend"                                                                                                                                                               |
|                                                                                                                                                                                          |
| *   **Enthusiastic Language:** The use of exclamation marks ("!") further amplifies the positive tone, indicating excitement and strong approval.                                        |
|                                                                                                                                                                                          |
| *   **Lack of Negative or Neutral Elements:** There are no words, phrases, or implications that suggest any dissatisfaction, criticism, or even indifference.                            |
|                                                                                                                                                                                          |
| In summary, the author's language is enthusiastic and uses multiple strong positive descriptors, leaving no room for doubt that their opinion of the product is overwhelmingly positive. |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

8. Limpia

Para evitar cargos continuos en tu cuenta de Google Cloud, borra los recursos creados durante este codelab.

Borra el conjunto de datos y la conexión

Ejecuta lo siguiente en Cloud Shell para borrar tus conjuntos de datos y la conexión:

bq rm -r -f --location=$REGION iceberg_ai
bq rm -r -f --location=US iceberg_object_ai
bq rm --connection $PROJECT_ID.US.my-conn

Borra los buckets de GCS y los archivos locales

Libera espacio en los buckets de GCS y los archivos locales:

# Delete GCS buckets
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
gcloud storage rm -r gs://dataproc-temp-${REGION}-${PROJECT_NUMBER}-*
gcloud storage rm -r gs://dataproc-staging-${REGION}-${PROJECT_NUMBER}-*
gcloud storage rm -r gs://${DEPS_BUCKET}

# Delete local files
rm -f train_model.sql predict_fare.sql create_object_table.sql query_images.sql sentiment_analysis.sql test.py query.py history.py timetravel.py review.txt

También puedes borrar todo el proyecto si lo creaste solo para este lab.

9. Felicitaciones

¡Felicitaciones! Compilaste correctamente un Unified Data Lakehouse con Apache Iceberg, BigLake y BigQuery AI.

Qué aprendiste

  • Cómo conectarse y consultar catálogos de REST públicos de Apache Iceberg.
  • Usar el viaje en el tiempo en Iceberg para auditar versiones de conjuntos de datos.
  • Entrenar modelos de BigQuery ML en datos estructurados.
  • Conectar datos no estructurados (imágenes) con tablas de objetos y ObjectRef.
  • Usar Gemini directamente en BigQuery SQL para analizar imágenes.

Documentos de referencia