‫Rails 8 على Google Cloud: من البداية إلى الذكاء الاصطناعي

1. مقدمة

مرحبًا بك في ورشة عمل Rails 8 على Google Cloud. في هذا الدرس التطبيقي العملي حول الترميز، ستنتقل بتطبيق حديث من Rails 8 من خط أساس بسيط محلي SQLite إلى بنية مرجعية جاهزة للاستخدام في بيئة الإنتاج ومناسبة للمؤسسات على Google Cloud.

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

يمكنك البدء من الإعداد المحلي المعتاد "يعمل على جهازي" (قرص مؤقت، وSQLite مضمّن، ومهام داخل العملية) وتطويره تدريجيًا إلى بنية سحابية متينة ومناسبة للمؤسسات على Google Cloud:

  1. 🐘 من SQLite المحلّي → Cloud SQL (PostgreSQL): لن يتم محو قاعدة البيانات عند إعادة التشغيل. ننتقل إلى PostgreSQL المُدارة التي يتم ربطها بشكل آمن من خلال الخادم الوكيل للمصادقة في Cloud SQL (بروتوكول TLS المتبادل، بدون عناوين IP عامة).
  2. من "مساحة التخزين المحلية المؤقتة" → "مساحة تخزين Google Cloud الخاصة": القرص بدون خادم مؤقت. نربط ActiveStorage بخدمة Google Cloud Storage الخاصة باستخدام عناوين URL الموقّعة في "إدارة الهوية وإمكانية الوصول" (iam: true)، بدون حِزم عامة أو مفاتيح مسرَّبة.
  3. 🚀 من المهام داخل العملية → خدمة Cloud Run المتعددة الحاويات: نحن نربط بين بنية Rails المتكاملة والسحابة الإلكترونية بدون خادم. باستخدام Docker Compose العادي، نشغّل Solid Queue كحاوية عامل مخصّصة إلى جانب Puma في وحدة Cloud Run متعددة الحاويات، ما يمنع انتشار الخدمات المصغّرة ونقص الموارد في سلاسل المحادثات. DHH سيكون سعيدًا بذلك.

🍌 بما أنّنا نعيش في عام 2026، سنعزّز المجموعة الكاملة من خلال Google Antigravity. هذا ليس مجرد فكرة عابرة، بل لدينا طلبات و مهارات لإرشادك بشأن كيفية الاستفادة من ورشة العمل هذه على أفضل وجه (والاستمتاع بها).

‫🏛️ البنية المرجعية المستهدَفة (الخيار 1: مؤسسة متكاملة تستخدم رسومات متجهة مسطّحة ونظيفة)

في ما يلي بنية Google Cloud المرجعية الأساسية التي ستنشئها وتنفّذها (الخيار 1: Clean Flat Vector Enterprise)، والتي تعرض خدمة Cloud Run المتعددة الحاويات في مرحلة الإنتاج والمرتبطة بجميع خدمات الذكاء الاصطناعي وخدمات الثبات المُدارة من Google Cloud:

النوع 1: Clean Flat Vector Enterprise — Rails 8 على بنية Google Cloud

‫🎞️ التطور المعماري: من البداية إلى البنية المستنِدة إلى السحابة الإلكترونية

شاهِد عملية التطوير التدريجي لمجموعة التكنولوجيات من أساس مؤقت لجهاز واحد إلى مخطط Google Cloud مُدار بالكامل في كل مرحلة من مراحل ورشة العمل:

تطوّر بنية ورشة العمل

أهداف الدورة التعليمية

  • كيفية استخدام Google Antigravity في البرمجة الثنائية لتوضيح Rails 8 وGoogle Cloud
  • كيفية إجراء عمليات التشخيص المبرمَجة قبل الرحلة (just workshop-test)
  • كيفية توفير البنية الأساسية لخدمة Google Cloud بشكل غير متزامن باستخدام Terraform مع مواصلة التطوير المحلي بدون حظر باستخدام موفّر google
  • كيفية التخلص من أنماط الأمان غير الفعّالة: حِزم GCS الخاصة (iam: true) وأنفاق الخادم الوكيل للمصادقة في Cloud SQL المستندة إلى بروتوكول TLS المتبادل بدلاً من فتح 0.0.0.0/0
  • كيفية إدخال المفاتيح السرية مباشرةً من Google Cloud Secret Manager
  • كيفية تنسيق مهام الخلفية غير المتزامنة القائمة على الذكاء الاصطناعي التوليدي (أداة إنشاء أغلفة NanoBanana، وأداة إنشاء البودكاست الثنائي اللغة) من خلال Solid Queue

لِنبدأ.

2. الخطوة 0: المتطلبات الأساسية وإعداد Antigravity وتأكيد الفوترة

💡 السيناريو: أنت وفريقك تعملون على إنشاء تطبيق Rails 8 مهم للغاية. قبل تعديل الرمز أو إطلاق موارد السحابة الإلكترونية، يجب إنشاء سلسلة الأدوات وربط Google Antigravity والتحقّق من بيانات اعتماد Google Cloud وأساس الفوترة.

1. قائمة التحقّق من المتطلّبات الأساسية

قبل البدء، تأكَّد من توفّر الأدوات التالية في بيئتك:

  • just (الإصدار 1.21 والإصدارات الأحدث): (just --version) أداة تنفيذ المهام: كل أمر في ورشة العمل هذه هو justوصفة (اطلب من أداة الذكاء الاصطناعي تثبيتها أو اطّلِع على skills/rails8app-workshop).
  • ‫Git (الإصدار 2.30 أو أحدث): (git --version) للتحكّم في الإصدارات وإنشاء الفروع واستنساخ المستودع
  • حزمة تطوير البرامج (SDK) من Google Cloud (gcloud واجهة سطر الأوامر): يجب أن تكون مثبّتة ومحدّثة.
  • واجهة سطر الأوامر (CLI) في Terraform (الإصدار 1.5 والإصدارات الأحدث): لتوفير البنية الأساسية بشكل تعريفي
  • docker وdocker-compose: يتم تثبيتهما وتشغيلهما على الجهاز.
  • ‫Ruby 3.4.5: (ruby -v — تم تثبيته بواسطة blog/.ruby-version؛ اطلب من أداة الذكاء الاصطناعي تثبيت 3.4.5 إذا لم يكن مثبّتًا). يتم الإعلان عن Rails 8 في Gemfile ويتم تثبيته تلقائيًا بواسطة bundle install.
  • ‫Google Antigravity 2.0: مساعدك المستند إلى الذكاء الاصطناعي المستقل في البرمجة الثنائية (تنزيل Google Antigravity 2.0) ستسجّل الدخول باستخدام حسابك الشخصي على Gmail، ولن تحتاج إلى رمز مميّز لواجهة برمجة التطبيقات.

2. مصادقة Google Cloud وإعدادات Dedicated وADC

لتجنُّب حدوث تعارضات مع عمليات إعداد gcloud الحالية للشركات أو الحسابات الشخصية أو الحسابات المتعددة، ننصحك بشدة بإنشاء إعداد مخصّص باسم ورشة العمل هذه:

# 1. Create and activate dedicated workshop configuration (Fixes Issue #38)
gcloud config configurations create rails8-on-gcp-workshop --activate 2>/dev/null || \
  gcloud config configurations activate rails8-on-gcp-workshop

# 2. Authenticate your account and Application Default Credentials (ADC)
gcloud auth login $GOOGLE_CLOUD_ACCOUNT
gcloud auth application-default login

# 3. Configure active project and region
gcloud config set project $GOOGLE_CLOUD_PROJECT
gcloud config set compute/region europe-west1

💡 لماذا يجب استخدام إعدادات مخصّصة؟ يؤدي استخدام gcloud config configurations create rails8-on-gcp-workshop إلى عزل جميع إعدادات واجهة سطر الأوامر (الحساب، ومشروع الحصة، والمنطقة التلقائية) خصيصًا لورشة العمل هذه. بعد الانتهاء، يمكنك العودة إلى الإعدادات العادية في أي وقت من خلال النقر على gcloud config configurations activate default.

إعدادات gcloud النشطة

3- ‫🚨 تأكيد الفوترة في Google Cloud Platform

⚠️ ملاحظة: تتطلّب عمليات نشر Google Cloud SQL وCloud Run حساب فوترة نشطًا مرتبطًا أو أرصدة تعليمية صالحة في ورشة العمل. لنتأكّد الآن من صحة الإعداد. لإجراء ذلك، نفِّذ عملية التحقّق من صحة معلومات الفوترة:

gcloud beta billing projects describe $GOOGLE_CLOUD_PROJECT

يُرجى التأكّد من إرجاع جهاز billingEnabled: true. يمكنك أيضًا إدراج جميع حسابات الفوترة:

gcloud billing accounts list

تأكَّد من أنّ أحدها على الأقل يعرض OPEN: True. إذا لم يكن أيّ منها نشطًا، اربط حساب فوترة أو استردّ قسيمة رصيد ورشة العمل في صفحة الفوترة في Google Cloud Console.

حساب فوترة نشط على Google Cloud Platform

4. استنساخ المستودع والإقران مع Antigravity

استنسِخ المستودع وأدخِل الدليل. لاحظ أنّنا نبقى على main بالكامل:

git clone https://github.com/palladius/rails8-app-on-gcp.git
cd rails8-app-on-gcp

افتح هذا الدليل في Google Antigravity. ستفحص Antigravity المستودع تلقائيًا، وتقرأ AGENTS.md، وتكون جاهزة لمساعدتك في البرمجة الثنائية.

5- 🪫 إذا نفدت وحدات الرصيد المجانية في Antigravity

⚠️ إنّ أرصدة Cloud التي استرددتها للتوّ لا تعيد تعبئة Antigravity. هما واجهتا فوترة منفصلتان: تقع حصة Antigravity على خطط Google AI (Google One)، بينما يتم إرسال القسائم ورصيد Cloud إلى حساب فوترة على Cloud. لن يؤدي ربط حساب فوترة بمشروعك على Google Cloud Platform إلى منحك طلبًا إضافيًا واحدًا من Antigravity.

