Rails 8 w Google Cloud: od zera do AI

1. Wprowadzenie

Witamy na warsztatach Rails 8 w Google Cloud. W tym praktycznym ćwiczeniu przekształcisz nowoczesną aplikację Rails 8 z prostej lokalnej wersji podstawowej SQLite w gotową do wdrożenia w środowisku produkcyjnym architekturę referencyjną klasy korporacyjnej w Google Cloud.

Podczas tych warsztatów nie tylko wdrożymy aplikację, ale też przeprowadzimy opartą na naszych doświadczeniach modernizację chmury na potrzeby produkcji (metoda lift-and-shift w odpowiedni sposób).

Zaczynasz od typowej konfiguracji lokalnej „działa na moim komputerze” (dysk tymczasowy, wbudowana baza danych SQLite, zadania w procesie) i stopniowo przekształcasz ją w architekturę chmurową klasy korporacyjnej, odporną na awarie w Google Cloud:

  1. 🐘 Z lokalnej bazy danych SQLite → Cloud SQL (PostgreSQL): koniec z czyszczeniem bazy danych po ponownym uruchomieniu. Przenosimy się do zarządzanej bazy danych PostgreSQL, która jest bezpiecznie połączona za pomocą serwera proxy uwierzytelniania Cloud SQL (mTLS, bez publicznych adresów IP).
  2. 🪣 Z efemerycznej pamięci lokalnej → prywatny GCS: bezserwerowy dysk jest tymczasowy. Łączymy ActiveStorage z prywatnym Google Cloud Storage za pomocą podpisanych adresów URL IAM (iam: true) – bez publicznych zasobników i wycieków kluczy.
  3. 🚀 Od zadań w procesie do Cloud Run z wieloma kontenerami: łączymy monolityczną aplikację Rails z bezserwerową chmurą. Za pomocą standardowego narzędzia Docker Compose uruchamiamy Solid Queue jako dedykowany kontener roboczy obok Pumy w podzie Cloud Run z wieloma kontenerami – bez rozrastania się mikrousług i bez głodzenia wątków. DHH byłby dumny!

🍌 A ponieważ żyjemy w 2026 roku, wzbogacamy cały pakiet o Google Antigravity. To nie jest coś, o czym pomyśleliśmy na końcu. Mamy wskazówki i umiejętności, które pomogą Ci lepiej wykorzystać (i czerpać przyjemność z) tych warsztatów.

🏛️ Docelowa architektura referencyjna (wariant 1: czysta, płaska grafika wektorowa dla przedsiębiorstw)

Oto kanoniczna architektura referencyjna Google Cloud, którą utworzysz i wdrożysz (wariant 1: czysta, płaska, wektorowa architektura klasy enterprise). Pokazuje ona naszą produkcyjną usługę Cloud Run z wieloma kontenerami połączoną ze wszystkimi zarządzanymi usługami Google Cloud do przechowywania danych i usługami AI:

Wariant 1. Clean Flat Vector Enterprise – architektura Rails 8 w Google Cloud

🎞️ Ewolucja architektury: od zera do architektury chmurowej

Obserwuj, jak nasz stos jest stopniowo modernizowany od efemerycznej konfiguracji na jednym urządzeniu do w pełni zarządzanego projektu Google Cloud na każdym etapie warsztatów:

Warsztat dotyczący ewolucji architektury

Czego się nauczysz

Zaczynajmy!

2. Krok 0. Wymagania wstępne, konfiguracja Antigravity i weryfikacja płatności

💡 Scenariusz: Ty i Twój zespół tworzycie aplikację Rails 8 o znaczeniu krytycznym. Zanim zaczniemy pisać kod lub uruchamiać zasoby w chmurze, musimy skonfigurować łańcuch narzędzi, połączyć się z Google Antigravity oraz zweryfikować dane logowania do Google Cloud i podstawy rozliczeń.

1. Lista kontrolna wymagań wstępnych

Zanim zaczniemy, upewnij się, że masz w swoim środowisku te narzędzia:

  • just (1.21+): (just --version) narzędzie do wykonywania zadań – każde polecenie w tych warsztatach to just przepis (poproś AI Harness o jego zainstalowanie lub zobacz skills/rails8app-workshop).
  • Git (2.30 lub nowszy): (git --version) do kontroli wersji, rozgałęziania i klonowania repozytorium.
  • Pakiet SDK Google Cloud (gcloud CLI): zainstalowany i aktualny.
  • Interfejs wiersza poleceń Terraform (wersja 1.5 lub nowsza): do deklaratywnego udostępniania infrastruktury.
  • docker i docker-compose: zainstalowane i działające lokalnie.
  • Ruby3.4.5: (ruby -v – przypięte przez blog/.ruby-version; poproś AI Harness o zainstalowanie 3.4.5, jeśli go brakuje). Rails 8 jest zadeklarowany w Gemfile i automatycznie instalowany przez bundle install.
  • Google Antigravity 2.0: autonomiczny asystent AI do programowania w parach (pobierz Google Antigravity 2.0). Zalogujesz się za pomocą osobistego konta Gmail – nie potrzebujesz tokena API.

2. Uwierzytelnianie Google Cloud, konfiguracja dedykowana i ADC

Aby uniknąć kolizji z istniejącymi konfiguracjami gcloud na kontach firmowych, osobistych lub wielu kontach, zdecydowanie zalecamy utworzenie dedykowanej konfiguracji nazwanej na potrzeby tych warsztatów:

# 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

💡 Dlaczego warto używać konfiguracji dedykowanej? Użycie gcloud config configurations create rails8-on-gcp-workshop izoluje wszystkie ustawienia interfejsu CLI (konto, projekt limitu, region domyślny) specjalnie na potrzeby tych warsztatów. Gdy skończysz, możesz w każdej chwili wrócić do normalnej konfiguracji, klikając gcloud config configurations activate default.

Aktywna konfiguracja gcloud

3. 🚨 Weryfikacja rozliczeń GCP

⚠️ Uwaga: wdrożenia Google Cloud SQL i Cloud Run wymagają aktywnego połączonego konta rozliczeniowego lub ważnych środków edukacyjnych na warsztaty. Sprawdźmy teraz, czy konfiguracja jest prawidłowa. Aby to zrobić, uruchom weryfikację płatności:

gcloud beta billing projects describe $GOOGLE_CLOUD_PROJECT

Upewnij się, że urządzenie billingEnabled: true zostało zwrócone. Możesz też wyświetlić listę wszystkich kont rozliczeniowych:

gcloud billing accounts list

Upewnij się, że co najmniej jeden z nich zawiera symbol OPEN: True. Jeśli żadne z nich nie są aktywne, połącz konto rozliczeniowe lub wykorzystaj kupon na środki na szkolenie na stronie Płatności w konsoli Google Cloud.

Aktywne konto rozliczeniowe GCP

4. Sklonuj repozytorium i sparuj je z Antigravity

Sklonuj repozytorium i otwórz katalog. Zwróć uwagę, że pozostajemy w całości w przestrzeni main:

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

Otwórz ten katalog w Google Antigravity. Antigravity automatycznie sprawdzi repozytorium, odczyta AGENTS.md i będzie czekać na Twoje polecenia jako programista pracujący w parze.

5. 🪫 Jeśli Antigravity wyczerpie bezpłatne środki

⚠️ Środki w chmurze, które właśnie zostały wykorzystane, NIE uzupełniają Antigravity. Są to 2 osobne platformy rozliczeniowe: limit Antigravity jest powiązany z abonamentami Google AI (Google One), a vouchery i środki w Google Cloud są powiązane z kontem rozliczeniowym Cloud. Połączenie konta rozliczeniowego z projektem GCP nie spowoduje dodania ani jednego dodatkowego promptu Antigravity.

