Workshop Mod Aplikasi

1. Pengantar

Terakhir Diperbarui: 01-11-2024

Bagaimana cara memodernisasi aplikasi PHP lama ke Google Cloud?

(📽️ tonton video pengantar 7 menit untuk codelab ini)

Aplikasi lama yang berjalan di lokal dan perlu dimodernisasi adalah hal yang umum. Hal ini berarti menjadikannya skalabel, aman, dan dapat di-deploy di berbagai lingkungan.

Dalam workshop ini, Anda akan:

  1. Masukkan Aplikasi PHP ke dalam container.
  2. Beralih ke Layanan Database Terkelola ( Cloud SQL).
  3. Deploy ke Cloud Run (alternatif tanpa operasi untuk GKE/Kubernetes).
  4. Amankan Aplikasi dengan Identity and Access Management (IAM) dan Secret Manager.
  5. Tentukan pipeline CI/CD melalui Cloud Build. Cloud Build dapat dihubungkan dengan repositori Git Anda yang dihosting di penyedia Git populer seperti GitHub atau GitLab, dan dipicu pada setiap push ke main, misalnya.
  6. Hosting gambar aplikasi di Cloud Storage. Hal ini dicapai melalui pemasangan, dan tidak ada kode yang diperlukan untuk mengubah aplikasi.
  7. Memperkenalkan fungsi AI Generatif melalui Gemini, yang diatur melalui Cloud Functions (serverless).
  8. Pahami SLO dan cara mengoperasikan aplikasi yang baru diperbarui.

Dengan mengikuti langkah-langkah ini, Anda dapat memodernisasi aplikasi PHP secara bertahap, sehingga meningkatkan skalabilitas, keamanan, dan fleksibilitas deployment-nya. Selain itu, dengan beralih ke Google Cloud, Anda dapat memanfaatkan infrastruktur dan layanannya yang canggih untuk memastikan aplikasi Anda berjalan lancar di lingkungan berbasis cloud.

Kami yakin bahwa apa yang akan Anda pelajari dengan mengikuti langkah-langkah sederhana ini dapat diterapkan untuk aplikasi dan organisasi Anda sendiri dengan bahasa/stack dan kasus penggunaan yang berbeda.

Tentang Aplikasi

Aplikasi ( code, dengan lisensi MIT) yang akan Anda fork adalah aplikasi PHP 5.7 dasar dengan autentikasi MySQL. Ide utama Aplikasi ini adalah menyediakan platform tempat pengguna dapat mengupload foto, dan administrator memiliki kemampuan untuk memberi tag pada gambar yang tidak pantas. Aplikasi ini memiliki dua tabel:

  • Pengguna. Sudah dikompilasi sebelumnya dengan admin. Orang baru dapat mendaftar.
  • Images. Dilengkapi dengan beberapa contoh gambar. Pengguna yang login dapat mengupload gambar baru. Kita akan menambahkan beberapa keajaiban di sini.

Sasaran Anda

Kita ingin memodernisasi aplikasi lama agar dapat diakses di Google Cloud. Kami akan memanfaatkan alat dan layanannya untuk meningkatkan skalabilitas, meningkatkan keamanan, mengotomatiskan pengelolaan infrastruktur, dan mengintegrasikan fitur canggih seperti pemrosesan gambar, pemantauan, dan penyimpanan data menggunakan layanan seperti Cloud SQL, Cloud Run, Cloud Build, Secret Manager, dan lainnya.

445f7a9ae37e9b4d.png

Yang lebih penting, kami ingin melakukannya langkah demi langkah agar Anda dapat mempelajari proses pemikiran di balik setiap langkah, dan biasanya setiap langkah membuka kemungkinan baru untuk langkah berikutnya (contoh: modul 2 -> 3, dan 6 -> 7).

Belum yakin? Tonton video berdurasi 7 menit ini di YouTube.

Yang Anda butuhkan

  • Komputer dengan browser, yang terhubung ke internet.
  • Beberapa kredit GCP. Lihat langkah berikutnya untuk melakukannya.
  • Anda akan menggunakan Cloud Shell. Alat ini dilengkapi dengan semua perintah bawaan yang Anda butuhkan dan IDE.
  • Akun GitHub. Anda memerlukan ini untuk membuat cabang kode asli 🧑🏻‍💻 gdgpescara/app-mod-workshop dengan repo git Anda sendiri. Hal ini diperlukan untuk memiliki pipeline CI/CD Anda sendiri (commit otomatis -> build -> deploy)

Contoh solusi dapat ditemukan di sini:

Workshop ini dirancang untuk diselesaikan di Cloud Shell (di browser).

Namun, upaya ini juga dapat dilakukan dari komputer lokal Anda.

2. Penyiapan Kredit dan Fork

6dafc658860c0ce5.png

Tukarkan kredit GCP dan siapkan lingkungan GCP Anda [opsional]

Untuk menjalankan workshop ini, Anda memerlukan Akun Penagihan dengan sejumlah kredit. Jika sudah memiliki penagihan sendiri, Anda dapat melewati langkah ini.

Buat akun Gmail Google yang baru (*) untuk ditautkan ke kredit GCP Anda. Minta instruktur Anda untuk memberikan link guna menukarkan kredit GCP atau gunakan kredit di sini: bit.ly/PHP-Amarcord-credits .

Login dengan akun yang baru dibuat dan ikuti petunjuknya.

ff739240dbd84a30.png

(

) Mengapa saya memerlukan akun Gmail baru?*

Kami melihat orang gagal menyelesaikan codelab karena akun mereka (terutama email kerja atau siswa) sebelumnya pernah menggunakan GCP dan memiliki Kebijakan organisasi yang membatasi kemampuan mereka untuk melakukannya. Sebaiknya buat akun Gmail baru atau gunakan akun Gmail yang sudah ada (gmail.com) yang belum pernah menggunakan GCP.

Klik tombol untuk menukarkan kredit.

331658dc50213403.png

Isi formulir berikut dengan Nama dan Nama Belakang Anda, lalu setujui Persyaratan dan Ketentuan.

Anda mungkin harus menunggu beberapa detik sebelum Akun Penagihan muncul di sini: https://console.cloud.google.com/billing

Setelah selesai, buka Konsol Google Cloud dan buat project baru dengan mengklik Pemilih Project di menu dropdown kiri atas tempat "No organization" ditampilkan. Lihat di bawah

bd7548f78689db0b.png

Buat project baru jika Anda belum memilikinya seperti yang ditunjukkan pada screenshot di bawah. Ada opsi "PROJECT BARU" di pojok kanan atas.

6c82aebcb9f5cd47.png

Pastikan untuk menautkan project baru dengan akun penagihan uji coba GCP sebagai berikut.

f202527d254893fb.png

Anda sudah siap menggunakan Google Cloud Platform. Jika Anda seorang pemula atau hanya ingin melakukan semuanya di lingkungan Cloud, Anda dapat mengakses Cloud Shell dan editornya melalui tombol berikut di sudut kiri atas seperti yang ditunjukkan di bawah.

7d732d7bf0deb12e.png

Pastikan project baru Anda dipilih di kiri atas:

Tidak dipilih (buruk):

c2ffd36a781b276a.png

Dipilih (baik):

594563c158f4f590.png

Fork Aplikasi dari GitHub

  1. Buka aplikasi demo: https://github.com/gdgpescara/app-mod-workshop
  2. Klik 🍴 fork.
  3. Jika Anda belum memiliki akun github, Anda harus membuat akun baru.
  4. Edit konten sesuai keinginan Anda.

734e51bfc29ee5df.png

  1. Buat clone kode Aplikasi menggunakan
  2. git clone https://github.com/YOUR-GITHUB-USER/YOUR-REPO-NAME
  1. Buka folder project yang di-clone dengan editor favorit Anda. Jika memilih Cloud Shell, Anda dapat melakukannya dengan mengklik "Open Editor" seperti yang ditunjukkan di bawah.

40f5977ea4c1d1cb.png

Anda memiliki semua yang diperlukan dengan Editor Google Cloud Shell seperti yang ditunjukkan pada gambar berikut

a4e5ffb3e9a35e84.png

Hal ini dapat dilakukan secara visual dengan mengklik "Open Folder" dan memilih folder, kemungkinan app-mod-workshop di folder beranda Anda.

3. Modul 1: Membuat Instance SQL

645902e511a432a6.png

Buat Instance Google Cloud SQL

Aplikasi PHP kita akan terhubung ke database MySQL, sehingga kita perlu mereplikasinya ke Google Cloud untuk migrasi yang lancar. Cloud SQL adalah pilihan yang tepat karena memungkinkan Anda menjalankan database MySQL yang terkelola sepenuhnya di Cloud. Berikut langkah-langkah yang harus diikuti:

  1. Buka halaman Cloud SQL: https://console.cloud.google.com/sql/instances
  2. Klik "Create Instance"
  3. Aktifkan API (jika diperlukan). Proses ini mungkin memerlukan waktu beberapa detik.
  4. Pilih MySQL.
  5. (Kami mencoba mendapatkan versi termurah untuk Anda, sehingga baterainya lebih tahan lama):
  • Edisi: Enterprise
  • Preset: development (kami mencoba Sandbox dan tidak berhasil)
  • Mysql Ver: 5.7 (wow, ini seperti kembali ke masa lalu!)
  1. ID instance: pilih appmod-phpapp (jika Anda mengubahnya, jangan lupa untuk mengubah juga skrip dan solusi mendatang yang sesuai).
  2. Sandi: apa pun yang Anda inginkan, tetapi catat sebagai CLOUDSQL_INSTANCE_PASSWORD
  3. Region: tetap sama dengan yang Anda pilih untuk aplikasi lainnya (misalnya, Milan = europe-west8)
  4. Ketersediaan zona: Zona Tunggal (kita menghemat uang untuk demo)