إذا توقّف الوكيل بسبب رسالة بشأن الحصة، يمكنك اتّباع الخطوات التالية، علمًا بأنّ الخطوتَين الأوليَين مجانيتان وفوريتان:

  1. تبديل مجموعة النماذج يتم تتبُّع الحصة لكل مجموعة نماذج، وتعرض Antigravity مجموعتَين مستقلتَين (نماذج Gemini مقابل نماذج Claude/GPT). تتوفّر نماذج Claude وGPT-OSS في الإصدار المجاني، لذا فإنّ استنفاد رصيد Gemini غالبًا ما يترك الرصيد الآخر بدون تغيير. استخدِم أداة اختيار النموذج تحت مربّع الطلب.أداة اختيار نموذج Antigravity: التبديل بين مجموعات Gemini وClaude وGPT-OSS
  2. اختَر نموذج Flash بدلاً من Pro. ترتبط حدود المعدّل بمقدار العمل الذي ينفّذه الوكيل لكل طلب، لذا فإنّ النموذج الأخف يوسّع ما تبقى.
  3. استخدام Antigravity CLI مع مفتاح Gemini API الخاص بك لا تتوفّر ميزة "استخدام مفتاحك الخاص" في بيئة التطوير المتكاملة Antigravity، ولكن يمكنك استخدام واجهة سطر الأوامر كبديل. أنشئ مفتاحًا مجانيًا في Google AI Studio (يجب إنشاء مشروع ولكن لا يلزم إنشاء حساب فوترة)، ثم نزِّل Antigravity CLI واضبط إعداداتها.
  4. انتظِر أو رقِّ. يتم تجديد حصة المستوى المجاني أسبوعيًا (توضّح رسالة الخطأ تاريخ إعادة الضبط). إنّ Google AI Pro/Ultra هي الطريقة الوحيدة الموثّقة لزيادة الحد الأساسي داخل تطبيق سطح المكتب، ويتم إعادة ملء هذه الخطط كل خمس ساعات ويمكن استخدام وحدات AI التي تم شراؤها في تجاوز الحد.
  5. مع أنّ هذه ورشة عمل حول Antigravity، من المفترض أن تعمل معظم أدوات الترميز المستندة إلى الذكاء الاصطناعي أيضًا، ما عليك سوى توجيهها إلى الدليل AGENTS.md والدليل skills/ في المستودع.

6. التحقّق الآلي من صحة الخطوة 0

تأكَّد من أنّ بيئة الخطوة 0 متوافقة بنسبة% 100 مع مجموعة التقييم:

just workshop-eval 0

✨ لحظة الإبهار: تتحقّق ميزة "الجاذبية المضادة" ومحرّك تقييم ورشة العمل من إصدارات واجهة سطر الأوامر وبيئة Ruby ومصادقة ADC في أقل من ثانيتَين.

3- الخطوة 1: بدء استخدام البنية الأساسية لبرنامج Terraform وتشخيص ما قبل التشغيل

💡 الاستراتيجية: يستغرق توفير قواعد البيانات المُدارة، مثل Google Cloud SQL PostgreSQL، من 10 إلى 12 دقيقة تقريبًا. بدلاً من الانتظار بلا فائدة لاحقًا، نطلق البنية الأساسية غير القابلة للتغيير من خلال Terraform الآن في الخلفية أثناء التطوير على الجهاز.

1. تشغيل "حزمة اختبارات التشخيص الآلية قبل الإطلاق"

قبل إطلاق البنية الأساسية السحابية، شغِّل حزمة اختبار شاملة قبل الإطلاق:

just workshop-test

هذا النص البرمجي (bin/workshop_diagnostics.rb):

  • يتحقّق من إعدادات هوية GOOGLE_CLOUD_ACCOUNT.
  • يتحقّق من الفوترة النشطة وربط المشروع.
  • تتحقّق هذه السمة من صحة بيانات الاعتماد التلقائية للتطبيق (ADC) في Vertex AI.
  • تؤكّد هذه السمة صورة الإصدار التجريبي من ActiveStorage (blog/app/assets/images/gcs_dev_image.jpg).

just workshop-test diagnostics output

إذا كان .env مفقودًا، انسَخه من النموذج الموثّق:

cp .env.dist .env
# Edit .env and configure GOOGLE_CLOUD_ACCOUNT with your Google/Gmail account

💡 ملاحظة: إذا أبلغت just workshop-test بأنّ واجهة برمجة التطبيقات لنظام الفوترة غير مفعّلة في مشروعك، يمكنك تفعيلها سريعًا من خلال:

gcloud services enable cloudbilling.googleapis.com

2. ⏱️ إطلاق البنية الأساسية لـ Terraform بشكل غير متزامن

نفِّذ وصفة Terraform المكوّنة من خطوة واحدة، والتي تنشئ حزمة حالة الخلفية البعيدة وتؤدي إلى نشر البنية الأساسية:

just terraform-apply

تمت تهيئة Terraform بنجاح

بديل يدوي (أو تنفيذ `iac/bin/terraform-apply.sh` مباشرةً)

# Fall back to active gcloud config if environment variables are unset
GOOGLE_CLOUD_PROJECT="${GOOGLE_CLOUD_PROJECT:-$(gcloud config get-value project)}"
GOOGLE_CLOUD_REGION="${GOOGLE_CLOUD_REGION:-europe-west1}"

# Create remote state bucket idempotently
gcloud storage buckets create "gs://${GOOGLE_CLOUD_PROJECT}-tfstate" --location="$GOOGLE_CLOUD_REGION" 2>/dev/null || \
  gcloud storage buckets describe "gs://${GOOGLE_CLOUD_PROJECT}-tfstate" >/dev/null

cd iac
terraform init -backend-config="bucket=${GOOGLE_CLOUD_PROJECT}-tfstate"
terraform apply -auto-approve -var="project_id=${GOOGLE_CLOUD_PROJECT}" -var="region=${GOOGLE_CLOUD_REGION}"
cd ..

ما توفّره أداة Terraform:

  • حزمة Google Cloud Storage: تم إنشاؤها مع إمكانية الوصول الخاص وتوقيع بيانات اعتماد "إدارة الهوية وإمكانية الوصول" (iam: true).
  • صورة اختبار Canary: يتم تحميل gcs_dev_image.jpg إلى الحزمة لتفعيل ميزة التحقّق من صحة البيانات الثنائية الكبيرة بين الأطراف.
  • مثيل PostgreSQL في Google Cloud SQL: يبدأ عملية توفير في الخلفية (من 10 إلى 12 دقيقة تقريبًا).

مثيل Cloud SQL PostgreSQL في Google Cloud Console

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

اكتملت عملية تطبيق Terraform بنجاح مع النتائج

📝 دوِّن عنوان URL الخاص بخدمة Cloud Run: من المفترض أن ينجح تطبيق Terraform وأن يطبع نواتج البنية الأساسية. دوِّن cloud_run_url (أو انسخه إلى الحافظة)، لأنّك ستحتاج إليه في الخطوات اللاحقة عند إثبات ملكية خدمتك المباشرة.

3- التحقّق الآلي من صحة الخطوة 1 (اختياري)

تأكَّد من استيفاء المتطلبات الأساسية وعمليات الضبط في الخطوة 1:

just workshop-eval 1

⏱️ ملاحظة: يتحقّق هذا التقييم من موارد السحابة الإلكترونية النشطة وقد يستغرق إكماله بضع دقائق. هذه الخطوة اختيارية تمامًا، فبينما يتم تنفيذها في نافذة الأوامر، يمكنك الانتقال فورًا إلى الخطوة التالية بدون انتظار.

4. الخطوة 2: الإعداد الأساسي المحلي وMailpit وإعداد المشرف

نقطة البداية هي تطبيق مدوّنة حديث ونظيف يعمل على Rails 8 على المضيف المحلي باستخدام SQLite واعتراض البريد الإلكتروني Mailpit وActiveStorage المستند إلى القرص.

1. تشغيل التطبيق محليًا

📁 انتبه إلى الدليل! يحتوي هذا المستودع على تطبيق Rails في blog/ وTerraform في iac/. لقد تركتك الخطوة 1 في جذر المستودع، لذا يتم تنفيذ كل أمر في الخطوة 2 من

blog/: يعيش Gemfile وbin/rails وcompose.yaml في هذه المنطقة. عند تشغيلها من الجذر، يتعذّر ذلك ويظهر الخطأ no configuration file provided: not found أو لا يظهر Gemfile.

انتقِل إلى تطبيق Rails، وتأكَّد من أنّك تستخدم إصدار Ruby المثبَّت، ثم ثبِّت حِزم gem وأعِدّ قاعدة البيانات:

cd blog
ruby -v          # must print 3.4.5 — see Step 0 if it does not
bundle install
bin/rails db:setup

🧯 خطأ في الإذن بالكتابة إلى

/var/lib/gems/

‫ أو

ruby -v

تعرض إصدار Ruby المثبّت على النظام (مثل 3.3.x)؟ يتجاهل نظام Ruby في Linux العادي ملفات .ruby-version تمامًا ويحاول تثبيت حِزم gem على مستوى النظام. تأكَّد من تحميل rbenv في نافذة الوحدة الطرفية النشطة:

eval "$(rbenv init - bash)"   # or: eval "$(rbenv init - zsh)"
ruby -v                       # confirm it now outputs 3.4.5!

ملاحظة: إذا تخطّيت تثبيت الإصدار 3.4.5 في الخطوة 0، شغِّل rbenv install 3.4.5. أو يمكنك ببساطة استخدام الوضع A (Docker Compose) أدناه (just compose-up)، الذي يشغّل حزمة Ruby المثبّتة بالكامل داخل الحاويات بدون التأثير في حِزم المضيف.

💡 هل تفضّل عدم التفكير في ذلك؟ ينفّذ just install وjust compose-up من جذر المستودع cd blog نيابةً عنك.

