Infrastruktur untuk aplikasi AI generatif berkemampuan RAG menggunakan GKE

Last reviewed 2024-04-02 UTC

Dokumen ini menyediakan arsitektur referensi yang dapat Anda gunakan untuk mendesain infrastruktur guna menjalankan aplikasi AI generatif dengan retrieval-augmented Generation (RAG) menggunakan Google Kubernetes Engine (GKE), Cloud SQL, dan alat open source seperti Ray, Hugging Face, dan LangChain. Untuk membantu Anda bereksperimen dengan arsitektur referensi ini, aplikasi contoh dan konfigurasi Terraform disediakan di GitHub.

Dokumen ini ditujukan untuk developer yang ingin membangun dan men-deploy aplikasi AI generatif yang berkemampuan RAG dengan cepat menggunakan alat dan model open source. Hal ini mengasumsikan bahwa Anda memiliki pengalaman dalam menggunakan GKE dan Cloud SQL, serta memiliki pemahaman konseptual tentang AI, machine learning (ML), dan model bahasa besar (LLM). Dokumen ini tidak memberikan panduan tentang cara mendesain dan mengembangkan aplikasi AI generatif.

Arsitektur

Diagram berikut menunjukkan tampilan tingkat tinggi arsitektur untuk aplikasi AI generatif berkemampuan RAG di Google Cloud:

Arsitektur tingkat tinggi untuk aplikasi AI generatif berkemampuan RAG di Google Cloud.

Arsitekturnya berisi subsistem penyaluran dan subsistem embedding.

  • Subsistem penyaluran menangani alur respons permintaan antara aplikasi dan penggunanya. Subsistem ini mencakup server frontend, server inferensi, dan layanan responsible AI (RAI). Subsistem inferensi berinteraksi dengan subsistem embedding melalui database vektor.
  • Subsistem penyematan memungkinkan kemampuan RAG dalam arsitektur. Subsistem ini melakukan hal berikut:
    • Menyerap data dari sumber data di Google Cloud, lokal, dan platform cloud lainnya.
    • Mengonversi data yang diserap ke embedding vektor.
    • Menyimpan embedding dalam database vektor.

Diagram berikut menunjukkan tampilan mendetail arsitektur:

Arsitektur mendetail untuk aplikasi AI generatif berkemampuan RAG di Google Cloud.

Seperti ditunjukkan dalam diagram sebelumnya, server frontend, server inferensi, dan layanan penyematan di-deploy di cluster GKE regional dalam mode Autopilot. Data untuk RAG diserap melalui bucket Cloud Storage. Arsitektur ini menggunakan instance Cloud SQL untuk PostgreSQL dengan ekstensi pgvector sebagai database vektor untuk menyimpan embeddings dan melakukan penelusuran semantik. Database vektor dirancang untuk menyimpan dan mengambil vektor berdimensi tinggi secara efisien.

Bagian berikut menjelaskan komponen dan aliran data di dalam setiap subsistem arsitektur.

Subsistem penyematan

Berikut adalah aliran data dalam subsistem embedding:

  1. Data dari sumber eksternal dan internal diupload ke bucket Cloud Storage oleh pengguna manusia atau secara terprogram. Data yang diupload mungkin berada di file, database, atau data yang di-streaming.
  2. (Tidak ditampilkan dalam diagram arsitektur.) Aktivitas upload data memicu peristiwa yang dipublikasikan ke layanan pesan seperti Pub/Sub. Layanan pesan mengirimkan notifikasi ke layanan penyematan.
  3. Saat menerima notifikasi peristiwa upload data, layanan embedding akan melakukan hal berikut:
    1. Mengambil data dari bucket Cloud Storage melalui driver CSI Cloud Storage FUSE.
    2. Membaca data yang diupload dan memprosesnya terlebih dahulu menggunakan Data Ray. Pra-pemrosesan dapat meliputi pemotongan data dan mengubahnya menjadi format yang sesuai untuk pembuatan embedding.
    3. Menjalankan tugas Ray untuk membuat embedding vektor dari data yang telah diproses sebelumnya menggunakan model open source seperti intfloat/multilingual-e5-small yang di-deploy di cluster yang sama.
    4. Menulis embedding vektor ke database vektor Cloud SQL untuk PostgreSQL.

