إنشاء بحيرة بيانات منظَّمة بتنسيق Iceberg باستخدام Google Cloud Lakehouse وKnowledge Catalog

1. مقدمة

في "سحابة بيانات" حديثة للمؤسسات، حيث يتم تخزين البيانات في أنظمة تخزين فعلية مختلفة، يواجه المستخدمون تحديًا كبيرًا في تصميم بنية الأمان المجزّأة.

كيف تضمن حماية البيانات الحسّاسة (مثل مبالغ المعاملات المالية) بشكل متّسق عند تخزين البيانات فعليًا بتنسيقات مفتوحة المصدر، مثل Parquet على مساحة تخزين Google Cloud، والاستعلام عنها من خلال محركات مختلفة متعددة، مثل BigQuery SQL أو Apache Spark؟

في هذا الدرس التطبيقي حول الترميز، ستنشئ بنية بحيرة بيانات منظَّمة تحلّ هذه المشاكل باستخدام جداول Apache Iceberg وBigQuery وKnowledge Catalog. ستستخدم "البنية الأساسية كرمز برمجي" (IaC) لتحديد سياسات الأمان التي لا تثق بأي جهاز، وكيفية تطبيقها بشكل ديناميكي على محركات الحوسبة المختلفة.

المتطلبات الأساسية

  • مشروع Google Cloud تم تفعيل الفوترة فيه
  • فهم أساسي لمفاهيم SQL و"إدارة الهوية وإمكانية الوصول" وCloud Storage

ما ستتعلمه

نظرة عامة على البنية: الإدارة العامة على Iceberg

6f05a096ec94f996.png

لتحقيق تحكّم دقيق في الوصول (مثل الأمان على مستوى الأعمدة وإخفاء البيانات) في تنسيقات البيانات المفتوحة المصدر، يجب إنشاء بنية أمان موحّدة وصارمة.

كما هو موضّح في الرسم البياني، يعتمد نمط مستودع البيانات الخاضع للإدارة هذا على ركيزتَين أساسيتَين لحلّ مشكلة الأمان المجزّأ:

🛡️ طبقات البنية الآمنة (على اليمين)

بدلاً من السماح للمستخدمين أو المحرّكات الخارجية بالوصول إلى Cloud Storage مباشرةً، الذي يتيح فقط أمانًا واسع النطاق على مستوى الحزمة، يمكنك إنشاء أساس آمن.

  • تنسيق مفتوح، بيانات وصفية مُدارة: تبقى البيانات فعليًا في Cloud Storage باستخدام تنسيق Apache Iceberg (Parquet) المفتوح، بينما تدير Lakehouse البيانات الوصفية الحاكمة بسلاسة.
  • حدود الأمان المنطقي: يمكنك فصل الوصول إلى التخزين الفعلي عن الوصول إلى البيانات المنطقية باستخدام اتصال آمن بمورد على السحابة الإلكترونية. لا يتم منح المستخدمين النهائيين إذن وصول مادي مباشر إلى ملفات GCS الأولية.
  • تفويض الحساب بدون ثقة: لضمان عدم تمكّن أي محرك تنفيذ من تجاوز قواعد الحوكمة، يتم توجيه جميع طلبات قراءة البيانات بدقة من خلال BigQuery Storage API. وينطبق ذلك سواء كان مصدر طلب البحث هو لغة SQL الأصلية في BigQuery أو Apache Spark مفتوح المصدر.

🎯 تنفيذ السياسات بشكل مركزي (صحيح)

بعد توفّر الأساس الآمن، يعمل "كتالوج المعرفة" كمركز موحّد لإدارة البيانات:

  • التعريف مرة واحدة والتطبيق في كل مكان: يمكنك تحديد "علامات السياسات" في "كتالوج المعرفة" مرة واحدة فقط، وتطبّق البنية قواعد إخفاء متسقة على مستوى جميع أوقات تشغيل التنفيذ المتوافقة.
  • إخفاء البيانات الديناميكي: عند طلب البيانات، يقيّم النظام هوية المستخدم في الوقت الفعلي. بينما يرى المستخدمون المصرّح لهم القيم الأولية غير المخفية (مثل 100.0) في كلّ من SQL وSpark، سيتلقّى المستخدمون الذين لديهم أذونات محدودة تلقائيًا قيم NULL مخفية للأعمدة المحظورة في كلّ من المحرّكين.
  • تتبُّع مصدر البيانات المبرمَج: أثناء تدفّق البيانات وتحويلها، يسجّل "كتالوج المعرفة" تلقائيًا البيانات الوصفية للتحويل، ما يوفّر إمكانية تدقيق وتتبُّع شاملة ومضمّنة بدون الحاجة إلى رمز تسجيل مخصّص.