2. ‫🌱 Smart Seed Auto-Discovery & Admin Bootstrap (المشكلتان ‎ #21 و‎#25)

تتضمّن قاعدة البيانات الأولية (db/seeds.rb) ميزة الاكتشاف التلقائي للبيئة الذكية:

  • تفحص هذه الأداة محوّل قاعدة البيانات النشط (SQLite مقابل Postgres) وإعدادات التخزين.
  • يرصد المرحلة 0 (المضيف المحلي) وينشئ تلقائيًا المستخدم الإداري الأوّلي ويملأ المنشور التربوي:
    • [LOCAL BASELINE] Welcome to Rails 8 on Localhost!
    • مرفق صورة SAD محلية جاهزة للاستخدام (local_sad_image.png) مع علامة مائية تُعلمك بأنّ مساحة التخزين على القرص المحلي مؤقتة
  • يؤدي ذلك تلقائيًا إلى إرسال رسالة إلكترونية لإعادة ضبط كلمة المرور من خلال ActionMailer.

أثبِت ملكية الحساب أو أعِد تشغيل عملية إنشاء الحساب (يتم تلقائيًا استخدام GOOGLE_CLOUD_ACCOUNT من .env):

bin/rails db:seed    # or from repo root: just seed

ابدأ حزمة التطوير المحلية من خلال Docker Compose:

docker compose up -d     # from blog/ — or `just compose-up` from the repo root

بدء تشغيل Rails وMailpit وLocalStack باستخدام Docker Compose

يعمل التطبيق وخادم SMTP المحلي في Mailpit وعارض قاعدة البيانات Adminer الآن معًا في حاويات معزولة:

  • تطبيق Rails: http://localhost:3000
  • واجهة مستخدم الويب في Mailpit: http://localhost:8025
  • واجهة مستخدم قاعدة بيانات Adminer: http://localhost:8081

🧯 تعارض المنفذ 3000 أو

server.pid

خطأ؟ إذا سبق لك بدء خادم تطوير أصلي (bin/dev)، أوقِفه باستخدام Ctrl+C. إذا أبلغ Docker عن أنّ المنفذ 3000 مخصّص حاليًا أو عثر على server.pid متبقٍّ، يمكنك تنظيفه باستخدام:

just compose-down
rm -f blog/tmp/pids/server.pid
just compose-up

3- تجربة Mailpit وتسجيل الدخول كمسؤول

  1. تتبُّع الرسائل الإلكترونية الصادرة باستخدام Mailpit: افتح http://localhost:8025 في المتصفّح. اطّلِع على صندوق البريد الوارد المحلي. أثناء db:seed، أرسل ActionMailer رسالة إلكترونية لإعداد المشرف، وقد التقطها Mailpit بأمان على الجهاز بدون الوصول إلى خوادم البريد الإلكتروني أو بيانات الاعتماد الخارجية:

واجهة مستخدم Mailpit على الويب تعرض رسالة إلكترونية تم اعتراضها لإعادة ضبط كلمة المرور 2. تسجيل الدخول إلى المدوّنة: افتح http://localhost:3000 في المتصفّح.

  • يمكنك النقر على رابط إعادة ضبط كلمة المرور مباشرةً في الرسالة الإلكترونية من Mailpit لضبط كلمة المرور.
  • يمكنك بدلاً من ذلك تسجيل الدخول باستخدام البريد الإلكتروني لحسابك على Google Cloud (من .env) وكلمة المرور التلقائية التي تم إعدادها مسبقًا: Ch4ng3m3!!1.
  1. مراقبة شارات "قياس الأداء المرئي":
    • لاحظ بانر البيئة الأصفر والشارات في واجهة المستخدِم: Notice: Ephemeral Database Active (POSTGRESQL) و[EPHEMERAL DB / STORAGE] 💾 Local.
    • لاحظ العلامة المائية للمنشور: طابع 🏠 (nanobanana_stamp_local.png في أسفل يسار صورة الغلاف). يوفّر ذلك تأكيدًا مرئيًا فوريًا على أنّ الأصول وقاعدة البيانات مرتبطة حاليًا بوحدة تخزين محلية مؤقتة.

الصفحة الرئيسية للمدونة المحلية التي تعرض إشعارات وبيانات القياس عن بُعد المؤقتة والشارات

💡 نصيحة احترافية اختيارية: تمرين على Rails Console هل يهمّك معرفة كيف يتفاعل Rails مع البيانات التي تم إدخالها من واجهة سطر الأوامر؟ الانتقال إلى وحدة التحكّم التفاعلية:

bin/rails console

افحص مستخدم المشرف الذي تمّت تعبئته ديناميكيًا باستخدام متغيّر البيئة:

user = User.find_by(email_address: ENV.fetch("GOOGLE_CLOUD_ACCOUNT"))
# Or simply grab the first user:
user = User.first
puts "Admin email: #{user.email_address} (created via: #{user.created_via})"
exit

4. التحقّق الآلي من صحة الخطوة 2

التحقّق من الإعدادات الأساسية المحلية وإعدادات المشرف:

just workshop-eval 2

just workshop-eval 2 verification output

5- الشرط: الحاويات غير الاحتفاظية

حاويات Cloud Run لا يتم تسجيل بياناتها وهي مؤقتة. إذا نشرنا قاعدة بيانات SQLite ودليل storage/ المحلي مباشرةً على Cloud Run، سيتم محو جميع المشاركات والصور المحمَّلة نهائيًا كلما تم توسيع نطاق الحاوية إلى صفر أو إعادة تشغيلها.

في الخطوة التالية، سننفّذ هذا الإعداد المؤقت عن قصد على Cloud Run لنشهد Stateless Shock بأنفسنا.

5- الخطوة 3: النشر 1 — الصدمة غير المسبوقة (تحقيق نجاح مبكر في 3 دقائق!)

بدلاً من الانتظار لمدة تتراوح بين 15 و20 دقيقة لقواعد البيانات المستندة إلى السحابة الإلكترونية قبل عرض أي شيء على الويب، يبدأ التطوير الحديث بدون خادم بتحقيق نجاح مبكر: نشر تطبيق Rails ذي الحاوية الواحدة مباشرةً على Google Cloud Run في أقل من 3 دقائق.

1. العودة إلى الماضي باستخدام "آلة الزمن"

لضمان أن يكون الإعداد الأوّلي مؤقتًا بنسبة% 100 (قاعدة بيانات SQLite محلية وتخزين ملفات القرص المحلية)، استخدِم محرّك Zero-Branch Time-Machine:

just workshop-rewind 1

💡 ما الذي حدث للتو؟workshop-rewind 1 طبَّق تراكب الإعدادات stage-1-stateless على blog/config/ بدون مغادرة فرع main. تم إعداد تطبيقك باستخدام SQLite على قرص الحاوية وActiveStorage على نظام الملفات المحلي.

2. نشر Puma في حاوية واحدة على Cloud Run

يمكنك النشر مباشرةً من الرمز المصدري إلى Cloud Run. تنشئ Rails 8 الحديثة تلقائيًا Dockerfile رسميًا وجاهزًا للاستخدام (مع عمليات إنشاء متعدّدة المراحل وjemalloc وإجراءات أمان غير جذرية). عند النشر من المجلد blog/، ترصد خدمة Google Cloud Build هذا المورد الأصلي Dockerfile مباشرةً وتوفّر خدمة مُدارة بدون خادم:

# Ensure you are inside the Rails application directory
cd blog

# Ensure default region is set
export GOOGLE_CLOUD_REGION="europe-west1"

# Deploy single-container service from source (with dummy key fallback if starting without credentials)
gcloud run deploy blog \
  --source . \
  --region $GOOGLE_CLOUD_REGION \
  --allow-unauthenticated \
  --set-env-vars GOOGLE_CLOUD_ACCOUNT=$GOOGLE_CLOUD_ACCOUNT,SECRET_KEY_BASE_DUMMY=1

من المفترَض أن يظهر لك ناتج تقدّم مشابه لما يلي:

gcloud run deploy building and deploying container

⏳ من المفترض أن تستغرق هذه العملية بضع دقائق بينما تحزّم Cloud Build الحاوية وتوفّر مراجعة Cloud Run.

🧯 تحديد المشاكل وحلّها والتحذيرات الشائعة

PERMISSION_DENIED: Build failed because the default service account is missing required IAM permissions

? هناك سببان مختلفان يؤديان إلى ظهور الخطأ نفسه:

  1. فعّلت gcloud للتو إحدى واجهات برمجة التطبيقات. إذا ظهرت الرسالة The following APIs are not enabled ... cloudbuild.googleapis.com عند النشر وأجبت بـ Y، من المحتمل أنّ عملية الإنشاء قد بدأت قبل اكتمال نشر الأذونات الجديدة، إذ يحذّر طلب gcloud نفسه من أنّ "هذه العملية ستستغرق بضع دقائق". انتظِر دقيقة واحدة وأعِد تنفيذ الأمر نفسه، وعادةً ما ينجح. تفعِّل الخطوة 1 terraform apply عادةً واجهة برمجة التطبيقات هذه نيابةً عنك، لذا من المفترض ألا يظهر لك الطلب على الإطلاق.
  2. لا تتضمّن "حساب الخدمة التلقائي" أدوارًا. تفرض مشاريع GCP الحديثة الحدّ الأدنى من الأذونات المميّزة، ما يؤدي إلى تعذُّر عمليات النشر من المصدر. يمكنك حلّ المشكلة باتّباع الخطوات التالية:
    تعرض أداة
    just project-status   # grants storage.admin, logging.logWriter, artifactregistry.writer, cloudbuild.builds.builder
    
    just workshop-test هذا الخطأ أيضًا الآن، لذا ننصحك بإعادة تشغيلها إذا استمر تعذُّر النشر.

أثناء عملية النشر:

  1. ينشئ Cloud Run صورة حاوية Rails.
  2. يتم تخصيص نطاق TLS عام وآمن له: https://blog-[hash]-[region].a.run.app.
  3. يبدأ توجيه حركة المرور إلى Puma على المنفذ 8080.

3- ‫✨ لحظة الإبهار المبكرة

افتح عنوان URL الذي تم إنشاؤه في Cloud Run في المتصفّح.

  1. تطبيق Rails 8 الحديث الخاص بك متاح الآن على Google Cloud!
  2. سجِّل الدخول باستخدام بيانات اعتماد المشرف (GOOGLE_CLOUD_ACCOUNT وAPP_ADMIN_PASSWORD). إذا لم تجد زر تسجيل الدخول، انقر على "مشاركة جديدة" وسيطلب منك إدخال اسم المستخدم وكلمة المرور أولاً:صفحة تسجيل الدخول إلى Rails 8 مع بانرات القياس عن بُعد المؤقتة
  3. انقر على "مشاركة جديدة"، واكتب مقالة بعنوان "مشاركتي الأولى على Cloud Run"، وأرفِق صورة، ثم انقر على إنشاء مشاركة.
  4. يتم نشر مشاركتك مع التنسيق الكامل، ويتم عرض صورتك.
  5. لاحظ بيانات القياس عن بُعد المرئية:
    • شارة العنوان: [EPHEMERAL DB / STORAGE] 💾 Local
    • علامة مائية على هيئة صورة: طابع 🏠 (شارة القرص المؤقت 127.0.0.1 في أسفل يسار الشاشة)

🐝 انضموا إلى قائمة الصدارة في "ورشة العمل المباشرة"! إذا كنت متصلاً بالإنترنت وكان المراقب يعرض قائمة الصدارة، وأردت الانضمام إليها، أضِف عنوان URL الخاص بخدمة Cloud Run هنا: 👉 تسجيل Cloud Run في قائمة الصدارة

تفاصيل مراجعة خدمة مدونة Google Cloud Run Console

4. 💥 The Catch: The Stateless Shock & The "Puma Workaround" Trap

‫Cloud Run هي منصة بدون خادم ولا يتم تسجيل بياناتها. عندما تنخفض عدد الزيارات إلى صفر، يقلّل Cloud Run عدد مثيلات الحاوية إلى صفر لتوفير المال. عند وصول طلب HTTP جديد أو نشر نسخة جديدة من الحاوية، يبدأ Cloud Run صورة حاوية جديدة ونظيفة تمامًا.

بانر المهام المتوقّفة

عند إنشاء مشاركة أو إرفاق صورة في عملية نشر بحاوية واحدة، يضيف Rails مهام ActiveJob إلى قائمة الانتظار (مثل تحليل أبعاد الصورة أو فهرسة البيانات الوصفية). ولكن بما أنّه لا يتم تشغيل عامل في الخلفية، سيظهر بانر التحذير التالي:

إشعار: 8 مهام في الخلفية في انتظار التنفيذ حاليًا

الحلّ المغري: تشغيل Solid Queue داخل Puma

قد يقول أحد المطوّرين الأذكياء: "لحظة! تتيح لنا Rails 8 تشغيل Solid Queue مباشرةً داخل عملية خادم الويب Puma من خلال تفعيل

SOLID_QUEUE_IN_PUMA=true

متغير البيئة!"

لنجرّب هذا الحلّ البديل على Cloud Run:

# Force a revision update to enable Solid Queue inside Puma
gcloud run services update blog \
  --region $GOOGLE_CLOUD_REGION \
  --update-env-vars SOLID_QUEUE_IN_PUMA=true

الآن، ارجع إلى المتصفّح وأعِد تحميل الصفحة:

💥 الصدمة عديمة الجنسية:

  1. الخبر السار: أصبح المنفِّذ Solid Queue نشطًا الآن داخل Puma. يتم استنزاف أي وظائف جديدة على الفور.
  2. الاستحمام بماء بارد (The Catch!):
    • بما أنّ Cloud Run نشر نسخة جديدة، تم استبدال مثيل الحاوية السابق.
    • تمت إزالة المقالة التي كتبتها وملف قاعدة بيانات SQLite على القرص بالكامل.
  3. الدرس المعماري: يؤدي تشغيل العمليات في الخلفية داخل Puma إلى استهلاك وحدة المعالجة المركزية/ذاكرة الوصول العشوائي الثمينة الخاصة بسلسلة الويب، ولا يزال لا يحل مشكلة الثبات.

5- التحقّق الآلي من صحة الخطوة 3

تحقَّق من عملية نشر Cloud Run ومعالجة التنبيهات:

just workshop-eval 3

✨ الدرس المستفاد: إنّ الحلول البديلة التي تستخدم حاوية واحدة، مثل SOLID_QUEUE_IN_PUMA، مفيدة في التطوير المحلي، ولكن في بنية السحابة الإلكترونية الحديثة:

  • تتطلّب ملفات الوسائط Google Cloud Storage (GCS) (الخطوة 4).
  • تتطلّب البيانات الارتباطية وقوائم انتظار المهام استخدام Google Cloud SQL المُدارة (الخطوة 5).
  • يجب وضع العمليات التي تتطلّب موارد كثيرة في حاوية sidecar مخصّصة (الخطوة 6).

6. الخطوة 4: نشر 3 — التخزين الدائم في "خدمة التخزين السحابي من Google" وتحذير POLA

في هذه الخطوة، نفصل مساحة تخزين الوسائط والملفات عن قرص الحاوية من خلال نقل ActiveStorage إلى Google Cloud Storage (GCS)، وذلك باستخدام عناوين URL الموقّعة القصيرة الأمد من خلال IAM Credentials API (iam: true).

قبل تعديل أي إعداد، عليك فهم ثلاثة أمور يحتاجها Rails للتواصل مع GCS:

‫① رقم تعريف المشروع + اسم الحزمة: يجب أن يعرف Rails مكان تخزين الكائنات الثنائية الكبيرة. يقرأ التطبيق GOOGLE_CLOUD_PROJECT من البيئة ويستمد اسم الحزمة على النحو التالي: ${GOOGLE_CLOUD_PROJECT}-activestorage-prod. لقد أنشأ Terraform من الخطوة 1 هذا الحزمة بالفعل، لذا ليس عليك اتّخاذ أي إجراء إضافي.

② حساب خدمة لديه الأذونات المناسبة: يتم تشغيل Cloud Run بصفتها حساب الخدمة التلقائي في Compute Engine (PROJECT_NUMBER-compute@developer.gserviceaccount.com). يحتاج حساب الخدمة هذا إلى دورَين في "إدارة الهوية وإمكانية الوصول" (IAM) في الحزمة:

  • ‫roles/storage.objectAdmin: لتحميل الكائنات الثنائية الكبيرة وعرضها
  • roles/iam.serviceAccountTokenCreator على نفسه — لطلب بيانات من واجهة برمجة التطبيقات IAM Credentials signBlob وإنشاء عناوين URL موقَّعة قصيرة الأمد

⚠️ الطريق المختصر المغري —

public: true أسرع طريقة لتفعيل GCS هي ضبط public: true في storage.yml ومنح allUsers:objectViewer في الحزمة. يتم تحميل الصور على الفور بدون الحاجة إلى تسجيل الدخول. يجب عدم فعل ذلك. يؤدي استخدام حزمة علنية إلى تسريب جميع وسائط المستخدم التي تم تحميلها إلى الإنترنت المفتوح إلى الأبد. يستخدم تصميمنا iam: true للحفاظ على خصوصية الحزمة بنسبة% 100، إذ إنّ كل عنوان URL لصورة هو رمز مميّز موقَّع وتنتهي صلاحيته يتم إنشاؤه على الفور من خلال واجهة برمجة التطبيقات IAM Credentials API.

③

‫ACTIVE_STORAGE_SERVICE=google: متغيّر البيئة الفردي الذي ينقل Rails من القرص المحلي إلى GCS في وقت التشغيل. لا يلزم إجراء أي تغييرات على الرموز البرمجية أو إعادة نشر منطق التطبيق، بل يكفي تبديل متغيّر بيئة واحد.

1. العودة إلى الماضي باستخدام "آلة الزمن"

انتقِل إلى المرحلة 2 من "آلة الزمن":

just workshop-rewind 2

افحص blog/config/storage.yml — ستلاحظ أنّ التطبيق يحدّد ثلاث بيئات GCS مسماة (dev / test / prod)، وكلها تستخدم النمط الآمن نفسه:

<% gcp_project = ENV.fetch("GOOGLE_CLOUD_PROJECT") { Rails.application.credentials.dig(:gcs, :project) } %>
<% gcs_signer_sa = ENV.fetch("GCS_SIGNER_SA_EMAIL") { "rails-cloudrun-sa@#{gcp_project}.iam.gserviceaccount.com" } %>

google_prod: &google_prod
  service: GCS
  project: <%= gcp_project %>
  bucket: <%= gcp_project %>-activestorage-prod  # ← derived from project ID, no extra env var!
  iam: true        # Sign URLs via IAM Credentials signBlob API (no private key JSON required!)
  gsa_email: <%= gcs_signer_sa %>

# Convenience alias — ACTIVE_STORAGE_SERVICE=google points here
google:
  <<: *google_prod

بنية التوقيع في &quot;إدارة الهوية وإمكانية الوصول&quot; في GCS

2. منح أذونات التخزين والتوقيع في "إدارة الهوية وإمكانية الوصول"

تأكَّد من أنّ حساب الخدمة لوقت تشغيل Cloud Run لديه أذونات لتوقيع عناوين URL وتحميل الكائنات:

export PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)")
export RUN_SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
export GCS_BUCKET="${GOOGLE_CLOUD_PROJECT}-activestorage-prod"