Klik tombol Create Instance untuk men-deploy database Cloud SQL; ⌛ perlu waktu sekitar 10 menit untuk menyelesaikannya⌛. Sementara itu, lanjutkan membaca dokumentasi; Anda juga dapat mulai menyelesaikan modul berikutnya ("Containerize your PHP App") karena tidak memiliki dependensi pada modul ini di bagian pertama (hingga Anda memperbaiki koneksi DB).

Catatan. Instance ini akan dikenai biaya sekitar 7 USD/hari. Pastikan untuk mengembangkannya setelah workshop.

Membuat DB dan Pengguna image_catalog di Cloud SQL

Project Aplikasi dilengkapi dengan folder db/ yang berisi dua file sql:

  1. 01_schema.sql : Berisi kode SQL untuk membuat dua tabel yang berisi data Pengguna dan Gambar.
  2. 02_seed.sql: Berisi kode SQL untuk mengisi data ke dalam tabel yang dibuat sebelumnya.

File ini akan digunakan nanti setelah database image_catalog dibuat. Anda dapat melakukannya dengan melakukan langkah-langkah berikut:

  1. Buka instance Anda dan Klik tab Databases:
  2. klik "Buat Database"
  3. panggil image_catalog (seperti pada konfigurasi aplikasi PHP).

997ef853e5ebd857.png

Kemudian, kita membuat pengguna database. Dengan ini, kita dapat melakukan autentikasi ke database image_catalog.

  1. Sekarang klik tab Pengguna
  2. Klik "Tambahkan akun pengguna".
  3. Pengguna: mari buat satu:
  • Nama pengguna: appmod-phpapp-user
  • Sandi: Pilih sandi yang dapat Anda ingat, atau klik "buat"
  • Pertahankan "Izinkan semua host (%)".
  1. klik TAMBAHKAN.

Buka DB ke IP yang sudah dikenal.

Perhatikan bahwa semua DB di Cloud SQL dibuat 'terisolasi'. Anda harus secara eksplisit menyiapkan jaringan yang dapat diakses dari.

  1. Klik instance Anda
  2. Buka Menu "Koneksi"
  3. Klik tab "Networking".
  4. Klik di bagian "Authorized networks". Sekarang tambahkan jaringan (yaitu, subnet).
  • Untuk saat ini, mari kita pilih setelan yang cepat namun TIDAK AMAN agar Aplikasi dapat berfungsi - Anda dapat membatasi aksesnya ke IP yang Anda percayai nanti:
  • Nama: "Semua orang di dunia - TIDAK AMAN".
  • Jaringan: "0.0.0.0/0" (Catatan: ini adalah bagian yang TIDAK AMAN!)
  • Klik SELESAI
  1. Klik simpan.

Anda akan melihat yang seperti ini:

5ccb9062a7071964.png

Catatan. Solusi ini adalah kompromi yang baik untuk menyelesaikan workshop dalam O(jam). Namun, lihat dokumen SECURITY untuk membantu mengamankan solusi Anda untuk produksi.

Saatnya menguji koneksi DB!

Mari kita lihat apakah pengguna image_catalog yang telah kita buat sebelumnya berfungsi.

Akses "Cloud SQL Studio" di dalam instance dan masukkan Database, Pengguna, dan Sandi yang akan diautentikasi seperti yang ditunjukkan di bawah:

d56765c6154c11a4.png

Setelah masuk, Anda dapat membuka Editor SQL dan melanjutkan ke bagian berikutnya.

Mengimpor Database dari codebase

Gunakan Editor SQL untuk mengimpor tabel image_catalog beserta datanya. Salin kode SQL dari file di repo ( 01_schema.sql, lalu 02_seed.sql) dan jalankan satu per satu secara berurutan.

Setelah itu, Anda akan mendapatkan dua tabel di image_catalog, yaitu users dan images seperti yang ditunjukkan di bawah:

65ba01e4c6c2dac0.png

Anda dapat mengujinya dengan menjalankan perintah berikut di editor: select * from images;

Pastikan juga untuk mencatat alamat IP Publik instance Cloud SQL, karena Anda akan memerlukannya nanti. Untuk mendapatkan IP, buka halaman utama instance Cloud SQL di halaman Overview. (Overview > Connect to this Instance > Public IP Address).

4. Modul 2: Masukkan Aplikasi PHP Anda ke dalam Container

e7f0e9979d8805f5.png

Kita ingin membangun aplikasi ini untuk cloud.

Artinya, mengemas kode dalam semacam file ZIP yang berisi semua info untuk menjalankannya di Cloud.

Ada beberapa cara untuk mengemasnya:

  • Docker. Sangat populer, tetapi cukup rumit untuk disiapkan dengan benar.
  • Buildpack. Kurang populer, tetapi cenderung 'menebak otomatis' apa yang akan dibangun dan apa yang akan dijalankan. Sering kali, fitur ini berfungsi dengan baik.

Dalam konteks workshop ini, kami akan menganggap Anda menggunakan Docker.

Jika Anda memilih untuk menggunakan Cloud Shell, sekarang saatnya membukanya kembali (klik di kanan atas konsol cloud).

ec6a6b90b39e03e.png

Tindakan ini akan membuka shell yang mudah di bagian bawah halaman, tempat Anda seharusnya sudah membuat fork kode di langkah penyiapan.

6999b906c0dedeb7.png

Docker

Jika Anda ingin memiliki kontrol, ini adalah solusi yang tepat untuk Anda. Hal ini masuk akal jika Anda perlu mengonfigurasi library tertentu, dan menyuntikkan perilaku tertentu yang tidak jelas (chmod dalam upload, file yang dapat dieksekusi non-standar di aplikasi Anda, ..)

Karena kita ingin men-deploy aplikasi dalam container ke Cloud Run, lihat dokumentasi berikut. Bagaimana cara mem-back port-nya dari PHP 8 ke PHP 5.7? Mungkin Anda bisa menggunakan Gemini untuk melakukannya. Sebagai alternatif, Anda dapat menggunakan versi yang sudah di-bake ini:

# Use the official PHP image: https://hub.docker.com/_/php
FROM php:5.6-apache

# Configure PHP for Cloud Run.
# Precompile PHP code with opcache.
# Install PHP's extension for MySQL
RUN docker-php-ext-install -j "$(nproc)" opcache mysqli pdo pdo_mysql && docker-php-ext-enable pdo_mysql

RUN set -ex; \
  { \
    echo "; Cloud Run enforces memory & timeouts"; \
    echo "memory_limit = -1"; \
    echo "max_execution_time = 0"; \
    echo "; File upload at Cloud Run network limit"; \
    echo "upload_max_filesize = 32M"; \
    echo "post_max_size = 32M"; \
    echo "; Configure Opcache for Containers"; \
    echo "opcache.enable = On"; \
    echo "opcache.validate_timestamps = Off"; \
    echo "; Configure Opcache Memory (Application-specific)"; \
    echo "opcache.memory_consumption = 32"; \
  } > "$PHP_INI_DIR/conf.d/cloud-run.ini"

# Copy in custom code from the host machine.
WORKDIR /var/www/html

COPY . .

# Setup the PORT environment variable in Apache configuration files: https://cloud.google.com/run/docs/reference/container-contract#port
ENV PORT=8080

# Tell Apache to use 8080 instead of 80.
RUN sed -i 's/80/${PORT}/g' /etc/apache2/sites-available/000-default.conf /etc/apache2/ports.conf

# Note: This is quite insecure and opens security breaches. See last chapter for hardening ideas.
# Uncomment at your own risk:
#RUN chmod 777 /var/www/html/uploads/

# Configure PHP for development.
# Switch to the production php.ini for production operations.
# RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# https://github.com/docker-library/docs/blob/master/php/README.md#configuration
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"

# Expose the port
EXPOSE 8080

Versi Dockerfile terbaru tersedia di sini.

Untuk menguji aplikasi secara lokal, kita perlu mengubah file config.php agar Aplikasi PHP kita terhubung dengan database MYSQL yang tersedia di Google CloudSQL. Berdasarkan penyiapan yang telah Anda lakukan sebelumnya, isi bagian yang kosong:

<?php
// Database configuration
$db_host = '____________';
$db_name = '____________';
$db_user = '____________';
$db_pass = '____________';

try {
    $pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    die("Errore di connessione: " . $e->getMessage());
}

session_start();
?>
  • DB_HOST adalah alamat IP publik Cloud SQL, Anda dapat menemukannya di konsol SQL:

bd27071bf450a8d0.png

  • DB_NAME harus tetap sama: image_catalog
  • DB_USER harus appmod-phpapp-user
  • DB_PASS adalah sesuatu yang Anda pilih. Siapkan dalam tanda kutip tunggal dan escape sesuai kebutuhan.

Selain itu, jangan ragu untuk menerjemahkan beberapa bagian berbahasa Italia 🇮🇹 ke dalam bahasa Inggris dengan bantuan Gemini.

Oke, setelah Anda memiliki Dockerfile dan telah mengonfigurasi Aplikasi PHP untuk terhubung ke DB, mari kita coba!