Seperti dijelaskan di bagian berikut, saat subsistem penayangan memproses permintaan pengguna, subsistem tersebut akan menggunakan embedding di database vektor untuk mengambil data khusus domain yang relevan.

Subsistem penayangan

Berikut adalah alur permintaan-respons dalam subsistem penayangan:

  1. Pengguna mengirimkan permintaan bahasa natural ke server frontend melalui antarmuka chat berbasis web. Server frontend berjalan di GKE.
  2. Server frontend menjalankan proses LangChain yang melakukan hal-hal berikut:
    1. Mengonversi permintaan bahasa natural ke embedding menggunakan model dan parameter yang sama dengan yang digunakan oleh layanan embedding.
    2. Mengambil data grounding yang relevan dengan melakukan penelusuran semantik untuk embedding dalam database vektor. Penelusuran semantik membantu menemukan embedding berdasarkan intent perintah, bukan konten tekstualnya.
    3. Membuat perintah kontekstual dengan menggabungkan permintaan asli dengan data ground yang diambil.
    4. Mengirim prompt yang kontekstual ke server inferensi, yang berjalan di GKE.
  3. Server inferensi menggunakan framework penyajian Hugging Face TGI untuk menyalurkan LLM open source seperti Mistral-7B-Instructions atau model terbuka Gemma.
  4. LLM menghasilkan respons terhadap perintah, dan server inferensi mengirimkan respons ke server frontend.

    Anda dapat menyimpan dan melihat log aktivitas respons permintaan di Cloud Logging, dan menyiapkan pemantauan berbasis log menggunakan Cloud Monitoring. Anda juga dapat memuat respons yang dihasilkan ke BigQuery untuk analisis offline.

  5. Server frontend memanggil layanan RAI untuk menerapkan filter keamanan yang diperlukan pada respons. Anda dapat menggunakan alat seperti Sensitive Data Protection dan Cloud Natural Language API untuk menemukan, memfilter, mengklasifikasi, dan melakukan de-identifikasi konten sensitif dalam respons.

  6. Server frontend mengirim respons yang difilter ke pengguna.

Produk yang digunakan

Berikut ringkasan produk Google Cloud dan open source yang digunakan arsitektur sebelumnya:

Produk Google Cloud

  • Google Kubernetes Engine (GKE): Layanan Kubernetes yang dapat Anda gunakan untuk men-deploy dan mengoperasikan aplikasi dalam container dalam skala besar menggunakan infrastruktur Google.
  • Cloud Storage: Penyimpanan objek tanpa batas dan hemat biaya untuk beragam jenis data. Data dapat diakses dari dalam dan luar Google Cloud, serta direplikasi ke berbagai lokasi untuk redundansi.
  • Cloud SQL: Layanan database relasional yang terkelola sepenuhnya untuk membantu Anda menyediakan, mengoperasikan, dan mengelola database MySQL, PostgreSQL, dan SQL Server di Google Cloud.

Produk open source

  • Inferensi Pembuatan Teks Wajah (TGI): Toolkit untuk men-deploy dan menyalurkan LLM.
  • Ray: Framework komputasi terpadu open source yang membantu Anda menskalakan workload AI dan Python.
  • LangChain: Framework untuk mengembangkan dan men-deploy aplikasi yang didukung LLM.

Kasus penggunaan

RAG adalah teknik yang efektif untuk meningkatkan kualitas output yang dihasilkan dari LLM. Bagian ini memberikan contoh kasus penggunaan yang memungkinkan Anda menggunakan aplikasi AI generatif yang kompatibel dengan RAG.

Rekomendasi produk yang dipersonalisasi

Situs belanja online mungkin menggunakan chatbot yang didukung LLM untuk membantu pelanggan menemukan produk atau mendapatkan bantuan terkait belanja. Pertanyaan dari pengguna dapat ditambahkan menggunakan data historis tentang perilaku pembelian pengguna dan pola interaksi situs. Data tersebut mungkin mencakup ulasan pengguna dan masukan yang disimpan di datastore tidak terstruktur atau metrik terkait penelusuran yang disimpan di data warehouse analisis web. Pertanyaan yang ditambahkan kemudian dapat diproses oleh LLM untuk menghasilkan respons yang dipersonalisasi yang mungkin lebih menarik dan memikat bagi pengguna.

Sistem bantuan klinis