# Grant Storage Object Admin
gcloud storage buckets add-iam-policy-binding gs://$GCS_BUCKET \
  --member="serviceAccount:$RUN_SA" \
  --role="roles/storage.objectAdmin"

# Grant Service Account Token Creator (required for IAM signed URLs)
gcloud iam service-accounts add-iam-policy-binding $RUN_SA \
  --member="serviceAccount:$RUN_SA" \
  --role="roles/iam.serviceAccountTokenCreator"

3- عملية النشر الثالثة على Cloud Run مع ربط GCS

أعِد النشر مع تفعيل GCS. تأكَّد من أنّك داخل المجلد blog/ (وإلا سيحاول Cloud Run إنشاء التطبيق باستخدام Buildpacks بدلاً من Dockerfile في Rails 8، ما يؤدي إلى حدوث أخطاء في الإنشاء).

استخدِم --update-env-vars (وليس --set-env-vars) للحفاظ على متغيرات البيئة الحالية (المفتاح الرئيسي وإعدادات الشراء داخل التطبيق وما إلى ذلك):

cd blog
gcloud run deploy blog \
  --source . \
  --region $GOOGLE_CLOUD_REGION \
  --update-env-vars ACTIVE_STORAGE_SERVICE=google

💡 ملاحظة: في حال تعذّر النشر، انقر على رابط سجلّات Cloud Build المتوفّر بسهولة في ناتج الوحدة الطرفية (كما هو موضّح في الشكل أدناه). يمكنك أيضًا طلب المساعدة من Antigravity في فك رموز الخطأ وإصلاحه.

رابط سجلّ الخطأ في Cloud Build في ناتج الوحدة الطرفية

4. ‫✨ The Surviving Image & The Cloud Stamp

  1. أعِد تحميل عنوان URL الخاص بمدونتك.
  2. أنشئ مشاركة مدونة جديدة بعنوان "النجاة من العاصفة" وحمِّل صورة.
  3. انظر إلى أسفل يسار الصورة:
    • تمت إزالة طابع 🏠.
    • تم استبداله بطابع Cloud GCS الملوّن (nanobanana_stamp_cloud.png).
  4. فرض إعادة تشغيل حاوية أخرى (من المفترض أن يؤدي ذلك إلى إنشاء مراجعة رابعة):
    gcloud run services update blog --region $GOOGLE_CLOUD_REGION --update-env-vars RESTART_TRIGGER=$(date +%s)
    
  5. أعِد تحميل الصفحة: أثناء إعادة ضبط قاعدة بيانات SQLite، تكون بيانات الصورة الثنائية آمنة وسليمة في Google Cloud Storage. إجراء عملية التحقّق من خلال واجهة سطر الأوامر (CLI):
    gcloud storage ls -l -r gs://$GCS_BUCKET/
    

