Developer

Auto Scaling SageMaker CDK: Trik Update Endpoint Tanpa Error

Update endpoint SageMaker AI di CDK sering bikin error ScalableTarget atau stack nyangkut. Ini solusi otomatis pakai custom resource AWS CloudFormation.

Tim Engineering Classai

· 10 menit baca

Arsitektur orkestrasi auto scaling SageMaker AI menggunakan AWS CDK dan CloudFormation custom resources
Gambar: AWS

Inti singkat

  • Update konfigurasi endpoint SageMaker AI bawaan CloudFormation sering gagal karena target auto scaling harus dicopot manual lebih dulu.
  • Salah langkah deregister manual bisa memicu error UPDATE_ROLLBACK_FAILED yang merusak stack CloudFormation.
  • Solusi construct CDK memakai tiga custom resource untuk mengorkestrasi pencopotan dan pemasangan auto scaling otomatis.
  • Pola asynchronous provider dengan Step Functions memastikan registrasi scaling policy menunggu endpoint berstatus InService.
  • Properti ConfigHash otomatis memicu orkestrasi ulang setiap kali ada perubahan pada konfigurasi endpoint atau kapasitas scaling.
Daftar isi9 bagian
  1. 01
  2. 02
  3. 03
  4. 04
  5. 05
  6. 06
  7. 07
  8. 08
  9. 09

Mengupdate konfigurasi endpoint SageMaker AI lewat AWS CloudFormation atau AWS CDK sering bikin deployment gagal total gara-gara bentrok dengan Application Auto Scaling. Masalah ini sekarang bisa beres otomatis berkat solusi reusable construct berbasis CloudFormation custom resources yang dirilis resmi oleh AWS.

Kalau kamu pernah bertugas mengurus infrastruktur machine learning di AWS, momen update konfigurasi endpoint real-time SageMaker AI rasanya sering bikin deg-degan. Niatnya cuma mau ganti tipe instance GPU, nambah jumlah instance minimal, atau sekadar utak-atik environment variables. Tapi pas perintah cdk deploy dijalankan, terminal malah memuntahkan pesan error merah yang bikin deployment gagal di tengah jalan.

Masalah klasik ini akhirnya dibedah tuntas oleh Lauren Eterno lewat artikel resmi AWS Machine Learning Blog. AWS membagikan solusi berupa construct CDK siap pakai yang mengatur alur lepas-pasang auto scaling secara otomatis tanpa intervensi manual sama sekali.

Panduan gratis · gratis

Panduan Cepat API AI untuk Developer

Dari request pertama sampai siap produksi: contoh kode, streaming, kontrol biaya, keamanan API key, dan penanganan error.

  • Contoh request dengan cURL, Python, dan JavaScript
  • Streaming dan format OpenAI vs Anthropic
  • Cara mengontrol biaya token
  • Checklist keamanan dan siap produksi

Dengan mengirim, kamu setuju menerima email dari Classai sesuai Kebijakan Privasi. Bisa berhenti berlangganan kapan saja.

Kenapa Update Endpoint SageMaker AI Sering Bikin CDK Error?

Akar masalahnya ada pada aturan ketat Amazon SageMaker AI. SageMaker mewajibkan kamu mencopot alias deregister scalable target dari Application Auto Scaling sebelum mengubah konfigurasi endpoint. Tapi di sisi lain, CloudFormation punya cara kerja deklaratif yang kaku. Pas target auto scaling dicopot di luar alur, CloudFormation bakal kehilangan jejak resource tersebut.

Menurut AWS, ada tiga skenario kegagalan (failure modes) yang paling sering bikin pusing para DevOps dan ML engineer:

  1. Update langsung saat auto scaling masih nempel: CloudFormation mencoba memperbarui endpoint sementara scalable target masih aktif terdaftar. SageMaker AI langsung menolak update ini dengan error:
text
The variant(s) "[AllTraffic]" must be deregistered as scalable targets with Application Auto Scaling before they can be removed or have their instance type updated.