2. الإعداد والمتطلبات

بدء Cloud Shell

على الرغم من إمكانية تشغيل Google Cloud عن بُعد من الكمبيوتر المحمول، ستستخدم في هذا الدرس التطبيقي حول الترميز Google Cloud Shell، وهي بيئة سطر أوامر تعمل في السحابة الإلكترونية.

من Google Cloud Console، انقر على رمز Cloud Shell في شريط الأدوات أعلى يسار الصفحة:

تفعيل Cloud Shell

لن يستغرق توفير البيئة والاتصال بها سوى بضع لحظات. عند الانتهاء، من المفترض أن يظهر لك ما يلي:

لقطة شاشة لواجهة سطر الأوامر في Google Cloud Shell توضّح أنّه تم ربط البيئة

يتم تحميل هذه الآلة الافتراضية مزوّدة بكل أدوات التطوير التي ستحتاج إليها. توفّر هذه الخدمة دليلًا منزليًا ثابتًا بسعة 5 غيغابايت، وتعمل على Google Cloud، ما يؤدي إلى تحسين أداء الشبكة والمصادقة بشكل كبير. يمكن إكمال جميع المهام في هذا الدرس العملي ضمن المتصفّح. لست بحاجة إلى تثبيت أي تطبيق.

إعداد البيئة

افتح 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)

تفعيل واجهات برمجة التطبيقات

فعِّل خدمات 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

تنزيل رمز المصدر للدرس التطبيقي حول الترميز

لتجنُّب إرباك Cloud Shell، ستنفّذ استخراجًا جزئيًا لتنزيل نصوص 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}

إعداد الهويات والأمان

ضبط اتصال مورد السحابة الإلكترونية وهي الجهة الوحيدة التي تحتفظ بمفاتيح إدارة الهوية وإمكانية الوصول المادية الدائمة لقراءة ملفات Iceberg الأولية.

# 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

بعد ذلك، عليك إعداد شخصيات المستخدمين. يتم منح المستخدمين إذن الوصول المنطقي، وليس إذن الوصول إلى مساحة التخزين الفعلية. لتجنُّب الأخطاء الناتجة عن تأخُّر نشر إدارة الهوية وإمكانية الوصول، عليك إنشاء الحسابات أولاً، والانتظار لبضع ثوانٍ، ثم تعيين أدوارها.

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- إنشاء جداول Iceberg أصلية من خلال Lakehouse

ستستخدم الإمكانات الأصلية في Lakehouse لإنشاء جداول Iceberg المُدارة.

إنشاء مجموعة بيانات BigQuery

أولاً، أنشئ مجموعة بيانات BigQuery لتجميع جداول Iceberg منطقيًا.

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

إنشاء جداول Iceberg

بعد ذلك، نفِّذ الأوامر التالية لإنشاء الجداول. لاحظوا الحقل OPTIONS حيث نحدّد table_format = 'ICEBERG' ونربطه مباشرةً بحزمة Cloud Storage والاتصال.

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 يعملان بالكامل. يدير Lakehouse البيانات الوصفية، ولكن يتم تخزين ملفات Parquet الفعلية بشكل آمن في حزمة GCS.

محاكاة مسار ETL

في سيناريو من العالم الواقعي، غالبًا ما يتم تجميع البيانات الأولية في جداول ملخّصة لإعداد تقارير النشاط التجاري. لنفترض أنّنا مهندسو بيانات ونريد إنشاء جدول ملخّص يومي للمبيعات من بيانات المعاملات الأولية.

(ملاحظة: نفِّذ هذه الخطوة الآن لكي يتوفّر لدى Google Cloud الوقت الكافي لمعالجة البيانات الوصفية في الخلفية. ستتعرّف على أهمية ذلك لاحقًا في هذا الدرس العملي.)

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

في بيئة الإنتاج، يصعب توسيع نطاق سياسات الحوكمة والحفاظ عليها من خلال واجهة المستخدم. بدلاً من ذلك، يُنصح بشدة باستخدام "البنية الأساسية كرمز" (IaC).

في هذا القسم، ستستخدم حزمة تطوير البرامج (SDK) من Google Cloud Python لإنشاء قواعد إدارة Zero-Trust وتنفيذها خطوة بخطوة بشكل آلي.

إعداد بيئة Python

أولاً، لنقم بإعداد بيئة Python معزولة (venv) لتجنُّب تعارض المكتبات وتثبيت حِزم Google Cloud SDK المطلوبة.

نفِّذ الأوامر التالية في 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، يبحث النص البرمجي ديناميكيًا عن معرّف علامة السياسة الذي أنشأته للتوّ ويطبّق سياسة بيانات:

