1. מבוא
ברוכים הבאים לסדנת Rails 8 ב-Google Cloud. ב-Codelab הזה תלמדו איך להעביר אפליקציית Rails 8 מודרנית מבסיס נתונים מקומי פשוט של SQLite לארכיטקטורת הפניה מוכנה לייצור ומתאימה לארגונים ב-Google Cloud.
בסדנה הזו אנחנו לא רק פורסים אפליקציה – אנחנו מבצעים מודרניזציה בענן ברמת ייצור, עם דעה מגובשת (שיטת Lift-and-shift (העברה בלי שינויים) בצורה נכונה!).
מתחילים בהגדרה מקומית טיפוסית של 'עובד במחשב שלי' (דיסק זמני, SQLite מוטמע, עבודות בתהליך) ובהדרגה מפתחים אותה לארכיטקטורת ענן ארגונית, חסינת תקלות ב-Google Cloud:
- 🐘 מ-SQLite מקומי → Cloud SQL (PostgreSQL): אין יותר מחיקה של מסד הנתונים בהפעלה מחדש. אנחנו עוברים ל-PostgreSQL מנוהל שמחובר בצורה מאובטחת באמצעות שרת proxy ל-Cloud SQL Auth (mTLS, ללא כתובות IP ציבוריות).
- 🪣 מ-Ephemeral Local Storage → Private GCS: דיסק ללא שרת הוא זמני. אנחנו מחברים את ActiveStorage ל-Google Cloud Storage פרטי באמצעות כתובות URL חתומות של IAM (
iam: true) – אין קטגוריות ציבוריות ואין מפתחות שנחשפו. - 🚀 ממשימות בתהליך ← Cloud Run עם כמה קונטיינרים: אנחנו מגשרים בין מונולית Rails לבין ענן בלי שרת! באמצעות Docker Compose רגיל, אנחנו מריצים את Solid Queue כקונטיינר ייעודי של Worker לצד Puma בפוד מרובה קונטיינרים של Cloud Run – בלי התפשטות של מיקרו-שירותים, בלי הרעבה של תהליכים, DHH היה גאה!
🍌 ובגלל שאנחנו חיים בשנת 2026, אנחנו משפרים את כל המערך באמצעות Google Antigravity. זה לא משהו שחשבנו עליו בדיעבד. יש לנו הנחיות ו מיומנויות שיעזרו לכם להשתמש בכלי בצורה טובה יותר (וליהנות) מהסדנה הזו!
🏛️ תרשים עזר לארכיטקטורה של היעד (וריאציה 1: וקטור שטוח ונקי של Enterprise)
זוהי ארכיטקטורת ההפניה הקנונית של Google Cloud שתיצרו ותפרסו (גרסה 1: Clean Flat Vector Enterprise). היא מדגימה את שירות הייצור הרב-קונטיינרי של Cloud Run שמחובר לכל שירותי ה-AI והשירותים המנוהלים של Google Cloud:

🎞️ האבולוציה של הארכיטקטורה: מאפס לארכיטקטורה מבוססת-ענן
במהלך הסדנה, תוכלו לראות איך המערכת שלנו עוברת מודרניזציה הדרגתית, החל מבסיס חד-מכוני זמני ועד לתוכנית Google Cloud מנוהלת במלואה.

מה תלמדו
- איך משתמשים בGoogle Antigravity כדי להבין את Rails 8 ואת Google Cloud.
- איך מריצים אבחון אוטומטי לפני ההשקה (
just workshop-test). - איך מספקים תשתית ענן ב-Google Cloud באופן אסינכרוני באמצעות Terraform תוך המשך פיתוח מקומי ללא חסימה באמצעות פלאגין שמתממשק עם שירותים חיצוניים
google. - איך מבטלים דפוסי אבטחה לא רצויים: מאגרי GCS פרטיים (
iam: true) ומנהרות mTLS של Cloud SQL Auth Proxy במקום לפתוח את0.0.0.0/0. - איך להחדיר סודות ישירות מ-Google Cloud Secret Manager.
- איך לתזמן משימות רקע אסינכרוניות של AI גנרטיבי (מחולל כריכות של NanoBanana, סינתוז של פודקאסט דו-לשוני) באמצעות Solid Queue.
קדימה, מתחילים!
2. שלב 0: דרישות מוקדמות, הגדרת Antigravity ואימות החיוב
💡 התרחיש: אתם והצוות שלכם יוצרים אפליקציית Rails 8 שהיא קריטית לפעילות העסקית. לפני שמתחילים לכתוב קוד או להפעיל משאבי ענן, צריך להגדיר את כלי הפיתוח, לחבר את Google Antigravity ולאמת את פרטי הכניסה ל-Google Cloud ואת בסיס החיוב.
1. רשימת משימות לביצוע לפני התחלת העבודה
לפני שמתחילים, חשוב לוודא שהכלים הבאים זמינים בסביבה שלכם:
-
just(גרסה 1.21 ואילך): (just --version) כלי להרצת משימות – כל פקודה בסדנה הזו היאjustמתכון (אפשר לבקש מ-AI Harness להתקין אותו, או לעיין במאמרskills/rails8app-workshop). - Git (גרסה 2.30 ואילך): (
git --version) לניהול גרסאות, ליצירת ענפים ולשיבוט המאגר. - Google Cloud SDK (
gcloudCLI): מותקן ומעודכן. - Terraform CLI (גרסה 1.5 ואילך): להקצאת תשתית הצהרתית.
-
dockerו-docker-compose: מותקן ופועל באופן מקומי. - Ruby
3.4.5: (ruby -v— pinned byblog/.ruby-version; ask your AI harness to install3.4.5if missing). Rails 8 מוצהר ב-Gemfileומוגדר אוטומטית על ידיbundle install. - Google Antigravity 2.0: עוזר ה-AI האוטונומי שלכם לתכנות בזוגות (הורדה של Google Antigravity 2.0). תתבצע כניסה באמצעות חשבון Gmail אישי – לא נדרש טוקן API.
2. אימות ל-Google Cloud, הגדרה ייעודית ו-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 מבודד את כל הגדרות ה-CLI (חשבון, פרויקט מכסה, אזור ברירת מחדל) במיוחד לסדנה הזו. אחרי שמסיימים, אפשר לחזור להגדרה הרגילה בכל שלב באמצעות gcloud config configurations activate default.