SageMaker memblokir proses update sebelum CloudFormation sempat menyentuh resource auto scaling.

  1. Deregister manual via CLI, tapi cuma ubah config endpoint: Banyak engineer mengakalinya dengan mencopot scalable target lewat AWS CLI, baru menjalankan deployment ulang. Cara ini kelihatannya sukses, tapi diam-diam berbahaya. Karena parameter auto scaling di template CloudFormation nggak berubah, CloudFormation menganggap nggak ada yang perlu diupdate. Hasilnya, endpoint berjalan normal tapi sama sekali tanpa auto scaling, dan sistem nggak ngasih peringatan error apa pun.

  2. Deregister manual via CLI, lalu ubah parameter auto scaling: Ini skenario paling horor. Pas kamu copot target via CLI lalu mengubah parameter scaling di CDK, CloudFormation bakal mencoba mencari ScalableTarget lama yang sebenarnya sudah kamu hapus manual. Hasilnya muncul error:

text
Resource of type 'AWS::ApplicationAutoScaling::ScalableTarget' with identifier 'endpoint/<name>/variant/AllTraffic|sagemaker:variant:DesiredInstanceCount|sagemaker' was not found.

Parahnya lagi, proses rollback otomatis CloudFormation juga bakal gagal (UPDATE_ROLLBACK_FAILED). Stack kamu bakal nyangkut dalam status rusak dan butuh perbaikan manual langsung di konsol CloudFormation.

Solusi Orkestrasi: 4 Langkah Custom Resource CDK

Biar nggak perlu drama rollback rusak, solusi dari AWS mengganti resource native AWS::ApplicationAutoScaling::ScalableTarget dan AWS::ApplicationAutoScaling::ScalingPolicy bawaan CloudFormation dengan tiga custom resources. Ketiga custom resource ini diatur urutan dependensinya menggunakan node.add_dependency di dalam kode CDK.

Alur orkestrasinya berjalan rapi dalam empat tahapan pasti:

  1. Deregister (Custom Resource): Mencopot scalable target sesaat sebelum pembaruan endpoint dimulai. Kalau ini adalah deployment pertama kali di mana scalable target memang belum ada, handler Python bakal menangkap error ObjectNotFoundException dengan aman dan lanjut jalan.
  2. Endpoint Update (Dikelola CDK): CDK memperbarui resource endpoint seperti biasa, menukar konfigurasi lama ke konfigurasi baru.
  3. Register Scalable Target (Custom Resource): Menunggu status endpoint benar-benar berubah jadi InService, baru mendaftarkan kembali scalable target ke Application Auto Scaling.
  4. Attach Scaling Policy (Custom Resource): Menunggu sampai scalable target siap menerima kueri (queryable), lalu memasang target tracking policy.
Four-step orchestration the construct runs during an endpoint update: deregister the scalable target, update the endpoint, register the scalable target, and attach the scaling policy
Diagram alur orkestrasi empat tahap saat update endpoint SageMaker AI: deregister target, update endpoint, register target, dan attach scaling policy. · Sumber: AWS

Kunci sukses dari alur ini ada pada asynchronous completion pattern bawaan CDK provider framework. Custom resource untuk mendaftarkan target dan memasang policy punya dua fungsi handler: on_event yang langsung merespons seketika, dan is_complete yang terus dicek secara berkala setiap 30 detik oleh sistem waiter. Batas waktu tunggunya lumayan panjang—sampai 1 jam untuk pendaftaran scalable target dan 15 menit untuk pemasangan policy—jadi nggak bakal terbentur batas timeout bawaan AWS Lambda biasa.

Selain itu, ada properti cerdas bernama ConfigHash. Hash ini menggabungkan isi konfigurasi endpoint dan parameter auto scaling jadi satu string unik. Begitu ada salah satu nilai yang berubah di file config, CloudFormation otomatis dipaksa memicu fungsi Lambda untuk menjalankan seluruh siklus orkestrasi dari awal.

Bedah Cara Kerja 3 Custom Resource di Balik Layar

Secara arsitektur, walaupun kamu cuma mendefinisikan tiga custom resource di kode construct CDK, stack yang ter-deploy ke AWS bakal membentuk infrastruktur pendukung yang lengkap. Provider framework CDK secara otomatis menghasilkan fungsi pembantu tambahan dan AWS Step Functions state machine untuk mengelola logika pengecekan waiter.

Dalam deployment nyatanya, stack bakal memuat:

  • 3 CloudFormation custom resources.
  • 2 AWS Step Functions state machines (khusus menjalankan waiter async).
  • 12 fungsi AWS Lambda (5 fungsi memuat logika handler buatan kita, sisanya 7 fungsi bawaan CDK framework).