Dokter di rumah sakit perlu dengan cepat menganalisis dan mendiagnosis kondisi kesehatan pasien untuk membuat keputusan tentang perawatan dan pengobatan yang tepat. Aplikasi AI generatif yang menggunakan LLM medis seperti Med-PaLM dapat digunakan untuk membantu dokter dalam proses diagnosis klinis mereka. Respons yang dihasilkan aplikasi dapat didasarkan pada catatan pasien historis dengan memberikan konteks terhadap perintah dokter dengan data dari database rekam medis elektronik (EHR) rumah sakit atau dari pusat informasi eksternal seperti PubMed.

Riset hukum yang didukung AI generatif memungkinkan pengacara dengan cepat membuat kueri undang-undang dan hukum kasus dalam jumlah besar untuk mengidentifikasi preseden hukum yang relevan atau merangkum konsep hukum yang kompleks. Hasil dari riset tersebut dapat ditingkatkan dengan menambahkan perintah pengacara dengan data yang diambil dari korpus kontrak milik firma hukum, komunikasi hukum sebelumnya, dan catatan kasus internal. Pendekatan desain ini memastikan bahwa respons yang dihasilkan relevan dengan domain hukum yang menjadi spesialisasi pengacara.

Pertimbangan desain

Bagian ini memberikan panduan untuk membantu Anda mengembangkan dan menjalankan arsitektur AI generatif berkemampuan RAG yang dihosting GKE dan memenuhi persyaratan khusus Anda untuk keamanan dan kepatuhan, keandalan, biaya, dan performa. Panduan di bagian ini tidaklah lengkap. Bergantung pada persyaratan khusus aplikasi Anda serta produk dan fitur Google Cloud yang digunakan, Anda mungkin perlu mempertimbangkan faktor desain dan konsekuensi tambahan.

Untuk panduan desain terkait alat open source dalam arsitektur referensi ini, seperti Hugging Face TGI, lihat dokumentasi untuk alat tersebut.

Keamanan, privasi, dan kepatuhan:

Bagian ini menjelaskan faktor-faktor yang harus dipertimbangkan saat Anda mendesain dan mem-build aplikasi AI generatif berkemampuan RAG di Google Cloud yang memenuhi persyaratan keamanan, privasi, dan kepatuhan Anda.

Produk Pertimbangan desain
GKE

Dalam mode operasi Autopilot, GKE melakukan pra-konfigurasi cluster Anda dan mengelola node sesuai dengan praktik terbaik keamanan, yang memungkinkan Anda berfokus pada keamanan khusus workload. Untuk informasi selengkapnya, lihat referensi berikut:

Untuk memastikan kontrol akses yang ditingkatkan bagi aplikasi Anda yang berjalan di GKE, Anda dapat menggunakan Identity-Aware Proxy (IAP). IAP terintegrasi dengan resource GKE Ingress dan memastikan bahwa hanya pengguna yang terautentikasi dengan peran Identity and Access Management (IAM) yang tepat yang dapat mengakses aplikasi. Untuk mengetahui informasi selengkapnya, lihat Mengaktifkan IAP untuk GKE.

Secara default, data Anda di GKE dienkripsi dalam penyimpanan dan saat transit menggunakan kunci enkripsi yang dikelola Google. Sebagai lapisan keamanan tambahan untuk data sensitif, Anda dapat mengenkripsi data pada lapisan aplikasi menggunakan kunci yang Anda miliki dan kelola dengan Cloud KMS. Untuk mengetahui informasi selengkapnya, lihat Mengenkripsi secret pada lapisan aplikasi.

Jika menggunakan cluster GKE Standar, Anda dapat menggunakan kemampuan enkripsi data tambahan berikut:

Cloud SQL

Instance Cloud SQL dalam arsitektur ini tidak harus dapat diakses dari internet publik. Jika akses eksternal ke instance Cloud SQL diperlukan, Anda dapat mengenkripsi koneksi eksternal menggunakan SSL/TLS atau konektor Proxy Auth Cloud SQL. Konektor Auth Proxy memberikan otorisasi koneksi menggunakan IAM. Konektor ini menggunakan koneksi TLS 1.3 dengan cipher AES 256 bit untuk memverifikasi identitas klien dan server serta mengenkripsi traffic data. Untuk koneksi yang dibuat menggunakan Java, Python, Go, atau Node.js, gunakan Konektor Bahasa yang sesuai, bukan konektor Proxy Auth.