في ما يلي قائمة بالملفات الثنائية الكبيرة (BLOB) وعناصر الوسائط التي قد تظهر لك في Google Cloud Storage Browser Console في حزمة الإنتاج (gs://$GCS_BUCKET):

تفاصيل حزمة Google Cloud Storage Console التي تعرض كائنات ثنائية كبيرة ثابتة للصور

5- ‫⚠️ التحذير: بانر تحذير بشأن المهام العالقة

عند تحميل الصورة، أضافت ActiveStorage مهمة تحليل (ActiveStorage::AnalyzeJob) إلى قائمة الانتظار لاستخراج الأبعاد والبيانات الوصفية.

ومع ذلك، لا يشغّل Cloud Run حاليًا سوى حاوية ويب واحدة (puma). في Rails 8، تخزّن Solid Queue المهام التي تعمل في الخلفية في قاعدة البيانات، ولكن لا ينفّذ أحد bundle exec rails solid_queue:start!

انظر إلى أعلى صفحة مدوّنتك: سيظهر لك بانر تحذير ساطع من عرض blog/app/views/layouts/_check_stuck_jobs.html.erb.

سنحلّ هذه المشكلة بشكل صحيح في الخطوة 6، عندما ننتقل إلى بنية sidecar متعددة الحاويات مناسبة للإنتاج مع حاوية worker مخصّصة تعمل عليها Solid Queue بشكل مستقل عن زيارات الويب.

6. التحقّق من صحة الخطوة 4 بشكل آلي

تحقَّق من إعدادات GCS وبيانات القياس عن بُعد للوظائف المتوقّفة:

just workshop-eval 4

7. الخطوة 5: قائمة التحقّق قبل الإطلاق: Cloud SQL وSecrets وإمكانية الاتصال بقاعدة البيانات

بعد أن أتقنت استخدام الحاويات غير الاحتفاظ بالحالة وGoogle Cloud Storage في الخطوتين 3 و4، أكملت أداة Terraform إعداد الخلفية الخاصة ببيئة الإنتاج في الخلفية.

قبل نشر بنية sidecar الكاملة المكوّنة من 3 حاويات (النشر 4 في الخطوة 6)، تعمل هذه الخطوة كـ فحص سريع قبل الرحلة للتحقّق من سلامة قاعدة البيانات وإمكانية الاتصال بها وإمكانية الوصول إلى البيانات السرية.

‫0. متغيرات بيئة التمهيد

صدِّر جميع متغيّرات shell المطلوبة مباشرةً من نواتج Terraform:

# Core project variables (should already be set from Step 0)
export GOOGLE_CLOUD_REGION="${GOOGLE_CLOUD_REGION:-europe-west1}"

# Compute runtime service account
export PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)")
export RUN_SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"

# Cloud SQL instance name (from Terraform output, NOT hardcoded)
export SQL_INSTANCE_NAME=$(cd iac && terraform output -raw sql_instance_name 2>/dev/null || gcloud sql instances list --format='value(name)' --limit=1)

# Database password (from Terraform output)
export DB_PASSWORD=$(cd iac && terraform output -raw db_password 2>/dev/null || echo "CHANGE_ME")

# GCS bucket names
export GCS_BUCKET="${GOOGLE_CLOUD_PROJECT}-activestorage-prod"

echo "✅ Pre-flight environment variables ready:"
echo "   RUN_SA=$RUN_SA"
echo "   SQL_INSTANCE_NAME=$SQL_INSTANCE_NAME"
echo "   GCS_BUCKET=$GCS_BUCKET"

1. فحص حالة Cloud SQL

تأكَّد من أنّ آلة PostgreSQL الافتراضية المُدارة قد انتهت من عملية التوفير الأوّلية وأنّها تعمل بشكل سليم:

echo "Checking instance: $SQL_INSTANCE_NAME"
gcloud sql instances describe $SQL_INSTANCE_NAME --format="value(state)"

يجب أن تكون النتيجة RUNNABLE.

2. (اختياري) اختبار اتصال قاعدة البيانات من الكمبيوتر المحمول

في مرحلة الإنتاج، تتصل حاويات Cloud Run بشكل آمن بخدمة Cloud SQL من خلال خادم وكيل مشفّر (127.0.0.1:5432) بدون عرض قاعدة البيانات على الإنترنت العام (0.0.0.0/0).

ℹ️ ملاحظة: هذه الخطوة اختيارية تمامًا. في Cloud Run، يتعامل حاوية sidecar الرسمية الخاصة بالخادم الوكيل للمصادقة في Cloud SQL مع الاتصال المتبادل عبر TLS تلقائيًا. لا تحتاج إلى تثبيت أي شيء على جهازك لإكمال ورشة العمل.

إذا كان لديك cloud-sql-proxy وpsql مثبّتَين على جهازك وتريد التحقّق من إمكانية الاتصال المباشر من الكمبيوتر المحمول، اتّبِع الخطوات التالية:

# Optional: test connectivity via Google-managed mTLS proxy
gcloud sql connect $SQL_INSTANCE_NAME --user=rails_user --database=rails_production

(إذا طُلب منك إدخال كلمة مرور، أدخِل

$DB_PASSWORD

. وإلّا، يمكنك الانتقال مباشرةً إلى الخطوة 3 أدناه.)

3- فحص الأسرار التي تم توفيرها باستخدام Terraform

يجب عدم تخزين كلمات مرور قاعدة البيانات أو مفاتيح واجهة برمجة التطبيقات أو مفاتيح Rails الرئيسية بنص عادي في git أو في متغيّرات بيئة الحاويات. في بنية السحابة الإلكترونية الحديثة، نستخدم Google Cloud Secret Manager لإدخال وقت التشغيل بدون ثقة.

ℹ️ نفّذت Terraform هذه الخطوة نيابةً عنك في الخطوة 1:

  1. ‫rails-master-key: لفك تشفير بيانات اعتماد Rails في وقت التشغيل
  2. ‫rails-db-password: كلمة مرور عشوائية آمنة من ناحية التشفير وتتطابق مع مستخدم Cloud SQL.
  3. rails-admin-password: كلمة مرور آمنة لمستخدم المشرف الأولي

تأكَّد من توفّر أسرارك وتفعيلها في Secret Manager:

gcloud secrets list --filter="name:rails-"

قائمة Google Cloud Secret Manager Console التي تم ضبط أسرار التطبيق فيها

🔑 مزامنة master.key مع الكمبيوتر المحمول (مزامنة محلية)

بما أنّ blog/config/master.key يتم تجاهله في git لأسباب أمنية، قد لا يتضمّن النسخة المحلية بعد. نفِّذ مقتطف الترميز السهل هذا الذي يصلح نفسه تلقائيًا لاسترداده من Secret Manager حتى تتمكّن أدوات Rails المحلية من فك تشفير بيانات الاعتماد:

if [ ! -f blog/config/master.key ]; then
  echo "📥 Pulling rails-master-key from Secret Manager to local blog/config/master.key..."
  gcloud secrets versions access latest --secret=rails-master-key > blog/config/master.key
  echo "🔑 Created local blog/config/master.key! 🎉"
else
  echo "✅ Local blog/config/master.key already exists! 🚀"
fi

🧪 Playground: هل تريد إنشاء سرّ مخصّص لمعرفة مدى سهولة ذلك؟ لا تقتصر خدمة Secret Manager على Rails فقط. يمكنك إنشاء أي سرّ باستخدام أمر بسيط واحد من واجهة سطر الأوامر:

echo -n "MySuperSecretValue123!" | gcloud secrets create workshop-fun-secret --data-file=-
echo "✨ Created workshop-fun-secret in Secret Manager! 🪄"

4. التحقّق من أذونات Secret Accessor

لكي تتمكّن حاويات Cloud Run من ربط هذه الأسرار كمتغيرات بيئية أو عمليات ربط وحدات تخزين عند بدء التشغيل، يجب أن يكون لحساب خدمة وقت التشغيل ($RUN_SA) الدور roles/secretmanager.secretAccessor في كل سر.

لقد منحك Terraform هذا الإذن خلال الخطوة 1. تحقَّق من ربط سياسة إدارة الهوية وإمكانية الوصول (IAM) على rails-master-key:

# Verify that Cloud Run runtime SA has Secret Accessor role
gcloud secrets get-iam-policy rails-master-key \
  --flatten="bindings[].members" \
  --format="table(bindings.role,bindings.members)" \
  --filter="bindings.members:$RUN_SA"

سيظهر لك ما يلي:

ROLE                                 MEMBERS
roles/secretmanager.secretAccessor   serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com

مقارنة أمان الخادم الوكيل للمصادقة في Cloud SQL

5- التحقّق الآلي من صحة البيانات قبل الرحلة

نفِّذ مجموعة اختبارات الخطوة 5 التلقائية للتأكّد من أنّ قاعدة البيانات قابلة للتنفيذ، وأنّ الأسرار غير فارغة، وأنّ أذونات "إدارة الهوية وإمكانية الوصول" صحيحة:

just workshop-eval 5

8. الخطوة 6: نشر 4 — حاويات Enterprise المتعددة (المعيار الذهبي)

والآن، لنجمّع البنية المرجعية النهائية: Multi-Container Cloud Run.

1. استعادة قاعدة الذهب

أعِد ضبط إعدادات المستودع إلى الحالة الأساسية main:

just workshop-restore-gold

2. فحص "مخطط الإنتاج" الذي يتضمّن 3 حاويات

افتح blog/compose.prod.yaml وافحص البنية:

بنية الإنتاج المتعددة الحاويات في Cloud Run

graph LR
  Client[Internet / HTTPS Client] -->|Port 8080| Web[Web Container: Puma]
  Web -->|Localhost:5432| Proxy[Sidecar: Cloud SQL Auth Proxy]
  Worker[Worker Container: Solid Queue] -->|Localhost:5432| Proxy
  Proxy -->|IAM mTLS Tunnel| CloudSQL[(Google Cloud SQL PostgreSQL)]

يتم تشغيل عملية نشر الإنتاج في ثلاث حاويات منسَّقة تشترك في الشبكة المحلية نفسها:

  1. web: يعرض هذا الخيار عدد الزيارات عبر HTTP على المنفذ 8080 من خلال Puma.
  2. ‫worker: حاوية مخصّصة لتنفيذ عمليات المعالجة في الخلفية تعمل على تشغيل bundle exec rails solid_queue:start.
  3. استبدِل cloudsql-proxy بما يلي: الحاوية الجانبية الرسمية للخادم الوكيل للمصادقة في Google Cloud SQL (gcr.io/cloud-sql-connectors/cloud-sql-proxy:2) المرتبطة بـ 127.0.0.1:5432.

3- تنفيذ عمليات نقل قواعد البيانات من خلال مهمة Cloud Run

قبل توجيه زيارات الويب إلى عملية النشر الجديدة، يجب تنفيذ عمليات نقل قواعد البيانات وعمليات التعبئة الأولية في Cloud SQL.

⚖️ كيفية تنفيذ عمليات نقل البيانات في الحاويات: استراتيجيتان

في بيئات السحابة الإلكترونية المستندة إلى الحاويات، هناك طريقتان شائعتان لتنفيذ عمليات نقل قواعد البيانات:

  • الخيار (أ): تلقائي داخل نقطة دخول Docker (entrypoint.sh ينفّذ db:migrate عند تشغيل الحاوية)
    • المزايا: مبرمَجة بالكامل، ولا يحتاج المطوّرون إلى تذكُّر تنفيذ أمر.
    • العيوب: خطيرة في التوسّع التلقائي بلا خادم عندما يوسّع Cloud Run نطاقه إلى 10 مثيلات في الوقت نفسه في ظل عدد كبير من الزيارات، تتنافس 10 حاويات لتنفيذ عمليات النقل في الوقت نفسه، ما يؤدي إلى قفل قاعدة البيانات أو حدوث حالات تنافسية في المخطط أو انتهاء مهلة التشغيل.
  • الخيار "ب": التحديد الواضح من خلال مهمة Cloud Run (اختيارنا المعماري)
    • المزايا: تحكّم كامل وأمان المعاملات وعدم حدوث أي حالات تعارض. يتم تشغيله كمهمة حاوية مخصّصة لمرة واحدة منفصلة تمامًا عن عرض حركة بيانات الويب. في حال تعذُّر نقل البيانات، لن يتأثّر عرض المحتوى على الويب.
    • العيوب: يتطلّب تشغيل مهمة واجهة سطر الأوامر أو خطوة CI/CD قبل النشر.