Mari kita bedah logika handler Python yang menjalankan tiga peran utamanya:

1. Handler Deregister

Fungsi ini berjalan sinkron dan langsung memanggil API deregister_scalable_target milik Application Auto Scaling sebelum endpoint disentuh:

python
def handler(event, context):
    resource_id = event["ResourceProperties"]["ResourceId"]
    if event["RequestType"] in ("Create", "Update"):
        try:
            client.deregister_scalable_target(
                ServiceNamespace="sagemaker",
                ResourceId=resource_id,
                ScalableDimension="sagemaker:variant:DesiredInstanceCount",
            )
        except client.exceptions.ObjectNotFoundException:
            pass  # Aman dilewati kalau deploy perdana
    return {"PhysicalResourceId": f"deregister-{resource_id}"}

Di level CDK, kita cukup mengunci urutan eksekusi dengan props.endpoint.node.add_dependency(deregister). Jadi CloudFormation nggak bakal berani menyentuh endpoint sebelum fungsi deregister selesai.

2. Handler Register Scalable Target

Saat CloudFormation selesai menukar konfigurasi, status endpoint di SageMaker belum tentu langsung siap pakai. Makanya handler is_complete bakal terus mengecek status endpoint via API describe_endpoint. Begitu statusnya InService, fungsi langsung mendaftarkan batas minimum dan maksimum kapasitas instance:

python
def handler(event, context):
    props = event["ResourceProperties"]
    resource_id = props["ResourceId"]
    endpoint_name = resource_id.split("/")[1]
    if event["RequestType"] == "Delete":
        return {"IsComplete": True}
    status = sm_client.describe_endpoint(EndpointName=endpoint_name)["EndpointStatus"]
    if status == "Failed":
        raise Exception("Endpoint in Failed state")
    if status != "InService":
        return {"IsComplete": False}
    as_client.register_scalable_target(
        ServiceNamespace="sagemaker",
        ResourceId=resource_id,
        ScalableDimension="sagemaker:variant:DesiredInstanceCount",
        MinCapacity=int(props["MinCapacity"]),
        MaxCapacity=int(props["MaxCapacity"]),
    )
    return {"IsComplete": True}

3. Handler Attach Scaling Policy

Setelah scalable target didaftarkan, ada jeda waktu singkat di mana target tersebut belum terbaca oleh kueri API. Handler ini memastikan target sudah benar-benar ada lewat describe_scalable_targets, baru kemudian mengeksekusi put_scaling_policy untuk memasang aturan Target Tracking Scaling ke metric endpoint.

python
def handler(event, context):
    props = event["ResourceProperties"]
    resource_id = props["ResourceId"]
    if event["RequestType"] == "Delete":
        return {"IsComplete": True}
    resp = client.describe_scalable_targets(
        ServiceNamespace="sagemaker",
        ResourceIds=[resource_id],
        ScalableDimension="sagemaker:variant:DesiredInstanceCount",
    )
    if not resp.get("ScalableTargets"):
        return {"IsComplete": False}
    client.put_scaling_policy(
        PolicyName=props["PolicyName"],
        ServiceNamespace="sagemaker",
        ResourceId=resource_id,
        ScalableDimension="sagemaker:variant:DesiredInstanceCount",
        PolicyType="TargetTrackingScaling",
        TargetTrackingScalingPolicyConfiguration=json.loads(props["ScalingPolicyConfig"]),
    )
    return {"IsComplete": True}

Tutorial Deploy Auto Scaling SageMaker CDK Langkah demi Langkah

Kalau kamu mau mencoba langsung implementasi ini, AWS sudah menyediakan repository contoh yang rapi. Sebelum mulai, pastikan akun AWS kamu sudah punya izin untuk Amazon SageMaker AI, AWS Lambda, IAM, Application Auto Scaling, dan Amazon CloudWatch. Kamu juga butuh AWS CDK v2, Python versi 3.12 ke atas, serta package manager uv.

Berikut langkah-langkah praktisnya:

Langkah 1: Clone Repository dan Pasang Dependensi

Buka terminal kamu dan unduh kode dari repository resmi AWS samples:

