Google Cloud Lakehouse 및 Knowledge Catalog로 관리형 Iceberg 레이크하우스 빌드

1. 소개

데이터가 다양한 물리적 스토리지 시스템에 저장되는 최신 엔터프라이즈 데이터 클라우드에서는 보안이 분산되는 대규모 아키텍처 문제가 발생합니다.

데이터가 Google Cloud 스토리지의 Parquet과 같은 오픈소스 형식으로 물리적으로 저장되고 BigQuery SQL 또는 Apache Spark와 같은 여러 엔진에서 쿼리될 때 민감한 정보 (예: 금융 거래 금액)가 일관되게 보호되도록 하려면 어떻게 해야 하나요?

이 Codelab에서는 Apache Iceberg 테이블, BigQuery, Knowledge Catalog를 사용하여 이러한 문제를 해결하는 거버넌스 데이터 레이크하우스 아키텍처를 빌드합니다. 코드형 인프라 (IaC)를 사용하여 제로 트러스트 보안 정책과 다양한 컴퓨팅 엔진에서 동적으로 적용되는 방식을 정의합니다.

기본 요건

  • 결제가 사용 설정된 Google Cloud 프로젝트.
  • SQL, IAM, Cloud Storage 개념에 대한 기본적인 이해

학습할 내용

아키텍처 개요: Iceberg의 범용 거버넌스

6f05a096ec94f996.png

오픈소스 데이터 형식에 대해 세분화된 액세스 제어 (예: 열 수준 보안 및 데이터 마스킹)를 구현하려면 엄격하고 통합된 보안 아키텍처를 설정해야 합니다.

다이어그램에 나와 있듯이 이 관리형 레이크하우스 패턴은 두 가지 주요 요소를 기반으로 분산된 보안 문제를 해결합니다.

🛡️ 보안 아키텍처 레이어 (왼쪽)

사용자 또는 외부 엔진이 Cloud Storage에 직접 액세스하도록 허용하는 대신(버킷 수준의 광범위한 보안만 지원) 안전한 기반을 구축합니다.

  • 개방형 형식, 관리형 메타데이터: 데이터는 개방형 Apache Iceberg (Parquet) 형식을 사용하여 Cloud Storage에 물리적으로 유지되며, Lakehouse는 거버넌스 메타데이터를 원활하게 관리합니다.
  • 논리적 보안 경계: 보안 Cloud 리소스 연결을 사용하여 물리적 스토리지 액세스와 논리적 데이터 액세스를 분리합니다. 최종 사용자에게는 원시 GCS 파일에 대한 직접 물리적 IAM 액세스 권한이 부여되지 않습니다.
  • 무신뢰 컴퓨팅 위임: 실행 엔진이 거버넌스 규칙을 우회하지 못하도록 모든 데이터 읽기 요청은 BigQuery Storage API를 통해 엄격하게 라우팅됩니다. 쿼리가 기본 BigQuery SQL에서 시작되든 오픈소스 Apache Spark에서 시작되든 적용됩니다.

🎯 중앙 집중식 정책 시행 (오른쪽)

보안 기반이 마련되면 Knowledge Catalog는 거버넌스를 위한 통합 두뇌 역할을 합니다.

  • 한 번 정의하고 어디서나 적용: Knowledge Catalog에서 정책 태그를 한 번만 정의하면 아키텍처에서 지원되는 모든 실행 런타임에 일관된 마스킹 규칙이 보편적으로 적용됩니다.
  • 동적 데이터 마스킹: 데이터가 쿼리되면 시스템에서 즉시 사용자의 ID를 평가합니다. 권한이 있는 사용자는 SQL과 Spark 모두에서 마스킹되지 않은 원시 값 (예: 100.0)을 볼 수 있지만, 제한된 사용자는 두 엔진 모두에서 제한된 열에 대해 마스킹된 NULL 값을 자동으로 받게 됩니다.
  • 자동 데이터 계보: 데이터가 흐르고 변환될 때 Knowledge Catalog는 변환 메타데이터를 자동으로 캡처하여 맞춤 로깅 코드 없이 기본 제공되는 엔드 투 엔드 감사 가능성과 추적 가능성을 제공합니다.

2. 설정 및 요건

Cloud Shell 시작

Google Cloud를 노트북에서 원격으로 실행할 수 있지만, 이 Codelab에서는 Cloud에서 실행되는 명령줄 환경인 Google Cloud Shell을 사용합니다.