Instal docker jika Anda belum memilikinya ( link). Anda tidak memerlukan langkah ini jika menggunakan Cloud Shell (keren, bukan?).

Sekarang, coba bangun dan jalankan Aplikasi PHP dalam Container Anda dengan perintah build dan jalankan docker yang sesuai.

# Build command - don't forget the final . This works if Dockerfile is inside the code folder:
$ docker build -t my-php-app-docker .   

# Local Run command: most likely ports will be 8080:8080
$ docker run -it -p <CONTAINER_PORT>:<LOCAL_MACHINE_PORT> my-php-app-docker

Jika semuanya berfungsi, Anda akan dapat melihat halaman web berikut saat terhubung ke host lokal. Sekarang aplikasi Anda berjalan di port 8080, klik ikon "Web preview" (browser dengan mata), lalu Preview on port 8080 (atau "Change port" untuk port lain)

33a24673f4550454.png

Menguji hasil di browser Anda

Sekarang aplikasi Anda akan terlihat seperti ini:

2718ece96b1f18b6.png

Jika Anda login dengan Admin/admin123, Anda akan melihat sesuatu seperti ini.

68b62048c2e86aea.png

Hebat!!! Selain teks Italia, semuanya berfungsi! 🎉🎉🎉

Jika dockerisasi Anda sudah benar, tetapi kredensial DB salah, Anda mungkin akan mendapatkan pesan seperti ini:

e22f45b79bab86e1.png

Coba lagi, Anda hampir berhasil!

Menyimpan ke Artifact Registry [opsional]

Sekarang, Anda seharusnya sudah memiliki aplikasi PHP dalam container yang berfungsi dan siap di-deploy ke cloud. Selanjutnya, kita memerlukan tempat di cloud untuk menyimpan image Docker dan membuatnya dapat diakses untuk deployment ke layanan Google Cloud seperti Cloud Run. Solusi penyimpanan ini disebut Artifact Registry, layanan Google Cloud yang terkelola sepenuhnya dan dirancang untuk menyimpan artefak aplikasi, termasuk image container Docker, paket Maven, modul npm, dan lainnya.

Mari kita buat repositori di Google Cloud Artifact Registry menggunakan tombol yang sesuai.

e1123f0c924022e6.png

Pilih nama yang valid, format, dan region yang sesuai untuk menyimpan artefak.

4e516ed209c470ee.png

Kembali ke tag lingkungan pengembangan lokal Anda dan kirim image container App ke repositori Artifact Registry yang baru saja dibuat. Selesaikan perintah berikut untuk melakukannya.

  • docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
  • docker push TARGET_IMAGE[:TAG]

Hasilnya akan terlihat seperti screenshot berikut.

1e498feb4e88be9f.png

Selamat 🎉🎉🎉 Anda dapat melanjutkan ke level berikutnya. Sebelum itu, mungkin luangkan waktu 2 menit untuk mencoba mengupload/login/logout dan memahami endpoint aplikasi.Anda akan memerlukannya nanti.

Kemungkinan error

Jika Anda mengalami error penampungan, coba gunakan Gemini untuk menjelaskan dan memperbaiki error tersebut, dengan memberikan:

  • Dockerfile Anda saat ini
  • Error yang diterima
  • [jika diperlukan] kode PHP yang sedang dieksekusi.

Izin Upload. Coba juga endpoint /upload.php dan coba upload gambar. Anda mungkin mendapatkan error di bawah. Jika ya, Anda harus melakukan beberapa perbaikan chmod/chown di Dockerfile.

Peringatan: move_uploaded_file(uploads/image (3).png): gagal membuka stream: Permission denied in /var/www/html/upload.php on line 11

PDOException "could not find driver" (atau "Errore di connessione: could not find driver"). Pastikan Dockerfile Anda memiliki library PDO yang tepat untuk mysql (pdo_mysql), untuk terhubung ke DB. Dapatkan inspirasi dari solusi di sini.

Tidak dapat meneruskan permintaan Anda ke backend. Tidak dapat terhubung ke server di port 8080. Artinya, Anda mungkin mengekspos port yang salah. Pastikan Anda mengekspos port tempat Apache/Nginx benar-benar melayani. Hal ini tidak sepele. Jika memungkinkan, coba jadikan port tersebut 8080 (memudahkan penggunaan Cloud Run). Jika Anda ingin mempertahankan port 80 (misalnya, karena Apache menginginkannya), gunakan perintah lain untuk menjalankannya:

$ docker run -it -p 8080:80 # force 80

# Use the PORT environment variable in Apache configuration files.

# https://cloud.google.com/run/docs/reference/container-contract#port

RUN sed -i 's/80/${PORT}/g' /etc/apache2/sites-available/000-default.conf /etc/apache2/ports.conf

5. Modul 3: Men-deploy Aplikasi ke Cloud Run

9ffca42774f6c5d1.png

Mengapa Cloud Run?

Pertanyaan yang adil. Beberapa tahun lalu, Anda pasti akan memilih Google App Engine.

Sederhananya, saat ini Cloud Run memiliki stack teknologi yang lebih baru, lebih mudah di-deploy, lebih murah, dan dapat diskalakan ke 0 saat Anda tidak menggunakannya. Dengan fleksibilitasnya untuk menjalankan container stateless apa pun dan integrasinya dengan berbagai layanan Google Cloud, layanan ini ideal untuk men-deploy microservice dan aplikasi modern dengan overhead minimal dan efisiensi maksimal.

Lebih khusus lagi, Cloud Run adalah platform terkelola sepenuhnya oleh Google Cloud yang memungkinkan Anda menjalankan aplikasi dalam container stateless di lingkungan serverless. Layanan ini secara otomatis menangani semua infrastruktur, menskalakan dari nol untuk memenuhi traffic masuk dan turun saat tidak ada aktivitas, sehingga hemat biaya dan efisien. Cloud Run mendukung bahasa atau library apa pun selama dikemas dalam container, sehingga memberikan fleksibilitas yang besar dalam pengembangan. Cloud Run terintegrasi dengan baik dengan layanan Google Cloud lainnya dan cocok untuk membangun microservice, API, situs, dan aplikasi berbasis peristiwa tanpa perlu mengelola infrastruktur server.

Prasyarat

Untuk menyelesaikan tugas ini, Anda harus menginstal gcloud di komputer lokal Anda. Jika belum, lihat petunjuknya di sini. Namun, jika Anda menggunakan Google Cloud Shell, tidak ada tindakan yang perlu dilakukan.

Sebelum men-deploy...

Jika Anda bekerja di lingkungan lokal, lakukan autentikasi ke Google Cloud dengan perintah berikut

  • $ gcloud auth login –update-adc # not needed in Cloud Shell

Tindakan ini akan mengautentikasi Anda melalui login OAuth di browser Anda. Pastikan Anda login melalui Chrome dengan pengguna yang sama (misalnya, vattelapesca@gmail.com) yang login ke Google Cloud dengan penagihan diaktifkan.

Aktifkan Cloud Run API dengan perintah berikut:

  • $ gcloud services enable run.googleapis.com cloudbuild.googleapis.com

Pada tahap ini, semuanya sudah siap di-deploy ke Cloud Run.

Men-deploy Aplikasi ke Cloud Run melalui gcloud

Perintah yang memungkinkan Anda men-deploy Aplikasi di Cloud Run adalah gcloud run deploy. Ada beberapa opsi yang dapat ditetapkan untuk mencapai sasaran Anda. Kumpulan opsi minimum (yang dapat Anda berikan melalui command line, atau alat akan menanyakannya dengan perintah interaktif) adalah sebagai berikut:

  1. Nama Layanan Cloud Run yang ingin Anda deploy untuk Aplikasi Anda. Layanan Cloud Run akan menampilkan URL yang menyediakan endpoint ke Aplikasi Anda.
  2. Region Google Cloud tempat Aplikasi Anda akan berjalan. (--region REGION)
  3. Image Container yang membungkus Aplikasi Anda.
  4. Variabel Lingkungan yang perlu digunakan Aplikasi Anda selama eksekusinya.
  5. Flag Allow-Unauthenticated yang mengizinkan semua orang mengakses Aplikasi Anda tanpa autentikasi lebih lanjut.

Lihat dokumentasi (atau scroll ke bawah untuk melihat kemungkinan solusi) untuk mengetahui cara menerapkan opsi ini ke command line Anda.

Deployment akan memerlukan waktu beberapa menit. Jika semuanya sudah benar, Anda akan melihat tampilan seperti ini di Konsol Google Cloud.

ef1029fb62f8de81.png

f7191d579c21ca3e.png

Klik URL yang diberikan oleh Cloud Run dan uji Aplikasi Anda. Setelah diautentikasi, Anda akan melihat tampilan seperti ini.

d571a90cd5a373f9.png

"gcloud run deploy" tanpa argumen

Anda mungkin telah memperhatikan bahwa gcloud run deploy mengajukan pertanyaan yang tepat dan mengisi bagian yang Anda kosongkan. Ini hebat sekali!

