1. Pengantar
Selamat datang di workshop Rails 8 di Google Cloud. Dalam codelab praktik ini, Anda akan membawa aplikasi Rails 8 modern dari dasar SQLite lokal sederhana ke arsitektur referensi tingkat produksi yang siap digunakan perusahaan di Google Cloud.
Dalam workshop ini, kita tidak hanya men-deploy aplikasi, tetapi juga menjalankan modernisasi cloud yang memiliki opini dan berkualitas produksi (lift-and-shift yang dilakukan dengan benar!).
Anda memulai dari penyiapan lokal "berfungsi di komputer saya" yang umum (ephemeral disk, SQLite tersemat, tugas dalam proses) dan secara progresif mengembangkannya menjadi arsitektur cloud yang siap untuk Enterprise dan andal di Google Cloud:
- 🐘 Dari SQLite Lokal → Cloud SQL (PostgreSQL): Tidak ada lagi penghapusan DB saat dimulai ulang. Kami beralih ke PostgreSQL terkelola yang terhubung dengan aman melalui Proxy Auth Cloud SQL (mTLS, tanpa IP publik).
- 🪣 Dari Penyimpanan Lokal Sementara → GCS Pribadi: Disk serverless bersifat sementara. Kami menghubungkan ActiveStorage ke Google Cloud Storage pribadi menggunakan URL bertanda tangan IAM (
iam: true) — tidak ada bucket publik, tidak ada kebocoran kunci. - 🚀 Dari In-Process Jobs → Multi-Container Cloud Run: Kami menghubungkan monolit Rails dan cloud serverless. Dengan Docker Compose standar, kita menjalankan Solid Queue sebagai container pekerja khusus bersama Puma dalam pod multi-container Cloud Run — tanpa perlu khawatir tentang microservice yang berlebihan, tanpa kekurangan thread, DHH pasti bangga!
🍌 Karena kita hidup di tahun 2026, kita akan meningkatkan seluruh stack dengan Google Antigravity. Ini bukan sekadar ide, kami memiliki perintah dan keterampilan untuk memandu Anda memanfaatkan workshop ini dengan lebih baik (dan menikmatinya).
🏛️ Arsitektur Referensi Target (Varian 1: Clean Flat Vector Enterprise)
Berikut adalah arsitektur referensi Google Cloud kanonis yang akan Anda bangun dan deploy (Variant 1: Clean Flat Vector Enterprise), yang menampilkan layanan Cloud Run multi-container produksi kami yang terhubung ke semua layanan AI dan persistensi Google Cloud terkelola:

🎞️ Evolusi Arsitektur: Dari Nol hingga Berbasis Cloud
Saksikan modernisasi stack kami secara progresif dari dasar satu mesin sementara hingga cetak biru Google Cloud yang dikelola sepenuhnya di setiap pencapaian workshop:

Yang akan Anda pelajari
- Cara melakukan pemrograman berpasangan dengan Google Antigravity untuk mempermudah pemahaman tentang Rails 8 dan Google Cloud.
- Cara menjalankan diagnostik pra-penerbangan otomatis (
just workshop-test). - Cara menyediakan infrastruktur cloud Google Cloud secara asinkron menggunakan Terraform sambil melanjutkan pengembangan lokal tanpa memblokir menggunakan provider
google. - Cara menghilangkan anti-pola keamanan: bucket GCS pribadi (
iam: true) dan tunnel mTLS Proxy Auth Cloud SQL, bukan membuka0.0.0.0/0. - Cara menyuntikkan secret langsung dari Google Cloud Secret Manager.
- Cara mengatur tugas latar belakang GenAI asinkron (generator cover NanoBanana, sintesis podcast bilingual) melalui Solid Queue.
Mari kita mulai!
2. Langkah 0: Prasyarat, Penyiapan Antigravitasi & Verifikasi Penagihan
💡 Skenario: Anda dan tim Anda sedang membangun aplikasi Rails 8 yang sangat penting. Sebelum menyentuh kode atau meluncurkan resource cloud, kita harus membuat toolchain, menghubungkan Google Antigravity, serta memverifikasi kredensial dan dasar penagihan Google Cloud.
1. Checklist Prasyarat
Sebelum memulai, pastikan Anda memiliki alat berikut di lingkungan Anda:
just(1.21+): (just --version) pelaksana tugas — setiap perintah dalam workshop ini adalah resepjust(minta AI harness Anda untuk menginstalnya, atau lihatskills/rails8app-workshop).- Git (2.30+): (
git --version) untuk kontrol versi, percabangan, dan meng-clone repositori. - Google Cloud SDK (CLI
gcloud): Sudah diinstal dan diupdate. - CLI Terraform (1.5+): Untuk penyediaan infrastruktur deklaratif.
docker&docker-compose: Diinstal dan dijalankan secara lokal.- Ruby
3.4.5: (ruby -v— disematkan olehblog/.ruby-version; minta harness AI Anda untuk menginstal3.4.5jika belum ada). Rails 8 dideklarasikan diGemfiledan diinstal secara otomatis olehbundle install. - Google Antigravity 2.0: Asisten pemrograman berpasangan AI otonom Anda (Download Google Antigravity 2.0). Anda akan login dengan Gmail pribadi Anda — tidak memerlukan token API.
2. Autentikasi Google Cloud, Konfigurasi Khusus & ADC
Untuk mencegah konflik dengan penyiapan gcloud perusahaan, pribadi, atau multi-akun yang ada, sebaiknya buat konfigurasi bernama khusus untuk workshop ini:
# 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
💡 Mengapa Konfigurasi Khusus? Penggunaan gcloud config configurations create rails8-on-gcp-workshop mengisolasi semua setelan CLI (akun, project kuota, region default) khusus untuk workshop ini. Setelah selesai, Anda dapat beralih kembali ke penyiapan normal kapan saja dengan gcloud config configurations activate default.

3. 🚨 Verifikasi Penagihan GCP
⚠️ Catatan: Deployment Google Cloud SQL dan Cloud Run memerlukan akun penagihan tertaut yang aktif atau kredit edukasi workshop yang valid. Sekarang, mari pastikan penyiapan Anda sudah benar. Untuk melakukannya, jalankan pemeriksaan verifikasi penagihan:
gcloud beta billing projects describe $GOOGLE_CLOUD_PROJECT
Pastikan billingEnabled: true ditampilkan. Anda juga dapat mencantumkan semua akun penagihan Anda:
gcloud billing accounts list
Pastikan setidaknya salah satu di antaranya menampilkan OPEN: True. Jika tidak ada yang aktif, tautkan akun penagihan atau tukarkan kupon kredit workshop Anda di Halaman Penagihan Konsol Google Cloud.