Google Cloud Console의 오른쪽 상단 툴바에 있는 Cloud Shell 아이콘을 클릭합니다.

Cloud Shell 활성화

환경을 프로비저닝하고 연결하는 데 몇 분 정도 소요됩니다. 완료되면 다음과 같이 표시됩니다.

환경이 연결되었음을 보여주는 Google Cloud Shell 터미널 스크린샷

가상 머신에는 필요한 개발 도구가 모두 들어있습니다. 영구적인 5GB 홈 디렉터리를 제공하고 Google Cloud에서 실행되므로 네트워크 성능과 인증이 크게 개선됩니다. 이 Codelab의 모든 작업은 브라우저 내에서 수행할 수 있습니다. 아무것도 설치할 필요가 없습니다.

환경 초기화

Cloud Shell을 열고 프로젝트 변수를 설정하여 모든 명령어가 올바른 인프라를 타겟팅하도록 합니다.

export PROJECT_ID=$(gcloud config get-value project)
export REGION="us-central1"
export ICEBERG_BUCKET="iceberg-retail-demo-${PROJECT_ID}"
export DATASET_ID="lakehouse_retail_demo"
export CONN_NAME="iceberg-bq-conn-demo"

그런 다음 두 페르소나를 정의합니다.

export USER_ANALYST="retail-analyst-demo"
export EMAIL_ANALYST="${USER_ANALYST}@${PROJECT_ID}.iam.gserviceaccount.com"

export USER_MANAGER="retail-manager-demo"
export EMAIL_MANAGER="${USER_MANAGER}@${PROJECT_ID}.iam.gserviceaccount.com"
export CURRENT_USER=$(gcloud config get-value account)

API 사용 설정

필요한 Google Cloud 서비스를 사용 설정합니다.

gcloud services enable \
  bigquery.googleapis.com \
  bigqueryconnection.googleapis.com \
  datacatalog.googleapis.com \
  bigquerydatapolicy.googleapis.com \
  datalineage.googleapis.com \
  dataplex.googleapis.com \
  dataproc.googleapis.com \
  storage-component.googleapis.com

Codelab 소스 코드 다운로드

Cloud Shell이 어수선해지지 않도록 스파스 체크아웃을 실행하여 Google Cloud DevRel 저장소에서 이 Codelab에 필요한 Python 스크립트만 다운로드합니다.

# Shallow clone without full history
git clone --depth 1 --filter=blob:none --sparse https://github.com/GoogleCloudPlatform/devrel-demos.git
cd devrel-demos

# Download only the specific folder
git sparse-checkout set data-analytics/governed-lakehouse
cd data-analytics/governed-lakehouse

스토리지 만들기

매우 안전한 관리 Iceberg 데이터를 저장할 버킷을 만듭니다.

gcloud storage buckets create gs://${ICEBERG_BUCKET} --location=${REGION}

ID 및 보안 준비

클라우드 리소스 연결을 구성합니다. 이는 원시 Iceberg 파일을 읽기 위한 영구 실제 IAM 키를 보유하는 유일한 엔티티입니다.

# Create the BigQuery connection
bq mk --connection \
    --connection_type=CLOUD_RESOURCE \
    --location=${REGION} \
    ${CONN_NAME}

# Retrieve the connection's automatically generated Service Account
export BQ_CONN_SVC_ACCT=$(bq show --format=json --connection ${REGION}.${CONN_NAME} \
    | jq -r '.cloudResource.serviceAccountId')

# Grant Storage Object Admin to the connection for the Iceberg bucket
gcloud storage buckets add-iam-policy-binding gs://${ICEBERG_BUCKET} \
    --member="serviceAccount:${BQ_CONN_SVC_ACCT}" \
    --role="roles/storage.objectAdmin" \
    --quiet

다음으로 사용자 페르소나를 설정합니다. 사용자에게는 물리적 스토리지 액세스가 아닌 논리적 액세스 권한이 부여됩니다. IAM 전파 지연으로 인한 오류를 방지하려면 먼저 계정을 만들고 몇 초 정도 기다린 후 역할을 할당합니다.

echo "Creating Service Accounts..."
for USER in "${USER_ANALYST}" "${USER_MANAGER}"; do
    gcloud iam service-accounts create ${USER} --display-name="Lakehouse ${USER}"
done