Jeśli agent zatrzyma się z komunikatem o wyczerpaniu limitu, wykonaj te czynności – pierwsze 2 są bezpłatne i natychmiastowe:

  1. Przełącz pulę modeli Limit jest śledzony w przypadku każdej rodziny modeli, a Antigravity wyświetla 2 niezależne pule (modele Gemini i modele Claude/GPT). Modele Claude i GPT-OSS są dostępne na poziomie bezpłatnym, więc wyczerpanie puli Gemini często nie wpływa na drugą pulę. Użyj selektora modelu pod polem promptu.Selektor modeli Antigravity – przełączanie między pulami Gemini, Claude i GPT-OSS.
  2. Wybierz model Flash zamiast Pro. Limity częstotliwości są powiązane z ilością pracy, jaką agent wykonuje w ramach każdego żądania, więc lżejszy model pozwala na większą liczbę żądań.
  3. Używaj Antigravity CLI z własnym kluczem Gemini API. Funkcja Bring-your-own-key nie jest obsługiwana w IDE Antigravity – udokumentowaną alternatywą jest interfejs CLI. Utwórz bezpłatny klucz w Google AI Studio (wymaga projektu, ale nie wymaga konta rozliczeniowego), a następnie pobierz i skonfiguruj Antigravity CLI.
  4. Poczekaj lub przejdź na wyższą wersję. Limit poziomu bezpłatnego jest odświeżany co tydzień (data resetu jest podana w komunikacie o błędzie). Google AI Pro/Ultra to jedyny udokumentowany sposób na zwiększenie limitu podstawowego w aplikacji na komputery. Te pakiety są odświeżane co 5 godzin i można w nich wykorzystywać kupione środki na AI na przekroczenie limitu.
  5. Chociaż są to warsztaty Antigravity, większość uprzęży do kodowania AI powinna również działać – wystarczy skierować je na katalogi AGENTS.md i skills/ w repozytorium.

6. Automatyczna weryfikacja na etapie 0

Sprawdź, czy środowisko na etapie 0 jest w 100% zgodne z pakietem oceniającym:

just workshop-eval 0

✨ Wow Moment: Antigravity i silnik oceny warsztatów weryfikują wersje interfejsu CLI, środowisko Ruby i uwierzytelnianie ADC w mniej niż 2 sekundy.

3. Krok 1. Rozpoczęcie pracy z infrastrukturą Terraform i diagnostyka przed lotem

💡 Strategia: aprowizacja zarządzanych baz danych, takich jak Google Cloud SQL PostgreSQL, trwa około 10–12 minut. Zamiast czekać bezczynnie, uruchamiamy niezmienną infrastrukturę za pomocą Terraform w tle, podczas gdy pracujemy lokalnie.

1. Uruchom automatyczny pakiet diagnostyczny przed lotem

Przed uruchomieniem infrastruktury w chmurze przeprowadź kompleksowy zestaw testów przed lotem:

just workshop-test

Ten skrypt (bin/workshop_diagnostics.rb):

  • Weryfikuje konfigurację tożsamości GOOGLE_CLOUD_ACCOUNT.
  • Sprawdza aktywne płatności i powiązanie projektu.
  • Weryfikuje domyślne uwierzytelnianie aplikacji (ADC) w Vertex AI.
  • Potwierdza obraz testowy ActiveStorage (blog/app/assets/images/gcs_dev_image.jpg).

just workshop-test diagnostics output

Jeśli brakuje znaku .env, skopiuj go z udokumentowanego szablonu:

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

💡 Wskazówka: jeśli just workshop-test zgłosi, że interfejs Billing API jest wyłączony w Twoim projekcie, możesz go szybko włączyć w ten sposób:

gcloud services enable cloudbilling.googleapis.com

2. ⏱️ Uruchamianie infrastruktury Terraform asynchronicznie

Uruchom jednoetapowy przepis Terraform, który utworzy zdalny zasobnik stanu backendu i uruchomi wdrażanie infrastruktury:

just terraform-apply

Terraform został zainicjowany

