بناء منصة سحابية هجينة متطورة: ربط Kubernetes المحلي بخدمات Google Cloud بأمان وفعالية


مساحة إعلانية - أضف كود أدسنس هنا

مقدمة

في هذا المقال، ستتعلم كيفية تصميم وبناء منصة سحابية هجينة آمنة وقابلة للتطوير، تربط بنية Kubernetes التحتية المحلية الخاصة بك بمنصة Google Cloud Platform. يتيح هذا للتطبيقات المحلية استهلاك خدمات السحابة (خاصةً وحدات معالجة الرسوميات GPUs) دون الحاجة إلى مفاتيح طويلة الأمد وعرضة للخطر، أو إدارة يدوية لبيانات الاعتماد، أو أنماط شبكة محفوفة بالمخاطر.

لمن هذا المقال؟

  • مهندسو المنصات (Platform engineers)، ومهندسو موثوقية المواقع (SREs)، ومهندسو السحابة ذوو التركيز الأمني الذين يديرون بيئات Kubernetes مختلطة (محلية وسحابية).
  • الفرق التي تحتاج إلى وصول قابل للتدقيق والتوسع من أعباء العمل المحلية إلى موارد GCP (خاصةً مثيلات GPU) مع تقليل النفقات التشغيلية ومخاطر الانتشار.

ماذا ستحصل من هذا الدليل؟

  • الدافع والجوانب الاقتصادية وراء النهج الهجين (لماذا تدفع وحدات GPU أعباء العمل نحو السحابة غالبًا).
  • المزالق الشائعة لمفاتيح حسابات الخدمة وكيف تحدث "فجوات هوائية عرضية" في البيئات الحقيقية.
  • نمط عملي وشامل يستخدم Workload Identity Federation لمنح وحدات الـ Pods المحلية وصولاً قصير الأمد وقابلاً للتدقيق إلى GCP دون تضمين مفاتيح.

يتضمن المقال: شروحات مفاهيمية، ومقايضات أمنية، وأفضل الممارسات التشغيلية. أمثلة عملية وموارد Kubernetes/Terraform (موجودة في مستودع GitHub المشار إليه في نهاية المقال) حتى تتمكن من إعادة إنشاء الإعداد في بيئتك. استمر في القراءة لفهم الجانب النظري، ثم اتبع الأقسام العملية لتوفير موارد GCP، وتكوين Federation، وتطبيق السياسات باستخدام CEL و Kyverno، والتحقق من الوصول الآمن والقابل للتوسع لوحدات GPU من مجموعات Kubernetes المحلية الخاصة بك.

ملاحظة: موارد Kubernetes و Terraform مرتبطة في مستودع GitHub في نهاية هذا المقال.

المتطلبات المسبقة

قبل المتابعة، ستحتاج إلى:

  • مجموعة Kubernetes ليست GKE (محلية، أو على أجهزة عارية، أو مجموعة افتراضية).
  • مشروع Google Cloud مع تمكين واجهات برمجة التطبيقات (APIs) التالية: IAM، و Security Token Service (STS)، و Workload Identity.
  • تثبيت وتكوين Terraform.
  • تثبيت Kyverno في مجموعتك.
  • تثبيت Python 3 مع مكتبتي google-cloud-secret-manager و google-cloud-aiplatform (لخطوات التحقق. الكود متاح في مستودع GitHub).
  • وصول kubectl إلى مجموعتك.

لماذا تعتبر السحابة الهجينة مهمة؟

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

  • تحميل التحليلات إلى BigQuery: تحتفظ بتطبيقات التحليلات الخاصة بك محليًا لسيادة البيانات، ولكنك تقوم بتوجيه مجموعات البيانات الكبيرة إلى BigQuery للحصول على قوة معالجة عالمية المستوى — دون شراء خوادم إضافية.
  • إنشاء شبكة موحدة باستخدام Cloud Interconnect: باستخدام Cloud Interconnect أو Cloud VPN، يصبح مركز البيانات المحلي الخاص بك امتدادًا لشبكة Google Cloud Platform (GCP) Virtual Private Cloud (VPC). يمكن لتطبيقات الفواتير المحلية الخاصة بك التحدث إلى خدمات المستخدم المستندة إلى السحابة بزمن انتقال منخفض ودون التعرض للإنترنت العام.
  • قابلية التوسع الفعالة من حيث التكلفة عبر Cloud Storage: يمكنك استخدام التخزين السحابي كواجهة خلفية للتطبيقات المحلية، وتخزين السجلات والنسخ الاحتياطية والبيانات التاريخية مع الدفع فقط مقابل ما تستخدمه.
  • المزامنة القائمة على الأحداث باستخدام Pub/Sub: عندما يحدث شيء ما محليًا، تتيح رسالة عبر Cloud Pub/Sub لخدمات السحابة التفاعل فورًا — دون الحاجة إلى استقصاء يدوي.

اقتصاديات الهجين: وحدات GPU غيّرت كل شيء

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