# 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. بعد ذلك، يتمّ منح دور maskedReader في "إدارة الهوية وإمكانية الوصول" لشخصية المحلّل تحديدًا، ما يضمن عدم رؤية سوى النسخة المخفية من البيانات.

نفِّذ النص البرمجي لضبط قاعدة الإخفاء:

python 2_create_masking.py

منح إذن وصول دقيق

بسبب إعداداتنا التي لا تثق بأي مستخدم، لا يمكن لأحد قراءة العمود الذي تم وضع علامة عليه في الوقت الحالي. يجب منح إذن الوصول إلى الحساب الإداري وحسابك الشخصي بشكلٍ صريح.

داخل 3_grant_access.py، يمكنك تعديل سياسة "إدارة الهوية وإمكانية الوصول" الخاصة بعلامة السياسة نفسها:

# 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 على الفور العلامات المنطقية في "كتالوج المعرفة" بملفات Parquet الفعلية المخزَّنة في حزمة Cloud Storage.

نفِّذ النص البرمجي لتعديل مخطط الجدول:

python 4_attach_tag.py

5- التحقّق من سياسات "كتالوج المعارف"

حان الوقت لاختبار ما إذا كانت الحوكمة المركزية فعّالة. ستختبر ذلك على محركَين مختلفَين لإثبات أنّ سياسات "كتالوج المعرفة" يتم تطبيقها على مستوى العالم.

التحقّق باستخدام لغة SQL الأصلية في BigQuery

أولاً، ستستخدم Cloud Shell لتولّي هوية الشخصيتَين واستعلام الجدول باستخدام محرّك SQL الأصلي في BigQuery.

الاختبار بصفتك "المشرف" (مستخدم لديه امتيازات):

# 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\`"

بسبب قاعدة إخفاء البيانات في "كتالوج المعرفة"، يعرض عمود "المبلغ" القيمة 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 |
+----+----------+--------+------------------+

استعادة هويتك

نظِّف حالة مصادقة Cloud Shell للعودة إلى حساب المستخدم الإداري.

# Unset impersonation
gcloud config unset auth/impersonate_service_account

التحقّق باستخدام Apache Spark (تفويض الحساب)

ماذا لو استخدم عالم بيانات Apache Spark لقراءة هذا الجدول؟ إذا قرأ Spark ملفات Parquet الفعلية في GCS مباشرةً، سيتم تجاوز قواعد إخفاء البيانات في Knowledge Catalog بالكامل لأنّ Cloud Storage لا يفهم سوى الأذونات على مستوى الحزمة.

لمنع حدوث ذلك، يمكنك فرض تفويض الحوسبة باستخدام موصّل Spark-BigQuery. تعمل أداة الربط هذه كجسر آمن، حيث توجّه طلبات القراءة في Spark من خلال BigQuery Storage API ليتم تقييم قواعد إدارة Knowledge Catalog بشكلٍ ديناميكي قبل إرسال أي بيانات إلى مجموعة Spark.

ألقِ نظرة على المنطق الأساسي داخل النص البرمجي 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)

لاحظ أنّنا لا نشير إلى Spark بمسار gs:// لملفات Iceberg. من خلال تحديد .format("bigquery")، تعترض واجهة BigQuery Storage API طلب القراءة، وتتحقّق من هوية المستخدم الذي ينفّذ مهمة Spark، وتطبّق قواعد إخفاء البيانات في Knowledge Catalog، ولا تعرض سوى البيانات المصرّح بها في Spark DataFrame.

حمِّل نص PySpark البرمجي هذا إلى حزمة Cloud Storage ليتمكّن Dataproc من الوصول إليه:

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

تشغيل Spark كمدير:

ستستخدم الحوسبة بدون خادم من Google Cloud لـ Apache Spark. تتيح لك هذه الخدمة المُدارة تشغيل أحمال عمل 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 الطلب وفرضت سياسة 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

لقد أثبتّ للتو أنّ النتيجة متطابقة بغض النظر عن المحرك. تم تطبيق سياسة "كتالوج المعرفة" بنجاح. ولكن في مرحلة الإنتاج، أيّهما يجب استخدامه؟

  • BigQuery SQL: مناسبة لسير العمل التي تكون فيها SQL هي المحرّك المطلوب وتنفّذ العمليات الحسابية مباشرةً في مكانها. وهي مثالية لإجراء الإحصاءات السريعة وذكاء الأعمال.
  • Apache Spark: تتيح إمكانية تنفيذ مهام أكثر تعقيدًا من خلال استخدام Python، ما يجعلها مناسبة تمامًا لعمليات تعلُّم الآلة المتقدّمة أو رمز Hadoop القديم.