نختار الخيار "ب" لضمان أعلى مستوى من موثوقية الإنتاج.

‫⚠️ مهم: DATABASE_URL التنسيق والالتزام الصارم بمعيار RFC 3986 في Ruby 3.4.5

تتصل "مهام Cloud Run" بخدمة Cloud SQL باستخدام الخادم الوكيل الأصلي لمقبس Unix (/cloudsql/PROJECT:REGION:INSTANCE). DATABASE_URL يجب استخدام تنسيق الشرطة المائلة الثلاثية:

postgresql:///rails_production?user=rails_user&password=PASS&host=/cloudsql/PROJECT:REGION:INSTANCE
# Core environment variables for database migration & initial admin seed
export GOOGLE_CLOUD_ACCOUNT="${GOOGLE_CLOUD_ACCOUNT:-$(gcloud config get-value account)}"
echo "Admin user will be seeded for: $GOOGLE_CLOUD_ACCOUNT"

# Get the latest blog image from Cloud Run (jobs don't support --source)
export BLOG_IMAGE=$(gcloud run services describe blog --region=$GOOGLE_CLOUD_REGION --format='value(spec.template.spec.containers[0].image)')

# Build the DATABASE_URL in triple-slash format (required for Unix socket proxy)
export DB_PASSWORD=$(gcloud secrets versions access latest --secret=rails-db-password)
export CLOUDSQL_CONNECTION="${GOOGLE_CLOUD_PROJECT}:${GOOGLE_CLOUD_REGION}:${SQL_INSTANCE_NAME}"
export DB_URL="postgresql:///rails_production?user=rails_user&password=${DB_PASSWORD}&host=/cloudsql/${CLOUDSQL_CONNECTION}"

# Create migration job — note ALL 4 database URL env vars and GOOGLE_CLOUD_ACCOUNT for seed user creation
gcloud run jobs create rails-migrate \
  --image=$BLOG_IMAGE \
  --command "bin/rails" \
  --args "db:prepare" \
  --set-env-vars="DATABASE_URL=${DB_URL},DATABASE_QUEUE_URL=${DB_URL},DATABASE_CACHE_URL=${DB_URL},DATABASE_CABLE_URL=${DB_URL},GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT},GOOGLE_CLOUD_ACCOUNT=${GOOGLE_CLOUD_ACCOUNT}" \
  --set-secrets="RAILS_MASTER_KEY=rails-master-key:latest" \
  --set-cloudsql-instances="${CLOUDSQL_CONNECTION}" \
  --service-account=$RUN_SA \
  --region $GOOGLE_CLOUD_REGION 2>/dev/null || true

# Execute migration job
gcloud run jobs execute rails-migrate --region $GOOGLE_CLOUD_REGION --wait

💡 قواعد بيانات متعدّدة في Rails 8: يستخدم التطبيق 4 قواعد بيانات (أساسية، وقائمة انتظار، وذاكرة تخزين مؤقت، وكابل) لكل database.yml. يجب أن تشير جميع متغيرات البيئة الأربعة DATABASE_*_URL إلى مثيل Cloud SQL نفسه، وإلا لن يتم إنشاء جداول Solid Queue/Cache/Cable.

بعد db:prepare، حمِّل مخططات Solid Queue/Cache/Cable:

# Schema load for queue, cache, and cable databases (creates solid_queue_jobs, etc.)
gcloud run jobs update rails-migrate \
  --command "bash" \
  --args "-c,DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bin/rails db:schema:load:queue db:schema:load:cache db:schema:load:cable" \
  --region $GOOGLE_CLOUD_REGION

gcloud run jobs execute rails-migrate --region $GOOGLE_CLOUD_REGION --wait

ناتج تنفيذ الأمر gcloud run jobs executions describe JOBNAMEفي الشكل أعلاه، يمكنك الاطّلاع على ناتج محتمل لـ

gcloud run jobs executions describe $JOBNAME

وحدة تحكّم مهام Google Cloud Run تعرض حالة &quot;تمت&quot; لمهمة rails-migrateوحدة تحكّم Google Cloud Run Jobs تعرض

rails-migrate

تم التنفيذ بنجاح

4. عملية النشر الرابعة: نشر حاويات متعددة على Cloud Run باستخدام Docker Compose

يمكنك الآن نشر حزمة الإنتاج الأصلية المكوّنة من 3 حاويات (web وworker وcloudsql-proxy) مباشرةً من compose.prod.yaml:

# 1. Self-healing environment exports (guarantees no missing master key or DB password)
export RAILS_MASTER_KEY="${RAILS_MASTER_KEY:-$(cat config/master.key 2>/dev/null || gcloud secrets versions access latest --secret=rails-master-key)}"
export DB_PASSWORD=$(gcloud secrets versions access latest --secret=rails-db-password)
export CLOUDSQL_INSTANCE_CONNECTION_NAME="${GOOGLE_CLOUD_PROJECT}:${GOOGLE_CLOUD_REGION}:${SQL_INSTANCE_NAME}"

# 2. Database credentials & GCS attachments
export DB_USER="rails_user"
export DB_NAME="rails_production"
export GCS_BUCKET="${GCS_BUCKET}"
export ACTIVE_STORAGE_SERVICE="google"
export GOOGLE_CLOUD_ACCOUNT="${GOOGLE_CLOUD_ACCOUNT:-$(gcloud config get-value account)}"
export GEMINI_API_KEY="${GEMINI_API_KEY:-dummy-key}"

# 3. Deploy the full multi-container sidecar architecture to Cloud Run
gcloud run compose up compose.prod.yaml \
  --region=$GOOGLE_CLOUD_REGION \
  --allow-unauthenticated

علامة التبويب &quot;حاويات Google Cloud Run Console&quot; التي تعرض 3 حاويات جانبيةوحدة تحكّم Google Cloud Run: علامة التبويب "الحاويات" تعرض جميع حاويات Sidecar الثلاث (

cloudsql-proxy

،

web

،

worker

)