Alternatywna metoda ręczna (lub bezpośrednie uruchomienie skryptu `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 ..

Co zapewnia Terraform:

  • Zasobnik Google Cloud Storage: utworzony z dostępem prywatnym i podpisywaniem certyfikatów IAM (iam: true).
  • Obraz testowy Canary: przesyła gcs_dev_image.jpg do zasobnika, aby włączyć pełną weryfikację obiektu blob.
  • Instancja Google Cloud SQL PostgreSQL: inicjuje udostępnianie w tle (ok. 10–12 minut).

Instancja Cloud SQL PostgreSQL w konsoli Google Cloud

💡 Uwaga: możesz bezpiecznie zignorować ostrzeżenie o braku automatycznych kopii zapasowych – nie będą one potrzebne w przypadku tej bazy danych warsztatowych (chociaż automatyczne codzienne kopie zapasowe są niezbędną sprawdzoną metodą w przypadku każdej prawdziwej produkcyjnej bazy danych).

Zastosowanie Terraform zostało zakończone z wynikami

📝 Zanotuj adres URL Cloud Run: zastosowanie Terraform powinno się powieść i wyświetlić dane wyjściowe infrastruktury. Zanotuj cloud_run_url (lub skopiuj go do schowka) – będzie Ci potrzebny w dalszych krokach podczas weryfikacji usługi na żywo.

3. Automatyczna weryfikacja na etapie 1 (opcjonalna)

Sprawdź, czy wymagania wstępne i konfiguracje z kroku 1 są spełnione:

just workshop-eval 1

⏱️ Uwaga: ta ocena sprawdza aktywne zasoby w chmurze i może potrwać kilka minut. Jest to całkowicie opcjonalne – podczas gdy skrypt działa w terminalu, możesz od razu przejść do następnego kroku bez czekania.

4. Krok 2. Lokalny punkt odniesienia, Mailpit i wprowadzenie administratora

Zaczynamy od czystej, nowoczesnej aplikacji blogowej Rails 8 działającej na hoście lokalnym z bazą danych SQLite, przechwytywaniem e-maili Mailpit i ActiveStorage na dysku.

1. Lokalne uruchamianie aplikacji

📁 Uważaj na katalog! W tym repozytorium znajduje się aplikacja Rails w blog/ i Terraform w iac/. Po wykonaniu kroku 1 znajdziesz się w katalogu głównym repozytorium, więc każde polecenie w kroku 2 będzie wykonywane z

blog/ – Gemfile, bin/rails i compose.yaml mieszkają w tym samym miejscu. Uruchomienie ich z katalogu głównego kończy się niepowodzeniem z błędem no configuration file provided: not found lub brakiem Gemfile.

Przejdź do aplikacji Rails, sprawdź, czy używasz przypiętej wersji Ruby, a następnie zainstaluj gemy i skonfiguruj bazę danych:

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

Błąd uprawnień podczas zapisywania w

/var/lib/gems/

lub

ruby -v

pokazuje systemową wersję Ruby (np. 3.3.x)? Ruby w czystym systemie Linux całkowicie ignoruje pliki .ruby-version i próbuje zainstalować gemy w całym systemie. Sprawdź, czy w aktywnej powłoce terminala wczytano rbenv:

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

Uwaga: Jeśli w kroku 0 pominięto instalację wersji 3.4.5, uruchom rbenv install 3.4.5. Możesz też użyć trybu A (Docker Compose) poniżej (just compose-up), który uruchamia przypięty stos Ruby w całości w kontenerach bez dotykania gemów hosta.

💡 Nie chcesz o tym myśleć? just install i just compose-up z katalogu głównego repozytorium wykonują za Ciebie cd blog.

2. 🌱 Automatyczne wykrywanie węzłów początkowych i inicjowanie administratora (problemy 21 i 25)

Baza danych (db/seeds.rb) zawiera automatyczne wykrywanie inteligentnego środowiska:

  • Sprawdza aktywny adapter bazy danych (SQLite lub Postgres) i konfigurację pamięci.
  • Wykrywa etap 0 (localhost) i automatycznie tworzy początkowego użytkownika administracyjnego oraz wypełnia post pedagogiczny:
    • [LOCAL BASELINE] Welcome to Rails 8 on Localhost!
    • Gotowy lokalny załącznik z obrazem smutnej buźki (local_sad_image.png) ze znakiem wodnym informującym, że lokalna pamięć dyskowa jest efemeryczna.
  • Automatycznie wysyła e-maila z prośbą o zresetowanie hasła za pomocą ActionMailera.

Sprawdź lub ponownie uruchom prompt (automatycznie używa Twojego promptu GOOGLE_CLOUD_ACCOUNT z .env):

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

Uruchom lokalny stos technologiczny za pomocą Docker Compose:

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

Docker Compose uruchamia Rails, Mailpit i lokalny stos

Aplikacja, lokalny serwer SMTP Mailpit i przeglądarka bazy danych Adminer działają teraz razem w odizolowanych kontenerach:

  • Aplikacja Rails: http://localhost:3000
  • Interfejs internetowy Mailpit: http://localhost:8025
  • Interfejs bazy danych Adminer: http://localhost:8081

🧯 Konflikt portu 3000 lub

server.pid

błąd? Jeśli wcześniej uruchomiono natywny serwer deweloperski (bin/dev), zatrzymaj go za pomocą polecenia Ctrl+C. Jeśli Docker zgłosi, że port 3000 jest już przydzielony, lub znajdzie pozostałościserver.pid po poprzednim uruchomieniu, zwalniaj miejsce za pomocą tego polecenia:

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

3. Korzystanie z Mailpita i logowanie administratora

  1. Przechwytywanie wychodzących e-maili za pomocą Mailpit: otwórz http://localhost:8025 w przeglądarce. Sprawdź lokalną skrzynkę odbiorczą. Podczas db:seed ActionMailer wysłał e-maila z informacjami o wprowadzeniu administratora, który Mailpit bezpiecznie przechwycił lokalnie bez dotykania zewnętrznych serwerów poczty e-mail ani danych logowania:

Interfejs internetowy Mailpit wyświetlający przechwyconą wiadomość e-mail z prośbą o zresetowanie hasła 2. Zaloguj się na blogu: otwórz http://localhost:3000 w przeglądarce.

  • Aby ustawić hasło, możesz kliknąć link do resetowania hasła bezpośrednio w e-mailu z Mailpita.
  • Możesz też zalogować się, używając adresu e-mail konta Google Cloud (z domeny .env) i domyślnego hasła początkowego: Ch4ng3m3!!1.
  1. Obserwuj plakietki telemetrii wizualnej:
    • Zwróć uwagę na żółty baner środowiska i odznaki w interfejsie: Notice: Ephemeral Database Active (POSTGRESQL) i [EPHEMERAL DB / STORAGE] 💾 Local.
    • Zwróć uwagę na znak wodny posta: stempel 🏠 (nanobanana_stamp_local.png w prawym dolnym rogu obrazu okładki). Dzięki temu od razu zobaczysz, że zasoby i baza danych są obecnie powiązane z tymczasową pamięcią lokalną.

Strona główna lokalnego bloga z wyświetlonymi powiadomieniami o telemetrii i plakietkami

💡 Opcjonalna wskazówka: ćwiczenia w konsoli Rails Chcesz się dowiedzieć, jak Rails wchodzi w interakcję z danymi początkowymi z interfejsu CLI? Przejdź do konsoli interaktywnej:

bin/rails console

Dynamicznie sprawdź zainicjowanego użytkownika z uprawnieniami administracyjnymi za pomocą zmiennej środowiskowej:

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. Automatyczna weryfikacja w kroku 2

Sprawdź lokalny poziom odniesienia i konfigurację administratora:

just workshop-eval 2

just workshop-eval 2 verification output

5. Problem: kontenery bezstanowe

Kontenery Cloud Run są bezstanowe i krótkotrwałe. Jeśli wdrożymy bazę danych SQLite i lokalny katalog storage/ bezpośrednio w Cloud Run, wszystkie posty i przesłane obrazy zostaną trwale usunięte za każdym razem, gdy kontener zostanie przeskalowany do zera lub ponownie uruchomiony.

W następnym kroku celowo wdrożymy tę tymczasową konfigurację w Cloud Run, aby na własne oczy zobaczyć Stateless Shock.

5. Krok 3. Wdróż 1 – The Stateless Shock (wczesny efekt WOW w 3 minuty!)

Zamiast czekać 15–20 minut na bazy danych w chmurze, zanim cokolwiek pojawi się w internecie, nowoczesne tworzenie aplikacji bezserwerowych zaczyna się od szybkiego sukcesu: wdrożenia aplikacji Rails z jednym kontenerem bezpośrednio w Google Cloud Run w mniej niż 3 minuty.

1. Powrót do przeszłości

Aby zagwarantować, że nasza konfiguracja początkowa jest w 100% efemeryczna (lokalna baza danych SQLite i lokalna pamięć masowa plików), użyj silnika Zero-Branch Time-Machine:

just workshop-rewind 1

💡 Co się stało?workshop-rewind 1 zastosował nakładkę konfiguracji stage-1-stateless do blog/config/ bez opuszczania gałęzi main. Aplikacja jest skonfigurowana z bazą danych SQLite na dysku kontenera i ActiveStorage w lokalnym systemie plików.

2. Wdrażanie PUMY w jednym kontenerze w Cloud Run

wdrażać bezpośrednio z kodu źródłowego w Cloud Run; Nowoczesny Rails 8 automatycznie generuje oficjalny, gotowy do wdrożenia w środowisku produkcyjnym Dockerfile od razu po zainstalowaniu (z wieloetapowymi kompilacjami, jemalloc i zabezpieczeniami dla użytkowników bez uprawnień roota). Gdy wdrożysz aplikację z folderu blog/, Google Cloud Build wykryje natywny plik Dockerfile i udostępni zarządzaną usługę bezserwerową:

# 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

Powinny pojawić się dane wyjściowe podobne do tych:

gcloud run deploy building and deploying container

⏳ Powinno to potrwać kilka minut, podczas których Cloud Build spakuje kontener i udostępni wersję Cloud Run.

🧯 Rozwiązywanie problemów i najczęstsze pułapki

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

? Ten sam błąd może mieć 2 różne przyczyny:

  1. gcloud właśnie włączył interfejs API. Jeśli wdrożenie wyświetliło The following APIs are not enabled ... cloudbuild.googleapis.com, a Ty odpowiedziałeś(-aś) Y, kompilacja mogła się rozpocząć, zanim nowe uprawnienia zostały w pełni rozpowszechnione. W wyskakującym okienku gcloud pojawia się ostrzeżenie „To może potrwać kilka minut”. Odczekaj minutę i uruchom ponownie to samo polecenie. Zwykle działa ono poprawnie. Krok 1 terraform apply zwykle włącza ten interfejs API, więc nie powinno się wyświetlać żadne okno.
  2. Domyślne konto usługi Compute rzeczywiście nie ma przypisanych ról. Nowoczesne projekty GCP wymuszają w nim zasadę jak najmniejszych uprawnień, co powoduje przerwanie wdrożeń źródłowych. Rozwiąż problem za pomocą:
    just project-status   # grants storage.admin, logging.logWriter, artifactregistry.writer, cloudbuild.builds.builder
    
    just workshop-test też teraz to zgłasza, więc warto ponownie uruchomić proces, jeśli wdrożenie nadal się nie udaje.

Podczas wdrażania:

  1. Cloud Run skompiluje obraz kontenera Rails.
  2. Przypisuje publiczną, bezpieczną domenę TLS: https://blog-[hash]-[region].a.run.app.
  3. Ruch zacznie być kierowany do Pumy na porcie 8080.

3. ✨ Wczesny moment WOW

Otwórz wygenerowany adres URL Cloud Run w przeglądarce.

  1. Nowoczesna aplikacja Rails 8 jest już dostępna w Google Cloud.
  2. Zaloguj się, używając danych logowania administratora (GOOGLE_CLOUD_ACCOUNT i APP_ADMIN_PASSWORD). Jeśli nie widzisz przycisku logowania, kliknij „Nowy post”. Najpierw pojawi się prośba o podanie nazwy użytkownika i hasła:Strona logowania Rails 8 z banerami telemetrycznymi o krótkim czasie wyświetlania
  3. Kliknij „Nowy post”, napisz artykuł zatytułowany „Mój pierwszy post w Cloud Run”, dołącz zdjęcie i kliknij Utwórz post.
  4. Post zostanie opublikowany z pełnym formatowaniem, a obraz zostanie wyrenderowany.
  5. Zwróć uwagę na telemetrię wizualną:
    • Plakietka w nagłówku: [EPHEMERAL DB / STORAGE] 💾 Local
    • Graficzny znak wodny na obrazie: stempel 🏠 (127.0.0.1 plakietka dysku tymczasowego w prawym dolnym rogu).

🐝 Dołącz do tabeli wyników warsztatów na żywo! Jeśli jesteś online, a osoba nadzorująca wyświetla tablicę wyników i chcesz do niej dołączyć, dodaj tutaj adres URL Cloud Run: 👉 zarejestruj Cloud Run na tablicy wyników.

Szczegóły wersji usługi na blogu konsoli Google Cloud Run

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

Cloud Run to bezstanowa platforma bezserwerowa. Gdy ruch w internecie spada do zera, Cloud Run skaluje się w dół do zera instancji kontenera, aby zaoszczędzić pieniądze. Gdy nadejdzie nowe żądanie HTTP lub zostanie wdrożona nowa wersja kontenera, Cloud Run uruchomi zupełnie nowy, czysty obraz kontenera.

Baner z zablokowanymi zadaniami

Gdy tworzysz posta lub dołączasz obraz w przypadku wdrożenia w jednym kontenerze, Rails umieszcza w kolejce zadania ActiveJob (np. analizę wymiarów obrazu lub indeksowanie metadanych). Ponieważ nikt nie uruchamia procesu w tle, zobaczysz baner z ostrzeżeniem:

Uwaga: 8 zadań w tle oczekuje obecnie na wykonanie

Kuszące rozwiązanie: uruchamianie Solid Queue w Pumie

Sprytny deweloper może powiedzieć: „Czekaj! Rails 8 umożliwia uruchamianie Solid Queue bezpośrednio w procesie serwera WWW Puma przez włączenie

SOLID_QUEUE_IN_PUMA=true

zmienna środowiskowa!

Przetestujmy to obejście w 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

Teraz wróć do przeglądarki i odśwież stronę:

💥 The Stateless Shock:

  1. Dobra wiadomość: proces Solid Queue jest już aktywny w Pumie. Wszystkie nowe zadania są natychmiast wyczerpywane.
  2. The Cold Shower (The Catch!):
    • Cloud Run wdrożył nową wersję, więc poprzednia instancja kontenera została zastąpiona.
    • Artykuł, który napisałeś, i plik bazy danych SQLite na dysku zostały całkowicie usunięte.
  3. Lekcja z architektury: uruchamianie procesów w tle w Pumie zużywa cenne zasoby procesora i pamięci RAM wątków internetowych i nie rozwiązuje problemu z trwałością.

5. Automatyczna weryfikacja na etapie 3

Sprawdź wdrożenie Cloud Run i obsługę alertów:

just workshop-eval 3

✨ Lekcja: obejścia z jednym kontenerem, takie jak SOLID_QUEUE_IN_PUMA, są przydatne w przypadku programowania lokalnego, ale w nowoczesnej architekturze chmury:

  • Pliki multimedialne wymagają Google Cloud Storage (GCS) (krok 4).
  • Dane relacyjne i kolejki zadań wymagają zarządzanej usługi Google Cloud SQL (krok 5).
  • Pracownicy działający w tle, którzy zużywają dużo zasobów, powinni znajdować się w osobnym kontenerze pomocniczym (krok 6).

6. Krok 4. Wdróż 3 – trwałe przechowywanie w GCS i ostrzeżenie POLA

W tym kroku odłączamy pamięć masową plików multimedialnych i innych od dysku kontenera, przełączając ActiveStorage na Google Cloud Storage (GCS) i używając krótkotrwałych podpisanych adresów URL za pomocą interfejsu IAM Credentials API (iam: true).

Zanim wprowadzisz jakiekolwiek zmiany w konfiguracji, zapoznaj się z 3 elementami, których Rails potrzebuje do komunikacji z GCS:

① Identyfikator projektu + nazwa zasobnika – Rails musi wiedzieć, gdzie przechowywać obiekty blob. Aplikacja odczytuje wartość GOOGLE_CLOUD_PROJECT ze środowiska i wyznacza nazwę zasobnika jako ${GOOGLE_CLOUD_PROJECT}-activestorage-prod. Terraform z kroku 1 utworzył już ten zasobnik, więc nie musisz niczego więcej robić.

② Konto usługi z odpowiednimi uprawnieniami – Cloud Run działa jako domyślne konto usługi Compute Engine (PROJECT_NUMBER-compute@developer.gserviceaccount.com). To konto usługi musi mieć w zasobniku 2 role uprawnień:

  • roles/storage.objectAdmin – do przesyłania i udostępniania obiektów binarnych.
  • roles/iam.serviceAccountTokenCreator – do wywoływania interfejsu IAM Credentials signBlob API i generowania krótkotrwałych podpisanych adresów URL.

⚠️ Kuszący skrót

public: true Najszybszym sposobem na uruchomienie GCS jest ustawienie public: true w storage.yml i przyznanie allUsers:objectViewer w zasobniku. Obrazy wczytują się natychmiast i nie wymagają logowania. Nie rób tego. Publiczny zasobnik udostępnia wszystkie przesłane przez użytkowników multimedia w otwartym internecie na zawsze. Nasz projekt wykorzystuje iam: true, aby zachować 100% prywatność zasobnika – każdy adres URL obrazu to podpisany, wygasający token generowany na bieżąco przez interfejs IAM Credentials API.

③

ACTIVE_STORAGE_SERVICE=google – pojedyncza zmienna środowiskowa, która w czasie działania przełącza Rails z dysku lokalnego na GCS. Nie wymaga to zmiany kodu ani ponownego wdrażania logiki aplikacji – wystarczy przełączyć jedną zmienną środowiskową.

1. Powrót do przeszłości

Przejdź do etapu 2 wehikułu czasu:

just workshop-rewind 2

Sprawdź blog/config/storage.yml – zobaczysz, że aplikacja definiuje 3 nazwane środowiska GCS (dev / test / prod), które korzystają z tego samego bezpiecznego wzorca:

<% 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

Architektura podpisywania GCS IAM

2. Przyznawanie uprawnień do przechowywania i podpisywania w IAM

Sprawdź, czy wykonawcze konto usługi Cloud Run ma uprawnienia do podpisywania adresów URL i przesyłania obiektów:

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. Trzecie wdrożenie w Cloud Run z dołączonym GCS

Ponownie wdróż z włączoną usługą GCS. Upewnij się, że znajdujesz się w folderze blog/ (w przeciwnym razie Cloud Run spróbuje utworzyć kompilację za pomocą Buildpacks zamiast Rails 8 Dockerfile, co spowoduje błędy kompilacji).

Użyj znaku --update-env-vars (nie --set-env-vars), aby zachować istniejące zmienne środowiskowe (klucz główny, konfiguracja IAP itp.):

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

💡 Wskazówka: jeśli wdrożenie się nie powiedzie, kliknij link do logów Cloud Build, który jest wygodnie dostępny w danych wyjściowych terminala (jak pokazano na ilustracji poniżej). Możesz też poprosić Antigravity o pomoc w rozszyfrowaniu i naprawieniu błędu.

Link do logu błędu Cloud Build w danych wyjściowych terminala

4. ✨ The Surviving Image & The Cloud Stamp

  1. Odśwież adres URL bloga.
  2. Utwórz nowy post na blogu zatytułowany „Przetrwanie w chmurze” i prześlij zdjęcie.
  3. Spójrz na prawy dolny róg obrazu:
    • Znaczek 🏠 zniknął.
    • Zastąpiono go kolorową pieczątką Cloud GCS (nanobanana_stamp_cloud.png).
  4. Wymuś ponowne uruchomienie innego kontenera (co powinno spowodować wdrożenie czwartej wersji):
    gcloud run services update blog --region $GOOGLE_CLOUD_REGION --update-env-vars RESTART_TRIGGER=$(date +%s)
    
  5. Odśwież stronę: podczas resetowania bazy danych SQLite binarny plik obrazu jest bezpiecznie przechowywany w Google Cloud Storage. Weryfikacja za pomocą interfejsu wiersza poleceń:
    gcloud storage ls -l -r gs://$GCS_BUCKET/
    

Oto lista przesłanych obiektów binarnych i multimediów, które możesz zobaczyć w konsoli przeglądarki Google Cloud Storage w zasobniku produkcyjnym (gs://$GCS_BUCKET):

Szczegóły zasobnika w konsoli Google Cloud Storage pokazujące trwałe obiekty blob obrazów

5. ⚠️ Problem: baner ostrzegawczy dotyczący utkniętych zadań

Gdy przesyłasz obraz, ActiveStorage umieszcza w kolejce zadanie analizy (ActiveStorage::AnalyzeJob), aby wyodrębnić wymiary i metadane.

Cloud Run uruchamia jednak obecnie tylko jeden kontener internetowy (puma). W Rails 8 Solid Queue przechowuje zadania w tle w bazie danych, ale nikt ich nie wykonuje bundle exec rails solid_queue:start!

Spójrz na górę strony bloga: zobaczysz jasny baner ostrzegawczy wyrenderowany przez blog/app/views/layouts/_check_stuck_jobs.html.erb.

Rozwiążemy ten problem w kroku 6, gdy przejdziemy na architekturę wielokontenerową klasy produkcyjnej z kontenerem pomocniczym i dedykowanym kontenerem worker, w którym kolejka Solid Queue będzie działać niezależnie od ruchu w internecie.

6. Automatyczna weryfikacja kroku 4

Sprawdź konfigurację GCS i telemetrię zablokowanych zadań:

just workshop-eval 4

7. Krok 5. Lista kontrolna przed wdrożeniem: Cloud SQL, Secrets i łączność z bazą danych

W krokach 3 i 4 udało Ci się opanować kontenery bezstanowe i Google Cloud Storage, a Terraform w tym czasie przygotował w tle backend produkcyjny.

Przed wdrożeniem pełnej architektury 3 kontenerów w produkcyjnym kontenerze pomocniczym (wdrożenie 4 w kroku 6) ten krok służy jako szybka kontrola przed lotem, która pozwala sprawdzić stan bazy danych, łączność i dostęp do kluczy tajnych.

0. Zmienne środowiskowe wczytywania

Wyeksportuj wszystkie wymagane zmienne powłoki bezpośrednio z danych wyjściowych 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. Sprawdzanie stanu Cloud SQL

Sprawdź, czy zarządzana instancja PostgreSQL zakończyła wstępną obsługę administracyjną i jest w dobrym stanie:

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

Dane wyjściowe muszą mieć postać RUNNABLE.

2. (Opcjonalnie) Testowanie połączenia z bazą danych z laptopa

W środowisku produkcyjnym kontenery Cloud Run łączą się bezpiecznie z Cloud SQL za pomocą zaszyfrowanego pomocniczego proxy (127.0.0.1:5432), nie udostępniając bazy danych publicznemu internetowi (0.0.0.0/0).

ℹ️ Uwaga: ten krok jest całkowicie opcjonalny. W Cloud Run oficjalny kontener pomocniczy serwera proxy uwierzytelniania Cloud SQL automatycznie obsługuje połączenia mTLS. Aby wziąć udział w warsztatach, nie musisz niczego instalować lokalnie.

Jeśli na komputerze masz zainstalowane cloud-sql-proxy i psql i chcesz sprawdzić bezpośrednie połączenie z laptopa:

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

(Jeśli pojawi się prośba o hasło, wpisz

$DB_PASSWORD

W przeciwnym razie możesz przejść bezpośrednio do kroku 3 poniżej).

3. Sprawdzanie obiektów tajnych obsługiwanych przez Terraform

Nigdy nie przechowuj haseł do baz danych, kluczy interfejsu API ani kluczy głównych Rails w postaci zwykłego tekstu w systemie Git ani w zmiennych środowiskowych kontenera. W nowoczesnej architekturze chmury do wstrzykiwania w czasie działania w modelu zero-trust używamy Google Cloud Secret Manager.

ℹ️ Terraform zrobił to za Ciebie w kroku 1:

  1. rails-master-key – do odszyfrowywania danych logowania Rails w czasie wykonywania.
  2. rails-db-password – kryptograficznie bezpieczne losowe hasło pasujące do użytkownika Cloud SQL.
  3. rails-admin-password – bezpieczne hasło dla początkowego administratora.

Sprawdź, czy obiekty tajne są obecne i aktywne w usłudze Secret Manager:

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

Lista aplikacji skonfigurowanych w konsoli usługi Google Cloud Secret Manager

🔑 Synchronizowanie master.key z laptopem (synchronizacja lokalna)

Ze względów bezpieczeństwa plik blog/config/master.key jest ignorowany przez Git, więc może go jeszcze nie być w Twoim lokalnym klonie. Uruchom ten przyjazny fragment kodu samonaprawczego, aby pobrać go z usługi Secret Manager, dzięki czemu lokalne narzędzia Rails będą mogły odszyfrować dane logowania:

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

🧪 Plac zabaw: chcesz utworzyć niestandardowy tajny klucz, aby sprawdzić, jak łatwo to zrobić? Secret Manager nie jest przeznaczony tylko dla Rails. Możesz utworzyć dowolny obiekt tajny za pomocą prostego polecenia interfejsu wiersza poleceń:

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

4. Weryfikowanie uprawnień do uzyskiwania dostępu do obiektów tajnych

Aby kontenery Cloud Run mogły montować te obiekty tajne jako zmienne środowiskowe lub punkty montowania woluminów podczas uruchamiania, wykonawcze konto usługi ($RUN_SA) musi mieć rolę roles/secretmanager.secretAccessor w przypadku każdego obiektu tajnego.

Terraform przyznał już to uprawnienie w kroku 1. Sprawdź powiązanie zasady uprawnień w 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"

Zobaczysz, że:

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

Porównanie bezpieczeństwa serwera proxy uwierzytelniania Cloud SQL

5. Automatyczna weryfikacja przed publikacją

Uruchom automatyczny pakiet testów z kroku 5, aby potwierdzić, że baza danych jest uruchomiona, wpisy tajne nie są puste, a uprawnienia są prawidłowe:

just workshop-eval 5

8. Krok 6. Wdróż 4 – Enterprise Multi-Container Sidecars (złoty standard)

Teraz tworzymy najlepszą architekturę referencyjną: Cloud Run z wieloma kontenerami.

1. Przywracanie standardu złota

Przywróć konfigurację repozytorium do stanu kanonicznego main:

just workshop-restore-gold

2. Sprawdzanie schematu produkcyjnego z 3 kontenerami

Otwórz plik blog/compose.prod.yaml i sprawdź architekturę:

Architektura produkcyjna Cloud Run z wieloma kontenerami

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)]

Wdrożenie produkcyjne uruchamia 3 skoordynowane kontenery korzystające z tej samej sieci lokalnej:

  1. web: obsługuje ruch HTTP na porcie 8080 za pomocą serwera Puma.
  2. worker: dedykowany kontener przetwarzania w tle z uruchomionym bundle exec rails solid_queue:start.
  3. cloudsql-proxy: oficjalny kontener dodatkowy serwera proxy uwierzytelniania Google Cloud SQL (gcr.io/cloud-sql-connectors/cloud-sql-proxy:2) powiązany z 127.0.0.1:5432.

3. Uruchamianie migracji baz danych za pomocą zadania Cloud Run

Zanim przekierujemy ruch internetowy do nowego wdrożenia, musimy przeprowadzić migrację bazy danych i początkowe wypełnienie bazy danych w Cloud SQL.

⚖️ Jak przeprowadzać migracje w kontenerach: 2 strategie

W środowiskach chmurowych opartych na kontenerach istnieją 2 powszechne sposoby przeprowadzania migracji baz danych:

  • Opcja A. Automatyczne w punkcie wejścia Dockera (entrypoint.sh wykonuje db:migrate podczas uruchamiania kontenera)
    • Zalety: w pełni zautomatyzowane; deweloperzy nie muszą pamiętać o uruchamianiu polecenia.
    • Wady: niebezpieczne w przypadku autoskalowania bez serwera. Gdy Cloud Run skaluje się do 10 instancji jednocześnie przy dużym natężeniu ruchu, 10 kontenerów konkuruje o równoczesne uruchomienie migracji, co powoduje blokowanie bazy danych, warunki wyścigu schematu lub przekroczenie limitu czasu rozruchu.
  • Opcja B. Jawne za pomocą zadania Cloud Run (nasz wybór architektury)
    • Zalety: pełna kontrola, bezpieczeństwo transakcji i brak warunków wyścigu. Działa jako dedykowane jednorazowe zadanie kontenera całkowicie odłączone od obsługi ruchu internetowego. Jeśli migracja się nie powiedzie, wyświetlanie reklam w internecie nie zostanie naruszone.
    • Wady: przed wdrożeniem wymaga uruchomienia zadania interfejsu wiersza poleceń lub kroku CI/CD.

Wybieramy opcję B, aby zapewnić maksymalną niezawodność produkcji.

⚠️ Krytyczny: DATABASE_URL Format & Ruby 3.4.5 RFC 3986 Strictness

Usługi Cloud Run Jobs łączą się z Cloud SQL za pomocą natywnego serwera proxy gniazda Unix (/cloudsql/PROJECT:REGION:INSTANCE). DATABASE_URL musi używać formatu z trzema ukośnikami:

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: aplikacja korzysta z 4 baz danych (głównej, kolejki, pamięci podręcznej i kablowej) na database.yml. Wszystkie 4 zmienne środowiskowe DATABASE_*_URL muszą wskazywać tę samą instancję Cloud SQL. W przeciwnym razie tabele Solid Queue/Cache/Cable nie zostaną utworzone.

Po db:prepare wczytaj schematy 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

Dane wyjściowe polecenia gcloud run jobs executions describe JOBNAMENa powyższym rysunku widać możliwe wyniki

gcloud run jobs executions describe $JOBNAME

Konsola zadań Google Cloud Run z informacją o zakończeniu zadania rails-migrateKonsola zadań Google Cloud Run

rails-migrate

wykonanie zakończone

4. Czwarte wdrożenie: wdrażanie Cloud Run z wieloma kontenerami za pomocą Docker Compose

Teraz wdróż prawdziwy 3-kontenerowy stos produkcyjny (web, worker, cloudsql-proxy) bezpośrednio z 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

Karta Kontenery w konsoli Google Cloud Run, na której widać 3 kontenery pomocniczeKonsola Google Cloud Run: karta „Kontenery” z wyświetlonymi 3 kontenerami pomocniczymi (

cloudsql-proxy

,

web

,

worker

)

💡 Ciekawostka dotycząca interfejsu / błąd konsoli: w menu konsoli możesz zauważyć, że Cloud Run oznacza pierwszy wpis jako Ingress container: cloudsql-proxy. Bez obaw, Jest to tylko błąd wizualny interfejsu w konsoli Google Cloud (śledzony w #149) spowodowany kolejnością zależności kontenera. W rzeczywistości tylko kontener web nasłuchuje na porcie HTTP 8080 i obsługuje 100% ruchu internetowego przychodzącego, a kontener cloudsql-proxy obsługuje wyłącznie prywatny ruch PostgreSQL na porcie 5432.

5. ✨ Wow Moment: zero ostrzeżeń i pełna trwałość w chmurze!

Weź głęboki oddech i spójrz na ekran: wszystkie banery ostrzegawcze zniknęły! 🪄

Blog o wdrażaniu w Cloud Run bez ostrzeżeń i trwałych postów w CloudBlog o produkcyjnej wersji Rails 8: przejrzysty interfejs, brak banerów z ostrzeżeniami i trwały stan Cloud SQL

Aplikacja jest teraz oficjalnie gotowa do wdrożenia w środowisku produkcyjnym i chmurowym:

  • Obsługiwane przez zarządzaną usługę Google Cloud SQL (PostgreSQL) za pomocą bezpiecznego tunelowania mTLS.
  • Media są bezpiecznie przechowywane w prywatnym Google Cloud Storage z podpisywaniem obiektów binarnych IAM.
  • Brak ostrzeżeń o zerowej ilości pamięci tymczasowej i brak alertów o zablokowanych zadaniach.

Otwórz adres URL Cloud Run:

  1. Sprawdź plakietki telemetrii:
    • [CLOUD PERSISTENT 🐘 ☁️] został zweryfikowany w Twoim pliku początkowym bazy danych.
    • W stopce oba oznaczenia Google Cloud Run i GCS świecą na zielono.
    • Baner z ostrzeżeniem o zablokowanych zadaniach zniknął.
  2. tworzyć posty na blogu i komentarze;
  3. Uruchamiaj ponownie i wdrażaj tyle razy, ile chcesz: Twoje dane, posty, komentarze i zasoby będą zawsze przechowywane w Cloud SQL i GCS.

6. Automatyczna weryfikacja kroku 6

Sprawdź konfigurację wielu kontenerów:

just workshop-eval 6

9. Krok 7. Potoki generatywnej AI, Podcastifier i poszukiwanie skarbów w GCS 🏴‍☠️

Dzięki Solid Queue działającemu w dedykowanym kontenerze i aktywnemu Google Cloud Storage możemy uwolnić asynchroniczną generatywną AI.

1. Generator okładek w stylu retro NanoBanana (gotowa do użycia platforma generatywnej AI)

Maskotka NanoBanana

Dobra wiadomość: ta funkcja jest już w pełni zaimplementowana w bazie kodu. Nie musisz pisać żadnego kodu – Twoim zadaniem jest uruchomienie i obserwowanie działania asynchronicznego potoku GenAI w chmurze.

🧠 Jak to działa za kulisami

  • Uwierzytelnianie: nie wymaga kluczy interfejsu API. Automatycznie używa domyślnego uwierzytelniania aplikacji (roles/aiplatform.user w Vertex AI) automatycznie za pomocą konta usługi środowiska wykonawczego Cloud Run. (Jeśli podano GEMINI_API_KEY, wywołuje bezpośrednio interfejs Gemini API).
  • Potok: gdy artykuł zostanie utworzony bez obrazu w tle, wywołanie zwrotne after_commit umieści w kolejce GenerateCoverImageJob w Solid Queue.
  • Model: model multimodalny Google (Gemini 2.5 Flash Image / Imagen w Vertex AI) generuje autentyczny plakat włoskiego filmu z lat 60. XX wieku („Locandina di un film 1960”) z bananem i błyszczącym rubinem w kształcie cyfry 8.
  • Dynamiczny znak wodny dotyczący pochodzenia: używa libvips do dynamicznego umieszczania wizualnej plakietki w rogu (kolorowa chmurka w GCS).
  • Szybka wymiana strumienia: gdy obraz jest załączony, Turbo::StreamsChannel.broadcast_refresh_to(post) aktualizuje przeglądarkę czytelnika na żywo bez ręcznego ponownego wczytywania strony.

Diagram sekwencji architektury asynchronicznej NanoBanana AI

📐 Wyświetl źródło diagramu sekwencji 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!

🧪 Wypróbuj na żywo

Możesz go wywołać na 2 sposoby:

  • Opcja A (interfejs internetowy): kliknij Nowy post, wpisz tytuł, np. „Architektura bezserwerowa z Ruby on Rails 8”, napisz treść i pozostaw pole wejściowe pliku obrazu okładki puste. Kliknij Utwórz posta.
  • Opcja B (CLI Runner): uruchom z terminala:
    bin/rails runner 'Post.create!(title: "Serverless Architecture with Ruby on Rails 8", body: "Exploring asynchronous AI pipelines with Solid Queue and NanoBanana!")'
    

Odczekaj około 15–30 sekund: obserwuj logi instancji roboczej Solid Queue w Cloud Run. Plakat w stylu retro pojawi się bezpośrednio w Twoim poście.

📸 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. Wyzwanie „Dwujęzyczny podcast” (praktyczne ćwiczenie z kodowania antygrawitacyjnego)

⚠️ Ważna uwaga: w przeciwieństwie do generatora okładek powyżej ta funkcja NIE jest jeszcze wdrożona!

Jeśli otworzysz blog/app/jobs/podcastifier_job.rb, zobaczysz pusty szkielet:

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

Twoim zadaniem w tym praktycznym ćwiczeniu jest współpraca z Google Antigravity w celu ukończenia tej funkcji:

  1. Prompt Antigravity:„Pomóż mi wdrożyć PodcastifierJob w app/jobs/podcastifier_job.rb. Powinien pobrać post, przetłumaczyć go lub wygenerować włoskie podsumowanie audio za pomocą usługi Google Cloud Text-to-Speech (głos „it-IT-Wavenet-A” z kodem języka „it-IT” za pomocą danych logowania ADC) i dołączyć plik audio do post.podcast_audio_it za pomocą ActiveStorage!
  2. Dodaj odtwarzacz do widoku: w app/views/posts/show.html.erb dodaj odtwarzacz HTML5 , gdy @post.podcast_audio_it.attached?.
  3. Wywołaj syntezę: po zapisaniu Solid Queue wykona zadanie zamiany tekstu na mowę asynchronicznie w tle.

Dwujęzyczny interfejs Podcastifier z 2 odtwarzaczami audio

🎧 Przykładowe wyniki działania narzędzia Podcastifier:

💡 Potrzebujesz pomocy lub masz problem? Kompletne, sprawdzone w praktyce rozwiązanie referencyjne jest dostępne w gałęzi solutions/podcastifier. W każdej chwili możesz sprawdzić tę informację, klikając git diff solutions/podcastifier.

3. [OPCJONALNIE] 🏴‍☠️ Poszukiwanie skarbów w GCS (odzyskiwanie obiektu w konsoli)

ℹ️ Sprawdzenie wymagań wstępnych: lokalne

cloud-sql-proxy

Wymagany interfejs CLI To opcjonalne ćwiczenie umożliwia bezpośrednie połączenie lokalnego laptopa z Google Cloud SQL za pomocą zaszyfrowanego tunelu mTLS. Sprawdź, czy masz zainstalowany serwer proxy interfejsu wiersza poleceń:

which cloud-sql-proxy
  • Jeśli jest zainstalowana: przejdź do dalszej części instrukcji.
  • Jeśli nie masz zainstalowanej aplikacji i chcesz wykonać to zadanie: zainstaluj ją, wykonując te czynności:
    # 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
    
  • W innym przypadku: możesz przejść do automatycznej weryfikacji w kroku 7.

Pamiętasz zdjęcie, które zostało przesłane w kroku 4, zanim ponowne uruchomienie kontenera usunęło tymczasową bazę danych SQLite? Ten plik obrazu nadal znajduje się bezpiecznie w Twoim prywatnym zasobniku GCS jako „osierocony obiekt blob”.

Użyjemy konsoli Rails, aby odzyskać ten plik i dołączyć go do posta w 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

W konsoli 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

Odśwież bloga: zdjęcie przesłane w kroku 4 w sekcji Stateless Shock zostanie przywrócone i na stałe dołączone do posta w Cloud SQL.

4. Automatyczna weryfikacja kroku 7

Weryfikacja zadań i komponentów generatywnej AI:

just workshop-eval 7

10. Krok 8. Wybierz własną przygodę / zaawansowane zadania 🏆

Teraz, gdy masz już opanowaną kanoniczną architekturę referencyjną, wybierz zadanie końcowe.

🛡️ Quest 1. Zero-Trust Google IAP (Identity-Aware Proxy)

  • Trudność: średnia (zabezpieczenia klasy korporacyjnej)
  • Cel: zabezpiecz aplikację Cloud Run, aby dostęp do niej miały tylko zweryfikowane konta Google lub Gmail, całkowicie eliminując logowanie za pomocą hasła.
  • Jak to działa:
    1. Zewnętrzny system równoważenia obciążenia aplikacji HTTPS kończy uwierzytelnianie Google OAuth i weryfikuje tożsamość, zanim ruch dotrze do Cloud Run.
    2. Google przekazuje nagłówki zweryfikowanej tożsamości (X-Goog-Authenticated-User-Email).
    3. Rails automatycznie loguje zweryfikowaną tożsamość Google za pomocą:blog/app/controllers/concerns/iap_authenticatable.rb
      module IapAuthenticatable
        extend ActiveSupport::Concern
        included { before_action :authenticate_via_iap }
      
        private
        def authenticate_via_iap
          return unless (raw = request.headers["X-Goog-Authenticated-User-Email"]).present?
          email = raw.sub(/^accounts\.google\.com:/, "")
          user = User.find_or_create_by!(email_address: email) { |u| u.password = SecureRandom.hex(16) }
          start_new_session_for(user) unless authenticated?
        end
      end
      
    4. Włącz moduł Terraform w iac/iap.tf za pomocą enable_iap = true i określ dozwolone konta Google:
      iap_allowed_users = [var.google_cloud_account]
      

Baner z informacją o rozpoznaniu logowania przez IAP

📊 Kurs 2. Telemetria SRE w środowisku produkcyjnym i Cloud Logging

  • Poziom trudności: średni (dostrzegalność)
  • Cel: przesyłanie strumieniowe logów aplikacji w formacie JSON z identyfikatorami korelacji śledzenia bezpośrednio do Cloud Logging i wykrywanie wyjątków w środowisku produkcyjnym w czasie rzeczywistym za pomocą Cloud Error Reporting.
  • Jak to działa:
    • Skonfiguruj blog/config/environments/production.rb, aby emitował uporządkowane dzienniki JSON z etykietami śledzenia GCP.
    • Wywołaj celowo wyjątek testowy i obserwuj, jak Raportowanie błędów w Google Cloud grupuje błędy i natychmiast Cię o nich powiadamia.

📸 TODO(riccardo): dodaj zrzut ekranu usługi Google Cloud Error Reporting pokazujący pogrupowane wyjątki produkcyjne i strukturalny ładunek JSON usługi Cloud Logging z etykietami śledzenia

🧠 Quest 3. pgvector Wyszukiwanie semantyczne i RAG w Gemini

  • Poziom trudności: trudny (projekt końcowy z zakresu generatywnej AI)
  • Cel: wyszukiwanie artykułów na podstawie koncepcji przy użyciu wektorów dystrybucyjnych przechowywanych w PostgreSQL w Cloud SQL.
  • Jak to działa:
    • Włącz rozszerzenie vector w instancji Cloud SQL PostgreSQL:
      CREATE EXTENSION IF NOT EXISTS vector;
      
    • Dodaj gniazdo neighbor do blog/Gemfile i wygeneruj wektory dystrybucyjne tekstu za pomocą Gemini (text-embedding-004) na Post#after_save.
    • Wykonywanie zapytań o odległość kosinusową (<=>) w celu obsługi paska wyszukiwania semantycznego za pomocą Turbo Streams.

📸 TODO(riccardo): dodać zrzut ekranu paska wyszukiwania semantycznego na blogu na żywo, który zwraca dopasowania koncepcyjne z wynikami podobieństwa kosinusowego

⚡ Quest 4. Asystent audytu SEO i wydajności

  • Trudność: łatwe / przyjemne (dla deweloperów)
  • Cel: przeprowadź audyt Core Web Vitals, największego wyrenderowania treści (LCP) oraz wskaźników czytelności Flescha-Kincaida i Foga za pomocą narzędzi Antigravity i 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

💡 Pytanie: na tradycyjnych maszynach wirtualnych lub platformach PaaS w stylu Heroku programiści często łączą się przez SSH z działającą instancją lub używają heroku run rails console, aby na żywo sprawdzać lub poprawiać dane produkcyjne. Jaki jest odpowiednik natywny Cloud Run i jak go zabezpieczyć?

Dlaczego

gcloud run exec

nie istnieje w przypadku usług Cloud Run

Cloud Run to platforma bezserwerowa: kontener może działać jako zero, jedna lub setki identycznych instancji bez stabilnej tożsamości ani adresu powłoki. Różni się to zasadniczo od Kubernetes (kubectl exec), gdzie konkretny pod ma nazwane gniazdo, do którego można się podłączyć.

# ❌ 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

4 wzorce natywne GCP

Wzór

Polecenie

Interaktywny?

Bezpieczeństwo

1. Lokalny serwer proxy + zdalna baza danych

DATABASE_URL=... rails console

✅ Pełna

🔒 Wzajemne uwierzytelnianie TLS serwera proxy uwierzytelniania Cloud SQL

2. Zadanie Cloud Run (jednorazowe)

gcloud run jobs execute --wait

❌ Tylko skrypt

🔒 Wymagana rola uprawnień

3. Gem konsoli sieciowej + zakupy w aplikacji

Przeglądarka → /rails/console

✅ Pełna

🛡️ IAP Zero-Trust

4. Cloud Shell + Proxy

Cloud Shell → bin/rails c

✅ Pełna

🔒 Tożsamość Google

Wzorzec 1 (zalecany do sprawdzania danych): uruchom lokalną konsolę Rails, która komunikuje się bezpośrednio z Cloud SQL PostgreSQL za pomocą serwera proxy uwierzytelniania 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

Zyskujesz pełną interaktywność IRB bez narażania sieci na zagrożenia – serwer proxy Cloud SQL używa mTLS przez gniazdo domeny Unix lub interfejs zwrotny.

Wzorzec 3 (konsola internetowa + IAP Zero-Trust, ścieżka wzmocnionej przeglądarki):

web-consoleGem zapewnia sesję IRB w przeglądarce. Bez ochrony jest to krytyczna luka w zabezpieczeniach – nigdy nie udostępniaj jej publicznie. W połączeniu z płatnościami w aplikacji Google (Quest 1) obszar ataku się zmniejsza: tylko zweryfikowane tożsamości Google na liście dozwolonych mogą uzyskać dostęp do punktu końcowego /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

⚠️ Wzorzec 3 wymaga wcześniejszego włączenia misji 1 (zakupy w aplikacji). Bez niego /rails/console na publicznym adresie URL jest jak pozostawienie serwerowni bez zabezpieczeń. Dzięki IAP staje się on potężnym narzędziem dla operatorów, zabezpieczonym przez Zero-Trust na poziomie Google.

Zapytaj Antigravity: „Pokaż mi, jak uruchomić

rails console --sandbox

w bezpieczny sposób w odniesieniu do mojej produkcyjnej bazy danych Cloud SQL za pomocą serwera proxy uwierzytelniania Cloud SQL."

🏆 Ukończenie The Hive: dowód pracy zweryfikowany przez nadzorującego

Po ukończeniu wybranego zadania odbierz trofeum za ukończenie kroku 8 🏆 na tablicy wyników The Hive:

  1. Prześlij zadanie w GitHubie:
    • Otwórz nowy problem w oficjalnym repozytorium: github.com/palladius/rails8-app-on-gcp/issues/new
    • Format tytułu: 🎓 [Step 8 Completed] :
    • W treści podaj krótki opis tego, co udało Ci się osiągnąć, oraz link do wdrożenia w Cloud Run.
  2. Połącz aplikację Cloud Run z problemem:
    • Zanotuj numer problemu (np. 88 lub pełny adres URL https://github.com/palladius/rails8-app-on-gcp/issues/88).
    • Wstrzyknij zmienną środowiskową STEP_8_GHI do usługi Cloud Run:
      gcloud run services update blog \
        --region europe-west1 \
        --update-env-vars STEP_8_GHI=<YOUR_ISSUE_NUMBER>
      
    • Sprawdź, czy punkt końcowy /status.json zgłasza teraz Twoje zadanie:
      curl -s https://<YOUR_APP>.run.app/status.json | jq .quest
      
      Powinno pojawić się:
      {
        "step_8_completed": true,
        "ghi_issue": 88,
        "ghi_url": "https://github.com/palladius/rails8-app-on-gcp/issues/88"
      }
      
  3. Uzyskaj weryfikację przez nadzorującego i odblokuj złote trofeum:
    • Sprawdź tablicę wyników The Hive: w Twoim wierszu pojawi się teraz bursztynowa plakietka: ⏳ GHI #XX review pending.
    • Poproś prowadzących warsztaty (@palladius, @emilianodellacasa lub @ricc) o sprawdzenie wdrożenia.
    • Gdy tylko egzaminator skomentuje Twój problem LGTM, The Hive automatycznie zmieni Twój postęp na 8/8 🏆, a przy Twoim profilu pojawi się złoto-fioletowa świecąca odznaka i trofeum z linkiem do Twojego problemu końcowego.

11. Podsumowanie i czyszczenie 🎓

Gratulacje! 🎉

Masz utworzoną i wdrożoną w Google Cloud Platform aplikację Rails 8 klasy produkcyjnej, gotową do użycia w firmie:

  • Zero-Branch Progression: opanowanie nowoczesnych przepływów pracy z nakładkami Time-Machine na main.
  • Cloud Storage: skalowalne, prywatne miejsce na obiekty z podpisywaniem obiektów binarnych IAM (iam: true).
  • Cloud SQL: zarządzana usługa PostgreSQL zabezpieczona tunelami mTLS serwera proxy uwierzytelniania Cloud SQL.
  • Secret Manager: brak wycieków danych logowania w postaci zwykłego tekstu lub plików .env.
  • Cloud Run Multi-Container: odizolowane kontenery pomocnicze Puma Web, Solid Queue Worker i proxy.
  • Generatywna AI: asynchroniczne generowanie plakatów w stylu vintage i synteza podcastów TTS za pomocą Vertex AI.
  • Quests: Zero-Trust IAP, SRE Observability, and pgvector embeddings!

🧹 Czyszczenie zasobów

Aby uniknąć dalszych opłat po zakończeniu warsztatów, pamiętaj o zwolnieniu miejsca na zasobach Google Cloud. Wybierz podejście, które najlepiej pasuje do Twojego środowiska:

Użyj tej opcji, jeśli korzystasz z istniejącego lub udostępnionego projektu w chmurze Google, w którym znajdują się inne zasoby lub dane. Usuwa tylko zasoby udostępnione podczas tych warsztatów (Cloud Run, Cloud SQL, Secret Manager, zasobniki GCS), pozostawiając resztę projektu bez zmian.

cd iac
terraform destroy -auto-approve

Opcja 2. Całkowite usunięcie projektu (bez śladu 🌪️)

Użyj tej opcji, jeśli utworzysz dedykowany projekt warsztatowy (np. korzystając z kredytów warsztatowych lub piaskownicy) i chcesz mieć pewność, że nie pozostanie po nim żaden ślad, w tym logi, metadane, konta usług i linki do rozliczeń.

gcloud projects delete $GOOGLE_CLOUD_PROJECT --quiet