bash
git clone https://github.com/aws-samples/sample-sagemaker-endpoint-autoscaling.git
cd sample-sagemaker-endpoint-autoscaling
uv sync

Langkah 2: Siapkan File Konfigurasi

Semua pengaturan endpoint dan auto scaling disimpan dalam satu file di config/config.yaml. Buka file tersebut dan sesuaikan dengan model yang kamu miliki di Amazon S3 serta image container di Amazon ECR:

yaml
endpoint:
  endpoint_name: "my-model-endpoint"
  image: "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-model:latest"
  model_data_url: "s3://my-bucket/models/model.tar.gz"
  instance_type: "ml.m5.large"
  initial_instance_count: 1
  environment: {}
autoscaling:
  min_capacity: 1
  max_capacity: 10
  target_value: 100
  scale_in_cooldown: 600
  scale_out_cooldown: 120

Langkah 3: Bootstrap dan Deploy Stack

Jalankan proses bootstrap environment CDK (kalau akun kamu belum pernah memakai CDK sebelumnya), lalu luncurkan stack:

bash
uv run cdk bootstrap
uv run cdk deploy InferenceStack

CloudFormation bakal membuat model SageMaker AI, endpoint configuration, endpoint real-time, lalu memicu tiga custom resource untuk mengaktifkan auto scaling secara berurutan.

Langkah 4: Verifikasi Hasil Deployment Lewat AWS CLI

Setelah deployment selesai, pastikan scalable target dan scaling policy sudah terdaftar sempurna di AWS Application Auto Scaling dengan perintah berikut:

bash
aws application-autoscaling describe-scalable-targets \
  --service-namespace sagemaker \
  --resource-ids endpoint/my-model-endpoint/variant/AllTraffic

aws application-autoscaling describe-scaling-policies \
  --service-namespace sagemaker \
  --resource-id endpoint/my-model-endpoint/variant/AllTraffic

Kalau output JSON menampilkan target variant AllTraffic dengan kapasitas min/max sesuai file yaml, berarti auto scaling sudah aktif.

Cara Uji Coba Update Konfigurasi dan Cek Hasilnya

Ujian sebenarnya adalah saat kita melakukan update konfigurasi. Sekarang coba ubah salah satu nilai di file config/config.yaml. Misalnya, ubah ukuran instance dari ml.m5.large menjadi ml.m5.xlarge.

Setelah file disimpan, deploy ulang stack kamu:

bash
uv run cdk deploy InferenceStack

Perhatikan prosesnya di terminal. Kamu bakal melihat custom resource deregister jalan lebih dulu untuk mencopot target lama. Begitu endpoint selesai berganti instance type dan statusnya kembali InService, custom resource berikutnya bakal otomatis mendaftarkan target scaling dan memasang policy kembali.

Cek ulang lewat AWS CLI:

bash
aws application-autoscaling describe-scalable-targets \
  --service-namespace sagemaker \
  --resource-ids endpoint/my-model-endpoint/variant/AllTraffic

Target auto scaling tetap terdaftar rapi tanpa perlu kamu sentuh manual sama sekali. Nggak ada lagi error UPDATE_ROLLBACK_FAILED yang bikin stack macet.

Kalau sudah selesai bereksperimen dan mau menghapus semua resource agar tagihan AWS nggak bengkak, jalankan perintah pembersihan ini:

bash
uv run cdk destroy InferenceStack

Perintah ini otomatis membersihkan endpoint SageMaker, endpoint config, model, semua fungsi Lambda, IAM roles, alarm CloudWatch, sekaligus mencopot scalable target sampai bersih.

Cara Pasang Construct ke Stack CDK yang Sudah Ada

Kalau tim kamu sudah punya CDK stack sendiri yang mengelola resource SageMaker AI dan cuma butuh modul orkestrasinya saja, kamu nggak perlu memakai template repository dari awal. Kamu cukup menyalin folder sagemaker_autoscaling/constructs/ ke dalam direktori project CDK kamu.

Berikut contoh cara mengintegrasikannya langsung ke kode Python CDK yang sudah ada:

python
import json
from sagemaker_autoscaling.constructs.sagemaker_autoscaling_construct import (
    AutoscalingConfig,
    SagemakerAutoscalingConstruct,
    SagemakerAutoscalingProps,
)