echo "⏳ Waiting 15 seconds for IAM propagation..."
sleep 15

echo "Granting IAM Roles to Service Accounts..."
for USER in "${USER_ANALYST}" "${USER_MANAGER}"; do
    EMAIL="${USER}@${PROJECT_ID}.iam.gserviceaccount.com"
    
    # Allow Cloud Shell to impersonate them for testing
    gcloud iam service-accounts add-iam-policy-binding ${EMAIL} \
        --member="user:${CURRENT_USER}" \
        --role="roles/iam.serviceAccountTokenCreator" \
        --quiet

    # Allow logical viewing of the catalog, querying, and running Dataproc jobs
    for ROLE in "roles/datacatalog.viewer" "roles/bigquery.dataViewer" "roles/bigquery.user" "roles/bigquery.connectionUser" "roles/serviceusage.serviceUsageConsumer" "roles/dataproc.worker"; do
        gcloud projects add-iam-policy-binding ${PROJECT_ID} \
            --member="serviceAccount:${EMAIL}" \
            --role="${ROLE}" \
            --quiet
    done
done

# Grant the Manager data creation rights
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:${EMAIL_MANAGER}" \
    --role="roles/bigquery.dataEditor" \
    --quiet

echo "✅ Identity and Security setup completed!"

3. Lakehouse를 통해 기본 Iceberg 테이블 만들기

Lakehouse의 기본 기능을 사용하여 관리형 Iceberg 테이블을 만듭니다.

BigQuery 데이터 세트 만들기

먼저 Iceberg 테이블을 논리적으로 그룹화할 BigQuery 데이터 세트를 만듭니다.

echo "Creating BigQuery Dataset..."
bq mk --location=${REGION} --dataset ${PROJECT_ID}:${DATASET_ID}

Iceberg 테이블 만들기

그런 다음 다음 명령어를 실행하여 테이블을 만듭니다. table_format = 'ICEBERG'을 지정하고 Cloud Storage 버킷 및 연결에 직접 매핑하는 OPTIONS 블록을 확인하세요.

echo "Creating Iceberg tables..."