Namun, di beberapa modul, kita akan menambahkan perintah ini ke pemicu Cloud Build sehingga kita tidak dapat mengajukan pertanyaan interaktif. Kita perlu mengisi setiap opsi dalam perintah. Jadi, Anda ingin membuat gcloud run deploy --option1 blah --foo bar --region your-fav-region emas. Bagaimana Anda melakukannya?

  1. ulangi langkah 2-3-4 hingga gcloud berhenti menanyakan pertanyaan:
  2. [LOOP] gcloud run deploy dengan opsi yang ditemukan sejauh ini
  3. Sistem [LOOP] meminta opsi X
  4. [LOOP] Cari di dokumen publik cara menyiapkan X dari CLI dengan menambahkan opsi --my-option [my-value].
  5. Kembali ke langkah 2 sekarang, kecuali jika gcloud selesai tanpa pertanyaan lebih lanjut.
  6. This gcloud run deploy BLAH BLAH BLAH rocks! Simpan perintah di suatu tempat, Anda akan memerlukannya nanti untuk langkah Cloud Build.

Kemungkinan solusinya ada di sini. Dokumen ada di sini.

Selamat 🎉🎉🎉 Anda telah berhasil men-deploy Aplikasi di Google Cloud dan menyelesaikan langkah pertama Modernisasi.

6. Modul 4: Sandi Bersih dengan Secret Manager

95cd57b03b4e3c73.png

Pada langkah sebelumnya, kita berhasil men-deploy dan menjalankan Aplikasi di Cloud Run. Namun, kami melakukannya dengan praktik buruk keamanan: menyediakan beberapa rahasia dalam teks biasa.

Iterasi pertama: Perbarui config.php untuk menggunakan ENV

Anda mungkin memperhatikan bahwa kita memasukkan sandi DB langsung ke dalam kode di file config.php. Tidak masalah untuk tujuan pengujian dan untuk melihat apakah Aplikasi berfungsi. Namun, Anda tidak dapat melakukan/menggunakan kode seperti itu di lingkungan produksi. Sandi (dan parameter koneksi DB lainnya) harus dibaca secara dinamis dan diberikan ke Aplikasi saat runtime. Ubah file config.php sehingga membaca parameter db dari variabel ENV. Jika gagal, Anda harus mempertimbangkan untuk menetapkan nilai default. Hal ini berguna jika Anda gagal memuat ENV, sehingga output halaman akan memberi tahu Anda apakah halaman tersebut menggunakan nilai default. Isi bagian yang kosong dan ganti kode di config.php.

<?php
// Database configuration with ENV variables. Set default values as well 
$db_host = getenv('DB_HOST') ?: 'localhost';
$db_name = getenv('DB_NAME') ?: 'image_catalog';
$db_user = getenv('DB_USER') ?: 'appmod-phpapp-user';
$db_pass = getenv('DB_PASS') ?: 'wrong_password';
// Note getenv() is PHP 5.3 compatible
try {
    $pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    die("Errore di connessione: " . $e->getMessage());
}

session_start();
?>

Saat Aplikasi Anda dikontainerisasi, Anda perlu menyediakan cara untuk memasok variabel ENV ke Aplikasi. Hal ini dapat dilakukan dengan beberapa cara:

  • Pada waktu build, di Dockerfile. Tambahkan 4 parameter ke Dockerfile sebelumnya menggunakan sintaksis ENV DB_VAR=ENV_VAR_VALUE. Tindakan ini akan menyiapkan nilai default yang dapat diganti saat runtime. Misalnya, ‘DB_NAME' dan ‘DB_USER' dapat ditetapkan di sini dan tidak di tempat lain.
  • Pada waktu run. Anda dapat menyiapkan variabel ini untuk Cloud Run, baik dari CLI maupun dari UI. Ini adalah tempat yang tepat untuk menempatkan semua 4 variabel Anda (kecuali jika Anda ingin mempertahankan default yang ditetapkan di Dockerfile).

Di localhost, Anda mungkin ingin menempatkan variabel ENV dalam file .env (periksa folder solutions).

Pastikan juga bahwa .env ditambahkan ke .gitignore Anda : Anda tidak ingin mengirimkan secret ke GitHub.

echo .env >> .gitignore

Setelah itu, Anda dapat menguji instance secara lokal:

docker run -it -p 8080:8080 --env-file .env my-php-app-docker

Sekarang Anda telah mencapai hal berikut:

  1. Aplikasi Anda akan membaca variabel secara dinamis dari ENV Anda
  2. Anda meningkatkan keamanan karena telah menghapus sandi DB dari kode Anda)

Sekarang Anda dapat men-deploy revisi baru ke Cloud Run. Mari kita buka UI dan tetapkan variabel lingkungan secara manual:

  • Buka https://console.cloud.google.com/run
  • Klik aplikasi Anda
  • Klik "Edit dan deploy revisi baru"
  • Pada tab pertama "Container(s)", klik tab bawah "Variables and secrets"
  • Klik "+ Tambahkan variabel" dan tambahkan semua variabel yang diperlukan. Anda akan mendapatkan sesuatu seperti ini:

7a5fbfa448544d3.png

f2780c35585388ca.png

Apakah ini sudah sempurna? Tidak. PASS Anda masih dapat dilihat oleh sebagian besar operator. Hal ini dapat dimitigasi dengan Google Cloud Secret Manager.

Iterasi kedua: Secret Manager

Sandi Anda telah hilang dari kode Anda sendiri: berhasil! Tapi tunggu - apakah kita sudah aman?

Sandi Anda masih dapat dilihat oleh siapa saja yang memiliki akses ke Konsol Google Cloud. Faktanya, jika Anda mengakses file deployment YAML Cloud Run, Anda akan dapat mengambilnya. Atau, jika Anda mencoba mengedit atau men-deploy revisi Cloud Run baru, sandi akan terlihat di bagian Variabel & Rahasia seperti yang ditunjukkan pada screenshot di bawah.

Secret Manager Google Cloud adalah layanan terpusat yang aman untuk mengelola informasi sensitif seperti kunci API, sandi, sertifikat, dan secret lainnya.

Dengan layanan ini, Anda dapat menyimpan, mengelola, dan mengakses secret dengan izin yang mendetail dan enkripsi yang kuat. Secret Manager yang terintegrasi dengan Identity and Access Management (IAM) Google Cloud memungkinkan Anda mengontrol siapa yang dapat mengakses secret tertentu, sehingga memastikan keamanan data dan kepatuhan terhadap peraturan.

Secret Manager juga mendukung rotasi dan pembuatan versi secret otomatis, sehingga menyederhanakan pengelolaan siklus proses secret dan meningkatkan keamanan di aplikasi di seluruh layanan Google Cloud.

Untuk mengakses Secret Manager, buka dari menu Hamburger ke layanan Security dan temukan di bagian Data Protection seperti yang ditunjukkan pada screenshot di bawah.

6df83a1c3cb757f6.png

Aktifkan Secret Manager API setelah Anda berada di sana seperti pada gambar berikut.

a96c312e2c098db1.png

  • Sekarang klik "Create a secret": Mari kita sebut secara rasional:
  • Nama: php-amarcord-db-pass
  • Nilai rahasia: ‘your DB password' (abaikan bagian "upload file").
  • anotasi link rahasia ini, akan terlihat seperti projects/123456789012/secrets/php-amarcord-db-pass. Ini adalah pointer unik ke secret Anda (Untuk Terraform, Cloud Run, dan lainnya). Nomor tersebut adalah nomor project unik Anda.

Tips: Coba gunakan konvensi penamaan yang konsisten untuk secret Anda, mulai dari kiri ke kanan, misalnya: cloud-devrel-phpamarcord-dbpass

  • Organisasi (dengan perusahaan)
  • Tim (dalam organisasi)
  • Aplikasi (dalam tim)
  • Nama variabel (dalam aplikasi)

Dengan begitu, Anda dapat memiliki ekspresi reguler yang mudah untuk menemukan semua rahasia Anda untuk satu aplikasi.

Membuat revisi Cloud Run baru

Setelah Secret baru tersedia, kita perlu menghapus variabel ENV DB_PASS dan menggantinya dengan Secret baru. Jadi:

  • Akses ke Cloud Run menggunakan Konsol Google Cloud
  • Pilih aplikasi.
  • Klik "Edit & Deploy a New Revision"
  • Temukan tab "Variabel & Secret".
  • Gunakan tombol "+ Reference a Secret" untuk mereset variabel ENV DB_PASS.
  • Gunakan "DB_PASS" yang sama untuk Secrets yang direferensikan dan gunakan versi terbaru.

9ed4e35be7654dcb.png

Setelah selesai, Anda akan mendapatkan error berikut

da0ccd7af39b04ed.png

Coba cari tahu cara memperbaikinya. Untuk menyelesaikannya, Anda perlu mengakses bagian IAM & Admin dan mengubah izin pemberian. Selamat melakukan proses debug!

Setelah Anda mengetahuinya, kembali ke Cloud Run dan deploy ulang revisi baru. Hasilnya akan terlihat seperti gambar berikut:

e89f9ca780169b6b.png

Tips: Konsol Developer (UI) sangat berguna untuk menunjukkan masalah izin. Luangkan waktu untuk membuka semua link untuk entitas Cloud Anda.

7. Modul 5: Menyiapkan CI/CD dengan Cloud Build

ba49b033c11be94c.png

Mengapa Pipeline CI/CD?

Sekarang, Anda pasti sudah mengetik gcloud run deploy beberapa kali, mungkin menjawab pertanyaan yang sama berulang kali.

Bosan men-deploy aplikasi secara manual dengan gcloud run deploy? Bukankah akan lebih baik jika aplikasi Anda dapat men-deploy dirinya sendiri secara otomatis setiap kali Anda mengirim perubahan baru ke repositori Git?

