1. Pengantar
Ringkasan
Dalam codelab ini, Anda akan mengonfigurasi Cloud Run untuk secara otomatis membangun dan men-deploy versi baru aplikasi setiap kali Anda mengirim perubahan kode sumber ke repositori GitHub.
Aplikasi demo ini menyimpan data pengguna ke firestore, tetapi hanya sebagian data yang disimpan dengan benar. Anda akan mengonfigurasi deployment berkelanjutan sedemikian rupa sehingga saat Anda mengirim perbaikan bug ke repositori GitHub, Anda akan otomatis melihat perbaikan tersebut tersedia di revisi baru.
Yang akan Anda pelajari
- Menulis aplikasi web Express dengan Cloud Shell Editor
- Hubungkan akun GitHub Anda ke Google Cloud untuk deployment berkelanjutan
- Men-deploy aplikasi Anda secara otomatis ke Cloud Run
- Pelajari cara menggunakan HTMX dan TailwindCSS
2. Penyiapan dan Persyaratan
Prasyarat
- Anda memiliki akun GitHub dan terbiasa membuat dan mengirim kode ke repositori.
- Anda sudah login ke Konsol Cloud.
- Anda sebelumnya telah men-deploy layanan Cloud Run. Misalnya, Anda dapat mengikuti men-deploy layanan web dari panduan memulai kode sumber untuk memulai.
Mengaktifkan Cloud Shell
- Dari Cloud Console, klik Aktifkan Cloud Shell .
Jika ini pertama kalinya Anda memulai Cloud Shell, Anda akan melihat layar perantara yang menjelaskan apa itu Cloud Shell. Jika Anda melihat layar perantara, klik Lanjutkan.
Perlu waktu beberapa saat untuk penyediaan dan terhubung ke Cloud Shell.
Mesin virtual ini dimuat dengan semua alat pengembangan yang diperlukan. Layanan ini menawarkan direktori beranda tetap sebesar 5 GB dan beroperasi di Google Cloud, sehingga sangat meningkatkan performa dan autentikasi jaringan. Sebagian besar pekerjaan Anda dalam codelab ini dapat dilakukan dengan browser.
Setelah terhubung ke Cloud Shell, Anda akan melihat bahwa Anda telah diautentikasi dan project sudah ditetapkan ke project ID Anda.
- Jalankan perintah berikut di Cloud Shell untuk mengonfirmasi bahwa Anda telah diautentikasi:
gcloud auth list
Output perintah
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- Jalankan perintah berikut di Cloud Shell untuk mengonfirmasi bahwa perintah gcloud mengetahui project Anda:
gcloud config list project
Output perintah
[core] project = <PROJECT_ID>
Jika tidak, Anda dapat menyetelnya dengan perintah ini:
gcloud config set project <PROJECT_ID>
Output perintah
Updated property [core/project].
3. Mengaktifkan API dan Menetapkan Variabel Lingkungan
Mengaktifkan API
Codelab ini memerlukan penggunaan API berikut. Anda dapat mengaktifkan API tersebut dengan menjalankan perintah berikut:
gcloud services enable run.googleapis.com \ cloudbuild.googleapis.com \ firestore.googleapis.com \ iamcredentials.googleapis.com
Menyiapkan variabel lingkungan
Anda dapat menetapkan variabel lingkungan yang akan digunakan di seluruh codelab ini.
REGION=<YOUR-REGION> PROJECT_ID=<YOUR-PROJECT-ID> PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)') SERVICE_ACCOUNT="firestore-accessor" SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com
4. Membuat akun layanan
Akun layanan ini akan digunakan oleh Cloud Run untuk memanggil Vertex AI Gemini API. Akun layanan ini juga akan memiliki izin untuk membaca dan menulis ke Firestore serta membaca secret dari Secret Manager.
Pertama, buat akun layanan dengan menjalankan perintah ini:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run access to Firestore"
Sekarang, beri akun layanan akses baca dan tulis ke Firestore.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/datastore.user
5. Membuat dan mengonfigurasi project Firebase
- Di Firebase console, klik Add project.
- Masukkan <YOUR_PROJECT_ID> untuk menambahkan Firebase ke salah satu project Google Cloud Anda yang sudah ada
- Jika diminta, tinjau dan setujui persyaratan Firebase.
- Klik Lanjutkan.
- Klik Confirm Plan untuk mengonfirmasi paket penagihan Firebase.
- Mengaktifkan Google Analytics di codelab ini bersifat opsional.
- Klik Add Firebase.
- Setelah project dibuat, klik Continue.
- Dari menu Build, klik Firestore database.
- Klik Buat database.
- Pilih wilayah dari drop-down Lokasi, lalu klik Berikutnya.
- Gunakan Mulai dalam mode produksi default, lalu klik Buat.
6. Menulis aplikasi
Pertama, buat direktori untuk kode sumber dan {i>cd<i} ke direktori tersebut.
mkdir cloud-run-github-cd-demo && cd $_
Lalu, buat file package.json
dengan konten berikut:
{ "name": "cloud-run-github-cd-demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node app.js", "nodemon": "nodemon app.js", "tailwind-dev": "npx tailwindcss -i ./input.css -o ./public/output.css --watch", "tailwind": "npx tailwindcss -i ./input.css -o ./public/output.css", "dev": "npm run tailwind && npm run nodemon" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@google-cloud/firestore": "^7.3.1", "axios": "^1.6.7", "express": "^4.18.2", "htmx.org": "^1.9.10" }, "devDependencies": { "nodemon": "^3.1.0", "tailwindcss": "^3.4.1" } }
Pertama, buat file sumber app.js
dengan konten di bawah ini. File ini berisi titik entri untuk layanan dan berisi logika utama untuk aplikasi.
const express = require("express"); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); const path = require("path"); const { get } = require("axios"); const { Firestore } = require("@google-cloud/firestore"); const firestoreDb = new Firestore(); const fs = require("fs"); const util = require("util"); const { spinnerSvg } = require("./spinnerSvg.js"); const service = process.env.K_SERVICE; const revision = process.env.K_REVISION; app.use(express.static("public")); app.get("/edit", async (req, res) => { res.send(`<form hx-post="/update" hx-target="this" hx-swap="outerHTML"> <div> <p> <label>Name</label> <input class="border-2" type="text" name="name" value="Cloud"> </p><p> <label>Town</label> <input class="border-2" type="text" name="town" value="Nibelheim"> </p> </div> <div class="flex items-center mr-[10px] mt-[10px]"> <button class="btn bg-blue-500 text-white px-4 py-2 rounded-lg text-center text-sm font-medium mr-[10px]">Submit</button> <button class="btn bg-gray-200 text-gray-800 px-4 py-2 rounded-lg text-center text-sm font-medium mr-[10px]" hx-get="cancel">Cancel</button> ${spinnerSvg} </div> </form>`); }); app.post("/update", async function (req, res) { let name = req.body.name; let town = req.body.town; const doc = firestoreDb.doc(`demo/${name}`); //TODO: fix this bug await doc.set({ name: name /* town: town */ }); res.send(`<div hx-target="this" hx-swap="outerHTML" hx-indicator="spinner"> <p> <div><label>Name</label>: ${name}</div> </p><p> <div><label>Town</label>: ${town}</div> </p> <button hx-get="/edit" class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]" > Click to update </button> </div>`); }); app.get("/cancel", (req, res) => { res.send(`<div hx-target="this" hx-swap="outerHTML"> <p> <div><label>Name</label>: Cloud</div> </p><p> <div><label>Town</label>: Nibelheim</div> </p> <div> <button hx-get="/edit" class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]" > Click to update </button> </div> </div>`); }); const port = parseInt(process.env.PORT) || 8080; app.listen(port, async () => { console.log(`booth demo: listening on port ${port}`); //serviceMetadata = helper(); }); app.get("/helper", async (req, res) => { let region = ""; let projectId = ""; let div = ""; try { // Fetch the token to make a GCF to GCF call const response1 = await get( "http://metadata.google.internal/computeMetadata/v1/project/project-id", { headers: { "Metadata-Flavor": "Google" } } ); // Fetch the token to make a GCF to GCF call const response2 = await get( "http://metadata.google.internal/computeMetadata/v1/instance/region", { headers: { "Metadata-Flavor": "Google" } } ); projectId = response1.data; let regionFull = response2.data; const index = regionFull.lastIndexOf("/"); region = regionFull.substring(index + 1); div = ` <div> This created the revision <code>${revision}</code> of the Cloud Run service <code>${service}</code> in <code>${region}</code> for project <code>${projectId}</code>. </div>`; } catch (ex) { // running locally div = `<div> This is running locally.</div>`; } res.send(div); });
Buat file dengan nama spinnerSvg.js
module.exports.spinnerSvg = `<svg id="spinner" alt="Loading..." class="htmx-indicator animate-spin -ml-1 mr-3 h-5 w-5 text-blue-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" > <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" ></circle> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" ></path> </svg>`;
Membuat file input.css
untuk tailwindCSS
@tailwind base; @tailwind components; @tailwind utilities;
Dan membuat file tailwind.config.js
untuk tailwindCSS
/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./**/*.{html,js}"], theme: { extend: {} }, plugins: [] };
Lalu, buat file .gitignore
.
node_modules/ npm-debug.log coverage/ package-lock.json .DS_Store
Sekarang, buat direktori public
baru.
mkdir public cd public
Dan dalam direktori publik tersebut, buat file index.html
untuk frontend, yang akan menggunakan htmx.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="https://unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous" ></script> <link href="./output.css" rel="stylesheet" /> <title>Demo 1</title> </head> <body class="font-sans bg-body-image bg-cover bg-center leading-relaxed" > <div class="container max-w-[700px] mt-[50px] ml-auto mr-auto"> <div class="hero flex items-center"> <div class="message text-base text-center mb-[24px]"> <h1 class="text-2xl font-bold mb-[10px]"> It's running! </h1> <div class="congrats text-base font-normal"> Congratulations, you successfully deployed your service to Cloud Run. </div> </div> </div> <div class="details mb-[20px]"> <p> <div hx-trigger="load" hx-get="/helper" hx-swap="innerHTML" hx-target="this">Hello</div> </p> </div> <p class="callout text-sm text-blue-700 font-bold pt-4 pr-6 pb-4 pl-10 leading-tight" > You can deploy any container to Cloud Run that listens for HTTP requests on the port defined by the <code>PORT</code> environment variable. Cloud Run will scale automatically based on requests and you never have to worry about infrastructure. </p> <h1 class="text-2xl font-bold mt-[40px] mb-[20px]"> Persistent Storage Example using Firestore </h1> <div hx-target="this" hx-swap="outerHTML"> <p> <div><label>Name</label>: Cloud</div> </p><p> <div><label>Town</label>: Nibelheim</div> </p> <div> <button hx-get="/edit" class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]" > Click to update </button> </div> </div> <h1 class="text-2xl font-bold mt-[40px] mb-[20px]"> What's next </h1> <p class="next text-base mt-4 mb-[20px]"> You can build this demo yourself! </p> <p class="cta"> <button class="bg-blue-500 text-white px-4 py-2 rounded-lg text-center text-sm font-medium" > VIEW CODELAB </button> </p> </div> </body> </html>
7. Menjalankan aplikasi secara lokal
Di bagian ini, Anda akan menjalankan aplikasi secara lokal untuk mengonfirmasi bahwa ada bug dalam aplikasi saat pengguna mencoba menyimpan data.
Pertama, Anda harus memiliki peran Datastore User untuk mengakses Firestore (jika menggunakan identitas Anda untuk autentikasi, misalnya Anda menjalankan Cloud Shell) atau Anda dapat meniru identitas akun pengguna yang dibuat sebelumnya.
Menggunakan ADC saat berjalan secara lokal
Jika menjalankan Cloud Shell, berarti Anda sudah menjalankannya di virtual machine Google Compute Engine. Kredensial Anda yang terkait dengan virtual machine ini (seperti yang ditunjukkan dengan menjalankan gcloud auth list
) akan otomatis digunakan oleh Application Default Credentials (ADC), sehingga Anda tidak perlu menggunakan perintah gcloud auth application-default login
. Namun, identitas Anda masih memerlukan peran Datastore User. Anda dapat langsung melanjutkan ke bagian Menjalankan aplikasi secara lokal.
Namun, jika menjalankan di terminal lokal (bukan di Cloud Shell), Anda harus menggunakan Kredensial Default Aplikasi untuk melakukan autentikasi ke Google API. Anda dapat 1) login menggunakan kredensial (asalkan Anda memiliki peran Pengguna Datastore), atau 2) Anda dapat login dengan meniru akun layanan yang digunakan dalam codelab ini.
Opsi 1) Menggunakan kredensial Anda untuk ADC
Jika ingin menggunakan kredensial, Anda dapat menjalankan gcloud auth list
terlebih dahulu untuk memverifikasi cara autentikasi Anda di gcloud. Selanjutnya, Anda mungkin perlu memberikan peran Vertex AI User ke identitas Anda. Jika identitas Anda memiliki peran Pemilik, berarti Anda sudah memiliki peran pengguna Datastore User ini. Jika tidak, Anda dapat menjalankan perintah ini untuk memberikan peran pengguna Vertex AI dan peran Datastore User kepada identitas Anda.
USER=<YOUR_PRINCIPAL_EMAIL> gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/datastore.user
Lalu jalankan perintah berikut
gcloud auth application-default login
Opsi 2) Meniru Identitas Akun Layanan untuk ADC
Jika Anda ingin menggunakan akun layanan yang dibuat di codelab ini, akun pengguna Anda harus memiliki peran Service Account Token Creator. Anda dapat memperoleh peran ini dengan menjalankan perintah berikut:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
Selanjutnya, Anda akan menjalankan perintah berikut untuk menggunakan ADC dengan akun layanan
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
Menjalankan aplikasi secara lokal
Selanjutnya, pastikan Anda berada di direktori utama cloud-run-github-cd-demo
untuk codelab Anda.
cd .. && pwd
Sekarang, Anda akan menginstal dependensi.
npm install
Terakhir, Anda dapat memulai aplikasi dengan menjalankan skrip berikut. Skrip ini juga akan menghasilkan file output.css dari tailwindCSS.
npm run dev
Sekarang buka {i>browser<i} web Anda ke http://localhost:8080. Jika berada di Cloud Shell, Anda dapat membuka situs dengan membuka tombol Web Preview dan memilih Preview Port 8080.
Masukkan teks untuk kolom input nama dan kota, lalu tekan simpan. Lalu muat ulang halaman. Anda akan melihat bahwa {i>field<i} kota tidak ada lagi. Anda akan memperbaiki bug ini di bagian berikutnya.
Menghentikan aplikasi Express agar tidak berjalan secara lokal (misalnya, Ctrl^c di MacOS).
8. Membuat Repositori GitHub
Di direktori lokal Anda, buat repositori baru dengan main sebagai nama cabang default.
git init git branch -M main
Commit codebase saat ini yang berisi bug. Anda akan memperbaiki bug setelah deployment berkelanjutan dikonfigurasi.
git add . git commit -m "first commit for express application"
Buka GitHub dan buat repositori kosong yang bersifat pribadi untuk Anda atau publik. Codelab ini merekomendasikan penamaan repositori Anda cloud-run-auto-deploy-codelab
Untuk membuat repositori kosong, Anda akan membiarkan semua setelan default tidak dicentang atau tidak disetel ke tidak ada sehingga tidak ada konten yang akan ada di repo secara default saat dibuat, misalnya
Jika Anda menyelesaikan langkah ini dengan benar, Anda akan melihat petunjuk berikut di halaman repositori yang kosong:
Anda akan mengikuti petunjuk push repositori yang ada dari command line dengan menjalankan perintah berikut:
Pertama, tambahkan repositori jarak jauh dengan menjalankan
git remote add origin <YOUR-REPO-URL-PER-GITHUB-INSTRUCTIONS>
lalu mengirim cabang utama ke repositori upstream.
git push -u origin main
9. Siapkan Deployment Berkelanjutan
Setelah memiliki kode di GitHub, Anda dapat menyiapkan deployment berkelanjutan. Buka Cloud Console untuk Cloud Run.
- Klik Create a Service
- Klik Continuously deploy from a repository
- Klik SIAPKAN CLOUD BUILD.
- Di bagian Repositori sumber
- Pilih GitHub sebagai Penyedia Repositori
- Klik Manage Connected repository untuk mengonfigurasi akses Cloud Build ke repo
- Pilih repositori Anda, lalu klik Next
- Di bagian Konfigurasi Build
- Keluar dari Cabang sebagai ^main$
- Untuk Build Type, pilih Go, Node.js, Python, Java, .NET Core, Ruby, atau PHP melalui buildpack Google Cloud
- Biarkan direktori konteks Build sebagai
/
- Klik Simpan
- Pada Autentikasi
- Klik Allow unauthenticated invocations
- Di bagian Penampung, Volume, Jaringan, Keamanan
- Pada tab Keamanan, pilih akun layanan yang Anda buat di langkah sebelumnya, misalnya
Cloud Run access to Firestore
- Pada tab Keamanan, pilih akun layanan yang Anda buat di langkah sebelumnya, misalnya
- Klik BUAT
Tindakan ini akan men-deploy layanan Cloud Run berisi bug yang akan Anda perbaiki di bagian berikutnya.
10. Memperbaiki bug
Memperbaiki bug dalam kode
Di Cloud Shell Editor, buka file app.js
dan buka komentar yang bertuliskan //TODO: fix this bug
ubah baris berikut dari
//TODO: fix this bug await doc.set({ name: name });
hingga
//fixed town bug await doc.set({ name: name, town: town });
Verifikasi perbaikan dengan menjalankan
npm run start
dan membuka {i>browser<i} web. Simpan data lagi untuk kota, dan muat ulang. Anda akan melihat data kota yang baru dimasukkan telah dipertahankan dengan benar saat dimuat ulang.
Setelah memverifikasi perbaikan, Anda siap untuk men-deploy-nya. Pertama, commit perbaikan.
git add . git commit -m "fixed town bug"
dan kemudian mengirimkannya ke repositori upstream di GitHub.
git push origin main
Cloud Build akan men-deploy perubahan Anda secara otomatis. Anda dapat membuka Konsol Cloud untuk layanan Cloud Run guna memantau perubahan deployment.
Memverifikasi perbaikan dalam produksi
Setelah Konsol Cloud untuk layanan Cloud Run Anda menampilkan revisi ke-2 kini melayani 100% traffic, misalnya Dengan https://console.cloud.google.com/run/detail/<YOUR_REGION>/<YOUR_SERVICE_NAME>/revisions, Anda dapat membuka URL layanan Cloud Run di browser dan memastikan bahwa data kota yang baru dimasukkan tetap ada setelah memuat ulang halaman.
11. Selamat!
Selamat, Anda telah menyelesaikan codelab!
Sebaiknya tinjau dokumentasi Cloud Run dan deployment berkelanjutan dari git.
Yang telah kita bahas
- Menulis aplikasi web Express dengan Cloud Shell Editor
- Hubungkan akun GitHub Anda ke Google Cloud untuk deployment berkelanjutan
- Men-deploy aplikasi Anda secara otomatis ke Cloud Run
- Pelajari cara menggunakan HTMX dan TailwindCSS
12. Pembersihan
Untuk menghindari tagihan yang tidak disengaja, (misalnya, jika layanan Cloud Run secara tidak sengaja dipanggil lebih sering daripada alokasi panggilan Cloud Run bulanan Anda di paket gratis), Anda dapat menghapus Cloud Run atau menghapus project yang Anda buat di Langkah 2.
Untuk menghapus layanan Cloud Run, buka Konsol Cloud Cloud Run di https://console.cloud.google.com/run dan hapus layanan Cloud Run yang Anda buat di codelab ini, misalnya menghapus layanan cloud-run-auto-deploy-codelab
.
Jika memilih untuk menghapus seluruh project, Anda dapat membuka https://console.cloud.google.com/cloud-resource-manager, pilih project yang dibuat pada Langkah 2, lalu pilih Hapus. Jika project dihapus, Anda harus mengubah project di Cloud SDK. Anda dapat melihat daftar semua project yang tersedia dengan menjalankan gcloud projects list
.