Secara default, Cloud SQL menggunakan kunci enkripsi data (DEK) yang dikelola Google dan kunci enkripsi kunci (KEK) untuk mengenkripsi data dalam penyimpanan. Jika perlu menggunakan KEK yang Anda kontrol dan kelola, Anda dapat menggunakan kunci enkripsi yang dikelola pelanggan (CMEK).

Untuk mencegah akses tidak sah ke Cloud SQL Admin API, Anda dapat membuat perimeter layanan menggunakan Kontrol Layanan VPC.

Untuk mengetahui informasi tentang cara mengonfigurasi Cloud SQL guna membantu memenuhi persyaratan residensi data, lihat Ringkasan residensi data.

Cloud Storage

Secara default, data yang disimpan di Cloud Storage dienkripsi menggunakan kunci enkripsi yang dikelola Google. Jika diperlukan, Anda dapat menggunakan CMEK atau kunci Anda sendiri yang dikelola dengan menggunakan metode pengelolaan eksternal seperti kunci enkripsi yang disediakan pelanggan (CSEK). Untuk mengetahui informasi selengkapnya, lihat Opsi enkripsi data.

Cloud Storage mendukung dua metode untuk mengontrol akses pengguna ke bucket dan objek Anda: IAM dan daftar kontrol akses (ACL). Dalam sebagian besar kasus, sebaiknya gunakan IAM, yang memungkinkan Anda memberikan izin di level bucket dan project. Untuk mengetahui informasi selengkapnya, lihat Ringkasan kontrol akses.

Data yang Anda muat ke subsistem penyerapan data melalui Cloud Storage mungkin mencakup data sensitif. Untuk melindungi data tersebut, Anda dapat menggunakan Sensitive Data Protection untuk menemukan, mengklasifikasi, dan melakukan de-identifikasi data. Untuk mengetahui informasi selengkapnya, lihat Menggunakan Perlindungan Data Sensitif dengan Cloud Storage.

Untuk mengurangi risiko pemindahan data yang tidak sah dari Cloud Storage, Anda dapat membuat perimeter layanan menggunakan Kontrol Layanan VPC.

Cloud Storage membantu Anda memenuhi persyaratan residensi data. Data disimpan atau direplikasi dalam region yang Anda tentukan.

Semua produk dalam arsitektur ini

Log audit Aktivitas Admin diaktifkan secara default untuk semua layanan Google Cloud yang digunakan dalam arsitektur referensi ini. Anda dapat mengakses log melalui Cloud Logging dan menggunakan log untuk memantau panggilan API atau tindakan lain yang mengubah konfigurasi atau metadata resource Google Cloud.

Log audit Akses Data juga diaktifkan secara default untuk semua layanan Google Cloud dalam arsitektur ini. Anda dapat menggunakan log ini untuk memantau hal-hal berikut:

  • Panggilan API yang membaca konfigurasi atau metadata resource.
  • Pengguna meminta untuk membuat, mengubah, atau membaca data resource yang disediakan pengguna.

Untuk panduan umum tentang prinsip keamanan yang perlu dipertimbangkan bagi aplikasi AI, lihat Memperkenalkan Framework AI Aman Google.

Keandalan

Bagian ini menjelaskan faktor desain yang harus Anda pertimbangkan untuk membangun dan mengoperasikan infrastruktur yang andal untuk aplikasi AI generatif berkemampuan RAG di Google Cloud.

Produk Pertimbangan desain
GKE

Dengan mode operasi Autopilot yang digunakan dalam arsitektur ini, GKE memberikan kemampuan keandalan bawaan berikut:

  • Workload Anda menggunakan cluster GKE regional. Bidang kontrol dan node pekerja tersebar di tiga zona berbeda dalam satu region. Workload Anda andal terhadap pemadaman zona. Cluster GKE regional memiliki waktu beroperasi SLA yang lebih tinggi daripada cluster zona.
  • Anda tidak perlu membuat node atau mengelola kumpulan node. GKE secara otomatis membuat node pool dan menskalakannya secara otomatis berdasarkan persyaratan workload Anda.