3. 🚨 אימות החיוב ב-GCP
⚠️ הערה: כדי לפרוס פתרונות ב-Google Cloud SQL וב-Cloud Run, צריך חשבון חיוב פעיל ומקושר או קרדיטים חינוכיים תקפים לסדנה. עכשיו נבדוק שההגדרה שלכם נכונה. כדי לעשות זאת, מריצים את בדיקת האימות של החיוב:
gcloud beta billing projects describe $GOOGLE_CLOUD_PROJECT
מוודאים שהערך billingEnabled: true מוחזר. אפשר גם להציג רשימה של כל החשבונות לחיוב:
gcloud billing accounts list
חשוב לוודא שלפחות אחד מהם מציג את OPEN: True. אם אין חשבון פעיל, מקשרים חשבון לחיוב או מממשים את שובר הקרדיט לסדנה בדף החיוב במסוף Google Cloud.

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. קישור חשבון לחיוב לפרויקט GCP לא ייתן לכם הנחיה נוספת אחת של Antigravity.
אם הסוכן מפסיק עם הודעת מכסה, אפשר לנסות את האפשרויות הבאות – שתי הראשונות הן בחינם ומתבצעות באופן מיידי:
- החלפת מאגר מודלים המיכסה מתועדת לכל משפחת מודלים, וב-Antigravity מוצגים שני מאגרי מידע נפרדים (מודלים של Gemini לעומת מודלים של Claude/GPT). מודלים של Claude ו-GPT-OSS זמינים בתוכנית בחינם, כך שלרוב, אם מאגר Gemini מתרוקן, המאגר השני נשאר ללא שימוש. משתמשים בבורר המודלים שמתחת לתיבת הפרומפט.

- בוחרים מודל Flash במקום מודל Pro. מגבלות הקצב קשורות לכמות העבודה שהסוכן מבצע לכל בקשה, ולכן מודל קל יותר מאפשר להשתמש במה שנשאר.
- משתמשים ב-Antigravity CLI עם מפתח Gemini API משלכם. אין תמיכה בשימוש במפתח משלכם ב-Antigravity IDE – אפשר להשתמש ב-CLI כחלופה. יוצרים מפתח בחינם ב-Google AI Studio (צריך פרויקט אבל לא חשבון לחיוב), ואז מורידים ומגדירים את Antigravity CLI.
- אפשר לחכות, או לשדרג. המכסה של רמת השירות החינמית מתאפסת מדי שבוע (הודעת השגיאה מציינת את תאריך האיפוס). Google AI Pro/Ultra היא הדרך המתועדת היחידה להגדיל את מכסת השימוש באפליקציה למחשב. המינויים האלה מתעדכנים כל חמש שעות, ואפשר להשתמש בקרדיטים ל-AI שרכשתם כדי לחרוג מהמכסה.
- למרות שזה סדנת Antigravity, רוב כלי ה-AI לתכנות אמורים לעבוד גם כן – פשוט מפנים את הכלי לספרייה
AGENTS.mdו-skills/במאגר.
6. אימות אוטומטי של שלב 0
מוודאים שהסביבה בשלב 0 תואמת ב-100% לחבילת ההערכה:
just workshop-eval 0
✨ הפתרון המדהים: המנוע של Antigravity להערכת סדנאות מאמת את גרסאות ה-CLI, את סביבת Ruby ואת אימות ה-ADC תוך פחות מ-2 שניות!
3. שלב 1: הפעלה של Terraform Infrastructure ואבחון לפני ההפעלה
💡 האסטרטגיה: הקצאת מסדי נתונים מנוהלים כמו Google Cloud SQL PostgreSQL נמשכת בערך 10-12 דקות. במקום לחכות בחיבוק ידיים, אנחנו מפעילים תשתית בלתי משתנה באמצעות Terraform ברקע בזמן שאנחנו מפתחים באופן מקומי.
1. הפעלת חבילת הכלים האוטומטית לאבחון לפני ההשקה
לפני שמשיקים תשתית ענן, מריצים את חבילת מקרים לבדיקה המקיפה לפני ההשקה:
just workshop-test
הסקריפט הזה (bin/workshop_diagnostics.rb):
- מאמת את הגדרות הזהות של
GOOGLE_CLOUD_ACCOUNT. - אימות של חיוב פעיל וקישור לפרויקט.
- מאמת את פרטי הכניסה ב-Application Default Credentials (ADC) ל-Vertex AI.
- מאשר את תמונת ה-canary seed של ActiveStorage (
blog/app/assets/images/gcs_dev_image.jpg).

אם התג .env חסר, מעתיקים אותו מהתבנית המתועדת:
cp .env.dist .env
# Edit .env and configure GOOGLE_CLOUD_ACCOUNT with your Google/Gmail account
💡 טיפ: אם just workshop-test מדווח שממשק ה-API לחיוב מושבת בפרויקט שלכם, תוכלו להפעיל אותו במהירות באמצעות:
gcloud services enable cloudbilling.googleapis.com
2. ⏱️ הפעלת תשתית Terraform באופן אסינכרוני
מריצים את המתכון של Terraform בשלב אחד, שיוצר את קטגוריית המצב של הקצה העורפי המרוחק ומפעיל את פריסת התשתית:
just terraform-apply