# Simpan kedua konfigurasi ke dalam config_hash biar sinkron
config_hash = json.dumps({
    "endpoint": {
        "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-model:latest",
        "modelDataUrl": "s3://my-bucket/models/model.tar.gz",
        "instanceType": "ml.m5.large",
        "initialInstanceCount": 1,
    },
    "autoscaling": {
        "minCapacity": 1,
        "maxCapacity": 10,
        "targetValue": 100,
        "scaleInCooldown": 600,
        "scaleOutCooldown": 120,
    },
})

# Pasang construct tepat di bawah resource endpoint CfnEndpoint
SagemakerAutoscalingConstruct(
    self, "Autoscaling",
    props=SagemakerAutoscalingProps(
        endpoint=your_endpoint,  # Objek CfnEndpoint kamu
        autoscaling=AutoscalingConfig(
            min_capacity=1,
            max_capacity=10,
            target_value=100,
            scale_in_cooldown=600,
            scale_out_cooldown=120,
        ),
        config_hash=config_hash,
    ),
)

Untuk melengkapi otomasi pipeline ML kamu, kamu juga bisa melihat artikel kami tentang arsitektur data AI seperti /developer/sagemaker-ai-training-session-custom-loop-tanpa-gpu buat memahami alur kerja lifecycle model di SageMaker.

Perbandingan: Native CloudFormation vs CDK Construct Baru

Biar makin jelas kenapa solusi construct ini jauh lebih aman buat pipeline CI/CD produksi, yuk lihat perbandingannya pada tabel berikut:

Fitur / SkenarioNative CloudFormationSolusi CDK Construct Baru
Update Ukuran InstanceGagal (AllTraffic variant must be deregistered)Berjalan mulus otomatis
Update Parameter Auto ScalingBerisiko UPDATE_ROLLBACK_FAILED kalau target dicopot manualAman, dieksekusi bertahap via custom resource
Intervensi Manual AWS CLIWajib dilakukan setiap kali mau redeployNol intervensi manual sama sekali
Pelacakan Status EndpointCloudFormation menganggap selesai saat update diterimaMenunggu sampai status endpoint benar-benar InService
Komponen Tambahan di StackCuma resource scaling native3 Custom Resource, 2 Step Functions, 12 Lambda
Penanganan RollbackRentan stuck dan bikin stack rusakBersih dan aman berkat graceful exception handler

Dampak Nyata Buat Engineer dan Startup AI di Indonesia

Buat para developer, DevOps, maupun startup AI di Indonesia, masalah infrastruktur kayak begini sering banget menguras waktu produktif. Saat model open source seperti DeepSeek atau Qwen mau di-deploy sendiri di atas instance SageMaker, kebutuhan untuk menaikkan kapasitas GPU pas jam kerja sibuk dan menurunkannya pas malam hari itu mutlak demi menghemat anggaran cloud.

Bayangkan skenario tim kamu lagi butuh update endpoint produksi yang melayani ribuan user di Indonesia. Kalau deployment tiba-tiba nyangkut gara-gara bentrok auto scaling CloudFormation, downtime layanan nggak bisa dihindari, dan engineer terpaksa lembur cuma buat membersihkan stack CloudFormation yang corrupt.

Dengan memakai construct ini, pipeline CI/CD di GitHub Actions atau AWS CodePipeline bisa berjalan mulus. Tim engineer bisa fokus menyempurnakan performa model tanpa harus was-was deployment bakal merusak konfigurasi autoscaling yang sudah ada.

Kalau kamu seorang software engineer yang pengin mendalami cara kerja sistem AI dari hulu ke hilir, kamu bisa gabung di kelas AI untuk Developer. Di sana kita belajar banyak soal integrasi API, arsitektur AI agent, sampai otomasi workflow kerja modern.

Prompt siap pakai
Kamu adalah AWS Solutions Architect senior. Saya memiliki stack AWS CDK Python yang mengelola endpoint real-time Amazon SageMaker AI dengan Application Auto Scaling.  Tolong review template CDK berikut dan berikan rekomendasi bagaimana cara mengimplementasikan custom resource orchestration untuk menangani update instance_type dan autoscaling parameters tanpa memicu error deregistration atau UPDATE_ROLLBACK_FAILED: [Lampirkan kode CDK stack kamu di sini]