Untuk menggunakan pipeline CI/CD, Anda memerlukan dua hal:

  1. Repositori Git Pribadi: Untungnya, Anda seharusnya sudah melakukan fork repositori workshop ke akun GitHub Anda di Langkah 2. Jika belum, kembali dan selesaikan langkah tersebut. Repositori yang di-fork akan terlihat seperti ini: https://github.com/<YOUR_GITHUB_USER>/app-mod-workshop
  2. Cloud Build. Layanan yang luar biasa dan murah ini memungkinkan Anda mengonfigurasi otomatisasi build untuk hampir semua hal: Terraform, aplikasi yang di-docker, ..

Bagian ini akan berfokus pada penyiapan Cloud Build.

Masuk ke Cloud Build!

Kita akan menggunakan Cloud Build untuk melakukannya:

  • membangun sumber Anda (dengan Dockerfile). Anggap ini sebagai "file .zip besar" yang berisi semua yang Anda butuhkan untuk mem-build dan menjalankannya (yaitu "artefak build" Anda).
  • Kirim artefak ini ke Artifact Registry (AR).
  • Kemudian, lakukan deployment dari AR ke Cloud Run untuk aplikasi "php-amarcord"
  • Tindakan ini akan membuat versi ("revisi") baru dari aplikasi yang ada (bayangkan lapisan dengan kode baru) dan kami akan mengonfigurasinya untuk mengalihkan traffic ke versi baru jika push berhasil.

Berikut adalah contoh beberapa build untuk aplikasi php-amarcord saya:

f30f42d4571ad5e2.png

Bagaimana cara melakukan semua ini?

  1. Dengan membuat satu file YAML yang sempurna: cloudbuild.yaml
  2. Dengan membuat pemicu Cloud Build.
  3. Dengan terhubung ke repositori GitHub kami melalui UI Cloud Build.

Buat pemicu (dan Hubungkan Repositori)

  • Buka https://console.cloud.google.com/cloud-build/triggers
  • Klik "Buat Pemicu".
  • Kompilasi:
  • Nama: Sesuatu yang bermakna seperti on-git-commit-build-php-app
  • Event: Push to branch
  • Sumber: "Hubungkan repositori baru" Teks alternatif
  • Tindakan ini akan membuka jendela di sebelah kanan: "Hubungkan repositori"
  • Penyedia sumber: "Github" (pertama)
  • "Lanjutkan"
  • Authenticate akan membuka jendela di github untuk melakukan autentikasi silang. Ikuti alurnya dan bersabarlah. Jika Anda memiliki banyak repo, proses ini mungkin memerlukan waktu beberapa saat.
  • "Pilih repo" Pilih akun/repo Anda dan centang bagian "Saya mengerti...".
  • Jika Anda mendapatkan error: The GitHub App is not installed on any of your repositories, lanjutkan dengan mengklik "Install Google Cloud Build" dan ikuti petunjuknya.
  • 23e0e0f1219afea3.pngKlik Hubungkan
  • bafd904ec07122d2.png
  • Bingo! Repositori Anda kini terhubung.
  • Kembali ke bagian Pemicu....
  • Konfigurasi: Terdeteksi otomatis (*)
  • Lanjutan: Pilih akun layanan "[PROJECT_NUMBER]- compute@developer.gserviceaccount.com"
  • xxxxx adalah project ID Anda
  • Akun layanan komputasi default cukup memadai untuk pendekatan lab - jangan gunakan di produksi. ( Pelajari lebih lanjut).
  • biarkan semuanya seperti apa adanya.
  • Klik tombol "Buat".

(*) Ini adalah cara paling sederhana karena memeriksa Dockerfile atau cloudbuild.yaml. Namun, cloudbuild.yaml memberi Anda kekuatan nyata untuk memutuskan apa yang harus dilakukan di setiap langkah.

Aku punya kekuatan!