חלופה ידנית (או הפעלה ישירה של `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 (
iam: true). - תמונה לבדיקת קנרי: מעלה את
gcs_dev_image.jpgל-bucket כדי לאפשר אימות מקצה לקצה של ה-blob. - מופע של Google Cloud SQL PostgreSQL: מתחילה הקצאת משאבים ברקע (כ-10 עד 12 דקות).

💡 הערה: אפשר להתעלם בבטחה מהאזהרה לגבי גיבויים אוטומטיים חסרים – לא נצטרך אותם למסד הנתונים של הסדנה הזו (אבל גיבויים אוטומטיים יומיים הם שיטה מומלצת חיונית לכל מסד נתונים אמיתי של ייצור!).

📝 שימו לב לכתובת ה-URL של Cloud Run: הפקודה Terraform apply אמורה להצליח ולהדפיס את הפלט של התשתית. רושמים את cloud_run_url (או מעתיקים אותו ללוח) – תצטרכו אותו בשלבים הבאים כדי לאמת את שירות השידור החי.
3. אימות אוטומטי בשלב 1 (אופציונלי)
מוודאים שהדרישות המוקדמות וההגדרות משלב 1 עברו בהצלחה:
just workshop-eval 1
⏱️ הערה: הבדיקה הזו בודקת משאבי ענן פעילים, והיא עשויה להימשך כמה דקות. הפעולה הזו היא אופציונלית לחלוטין – בזמן שהיא פועלת בטרמינל, אתם יכולים להמשיך מיד לשלב הבא בלי לחכות!
4. שלב 2: הגדרת בסיס מקומי, Mailpit והצטרפות של אדמינים
נקודת ההתחלה שלנו היא אפליקציית בלוג נקייה ומודרנית של Rails 8 שפועלת ב-localhost עם 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 המוצמד, ואז מתקינים את ה-gems ומגדירים את מסד הנתונים:
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)? מערכת Vanilla Linux Ruby מתעלמת לחלוטין מקבצים .ruby-version ומנסה להתקין אבני חן בכל המערכת. מוודאים שהסמל 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. אפשר גם להשתמש במצב א' (Docker Compose) שבהמשך (just compose-up), שמריץ את מחסנית Ruby המוצמדת בתוך קונטיינרים בלי לגעת ב-gems של המארח.
💡 לא רוצים לחשוב על זה? just install ו-just compose-up משורש המאגר מבצעים את cd blog בשבילכם.
2. 🌱 Smart Seed Auto-Discovery & Admin Bootstrap (בעיות מס' 21 ו-25)
ה-seed של מסד הנתונים (db/seeds.rb) כולל גילוי אוטומטי של סביבה חכמה:
- הוא בודק את מתאם מסד הנתונים הפעיל (SQLite לעומת Postgres) ואת הגדרות האחסון.
- הוא מזהה שלב 0 (Localhost) ויוצר באופן אוטומטי את משתמש האדמין הראשוני ומזין את הפוסט הפדגוגי:
[LOCAL BASELINE] Welcome to Rails 8 on Localhost!- קבצים מצורפים של תמונות עצובות מקומיות (
local_sad_image.png) שמוכנים לשימוש, עם סימן מים שמציין שאחסון הדיסק המקומי הוא זמני.
- הפעולה הזו מפעילה אוטומטית שליחה של אימייל לאיפוס הסיסמה דרך ActionMailer.
מאמתים או מריצים מחדש את ה-seed (המערכת משתמשת אוטומטית ב-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

האפליקציה, שרת ה-SMTP המקומי של Mailpit והכלי לצפייה במסד הנתונים של Adminer פועלים עכשיו יחד במאגרי תגים מבודדים:
- אפליקציית Rails: http://localhost:3000
- ממשק האינטרנט של Mailpit: http://localhost:8025
- Adminer DB UI: 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 והתחברות אדמין
- לבדוק אימיילים יוצאים באמצעות Mailpit: פותחים את
http://localhost:8025בדפדפן. בודקים את תיבת הדואר הנכנס המקומית. במהלךdb:seed, ActionMailer שלח אימייל להצטרפות אדמין, ש-Mailpit תפס באופן מקומי בצורה בטוחה בלי לגעת בשרתי אימייל חיצוניים או בפרטי כניסה:
2. מתחברים לבלוג: פותחים את http://localhost:3000 בדפדפן.
- כדי להגדיר את הסיסמה, אפשר ללחוץ על הקישור לאיפוס הסיסמה ישירות באימייל של Mailpit.
- אפשר גם להתחבר באמצעות כתובת האימייל בחשבון Google Cloud (מ-
.env) וסיסמת ברירת המחדל:Ch4ng3m3!!1.
- התבוננו בתגי הטלמטרייה החזותית:
- שימו לב לבאנר הסביבה הצהוב ולתגים בממשק המשתמש:
Notice: Ephemeral Database Active (POSTGRESQL)ו-[EPHEMERAL DB / STORAGE] 💾 Local. - שימו לב לסימן המים של הפוסט: חותמת הבית (
nanobanana_stamp_local.pngבפינה השמאלית התחתונה של תמונת השער). כך תוכלו לקבל אישור חזותי מיידי שהנכסים ומסד הנתונים שלכם קשורים כרגע לאחסון מקומי זמני.
- שימו לב לבאנר הסביבה הצהוב ולתגים בממשק המשתמש:

💡 טיפ למתקדמים: תרגול בקונסולת Rails רוצים לדעת איך Rails מתקשר עם הנתונים שזרעתם מ-CLI? מעבר למסוף האינטראקטיבי:
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

5. הבעיה: קונטיינרים חסרי מצב
קונטיינרים ב-Cloud Run הם חולפים ולא שומרים מצב. אם נבצע פריסה של מסד הנתונים של SQLite ושל ספריית storage/ המקומית ישירות ל-Cloud Run, כל הפוסטים והתמונות שהועלו יימחקו באופן סופי בכל פעם שמכולה תתבצע התאמה אוטומטית שלה לאפס או שהיא תופעל מחדש.
בשלב הבא, נבצע פריסה מכוונת של ההגדרה הזמנית הזו ב-Cloud Run כדי לראות את ההשפעה של חוסר מצב ממקור ראשון.
5. שלב 3: פריסה 1 – ההלם חסר המצב (WOW מוקדם תוך 3 דקות!)
במקום לחכות 15-20 דקות למסדי נתונים בענן לפני שרואים משהו באינטרנט, פיתוח מודרני ללא שרתים מתחיל בניצחון מוקדם: פריסת אפליקציית Rails עם קונטיינר יחיד ישירות ל-Google Cloud Run תוך פחות מ-3 דקות!
1. הילוך מהיר במכונת הזמן
כדי להבטיח שההגדרה הראשונית שלנו תהיה זמנית לחלוטין (מסד נתונים מקומי של 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 רשמית שמוכנה לייצור (עם גרסאות build מרובות שלבים, 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
הפלט של ההתקדמות אמור להיראות כך:

⏳ הפעולה הזו אמורה להימשך כמה דקות בזמן ש-Cloud Build אורז את הקונטיינר ומקצה את גרסת Cloud Run.
🧯 פתרון בעיות והערות חשובות
PERMISSION_DENIED: Build failed because the default service account is missing required IAM permissions
? יש שתי סיבות שונות לשגיאה הזו:
- gcloud just enabled an API for you. אם הפריסה הדפיסה
The following APIs are not enabled ... cloudbuild.googleapis.comועניתםY, יכול להיות שה-build התחיל לפני שההרשאות החדשות סיימו להתפשט – ההנחיה של gcloud עצמה מזהירה ש"זה ייקח כמה דקות". ממתינים דקה ומריצים מחדש את אותה פקודה. בדרך כלל זה מצליח. בדרך כלל, השלב הראשוןterraform applyמאפשר לכם להשתמש ב-API הזה, כך שלא אמורה להופיע בקשה כזו. - באמת חסרים תפקידים בחשבון השירות של Compute שמוגדר כברירת מחדל. בפרויקטים מודרניים של GCP נאכפות הרשאות מינימליות, ולכן פריסות של מקורות נכשלות. כדי לפתור את הבעיה:
גםjust project-status # grants storage.admin, logging.logWriter, artifactregistry.writer, cloudbuild.builds.builderjust workshop-testמדווח על זה עכשיו, לכן כדאי להריץ מחדש אם הפריסה ממשיכה להיכשל.
במהלך הפריסה:
- מערכת Cloud Run יוצרת את קובץ האימג' של קונטיינר Rails.
- הוא מקצה דומיין TLS ציבורי ומאובטח:
https://blog-[hash]-[region].a.run.app. - התנועה מתחילה להיות מנותבת אל Puma ביציאה 8080.
3. ✨ רגע ה-WOW המוקדם
פותחים את כתובת ה-URL שנוצרה ב-Cloud Run בדפדפן.
- האפליקציה המודרנית שלכם ב-Rails 8 פעילה ב-Google Cloud!
- מתחברים באמצעות פרטי הכניסה של האדמין (
GOOGLE_CLOUD_ACCOUNTו-APP_ADMIN_PASSWORD). אם לא מופיע לחצן כניסה, לוחצים על New Post (פוסט חדש) ומוצגת בקשה להזנת שם משתמש וסיסמה:
- לוחצים על פוסט חדש, כותבים מאמר עם הכותרת הפוסט הראשון שלי ב-Cloud Run, מצרפים תמונה ולוחצים על יצירת פוסט.
- הפוסט מתפרסם עם העיצוב המלא, והתמונה מוצגת.
- שימו לב לטלמטריה החזותית:
- תג בכותרת:
[EPHEMERAL DB / STORAGE] 💾 Local - סימן מים כתמונה: חותמת הבית (
127.0.0.1תג דיסק אחסון ארעי בפינה השמאלית התחתונה).
- תג בכותרת:
🐝 הצטרפות ללידרבורד של סדנת הסטרימינג בשידור חי אם אתם מחוברים לאינטרנט והבוחן מציג את טבלת המובילים, ואתם רוצים להצטרף אליה, אתם צריכים להוסיף את כתובת ה-URL של Cloud Run כאן: הרשמה של Cloud Run לטבלת המובילים

4. 💥 הבעיה: ההלם בלי שמירת מצב ומלכודת "הפתרון העקיף של Puma"
Cloud Run היא פלטפורמה ללא שמירת מצב (stateless) וללא שרת (serverless). כשתנועת האינטרנט יורדת לאפס, Cloud Run מצמצם את מספר מופעי הקונטיינר לאפס כדי לחסוך כסף. כשמתקבלת בקשת HTTP חדשה או כשפורסים גרסה חדשה של קונטיינר, Cloud Run מפעיל קובץ אימג' חדש ונקי של קונטיינר.
באנר של משימות תקועות
כשיוצרים פוסט או מצרפים תמונה בפריסה של קונטיינר יחיד, Rails מוסיף לתור משימות ActiveJob (כמו ניתוח של ממדי תמונה או יצירת אינדקס של מטא-נתונים). אבל מכיוון שאף אחד לא מפעיל עובד ברקע, יוצג באנר האזהרה:

הפתרון המפתה: הפעלת 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
עכשיו חוזרים לדפדפן ומרעננים את הדף:
💥 ההלם חסר המדינה:
- החדשות הטובות: עובד התור של Solid פעיל עכשיו בתוך Puma. כל משימה חדשה תגרום לניצול מיידי של המכסה.
- The Cold Shower (The Catch!):
-
- כי Cloud Run פרס גרסה חדשה, ולכן מופע הקונטיינר הקודם הוחלף.
- המאמר שכתבת וקובץ מסד הנתונים של SQLite בכונן נמחקו לגמרי!
- הלקח בנושא ארכיטקטורה: הפעלת תהליכי רקע בתוך Puma צורכת משאבי CPU/RAM יקרים של שרשור האינטרנט, ועדיין לא פותרת את בעיית ההתמדה.
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 – אחסון קבוע ב-GCS ואזהרה לגבי POLA
בשלב הזה, אנחנו מפרידים את האחסון של קובצי המדיה והקבצים מהדיסק של הקונטיינר על ידי מעבר מ-ActiveStorage ל-Google Cloud Storage (GCS), באמצעות כתובות URL חתומות לטווח קצר דרך IAM Credentials API (iam: true).
לפני שמשנים הגדרות, חשוב להבין את שלושת הדברים ש-Rails צריך כדי לתקשר עם GCS:
① מזהה הפרויקט + שם הקטגוריה – מערכת Rails צריכה לדעת איפה לאחסן את ה-blobs. האפליקציה קוראת את GOOGLE_CLOUD_PROJECT מהסביבה ומסיקה את שם הקטגוריה כ-${GOOGLE_CLOUD_PROJECT}-activestorage-prod. ה-bucket הזה כבר נוצר על ידי Terraform מהשלב הראשון, כך שלא צריך לעשות שום דבר נוסף.
② חשבון שירות עם ההרשאות המתאימות – Cloud Run פועל כחשבון השירות שמוגדר כברירת מחדל ב-Compute Engine (PROJECT_NUMBER-compute@developer.gserviceaccount.com). לחשבון השירות הזה צריכים להיות שני תפקידי IAM בדלי:
-
roles/storage.objectAdmin– להעלאה ולהצגה של Blob -
roles/iam.serviceAccountTokenCreatorבעצמו – כדי לשלוח קריאה ל-IAM CredentialssignBlobAPI וליצור כתובות URL חתומות לטווח קצר
⚠️ הקיצור המפתה –
public: true הדרך המהירה ביותר להפעיל את GCS היא להגדיר את public: true ב-storage.yml ולהעניק את allUsers:objectViewer בדלי. התמונות נטענות באופן מיידי, בלי צורך בכניסה לחשבון. אל תעשו את זה. אם הקטגוריה ציבורית, כל המדיה שהמשתמשים מעלים אליה דולפת לאינטרנט הפתוח, לתמיד. התוכנית שלנו משתמשת ב-iam: true כדי לשמור על פרטיות מלאה של הקטגוריה – כל כתובת 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

2. מתן הרשאות אחסון וחתימה ב-IAM
מוודאים שלחשבון השירות של זמן הריצה ב-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 במקום באמצעות Rails 8 Dockerfile, וזה יוביל לכשלים בבנייה).
משתמשים ב---update-env-vars (ולא ב---set-env-vars) כדי לשמור את משתני הסביבה הקיימים (מפתח ראשי, הגדרות IAP וכו'):
cd blog
gcloud run deploy blog \
--source . \
--region $GOOGLE_CLOUD_REGION \
--update-env-vars ACTIVE_STORAGE_SERVICE=google
💡 טיפ: אם הפריסה נכשלת, לוחצים על הקישור ליומני Cloud Build שמופיע בפלט של המסוף (כמו שמוצג באיור שלמטה). אתם יכולים גם לבקש מ-Antigravity עזרה בפענוח השגיאה ובתיקון שלה.

4. ✨ The Surviving Image & The Cloud Stamp
- מרעננים את כתובת ה-URL של הבלוג.
- תצור פוסט חדש בבלוג בשם "Surviving the Cloud" ותעלה תמונה.
- מסתכלים על הפינה השמאלית התחתונה של התמונה:
- החותמת 🏠 נעלמה!
- הוא מוחלף בחותמת הצבעונית של Cloud GCS (
nanobanana_stamp_cloud.png).
- אילוץ הפעלה מחדש של קונטיינר אחר (אמור להפעיל גרסה רביעית):
gcloud run services update blog --region $GOOGLE_CLOUD_REGION --update-env-vars RESTART_TRIGGER=$(date +%s) - מרעננים את הדף: בזמן האיפוס של מסד הנתונים SQLite, הקובץ הבינארי של התמונה מאובטח ב-Google Cloud Storage. אימות באמצעות CLI:
gcloud storage ls -l -r gs://$GCS_BUCKET/
זו רשימה של אובייקטים מסוג Blob ומדיה שהועלו, שאולי יופיעו במסוף דפדפן האחסון של Google Cloud Storage בקטגוריית הייצור (gs://$GCS_BUCKET):

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, סודות וקישוריות למסד נתונים
בזמן שתרגלתם את השימוש בקונטיינרים חסרי מצב וב-Google Cloud Storage בשלבים 3 ו-4, Terraform סיים להכין את הקצה העורפי של הסביבה שלכם ברקע.
לפני שמפעילים את ארכיטקטורת ה-sidecar המלאה של 3 מאגרי הייצור (הפעלה של 4 בשלב 6), השלב הזה משמש כבדיקה מהירה לפני ההפעלה כדי לוודא את תקינות מסד הנתונים, הקישוריות והגישה לסודות.
0. משתני סביבה של Bootstrap
מייצאים את כל משתני ה-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 באמצעות שרת proxy מוצפן מסוג sidecar (127.0.0.1:5432) בלי לחשוף את מסד הנתונים לאינטרנט הציבורי (0.0.0.0/0).
ℹ️ הערה: השלב הזה הוא אופציונלי לחלוטין. ב-Cloud Run, קובץ ה-sidecar הרשמי של Cloud SQL Auth Proxy מטפל בקישוריות mTLS באופן אוטומטי. לא צריך להתקין שום דבר באופן מקומי כדי להשתתף בסדנה.
אם התקנתם את 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
אסור לאחסן סיסמאות למסדי נתונים, מפתחות API או מפתחות ראשיים של Rails בטקסט פשוט ב-git או במשתני סביבה של קונטיינר. באדריכלות ענן מודרנית, אנחנו משתמשים ב-Google Cloud Secret Manager להחדרת זמן ריצה של אפס אמון.
ℹ️ Terraform טיפל בזה בשבילכם בשלב 1:
-
rails-master-key— לפענוח פרטי הכניסה של Rails בזמן ריצה. -
rails-db-password— סיסמה אקראית מאובטחת מבחינה קריפטוגרפית שתואמת למשתמש ב-Cloud SQL. -
rails-admin-password– סיסמה מאובטחת למשתמש האדמין הראשוני.
מוודאים שהסודות קיימים ופעילים ב-Secret Manager:
gcloud secrets list --filter="name:rails-"

🔑 סנכרון master.key עם המחשב הנייד (סנכרון מקומי)
מטעמי אבטחה, blog/config/master.key מוגדר כ-gitignored, ולכן יכול להיות שהעותק המקומי שלכם עדיין לא כולל אותו. מריצים את קטע הקוד הזה לתיקון עצמי כדי לשלוף את פרטי הכניסה מ-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
🧪 סביבת ניסויים: רוצים ליצור סוד בהתאמה אישית רק כדי לראות כמה זה קל? Secret Manager לא מיועד רק ל-Rails! אפשר ליצור סודות באמצעות פקודה פשוטה אחת ב-CLI:
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

5. אימות אוטומטי לפני הפעלת התהליך
מריצים את חבילת מקרים לבדיקה האוטומטית של שלב 5 כדי לוודא שהמסד נתונים ניתן להרצה, שהסודות לא ריקים והרשאות ה-IAM ירוקות:
just workshop-eval 5
8. שלב 6: פריסה 4 – Enterprise Multi-Container Sidecars (הסטנדרט המוביל)
עכשיו נרכיב את ארכיטקטורת ההפניה האולטימטיבית: Multi-Container Cloud Run!
1. שחזור מדד הזהב
משחזרים את הגדרות המאגר למצב הקנוני main:
just workshop-restore-gold
2. בדיקת ה-Blueprint של סביבת הייצור עם 3 קונטיינרים
פותחים את blog/compose.prod.yaml ובודקים את הארכיטקטורה:

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)]
פריסת הייצור מפעילה שלושה קונטיינרים מתואמים שמשתפים את אותה רשת מקומית:
-
web: משרת תנועת HTTP ביציאה 8080 דרך Puma. -
worker: קונטיינר ייעודי לעיבוד ברקע שבו פועלbundle exec rails solid_queue:start. -
cloudsql-proxy: קובץ sidecar רשמי של שרת proxy ל-Google Cloud SQL Auth (gcr.io/cloud-sql-connectors/cloud-sql-proxy:2) שמשויך ל-127.0.0.1:5432.
3. הפעלת העברות של מסדי נתונים באמצעות משימת Cloud Run
לפני שמעבירים את תנועת האינטרנט לפריסה החדשה שלנו, צריך להפעיל העברות של מסדי נתונים וזריעה ראשונית מול Cloud SQL.
⚖️ איך מריצים העברות ב-Migrate to Containers: שתי אסטרטגיות
בסביבות ענן מבוססות-קונטיינרים, יש שתי גישות נפוצות להרצת העברות של מסדי נתונים:
- אפשרות א': אוטומטית בתוך Docker Entrypoint (
entrypoint.shמופעלתdb:migrateבהפעלה של הקונטיינר)- יתרונות: אוטומטי לחלוטין; המפתחים לא צריכים לזכור להריץ פקודה.
- חסרונות: מסוכן בהרחבה אוטומטית ללא שרתים (serverless)! כש-Cloud Run מבצעת הגדלה של מספר השרתים עד ל-10 מופעים בו-זמנית בתנאים של תנועה גבוהה, 10 קונטיינרים מתחרים על הפעלת העברות נתונים בו-זמנית, מה שגורם לנעילת מסד הנתונים, לתנאי מירוץ של סכימה או לזמני קצוב לתפוגה של אתחול.
- אפשרות ב': הפעלה מפורשת באמצעות Cloud Run Job (הבחירה הארכיטקטונית שלנו)
- יתרונות: שליטה מלאה, בטיחות טרנזקציות ואפס תנאי מירוץ. הוא פועל כמשימת קונטיינר ייעודית חד-פעמית, שמנותקת לחלוטין מהצגת תנועת אינטרנט. אם ההעברה נכשלת, לא תהיה לכך השפעה על הצגת המודעות באתר.
- חסרונות: צריך להפעיל משימת CLI או שלב CI/CD לפני הפריסה.
אנחנו בוחרים באפשרות ב' כדי להבטיח את מהימנות הייצור.
⚠️ קריטי: DATABASE_URL פורמט ו-Ruby 3.4.5 RFC 3986 Strictness
משימות Cloud Run מתחברות ל-Cloud SQL באמצעות שרת proxy מקורי של Unix socket (/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 Multi-Database: האפליקציה משתמשת ב-4 מסדי נתונים (primary, queue, cache, cable) לכל database.yml. כל 4 משתני הסביבה 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
מסוף 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
Google Cloud Run Console: הכרטיסייה Containers (מאגרי תמונות) מציגה את כל שלושת מאגרי התמונות של sidecar (
cloudsql-proxy
,
web
,
worker
)
💡 באג בממשק המשתמש או במסוף: בתפריט הנפתח של המסוף, יכול להיות שתראו שהתווית Cloud Run מופיעה בערך העליון כ-Ingress container: cloudsql-proxy. אל דאגה! זוהי תקלה בממשק המשתמש החזותי במסוף Google Cloud (במעקב ב-#149) שנגרמת בגלל סדר התלות של הקונטיינרים. בפועל, web הוא הקונטיינר היחיד שמקשיב ליציאת HTTP 8080 ומטפל ב-100% מתנועת האינטרנט הנכנסת, בעוד ש-cloudsql-proxy משרת אך ורק תנועת PostgreSQL פרטית ביציאה 5432.
5. ✨ רגע הוואו: אפס אזהרות והתמדה מלאה בענן!
קחו נשימה עמוקה והביטו במסך: כל באנרי האזהרה נעלמו! 🪄
הבלוג בנושא Rails 8 בייצור: ממשק משתמש נקי, ללא באנרים של אזהרות ומצב Cloud SQL מתמשך!
האפליקציה עכשיו מתאימה לסביבת ייצור ופועלת בענן:
- הגיבוי מתבצע באמצעות Google Cloud SQL (PostgreSQL) מנוהל, דרך מנהור mTLS מאובטח.
- הקבצים מאוחסנים בצורה מאובטחת ב-Google Cloud Storage פרטי עם חתימה של IAM blob.
- אפס אזהרות לגבי אחסון זמני ואפס התראות לגבי משרות תקועות.
פותחים את כתובת ה-URL של Cloud Run:
- בודקים את תגי הטלמטריה:
[CLOUD PERSISTENT 🐘 ☁️]מאומת ב-database seed שלכם.- התגים של Google Cloud Run ושל GCS בכותרת התחתונה ירוקים!
- באנר האזהרה לגבי משרות תקועות נעלם לגמרי.
- ליצור פוסטים ותגובות בבלוג.
- אפשר להפעיל מחדש או לפרוס מחדש כמה פעמים שרוצים: הנתונים, הפוסטים, התגובות והנכסים שלכם יישמרו לנצח ב-Cloud SQL וב-GCS.
6. אימות אוטומטי בשלב 6
בודקים את ההגדרה של כמה קונטיינרים:
just workshop-eval 6
9. שלב 7: צינורות של AI גנרטיבי, Podcastifier וחיפוש המטמון של GCS 🏴☠️
עם Solid Queue שפועל בקונטיינר ייעודי ו-Google Cloud Storage פעיל, אנחנו יכולים להפעיל בינה מלאכותית גנרטיבית אסינכרונית!
1. NanoBanana Vintage Cover Generator (צינור AI גנרטיבי מוכן לשימוש)

חדשות טובות: הפונקציונליות הזו כבר מיושמת במלואה בבסיס הקוד! אתם לא צריכים לכתוב קוד – המשימה שלכם היא להפעיל צינור (pipeline) אסינכרוני של AI גנרטיבי בענן ולצפות בו בפעולה.
🧠 איך זה עובד מאחורי הקלעים
- אימות: לא נדרשים מפתחות API! הוא משתמש אוטומטית ב-Application Default Credentials (
roles/aiplatform.userב-Vertex AI) דרך חשבון השירות של זמן הריצה ב-Cloud Run. (לחלופין, אם מסופקGEMINI_API_KEY, מתבצעת קריאה ישירה ל-Gemini API). - הצינור: כשיוצרים מאמר בלי תמונת שער, קריאה חוזרת (callback) של
after_commitמוסיפה אתGenerateCoverImageJobלתור של Solid Queue. - מודל: הנחיה למודל מולטימודאלי של Google (Gemini 2.5 Flash Image / Imagen ב-Vertex AI) ליצור פוסטר אותנטי לסרט איטלקי משנות ה-60 ("Locandina di un film 1960") עם בננה בתפקיד אורח ורובי נוצץ עם הספרה 8.
- סימן מים דינמי של מקור: נעשה שימוש ב-
libvipsכדי להוסיף באופן דינמי תג חזותי בפינה (חותמת ענן צבעונית ב-GCS). - החלפה מהירה של תמונות ב-Turbo Stream: כשמצרפים תמונה,
Turbo::StreamsChannel.broadcast_refresh_to(post)מעדכן את הדפדפן של הקורא בזמן אמת בלי לטעון מחדש את הדף באופן ידני.

📐 הצגת המקור של דיאגרמת הרצף של 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!
🧪 רוצים לנסות?
אפשר להפעיל אותו בשתי דרכים:
- אפשרות א' (ממשק משתמש באינטרנט): לוחצים על New post (פוסט חדש), מזינים כותרת כמו "Serverless Architecture with Ruby on Rails 8" (ארכיטקטורה ללא שרת עם Ruby on Rails 8), כותבים תוכן ומשאירים את הקלט של קובץ תמונת השער ריק. לוחצים על יצירת פוסט.
- Option B (CLI Runner): Run from your terminal:
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): add screenshot of a blog post with an AI-generated vintage 1960s Italian movie poster featuring a cameo banana and ruby 8
2. המסע להפיכת פודקאסט לדו-לשוני (תרגיל מעשי בתכנות ללא כוח משיכה)
⚠️ הודעה חשובה: בניגוד לכלי ליצירת כריכות שמופיע למעלה, התכונה הזו עדיין לא הוטמעה!
אם פותחים את blog/app/jobs/podcastifier_job.rb, מוצאים בו stub ריק של scaffold:
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 כדי להשלים את התכונה הזו:
- הנחיה ל-Antigravity:"עזור לי להטמיע את PodcastifierJob ב-
app/jobs/podcastifier_job.rb. הוא צריך לאחזר את הפוסט, לתרגם או ליצור סיכום באודיו באיטלקית באמצעות Google Cloud Text-to-Speech (קול 'it-IT-Wavenet-A' עם קוד שפה 'it-IT' באמצעות אישורי ADC), ולצרף את קובץ האודיו ל-post.podcast_audio_itבאמצעות ActiveStorage!" - Add Player to View: In
app/views/posts/show.html.erb, add an HTML5player when@post.podcast_audio_it.attached?. - הפעלת הסינתזה: כששומרים את התור, Solid Queue מפעיל את המשימה של המרת טקסט לדיבור באופן אסינכרוני ברקע.

🎧 דוגמאות לתוצאות של Podcastifier:
💡 לא הצלחת או שנתקעת? פתרון עזר מלא שנבדק היטב זמין בענף solutions/podcastifier. אפשר לבדוק את זה בכל שלב באמצעות git diff solutions/podcastifier.
3. [אופציונלי] 🏴☠️ חפש את המטמון ב-GCS (שחזור של Blob במסוף)
ℹ️ Prerequisite Check: Local
cloud-sql-proxy
נדרש CLI בתרגיל האופציונלי הזה מחברים את המחשב הנייד המקומי ישירות ל-Google Cloud SQL באמצעות מנהרת mTLS מוצפנת. בודקים אם שרת ה-Proxy של ה-CLI מותקן:
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 כ-blob יתום.
נשתמש במסוף 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
רענון הבלוג: התמונה שהועלתה במהלך Stateless Shock בשלב 4 חוזרת לחיים ומצורפת באופן קבוע לפוסט ב-Cloud SQL.
4. אימות אוטומטי של שלב 7
אימות של משימות ונכסים של AI גנרטיבי:
just workshop-eval 7
10. שלב 8: בחירת הרפתקה משלכם / משימות מתקדמות 🏆
אחרי שסיימתם את כל המשימות בארכיטקטורת ההפניה הקנונית, הגיע הזמן לבחור את משימת הסיום.
🛡️ Quest 1: Zero-Trust Google IAP (שרת proxy לאימות זהויות)
- רמת הקושי: בינונית (אבטחה ברמת הארגון)
- המטרה: לנעול את האפליקציה ב-Cloud Run כך שרק חשבונות Google או Gmail מאומתים יוכלו לגשת אליה, ולבטל לחלוטין את הכניסה באמצעות סיסמה.
- איך זה עובד:
- מאזן עומסים חיצוני של אפליקציות HTTPS (ALB) מפסיק את השימוש ב-Google OAuth ומאמת את הזהות לפני שתעבורת הנתונים מגיעה ל-Cloud Run.
- Google מעבירה כותרות של זהויות מאומתות (
X-Goog-Authenticated-User-Email). - מערכת 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 - מפעילים את מודול Terraform ב-
iac/iap.tfבאמצעותenable_iap = trueומציינים את חשבונות Google המורשים:iap_allowed_users = [var.google_cloud_account]

📊 קווסט 2: טלמטריית SRE של סביבת ייצור ו-Cloud Logging
- רמת הקושי: בינונית (ניראות)
- המטרה: להזרים יומני אפליקציות בפורמט JSON מובנה עם מזהי קורלציה של מעקב ישירות אל Google Cloud Logging ולזהות חריגים בייצור בזמן אמת באמצעות Cloud Error Reporting.
- איך זה עובד:
- מגדירים את
blog/config/environments/production.rbכך שיפלט יומנים מובנים בפורמט JSON עם תוויות מעקב של GCP. - מפעילים חריגה מכוונת של בדיקה וצופים ב-Google Cloud Error Reporting מקבץ את השגיאה ושולח לכם התראה באופן מיידי.
- מגדירים את
📸 TODO(riccardo): add screenshot of Google Cloud Error Reporting showing grouped production exceptions and Cloud Logging structured JSON payload with trace labels
🧠 קווסט 3: pgvector חיפוש סמנטי ו-Gemini RAG
- רמת הקושי: קשה (פרויקט גמר בנושא AI גנרטיבי)
- המטרה: חיפוש מאמרים באופן מושגי באמצעות הטמעות וקטורים שמאוחסנות ב-PostgreSQL ב-Cloud SQL.
- איך זה עובד:
- מפעילים את התוסף
vectorבמופע Cloud SQL PostgreSQL:CREATE EXTENSION IF NOT EXISTS vector; - מוסיפים את תצורת ה-Gem
neighborאלblog/Gemfileויוצרים הטמעות טקסט באמצעות Gemini (text-embedding-004) ב-Post#after_save. - מבצעים שאילתות של מרחק קוסינוס (
<=>) כדי להפעיל סרגל חיפוש סמנטי באמצעות Turbo Streams!
- מפעילים את התוסף
📸 TODO(riccardo): add screenshot of the live blog semantic search bar returning conceptual matches with cosine similarity scores
⚡ משימה 4: מסייע לביקורת SEO וביצועים
- רמת הקושי: קל / מהנה (חוויית מפתח)
- המטרה: בדיקת המדדים הבסיסיים של חוויית המשתמש, המהירות שבה נטען רכיב התוכן הכי גדול (LCP) ומדדי הקריאות של Flesch-Kincaid ו-Fog באמצעות הכלים Antigravity ו-Speedgrapher.
📸 TODO(riccardo): add screenshot of Speedgrapher / Lighthouse SEO and Core Web Vitals audit summary
🤔 Curiosity Box: Getting a Live Rails Console on Cloud Run
💡 השאלה: במכונות וירטואליות רגילות או בפלטפורמות PaaS בסגנון Heroku, מפתחים בדרך כלל מתחברים באמצעות SSH למופע פעיל או משתמשים ב-heroku run rails console כדי לבדוק או לתקן נתוני ייצור בזמן אמת. מה המקבילה המקורית של Cloud Run, ואיך אפשר להקשיח אותה?
למה
gcloud run exec
לא קיים בשירותי Cloud Run
Cloud Run היא פלטפורמה בלי שרת (serverless): הקונטיינר שלכם יכול לפעול כאפס, כאחד או כמאות מכונות זהות, ללא זהות יציבה או מעטפת שניתן להקצות לה כתובת. זה שונה באופן מהותי מ-Kubernetes (kubectl exec), שבו ל-pod ספציפי יש שקע בעל שם שאפשר להתחבר אליו.
# ❌ 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
ארבעת הדפוסים המקוריים של GCP
דוגמת קוד | פקודה | אינטראקטיבי? | אבטחה |
1. שרת proxy מקומי + מסד נתונים מרוחק |
| ✅ מלא | 🔒 Cloud SQL Auth Proxy mTLS |
2. משימה ב-Cloud Run (חד-פעמית) |
| ❌ רק סקריפט | 🔒 נדרש תפקיד IAM |
3. Web Console gem + IAP | דפדפן → | ✅ מלא | 🛡️ IAP Zero-Trust |
4. Cloud Shell + Proxy | Cloud Shell → | ✅ מלא | 🔒 זהות ב-Google |
תבנית 1 (מומלצת לבדיקת נתונים): מריצים מסוף Rails מקומי שמתקשר ישירות עם Cloud SQL PostgreSQL באמצעות שרת proxy ל-Cloud SQL Auth:
# 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 או דרך loopback.
תבנית 3 (מסוף אינטרנט + IAP Zero-Trust, הנתיב של הדפדפן המוקשח):
web-console Gem מספק סשן IRB מבוסס-דפדפן. בלי הגנה, מדובר בפגיעות אבטחה קריטית – אסור לחשוף אותה לציבור. אבל בשילוב עם Google IAP (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, צריך קודם להפעיל את קווסט 1 (IAP). בלי זה, /rails/console בכתובת URL ציבורית שווה ערך להשארת חדר השרתים שלכם לא נעול. עם IAP, הוא הופך לכלי הפעלה רב עוצמה שמוגן על ידי Zero-Trust ברמה של Google.
שואלים את Antigravity: "תראה לי איך מריצים
rails console --sandbox
בצורה בטוחה מול מסד הנתונים של Cloud SQL בסביבת הייצור באמצעות שרת proxy ל-Cloud SQL Auth".
🏆 סיום הלימודים ב-The Hive: הוכחת עבודה שאומתה על ידי משגיח
אחרי שתשלימו את המשימה שבחרתם, תוכלו לקבל את גביע הסיום של שלב 8 🏆 בטבלת המובילים של The Hive:
- שליחת המשימה ב-GitHub:
- פותחים בקשה חדשה בנושא במאגר הרשמי: github.com/palladius/rails8-app-on-gcp/issues/new
- פורמט הכותרת:
🎓 [Step 8 Completed]: - בגוף ההודעה, צריך לכלול תיאור קצר של מה שהשגתם וקישור לפריסת Cloud Run בשידור חי.
- קישור האפליקציה ב-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" }
- רושמים את מספר הבעיה (למשל,
- איך מקבלים את הבדיקה של המשגיח ופותחים את הגביע המוזהב:
- בודקים את רשימת המובילים ב-The Hive: בשורה שלכם יופיע עכשיו תג בצבע ענבר:
⏳ GHI #XX review pending. - אפשר לבקש מהמשגיחים בסדנה (@palladius, @emilianodellacasa או @ricc) לבדוק את הפריסה.
- ברגע שהבוחן יוסיף תגובה
LGTMלבעיה שלכם, ההתקדמות שלכם ב-The Hive תעלה אוטומטית ל-8/8 🏆 עם תג מוזהב/סגול זוהר וגביע קבוע שמקשר ישירות לבעיה המרכזית שלכם!
- בודקים את רשימת המובילים ב-The Hive: בשורה שלכם יופיע עכשיו תג בצבע ענבר:
11. סיכום וניקוי 🎓
מעולה! 🎉
בניתם ופרסתם אפליקציית Rails 8 באיכות של מוצר, שמוכנה לשימוש בארגון ב-Google Cloud Platform:
- Zero-Branch Progression: תהליכי עבודה מודרניים עם שכבות-על של Time-Machine ב-
main. - Cloud Storage: אחסון אובייקטים פרטי וניתן להרחבה עם חתימת blob של IAM (
iam: true). - Cloud SQL: PostgreSQL מנוהל ומאובטח באמצעות מנהרות mTLS של שרת proxy ל-Cloud SQL Auth.
- Secret Manager: אין דליפות של פרטי כניסה בטקסט פשוט או של קבצים
.env. - Cloud Run Multi-Container: קובצי עזר מבודדים של Puma web, Solid Queue worker ו-proxy.
- AI גנרטיבי: יצירה אסינכרונית של פוסטר וינטג' וסינתזה של פודקאסט מ-TTS באמצעות Vertex AI.
- משימות: Zero-Trust IAP, SRE Observability ו-pgvector embeddings!
🧹 ניקוי משאבים
כדי להימנע מחיובים שוטפים אחרי סיום הסדנה, חשוב למחוק את המשאבים שלכם ב-Google Cloud. בוחרים את הגישה שהכי מתאימה לסביבה שלכם:
אפשרות 1: Terraform Teardown (מומלצת / ברירת מחדל 🟢)
משתמשים באפשרות הזו אם אתם משתמשים בפרויקט קיים או משותף ב-Google Cloud שבו נמצאים משאבים או נתונים אחרים. הוא משמיד רק את המשאבים שהוקצו במהלך הסדנה הזו (Cloud Run, Cloud SQL, Secret Manager, GCS buckets), בלי לגעת בשאר הפרויקט.
cd iac
terraform destroy -auto-approve
אפשרות 2: מחיקה מלאה של הפרויקט (לא משאירים שום זכר 🌪️)
משתמשים באפשרות הזו אם יצרתם פרויקט סדנה ייעודי (למשל, באמצעות קרדיטים לסדנה או ארגז חול) ורוצים לוודא שלא יישאר שום זכר לפרויקט – כולל יומנים, מטא-נתונים, חשבונות שירות וקישורים לחיוב.
gcloud projects delete $GOOGLE_CLOUD_PROJECT --quiet