Classai Router: Solusi Pakai Model Frontier Tanpa Ribet Kelola GPU

Deploy endpoint model LLM sendiri di atas Amazon SageMaker AI memang ngasih kontrol penuh atas infrastruktur dan data. Tapi buat banyak tim produk dan developer di Indonesia, biaya sewa instance GPU 24/7 beserta kerumitan mengelola auto scaling sering kali bikin boncos sebelum produknya menghasilkan.

Kalau kebutuhan aplikasi kamu adalah mengakses model AI bertenaga tinggi dengan latensi cepat tanpa pusing mengurus container, instance failure, atau error CloudFormation, kamu bisa melirik alternatif seperti Classai Router.

Lewat Classai Router, kamu cukup memakai satu API key yang kompatibel langsung dengan SDK OpenAI maupun Anthropic. Kamu bisa langsung memanggil model-model unggulan seperti gpt-4o (Rp525 / Rp2.700 per 1 juta token input/output), deepseek-v4.1-flash (Rp1.155 / Rp4.200 per 1 juta token), atau claude-sonnet-5 (Rp3.210 / Rp15.750 per 1 juta token). Seluruh pembayaran memakai rupiah via QRIS atau transfer virtual account bank lokal tanpa butuh kartu kredit luar negeri, dan kamu cuma membayar token yang benar-benar dipakai aplikasi kamu.

Sumber & referensi

  1. 01

Artikel ini ditulis ulang oleh Tim Classai dengan bantuan AI berdasarkan sumber di atas — bukan terjemahan. Ada yang keliru? Kabari kami di [email protected].

Pertanyaan yang sering ditanyakan

Kenapa update endpoint SageMaker AI selalu gagal di AWS CDK?

Update gagal karena SageMaker AI mewajibkan scalable target Application Auto Scaling dicopot lebih dulu sebelum konfigurasi endpoint diubah. CloudFormation bawaan nggak bisa menangani urutan pelepasan dan pemasangan ini secara otomatis.

Apa bahayanya mencopot target auto scaling SageMaker lewat AWS CLI manual?

Jika dicopot manual via CLI lalu ada perubahan parameter scaling di template, CloudFormation akan mencari scalable target yang sudah hilang. Ini memicu error fatal UPDATE_ROLLBACK_FAILED yang merusak stack CloudFormation.

Bagaimana cara kerja construct CDK baru ini mengatasi masalah tersebut?

Construct ini memakai tiga CloudFormation custom resource untuk mencopot auto scaling sebelum update endpoint berjalan, lalu memasang dan mendaftarkan kembali scaling policy setelah endpoint berstatus InService via Step Functions waiter.

Apakah construct ini menambah biaya tambahan di akun AWS saya?

Biaya utamanya tetap berasal dari instance SageMaker AI kamu. Fungsi Lambda dan AWS Step Functions yang dibuat oleh CDK framework hanya berjalan beberapa detik saat proses deployment berlangsung, sehingga biayanya sangat minim.

Luthfi

Classai Weekly

dari Luthfi · Setiap Senin, 06.30 WIB

Nggak sempat ngikutin berita AI? Biar aku yang rangkumin tiap Senin.

  • 5 berita AI paling penting minggu ini — udah disaring, nggak perlu baca puluhan situs
  • Kenapa itu penting buat kamu — plus opini jujur Luthfi soal arahnya
  • 1 prompt siap pakai yang bisa langsung dicoba
  • Pilihan kelas & model AI minggu ini — kadang ada promo khusus pelanggan

Dengan mengirim, kamu setuju menerima email dari Classai sesuai Kebijakan Privasi. Bisa berhenti berlangganan kapan saja.

Lihat contoh edisi

Bermanfaat? Bagikan ke tim atau temanmu.

Langkah berikutnya

Sudah paham teorinya. Sekarang praktikkan.

Daftar, isi saldo mulai puluhan ribu rupiah, dan panggil model AI pilihanmu dari kode yang sudah ada.

Ditulis oleh

Tim Engineering Classai

Pengembang Classai Router

Tim Engineering Classai membangun dan menjalankan Classai Router, gateway API AI yang kompatibel dengan format OpenAI dan Anthropic. Kami menulis panduan teknis dari pengalaman mengoperasikan API AI setiap hari.

Baca juga

Artikel terkait

Semua Developer