4. Membuat clone Repositori & Memasangkan dengan Antigravity
Buat clone repositori dan buka direktori. Perhatikan bahwa kita tetap sepenuhnya di main:
git clone https://github.com/palladius/rails8-app-on-gcp.git
cd rails8-app-on-gcp
Buka direktori ini di Google Antigravity. Antigravity akan otomatis memeriksa repositori, membaca AGENTS.md, dan siap membantu Anda sebagai programmer berpasangan.
5. 🪫 Jika Antigravity Kehabisan Kredit Gratis
⚠️ Kredit Cloud yang baru saja Anda tukarkan TIDAK akan mengisi ulang Antigravity. Ada dua platform penagihan terpisah: Kuota Antigravity berada di paket AI Google (Google One), sementara voucher dan kredit Cloud berada di akun Penagihan Cloud. Menautkan akun penagihan ke project GCP tidak akan memberi Anda satu perintah Antigravitasi tambahan.
Jika agen berhenti dengan pesan kuota, lakukan langkah-langkah berikut — dua langkah pertama gratis dan instan:
- Ganti kumpulan model. Kuota dilacak per kelompok model, dan Antigravity menampilkan dua kumpulan independen (model Gemini vs. model Claude/GPT). Model Claude dan GPT-OSS tersedia di paket gratis, sehingga jika satu pool Gemini habis, pool lainnya tidak akan terpengaruh. Gunakan pemilih model di bawah kotak perintah.

- Pilih model Flash, bukan Pro. Batas kapasitas berkorelasi dengan jumlah pekerjaan yang dilakukan agen per permintaan, sehingga model yang lebih ringan akan memperpanjang sisa kuota.
- Gunakan Antigravity CLI dengan kunci Gemini API Anda sendiri. Bring-your-own-key tidak didukung di Antigravity IDE — CLI adalah alternatif yang didokumentasikan. Buat kunci gratis di Google AI Studio (memerlukan project, tetapi tidak memerlukan akun penagihan), lalu download dan konfigurasi Antigravity CLI.
- Tunggu, atau upgrade. Kuota tingkat gratis diperbarui setiap minggu (pesan error menyatakan tanggal reset Anda). Google AI Pro/Ultra adalah satu-satunya cara yang terdokumentasi untuk meningkatkan baseline di dalam aplikasi desktop; paket tersebut diperbarui setiap lima jam dan dapat menggunakan poin AI yang dibeli untuk penggunaan berlebih.
- Meskipun ini adalah workshop Antigravity, sebagian besar alat coding AI juga akan berfungsi — cukup arahkan alat Anda ke direktori
AGENTS.mddanskills/di repo.
6. Validasi Langkah 0 Otomatis
Verifikasi bahwa lingkungan Langkah 0 Anda 100% mematuhi rangkaian evaluasi:
just workshop-eval 0
✨ Momen Wow: Antigravity dan mesin evaluasi workshop memverifikasi versi CLI, lingkungan Ruby, dan autentikasi ADC Anda dalam waktu kurang dari 2 detik.
3. Langkah 1: Memulai Infrastruktur Terraform & Diagnostik Pra-Peluncuran
💡 Strategi: Database terkelola seperti Google Cloud SQL PostgreSQL memerlukan waktu sekitar 10–12 menit untuk disediakan. Daripada menunggu tanpa melakukan apa pun, kita meluncurkan infrastruktur immutable melalui Terraform sekarang di latar belakang saat kita mengembangkan secara lokal.
1. Menjalankan Suite Diagnostik Pra-Penerbangan Otomatis
Sebelum meluncurkan infrastruktur cloud, jalankan rangkaian pengujian pra-peluncuran yang komprehensif:
just workshop-test
Skrip ini (bin/workshop_diagnostics.rb):
- Memverifikasi konfigurasi identitas
GOOGLE_CLOUD_ACCOUNTAnda. - Memverifikasi penagihan aktif dan penautan project.
- Memvalidasi Kredensial Default Aplikasi (ADC) untuk Vertex AI.
- Mengonfirmasi image canary seed ActiveStorage (
blog/app/assets/images/gcs_dev_image.jpg).

Jika .env tidak ada, salin dari template yang didokumentasikan:
cp .env.dist .env
# Edit .env and configure GOOGLE_CLOUD_ACCOUNT with your Google/Gmail account
💡 Tips: Jika just workshop-test melaporkan bahwa API penagihan dinonaktifkan di project Anda, aktifkan dengan cepat melalui:
gcloud services enable cloudbilling.googleapis.com
2. ⏱️ Meluncurkan Infrastruktur Terraform Secara Asinkron
Jalankan resep Terraform satu langkah, yang membuat bucket status backend jarak jauh dan memicu deployment infrastruktur:
just terraform-apply

Alternatif manual (atau jalankan `iac/bin/terraform-apply.sh` secara langsung)
# 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 ..
Yang Disediakan Terraform:
- Bucket Google Cloud Storage: Dibuat dengan akses pribadi dan penandatanganan Kredensial IAM (
iam: true). - Gambar Pengujian Canary: Mengupload
gcs_dev_image.jpgke bucket untuk mengaktifkan verifikasi blob end-to-end. - Instance PostgreSQL Google Cloud SQL: Memulai penyediaan di latar belakang (~10-12 menit).

💡 Catatan: Anda dapat mengabaikan peringatan tentang pencadangan otomatis yang tidak ada dengan aman — kita tidak akan memerlukannya untuk database workshop ini (meskipun pencadangan harian otomatis adalah praktik terbaik yang penting untuk database produksi yang sebenarnya).