الخلاصة: بغض النظر عن المحرّك المستخدَم، من خلال فرض تفويض الحوسبة، لا يمكن أبدًا تجاوز طبقة إدارة الثقة المعدومة المركزية.

6. تتبُّع سير البيانات آليًا

في أي بنية بيانات مؤسسية، من المهم جدًا معرفة مصدر بياناتك بالضبط وكيف تم تغييرها لأغراض الامتثال وتصحيح الأخطاء وإثبات الموثوقية. يُعرف هذا المفهوم باسم سلسلة البيانات. تجيب هذه الأداة عن أسئلة أساسية، مثل: "إذا كان أحد المدراء يطّلع على تقرير مبيعات يومي، ما هي الجداول الأولية التي تم استخدامها لاحتساب هذه الأرقام؟"

في السابق، كان تتبُّع مراحل النشاط هذه يتطلّب من مهندسي البيانات كتابة رمز تسجيل مخصّص يدويًا أو استخدام أدوات معقّدة تابعة لجهات خارجية لتحليل نصوص لغة الاستعلامات البنيوية (SQL) البرمجية. ومع ذلك، في مستودع بيانات Google Cloud Lakehouse المنظَّم، يتم تتبُّع هذه المعلومات تلقائيًا وبشكل كامل.

تذكَّر transactions_summary الجدول الذي أنشأته من جدول المعاملات الأولية في وقت سابق من الدرس العملي؟ عندما نفّذت BigQuery عبارة CREATE TABLE AS SELECT، سجّل محرّك الحوسبة تلقائيًا البيانات الوصفية للتحويل وأرسلها إلى Knowledge Catalog. لنطّلع على النتيجة.

عرض مصدر البيانات

  1. في Google Cloud Console، انتقِل إلى Knowledge Catalog > بحث.
  2. اكتب lakehouse_retail_demo.transactions في شريط البحث وانقر على الجدول.
  3. انقر على علامة التبويب مصدر البيانات.

c890a11d6ea1cca4.png

سيظهر لك رسم بياني تفاعلي من إنشاء Knowledge Engine يثبت أنّ الجدول المستهدف (transactions_summary) تم استخراجه من جدول Iceberg الأصلي الخاضع للحوكمة (transactions). لقد حققت إمكانية التتبّع الشامل الضرورية لتدقيق البيانات.

7. تَنظيم

لتجنُّب تحمّل رسوم في حسابك على Google Cloud مقابل الموارد المستخدَمة في هذا الدرس التطبيقي حول الترميز، اتّبِع الخطوات التالية.

إزالة موارد إدارة "كتالوج المعرفة"

قبل حذف مجموعة بيانات 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

إزالة الهويات ومواد عرض التخزين والحوسبة

بعد فصل طبقة الحوكمة، يمكنك حذف جداول 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. تهانينا!

لقد نفّذت بنجاح Data Lakehouse خاضعًا للحوكمة بالكامل وقابلاً للاستكشاف.

لقد تعلّمتَ ما يلي:

  • التكامل الأصلي مع Iceberg: يمكن لـ Lakehouse إدارة جداول Iceberg مفتوحة المصدر بشكلٍ أصلي مع تخزين الملفات المادية بأمان في Cloud Storage.
  • تفويض الحساب لأغراض الأمان: من خلال توجيه طلبات البحث عبر BigQuery Storage API، فرضت إخفاءً ديناميكيًا دقيقًا على الملفات المادية التي لا يمكنها بشكلٍ أصلي حظر الوصول الجزئي.
  • الإدارة المستقلة عن المحرّك: تتيح لك "علامات السياسة" تحديد القواعد مرة واحدة وفرضها على مستوى جميع عمليات البحث سواء تم إجراؤها من خلال SQL الأصلي أو أوقات تشغيل Apache Spark.
  • إمكانية اكتشاف البيانات: تتبّع "محرك المعرفة" تلقائيًا مصدر البيانات، ما يوفّر إمكانية التدقيق الأساسية في المؤسسة.

ما هي الخطوات التالية؟

  • استكشاف عناصر التحكّم المتقدّمة في الوصول: لتنفيذ سيناريوهات أمان أكثر تعقيدًا، راجِع المستندات الرسمية حول تخصيص Lakehouse باستخدام ميزات إضافية.
  • إدارة البيانات غير المنظَّمة للذكاء الاصطناعي التوليدي: استكشِف جداول الكائنات. يمكنك توسيع نطاق نمط "الجسر الآمن" هذا ليشمل الملفات غير المنظَّمة (ملفات PDF والصور) في Cloud Storage، ما يؤدي إلى إنشاء أساس آمن ومحكوم للبيانات من أجل Vertex AI وعمليات التوليد المعزّز بالاسترجاع.