# Inventory table
bq query --use_legacy_sql=false \
"CREATE OR REPLACE TABLE \`${PROJECT_ID}.${DATASET_ID}.inventory\` (
    product_id INT64, 
    product_name STRING, 
    stock_count INT64
) 
WITH CONNECTION \`${REGION}.${CONN_NAME}\` 
OPTIONS (
    file_format = 'PARQUET',
    table_format = 'ICEBERG',
    storage_uri = 'gs://${ICEBERG_BUCKET}/inventory/'
);"

# Transactions table
bq query --use_legacy_sql=false \
"CREATE OR REPLACE TABLE \`${PROJECT_ID}.${DATASET_ID}.transactions\` (
    id INT64, 
    item STRING, 
    amount FLOAT64, 
    transaction_date DATE
) 
WITH CONNECTION \`${REGION}.${CONN_NAME}\` 
OPTIONS (
    file_format = 'PARQUET',
    table_format = 'ICEBERG',
    storage_uri = 'gs://${ICEBERG_BUCKET}/transactions/'
);"

데이터로 테이블 채우기

마지막으로 새로 생성된 Iceberg 테이블에 샘플 데이터를 삽입합니다.

echo "Inserting data into Iceberg tables..."

# Insert into Inventory table
bq query --use_legacy_sql=false \
"INSERT INTO \`${PROJECT_ID}.${DATASET_ID}.inventory\` (product_id, product_name, stock_count)
VALUES (101, 'Widget A', 500), (102, 'Widget B', 250), (103, 'Widget C', 800);"

# Insert into Transactions table
bq query --use_legacy_sql=false \
"INSERT INTO \`${PROJECT_ID}.${DATASET_ID}.transactions\` (id, item, amount, transaction_date)
VALUES 
    (1, 'Widget A', 100.0, DATE '2024-01-01'), 
    (2, 'Widget B', 150.0, DATE '2024-01-02'), 
    (3, 'Widget C', 50.0, DATE '2024-01-03');"

이제 완전히 작동하는 Iceberg 테이블이 두 개 있습니다. 레이크하우스는 메타데이터를 관리하지만 실제 Parquet 파일은 GCS 버킷에 안전하게 저장됩니다.

ETL 파이프라인 시뮬레이션

실제 시나리오에서는 원시 데이터가 비즈니스 보고를 위해 요약 표로 집계되는 경우가 많습니다. 데이터 엔지니어 역할을 맡아 원시 거래 데이터에서 일일 판매 요약 테이블을 만들어 보겠습니다.

(참고: Google Cloud에서 백그라운드 메타데이터를 처리할 충분한 시간이 있도록 지금 이 단계를 실행하세요. 이것이 중요한 이유는 Codelab 후반부에서 알아봅니다.

echo "Creating transactions summary table..."
bq query --use_legacy_sql=false \
"CREATE TABLE \`${PROJECT_ID}.${DATASET_ID}.transactions_summary\` AS 
 SELECT transaction_date, SUM(amount) as total_sales, COUNT(id) as transaction_count 
 FROM \`${PROJECT_ID}.${DATASET_ID}.transactions\` 
 GROUP BY transaction_date;"

4. 중앙화된 거버넌스: Python을 사용하여 정책 정의

프로덕션 환경에서는 UI를 통해 거버넌스 정책을 구성하기가 어렵고 확장 및 유지관리도 어렵습니다. 대신 코드형 인프라 (IaC)를 사용하는 것이 좋습니다.

이 섹션에서는 Google Cloud Python SDK를 사용하여 제로 트러스트 거버넌스 규칙을 프로그래매틱 방식으로 단계별로 만들고 적용합니다.

Python 환경 설정

먼저 라이브러리 충돌을 방지하고 필요한 Google Cloud SDK를 설치하기 위해 격리된 Python 환경 (venv)을 설정합니다.

Cloud Shell에서 다음 명령어를 실행합니다.

# Create and activate a virtual environment
python3 -m venv lakehouse_env
source lakehouse_env/bin/activate

# Install required Knowledge Catalog and BigQuery governance libraries
pip install google-cloud-datacatalog google-cloud-bigquery-datapolicies google-cloud-bigquery --quiet

echo "✅ Python environment is ready!"

분류 및 정책 태그 만들기

분류는 논리적 컨테이너이고 정책 태그는 민감한 열에 연결할 특정 라벨입니다. 열 수준 보안을 적용하려면 먼저 논리적 컨테이너 (분류)와 특정 라벨 (정책 태그)이 필요합니다.

1_create_taxonomy.py 내부를 살펴보면 다음과 같은 핵심 로직이 표시됩니다.

# Create Taxonomy with Fine-Grained Access Control enabled
taxonomy = datacatalog_v1.Taxonomy(
    display_name="BusinessCritical",
    activated_policy_types=[datacatalog_v1.Taxonomy.PolicyType.FINE_GRAINED_ACCESS_CONTROL]
)
created_taxonomy = client.create_taxonomy(parent=parent, taxonomy=taxonomy)

# Create Policy Tag inside the Taxonomy
policy_tag = datacatalog_v1.PolicyTag(display_name="RestrictedFinancial")
created_policy_tag = client.create_policy_tag(parent=created_taxonomy.name, policy_tag=policy_tag)

FINE_GRAINED_ACCESS_CONTROL 정책 유형을 명시적으로 설정하면 표준 메타데이터 태그가 엄격한 제로 트러스트 보안 경계로 변환됩니다. 이 태그가 있는 열은 기본적으로 모든 사용자의 액세스를 거부합니다.

스크립트를 실행하여 리소스를 만듭니다.

python 1_create_taxonomy.py

마스킹 규칙 (데이터 정책) 구성

이제 권한이 없는 사용자가 태그가 지정된 열을 쿼리할 때 발생하는 상황을 정의합니다. 값이 NULL로 반환되도록 하는 데이터 정책을 만들고 이 규칙을 분석가 페르소나에 연결합니다.

2_create_masking.py 내에서 스크립트는 방금 만든 정책 태그 ID를 동적으로 조회하고 데이터 정책을 적용합니다.

# Define a Masking Policy that always returns NULL
data_policy = bigquery_datapolicies_v1.DataPolicy(
    data_policy_id="mask_financial_null",
    policy_tag=policy_tag_id,
    data_policy_type=bigquery_datapolicies_v1.DataPolicy.DataPolicyType.DATA_MASKING_POLICY,
    data_masking_policy=bigquery_datapolicies_v1.DataMaskingPolicy(
        predefined_expression=bigquery_datapolicies_v1.DataMaskingPolicy.PredefinedExpression.ALWAYS_NULL
    )
)

# ... (Policy creation code) ...

# Bind the Masked Reader role to the Analyst
iam_policy.bindings.add(
    role="roles/bigquerydatapolicy.maskedReader", 
    members=[f"serviceAccount:{analyst_email}"]
)

이 코드는 기본 값이 NULL로 반환되도록 하는 규칙을 프로그래매틱 방식으로 만듭니다. 그런 다음 마스킹된Reader IAM 역할을 분석가 페르소나에만 할당하여 데이터의 마스킹된 버전만 볼 수 있도록 합니다.

스크립트를 실행하여 마스킹 규칙을 구성합니다.

python 2_create_masking.py

세분화된 액세스 권한 부여

제로 트러스트 설정으로 인해 현재 태그가 지정된 열을 읽을 수 있는 사용자가 없습니다. 관리자 및 개인 계정에 대한 액세스 권한을 명시적으로 부여해야 합니다.

3_grant_access.py 내에서 정책 태그 자체의 IAM 정책을 수정합니다.

# Grant original data read access
iam_policy.bindings.add(
    role="roles/datacatalog.categoryFineGrainedReader",
    members=[f"serviceAccount:{manager_email}", f"user:{current_user}"]
)
client.set_iam_policy(request=iam_policy_pb2.SetIamPolicyRequest(resource=policy_tag_id, policy=iam_policy))

categoryFineGrainedReader 역할을 추가하면 이러한 특정 주 구성원이 마스킹 규칙을 우회하고 마스킹되지 않은 원시 데이터를 읽을 수 있습니다.

스크립트를 실행하여 액세스 권한을 부여합니다.

python 3_grant_access.py

BigQuery 테이블에 정책 태그 연결

마지막으로 이 논리적 정책 태그를 실제 Iceberg 테이블 스키마에 연결해야 합니다.

4_attach_tag.py를 살펴보세요. 스크립트는 BigQuery 테이블 스키마를 가져오고, 필드를 반복하고, amount 열에만 태그를 연결합니다.

new_schema =[]
for field in table.schema:
    if field.name == 'amount':
        # Wrap the Policy Tag ID and attach it to the column
        policy_tags_list = bigquery.PolicyTagList(names=[policy_tag_id])
        new_field = bigquery.SchemaField(
            name=field.name, field_type=field.field_type, mode=field.mode,
            description=field.description, policy_tags=policy_tags_list
        )
        new_schema.append(new_field)
    else:
        new_schema.append(field)

# Update the table schema in BigQuery
table.schema = new_schema
client.update_table(table, ["schema"])

이 스키마 업데이트가 적용되면 Lakehouse는 Knowledge Catalog 논리 태그를 Cloud Storage 버킷에 저장된 실제 Parquet 파일에 즉시 연결합니다.

스크립트를 실행하여 테이블 스키마를 업데이트합니다.

python 4_attach_tag.py

5. Knowledge Catalog 정책 확인

이제 중앙 집중식 거버넌스가 작동하는지 테스트할 차례입니다. Knowledge Catalog 정책이 보편적으로 적용되는지 증명하기 위해 두 가지 엔진에서 이를 테스트합니다.

BigQuery 네이티브 SQL을 사용하여 확인

먼저 Cloud Shell을 사용하여 두 페르소나의 ID를 가정하고 BigQuery의 기본 SQL 엔진을 사용하여 테이블을 쿼리합니다.

관리자 (권한이 있는 사용자)로 테스트합니다.

# Impersonate the manager
gcloud config set auth/impersonate_service_account ${EMAIL_MANAGER}

# Query the transactions table
bq query --use_legacy_sql=false "SELECT * FROM \`${PROJECT_ID}.${DATASET_ID}.transactions\`"

관리자에게 세분화된 권한의 리더 역할이 있으므로 원시 금액 값이 표시됩니다.

+----+----------+--------+------------------+
| id |   item   | amount | transaction_date |
+----+----------+--------+------------------+
|  1 | Widget A |  100.0 |       2024-01-01 |
|  3 | Widget C |   50.0 |       2024-01-03 |
|  2 | Widget B |  150.0 |       2024-01-02 |
+----+----------+--------+------------------+

분석가 (제한된 사용자)로 테스트합니다.

gcloud config set auth/impersonate_service_account ${EMAIL_ANALYST}

bq query --use_legacy_sql=false "SELECT * FROM \`${PROJECT_ID}.${DATASET_ID}.transactions\`"

Knowledge Catalog 마스킹 규칙으로 인해 금액 열이 모든 행에 대해 NULL로 반환됩니다.

+----+----------+--------+------------------+
| id |   item   | amount | transaction_date |
+----+----------+--------+------------------+
|  1 | Widget A |   NULL |       2024-01-01 |
|  3 | Widget C |   NULL |       2024-01-03 |
|  2 | Widget B |   NULL |       2024-01-02 |
+----+----------+--------+------------------+

ID 복원

Cloud Shell 인증 상태를 정리하여 관리자 사용자로 돌아갑니다.

# Unset impersonation
gcloud config unset auth/impersonate_service_account

Apache Spark를 사용하여 확인 (컴퓨팅 위임)

데이터 과학자가 Apache Spark를 사용하여 이 테이블을 읽으면 어떻게 될까요? Spark가 실제 GCS Parquet 파일을 직접 읽으면 Cloud Storage는 버킷 수준 권한만 이해하므로 Knowledge Catalog 마스킹 규칙이 완전히 우회됩니다.

이를 방지하려면 Spark-BigQuery 커넥터를 사용하여 컴퓨팅 위임을 적용하세요. 이 커넥터는 보안 브리지 역할을 하며, Spark 읽기 요청을 BigQuery Storage API를 통해 라우팅하여 데이터가 Spark 클러스터로 전송되기 전에 Knowledge Catalog 거버넌스 규칙이 동적으로 평가되도록 합니다.

다운로드한 read_transactions.py 스크립트의 핵심 로직을 살펴보세요.

# Reading data via Compute Delegation (Knowledge Catalog policies are applied dynamically here)
df = spark.read \
    .format("bigquery") \
    .option("table", f"{project_id}.{dataset_id}.{table_name}") \
    .load()

print("\n=== 📊 Data Preview ===")
df.show(truncate=False)

Iceberg 파일의 gs:// 경로를 Spark에 지정하지 않습니다. .format("bigquery")를 지정하면 BigQuery Storage API가 읽기 요청을 가로채고, Spark 작업을 실행하는 사용자의 ID를 확인하고, Knowledge Catalog 마스킹 규칙을 적용하고, 권한이 있는 데이터만 Spark DataFrame에 반환합니다.

Managed Apache Spark가 액세스할 수 있도록 이 PySpark 스크립트를 Cloud Storage 버킷에 업로드합니다.

# Upload script to GCS
gsutil cp read_transactions.py gs://${ICEBERG_BUCKET}/scripts/read_transactions.py

관리자로 Spark를 실행합니다.

관리형 Apache Spark를 사용합니다. 이 관리형 서비스를 사용하면 전용 클러스터를 프로비저닝, 구성 또는 관리할 필요 없이 Spark 워크로드를 직접 실행할 수 있습니다.

echo "🚀 Submitting Managed Apache Spark Job as [MANAGER]..."
gcloud dataproc batches submit pyspark gs://${ICEBERG_BUCKET}/scripts/read_transactions.py \
    --project=${PROJECT_ID} \
    --region=${REGION} \
    --service-account=${EMAIL_MANAGER} \
    --version=2.3 \
    -- ${PROJECT_ID} ${DATASET_ID} \
    --format="value(name)"

터미널에서 작업 출력 로그를 확인합니다. 관리자에게 세분화된 권한의 리더 역할이 있으므로 Spark는 마스킹되지 않은 원시 금액을 성공적으로 가져옵니다.

=== 📊 Data Preview ===
+---+--------+------+-------------------+
|id |item    |amount|transaction_date   |
+---+--------+------+-------------------+
|1  |Widget A|100.0 |2024-01-01         |
|2  |Widget B|150.0 |2024-01-02         |
|3  |Widget C|50.0  |2024-01-03         |
+---+--------+------+-------------------+

분석가로 Spark를 실행합니다.

이제 동일한 Spark 작업을 제출하되 이번에는 분석가 페르소나를 가장합니다.

echo "🚀 Submitting Managed Apache Spark Job as [ANALYST]..."
gcloud dataproc batches submit pyspark gs://${ICEBERG_BUCKET}/scripts/read_transactions.py \
    --project=${PROJECT_ID} \
    --region=${REGION} \
    --service-account=${EMAIL_ANALYST} \
    --version=2.3 \
    -- ${PROJECT_ID} ${DATASET_ID} \
    --format="value(name)"

로그를 다시 확인합니다. 분석가가 정확히 동일한 Spark 코드를 실행했지만 BigQuery Storage API가 요청을 가로채고 Knowledge Catalog 정책을 적용했습니다. 분석가의 Spark DataFrame에 금액이 null로 표시됩니다.

=== 📊 Data Preview ===
+---+--------+------+-------------------+
|id |item    |amount|transaction_date   |
+---+--------+------+-------------------+
|1  |Widget A|null  |2024-01-01         |
|2  |Widget B|null  |2024-01-02         |
|3  |Widget C|null  |2024-01-03         |
+---+--------+------+-------------------+

아키텍처 트레이드 오프: BigQuery SQL과 Spark 비교

엔진에 관계없이 결과가 동일함을 증명했습니다. Knowledge Catalog 정책이 적용되었습니다. 하지만 프로덕션에서는 어떤 것을 사용해야 할까요?

  • BigQuery SQL: SQL이 원하는 엔진이고 계산을 직접 실행하는 워크플로에 적합합니다. 빠른 분석 및 비즈니스 인텔리전스에 적합합니다.
  • Apache Spark: Python을 사용하여 더 복잡한 워크로드를 처리할 수 있으므로 고급 머신러닝 파이프라인이나 기존 Hadoop 코드에 적합합니다.

핵심 사항: 어떤 엔진을 사용하든 컴퓨팅 위임을 적용하면 중앙 집중식 제로 트러스트 거버넌스 레이어를 우회할 수 없습니다.

6. 자동화된 데이터 계보

모든 엔터프라이즈 데이터 아키텍처에서 데이터의 출처와 변경 방식을 정확히 파악하는 것은 규정 준수, 디버깅, 신뢰 구축에 매우 중요합니다. 이 개념을 데이터 계보라고 합니다. '관리자가 일일 판매 보고서를 보고 있다면 이 수치를 계산하는 데 어떤 원시 테이블이 사용되었나요?'와 같은 기본적인 질문에 답합니다.

일반적으로 이 수명 주기를 추적하려면 데이터 엔지니어가 맞춤 로깅 코드를 수동으로 작성하거나 복잡한 서드 파티 도구를 사용하여 SQL 스크립트를 파싱해야 합니다. 하지만 관리형 Google Cloud 레이크하우스에서는 이 추적이 기본적으로 제공되며 완전히 자동입니다.

코드랩 앞부분에서 원시 거래 테이블에서 만든 transactions_summary 테이블을 기억하시나요? BigQuery가 CREATE TABLE AS SELECT 문을 실행했을 때 컴퓨팅 엔진이 변환 메타데이터를 자동으로 캡처하여 Knowledge Catalog로 전송했습니다. 결과를 확인해 보겠습니다.

계보 시각화하기

  1. Google Cloud 콘솔에서 Knowledge Catalog > 검색으로 이동합니다.
  2. 검색창에 lakehouse_retail_demo.transactions를 입력하고 표를 클릭합니다.
  3. 계보 탭을 클릭합니다.

c890a11d6ea1cca4.png

타겟 테이블 (transactions_summary)이 관리되는 원시 Iceberg 테이블 (transactions)에서 파생되었음을 증명하는 Knowledge Engine에서 생성된 대화형 그래프가 표시됩니다. 데이터 감사에 필수적인 엔드 투 엔드 추적 가능성을 확보했습니다.

7. 삭제

이 Codelab에서 사용한 리소스의 비용이 Google Cloud 계정에 청구되지 않도록 하려면 다음 단계를 따르세요.

Knowledge Catalog 거버넌스 리소스 삭제

BigQuery 데이터 세트 또는 Cloud Storage 버킷을 삭제하기 전에 논리적 거버넌스 규칙을 삭제해야 합니다. 저장소의 cleanup_governance.py 스크립트를 살펴보면 다음과 같은 종료 시퀀스가 표시됩니다.

# 1. Delete Data Policy
data_policy_name = f"{parent_loc}/dataPolicies/mask_financial_null"
dp_client.delete_data_policy(name=data_policy_name)

# 2. Find and Delete Taxonomy (This auto-deletes child Policy Tags)
taxonomies = catalog_client.list_taxonomies(parent=parent_loc)
taxonomy_id = next((t.name for t in taxonomies if t.display_name == "BusinessCritical"), None)
catalog_client.delete_taxonomy(name=taxonomy_id)

여기서 순서가 중요합니다. 스크립트는 먼저 정책 태그를 사용하므로 데이터 정책 (마스킹 규칙)을 삭제합니다. 정책이 삭제되면 상위 분류를 삭제할 때 리소스 종속성 오류를 트리거하지 않고 모든 기본 정책 태그가 자동으로 캐스케이드되어 삭제됩니다.

Python 정리 스크립트를 실행합니다.

python cleanup_governance.py

ID, 스토리지, 컴퓨팅 애셋 삭제

이제 거버넌스 레이어가 분리되었으므로 BigQuery 테이블, Cloud Storage 버킷, 서비스 계정, 로컬 Python 환경을 안전하게 삭제할 수 있습니다.

Cloud Shell에서 다음 포괄적인 정리 블록을 복사하여 실행합니다.

echo "Deleting Service Accounts and Impersonation Bindings..."
export CURRENT_USER=$(gcloud config get-value account)

for USER in "${USER_ANALYST}" "${USER_MANAGER}"; do
    EMAIL="${USER}@${PROJECT_ID}.iam.gserviceaccount.com"
    
    # Remove impersonation binding
    gcloud iam service-accounts remove-iam-policy-binding ${EMAIL} \
        --member="user:${CURRENT_USER}" \
        --role="roles/iam.serviceAccountTokenCreator" \
        --quiet > /dev/null 2>&1
        
    # Delete the Service Account
    gcloud iam service-accounts delete ${EMAIL} --quiet
done

echo "Removing BigQuery Dataset and Tables..."
bq rm -f ${DATASET_ID}.transactions_summary
bq rm -f ${DATASET_ID}.transactions
bq rm -f ${DATASET_ID}.inventory
bq rm -f -d ${DATASET_ID}

echo "Removing BigQuery Cloud Resource Connection..."
bq rm --connection --location=${REGION} ${CONN_NAME}

echo "Removing Iceberg Cloud Storage Bucket..."
gcloud storage rm --recursive gs://${ICEBERG_BUCKET} --quiet

echo "Removing Auto-generated Dataproc Staging & Temp Buckets..."
for BUCKET in $(gcloud storage ls | grep -E "gs://dataproc-(staging|temp)-${REGION}"); do
    gcloud storage rm --recursive $BUCKET --quiet
done

echo "Deactivating and removing the local Python environment..."
deactivate
cd ../..
rm -rf devrel-demos

echo "✅ Clean up completed successfully!"

이 단계를 완료하면 프로젝트에 고아 리소스나 숨겨진 정책이 남아 있지 않습니다.

8. 축하합니다.

완전한 거버넌스를 갖춘 검색 가능한 데이터 레이크하우스를 구현했습니다.

다음과 같은 내용을 학습했습니다.

  • 네이티브 Iceberg 통합: 레이크하우스는 Cloud Storage에 실제 파일을 안전하게 저장하면서 오픈소스 Iceberg 테이블을 네이티브로 관리할 수 있습니다.
  • 보안을 위한 컴퓨팅 위임: BigQuery Storage API를 통해 쿼리를 라우팅하여 기본적으로 부분 액세스를 제한할 수 없는 실제 파일에 세부적인 동적 마스킹을 적용했습니다.
  • 엔진에 구애받지 않는 거버넌스: 정책 태그를 사용하면 기본 SQL 또는 Apache Spark 런타임을 통해 쿼리하는지 여부에 관계없이 규칙을 한 번 정의하고 보편적으로 적용할 수 있습니다.
  • 데이터 검색 가능성: Knowledge Engine은 데이터 계보를 자동으로 추적하여 필수적인 엔터프라이즈 감사 가능성을 제공했습니다.

다음 단계

  • 고급 액세스 제어 살펴보기: 더 복잡한 보안 시나리오를 구현하려면 추가 기능으로 Lakehouse 맞춤설정에 관한 공식 문서를 검토하세요.
  • 생성형 AI를 위한 비정형 데이터 관리: 객체 테이블을 살펴봅니다. 이 정확한 보안 브리지 패턴을 Cloud Storage의 비구조화 파일 (PDF, 이미지)로 확장하여 Gemini Enterprise Agent Engine 및 RAG 파이프라인을 위한 안전하고 관리되는 데이터 기반을 구축합니다.