1. 소개
데이터가 다양한 물리적 스토리지 시스템에 걸쳐 있는 최신 엔터프라이즈 데이터 클라우드에서는 보안이 단편화되는 대규모 아키텍처 문제가 발생합니다.
데이터가 Google Cloud Storage의 Parquet과 같은 오픈소스 형식으로 물리적으로 저장되고 BigQuery SQL 또는 Apache Spark와 같은 여러 가지 다양한 엔진으로 쿼리될 때 민감한 데이터 (예: 금융 거래 금액)가 일관되게 보호되도록 하려면 어떻게 해야 할까요?
이 Codelab에서는 Apache Iceberg 테이블, BigQuery, Dataplex Universal Catalog를 사용하여 이러한 문제를 해결하는 관리형 데이터 레이크하우스 아키텍처를 빌드합니다. 코드형 인프라 (IaC)를 사용하여 제로 트러스트 보안 정책과 다양한 컴퓨팅 엔진에서 동적으로 적용되는 방식을 정의합니다.
기본 요건
- 결제가 사용 설정된 Google Cloud 프로젝트.
- SQL, IAM, Cloud Storage 개념에 대한 기본적인 이해.
학습할 내용
- Cloud Storage가 기본적으로 데이터를 보유하는 BigQuery에서 Apache Iceberg용 BigLake 테이블을 만드는 방법.
- 열 수준 보안 및 데이터 마스킹을 위해 정책 태그를 사용하여 중앙 집중식 데이터 정책을 적용하는 방법.
- Cloud 리소스 연결을 사용하여 물리적 스토리지 액세스를 논리적 데이터 액세스와 분리하는 방법.
- Apache Spark용 Google Cloud 서버리스를 사용하여 제로 트러스트 컴퓨팅 위임을 적용하여 오픈소스 엔진이 거버넌스를 우회할 수 없도록 하는 방법.
- 자동화된 데이터 계보를 시각화하는 방법.
아키텍처 개요: Iceberg의 범용 거버넌스