Sekarang, pemicu tidak akan berfungsi kecuali jika Anda memberikan akun layanan Cloud Build (apa itu akun layanan? Email "robot" yang bertindak atas nama Anda untuk suatu tugas - dalam hal ini membangun sesuatu di Cloud.

SA Anda tidak akan dapat membangun dan men-deploy kecuali jika Anda mengizinkannya. Untungnya, caranya mudah.

8715acca72286a46.png

Di mana YAML Cloud Build?

Sebaiknya luangkan waktu untuk membuat YAML Build cloud Anda sendiri.

Namun, jika tidak punya waktu, atau tidak ingin meluangkan waktu, Anda bisa mendapatkan beberapa inspirasi di folder solusi ini: .solutions

Sekarang Anda dapat mengirim perubahan ke GitHub dan mengamati Cloud Build.

Menyiapkan Cloud Build bisa jadi rumit. Harapkan beberapa kali bolak-balik dengan:

  • Memeriksa log di https://console.cloud.google.com/cloud-build/builds;region=global
  • Menemukan kesalahan Anda.
  • Memperbaiki kode, dan menerbitkan ulang commit git / push git.
  • Terkadang error tidak ada dalam kode, tetapi dalam beberapa konfigurasi. Dalam hal ini, Anda dapat mengeluarkan build baru dari UI (cloud build > "Triggers" > Run)

97acd16980a144ab.png

Perhatikan bahwa jika Anda menggunakan solusi ini, masih ada beberapa pekerjaan yang harus dilakukan. Misalnya, Anda perlu menyetel variabel ENV untuk endpoint dev/prod yang baru dibuat:

3da8723e4ff80c0a.png

Anda dapat melakukannya dengan dua cara:

  • Melalui UI - dengan menyetel ulang variabel ENV
  • Melalui CLI dengan membuat skrip "sempurna" untuk Anda. Contohnya dapat ditemukan di sini: gcloud-run-deploy.sh . Anda perlu menyesuaikan beberapa hal, misalnya endpoint dan nomor project; Anda dapat menemukan nomor project di Ringkasan Cloud.

Bagaimana cara melakukan commit kode ke github?

Mengajari Anda cara terbaik untuk git push ke github berada di luar cakupan workshop ini. Namun, jika Anda mengalami masalah dan berada di Cloud Shell, ada dua cara:

  1. CLI. Tambahkan kunci SSH secara lokal dan tambahkan remote dengan git@github.com:YOUR_USER/app-mod-workshop.git (bukan http)
  2. VSCode. Jika menggunakan editor Cloud Shell, Anda dapat menggunakan tab Kontrol sumber (ctrl-shift-G), mengklik "sinkronkan perubahan", dan mengikuti petunjuk. Anda akan dapat mengautentikasi akun GitHub ke VS Code dan melakukan pull/push dengan mudah dari sana.

f0d53f839c7fa3b6.png

Ingatlah untuk git add clodubuild.yaml di antara file lain, atau tidak akan berfungsi.

"Paritas dev/prod" dalam vs dangkal [opsional]

Jika Anda menyalin versi model dari sini, Anda akan memiliki dua versi DEV dan PROD yang identik. Hal ini keren, dan sesuai dengan aturan 10 Aplikasi Dua Belas Faktor.

Namun, kita menggunakan dua endpoint Web yang berbeda agar aplikasi mengarah ke Database yang sama. Hal ini sudah cukup untuk workshop; namun, dalam kehidupan nyata, Anda harus meluangkan waktu untuk membuat lingkungan produksi yang tepat. Artinya, Anda harus memiliki dua database (satu untuk pengembangan dan satu untuk produksi) serta memilih tempat untuk menempatkannya demi pemulihan dari bencana / ketersediaan tinggi. Hal ini berada di luar cakupan workshop ini, tetapi ini adalah beberapa bahan renungan.

Jika Anda memiliki waktu untuk melakukan produksi versi "mendalam", harap ingat semua resource yang perlu diduplikasi, seperti:

  • Database Cloud SQL (dan mungkin instance SQL).
  • Bucket GCS
  • Cloud Function.
  • Anda dapat menggunakan Gemini 1.5 Flash sebagai model dalam pengembangan (lebih murah, lebih cepat), dan Gemini 1.5 Pro (lebih canggih).

Secara umum, setiap kali Anda melakukan sesuatu terkait aplikasi, berpikir secara kritis: apakah produksi harus memiliki nilai yang sama atau tidak? Jika tidak, duplikasi upaya Anda. Tentu saja, hal ini jauh lebih mudah dilakukan dengan Terraform, tempat Anda dapat menyuntikkan lingkungan (-dev, -prod) sebagai akhiran ke resource Anda.

8. Modul 6: Pindah ke Google Cloud Storage

a680e0f287dd2dfb.png

Penyimpanan

dc3a4b8ea92aaef6.png

Saat ini, aplikasi menyimpan status di container Docker. Jika mesin rusak, aplikasi error, atau cukup dengan mendorong revisi baru, revisi baru akan dijadwalkan, dengan penyimpanan baru yang kosong: 🙈

Bagaimana cara memperbaikinya? Ada beberapa pendekatan.

  1. Menyimpan gambar di DB. Itulah yang akhirnya saya lakukan dengan aplikasi PHP sebelumnya. Ini adalah solusi paling sederhana karena tidak menambah kerumitan. Namun, hal ini pasti akan menambah latensi dan beban pada DB Anda.
  2. Memigrasikan aplikasi Cloud Run ke solusi yang kompatibel dengan penyimpanan: GCE + Persistent Disk? Mungkin GKE + Storage? Catatan: apa yang Anda dapatkan dalam kontrol, Anda kehilangan dalam kelincahan.
  3. Pindah ke GCS. Google Cloud Storage menawarkan Penyimpanan terbaik di kelasnya untuk seluruh Google Cloud dan merupakan solusi paling idiomatis di Cloud. Namun, kita perlu berurusan dengan library PHP. Apakah kita memiliki library PHP 5.7 untuk GCS? Apakah PHP 5.7 bahkan mendukung Composer (sepertinya PHP 5.3.2 adalah versi paling awal yang didukung oleh Composer)?
  4. Mungkin menggunakan docker sidecar?
  5. Atau, gunakan Pemasangan Volume Cloud Run GCS. Kedengarannya luar biasa.

🤔 Memigrasikan penyimpanan (uraian)

[Open Ended] Dalam latihan ini, kami ingin Anda menemukan solusi untuk memindahkan gambar Anda dengan cara yang dipertahankan.

Uji penerimaan

Saya tidak ingin memberi tahu Anda solusinya, tetapi saya ingin hal ini terjadi:

  1. Anda mengupload newpic.jpg. Anda akan melihatnya di aplikasi.
  2. Anda mengupgrade aplikasi ke versi baru.
  3. newpic.jpg masih ada, terlihat.

💡 Kemungkinan solusi (Pemasangan Volume Cloud Run GCS)

Ini adalah solusi yang sangat elegan yang memungkinkan kita melakukan upload file dengan status sambil tidak menyentuh KODE SAMA SEKALI (selain menampilkan deskripsi gambar, tetapi itu tidak penting dan hanya untuk memuaskan mata).

Dengan demikian, Anda dapat memasang folder dari Cloud Run ke GCS, jadi:

  1. Semua upload ke GCS akan terlihat di aplikasi Anda.
  2. Semua upload ke aplikasi Anda akan diupload ke GCS
  3. Keajaiban akan terjadi pada objek yang diupload di GCS (bab 7).

Catatan. Baca petunjuk FUSE. Hal ini TIDAK boleh dilakukan jika performa menjadi masalah.

Buat bucket GCS

GCS adalah layanan penyimpanan yang selalu ada di Google Cloud. Layanan ini telah teruji dan digunakan oleh setiap layanan GCP yang memerlukan penyimpanan.

Perhatikan bahwa Cloud Shell mengekspor PROJECT_ID sebagai GOOGLE_CLOUD_PROJECT:

$ export PROJECT_ID=$GOOGLE_CLOUD_PROJECT

#!/bin/bash

set -euo pipefail

# Your Cloud Run Service Name, eg php-amarcord-dev
SERVICE_NAME='php-amarcord-dev'
BUCKET="${PROJECT_ID}-public-images"
GS_BUCKET="gs://${BUCKET}"

# Create bucket
gsutil mb -l "$GCP_REGION" -p "$PROJECT_ID" "$GS_BUCKET/"

# Copy original pictures there - better if you add an image of YOURS before.
gsutil cp ./uploads/*.png "$GS_BUCKET/"

Mengonfigurasi Cloud Run untuk memasang bucket di folder /uploads/

Sekarang mari kita beralih ke bagian yang elegan. Kita membuat volume php_uploads dan menginstruksikan Cloud Run untuk melakukan pemasangan FUSE di MOUNT_PATH (seperti /var/www/html/uploads/):

#!/bin/bash

set -euo pipefail

# .. keep variables from previous script..

# Uploads folder within your docker container.
# Tweak it for your app code.
MOUNT_PATH='/var/www/html/uploads/'

# Inject a volume mount to your GCS bucket in the right folder.
gcloud --project "$PROJECT_ID" beta run services update "$SERVICE_NAME" \
    --region $GCP_REGION \
    --execution-environment gen2 \
    --add-volume=name=php_uploads,type=cloud-storage,bucket="$BUCKET"  \
    --add-volume-mount=volume=php_uploads,mount-path="$MOUNT_PATH"

Sekarang, ulangi langkah ini untuk semua endpoint yang ingin Anda arahkan ke Cloud Storage.

Anda juga dapat melakukan hal yang sama dari UI

  1. Di tab "Volumes", buat Volume Mounts yang mengarah ke bucket Anda, dengan jenis "Cloud Storage bucket", misalnya dengan nama "php_uploads".
  2. Di bagian Container(s) > Volume Mounts, pasang volume yang baru saja Anda buat di titik volume yang diminta oleh aplikasi Anda. Hal ini bergantung pada dockerfile, tetapi mungkin terlihat seperti var/www/html/uploads/ .

Bagaimanapun, jika berhasil, pengeditan revisi Cloud Run baru akan menampilkan sesuatu seperti ini:

6c2bb98fc1b0e077.png

Sekarang, uji aplikasi baru dengan mengupload satu gambar baru ke endpoint /upload.php.

Gambar harus mengalir dengan lancar di GCS tanpa menulis satu baris PHP pun:

70032b216afee2d7.png

Apa yang baru saja terjadi?

Terjadi sesuatu yang sangat ajaib.

Aplikasi lama dengan kode lama masih berfungsi. Stack baru yang dimodernisasi memungkinkan kita memiliki semua gambar/foto di aplikasi kita yang berada dengan nyaman di Cloud Bucket stateful. Sekarang tidak ada batasan:

  • Ingin mengirim email setiap kali ada gambar dengan konten "berbahaya" atau "telanjang" yang masuk? Anda dapat melakukannya tanpa menyentuh kode PHP.
  • Ingin menggunakan model Multimodal Gemini setiap kali ada gambar masuk untuk mendeskripsikannya, dan mengupload DB dengan deskripsinya? Anda dapat melakukannya tanpa menyentuh kode PHP. Anda tidak percaya pada saya? Lanjutkan membaca di bab 7.

Kita baru saja membuka peluang besar di sini.

9. Modul 7: Memberdayakan Aplikasi Anda dengan Google Gemini

c00425f0ad83b32c.png

Sekarang Anda memiliki aplikasi PHP baru yang modern dan canggih (seperti Fiat 126 tahun 2024) dengan penyimpanan yang di-Cloud-kan.

Apa yang dapat dilakukan dengannya?

Prasyarat

Pada bab sebelumnya, solusi model memungkinkan kita memasang gambar /uploads/ di GCS, de facto memisahkan logika Aplikasi dari penyimpanan gambar.

Latihan ini mengharuskan Anda untuk:

  • Berhasil menyelesaikan latihan di bab 6 (penyimpanan).
  • Memiliki bucket GCS dengan upload gambar, tempat orang mengupload gambar di aplikasi Anda dan gambar mengalir ke bucket Anda.

Siapkan Cloud Function (di Python)

Pernahkah Anda bertanya-tanya cara menerapkan aplikasi berbasis peristiwa? Seperti:

  • saat <event> terjadi => kirim email
  • saat <event> terjadi => jika <condition> benar, perbarui Database.

Peristiwa dapat berupa apa saja, mulai dari tersedianya data baru di BigQuery, perubahan objek baru dalam folder di GCS, atau pesan baru yang menunggu dalam antrean di Pub/Sub.

Google Cloud mendukung beberapa paradigma untuk mencapai hal ini. Terutama:

Dalam latihan ini, kita akan mempelajari Cloud Function untuk mencapai hasil yang cukup spektakuler. Kami juga akan memberikan latihan opsional untuk Anda.

Perhatikan bahwa contoh kode diberikan di bagian .solutions/

Menyiapkan Cloud Function (🐍 python)

Kami mencoba membuat GCF yang sangat ambisius.

  1. Saat image baru dibuat di GCS.. (mungkin karena seseorang telah menguploadnya di aplikasi - tetapi tidak hanya itu)
  2. .. panggil Gemini untuk mendeskripsikannya dan mendapatkan deskripsi tekstual gambar .. (sebaiknya periksa MIME dan pastikan itu adalah gambar, bukan PDF, MP3, atau Teks)
  3. .. dan perbarui DB dengan deskripsi ini. (hal ini mungkin memerlukan penambalan DB untuk menambahkan kolom description ke tabel images).

Membuat patch DB untuk menambahkan description ke gambar

  1. Buka Cloud SQL Studio:

b92b07c4cba658ef.png

  1. Masukkan pengguna dan sandi Anda untuk Images DB
  2. Suntikkan SQL ini yang menambahkan kolom untuk deskripsi gambar:

ALTER TABLE images ADD COLUMN description TEXT;

3691aced78a6389.png

Dan bingo! Coba sekarang untuk memeriksa apakah berhasil:

SELECT * FROM images;

Anda akan melihat kolom deskripsi baru:

bed69d6ad0263114.png

Tulis Gemini f(x)

Catatan. Fungsi ini sebenarnya dibuat dengan bantuan Gemini Code Assist.

Catatan. Saat membuat fungsi ini, Anda mungkin mengalami error izin IAM. Beberapa di antaranya didokumentasikan di bawah paragraf "Kemungkinan error".

  1. Mengaktifkan API
  2. Buka https://console.cloud.google.com/functions/list
  3. Klik "Create Function"
  4. Mengaktifkan API dari wizard API:

d22b82658cfd4c48.png

Anda dapat membuat GCF dari UI atau dari command line. Di sini kita akan menggunakan command line.

Kemungkinan kode dapat ditemukan di bagian .solutions/

  1. Buat folder untuk menghosting kode Anda, misalnya "gcf/". Buka folder.
  2. Buat file requirements.txt:
google-cloud-storage
google-cloud-aiplatform
pymysql
  1. Buat fungsi python. Contoh kode di sini: gcf/main.py.
#!/usr/bin/env python

"""Complete this"""

from google.cloud import storage
from google.cloud import aiplatform
import vertexai
from vertexai.generative_models import GenerativeModel, Part
import os
import pymysql
import pymysql.cursors

# Replace with your project ID
PROJECT_ID = "your-project-id"
GEMINI_MODEL = "gemini-1.5-pro-002"
DEFAULT_PROMPT = "Generate a caption for this image: "

def gemini_describe_image_from_gcs(gcs_url, image_prompt=DEFAULT_PROMPT):
    pass

def update_db_with_description(image_filename, caption, db_user, db_pass, db_host, db_name):
    pass

def generate_caption(event, context):
    """
    Cloud Function triggered by a GCS event.
    Args:
        event (dict): The dictionary with data specific to this type of event.
        context (google.cloud.functions.Context): The context parameter contains
                                                event metadata such as event ID
                                                and timestamp.
    """
    pass
  1. Dorong fungsi. Anda dapat menggunakan skrip yang mirip dengan ini: gcf/push-to-gcf.sh.

Catatan 1. Pastikan untuk mendapatkan ENV dengan nilai yang tepat, atau cukup tambahkan di atas (GS_BUCKET=blah, ..):

Catatan 2. Tindakan ini akan mengirimkan semua kode lokal (.), jadi pastikan untuk menyertakan kode Anda dalam folder tertentu dan menggunakan .gcloudignore seperti seorang profesional untuk menghindari pengiriman library yang besar. ( contoh).

#!/bin/bash

set -euo pipefail

# add your logic here, for instance:
source .env || exit 2 

echo "Pushing ☁️ f(x)☁ to 🪣 $GS_BUCKET, along with DB config.. (DB_PASS=$DB_PASS)"

gcloud --project "$PROJECT_ID" functions deploy php_amarcord_generate_caption \
    --runtime python310 \
    --region "$GCP_REGION" \
    --trigger-event google.cloud.storage.object.v1.finalized \
    --trigger-resource "$BUCKET" \
    --set-env-vars "DB_HOST=$DB_HOST,DB_NAME=$DB_NAME,DB_PASS=$DB_PASS,DB_USER=$DB_USER" \
    --source . \
    --entry-point generate_caption \
    --gen2

Catatan: dalam contoh ini, generate_caption akan menjadi metode yang dipanggil, dan Cloud Function akan meneruskan peristiwa GCS ke metode tersebut dengan semua info yang relevan (nama bucket, nama objek, ..). Luangkan waktu untuk men-debug dict python peristiwa tersebut.

Menguji fungsi

Unit Tests

Fungsi ini memiliki banyak bagian yang saling terhubung. Anda mungkin ingin menguji semua yang lajang.

Contohnya ada di gcf/test.py.

UI Cloud Functions

Luangkan juga waktu untuk menjelajahi fungsi Anda di UI. Setiap tab layak untuk dijelajahi, terutama Source (favorit saya), Variables, Trigger, dan Logs; Anda akan menghabiskan banyak waktu di Logs untuk memecahkan masalah error (lihat juga kemungkinan error di bagian bawah halaman ini). Pastikan juga untuk memeriksa Permissions.

cf3ded30d532a2c7.png

E2E Test

Saatnya menguji fungsi secara manual.

  1. Buka aplikasi Anda, lalu login
  2. Upload gambar (jangan terlalu besar, kami pernah melihat masalah dengan gambar besar)
  3. periksa di UI apakah gambar telah diupload.
  4. Periksa di Cloud SQL Studio bahwa deskripsi telah diperbarui. Login dan jalankan kueri ini: SELECT * FROM images.

43a680b12dbbdda0.png

Dan ini terbukti efektif. Kita mungkin juga ingin memperbarui frontend untuk menampilkan deskripsi tersebut.

Perbarui PHP untuk menampilkan [opsional]

Kami telah membuktikan bahwa aplikasi berfungsi. Namun, akan lebih baik jika pengguna juga dapat melihat deskripsi tersebut.

Kita tidak perlu menjadi ahli PHP untuk menambahkan deskripsi ke index.php. Kode ini akan melakukan (ya, Gemini juga menulisnya untuk saya!):

<?php if (!empty($image['description'])): ?>
    <p class="font-bold">Gemini Caption:</p>
    <p class="italic"><?php echo $image['description']; ?></p>
<?php endif; ?>

Posisikan kode ini di dalam foreach sesuai keinginan Anda.

Pada langkah berikutnya, kita juga melihat versi UI yang lebih menarik, berkat Gemini Code Assist. Versi yang lebih bagus mungkin terlihat seperti ini:

fdc12de0c88c4464.png

Kesimpulan

Anda mendapatkan Cloud Function yang dipicu pada objek baru yang masuk ke GCS yang dapat menganotasi konten gambar seperti yang dapat dilakukan manusia, dan otomatis memperbarui DB. Wow!

Apa langkah selanjutnya? Anda dapat mengikuti alasan yang sama untuk mendapatkan dua fungsi yang hebat.

[opsional] Tambahkan Cloud Functions lainnya [tidak terbatas]

Ada beberapa fitur tambahan yang terlintas di pikiran.

📩 Pemicu Email

Pemicu email yang mengirimkan email kepada Anda setiap kali seseorang mengirim gambar.

  • Terlalu sering? Tambahkan batasan lebih lanjut: Gambar BESAR, atau gambar yang konten Gemini-nya berisi kata "telanjang/ketelanjangan/kekerasan".
  • Pertimbangkan untuk memeriksa EventArc untuk hal ini.

🚫 Memoderasi foto tidak pantas secara otomatis

Saat ini, admin manusia menandai gambar sebagai "tidak pantas". Bagaimana jika Gemini yang melakukan tugas berat dan memoderasi ruang? Tambahkan pengujian untuk menandai konten pemicu yang tidak pantas dan perbarui DB seperti yang kita pelajari di fungsi sebelumnya. Artinya, pada dasarnya mengambil fungsi sebelumnya, mengubah perintah, dan memperbarui DB berdasarkan jawaban.

Peringatan. AI generatif memiliki output yang tidak dapat diprediksi. Pastikan "output materi iklan" dari Gemini "sesuai". Anda dapat meminta jawaban deterministik seperti skor keyakinan dari 0 hingga 1, JSON, .. Anda dapat melakukannya dengan berbagai cara, misalnya: * Menggunakan library python pydantic, langchain, .. * Menggunakan Output Terstruktur Gemini.

Tips. Anda dapat memiliki BEBERAPA fungsi atau memiliki satu perintah yang memaksakan jawaban JSON (berfungsi sangat baik dengan "Output Terstruktur Gemini" seperti yang dijelaskan di atas) seperti:

Apa perintah yang digunakan untuk membuat gambar ini?

{
    "description": "This is the picture of an arrosticino",
    "suitable": TRUE
}

Anda dapat menambahkan kolom tambahan dalam perintah untuk mendapatkan insight seperti: apakah ada hal baik tentangnya? Buruk tentang hal itu? Apakah Anda mengenali tempat ini? Apakah ada teks (OCR tidak pernah semudah ini):

  • goods: "Kelihatannya seperti makanan lezat"
  • bads: "Kelihatannya seperti makanan tidak sehat"
  • OCR: "Da consumare preferibilmente prima del 10 Novembre 2024"
  • location: "Pescara, Lungomare"

Meskipun biasanya lebih baik memiliki N fungsi untuk N hasil, akan sangat bermanfaat untuk melakukan satu fungsi yang melakukan 10 hal. Baca artikel oleh Riccardo ini untuk mengetahui caranya.

Kemungkinan error (sebagian besar terkait IAM / izin)

Saat pertama kali mengembangkan solusi ini, saya mengalami beberapa masalah izin IAM. Saya akan menambahkannya di sini untuk berempati dan memberikan beberapa ide tentang cara memperbaikinya.

Error: izin untuk Akun Layanan tidak memadai

  1. Perhatikan bahwa untuk men-deploy fungsi GCF yang memantau bucket GCS, Anda perlu menyiapkan izin yang tepat untuk Akun Layanan yang Anda gunakan untuk tugas tersebut, seperti pada gambar:

22f51012fa6b4a24.png

Anda mungkin juga harus mengaktifkan EventArc API beberapa menit sebelum API tersebut tersedia sepenuhnya.

Error: Cloud Run invoker tidak ada

  1. Komentar lain dari UI untuk pemberian izin GCF adalah ini ( Peran Cloud Run Invoker):

be72e17294f2d3f3.png

Error ini dapat diperbaiki dengan menjalankan perintah di image, yang mirip dengan fix-permissions.sh

Masalah ini dijelaskan di sini: https://cloud.google.com/functions/docs/securing/authenticating

Error: Batas memori terlampaui

Saat pertama kali saya menjalankannya, log saya mungkin mengatakan: "‘Batas memori 244 MiB terlampaui dengan 270 MiB yang digunakan. Pertimbangkan untuk meningkatkan batas memori, lihat https://cloud.google.com/functions/docs/configuring/memory'". Sekali lagi, tambahkan RAM ke GCF Anda. Hal ini sangat mudah dilakukan di UI. Berikut kemungkinan perubahannya:

bed69d6ad0263114.png

Atau, Anda juga dapat memperbaiki skrip deployment Cloud Run untuk meningkatkan MEM/CPU. Proses ini memerlukan waktu lebih lama.

Error: PubSub Published

Membuat pemicu dengan GCF v1 pernah memberikan error ini:

e5c338ee35ad4c24.png

Sekali lagi, masalah ini mudah diperbaiki dengan membuka IAM dan memberikan peran "Pub/Sub Publisher" ke Akun Layanan Anda.

Error: Vertex AI belum digunakan

Jika Anda menerima error ini:

Permission Denied: 403 Vertex AI API belum pernah digunakan dalam project YOUR_PROJECT sebelumnya atau dinonaktifkan. Aktifkan dengan membuka https://console.developers.google.com/apis/api/aiplatform.googleapis.com/overview?project=YOR_PROJECT

Anda hanya perlu mengaktifkan Vertex AI API. Cara termudah untuk mengaktifkan SEMUA API yang diperlukan adalah sebagai berikut:

  1. https://console.cloud.google.com/vertex-ai
  2. Klik "enable all recommended APIS".

492f05ac377f3630.png

Error: Pemicu EventArc tidak ditemukan.

Jika Anda mendapatkan error ini, deploy ulang fungsi.

8ec4fc11833d7420.png

Error: 400 Service agents are being provisioned

400 Service agents are being provisioned ( https://cloud.google.com/vertex-ai/docs/general/access-control#service-agents ). Service agents are needed to read the Cloud Storage file provided. Jadi, coba lagi dalam beberapa menit.

Jika hal ini terjadi, tunggu beberapa saat atau tanyakan kepada karyawan Google.

10. Modul 8: Membuat SLO Ketersediaan

Dalam Bab ini, kami mencoba mencapainya:

  1. Membuat SLI
  2. Membuat SLO berdasarkan SLI
  3. Membuat Pemberitahuan berdasarkan SLO

f63426182c052123.png

Topik ini sangat penting bagi penulis, karena Riccardo bekerja di area SRE / DevOps Google Cloud.

(tidak terbatas) Buat SLI dan SLO untuk aplikasi ini

Seberapa bagus aplikasi jika Anda tidak dapat mengetahui kapan aplikasi tersebut tidak berfungsi?

Apa yang dimaksud dengan SLO?

Wah! Google menciptakan SLO. Untuk membaca lebih lanjut tentang hal ini, kami dapat menyarankan:

Langkah 1: Buat SLI/SLO Ketersediaan

Mari kita mulai dengan SLO Ketersediaan, karena ini adalah hal termudah dan mungkin paling penting yang ingin Anda ukur.

Untungnya, Cloud Run dilengkapi dengan dukungan SLO bawaan, berkat Istio.

Setelah aplikasi Anda ada di Cloud Run, hal ini sangat mudah dilakukan, hanya membutuhkan waktu 30 detik.

  • Buka halaman Cloud Run Anda.
  • Klik/pilih aplikasi Anda.
  • Pilih tab SLOs.
  • Klik "+ Buat SLO".
  • Ketersediaan, Berbasis permintaan
  • Lanjutkan
  • Bulan Kalender / 99%.
  • Klik "Buat SLO".

e471c7ebdc56cdf6.png

Langkah 2: Siapkan Pemberitahuan di SLO ini

Sebaiknya buat 2 pemberitahuan:

  1. Satu dengan kecepatan pembakaran rendah ("Slowburn") untuk memberi tahu Anda melalui email (mensimulasikan tiket prioritas rendah).
  2. Satu dengan rasio pembakaran tinggi ("Fastburn") untuk memberi tahu Anda melalui SMS (mensimulasikan tiket / pager berprioritas tinggi)

Buka SLO tab Anda dari sebelumnya.

Lakukan hal ini dua kali:

314bfd6b9ef0a260.png

  • Klik "Buat Pemberitahuan SLO" (tombol 🔔 dengan tanda plus di dalamnya, di sebelah kanan)
  • Durasi lihat balik, Nilai minimum laju pengeluaran:
  • [FAST]. Pertama: 60 menit / 10 kali
  • [LAMBAT]. Kedua: 720 menit / 2 x
  • Saluran notifikasi: klik Kelola saluran notifikasi
  • Pertama, "Email" -> Tambahkan baru -> ..
  • Kedua, "SMS" -> Tambahkan baru -> Verifikasi di ponsel.
  • Tips: Saya suka menggunakan emoji dalam nama. Sangat cocok untuk demo.
  • Setelah selesai, klik X besar di kanan atas.
  • Pilih telepon terlebih dahulu (cepat), lalu email (lambat).
  • Tambahkan beberapa dokumentasi contoh seperti:
  • [PHP Amarcord] Riccardo told me to type sudo reboot or to check documentation in http://example.com/playbooks/1.php but I guess he was joking.

Bingo!

Hasil akhir

Latihan ini dapat dianggap selesai setelah Anda memiliki 1 SLO yang berfungsi + 2x pemberitahuan untuk ketersediaan, dan pemberitahuan tersebut dikirim ke email dan ponsel Anda.

Jika mau, Anda dapat menambahkan Latensi (dan sebaiknya Anda melakukannya) atau bahkan yang lebih kompleks. Untuk latensi, pilih latensi yang Anda anggap wajar; jika ragu, pilih 200 md.

11. Langkah berikutnya

Anda sudah menyelesaikan SEMUANYA, apa yang kurang?

Beberapa hal yang perlu dipertimbangkan:

Bermain dengan Gemini

Anda dapat menggunakan Gemini dalam dua versi:

  1. Vertex AI. "Cara perusahaan", yang terjalin dengan GCP Anda, yang telah kita pelajari di bab 7 (GCF+Gemini). Semua autentikasi berfungsi dengan lancar, dan layanan saling terhubung dengan baik.
  2. AI Google. "Cara konsumen". Anda mendapatkan Kunci API Gemini dari sini dan mulai membuat skrip kecil yang dapat dikaitkan dengan workload yang sudah Anda miliki (pekerjaan eksklusif, cloud lain, localhost, dll.). Anda cukup mengganti kunci API dan kode akan mulai berfungsi secara ajaib.

Sebaiknya Anda mencoba menjelajahi (2) dengan project pribadi Anda.

Pengangkatan UI

Saya tidak pandai membuat UI. Namun, Gemini bisa melakukannya. Anda cukup mengambil satu halaman PHP, dan mengatakan sesuatu seperti ini:

I have a VERY old PHP application. I want to touch it as little as possible. Can you help me:

1. add some nice CSS to it, a single static include for tailwind or similar, whatever you prefer
2. Transform the image print with description into cards, which fit 4 per line in the canvas?

Here's the code:

-----------------------------------
[Paste your PHP page, for instance index.php - mind the token limit!]

Anda bisa mendapatkannya dengan mudah dalam waktu kurang dari 5 menit, cukup dengan satu Cloud Build. :)

Respons dari Gemini sempurna (artinya, saya tidak perlu mengubah apa pun):

8a3d5fe37ec40bf8.png

Berikut tata letak baru di aplikasi pribadi penulis:

81620eb90ae3229a.png

Catatan: kode ditempelkan sebagai gambar karena kami tidak ingin mendorong Anda untuk mengambil kode tersebut, tetapi meminta Gemini menulis kode untuk Anda, dengan batasan UI/frontend kreatif Anda sendiri; percayalah, Anda hanya perlu melakukan perubahan yang sangat kecil setelahnya.

Keamanan

Mengamankan aplikasi ini dengan benar bukanlah tujuan dalam workshop 4 jam ini, karena akan meningkatkan waktu penyelesaian workshop ini sebesar 1-2 kali lipat.

Namun, topik ini sangat penting. Kami telah mengumpulkan beberapa ide di SECURITY.

12. Selamat!

Selamat 🎉🎉🎉 , Anda telah berhasil memodernisasi aplikasi PHP lama dengan Google Cloud.

24cb9a39b1841fbd.png

Singkatnya, dalam codelab ini Anda telah mempelajari:

  • Cara men-deploy database di Google Cloud SQL dan cara memigrasikan database yang ada ke dalamnya.
  • Cara memasukkan aplikasi PHP ke dalam container dengan Docker dan Buildpack, lalu menyimpan image-nya ke Google Cloud Artifact Registry
  • Cara men-deploy Aplikasi dalam container ke Cloud Run dan membuatnya berjalan dengan Cloud SQL
  • Cara menyimpan/menggunakan parameter konfigurasi sensitif (seperti sandi DB) secara rahasia menggunakan Google Secret Manager
  • Cara menyiapkan pipeline CI/CD dengan Google Cloud Build untuk otomatis membangun dan men-deploy Aplikasi PHP Anda pada setiap pengiriman kode ke repo GitHub Anda.
  • Cara menggunakan Cloud Storage untuk "meng-cloud-kan" resource aplikasi Anda
  • Cara memanfaatkan teknologi serverless untuk membangun alur kerja yang luar biasa di atas Google Cloud tanpa menyentuh kode aplikasi Anda.
  • Gunakan kemampuan multimodal Gemini untuk kasus penggunaan yang sesuai.
  • Menerapkan prinsip SRE dalam Google Cloud

Ini adalah awal yang baik untuk perjalanan Anda dalam Modernisasi aplikasi dengan Google Cloud.

🔁 Masukan

Jika Anda ingin memberi tahu kami tentang pengalaman Anda dengan workshop ini, pertimbangkan untuk mengisi formulir masukan ini.

Kami menantikan masukan Anda serta PRs untuk potongan kode yang sangat Anda banggakan.

🙏 Terima kasih

Penulis ingin berterima kasih kepada Mirko Gilioli dan Maurizio Ipsale dari Datatonic atas bantuan mereka dalam penulisan dan pengujian solusi ini.