📝 Catat URL Cloud Run Anda: Penerapan Terraform akan berhasil dan mencetak output infrastruktur Anda. Catat cloud_run_url Anda (atau salin ke papan klip) — Anda akan membutuhkannya di langkah-langkah selanjutnya saat memverifikasi layanan aktif Anda.
3. Validasi Langkah 1 Otomatis (Opsional)
Pastikan prasyarat dan konfigurasi Langkah 1 Anda berhasil:
just workshop-eval 1
⏱️ Catatan: Evaluasi ini memeriksa resource cloud aktif dan mungkin memerlukan waktu beberapa menit untuk selesai. Langkah ini sepenuhnya opsional — saat berjalan di terminal, Anda dapat langsung melanjutkan ke langkah berikutnya tanpa menunggu.
4. Langkah 2: Dasar Lokal, Mailpit & Pengaktifan Admin
Titik awalnya adalah aplikasi blog Rails 8 yang bersih dan modern yang berjalan di localhost dengan SQLite, pencegatan email Mailpit, dan ActiveStorage berbasis disk.
1. Mem-boot Aplikasi Secara Lokal
📁 Perhatikan direktori! Repo ini menyimpan aplikasi Rails di blog/ dan Terraform di iac/. Langkah 1 membuat Anda berada di root repo, jadi setiap perintah di Langkah 2 dijalankan dari
blog/ — Gemfile, bin/rails, dan compose.yaml semuanya tinggal di sana. Menjalankannya dari root akan gagal dengan no configuration file provided: not found atau Gemfile yang tidak ada.
Pindah ke aplikasi Rails, konfirmasi bahwa Anda menggunakan Ruby yang disematkan, lalu instal gem dan siapkan database:
cd blog
ruby -v # must print 3.4.5 — see Step 0 if it does not
bundle install
bin/rails db:setup
🧯 Error izin saat menulis ke
/var/lib/gems/
atau
ruby -v
menampilkan Ruby sistem (misalnya, 3.3.x)? Ruby sistem Linux Vanilla sepenuhnya mengabaikan file .ruby-version dan mencoba menginstal gem di seluruh sistem. Pastikan rbenv dimuat di shell terminal aktif Anda:
eval "$(rbenv init - bash)" # or: eval "$(rbenv init - zsh)"
ruby -v # confirm it now outputs 3.4.5!
Catatan: Jika Anda melewati penginstalan 3.4.5 di Langkah 0, jalankan rbenv install 3.4.5. Atau cukup gunakan Mode A (Docker Compose) di bawah (just compose-up), yang menjalankan stack Ruby yang disematkan sepenuhnya di dalam container tanpa menyentuh gem host.
💡 Lebih baik tidak memikirkannya? just install dan just compose-up dari root repo akan melakukan cd blog untuk Anda.
2. 🌱 Penemuan Otomatis & Bootstrap Admin Smart Seed (Masalah #21 & #25)
Database seed (db/seeds.rb) memiliki Penemuan Otomatis Lingkungan Smart:
- Pemeriksaan ini memeriksa konfigurasi penyimpanan dan adaptor database aktif Anda (SQLite vs. Postgres).
- Deteksi Stage 0 (Localhost) dan buat pengguna admin awal secara otomatis serta isi postingan pedagogis:
[LOCAL BASELINE] Welcome to Rails 8 on Localhost!- Lampiran gambar sedih lokal langsung pakai (
local_sad_image.png) dengan watermark yang memberi tahu Anda bahwa penyimpanan disk lokal bersifat sementara.
- Fitur ini otomatis memicu email reset sandi melalui ActionMailer.
Verifikasi atau jalankan ulang data awal (secara otomatis menggunakan GOOGLE_CLOUD_ACCOUNT Anda dari .env):
bin/rails db:seed # or from repo root: just seed
Boot development stack lokal melalui Docker Compose:
docker compose up -d # from blog/ — or `just compose-up` from the repo root

Aplikasi, server SMTP Mailpit lokal, dan penampil database Adminer kini berjalan bersama dalam penampung terisolasi:
- Aplikasi Rails: http://localhost:3000
- UI Web Mailpit: http://localhost:8025
- UI DB Adminer: http://localhost:8081
🧯 Konflik port 3000 atau
server.pid
error? Jika sebelumnya Anda memulai server dev native (bin/dev), hentikan dengan Ctrl+C. Jika Docker mengeluh bahwa port 3000 sudah dialokasikan atau menemukan server.pid yang tersisa, bersihkan dengan:
just compose-down
rm -f blog/tmp/pids/server.pid
just compose-up
3. Pengalaman Mailpit & Login Admin
- Menangkap Email Keluar dengan Mailpit: Buka
http://localhost:8025di browser Anda. Lihat kotak masuk lokal. Selamadb:seed, ActionMailer mengirimkan email aktivasi admin yang berhasil ditangkap Mailpit secara lokal tanpa menyentuh server atau kredensial email eksternal:
2. Login ke Blog: Buka http://localhost:3000 di browser Anda.
- Anda dapat mengklik link reset sandi langsung di dalam email Mailpit untuk menyetel sandi Anda.
- Atau, login menggunakan email akun Google Cloud Anda (dari
.env) dan sandi default yang diisi:Ch4ng3m3!!1.
- Mengamati Badge Telemetri Visual:
- Perhatikan banner dan badge lingkungan berwarna kuning di UI:
Notice: Ephemeral Database Active (POSTGRESQL)dan[EPHEMERAL DB / STORAGE] 💾 Local. - Perhatikan watermark postingan: Stempel 🏠 (
nanobanana_stamp_local.pngdi sudut kanan bawah gambar sampul). Hal ini memberikan konfirmasi visual langsung bahwa aset dan database Anda saat ini terikat ke penyimpanan lokal sementara.
- Perhatikan banner dan badge lingkungan berwarna kuning di UI:

💡 Tips Tambahan Opsional: Latihan Konsol Rails Ingin tahu bagaimana Rails berinteraksi dengan data yang di-seed dari CLI? Buka konsol interaktif:
bin/rails console
Periksa pengguna admin yang di-seed secara dinamis menggunakan variabel lingkungan Anda:
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. Validasi Otomatis Langkah 2
Verifikasi dasar lokal dan penyiapan admin Anda:
just workshop-eval 2

5. Kendala: Container Stateless
Container Cloud Run bersifat stateless dan sementara. Jika kita men-deploy database SQLite dan direktori storage/ lokal secara langsung ke Cloud Run, semua postingan dan gambar yang diupload akan dihapus secara permanen setiap kali container diskalakan ke nol atau dimulai ulang.
Pada langkah berikutnya, kita akan sengaja men-deploy konfigurasi sementara ini ke Cloud Run untuk menyaksikan Stateless Shock secara langsung.
5. Langkah 3: Deploy 1 — The Stateless Shock (WOW Awal dalam 3 Menit!)
Alih-alih menunggu 15–20 menit untuk database cloud sebelum melihat apa pun di web, pengembangan serverless modern dimulai dengan kemenangan awal: men-deploy aplikasi Rails satu container kita langsung ke Google Cloud Run dalam waktu kurang dari 3 menit.
1. Mundur Cepat Mesin Waktu
Untuk menjamin bahwa konfigurasi awal kita 100% efemeral (database SQLite lokal dan penyimpanan file disk lokal), gunakan mesin Time-Machine Zero-Branch:
just workshop-rewind 1
💡 Apa yang baru saja terjadi?workshop-rewind 1 menerapkan overlay konfigurasi stage-1-stateless ke blog/config/ tanpa keluar dari cabang main. Aplikasi Anda dikonfigurasi dengan SQLite di disk container dan ActiveStorage di sistem file lokal.
2. Men-deploy Puma Single-Container ke Cloud Run
Men-deploy langsung dari kode sumber ke Cloud Run. Rails 8 modern secara otomatis menghasilkan Dockerfile resmi yang siap produksi secara langsung (dengan build multi-level, jemalloc, dan keamanan non-root). Saat Anda men-deploy dari folder blog/, Google Cloud Build akan mendeteksi Dockerfile native ini secara langsung dan menyediakan layanan serverless terkelola:
# 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
Anda akan melihat output progres yang mirip dengan ini:

⏳ Proses ini akan memakan waktu beberapa menit saat Cloud Build mengemas container dan menyediakan revisi Cloud Run.
🧯 Pemecahan Masalah & Peringatan Umum
PERMISSION_DENIED: Build failed because the default service account is missing required IAM permissions
? Dua penyebab berbeda menghasilkan error yang sama:
- gcloud baru saja mengaktifkan API untuk Anda. Jika deployment mencetak
The following APIs are not enabled ... cloudbuild.googleapis.comdan Anda menjawabY, build mungkin telah dimulai sebelum izin baru selesai diterapkan. Perintah gcloud sendiri memperingatkan "this will take a few minutes". Tunggu sebentar, lalu jalankan kembali perintah yang sama persis; biasanya akan berhasil.terraform applyLangkah 1 biasanya mengaktifkan API ini untuk Anda, sehingga Anda tidak akan melihat perintah sama sekali. - Akun Layanan Komputasi Default benar-benar tidak memiliki peran. Project GCP modern menerapkan hak istimewa terendah, yang merusak deployment sumber. Perbaiki dengan:
just project-status # grants storage.admin, logging.logWriter, artifactregistry.writer, cloudbuild.builds.builderjust workshop-testjuga melaporkannya sekarang, jadi sebaiknya jalankan ulang jika deployment terus gagal.
Selama deployment:
- Cloud Run akan membangun image container Rails Anda.
- Tindakan ini menetapkan domain TLS publik yang aman:
https://blog-[hash]-[region].a.run.app. - Traffic mulai dirutekan ke Puma di port 8080.
3. ✨ Momen WOW Awal
Buka URL Cloud Run yang dihasilkan di browser Anda.
- Aplikasi Rails 8 modern Anda sudah aktif di Google Cloud.
- Login dengan kredensial admin Anda (
GOOGLE_CLOUD_ACCOUNTdanAPP_ADMIN_PASSWORD). Jika Anda tidak menemukan tombol login, klik "Postingan Baru" dan Anda akan diminta untuk memasukkan nama pengguna dan sandi terlebih dahulu:
- Klik "Postingan Baru", tulis artikel berjudul "Postingan Cloud Run Pertama Saya", lampirkan gambar, lalu klik Buat Postingan.
- Postingan Anda dipublikasikan dengan format lengkap, dan gambar Anda dirender.
- Perhatikan telemetri visual:
- Badge header:
[EPHEMERAL DB / STORAGE] 💾 Local - Watermark gambar: Stempel 🏠 (badge ephemeral disk
127.0.0.1di pojok kanan bawah).
- Badge header:
🐝 Bergabunglah ke Papan Peringkat Hive Workshop Live! Jika Anda sedang online dan pengawas menampilkan papan peringkat, dan Anda ingin bergabung, tambahkan URL Cloud Run Anda di sini: 👉 Daftarkan Cloud Run Anda di Papan Peringkat

4. 💥 Masalah: Kejutan Stateless & Perangkap "Solusi Puma"
Cloud Run adalah platform stateless dan serverless. Saat traffic web turun menjadi nol, Cloud Run akan menurunkan skala instance container menjadi nol untuk menghemat biaya. Saat permintaan HTTP baru tiba atau revisi container baru di-deploy, Cloud Run akan memulai image container baru yang bersih.
Banner Tugas yang Tertunda
Saat Anda membuat postingan atau melampirkan gambar dalam deployment satu container, Rails akan mengantrekan tugas ActiveJob (seperti analisis dimensi gambar atau pengindeksan metadata). Namun, karena tidak ada yang menjalankan pekerja latar belakang, Anda akan melihat banner peringatan:

Solusi yang Menggoda: Menjalankan Solid Queue di dalam Puma
Developer yang cerdas mungkin berkata: "Tunggu! Rails 8 memungkinkan kita menjalankan Solid Queue langsung di dalam proses server web Puma dengan mengaktifkan
SOLID_QUEUE_IN_PUMA=true
variabel lingkungan!"
Mari kita uji solusi ini di 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
Sekarang, kembali ke browser Anda dan muat ulang halaman:
💥 Guncangan Stateless:
- Kabar Baik: Pekerja Solid Queue kini aktif di dalam Puma. Tugas baru akan langsung dihentikan.
- Mandi Air Dingin (Kejutan!):
- Karena Cloud Run men-deploy revisi baru, instance container sebelumnya diganti.
- Artikel yang Anda tulis dan file database SQLite di disk dihapus sepenuhnya.
- Pelajaran Arsitektur: Menjalankan pekerja latar belakang di dalam Puma menggunakan CPU/RAM thread web yang berharga, dan tetap tidak menyelesaikan persistensi.
5. Validasi Otomatis Langkah 3
Verifikasi penanganan pemberitahuan dan deployment Cloud Run Anda:
just workshop-eval 3
✨ Pelajaran: Solusi sementara satu penampung seperti SOLID_QUEUE_IN_PUMA berguna untuk pengembangan lokal, tetapi dalam arsitektur cloud modern:
- File media memerlukan Google Cloud Storage (GCS) (Langkah 4).
- Data relasional dan antrean tugas memerlukan Google Cloud SQL terkelola (Langkah 5).
- Worker latar belakang berat termasuk dalam container sidecar khusus (Langkah 6).
6. Langkah 4: Deploy 3 — GCS Persistent Storage & Peringatan POLA
Pada langkah ini, kita memisahkan penyimpanan media dan file dari disk penampung dengan mengalihkan ActiveStorage ke Google Cloud Storage (GCS), menggunakan URL bertanda tangan yang berumur pendek melalui IAM Credentials API (iam: true).
Sebelum Anda mengubah konfigurasi apa pun, pahami tiga hal yang diperlukan Rails untuk berkomunikasi dengan GCS:
① Project ID + Nama bucket — Rails harus mengetahui tempat penyimpanan blob. Aplikasi membaca GOOGLE_CLOUD_PROJECT dari lingkungan dan mendapatkan nama bucket sebagai ${GOOGLE_CLOUD_PROJECT}-activestorage-prod. Terraform Anda dari Langkah 1 sudah membuat bucket ini — tidak diperlukan upaya tambahan.
② Akun Layanan dengan izin yang tepat — Cloud Run berjalan sebagai akun layanan default Compute Engine (PROJECT_NUMBER-compute@developer.gserviceaccount.com). SA ini memerlukan dua peran IAM di bucket:
roles/storage.objectAdmin— untuk mengupload dan menyajikan blobroles/iam.serviceAccountTokenCreatordi dirinya sendiri — untuk memanggil IAM CredentialssignBlobAPI dan membuat URL bertanda tangan dengan jangka waktu singkat
⚠️ Pintasan yang Menggoda —
public: true Cara tercepat untuk membuat GCS berfungsi adalah dengan menyetel public: true di storage.yml dan memberikan allUsers:objectViewer di bucket. Gambar dimuat secara instan, tidak perlu login. Jangan lakukan ini. Bucket publik membocorkan semua media pengguna yang diupload ke internet terbuka, selamanya. Blueprint kami menggunakan iam: true untuk menjaga bucket tetap 100% pribadi — setiap URL gambar adalah token bertanda tangan yang akan segera berakhir dan dibuat secara langsung oleh IAM Credentials API.
③
ACTIVE_STORAGE_SERVICE=google — satu-satunya variabel lingkungan yang mengalihkan Rails dari disk lokal ke GCS saat runtime. Tidak ada perubahan kode, tidak ada deployment ulang logika aplikasi — hanya satu tombol variabel lingkungan.
1. Mundur Cepat Mesin Waktu
Maju ke tahap 2 mesin waktu:
just workshop-rewind 2
Periksa blog/config/storage.yml — Anda akan melihat aplikasi menentukan tiga lingkungan GCS bernama (dev / test / prod), yang semuanya menggunakan pola aman yang sama:
<% 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. Memberikan Izin Penandatanganan & Penyimpanan IAM
Pastikan akun layanan runtime Cloud Run Anda memiliki izin untuk menandatangani URL dan mengupload objek:
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. Deployment Ketiga ke Cloud Run dengan GCS Terlampir
Deploy ulang dengan GCS diaktifkan. Pastikan Anda berada di dalam folder blog/ (jika tidak, Cloud Run akan mencoba membangun menggunakan Buildpack, bukan Dockerfile Rails 8, sehingga menyebabkan kegagalan build).
Gunakan --update-env-vars (bukan --set-env-vars) untuk mempertahankan variabel lingkungan yang ada (kunci utama, konfigurasi IAP, dll.):
cd blog
gcloud run deploy blog \
--source . \
--region $GOOGLE_CLOUD_REGION \
--update-env-vars ACTIVE_STORAGE_SERVICE=google
💡 Tips: Jika deployment gagal, klik link log Cloud Build yang disediakan dengan mudah di output terminal Anda (seperti yang ditunjukkan pada gambar di bawah). Anda juga dapat meminta bantuan Antigravity untuk menguraikan dan memperbaiki error.

4. ✨ Gambar yang Bertahan & Stempel Cloud
- Muat ulang URL blog Anda.
- Buat postingan blog baru berjudul "Surviving the Cloud" dan upload foto.
- Lihat pojok kanan bawah gambar:
- Stempel 🏠 hilang!
- Digantikan oleh Cloud GCS Stamp (
nanobanana_stamp_cloud.png) berwarna-warni.
- Memaksa memulai ulang container lain (yang akan memicu revisi keempat):
gcloud run services update blog --region $GOOGLE_CLOUD_REGION --update-env-vars RESTART_TRIGGER=$(date +%s) - Muat ulang halaman: saat database SQLite direset, biner gambar aman dan terlindungi di Google Cloud Storage. Verifikasi melalui CLI:
gcloud storage ls -l -r gs://$GCS_BUCKET/
Berikut adalah daftar blob dan objek media yang diupload yang mungkin Anda lihat di Konsol Browser Google Cloud Storage di bucket produksi (gs://$GCS_BUCKET):

5. ⚠️ Kendala: Banner Peringatan Tugas yang Tertunda
Saat Anda mengupload gambar, ActiveStorage mengantrekan tugas analisis (ActiveStorage::AnalyzeJob) untuk mengekstrak dimensi dan metadata.
Namun, Cloud Run saat ini hanya menjalankan satu penampung web (puma). Di Rails 8, Solid Queue menyimpan tugas latar belakang di database, tetapi tidak ada yang menjalankan bundle exec rails solid_queue:start.
Lihat bagian atas halaman blog Anda: Anda akan melihat banner peringatan terang yang dirender oleh blog/app/views/layouts/_check_stuck_jobs.html.erb.
Kita akan menyelesaikannya dengan benar di Langkah 6, saat kita beralih ke arsitektur sidecar multi-container tingkat produksi dengan container worker khusus yang menjalankan Solid Queue secara independen dari traffic web.
6. Validasi Langkah 4 Otomatis
Verifikasi konfigurasi GCS dan telemetri tugas yang macet:
just workshop-eval 4
7. Langkah 5: Checklist Pra-Peluncuran: Cloud SQL, Secret & Konektivitas DB
Meskipun Anda telah menguasai container stateless dan Google Cloud Storage di Langkah 3 dan 4, Terraform telah menyelesaikan penyiapan backend produksi Anda di latar belakang.
Sebelum men-deploy arsitektur sidecar produksi 3-kontainer lengkap (Deploy 4 di Langkah 6), langkah ini berfungsi sebagai Pemeriksaan Pra-Penerbangan cepat untuk memverifikasi kesehatan database, konektivitas, dan akses rahasia.
0. Variabel Lingkungan Bootstrap
Ekspor semua variabel shell yang diperlukan langsung dari output Terraform Anda:
# 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. Pemeriksaan Kondisi Cloud SQL
Pastikan instance PostgreSQL terkelola Anda telah menyelesaikan penyediaan awal dan responsif:
echo "Checking instance: $SQL_INSTANCE_NAME"
gcloud sql instances describe $SQL_INSTANCE_NAME --format="value(state)"
Output harus berupa RUNNABLE.
2. (Opsional) Menguji Konektivitas Database dari Laptop Anda
Dalam produksi, penampung Cloud Run terhubung dengan aman ke Cloud SQL melalui proxy sidecar terenkripsi (127.0.0.1:5432) tanpa mengekspos database ke internet publik (0.0.0.0/0).
ℹ️ Catatan: Langkah ini sepenuhnya opsional. Di Cloud Run, container sidecar Proxy Auth Cloud SQL resmi menangani konektivitas mTLS secara otomatis. Anda tidak perlu menginstal apa pun secara lokal untuk menyelesaikan workshop ini.
Jika Anda telah menginstal cloud-sql-proxy dan psql di komputer dan ingin memverifikasi konektivitas langsung dari laptop:
# Optional: test connectivity via Google-managed mTLS proxy
gcloud sql connect $SQL_INSTANCE_NAME --user=rails_user --database=rails_production
(Jika diminta memasukkan sandi, masukkan
$DB_PASSWORD
. Jika tidak, Anda dapat langsung melanjutkan ke Langkah 3 di bawah.)
3. Memeriksa Secret yang Disediakan Terraform
Jangan pernah menyimpan sandi database, kunci API, atau kunci utama Rails dalam teks biasa di git atau di variabel lingkungan penampung. Dalam arsitektur cloud modern, kita menggunakan Google Cloud Secret Manager untuk injeksi runtime zero-trust.
ℹ️ Terraform Menangani Hal Ini untuk Anda di Langkah 1:
rails-master-key— untuk mendekripsi kredensial Rails saat runtime.rails-db-password— sandi acak yang aman secara kriptografis dan cocok dengan pengguna Cloud SQL.rails-admin-password— sandi yang aman untuk pengguna administrator awal Anda.
Pastikan secret Anda ada dan aktif di Secret Manager:
gcloud secrets list --filter="name:rails-"

🔑 Menyinkronkan master.key ke Laptop Anda (Sinkronisasi Lokal)
Karena blog/config/master.key diabaikan oleh Git untuk keamanan, clone lokal Anda mungkin belum memilikinya. Jalankan cuplikan perbaikan otomatis yang mudah digunakan ini untuk menariknya dari Secret Manager sehingga alat Rails lokal Anda dapat mendekripsi kredensial:
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: Ingin membuat secret kustom hanya untuk melihat betapa mudahnya? Secret Manager bukan hanya untuk Rails. Anda dapat membuat secret apa pun dalam satu perintah CLI sederhana:
echo -n "MySuperSecretValue123!" | gcloud secrets create workshop-fun-secret --data-file=-
echo "✨ Created workshop-fun-secret in Secret Manager! 🪄"
4. Memverifikasi Izin Pengakses Secret
Agar container Cloud Run dapat memasang secret ini sebagai variabel lingkungan atau pemasangan volume saat booting, akun layanan runtime ($RUN_SA) memerlukan peran roles/secretmanager.secretAccessor di setiap secret.
Terraform sudah memberikan izin ini selama Langkah 1. Verifikasi binding kebijakan IAM di 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"
Anda akan melihat:
ROLE MEMBERS
roles/secretmanager.secretAccessor serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com

5. Validasi Pra-Peluncuran Otomatis
Jalankan rangkaian pengujian Langkah 5 otomatis untuk mengonfirmasi bahwa database Anda dapat dijalankan, secret tidak kosong, dan izin IAM berwarna hijau:
just workshop-eval 5
8. Langkah 6: Deployment 4 — Sidecar Multi-Container Enterprise (Standar Emas)
Sekarang kita menyusun arsitektur referensi terbaik: Multi-Container Cloud Run.
1. Memulihkan Standar Emas
Pulihkan konfigurasi repositori Anda ke status main kanonis:
just workshop-restore-gold
2. Memeriksa Cetak Biru Produksi 3-Container
Buka blog/compose.prod.yaml dan periksa arsitektur:

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)]
Deployment produksi menjalankan tiga container yang terkoordinasi dan berbagi jaringan lokal yang sama:
web: Menayangkan traffic HTTP di port 8080 melalui Puma.worker: Container pemrosesan latar belakang khusus yang menjalankanbundle exec rails solid_queue:start.cloudsql-proxy: Sidecar Proxy Auth Google Cloud SQL resmi (gcr.io/cloud-sql-connectors/cloud-sql-proxy:2) terikat ke127.0.0.1:5432.
3. Menjalankan Migrasi Database melalui Tugas Cloud Run
Sebelum merutekan traffic web ke deployment baru, kita harus menjalankan migrasi database dan pengisian awal terhadap Cloud SQL.
⚖️ Cara Menjalankan Migrasi di Container: Dua Strategi
Di lingkungan cloud yang di-containerisasi, ada dua pendekatan umum untuk menjalankan migrasi database:
- Opsi A: Otomatis di dalam Titik Entri Docker (
entrypoint.shmenjalankandb:migratesaat container di-boot)- Kelebihan: Sepenuhnya otomatis; developer tidak perlu mengingat untuk menjalankan perintah.
- Kekurangan: Berbahaya dalam penskalaan otomatis serverless! Saat Cloud Run meningkatkan skala hingga 10 instance secara bersamaan di bawah traffic tinggi, 10 container berlomba untuk menjalankan migrasi secara bersamaan, sehingga menyebabkan penguncian database, kondisi persaingan skema, atau waktu tunggu booting.
- Opsi B: Secara eksplisit melalui Tugas Cloud Run (Pilihan Arsitektur Kami)
- Kelebihan: Kontrol penuh, keamanan transaksional, dan kondisi tanpa persaingan. Berjalan sebagai tugas container sekali jalan khusus yang sepenuhnya terlepas dari penayangan traffic web. Jika migrasi gagal, penayangan web tidak akan terpengaruh.
- Kontra: Memerlukan pemicuan tugas CLI atau langkah CI/CD sebelum deployment.
Kami memilih Opsi B untuk keandalan produksi maksimum.
⚠️ Penting: DATABASE_URL Format & Kepatuhan RFC 3986 Ruby 3.4.5
Job Cloud Run terhubung ke Cloud SQL menggunakan proxy soket Unix native (/cloudsql/PROJECT:REGION:INSTANCE). DATABASE_URL Harus menggunakan format tiga garis miring:
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
💡 Multi-Database Rails 8: Aplikasi menggunakan 4 database (primary, queue, cache, cable) per database.yml. Semua variabel lingkungan DATABASE_*_URL harus mengarah ke instance Cloud SQL yang sama, jika tidak, tabel Solid Queue/Cache/Cable tidak akan dibuat.
Setelah db:prepare, muat skema 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
Pada gambar di atas, Anda dapat melihat kemungkinan output
gcloud run jobs executions describe $JOBNAME
Konsol Tugas Google Cloud Run yang menampilkan
rails-migrate
eksekusi Berhasil
4. Deployment Keempat: Men-deploy Cloud Run Multi-Container melalui Docker Compose
Sekarang deploy stack produksi 3-kontainer asli (web, worker, cloudsql-proxy) langsung dari 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
Konsol Google Cloud Run: Tab 'Containers' yang menampilkan ketiga container sidecar (
cloudsql-proxy
,
web
,
worker
)
💡 Keingintahuan UI / Masalah Konsol: Di dropdown Konsol, Anda mungkin melihat Cloud Run memberi label entri teratas sebagai Ingress container: cloudsql-proxy. Jangan khawatir! Ini murni gangguan UI visual di Konsol Google Cloud (dilacak di #149) yang disebabkan oleh pengurutan dependensi penampung. Pada kenyataannya, web adalah satu-satunya container yang memproses port HTTP 8080 dan menangani 100% traffic web ingress, sementara cloudsql-proxy secara ketat melayani traffic PostgreSQL pribadi di port 5432.
5. ✨ Momen Wow: Tanpa Peringatan & Persistensi Cloud Penuh!
Tarik napas dalam-dalam dan kagumi layar Anda: semua banner peringatan telah hilang! 🪄
Blog Rails 8 produksi: UI yang bersih, tanpa banner peringatan, dan status Cloud SQL yang persisten!
Aplikasi kini secara resmi Siap Produksi & Berbasis Cloud:
- Didukung oleh Google Cloud SQL (PostgreSQL) terkelola melalui tunneling mTLS yang aman.
- Media disimpan dengan aman di Google Cloud Storage pribadi dengan penandatanganan blob IAM.
- Tidak ada peringatan penyimpanan sementara dan tidak ada peringatan tugas yang macet.
Buka URL Cloud Run Anda:
- Lihat badge telemetri:
[CLOUD PERSISTENT 🐘 ☁️]diverifikasi dalam data awal database Anda.- Badge Google Cloud Run dan GCS di footer berwarna hijau.
- Banner peringatan tugas yang macet telah hilang sepenuhnya.
- Membuat postingan dan komentar blog.
- Mulai ulang atau deploy ulang sebanyak yang Anda inginkan: data, postingan, komentar, dan aset Anda akan tetap ada selamanya di Cloud SQL dan GCS.
6. Validasi Langkah 6 Otomatis
Verifikasi konfigurasi multi-container:
just workshop-eval 6
9. Langkah 7: Pipeline AI Generatif, Podcastifier & The GCS Treasure Hunt 🏴☠️
Dengan Solid Queue yang berjalan di container khusus dan Google Cloud Storage yang aktif, kita dapat memanfaatkan AI Generatif asinkron.
1. Generator Cover Vintage NanoBanana (Pipeline GenAI Siap Pakai)

Kabar baiknya: fungsi ini sudah diimplementasikan sepenuhnya dalam codebase. Anda tidak perlu menulis kode apa pun — tugas Anda di sini adalah memicu dan mengamati pipeline GenAI cloud asinkron yang sedang berjalan.
🧠 Cara Kerjanya di Balik Layar
- Autentikasi: Tidak memerlukan kunci API! Layanan ini menggunakan Kredensial Default Aplikasi (
roles/aiplatform.userdi Vertex AI) secara otomatis melalui akun layanan runtime Cloud Run. (Atau, jikaGEMINI_API_KEYdisediakan, fungsi ini akan memanggil Gemini API secara langsung). - Pipeline: Saat artikel dibuat tanpa gambar sampul, callback
after_commitmengantrekanGenerateCoverImageJobke Solid Queue. - Model: Model ini meminta model multimodal Google (Gemini 2.5 Flash Image / Imagen di Vertex AI) untuk membuat poster film Italia tahun 1960-an yang autentik ("Locandina di un film 1960") lengkap dengan cameo pisang dan "8" rubi yang berkilau.
- Watermark Perujukan Dinamis: Menggunakan
libvipsuntuk memberi stempel badge visual secara dinamis di sudut (stempel awan berwarna-warni di GCS). - Penggantian Cepat Turbo Stream: Saat gambar dilampirkan,
Turbo::StreamsChannel.broadcast_refresh_to(post)memperbarui browser pembaca secara langsung tanpa memuat ulang halaman secara manual.

📐 Melihat Sumber Diagram Urutan 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!
🧪 Coba Langsung!
Anda dapat memicunya dengan dua cara:
- Opsi A (UI Web): Klik Postingan baru, masukkan judul seperti "Arsitektur Serverless dengan Ruby on Rails 8", tulis beberapa konten, dan biarkan input file gambar sampul kosong. Klik Buat Postingan.
- Opsi B (CLI Runner): Jalankan dari terminal Anda:
bin/rails runner 'Post.create!(title: "Serverless Architecture with Ruby on Rails 8", body: "Exploring asynchronous AI pipelines with Solid Queue and NanoBanana!")'
Tunggu ~15–30 detik: lihat log pekerja Solid Queue di Cloud Run. Poster vintage akan muncul langsung di postingan Anda.
📸 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. Misi Bilingual Podcastifier (Latihan Coding Antigravitasi Interaktif)
⚠️ Pemberitahuan Penting: Tidak seperti Pembuat Sampul di atas, fitur ini BELUM diterapkan!
Jika Anda membuka blog/app/jobs/podcastifier_job.rb, Anda akan menemukan stub scaffold kosong:
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
Misi Anda dalam latihan langsung ini adalah melakukan pemrograman berpasangan dengan Google Antigravity untuk menyelesaikan fitur ini:
- Perintah Antigravitasi:"Bantu saya menerapkan PodcastifierJob di
app/jobs/podcastifier_job.rb. Aplikasi ini akan mengambil Postingan, menerjemahkan atau menyintesis ringkasan audio berbahasa Italia menggunakan Google Cloud Text-to-Speech (suara 'it-IT-Wavenet-A' dengan kode bahasa 'it-IT' melalui kredensial ADC), dan melampirkan file audio kepost.podcast_audio_itmenggunakan ActiveStorage!" - Menambahkan Pemutar ke Tampilan: Di
app/views/posts/show.html.erb, tambahkan pemutar HTML5saat@post.podcast_audio_it.attached?. - Memicu Sintesis: Saat disimpan, Solid Queue akan menjalankan tugas text-to-speech secara asinkron di latar belakang.

🎧 Contoh Output Podcastifier:
💡 Perlu Bantuan atau Mengalami Kebuntuan? Solusi referensi lengkap yang telah teruji tersedia di cabang solutions/podcastifier. Anda dapat memeriksanya kapan saja dengan git diff solutions/podcastifier.
3. [OPSIONAL] 🏴☠️ Perburuan Harta Karun GCS (Pemulihan Blob Konsol)
ℹ️ Pemeriksaan Prasyarat: Lokal
cloud-sql-proxy
CLI Diperlukan Latihan opsional ini menghubungkan laptop lokal Anda langsung ke Google Cloud SQL melalui tunnel mTLS terenkripsi. Periksa apakah Anda telah menginstal proxy CLI:
which cloud-sql-proxy
- Jika sudah diinstal: Lanjutkan di bawah.
- Jika TIDAK diinstal dan Anda ingin menyelesaikan misi ini: Instal dengan:
# 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 - Jika tidak: Anda dapat langsung melewati ke validasi otomatis Langkah 7.
Ingat foto yang Anda upload kembali di Langkah 4 sebelum memulai ulang container menghapus database SQLite sementara? File gambar tersebut masih tersimpan dengan aman di bucket GCS pribadi Anda sebagai "blob yang tidak terkait".
Mari kita gunakan konsol Rails untuk menyelamatkannya dan melampirkannya ke postingan 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
Di dalam konsol 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): tambahkan screenshot konsol Rails interaktif yang menjalankan cuplikan GCS Treasure Hunt dan menyelamatkan blob ActiveStorage yang tidak memiliki induk
Muat ulang blog Anda: foto yang diupload selama Stateless Shock Langkah 4 dihidupkan kembali dan terlampir secara permanen ke postingan Cloud SQL Anda.
4. Validasi Langkah 7 Otomatis
Memverifikasi tugas dan aset GenAI:
just workshop-eval 7
10. Langkah 8: Pilih Petualangan Anda Sendiri / Misi Lanjutan 🏆
Setelah Anda menguasai arsitektur referensi kanonis, pilih misi kelulusan Anda.
🛡️ Quest 1: Zero-Trust Google IAP (Identity-Aware Proxy)
- Kesulitan: Sedang (Enterprise Security)
- Tujuan: Mengamankan aplikasi Cloud Run Anda sehingga hanya akun Google / Gmail terverifikasi Anda yang dapat mengaksesnya, sehingga menghilangkan login sandi sepenuhnya.
- Cara Kerjanya:
- Load Balancer Aplikasi HTTPS Eksternal menghentikan Google OAuth dan memverifikasi identitas sebelum traffic mencapai Cloud Run.
- Google meneruskan header identitas terverifikasi (
X-Goog-Authenticated-User-Email). - Rails otomatis login ke identitas Google terverifikasi melalui
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 - Aktifkan modul Terraform di
iac/iap.tfdenganenable_iap = truedan tentukan Akun Google yang diizinkan:iap_allowed_users = [var.google_cloud_account]