오픈소스 데이터 형식에 대한 세분화된 액세스 제어 (예: 열 수준 보안 및 데이터 마스킹)를 구현하려면 엄격하고 통합된 보안 아키텍처를 설정해야 합니다.
다이어그램에 나와 있는 것처럼 이 관리형 레이크하우스 패턴은 단편화된 보안 문제를 해결하기 위해 두 가지 주요 요소에 의존합니다.
🛡️ 보안 아키텍처 레이어 (왼쪽)
사용자 또는 외부 엔진이 Cloud Storage에 직접 액세스하도록 허용하는 대신(광범위한 버킷 수준 보안만 지원) 보안 기반을 빌드합니다.
- 오픈 형식, 관리형 메타데이터: 데이터는 오픈 Apache Iceberg (Parquet) 형식을 사용하여 Cloud Storage에 물리적으로 유지되는 반면 BigLake는 거버넌스 메타데이터를 원활하게 관리합니다.
- 논리적 보안 경계: 보안 Cloud 리소스 연결을 사용하여 물리적 스토리지 액세스를 논리적 데이터 액세스와 분리합니다. 최종 사용자에게는 원시 GCS 파일에 대한 직접적인 물리적 IAM 액세스 권한이 부여되지 않습니다.
- 제로 트러스트 컴퓨팅 위임: 실행 엔진이 거버넌스 규칙을 우회할 수 없도록 모든 데이터 읽기 요청은 BigQuery Storage API를 통해 엄격하게 라우팅됩니다. 이는 쿼리가 기본 BigQuery SQL에서 비롯되는지 오픈소스 Apache Spark에서 비롯되는지에 관계없이 적용됩니다.
🎯 중앙 집중식 정책 적용 (오른쪽)
보안 기반이 마련되면 Dataplex는 거버넌스를 위한 통합 브레인 역할을 합니다.
- 한 번 정의하고 어디서나 적용: Dataplex에서 정책 태그를 한 번만 정의하면 아키텍처에서 지원되는 모든 실행 런타임에 일관된 마스킹 규칙을 보편적으로 적용합니다.
- 동적 데이터 마스킹: 데이터가 쿼리되면 시스템에서 사용자의 ID를 즉시 평가합니다. 승인된 사용자는 SQL과 Spark 모두에서 마스킹되지 않은 원시 값 (예: 100.0)을 볼 수 있지만 제한된 사용자는 두 엔진 모두에서 제한된 열에 대해 마스크 처리된 NULL 값을 자동으로 수신합니다.
- 자동화된 데이터 계보: 데이터가 흐르고 변환될 때 Dataplex는 변환 메타데이터를 자동으로 캡처하여 커스텀 로깅 코드가 필요 없는 기본 제공 엔드 투 엔드 감사 가능성과 추적 가능성을 제공합니다.
2. 설정 및 요건
Cloud Shell 시작
Google Cloud를 노트북에서 원격으로 실행할 수 있지만, 이 Codelab에서는 Cloud에서 실행되는 명령줄 환경인 Google Cloud Shell을 사용합니다.
Google Cloud Console의 오른쪽 상단 툴바에 있는 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을 어수선하게 만들지 않기 위해 스파스 체크아웃 을 실행하여 이 Codelab에 필요한 Python 스크립트만 Google Cloud DevRel 저장소에서 다운로드합니다.
# 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. BigLake를 통해 기본 Iceberg 테이블 만들기
BigLake의 기본 기능을 사용하여 관리형 Iceberg 테이블을 만듭니다.
BigQuery 데이터 세트 만들기
먼저 BigQuery 데이터 세트를 만들어 Iceberg 테이블을 논리적으로 그룹화합니다.
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 테이블이 두 개 있습니다. BigLake는 메타데이터를 관리하지만 물리적 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 Dataplex 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로 반환하도록 하는 규칙을 프로그래매틱 방식으로 만듭니다. 그런 다음 마스크 처리된 리더 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"])
이 스키마 업데이트가 적용되면 BigLake는 Dataplex 논리적 태그를 Cloud Storage 버킷에 저장된 물리적 Parquet 파일로 즉시 연결합니다.
스크립트를 실행하여 테이블 스키마를 업데이트합니다.
python 4_attach_tag.py
5. Dataplex 정책 확인
이제 중앙 집중식 거버넌스가 작동하는지 테스트할 시간입니다. Dataplex 정책이 보편적으로 적용됨을 증명하기 위해 두 가지 다른 엔진에서 이를 테스트합니다.
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\`"
Dataplex 마스킹 규칙으로 인해 금액 열은 모든 행에 대해 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는 버킷 수준 권한만 이해하므로 Dataplex 마스킹 규칙이 완전히 우회됩니다.
이를 방지하려면 컴퓨팅 위임을(를) Spark-BigQuery 커넥터를 사용하여 적용합니다. 이 커넥터는 보안 브리지 역할을 하며 Spark 읽기 요청을 BigQuery Storage API를 통해 라우팅하므로 데이터가 Spark 클러스터로 전송되기 전에 Dataplex 거버넌스 규칙이 동적으로 평가됩니다.
다운로드한 read_transactions.py 스크립트 내의 핵심 로직을 살펴보세요.
# Reading data via Compute Delegation (Dataplex 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)
Spark가 Iceberg 파일의 gs:// 경로를 가리키지 않는다는 점에 유의하세요. .format("bigquery")를 지정하면 BigQuery Storage API가 읽기 요청을 가로채고 Spark 작업을 실행하는 사용자의 ID를 확인하고 Dataplex 마스킹 규칙을 적용하며 승인된 데이터만 Spark DataFrame으로 다시 반환합니다.
Dataproc에서 액세스할 수 있도록 이 PySpark 스크립트를 Cloud Storage 버킷에 업로드합니다.
# Upload script to GCS
gsutil cp read_transactions.py gs://${ICEBERG_BUCKET}/scripts/read_transactions.py
관리자로 Spark 실행:
Apache Spark용 Google Cloud 서버리스를 사용합니다. 이 관리형 서비스를 사용하면 전용 클러스터를 프로비저닝, 구성 또는 관리할 필요 없이 Spark 워크로드를 직접 실행할 수 있습니다.
echo "🚀 Submitting Dataproc Serverless 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 Dataproc Serverless 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가 요청을 가로채고 Dataplex 정책을 적용했습니다. 애널리스트의 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
엔진에 관계없이 결과가 동일함을 증명했습니다. Dataplex 정책이 성공적으로 적용되었습니다. 하지만 프로덕션에서는 어떤 것을 사용해야 할까요?
- BigQuery SQL: SQL이 원하는 엔진이고 계산을 직접 실행하는 워크플로에 적합합니다. 빠른 분석 및 비즈니스 인텔리전스에 적합합니다.
- Apache Spark: Python을 사용하여 더 복잡한 워크로드를 허용하므로 고급 머신러닝 파이프라인 또는 기존 Hadoop 코드에 적합합니다.
주요 내용: 어떤 엔진을 사용하든 컴퓨팅 위임을 적용하면 중앙 집중식 제로 트러스트 거버넌스 레이어를 우회할 수 없습니다.
6. 자동화된 데이터 계보
모든 엔터프라이즈 데이터 아키텍처에서 데이터의 출처와 변경된 방식을 정확히 파악하는 것은 규정 준수, 디버깅, 신뢰 구축에 매우 중요합니다. 이 개념을 데이터 계보 라고 합니다. '관리자가 일일 판매 보고서를 보고 있다면 이러한 숫자를 계산하는 데 사용된 원시 테이블은 무엇인가요?'와 같은 기본적인 질문에 답합니다.
일반적으로 이 수명 주기를 추적하려면 데이터 엔지니어가 커스텀 로깅 코드를 수동으로 작성하거나 복잡한 서드 파티 도구를 사용하여 SQL 스크립트를 파싱해야 합니다. 하지만 관리형 Google Cloud 레이크하우스에서는 이 추적이 기본 제공되며 완전히 자동입니다.
Codelab의 앞부분에서 원시 트랜잭션 테이블에서 만든 transactions_summary 테이블을 기억하시나요? BigQuery가 CREATE TABLE AS SELECT 문을 실행하면 컴퓨팅 엔진이 변환 메타데이터를 자동으로 캡처하여 Dataplex로 전송합니다. 결과를 살펴보겠습니다.
계보 시각화
- Google Cloud 콘솔에서 Dataplex Universal Catalog > 검색으로 이동합니다.
- 검색창에
lakehouse_retail_demo.transactions를 입력하고 테이블을 클릭합니다. - 계보 탭을 클릭합니다.

Dataplex Knowledge Engine에서 생성한 대화형 그래프가 표시됩니다. 이 그래프는 대상 테이블 (transactions_summary)이 관리형 원시 Iceberg 테이블 (transactions)에서 파생되었음을 증명합니다. 데이터 감사에 필수적인 엔드 투 엔드 추적 가능성을 달성했습니다.
7. 삭제
이 Codelab에서 사용한 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 다음 단계를 따르세요.
Dataplex 거버넌스 리소스 삭제
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 통합: BigLake는 물리적 파일을 Cloud Storage에 안전하게 저장하면서 오픈소스 Iceberg 테이블을 기본적으로 관리할 수 있습니다.
- 보안을 위한 컴퓨팅 위임: BigQuery Storage API를 통해 쿼리를 라우팅하여 기본적으로 부분 액세스를 제한할 수 없는 물리적 파일에 세분화된 동적 마스킹을 적용했습니다.
- 엔진에 구애받지 않는 거버넌스: 정책 태그를 사용하면 기본 SQL 또는 Apache Spark 런타임을 통해 쿼리되는지 여부에 관계없이 규칙을 한 번 정의하고 보편적으로 적용할 수 있습니다.
- 데이터 검색 가능성: Dataplex Knowledge Engine은 데이터 계보를 자동으로 추적하여 필수적인 엔터프라이즈 감사 가능성을 제공했습니다.
다음 단계
- 고급 액세스 제어 살펴보기: 더 복잡한 보안 시나리오를 구현하려면 추가 기능으로 BigLake를 맞춤설정하는 방법에 관한 공식 문서를 검토하세요.
- 생성형 AI를 위한 비정형 데이터 관리: BigLake 객체 테이블을 알아봅니다. 이 정확한 보안 브리지 패턴을 Cloud Storage의 비정형 파일 (PDF, 이미지)로 확장하여 Vertex AI 및 RAG 파이프라인을 위한 보안 관리형 데이터 기반을 구축합니다.