Untuk memastikan ketersediaan kapasitas GPU yang memadai saat diperlukan untuk menskalakan cluster GKE, Anda dapat membuat dan menggunakan reservasi. Reservasi memberikan kapasitas pasti di zona tertentu untuk resource yang ditentukan. Reservasi dapat bersifat spesifik untuk project, atau dibagikan ke beberapa project. Anda dikenai biaya untuk resource yang dicadangkan meskipun resource tersebut tidak disediakan atau digunakan. Untuk mengetahui informasi selengkapnya, lihat Memakai resource zona yang dicadangkan.

Cloud SQL

Untuk memastikan database vektor andal terhadap kegagalan database dan pemadaman zona, gunakan instance Cloud SQL yang dikonfigurasi dengan HA. Jika terjadi kegagalan database utama atau pemadaman layanan zona, Cloud SQL akan beralih secara otomatis ke database standby di zona lain. Anda tidak perlu mengubah alamat IP untuk endpoint database.

Untuk memastikan bahwa instance Cloud SQL Anda tercakup dalam SLA, ikuti panduan operasional yang direkomendasikan. Misalnya, pastikan CPU dan memori memiliki ukuran yang tepat untuk workload, dan aktifkan peningkatan penyimpanan otomatis. Untuk mengetahui informasi selengkapnya, lihat Panduan operasional.

Cloud Storage Anda dapat membuat bucket Cloud Storage di salah satu dari tiga jenis lokasi: regional, dual-region, atau multi-region. Data yang disimpan di bucket regional direplikasi secara sinkron di beberapa zona dalam satu region. Untuk ketersediaan yang lebih tinggi, Anda dapat menggunakan bucket dual-region atau multi-region, tempat data direplikasi secara asinkron di berbagai region.

Pengoptimalan biaya

Bagian ini memberikan panduan untuk membantu Anda mengoptimalkan biaya penyiapan dan pengoperasian aplikasi AI generatif berkemampuan RAG di Google Cloud.

Produk Pertimbangan desain
GKE

Dalam mode Autopilot, GKE mengoptimalkan efisiensi infrastruktur cluster Anda berdasarkan persyaratan workload. Anda tidak perlu terus-menerus memantau penggunaan resource atau mengelola kapasitas untuk mengontrol biaya.

Jika Anda dapat memprediksi penggunaan CPU, memori, dan penyimpanan sementara di cluster GKE Autopilot, Anda dapat menghemat biaya dengan mendapatkan diskon untuk abonemen. Untuk mengetahui informasi selengkapnya, lihat Diskon abonemen GKE.

Untuk mengurangi biaya menjalankan aplikasi, Anda dapat menggunakan Spot VM untuk node GKE Anda. Harga Spot VM lebih murah daripada VM standar, tetapi tidak memberikan jaminan ketersediaan. Untuk mengetahui informasi tentang manfaat node yang menggunakan Spot VM, cara kerjanya di GKE, dan cara menjadwalkan workload pada node tersebut, lihat Spot VM.

Untuk panduan pengoptimalan biaya selengkapnya, lihat Praktik terbaik untuk menjalankan aplikasi Kubernetes yang hemat biaya di GKE.

Cloud SQL

Konfigurasi ketersediaan tinggi (HA) membantu mengurangi periode nonaktif untuk database Cloud SQL Anda saat zona atau instance menjadi tidak tersedia. Namun, biaya instance yang dikonfigurasi dengan HA lebih tinggi daripada biaya instance mandiri. Jika tidak memerlukan HA untuk database vektor, Anda dapat mengurangi biaya dengan menggunakan instance mandiri, yang tidak andal terhadap gangguan zona.

Anda dapat mendeteksi apakah instance Cloud SQL Anda disediakan secara berlebihan dan mengoptimalkan penagihan dengan menggunakan insight dan rekomendasi biaya Cloud SQL yang didukung oleh Active Assist. Untuk mengetahui informasi selengkapnya, lihat Mengurangi instance Cloud SQL yang disediakan secara berlebihan.

Jika Anda dapat memprediksi kebutuhan CPU dan memori instance Cloud SQL, Anda dapat menghemat uang dengan mendapatkan diskon untuk abonemen. Untuk mengetahui informasi selengkapnya, lihat Diskon abonemen Cloud SQL.