📊 Quest 2: Telemetri SRE Produksi & Cloud Logging
- Kesulitan: Sedang (Kemampuan Observasi)
- Tujuan: Streaming log aplikasi JSON terstruktur dengan ID korelasi pelacakan langsung ke Google Cloud Logging dan tangkap pengecualian produksi secara real-time dengan Cloud Error Reporting.
- Cara Kerjanya:
- Konfigurasi
blog/config/environments/production.rbuntuk memancarkan log JSON terstruktur dengan label rekaman aktivitas GCP. - Aktifkan pengecualian pengujian yang disengaja dan lihat Google Cloud Error Reporting mengelompokkan dan memberi tahu Anda secara instan.
- Konfigurasi
📸 TODO(riccardo): tambahkan screenshot Google Cloud Error Reporting yang menampilkan pengecualian produksi yang dikelompokkan dan payload JSON terstruktur Cloud Logging dengan label rekaman aktivitas
🧠 Quest 3: pgvector Penelusuran Semantik & RAG Gemini
- Tingkat kesulitan: Sulit (Capstone AI Generatif)
- Tujuan: Menelusuri artikel secara konseptual menggunakan embedding vektor yang disimpan di PostgreSQL pada Cloud SQL.
- Cara Kerjanya:
- Aktifkan ekstensi
vectordi instance Cloud SQL PostgreSQL Anda:CREATE EXTENSION IF NOT EXISTS vector; - Tambahkan gem
neighborkeblog/Gemfiledan buat embedding teks dengan Gemini (text-embedding-004) diPost#after_save. - Lakukan kueri jarak kosinus (
<=>) untuk mendukung kotak penelusuran semantik dengan Turbo Streams.
- Aktifkan ekstensi
📸 TODO(riccardo): tambahkan screenshot kotak penelusuran semantik blog live yang menampilkan kecocokan konseptual dengan skor kemiripan kosinus
⚡ Quest 4: Asisten Audit SEO & Performa
- Kesulitan: Mudah / Menyenangkan (Pengalaman Developer)
- Sasaran: Mengaudit Core Web Vitals, Largest Contentful Paint (LCP), dan indeks keterbacaan Flesch-Kincaid / Fog menggunakan alat Antigravity dan Speedgrapher.
📸 TODO(riccardo): tambahkan screenshot ringkasan audit SEO dan Core Web Vitals Speedgrapher / Lighthouse
🤔 Kotak Rasa Ingin Tahu: Mendapatkan Konsol Rails Langsung di Cloud Run
💡 Pertanyaan: Di platform PaaS gaya Heroku atau VM tradisional, developer biasanya menggunakan SSH ke instance yang sedang berjalan atau menggunakan heroku run rails console untuk memeriksa atau memperbaiki data produksi secara langsung. Apa yang setara dengan Cloud Run, dan bagaimana cara memperkuatnya?
Mengapa
gcloud run exec
tidak ada untuk Layanan Cloud Run
Cloud Run adalah platform serverless: container Anda dapat berjalan sebagai nol, satu, atau ratusan instance yang identik, tanpa identitas yang stabil atau shell yang dapat dialamatkan. Hal ini pada dasarnya berbeda dengan Kubernetes (kubectl exec), di mana pod tertentu memiliki soket bernama yang dapat Anda lampirkan.
# ❌ 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
Empat Pola Berbasis GCP
Pola | Perintah | Interaktif? | Keamanan |
1. Proxy lokal + DB jarak jauh |
| ✅ Penuh | 🔒 mTLS Proxy Auth Cloud SQL |
2. Tugas Cloud Run (sekali jalan) |
| ❌ Hanya skrip | 🔒 Peran IAM diperlukan |
3. Gem Konsol Web + IAP | Browser → | ✅ Penuh | 🛡️ Zero-Trust IAP |
4. Cloud Shell + Proxy | Cloud Shell → | ✅ Penuh | 🔒 Identitas Google |
Pola 1 (Direkomendasikan untuk pemeriksaan data): Jalankan konsol Rails lokal yang berkomunikasi langsung dengan Cloud SQL PostgreSQL Anda melalui Proxy Auth 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
Anda mendapatkan interaktivitas IRB penuh dengan eksposur jaringan nol — Proxy Cloud SQL menggunakan mTLS melalui soket domain Unix atau loopback.
Pola 3 (Konsol Web + Zero Trust IAP, jalur browser yang diperkuat):
Gem web-console menyediakan sesi IRB berbasis browser. Tanpa perlindungan, ini adalah kerentanan keamanan yang sangat penting — jangan pernah mengeksposnya ke publik. Namun, jika digabungkan dengan Google IAP (Quest 1), Permukaan serangan akan menyusut: hanya identitas Google terverifikasi dalam daftar yang diizinkan yang dapat mencapai endpoint /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
⚠️ Pola 3 mengharuskan Quest 1 (IAP) diaktifkan terlebih dahulu. Tanpa itu, /rails/console di URL publik sama dengan membiarkan ruang server Anda tidak terkunci. Dengan IAP, alat ini menjadi alat operator yang andal dan diperkuat dengan Zero Trust tingkat Google.
Tanya Antigravity: "Tunjukkan cara menjalankan
rails console --sandbox
terhadap database produksi Cloud SQL saya dengan aman menggunakan Proxy Auth Cloud SQL."
🏆 Lulus di The Hive: Bukti Hasil Kerja yang Divalidasi Pengawas
Setelah menyelesaikan misi yang Anda pilih, klaim Piala Kelulusan Langkah 8 🏆 di Papan Peringkat The Hive:
- Kirimkan Quest Anda di GitHub:
- Buka Masalah baru di repositori resmi: github.com/palladius/rails8-app-on-gcp/issues/new
- Format judul:
🎓 [Step 8 Completed]: - Di isi email, sertakan deskripsi singkat tentang apa yang telah Anda selesaikan dan link ke deployment Cloud Run aktif Anda.
- Menghubungkan Aplikasi Cloud Run ke Masalah Anda:
- Catat Nomor masalah Anda (mis.
88atau URL lengkaphttps://github.com/palladius/rails8-app-on-gcp/issues/88). - Masukkan variabel lingkungan
STEP_8_GHIke dalam layanan Cloud Run Anda:gcloud run services update blog \ --region europe-west1 \ --update-env-vars STEP_8_GHI=<YOUR_ISSUE_NUMBER> - Pastikan endpoint
/status.jsonAnda kini melaporkan misi Anda: Anda akan melihat: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" }
- Catat Nomor masalah Anda (mis.
- Dapatkan Peninjauan Pengawas Anda & Buka Kunci Trofi Emas:
- Periksa Papan Peringkat The Hive: baris Anda kini akan menampilkan badge kuning:
⏳ GHI #XX review pending. - Minta pengawas workshop (@palladius, @emilianodellacasa, atau @ricc) untuk memeriksa deployment Anda.
- Segera setelah pengawas mengomentari
LGTMmasalah Anda, The Hive akan otomatis meningkatkan progres Anda menjadi 8/8 🏆 dengan badge bercahaya emas/ungu dan piala permanen yang langsung ditautkan ke masalah utama Anda.
- Periksa Papan Peringkat The Hive: baris Anda kini akan menampilkan badge kuning:
11. Kesimpulan dan Pembersihan 🎓
Selamat! 🎉
Anda telah membangun dan men-deploy aplikasi Rails 8 tingkat produksi yang siap digunakan perusahaan di Google Cloud Platform:
- Progresi Tanpa Cabang: Menguasai alur kerja modern dengan overlay Mesin Waktu di
main. - Cloud Storage: Penyimpanan objek pribadi yang skalabel dengan penandatanganan blob IAM (
iam: true). - Cloud SQL: PostgreSQL terkelola yang diamankan dengan tunnel mTLS Proxy Auth Cloud SQL.
- Secret Manager: Tidak ada kredensial teks biasa atau kebocoran file
.env. - Multi-Container Cloud Run: Web Puma yang terisolasi, pekerja Solid Queue, dan sidecar proxy.
- AI Generatif: Pembuatan poster vintage asinkron dan sintesis podcast TTS dengan Vertex AI.
- Quest: Zero-Trust IAP, Observabilitas SRE, dan embedding pgvector
🧹 Pembersihan Resource
Untuk menghindari biaya berkelanjutan setelah menyelesaikan workshop, pastikan untuk membersihkan resource Google Cloud Anda. Pilih pendekatan yang paling sesuai dengan lingkungan Anda:
Opsi 1: Penonaktifan Terraform (Direkomendasikan / Default 🟢)
Gunakan opsi ini jika Anda menggunakan project Google Cloud yang sudah ada atau dibagikan tempat resource atau data lain berada. Hal ini akan menghancurkan hanya resource yang disediakan selama workshop ini (Cloud Run, Cloud SQL, Secret Manager, bucket GCS), dan tidak memengaruhi project Anda yang lain.
cd iac
terraform destroy -auto-approve
Opsi 2: Penghapusan Total Project (Tidak Meninggalkan Jejak 🌪️)
Gunakan opsi ini jika Anda membuat project workshop khusus (misalnya, menggunakan kredit workshop atau sandbox) dan ingin memastikan tidak ada jejak yang tersisa—termasuk log, metadata, akun layanan, dan link penagihan.
gcloud projects delete $GOOGLE_CLOUD_PROJECT --quiet