ثم جاءت موجة الذكاء الاصطناعي. فجأة، يحتاج كل فريق إلى وحدات معالجة الرسوميات (GPUs). ليس واحدة أو اثنتين — بل العشرات من وحدات A100 للتدريب، وأساطيل من نقاط نهاية الاستدلال (inference endpoints)، وقواعد بيانات متجهة تحتاج إلى أن تكون قريبة من النماذج. وحدات GPU نادرة. تمتد المهل الزمنية لأجهزة GPU المحلية إلى أشهر. يوفرها موفرو السحابة في دقائق.

تبدو البنية التي تحقق جدوى اقتصادية على النحو التالي:

  • يتعامل مركز البيانات المحلي مع الجزء الأكبر من الحوسبة — خوادم الويب، ومنطق الأعمال، وقواعد البيانات، والمعالجة الدفعية. هذه حوسبة سلعية دفعت ثمنها بالفعل.
  • تتعامل السحابة مع ما هو نادر — الاستدلال المعزز بوحدات GPU، وتدريب النماذج، ونقاط نهاية الذكاء الاصطناعي/تعلم الآلة. تدفع مقابل كل طلب، وتتوسع عند الطلب، ولا تنتظر ستة أشهر للحصول على الأجهزة.

السحابة ليست وجهة انتقال كاملة — إنها امتداد للقدرات التي لا يمكنك بناؤها بسهولة محليًا. لكن أعباء العمل المحلية هذه تحتاج إلى المصادقة على خدمات السحابة. كل استدعاء API من مركز البيانات إلى نقطة نهاية Vertex AI، وكل طلب إلى خدمة استدلال مدعومة بوحدات GPU، وكل عملية كتابة إلى Cloud Storage لموارد النماذج — كل ذلك يتطلب بيانات اعتماد. هذه هي المشكلة التي يحلها هذا المقال.

لماذا تفشل مفاتيح حسابات الخدمة عند التوسع؟

إليك سيناريو يتكرر في آلاف الشركات يوميًا. يحتاج فريق تطوير إلى تطبيقهم المحلي للكتابة إلى Google Cloud Storage. الحل "الواضح"؟ إنشاء مفتاح حساب خدمة GCP، وتشفيره باستخدام base64، وتخزينه في Kubernetes Secret، وتركيبه في الـ Pod:

apiVersion: v1
kind: Secret
metadata:
  name: gcp-credentials
type: Opaque
data:
  key.json: eyJ0eXBlIjoic2VydmljZV9hY2NvdW50IiwicHJvamVjdF9pZCI6…

هذا يعمل. لكنه يقدم أيضًا مشاكل خطيرة:

  • لا تنتهي صلاحيته أبدًا. هذا المفتاح صالح حتى يتذكر شخص ما تدويره (وهو ما لن يحدث) أو يتم اختراقه (وهو ما سيحدث).
  • يمكن تسريبه بسهولة. يمكن لأي شخص لديه حق الوصول للقراءة إلى هذا الـ namespace تشغيل kubectl get secret -o yaml والحصول على وصول دائم إلى GCP.
  • لا يوجد مسار تدقيق لعبء العمل الفعلي. ترى GCP أن "service-account-xyz قام بالوصول إلى هذا الـ bucket" — وليس "pod frontend-abc-123 في الـ namespace production".
  • يتوسع بشكل سيء للغاية. 50 فريقًا × 3 بيئات × 4 مشاريع GCP = 600 مفتاح للتتبع والتدوير والأمل في عدم ارتكابها في Git.

فرق الأمن تعرف ذلك. لهذا السبب قامت العديد من المنظمات بالشيء الوحيد المعقول: لقد عطلت إنشاء مفتاح حساب الخدمة بالكامل.

كيف تحدث "الفجوة الهوائية العرضية"؟

عندما تقوم بتعطيل إنشاء المفاتيح، فأنت لم تحل مشكلة منصة السحابة الهجينة — بل جعلتها مشكلة شخص آخر. هذا الشخص عادة ما يكون فريق المنصة الذي يحدق في تذكرة Jira تقول "لا يمكن الوصول إلى GCP من البيئة المحلية، P1، يمنع الإصدار".

النتيجة؟ منصة "السحابة الهجينة" الخاصة بك ليست هجينة على الإطلاق. إنها نظامان منفصلان. تلجأ الفرق إلى بناء خدمات وسيطة، وبوابات API تقوم بتوكيل الطلبات، أو إيجاد طرق إبداعية للحصول على المفاتيح على أي حال. لا شيء من هذا يعتبر منصة. إنه حل مؤقت.

كيف تسد Workload Identity Federation الفجوة؟

كل مجموعة Kubernetes تصدر بالفعل رموز هوية موقعة تشفيريًا لكل Pod. ولدى Google Cloud خدمة مصممة خصيصًا للثقة في هذه الرموز. هذا هو Workload Identity Federation — وبالاقتران مع OpenID Connect (OIDC)، إنها القطعة المفقودة التي تجعل المنصات الهجينة تعمل بالفعل.

الخدمة مُسماة جيدًا بسبب كلمة Federation، فهي تعني أن GCP لا تخزن هويتك — بل توافق على الثقة في الهويات الصادرة عن نظام آخر، طالما يمكن التحقق منها تشفيريًا.

يعمل كل هذا من خلال مجموعة من الخطوات المنسقة جيدًا بالترتيب التالي:

  1. يقدم الـ Pod رمز JWT الصادر عن Kubernetes إلى نقطة نهاية STS الخاصة بـ GCP.
  2. يتحقق STS من التوقيع مقابل JWKS العام لمجموعتك.
  3. يتحقق STS من ادعاءات JWT مقابل قواعد Workload Identity Pool (الجمهور، المصدر، شروط CEL).
  4. يعيد STS رمز وصول Google قصير الأمد (عادةً ساعة واحدة) يستخدمه الـ Pod لاستدعاءات API.

من الجدير بالذكر أيضًا أن Workload Identity Federation ليست خاصة بـ Kubernetes. إنها تعمل مع AWS IAM، و Azure AD، و GitHub Actions OIDC، وأي موفر هوية متوافق مع OIDC.

كيف تعمل هوية Kubernetes؟

كل Pod مع ServiceAccount يحصل على JSON Web Token (JWT) يتم تركيبه تلقائيًا في المسار /run/secrets/kubernetes.io/serviceaccount/token. هذا ليس مجرد كتلة مبهمة — إنه تأكيد هوية موقع:

{
  "iss": "https://kubernetes.default.svc.cluster.local",
  "sub": "system:serviceaccount:production:backend-api",
  "aud": ["https://iam.googleapis.com/..."],
  "kubernetes.io": {
    "namespace": "production",
    "serviceaccount": {
      "name": "backend-api"
    }
  },
  "exp": 1735689600
}

في JWT، الادعاءات (claims) هي مجرد أزواج قيم-مفاتيح داخل حمولة الرمز (token's payload) — كل منها ادعاء يقدمه المصدر حول الموضوع. فكر فيها كحقائق يؤكدها الرمز، موقعة تشفيريًا بحيث يمكن للمحقق الوثوق بها.

الفكرة الحاسمة: يتم إنشاء هذا الرمز بواسطة مجموعة من JSON Web Key Set (JWKS) وهو قابل للتحقق من قبل أي شخص لديه المفاتيح العامة لمجموعتك، والتي يتم عرضها عبر نقطة نهاية JSON Web Key Set (JWKS):

kubectl get --raw /openid/v1/jwks

يمكن لخدمة Security Token Service (STS) من Google Cloud التحقق من صحة هذه الرموز. لا يتم تبادل مفاتيح. لا يتم تخزين أسرار. مجرد إثبات تشفيري للهوية.

كيفية إعداد موارد Google Cloud Platform

Workload Identity Pool هو حد ثقة — إعلان يقول "أنا أقبل الهويات من مصادر خارجية". يقوم OIDC Provider بتكوين كيفية التحقق من صحة تلك الهويات.

resource "google_iam_workload_identity_pool" "pool" {
  workload_identity_pool_id = "hybrid-platform-pool"
  project                   = "my-project"
}

resource "google_iam_workload_identity_pool_provider" "k8s_provider" {
  project                            = "my-project"
  workload_identity_pool_id          = google_iam_workload_identity_pool.pool.workload_identity_pool_id
  workload_identity_pool_provider_id = "on-prem-cluster"
  attribute_mapping = {
    "google.subject"      = "assertion.sub"
    "attribute.namespace" = "assertion['kubernetes.io']['namespace']"
  }
  attribute_condition = "attribute.namespace in [\"production\", \"staging\"]"
  oidc {
    issuer_uri = "https://kubernetes.default.svc.cluster.local"
    jwks_json  = file("jwks.json") # Your cluster's public keys
  }
}

نقطتان يجب ملاحظتهما هنا:

  • يستخرج attribute_mapping الادعاءات من Kubernetes JWT ويجعلها متاحة كسمات GCP. باستخدام assertion['kubernetes.io']['namespace']، يتم سحب الـ namespace بحيث يمكنك استخدامه للتحكم في الوصول.
  • attribute_condition هو المكان الذي تعيش فيه سياسة الأمان. المزيد عن هذا في القسم التالي.

كيفية استخدام CEL للتحكم الدقيق في الوصول

يستخدم حقل attribute_condition لغة التعبير الشائعة (Common Expression Language - CEL). يمكن لهذا السطر الواحد من السياسة أن يحل محل عشرات من ارتباطات إدارة الهوية والوصول (IAM):

attribute.namespace in ["production", "staging"]

باستخدام هذا الشرط، لا يمكن لـ Pod في الـ namespace kube-system المصادقة على GCP على الإطلاق — يتم رفض تبادل الرمز قبل حتى استشارة IAM. يمكنك الحصول على تعقيد أكبر:

// Only production namespace, and only specific service accounts
attribute.namespace == "production" && attribute.service_account in ["payment-processor", "order-service"]

// Allow staging, but only during business hours
attribute.namespace == "staging" && request.time.getHours("America/New_York") >= 9 && request.time.getHours("America/New_York") < 17

هذا دفاع متعمق. حتى لو قام شخص ما بإنشاء ServiceAccount ضار أو كان لديه وصول kubectl، فلن يتمكن من المصادقة على GCP إلا إذا اجتاز شرط CEL. يتم فرض الحدود الأمنية بواسطة بنية Google التحتية، وليس بالأمل في أن يتبع المطورون السياسة.

كيفية حقن بيانات الاعتماد تلقائيًا باستخدام Kyverno

وجود Workload Identity Federation فعال هو نصف المعركة فقط. لا ينبغي لعملائك ومطوريك أن يحتاجوا إلى فهم OIDC أو STS أو ملفات تكوين بيانات الاعتماد. يجب عليهم نشر تطبيقهم وجعله يعمل.

قبل أن نصل إلى الأتمتة، يجدر بنا التوقف عند ماهية ملف تكوين بيانات الاعتماد — لأن الاسم مضلل بعض الشيء. ملف تكوين بيانات الاعتماد (يُسمى أحيانًا "external account config" أو "ADC config") هو مستند JSON صغير يخبر مكتبات عميل Google كيفية الحصول على بيانات اعتماد في وقت التشغيل. إنه ليس بيانات اعتماد بحد ذاته. سترى الملف الفعلي لاحقًا في هذا المقال — لا يحتوي على أسرار. مجرد بيانات وصفية: جمهور Workload Identity Pool، ونقطة نهاية تبادل رمز STS، ونوع الرمز المصدر، والمسار في نظام ملفات الـ Pod حيث يوجد رمز Kubernetes ServiceAccount الحقيقي (قصير الأمد).

قارن ذلك بمفتاح حساب خدمة تقليدي:

  • مفتاح حساب الخدمة (key.json)
    • ماذا يوجد داخل الملف: مفتاح RSA خاص هو بيانات الاعتماد.
    • عمر المواد السرية: إلى الأبد، حتى يتم تدويره يدويًا.
    • إذا تسرب الملف: وصول طويل الأمد إلى حساب خدمة GCP.
    • نموذج الهوية: ينتحل شخصية حساب خدمة GCP مباشرةً.
    • من يتعامل مع التدوير: إنسان (أو لا أحد).
  • تكوين بيانات الاعتماد (credential-configuration.json)
    • ماذا يوجد داخل الملف: تعليمات لتبادل رمز خارجي.
    • عمر المواد السرية: الرمز المصدر يدور تلقائيًا (عمر افتراضي حوالي ساعة).
    • إذا تسرب الملف: عديم الفائدة بمفرده — يشير إلى رمز لا يمكن قراءته إلا بواسطة الـ Pod.
    • نموذج الهوية: يدمج هوية خارجية في GCP عبر STS.
    • من يتعامل مع التدوير: خادم API لـ Kubernetes، بشفافية.

ينتهي الأمر بكلا الملفين بالإشارة إليهما بواسطة GOOGLE_APPLICATION_CREDENTIALS ويبدوان قابلين للتبادل من وجهة نظر التطبيق — ولكن واحدًا منهما فقط خطير عند فقدانه. ملف تكوين بيانات الاعتماد آمن للشحن في ConfigMap تحديدًا لأنه لا يوجد شيء لسرقته.

وجود هذا الملف في ConfigMap هو نصف الحل. يجب أن ينتهي به المطاف في وحدات الـ Pods التي تحتاج إلى الوصول إلى خدمات GCP. هنا يأتي دور Kyverno. تقوم ClusterPolicy واحدة بحقن كل ما يحتاجه الـ Pod تلقائيًا:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: workload-identity-federation
spec:
  rules:
  - name: inject-gcp-credentials
    match:
      any:
      - resources:
          kinds:
          - Deployment
          selector:
            matchLabels:
              workload-identity-federation: "enabled"
    mutate:
      patchStrategicMerge:
        spec:
          template:
            spec:
              volumes:
              - name: workload-identity-credential-configuration
                configMap:
                  name: workload-identity-federation-config
              containers:
              - (name): "*"
                volumeMounts:
                - name: workload-identity-credential-configuration
                  mountPath: /etc/workload-identity
                  readOnly: true
                env:
                - name: GOOGLE_APPLICATION_CREDENTIALS
                  value: "/etc/workload-identity/credential-configuration.json"

تقوم سياسة المجموعة أعلاه بثلاثة أشياء:

  • تركب الـ ConfigMap داخل الحاويات في الـ Deployment في المسار /etc/workload-identity.
  • تحقن متغير بيئة يسمى GOOGLE_APPLICATION_CREDENTIALS يشير إلى المسار المطلق لملف تكوين بيانات الاعتماد.

من منظور المطور، هذا هو تكاملهم بالكامل:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    workload-identity-federation: "enabled" # That's it.
spec:
  # ... normal deployment spec

يخبر ملف تكوين بيانات الاعتماد (الذي تم إنشاؤه بواسطة Terraform كـ ConfigMap) مكتبات عميل Google كيفية تبادل الرموز:

{
  "type": "external_account",
  "audience": "//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",
  "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
  "token_url": "https://sts.googleapis.com/v1/token",
  "credential_source": {
    "file": "/run/secrets/kubernetes.io/serviceaccount/token"
  }
}

ملف JSON هذا هو تكوين بيانات اعتماد لـ Workload Identity Federation من Google. يوجه مكتبات عميل Google Cloud للحصول على رموز وصول السحابة عن طريق تبادل رمز Kubernetes ServiceAccount (الموجود في /run/secrets/kubernetes.io/serviceaccount/token) مقابل رمز وصول Google Cloud، باستخدام موفر هوية خارجي تم تكوينه عبر Workload Identity Pool. يتيح هذا لأعباء العمل التي تعمل خارج GCP، مثل مجموعات Kubernetes المحلية، المصادقة على خدمات Google Cloud دون الحاجة إلى إدارة مفاتيح حسابات الخدمة طويلة الأمد.

كل حزمة SDK ومكتبة عميل من Google Cloud تفهم هذا التنسيق. تعمل Python و Go و Java و Node.js جميعها بشكل مباشر.

كيفية منح أذونات IAM للهويات الموحدة

يحتاج رمز حساب الخدمة الذي وثقت به خدمة STS، والمعروف أيضًا باسم الهوية الموحدة (federated identity)، إلى أذونات للوصول إلى الموارد. تقوم بربط أدوار IAM بسمات Identity Pool:

resource "google_project_iam_member" "secret_access" {
  for_each = toset(["production", "staging"])
  project  = "my-project"
  role     = "roles/secretmanager.secretAccessor"
  member   = "principalSet://iam.googleapis.com/projects/\({PROJECT_NUMBER}/locations/global/workloadIdentityPools/\){POOL_ID}/attribute.namespace/${each.value}"
}

يمنح هذا وصول Secret Manager لجميع وحدات الـ Pods التي تمت المصادقة عليها من الـ namespaces production أو staging. تسمح صيغة principalSet بالمطابقة على السمات. يمكنك أيضًا التقييد على حسابات خدمة محددة:

member = "principal://iam.googleapis.com/.../subject/system:serviceaccount:production:payment-processor"

كيفية التحقق من الإعداد

يمكنك التحقق من الإعداد باستخدام نص برمجي بسيط بلغة Python يقوم بسرد الأسرار من Secret Manager. يتم تشغيل هذا داخل Pod على مجموعتك المحلية:

# list_secrets.py - running on-prem, accessing GCP Secret Manager
from google.cloud import secretmanager

def list_secrets(project_id: str):
    """
    List all secrets in a GCP project.
    No credentials are passed explicitly. The google-cloud-secret-manager library automatically:
    1. Reads GOOGLE_APPLICATION_CREDENTIALS env var (set by Kyverno)
    2. Loads the credential configuration JSON
    3. Reads the K8s ServiceAccount token from /run/secrets/...
    4. Exchanges it for a GCP access token via STS
    5. Uses that token to call the Secret Manager API
    """
    client = secretmanager.SecretManagerServiceClient()
    parent = f"projects/{project_id}"
    print(f"Secrets in {project_id}:")
    print("-" * 40)
    for secret in client.list_secrets(request={"parent": parent}):
        secret_name = secret.name.split("/")[-1]
        print(f" - {secret_name}")
    print("-" * 40)
    print("Authentication: Workload Identity Federation")
    print("Credentials: None stored, token exchanged at runtime")

if __name__ == "__main__":
    list_secrets("my-project-id")

شغّل هذا داخل الـ Pod الموسوم الخاص بك:

$ kubectl exec -it my-app-xyz -- python list_secrets.py
Secrets in my-project-id:
----------------------------------------
 - database-password
 - api-key-stripe
 - oauth-client-secret
 - ml-model-api-key
----------------------------------------
Authentication: Workload Identity Federation
Credentials: None stored, token exchanged at runtime

لا يوجد مفتاح حساب خدمة. لا توجد أسرار مركبة. مجرد رمز Kubernetes ServiceAccount يتم تبادله مقابل بيانات اعتماد GCP في وقت التشغيل. يعمل هذا النمط نفسه لأي خدمة GCP — Secret Manager، و Cloud Storage، و BigQuery، و Pub/Sub، و Vertex AI.

كيفية ربط التطبيقات المحلية بوحدات GPU السحابية

لنفترض سير عمل نموذجي: خدمة معالجة الطلبات المحلية تحتاج إلى استدعاء نقطة نهاية Vertex AI للكشف عن الاحتيال. يعمل النموذج على وحدات GPU في Google Cloud (يمكنك تشغيل وحدات A100 في دقائق، وليس شهور). يبقى منطق التطبيق محليًا (لقد دفعت بالفعل ثمن تلك الحوسبة).

مع وجود ارتباطات IAM، يمكن لأي Pod في الـ namespaces المسموح بها استدعاء Vertex AI:

# fraud_detector.py - running on-prem, calling cloud GPUs
from google.cloud import aiplatform

def check_fraud(transaction: dict) -> float:
    """
    Call a Vertex AI endpoint for fraud detection.
    The model runs on A100 GPUs in Google Cloud.
    This code runs on-prem in the datacenter.
    Authentication is automatic:
    1. Kyverno injected GOOGLE_APPLICATION_CREDENTIALS
    2. The aiplatform SDK reads the credential config
    3. K8s SA token is exchanged for GCP token via STS
    4. Request is authenticated to Vertex AI
    """
    endpoint = aiplatform.Endpoint(
        endpoint_name="projects/my-project/locations/us-central1/endpoints/fraud-model"
    )
    prediction = endpoint.predict(instances=[transaction])
    return prediction.predictions[0]["fraud_score"]

def generate_embeddings(texts: list[str]) -> list[list[float]]:
    """
    Generate text embeddings using a cloud-hosted model.
    Embedding models are GPU-intensive. Running them on-prem would require dedicated hardware.
    In the cloud, you pay per request.
    """
    from vertexai.language_models import TextEmbeddingModel
    model = TextEmbeddingModel.from_pretrained("text-embedding-004")
    embeddings = model.get_embeddings(texts)
    return [e.values for e in embeddings]

لا يفكر المطور في المصادقة على الإطلاق. يضيفون الـ label إلى الـ deployment الخاص بهم، ويمكن لـ Podهم المحلي استدعاء:

  • نقاط نهاية Vertex AI للاستدلال بالتعلم الآلي على وحدات GPU السحابية.
  • Cloud Storage لموارد النماذج وبيانات التدريب.
  • BigQuery لمخازن الميزات والتحليلات.
  • Pub/Sub لتدفق الأحداث بين البيئات.
  • Secret Manager لمفاتيح API والتكوين.

هذه هي المنصة الهجينة التي تعمل كما هو مقصود.

كيفية توسيع نطاق الوصول إلى GPU باستخدام شروط CEL

تصبح شروط CEL قوية بشكل خاص عندما تريد تقييد الوصول إلى GPU لـ namespaces محددة. على سبيل المثال، للسماح فقط لـ namespaces المتعلقة بالتعلم الآلي بالوصول إلى Vertex AI:

attribute.namespace in ["ml-inference", "ml-training", "data-science"] && attribute.service_account.startsWith("ml-")

يمكنك أيضًا منح مستويات وصول مختلفة لكل namespace:

# ML inference namespace gets prediction access
resource "google_project_iam_member" "ml_inference" {
  project = "my-project"
  role    = "roles/aiplatform.user"
  member  = "principalSet://iam.googleapis.com/.../attribute.namespace/ml-inference"
}

# Data science namespace gets full Vertex AI access (for experimentation)
resource "google_project_iam_member" "data_science" {
  project = "my-project"
  role    = "roles/aiplatform.admin"
  member  = "principalSet://iam.googleapis.com/.../attribute.namespace/data-science"
}

لا تحتاج فرق تطبيقات البيئة المحلية إلى معرفة أو الاهتمام بـ GCP IAM. يقومون بالنشر في الـ namespace الصحيح، ويضيفون label، وتتولى المنصة الباقي.

مقارنة الخصائص الأمنية

إليك مقارنة جنبًا إلى جنب بين نهجي المصادقة:

  • عمر بيانات الاعتماد:
    • مفاتيح حساب الخدمة: حتى يتم تدويرها يدويًا (غالبًا سنوات).
    • Workload Identity Federation: قصيرة الأمد (ساعة واحدة لرموز GCP).
  • مخاطر التسريب:
    • مفاتيح حساب الخدمة: عالية — يمكن نسخ المفتاح الثابت إلى أي مكان.
    • Workload Identity Federation: منخفضة — ينتهي صلاحية الرمز بسرعة.
  • مسار التدقيق:
    • مفاتيح حساب الخدمة: اسم حساب الخدمة فقط.
    • Workload Identity Federation: اسم الـ namespace + اسم حساب الخدمة.
  • العبء التشغيلي لإدارة المفاتيح:
    • مفاتيح حساب الخدمة: أكثر من 600 مفتاح على نطاق واسع.
    • Workload Identity Federation: صفر مفاتيح لإدارتها.
  • تطبيق السياسة الأمنية:
    • مفاتيح حساب الخدمة: يدوي / قائم على الثقة.
    • Workload Identity Federation: يتم فرضه بواسطة بنية GCP التحتية عبر CEL.
  • تجربة المطور:
    • مفاتيح حساب الخدمة: نسخ المفتاح، إنشاء سر، تركيب وحدة تخزين.
    • Workload Identity Federation: إضافة label واحد إلى الـ deployment.

تستحق الطبيعة قصيرة الأمد للرموز التأكيد. حتى في أسوأ السيناريوهات حيث يتم تسريب رمز بطريقة ما، فإنه ينتهي صلاحيته. تتمتع رموز Kubernetes ServiceAccount بعمر افتراضي قابل للتكوين، ورموز وصول GCP الصادرة عن STS صالحة لمدة ساعة واحدة. على النقيض من ذلك، يظل مفتاح حساب الخدمة صالحًا حتى يقوم شخص ما بتدويره صراحةً — غالبًا لسنوات.

تخطيط البنية التحتية ككود الكاملة

الحل بأكمله مُرمز في Terraform، ويدير موارد GCP و Kubernetes على حد سواء:

workload-identity-federation/
├── providers.tf # Google + Kubernetes providers
├── locals.tf    # Configuration (namespaces, project ID, etc.)
├── gcp.tf       # Identity pool, provider, IAM bindings
└── kubernetes.tf # ConfigMap with credential configuration

تطبيق terraform apply واحد يقوم بـ:

  • إنشاء Workload Identity Pool في GCP.
  • تكوين موفر OIDC باستخدام JWKS لمجموعتك.
  • إعداد ارتباطات IAM لـ namespaces المسموح بها.
  • إنشاء ConfigMaps في كل namespace مع تكوين بيانات الاعتماد.

بالاقتران مع سياسة Kyverno، تحصل على مسار عمل مؤتمت بالكامل:

New namespace added to allowed list
│
▼
Terraform creates ConfigMap in that namespace
│
▼
Developer deploys with label
│
▼
Kyverno injects credentials automatically
│
▼
Pod authenticates to GCP via OIDC
│
▼
Application accesses GCP services

لا تذاكر. لا طلبات مفاتيح. لا أسرار لإدارتها.

كيفية تشغيل إثبات المفهوم (PoC) باستخدام vCluster

للتحقق من أن هذا يعمل خارج GKE، يمكنك إعداد عرض توضيحي باستخدام vCluster — وهي مجموعة Kubernetes افتراضية تعمل داخل مجموعة Kubernetes أخرى. يثبت هذا أن الحل يعمل لأي مجموعة.

يمكنك إعداد vCluster في Docker باستخدام kind:

# vcluster.yaml
experimental:
  docker:
    nodes:
      - name: worker-1
      - name: worker-2
deploy:
  cni:
    flannel:
      enabled: true
  controlPlane:
    distro:
      k8s:
        version: "v1.35.0"
[root@localhost #] vcluster create hybrid --driver docker -f vcluster.yaml
[root@localhost #] kubectl get nodes
hybrid-control-plane   Ready    control-plane   14d   v1.34.0   192.168.107.2   <none>   Debian GNU/Linux 12 (bookworm)   7.0.5-orbstack-00330-ge3df4e19b0a0-dirty   containerd://2.1.3
hybrid-worker          Ready    <none>          14d   v1.34.0   192.168.107.3   <none>   Debian GNU/Linux 12 (bookworm)   7.0.5-orbstack-00330-ge3df4e19b0a0-dirty   containerd://2.1.3
hybrid-worker2         Ready    <none>          14d   v1.34.0   192.168.107.4   <none>   Debian GNU/Linux 12 (bookworm)   7.0.5-orbstack-00330-ge3df4e19b0a0-dirty   containerd://2.1.3

داخل vCluster، قم بنشر deployment اختبار بسيط:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gcp-test
  labels:
    workload-identity-federation: "enabled"
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gcp-test
  template:
    metadata:
      labels:
        app: gcp-test
    spec:
      containers:
      - name: test
        image: google/cloud-sdk:slim
        command: ["sleep", "infinity"]

ادخل إلى الـ Pod وتحقق:

$ kubectl exec -it gcp-test-xxx -- bash
# Inside the pod:
$ gcloud auth login --cred-file=$GOOGLE_APPLICATION_CREDENTIALS
Authenticated with external account credentials for: [principal://iam.googleapis.com/...]
$ gcloud secrets list --project=my-project
NAME              CREATED
database-password 2024-01-15T10:30:00Z
api-key           2024-01-14T09:15:00Z

لا مفاتيح. لا أسرار مركبة. مجرد Workload Identity Federation يعمل كما هو مصمم.

المشاكل الشائعة وكيفية حلها

كيفية التعامل مع استرجاع JWKS للمجموعات المعزولة (Air-Gapped Clusters)

إذا لم تكن نقطة نهاية اكتشاف OIDC لمجموعتك قابلة للوصول علنًا (معظم المجموعات المحلية ليست كذلك)، فستحتاج إلى تصدير JWKS يدويًا وتحميله إلى GCP:

kubectl get --raw /openid/v1/jwks > jwks.json

يجب تحديث هذا الملف إذا تم تدوير مفاتيح التوقيع الخاصة بالمجموعة. قم بإعداد مهمة دورية تتحقق من تغييرات المفاتيح وتحدث تكوين Terraform.

كيفية إصلاح عدم تطابق عنوان URL للمصدر (Issuer URL Mismatches)

يجب أن يتطابق ادعاء iss في رمز Kubernetes تمامًا مع عنوان URL للمصدر (issuer URL) المكون في موفر OIDC. بالنسبة للمجموعات التي تستخدم DNS داخليًا:

issuer_uri = "https://kubernetes.default.svc.cluster.local"

لا يحتاج عنوان URL هذا إلى أن يكون قابلاً للوصول من GCP — يوفر ملف JWKS مفاتيح التحقق. لكن يجب أن يتطابق تمامًا مع ما هو موجود في الرمز.

كيفية تصحيح أخطاء فشل تبادل الرمز (Token Exchange Failures)

عند فشل المصادقة، قد تكون رسائل الخطأ غامضة. الأسباب والحلول الشائعة:

  • الخطأ: invalid_grant
    • السبب المحتمل: عدم تطابق عنوان URL للمصدر (Issuer URL mismatch).
    • الإصلاح: تحقق من ادعاء iss في JWT مقابل issuer_uri المكون.
  • الخطأ: audience mismatch
    • السبب المحتمل: audience خاطئ في تكوين بيانات الاعتماد.
    • الإصلاح: أعد إنشاء JSON لتكوين بيانات الاعتماد عبر Terraform.
  • الخطأ: CEL condition failed
    • السبب المحتمل: الـ namespace ليس في القائمة المسموح بها.
    • الإصلاح: أضف الـ namespace إلى attribute_condition وأعد التطبيق.
  • الخطأ: JWKS validation failed
    • السبب المحتمل: تم تدوير مفاتيح التوقيع.
    • الإصلاح: أعد تصدير JWKS وحدث تكوين Terraform.

الخاتمة

بعد تنفيذ هذا الإعداد، تقوم أعباء العمل المحلية بالمصادقة على Google Cloud تمامًا كما تفعل أعباء عمل GKE — دون الحاجة إلى بيانات اعتماد طويلة الأمد. فريق الأمن سعيد (لا توجد مفاتيح للتدقيق)، والمطورون سعداء (فقط أضف label)، وفريق المنصة سعيد (لا مزيد من تذاكر إدارة بيانات الاعتماد).

إليك ما أنجزته في هذا الدليل:

  • فهمت لماذا تفشل مفاتيح حسابات الخدمة عند التوسع والمخاطر الأمنية التي تقدمها.
  • أنشأت Workload Identity Pool وموفر OIDC في GCP للوثوق بمصدر رمز مجموعتك.
  • استخدمت شروط CEL لفرض سياسات وصول دقيقة على مستوى الـ namespace.
  • أتمتت حقن بيانات الاعتماد في الـ Pods باستخدام Kyverno ClusterPolicy.
  • ربطت أدوار IAM بسمات الهوية الموحدة — لا توجد مفاتيح طويلة الأمد في أي مكان.
  • تحققت من الإعداد عن طريق استدعاء واجهات برمجة تطبيقات GCP (Secret Manager، Vertex AI) من Pod محلي.
  • أثبتت أن الحل يعمل على أي مجموعة Kubernetes باستخدام vCluster.

التقنيات المستخدمة هنا ليست جديدة. OIDC موجود في Kubernetes منذ الإصدار 1.20. Workload Identity Federation موجودة في GCP منذ سنوات. Kyverno و Terraform أدوات ناضجة. ما يجمعه هذا الدليل هو حل شامل يمكن للمطورين اعتماده بأقل جهد. إذا قامت مؤسستك بتعطيل مفاتيح حسابات الخدمة (أو يجب عليها ذلك)، فهذا هو المسار إلى الأمام. يمكن لمجموعاتك المحلية والسحابية أخيرًا أن تكون ما كان من المفترض أن تكون عليه دائمًا: امتدادات آمنة لبعضها البعض.

التطبيق الكامل متاح كوحدة Terraform مع سياسات Kyverno.

💡 الخلاصة التقنية

يمثل هذا المقال دليلاً شاملاً وحيويًا للمطورين والمهندسين في المنطقة العربية الذين يتطلعون إلى بناء بنى تحتية سحابية هجينة قوية وآمنة. في ظل التوجه المتزايد نحو اعتماد السحابة مع الحفاظ على بعض أعباء العمل الحساسة محليًا، توفر Workload Identity Federation حلاً جذريًا لمشكلة إدارة بيانات الاعتماد التي طالما كانت نقطة ضعف أمنية وتشغيلية. إن القدرة على ربط مجموعات Kubernetes المحلية بخدمات Google Cloud، خاصةً للاستفادة من موارد الحوسبة عالية الأداء مثل GPUs للذكاء الاصطناعي والتعلم الآلي، تفتح آفاقًا جديدة للمؤسسات العربية. يمكن للشركات الآن تسريع ابتكاراتها في مجالات الذكاء الاصطناعي دون الحاجة إلى استثمارات ضخمة في الأجهزة المحلية أو القلق بشأن تعقيدات الأمن. استخدام أدوات مثل Terraform و Kyverno لأتمتة هذه العملية يقلل بشكل كبير من العبء التشغيلي ويزيد من موثوقية النظام، مما يتيح للفرق التركيز على تطوير القيمة بدلاً من إدارة البنية التحتية. مستقبل هذا النهج واعد للغاية، حيث سيمكن المطورين العرب من بناء حلول أكثر تعقيدًا وأمانًا، مستفيدين من أفضل ما في العالمين: مرونة وقوة السحابة، مع الاحتفاظ بالتحكم والسيادة على البيانات المحلية.

مساحة إعلانية - أضف كود أدسنس هنا