💡 خطأ في واجهة المستخدم / خلل في وحدة التحكّم: في القائمة المنسدلة لوحدة التحكّم، قد تلاحظ أنّ Cloud Run يصنّف الإدخال الأول على أنّه Ingress container: cloudsql-proxy. لا داعي للقلق. هذا خلل مرئي في واجهة المستخدم في Google Cloud Console (تم تتبُّعه في #149) ناتج عن ترتيب تبعية الحاويات. في الواقع، web هو الحاوية الوحيدة التي تستمع إلى منفذ HTTP 8080 وتتعامل مع% 100 من زيارات الويب الواردة، بينما تعرض cloudsql-proxy زيارات PostgreSQL الخاصة فقط على المنفذ 5432.

5- ✨ لحظة الإبهار: عدم ظهور أي تحذيرات واستمرار التخزين السحابي الكامل!

خذ نفسًا عميقًا واستمتع بمشاهدة شاشتك: اختفت جميع بانرات التحذير! 🪄

مدونة حول Cloud Run بدون تحذيرات ومنشور دائم على Cloudمدوّنة Rails 8 المخصّصة للإنتاج: واجهة مستخدم نقية، ولا توجد أيّ لافتات تحذير، وحالة Cloud SQL ثابتة!

أصبح التطبيق الآن جاهزًا للاستخدام في بيئات الإنتاج ومصمّمًا للعمل على السحابة الإلكترونية:

  • يتم توفيرها من خلال Google Cloud SQL المُدارة (PostgreSQL) عبر إنشاء أنفاق آمنة باستخدام بروتوكول mTLS.
  • تخزين الوسائط بأمان في Google Cloud Storage الخاص مع توقيع كائن ثنائي كبير الحجم في "إدارة الهوية وإمكانية الوصول"
  • عدم ظهور أي تحذيرات بشأن مساحة التخزين المؤقتة أو تنبيهات بشأن تعذُّر إكمال أي مهام

افتح عنوان URL الخاص بخدمة Cloud Run:

  1. اطّلِع على شارات القياس عن بُعد:
    • تم التحقّق من [CLOUD PERSISTENT 🐘 ☁️] في بياناتك الأساسية.
    • تظهر شارتَا Google Cloud Run وGCS باللون الأخضر في التذييل.
    • تمت إزالة بانر التحذير بشأن الوظائف المتوقفة.
  2. إنشاء مشاركات في المدونة وتعليقات
  3. يمكنك إعادة التشغيل أو إعادة النشر عدة مرات كما تريد، إذ تبقى بياناتك ومشاركاتك وتعليقاتك ومواد العرض إلى الأبد في Cloud SQL وGCS.

6. التحقّق الآلي من صحة الخطوة 6

تحقَّق من إعداد الحاويات المتعدّدة:

just workshop-eval 6

9. الخطوة 7: مسارات الذكاء الاصطناعي التوليدي وPodcastifier و"بحث الكنز" في "خدمة التخزين السحابي من Google" 🏴‍☠️

من خلال تشغيل Solid Queue في حاوية مخصّصة وتفعيل Google Cloud Storage، يمكننا الاستفادة من الذكاء الاصطناعي التوليدي غير المتزامن.

1. ‫NanoBanana Vintage Cover Generator (مسار GenAI جاهز للاستخدام)

NanoBanana Mascot

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

🧠 طريقة عملها

  • المصادقة: لا يلزم استخدام أي مفاتيح لواجهة برمجة التطبيقات. يستخدم بيانات الاعتماد التلقائية للتطبيق (roles/aiplatform.user على Vertex AI) تلقائيًا من خلال حساب خدمة وقت التشغيل في Cloud Run. (بدلاً من ذلك، إذا تم توفير GEMINI_API_KEY، يتم استدعاء Gemini API مباشرةً).
  • سير العمل: عند إنشاء مقالة بدون صورة غلاف، يتم وضع طلب معاودة الاتصال after_commit في قائمة الانتظار ضمن GenerateCoverImageJob في قائمة انتظار Solid.
  • النموذج: يطلب من نموذج Google المتعدد الوسائط (Gemini 2.5 Flash Image / Imagen على Vertex AI) إنشاء ملصق فيلم إيطالي أصلي من الستينيات ("Locandina di un film 1960") يتضمّن مشهدًا قصيرًا لموزة و"8" ياقوتية لامعة.
  • علامة مائية ديناميكية لتحديد المصدر: تستخدم libvips لختم شارة مرئية بشكل ديناميكي في الزاوية (ختم سحابة ملونة على GCS).
  • التبديل السريع في Turbo Stream: عند إرفاق الصورة، يرسل Turbo::StreamsChannel.broadcast_refresh_to(post) آخر الأخبار إلى متصفّح القارئ مباشرةً بدون الحاجة إلى إعادة تحميل الصفحة يدويًا.

مخطط التسلسل غير المتزامن لبنية الذكاء الاصطناعي في NanoBanana

‫📐 عرض مصدر مخطط التسلسل في Mermaid

sequenceDiagram
  autonumber
  actor User as 👤 Reader / Author
  participant Web as 🌐 Web (Puma)
  participant DB as 🐘 Cloud SQL (Solid Queue)
  participant Worker as ⚙️ Worker (Solid Queue)
  participant Vertex as 🧠 Vertex AI (Gemini Flash / Imagen)
  participant GCS as ☁️ Google Cloud Storage

  User->>Web: POST /posts (Title & Body, No Cover)
  Web->>DB: 1. Save Post & Enqueue GenerateCoverImageJob
  Web-->>User: Redirect to Post (turbo_stream subscribed)

  Worker->>DB: 2. Dequeue GenerateCoverImageJob
  Worker->>Vertex: 3. Prompt for 1960s Italian Poster (ADC auth)
  Vertex-->>Worker: 4. Raw Image Buffer
  Worker->>Worker: 5. Stamp Provenance Watermark (libvips)
  Worker->>GCS: 6. Attach Blob via ActiveStorage (IAM signBlob)
  Worker->>Web: 7. Turbo::StreamsChannel.broadcast_refresh_to(post)
  Web-->>User: ⚡ Live UI Hot-Swap: Poster magically appears!

🧪 جرِّبها الآن

يمكنك تشغيلها بطريقتَين:

  • الخيار أ (واجهة مستخدم على الويب): انقر على منشور جديد، وأدخِل عنوانًا مثل "بنية بلا خادم باستخدام Ruby on Rails 8"، واكتب بعض المحتوى، واترك حقل إدخال ملف صورة الغلاف فارغًا. انقروا على إنشاء منشور.
  • الخيار "ب" (CLI Runner): شغِّل من نافذة الأوامر:
    bin/rails runner 'Post.create!(title: "Serverless Architecture with Ruby on Rails 8", body: "Exploring asynchronous AI pipelines with Solid Queue and NanoBanana!")'
    

انتظِر لمدة تتراوح بين 15 و30 ثانية تقريبًا: راقِب سجلّات المنفِّذ في Solid Queue في Cloud Run. سيظهر الملصق القديم مباشرةً في منشورك.

📸 TODO(riccardo): إضافة لقطة شاشة لمشاركة مدوّنة تتضمّن ملصقًا لفيلم إيطالي قديم من الستينيات من إنشاء الذكاء الاصطناعي يظهر فيه موز وروبي 8

2. مهمة "تحويل المحتوى إلى بودكاست بلغتين" (تمرين عملي على البرمجة المضادة للجاذبية)

⚠️ ملاحظة مهمة: على عكس "أداة إنشاء الأغلفة" الموضّحة أعلاه، لم يتم تنفيذ هذه الميزة بعد!

إذا فتحت blog/app/jobs/podcastifier_job.rb، سيظهر لك نموذج فارغ لرمز بديل:

class PodcastifierJob < ApplicationJob
  queue_as :default

  def perform(post_id)
    # TODO(student): COMPLETE_ME — Pair program with Google Antigravity to implement
    # Italian TTS podcast generation using voice 'it-IT-Wavenet-A' via Cloud TTS / ADC!
  end
end

مهمتك في هذا التمرين العملي هي البرمجة الثنائية مع Google Antigravity لإكمال هذه الميزة:

  1. طلب Antigravity:"ساعدني في تنفيذ PodcastifierJob في app/jobs/podcastifier_job.rb. يجب أن يستردّ المنشور، ويترجم أو يركّب نظرة عامة صوتية باللغة الإيطالية باستخدام خدمة "تحويل النص إلى كلام" من Google Cloud (الصوت it-IT-Wavenet-A مع رمز اللغة it-IT من خلال بيانات اعتماد ADC)، ويرفق الملف الصوتي بـ post.podcast_audio_it باستخدام ActiveStorage!"
  2. إضافة مشغّل إلى العرض: في app/views/posts/show.html.erb، أضِف مشغّل HTML5 عندما @post.podcast_audio_it.attached?.
  3. تشغيل عملية إنشاء الصوت: عند الحفظ، ستنفّذ Solid Queue مهمة تحويل النص إلى كلام بشكل غير متزامن في الخلفية.

Podcastifier Bilingual UI with Dual Audio Players

🎧 نماذج من نتائج Podcastifier:

💡 هل تحتاج إلى مساعدة أو واجهتك مشكلة؟ يتوفّر حلّ مرجعي كامل ومجرَّب على الفرع solutions/podcastifier. يمكنك الاطّلاع عليه في أي وقت باستخدام git diff solutions/podcastifier.

3- [اختياري] 🏴‍☠️ البحث عن الكنز في GCS (استرداد البيانات الثنائية الكبيرة في وحدة التحكّم)

ℹ️ التحقّق من المتطلّبات الأساسية: محلي

cloud-sql-proxy

واجهة سطر الأوامر مطلوبة يربط هذا التمرين الاختياري جهاز الكمبيوتر المحمول مباشرةً بخدمة Google Cloud SQL من خلال نفق mTLS مشفّر. تحقَّق مما إذا كان خادم وكيل واجهة سطر الأوامر مثبَّتًا:

which cloud-sql-proxy
  • في حال التثبيت: تابِع القراءة أدناه.
  • في حال عدم تثبيت التطبيق وأردت إكمال هذه المهمة: ثبِّته باستخدام:
    # Linux (x86_64)
    curl -o /tmp/cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.0/cloud-sql-proxy.linux.amd64
    chmod +x /tmp/cloud-sql-proxy && sudo mv /tmp/cloud-sql-proxy /usr/local/bin/cloud-sql-proxy
    # macOS (Homebrew)
    brew install cloud-sql-proxy
    
  • في ما عدا ذلك: يمكنك الانتقال مباشرةً إلى الخطوة 7 الخاصة بالتحقّق المبرمَج.

هل تتذكّر الصورة التي حمّلتها في الخطوة 4 قبل أن تؤدي إعادة تشغيل الحاوية إلى محو قاعدة بيانات SQLite المؤقتة؟ لا يزال ملف الصورة هذا مخزّنًا بأمان في حزمة GCS الخاصة بك كـ "كائن ثنائي كبير يتيم".

لنستخدم وحدة تحكّم Rails لإنقاذها وإرفاقها بمنشور Cloud SQL:

# Connect to your production database via Rails console and Cloud SQL Proxy
cloud-sql-proxy --port 5432 ${GOOGLE_CLOUD_PROJECT}:${GOOGLE_CLOUD_REGION}:${SQL_INSTANCE_NAME} &
DATABASE_URL=postgresql://rails_user:${DB_PASSWORD}@127.0.0.1:5432/rails_production bin/rails c

داخل وحدة تحكّم Rails:

# 1. Search for orphaned GCS blobs in ActiveStorage
orphan_blobs = ActiveStorage::Blob.where.missing(:attachments)
puts "🏴‍☠️ Found #{orphan_blobs.count} orphaned GCS blobs!"

# 2. Attach the surviving blob to your latest post
if orphan_blobs.any?
  post = Post.last
  post.cover_image.attach(orphan_blobs.first)
  post.save!
  puts "🎉 Rescued blob #{orphan_blobs.first.filename} attached to '#{post.title}'!"
end
exit

📸 TODO(riccardo): add screenshot of the interactive Rails console executing the GCS Treasure Hunt snippet and rescuing orphaned ActiveStorage blobs

أعِد تحميل مدونتك: ستتم استعادة الصورة التي تم تحميلها خلال الخطوة 4 من Stateless Shock وإرفاقها بشكل دائم بمشاركتك على Cloud SQL.

4. التحقّق الآلي من صحة الخطوة 7

التحقّق من صحة مهام وأصول الذكاء الاصطناعي التوليدي:

just workshop-eval 7

10. الخطوة 8: اختيار مغامرتك الخاصة / المهام المتقدّمة 🏆

بعد إتقان بنية المرجع الأساسية، اختَر مهمة التخرّج.

‫🛡️ المهمة 1: الثقة المعدومة في Google IAP (Identity-Aware Proxy)

  • مستوى الصعوبة: متوسط (أمان المؤسسة)
  • الهدف: حظر تطبيق Cloud Run بحيث لا يمكن الوصول إليه إلا من خلال حسابات Google أو Gmail التي تم إثبات ملكيتها، ما يؤدي إلى إلغاء تسجيل الدخول باستخدام كلمة المرور نهائيًا.
  • طريقة العمل:
    1. يوقف جهاز موازنة الحمل للتطبيقات الخارجية لبروتوكول HTTPS عملية Google OAuth ويتأكّد من الهوية قبل أن تصل الزيارات إلى Cloud Run.
    2. تعيد Google توجيه عناوين إثبات الهوية (X-Goog-Authenticated-User-Email).
    3. تسجّل Rails تلقائيًا هوية Google التي تم إثباتها من خلال blog/app/controllers/concerns/iap_authenticatable.rb:
      module IapAuthenticatable
        extend ActiveSupport::Concern
        included { before_action :authenticate_via_iap }
      
        private
        def authenticate_via_iap
          return unless (raw = request.headers["X-Goog-Authenticated-User-Email"]).present?
          email = raw.sub(/^accounts\.google\.com:/, "")
          user = User.find_or_create_by!(email_address: email) { |u| u.password = SecureRandom.hex(16) }
          start_new_session_for(user) unless authenticated?
        end
      end
      
    4. فعِّل وحدة Terraform في iac/iap.tf باستخدام enable_iap = true وحدِّد حسابات Google المسموح بها:
      iap_allowed_users = [var.google_cloud_account]
      

بانر &quot;تم التعرّف على عملية تسجيل الدخول&quot; داخل التطبيق

📊 المهمة 2: القياس عن بُعد في هندسة موثوقية الموقع (SRE) في مرحلة الإنتاج وCloud Logging

  • مستوى الصعوبة: متوسط (إمكانية تتبُّع البيانات)
  • الهدف: بث سجلات التطبيقات بتنسيق JSON المنظَّم مع معرّفات ربط التتبُّع مباشرةً إلى Google Cloud Logging ورصد استثناءات الإنتاج في الوقت الفعلي باستخدام Cloud Error Reporting.
  • طريقة العمل:
    • اضبط blog/config/environments/production.rb لإصدار سجلّات JSON منظَّمة تتضمّن تصنيفات تتبُّع GCP.
    • يمكنك تشغيل استثناء اختبار مقصود ومشاهدة مجموعة Google Cloud Error Reporting وإرسال إشعار إليك على الفور.

📸 TODO(riccardo): إضافة لقطة شاشة من Error Reporting في Google Cloud تعرض استثناءات مجمّعة في مرحلة الإنتاج وحِمل JSON منظَّم من Cloud Logging مع تصنيفات التتبُّع

‫🧠 Quest 3: pgvector البحث الدلالي وRAG في Gemini

  • مستوى الصعوبة: صعب (مشروع التخرّج باستخدام الذكاء الاصطناعي التوليدي)
  • الهدف: البحث عن المقالات بشكل مفاهيمي باستخدام تضمينات متجهة مخزَّنة في PostgreSQL على Cloud SQL
  • طريقة العمل:
    • فعِّل إضافة vector على مثيل PostgreSQL في Cloud SQL:
      CREATE EXTENSION IF NOT EXISTS vector;
      
    • أضِف neighbor gem إلى blog/Gemfile وأنشئ تضمينات نصية باستخدام Gemini (text-embedding-004) على Post#after_save.
    • إجراء طلبات بحث عن مسافة جيب التمام (<=>) لتشغيل شريط بحث دلالي باستخدام Turbo Streams

📸 مطلوب اتّخاذ إجراء(riccardo): إضافة لقطة شاشة لشريط البحث الدلالي في المدونة المباشرة يعرض نتائج مطابقة مفاهيمية مع نتائج تشابه جيب التمام

⚡ المهمة 4: مساعد تدقيق الأداء وتحسين محركات البحث

  • الصعوبة: سهلة / ممتعة (تجربة المطوّر)
  • الهدف: تدقيق Core Web Vitals و"سرعة عرض أكبر محتوى مرئي" (LCP) ومؤشرات سهولة القراءة وفقًا لمقياسَي Flesch-Kincaid وFog باستخدام أداتَي Antigravity وSpeedgrapher

📸 TODO(riccardo): add screenshot of Speedgrapher / Lighthouse SEO and Core Web Vitals audit summary

🤔 Curiosity Box: الحصول على وحدة تحكّم Rails مباشرة على Cloud Run

💡 السؤال: على الأجهزة الافتراضية التقليدية أو منصات PaaS المشابهة لمنصة Heroku، يستخدم المطوّرون عادةً SSH للوصول إلى مثيل قيد التشغيل أو استخدام heroku run rails console لفحص بيانات الإنتاج أو إصلاحها مباشرةً. ما هو المكافئ الأصلي لـ Cloud Run، وكيف يمكنك تعزيز أمانه؟

السبب

gcloud run exec

غير متوفّر لخدمات Cloud Run

‫Cloud Run هو نظام أساسي بدون خادم: قد يتم تشغيل الحاوية على شكل صفر أو واحد أو مئات من المثيلات المتطابقة، بدون هوية ثابتة أو غلاف قابل للعنونة. يختلف ذلك بشكل أساسي عن Kubernetes (kubectl exec)، حيث يحتوي كلّ جزء على مقبس مسمّى يمكنك ربطه به.

# ❌ This does NOT exist for Cloud Run Services:
gcloud run services exec blog --region europe-west1 -- rails console

# ✅ This exists for Cloud Run JOBS (non-interactive, one-shot tasks):
gcloud run jobs execute my-job --region europe-west1 --wait

الأنماط الأربعة الأصلية في Google Cloud Platform

نمط

الأوامر

تفاعلية؟

الأمان

1. خادم وكيل محلي + قاعدة بيانات بعيدة

DATABASE_URL=... rails console

✅ كامل

‫🔒 أمان النقل المتبادل (mTLS) في Cloud SQL Auth Proxy

2. مهمة Cloud Run (للمرة الواحدة)

gcloud run jobs execute --wait

❌ النص البرمجي فقط

‫🔒 دور "إدارة الهوية وإمكانية الوصول" مطلوب

3. Web Console gem + IAP

المتصفّح → /rails/console

✅ كامل

‫🛡️ IAP Zero-Trust

4. Cloud Shell + Proxy

Cloud Shell → bin/rails c

✅ كامل

‫🔒 هوية Google

النمط 1 (يُنصح به لفحص البيانات): شغِّل وحدة تحكّم Rails محلية تتواصل مباشرةً مع Cloud SQL PostgreSQL من خلال الخادم الوكيل للمصادقة في Cloud SQL:

# In one terminal: start the auth proxy
cloud-sql-proxy rails8-fl-20260907b-xm8ko4:europe-west1:your-instance

# In another terminal: open a read-write or sandbox console
DATABASE_URL=postgresql://rails_user:${DB_PASSWORD}@127.0.0.1:5432/rails_production \
  bin/rails console --sandbox

يمكنك الاستفادة من التفاعل الكامل مع IRB بدون أي تعرّض للشبكة، إذ يستخدم Cloud SQL Proxy بروتوكول mTLS عبر مقبس نطاق Unix أو عنوان IP للحلقة المتكررة.

النمط 3 (وحدة تحكّم الويب + IAP Zero-Trust، مسار المتصفّح المحسّن):

تقدّم جوهرة web-console جلسة IRB مستندة إلى المتصفّح. وبدون حماية، يصبح ثغرة أمنية خطيرة، لذا لا تعرضه مطلقًا على الملأ. ولكن عند دمجها مع خدمة "الشراء داخل التطبيق" من Google (Quest 1)، ينهار سطح الاختراق: يمكن فقط لهويات Google المعتمَدة في قائمة السماح الوصول إلى نقطة النهاية /rails/console.

# Gemfile — only load in development/staging, never production without IAP!
gem "web-console", group: :development

# config/environments/production.rb — if IAP is enabled:
config.web_console.allowed_ips = ["0.0.0.0/0"]  # IAP terminates auth before us

⚠️ يتطلّب النمط 3 تفعيل ميزة "الشراء داخل التطبيق" في Quest 1 أولاً. وبدون ذلك، يكون استخدام /rails/console على عنوان URL متاح للجميع بمثابة ترك غرفة الخادم مفتوحة. باستخدام IAP، تصبح أداة مشغّل فعّالة ومحمية بميزات "نهج الثقة المعدومة" من Google.

طرح سؤال على Antigravity: "كيف يمكنني تشغيل ؟"

rails console --sandbox

على قاعدة بيانات الإنتاج في Cloud SQL بأمان باستخدام الخادم الوكيل للمصادقة في Cloud SQL".

‫🏆 التخرّج من The Hive: إثبات العمل الذي تم التحقّق منه من خلال مراقب

بعد إكمال المهمة التي اخترتها، احصل على كأس التخرّج من الخطوة 8 🏆 في قائمة الصدارة في The Hive:

  1. إرسال مهمتك على GitHub:
    • افتح مشكلة جديدة في المستودع الرسمي: github.com/palladius/rails8-app-on-gcp/issues/new
    • تنسيق العنوان: 🎓 [Step 8 Completed] :
    • في نص الرسالة، أدرِج وصفًا موجزًا لما أنجزته ورابطًا إلى عملية نشر Cloud Run المباشرة.
  2. ربط تطبيق Cloud Run بالمشكلة:
    • دوِّن رقم المشكلة (مثل 88 أو عنوان URL الكامل https://github.com/palladius/rails8-app-on-gcp/issues/88).
    • أدخِل متغيّر البيئة STEP_8_GHI في خدمة Cloud Run:
      gcloud run services update blog \
        --region europe-west1 \
        --update-env-vars STEP_8_GHI=<YOUR_ISSUE_NUMBER>
      
    • تأكَّد من أنّ نقطة نهاية /status.json تعرض الآن مهمتك:
      curl -s https://<YOUR_APP>.run.app/status.json | jq .quest
      
      من المفترض أن يظهر لك ما يلي:
      {
        "step_8_completed": true,
        "ghi_issue": 88,
        "ghi_url": "https://github.com/palladius/rails8-app-on-gcp/issues/88"
      }
      
  3. الحصول على مراجعة من المراقب وفتح الكأس الذهبية:
    • اطّلِع على قائمة الصدارة في The Hive: سيعرض صفّك الآن شارة بلون كهرماني: ⏳ GHI #XX review pending.
    • اطلب من مراقبي ورشة العمل (@palladius أو @emilianodellacasa أو @ricc) فحص عملية النشر.
    • ما إن يعلّق أحد المشرفين LGTM على مشكلتك، سيرفع The Hive تلقائيًا مستوى تقدّمك إلى 8/8 🏆 مع شارة ذهبية/أرجوانية متوهجة وكأس دائم يرتبط مباشرةً بمشكلتك الرئيسية.

11. الخاتمة والحذف 🎓

تهانينا! 🎉

لقد أنشأت تطبيق Rails 8 ونشرته على Google Cloud Platform، وهو تطبيق جاهز للاستخدام في المؤسسات ومناسب لمرحلة الإنتاج:

  • التقدّم بدون فروع: إتقان سير العمل الحديث باستخدام تراكبات "آلة الزمن" على main
  • Cloud Storage: مساحة تخزين خاصة وقابلة للتوسّع مع توقيع كائن ثنائي كبير باستخدام "إدارة الهوية وإمكانية الوصول" (iam: true)
  • ‫Cloud SQL: خدمة PostgreSQL مُدارة وآمنة باستخدام أنفاق أمان النقل المتبادل (mTLS) في الخادم الوكيل للمصادقة في Cloud SQL.
  • Secret Manager: لا يتم تسريب أي بيانات اعتماد بنص عادي أو ملفات .env.
  • حاويات متعددة في Cloud Run: حاويات جانبية معزولة لشبكة Puma وعامل Solid Queue وخادم وكيل
  • الذكاء الاصطناعي التوليدي: إنشاء ملصقات قديمة بشكل غير متزامن وتجميع بودكاست باستخدام تكنولوجيا "تحويل النص إلى كلام" من خلال Vertex AI
  • مهمات: عمليات شراء داخل التطبيق مستندة إلى نهج الثقة المعدومة، وإمكانية مراقبة هندسة موثوقية المواقع الإلكترونية، وتضمينات pgvector

🧹 تنظيف الموارد

لتجنُّب الرسوم المستمرة بعد إكمال ورشة العمل، احرِص على تنظيف موارد Google Cloud. اختَر الطريقة الأنسب لبيئتك:

استخدِم هذا الخيار إذا كنت تستخدم مشروعًا حاليًا أو مشتركًا على Google Cloud يتضمّن موارد أو بيانات أخرى. ويؤدي إلى إتلاف فقط الموارد التي تم توفيرها خلال ورشة العمل هذه (Cloud Run وCloud SQL وSecret Manager وحِزم GCS)، مع ترك بقية مشروعك بدون تغيير.

cd iac
terraform destroy -auto-approve

الخيار 2: حذف المشروع بالكامل (بدون ترك أي أثر 🌪️)

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

gcloud projects delete $GOOGLE_CLOUD_PROJECT --quiet