Cloud Storage Untuk bucket Cloud Storage yang Anda gunakan untuk memuat data ke dalam subsistem penyerapan data, pilih kelas penyimpanan yang sesuai. Saat memilih kelas penyimpanan, pertimbangkan persyaratan retensi data dan frekuensi akses workload Anda. Misalnya, untuk mengontrol biaya penyimpanan, Anda dapat memilih class Standar dan menggunakan Object Lifecycle Management. Hal ini memungkinkan downgrade objek secara otomatis ke kelas penyimpanan berbiaya lebih rendah atau penghapusan objek berdasarkan kondisi yang Anda tetapkan.

Untuk memperkirakan biaya resource Google Cloud, gunakan Kalkulator Harga Google Cloud.

Pengoptimalan performa

Bagian ini menjelaskan faktor-faktor yang harus Anda pertimbangkan saat mendesain dan mem-build aplikasi AI generatif yang mendukung RAG di Google Cloud yang memenuhi persyaratan performa Anda.

Produk Pertimbangan desain
GKE Pilih class komputasi yang sesuai untuk Pod Anda berdasarkan persyaratan performa workload. Untuk Pod yang menjalankan server inferensi dan layanan embedding, sebaiknya gunakan jenis mesin GPU seperti nvidia-l4.
Cloud SQL

Untuk mengoptimalkan performa instance Cloud SQL Anda, pastikan CPU dan memori yang dialokasikan ke instance tersebut memadai untuk workload. Untuk mengetahui informasi selengkapnya, lihat Mengoptimalkan instance Cloud SQL yang kurang disediakan.

Guna meningkatkan waktu respons untuk penelusuran vektor perkiraan terdekat (ANN), gunakan indeks Inverted File with Flat Compression (IVFFlat) atau indeks Hierarchical Navigable Small World (HNSW)

Untuk membantu Anda menganalisis dan meningkatkan performa kueri database, Cloud SQL menyediakan alat Query Insights. Anda dapat menggunakan alat ini untuk memantau performa dan melacak sumber kueri yang bermasalah. Untuk mengetahui informasi selengkapnya, lihat Menggunakan Insight kueri untuk meningkatkan performa kueri.

Untuk mendapatkan ringkasan status dan performa database serta melihat metrik mendetail seperti koneksi puncak dan pemakaian disk, Anda dapat menggunakan dasbor System Insights. Untuk mengetahui informasi selengkapnya, lihat Menggunakan Insight sistem untuk meningkatkan performa sistem.

Cloud Storage Untuk mengupload file besar, Anda dapat menggunakan metode yang disebut upload komposit paralel. Dengan strategi ini, file besar dibagi menjadi beberapa bagian. Potongan tersebut diupload ke Cloud Storage secara paralel, lalu datanya dikomposisi ulang di cloud. Jika bandwidth jaringan dan kecepatan disk tidak membatasi faktor, upload komposit paralel dapat lebih cepat daripada operasi upload biasa. Namun, strategi ini memiliki beberapa keterbatasan dan implikasi biaya. Untuk informasi selengkapnya, lihat Upload komposit paralel.

Deployment

Untuk men-deploy topologi yang didasarkan pada arsitektur referensi ini, Anda dapat mendownload dan menggunakan kode contoh open source yang tersedia di repositori di GitHub. Kode contoh tidak ditujukan untuk kasus penggunaan produksi. Anda dapat menggunakan kode tersebut untuk bereksperimen dengan menyiapkan infrastruktur AI untuk aplikasi AI generatif berkemampuan RAG.

Kode contoh menjalankan hal berikut:

  1. Menyediakan instance Cloud SQL untuk PostgreSQL agar berfungsi sebagai database vektor.
  2. Men-deploy Ray, JupyterHub, dan Hugging Face TGI ke cluster GKE yang Anda tentukan.
  3. Men-deploy contoh aplikasi chatbot berbasis web ke cluster GKE agar Anda dapat memverifikasi kemampuan RAG.

Untuk petunjuk cara menggunakan kode contoh, lihat README untuk kodenya. Jika terjadi error saat Anda menggunakan kode contoh, dan jika masalah GitHub yang terbuka tidak ditemukan karena error tersebut, buat masalah di GitHub.

Kode contoh men-deploy resource Google Cloud yang dapat ditagih. Setelah selesai menggunakan kode, hapus resource yang tidak diperlukan lagi.

Langkah selanjutnya

Kontributor

Penulis: Kumar Dhanagopal | Developer Solusi Lintas Produk

Kontributor lainnya: