Serverless biasanya tepat untuk endpoint API dengan trafik tidak merata, pekerjaan singkat, dan kebutuhan membayar berdasarkan pemakaian. Container selalu aktif lebih cocok ketika aplikasi membutuhkan latensi konsisten, koneksi persisten, worker yang berjalan terus-menerus, atau kontrol runtime yang lebih besar.

Pilihan yang benar bukan sekadar membandingkan biaya awal. Tim perlu menghitung bentuk trafik, durasi proses, concurrency, batas koneksi database, kebutuhan observability, strategi deployment, serta siapa yang akan menangani insiden di luar jam kerja. Dalam banyak sistem, keputusan terbaik adalah hybrid: API bursty berjalan di serverless, sementara worker jangka panjang dan koneksi persisten berjalan pada container selalu aktif.

Perbedaan inti serverless dan container selalu aktif

Pada serverless, platform menjalankan unit komputasi saat ada event, misalnya request HTTP, pesan antrean, atau jadwal. Ketika tidak ada pekerjaan, jumlah instance dapat turun hingga nol. Pada container selalu aktif, satu atau lebih proses aplikasi dijaga tetap berjalan agar siap menerima request atau memproses pekerjaan.

  • Serverless: optimasi utama adalah elastisitas dan biaya saat idle rendah. Infrastruktur dasar, provisioning instance, dan sebagian besar scaling dikelola platform.
  • Container selalu aktif: optimasi utama adalah kontrol proses, koneksi hangat, latensi yang lebih stabil, dan kemampuan menjalankan proses yang tidak cocok dengan batas eksekusi serverless.

Keduanya tetap membutuhkan desain aplikasi yang baik. Serverless bukan berarti tanpa operasi, dan container bukan berarti harus dikelola secara manual pada VM. Container dapat dijalankan pada layanan orkestrasi terkelola, tetapi tim tetap perlu menentukan kapasitas minimum, autoscaling, health check, deployment, dan strategi pemulihan.

Memilih berdasarkan pola trafik dan jenis pekerjaan

Trafik bursty: serverless sering unggul

Trafik bursty ditandai lonjakan singkat yang sulit diprediksi, misalnya webhook pembayaran, kampanye email, unggahan dokumen, atau API yang ramai pada jam tertentu lalu sepi. Serverless dapat menambah kapasitas ketika request masuk dan mengurangi kapasitas saat lonjakan berakhir.

Gunakan serverless untuk request yang relatif singkat, stateless, dan dapat diulang dengan aman. Contohnya: validasi webhook, membuat URL unggahan sementara, transformasi data kecil, atau endpoint baca yang memakai cache.

Namun, scaling cepat tidak berarti tanpa batas. Platform biasanya menerapkan batas concurrency akun, layanan, fungsi, atau instance. Jika batas itu tercapai, request dapat diantrikan, ditolak, atau mengalami peningkatan latensi. Karena itu, uji beban harus memasukkan skenario lonjakan, bukan hanya rata-rata request per detik.

Trafik stabil: container selalu aktif sering lebih mudah diprediksi

Jika API menerima beban cukup stabil sepanjang hari, biaya per eksekusi serverless dapat tumbuh menjadi kurang menarik dibanding kapasitas container yang sudah berjalan. Container yang hangat juga menghindari cold start dan memungkinkan koneksi keluar, cache lokal, atau pool koneksi tetap hidup lebih lama.

Ini bukan aturan mutlak. Serverless masih dapat cocok untuk trafik stabil bila tim ingin meminimalkan pengelolaan infrastruktur atau jika beban komputasi per request sangat kecil. Tetapi evaluasi harus didasarkan pada total durasi eksekusi, memori, request, transfer data, dan kebutuhan kapasitas minimum, bukan asumsi bahwa serverless selalu lebih murah.

Pekerjaan berjadwal: pilih berdasarkan durasi dan sifat proses

Job terjadwal seperti sinkronisasi harian, pembuatan laporan, atau pembersihan data dapat dipicu oleh scheduler lalu dikerjakan di serverless bila durasinya pendek dan batas waktu eksekusi mencukupi. Untuk job yang lama, memakai banyak memori, membutuhkan proses anak, atau perlu mempertahankan koneksi ke layanan lain, worker container biasanya lebih aman.

Jangan membuat request HTTP pengguna menunggu proses terjadwal atau batch selesai. Scheduler sebaiknya hanya memicu pesan ke antrean. Worker kemudian mengambil pesan tersebut secara asinkron.

Faktor teknis yang paling sering menentukan keputusan

Cold start dan latensi

Cold start terjadi ketika platform perlu menyiapkan environment baru sebelum kode menerima request. Dampaknya bergantung pada runtime, ukuran artefak, proses inisialisasi, dependensi, jaringan, dan konfigurasi aplikasi. Pada API interaktif, cold start dapat menaikkan latensi ekor, yaitu request paling lambat yang tetap dirasakan pengguna.

Kurangi risiko cold start dengan menjaga artefak deployment kecil, menunda inisialisasi yang tidak diperlukan untuk setiap endpoint, memakai cache atau koneksi secara hati-hati, dan memisahkan fungsi ringan dari fungsi yang memiliki dependensi berat. Jika SLO latensi ketat dan request harus responsif secara konsisten, container selalu aktif dengan kapasitas minimum sering lebih sederhana untuk dioperasikan.

Concurrency, backpressure, dan database

Kesalahan umum pada serverless adalah membiarkan aplikasi menskalakan request lebih cepat daripada database mampu menerima koneksi atau query. Misalnya, setiap instance membuka beberapa koneksi baru, sementara instance dapat bertambah tajam saat lonjakan. Hasilnya bukan throughput lebih tinggi, melainkan kegagalan autentikasi, kehabisan koneksi, timeout, atau peningkatan lock contention.

Rancang kapasitas dari database ke arah aplikasi. Tentukan batas koneksi yang aman untuk aplikasi, sisakan kapasitas untuk migrasi, administrasi, replica, dan layanan lain, lalu turunkan batas tersebut menjadi concurrency aplikasi.

max_koneksi_aplikasi = max_koneksi_database - cadangan_operasional
instance_maksimum = floor(max_koneksi_aplikasi / koneksi_per_instance)
concurrency_maksimum = instance_maksimum * request_serentak_per_instance

Nilai koneksi_per_instance harus mencerminkan pool nyata, bukan asumsi satu request sama dengan satu koneksi. Bila tersedia, gunakan connection pooler yang sesuai dengan database Anda. Untuk operasi tulis yang berat, batasi concurrency worker dan gunakan antrean sebagai mekanisme backpressure.

Catatan: koneksi database yang dipakai secara global dan koneksi yang disimpan di dalam satu instance adalah dua hal berbeda. Reuse koneksi dalam instance dapat membantu, tetapi tidak menggantikan pembatasan jumlah instance dan concurrency total.

Worker, proses panjang, dan koneksi persisten

Container selalu aktif umumnya lebih cocok untuk consumer antrean yang harus terus melakukan polling, koneksi WebSocket, koneksi streaming, proses pengolahan media yang panjang, atau worker yang membutuhkan kontrol lifecycle proses. Worker seperti ini dapat memiliki concurrency tetap, health check, graceful shutdown, dan waktu drain ketika deployment berlangsung.

Serverless tetap cocok untuk worker berbasis event bila setiap pesan dapat diselesaikan dalam batas eksekusi yang tersedia, idempoten, dan aman untuk dicoba ulang. Hindari mengandalkan memori lokal sebagai sumber kebenaran karena instance dapat dihentikan atau diganti kapan saja.

Idempotensi dan retry

Baik serverless maupun container worker harus mengasumsikan pengiriman pesan dapat terjadi lebih dari sekali. Timeout jaringan dapat membuat pengirim tidak tahu apakah penerima sudah memproses pekerjaan. Karena itu, desain handler harus idempoten: menjalankan pesan yang sama dua kali tidak menghasilkan tagihan ganda, email duplikat, atau mutasi data yang salah.

function processPaymentEvent(event) {
  if (eventStore.exists(event.id)) return;

  database.transaction(() => {
    eventStore.insert(event.id);
    invoiceService.markPaid(event.invoiceId);
  });
}

Pada implementasi nyata, simpan penanda idempotensi dan mutasi bisnis dalam transaksi yang tepat. Tambahkan dead-letter queue atau mekanisme penanganan pesan gagal agar retry tanpa akhir tidak membebani sistem utama.

Kerangka estimasi biaya tanpa bergantung pada vendor

Bandingkan biaya menggunakan data observasi minimal beberapa siklus bisnis, termasuk jam puncak. Jangan hanya memakai rata-rata harian karena lonjakan menentukan kebutuhan scaling dan kapasitas database.

Model biaya serverless

biaya_serverless =
  biaya_request
  + (jumlah_eksekusi × durasi_rata_rata × alokasi_komputasi)
  + biaya_concurrency_atau_kapasitas_minimum
  + biaya_transfer_data
  + biaya_layanan_pendukung

Layanan pendukung mencakup antrean, object storage, database, observability, secret management, dan egress. Pada arsitektur event-driven, biaya request dan operasi antrean juga dapat meningkat jika satu aksi pengguna menghasilkan banyak event kecil.

Model biaya container selalu aktif

biaya_container =
  (jumlah_replika_minimum × kapasitas_per_replika × waktu_aktif)
  + kapasitas_tambahan_saat_puncak
  + biaya_load_balancer_dan_jaringan
  + biaya_layanan_pendukung
  + biaya_operasional_tim

Biaya idle merupakan komponen utama container selalu aktif. Namun, biaya operasional tim juga relevan: tuning autoscaler, patching image, penyelidikan masalah resource, rotasi sertifikat, dan respons insiden. Sebaliknya, serverless dapat memindahkan sebagian pekerjaan itu ke platform, tetapi menambah kebutuhan untuk memahami quota, retry event, tracing lintas layanan, dan pembatasan runtime.

Cara membandingkan secara adil

  1. Kelompokkan workload: API sinkron, worker antrean, job terjadwal, dan proses koneksi persisten.
  2. Ukur jumlah request atau pesan, durasi p50 dan p95, memori, ukuran payload, serta pola puncak.
  3. Masukkan kapasitas minimum yang diperlukan untuk memenuhi target latensi, bukan hanya kapasitas rata-rata.
  4. Masukkan biaya database, cache, antrean, log, trace, dan transfer data pada kedua opsi.
  5. Uji kegagalan: lonjakan mendadak, database melambat, retry massal, dan deployment saat traffic tinggi.

Matriks keputusan serverless vs container selalu aktif

KondisiPilihan yang cenderung tepatAlasan
API dengan trafik tidak merata dan request singkatServerlessScaling mengikuti event dan biaya idle dapat rendah.
API dengan target latensi ketat dan trafik stabilContainer selalu aktifInstance hangat dan kapasitas minimum memberi latensi lebih konsisten.
Worker antrean dengan pekerjaan singkat dan idempotenServerless atau hybridEvent dapat memicu pemrosesan sesuai volume antrean.
Worker panjang, consumer kontinu, atau koneksi persistenContainer selalu aktifKontrol lifecycle, drain, dan koneksi lebih baik.
Database memiliki batas koneksi ketatContainer atau serverless dengan concurrency ketatJumlah koneksi harus dapat diprediksi dan dibatasi.
Tim kecil dengan sedikit pengalaman operasiServerless untuk workload sederhanaMengurangi pengelolaan kapasitas dasar, tetapi quota dan observability tetap perlu dikelola.

Skenario SaaS kecil yang berkembang

Bayangkan SaaS B2B dengan API untuk aplikasi web, webhook dari penyedia pembayaran, ekspor laporan, dan notifikasi email. Pada tahap awal, trafik API rendah tetapi tidak merata. Tim dapat menempatkan endpoint autentikasi ringan, webhook, dan endpoint unggah pada serverless. Setiap webhook hanya memvalidasi payload, menyimpan event, lalu mengirim pekerjaan ke antrean.

Ekspor laporan dan pengiriman email dimulai sebagai worker berbasis event. Ketika pelanggan bertambah, ekspor menjadi lebih besar dan beberapa job membutuhkan waktu lebih panjang. Tim kemudian memindahkan consumer ekspor ke container selalu aktif dengan concurrency terbatas. Container tersebut mengambil pesan dari antrean, menghasilkan file, menyimpannya ke object storage, lalu memperbarui status job.

Pada tahap berikutnya, API utama mungkin memiliki trafik stabil pada jam kerja. Tim dapat tetap mempertahankan endpoint bursty di serverless, tetapi memindahkan API inti ke container dengan beberapa replika minimum bila data menunjukkan cold start atau biaya per eksekusi tidak lagi sesuai. Perpindahan tidak harus dilakukan sekaligus; kontrak API, antrean, dan observability yang konsisten membuat migrasi bertahap lebih aman.

Pola hybrid yang praktis

Pola hybrid memisahkan workload berdasarkan karakter teknisnya, bukan berdasarkan preferensi teknologi. Contoh alur berikut umum dan mudah dikembangkan:

  1. API serverless menerima request bursty, melakukan autentikasi, validasi, dan menulis data minimum.
  2. API mengirim pekerjaan berat ke antrean bersama correlation ID dan idempotency key.
  3. Worker container selalu aktif mengambil pesan dengan concurrency yang dibatasi oleh kapasitas database atau layanan eksternal.
  4. Worker menyimpan hasil ke database atau object storage.
  5. Klien membaca status pekerjaan melalui API atau menerima notifikasi setelah proses selesai.

Pemisahan ini membuat request pengguna tetap cepat, memberi backpressure saat downstream lambat, dan memungkinkan API serta worker diskalakan secara independen. Pastikan semua komponen meneruskan correlation ID agar log dan trace dapat ditelusuri dari request awal sampai worker selesai.

Operasi: observability, deployment, dan on-call

Observability minimum

Untuk kedua model, pantau error rate, latensi p50/p95/p99, throughput, saturation, dan dependency failure. Pada worker, tambahkan kedalaman antrean, usia pesan tertua, jumlah retry, dead-letter queue, dan waktu pemrosesan. Pada serverless, pantau throttling, concurrency, cold start bila metrik tersedia, serta kegagalan invokasi asinkron.

Log terstruktur sebaiknya memuat request ID atau trace ID, identitas tenant yang sudah disanitasi, jenis operasi, dan hasil proses. Jangan mencatat token, password, payload sensitif, atau data pribadi secara mentah.

Deployment dan rollback

Deployment serverless cenderung sederhana untuk unit kecil, tetapi perubahan event trigger, permission, environment variable, atau schema database tetap dapat menyebabkan insiden. Pada container, gunakan image immutable, health check, readiness check, dan graceful shutdown. Worker harus berhenti mengambil pesan baru saat shutdown, lalu menyelesaikan atau mengembalikan pekerjaan yang sedang diproses sesuai semantik antrean.

Untuk kedua model, gunakan migrasi database yang kompatibel mundur. Jangan merilis kode yang membutuhkan kolom baru sebelum kolom tersebut tersedia, dan jangan menghapus kolom lama sebelum semua versi aplikasi yang memakainya sudah tidak berjalan.

Beban on-call

Serverless mengurangi tugas menjaga host dan kapasitas dasar, tetapi on-call tetap harus menangani timeout, quota, retry storm, kegagalan event, dan masalah integrasi layanan. Container menambah tanggung jawab kapasitas, autoscaling, image security, dan health check, namun sering memberi jalur debugging yang lebih langsung untuk proses jangka panjang. Pilih model yang dapat didukung oleh kemampuan operasional tim saat ini, lalu dokumentasikan runbook untuk kegagalan paling mungkin.

Checklist keputusan sebelum memilih

  • Apakah trafik bursty, stabil, atau terutama berjadwal?
  • Berapa target latensi p95 dan p99 untuk endpoint pengguna?
  • Apakah pekerjaan dapat selesai dalam batas waktu eksekusi dan aman untuk retry?
  • Apakah aplikasi membutuhkan koneksi persisten, consumer kontinu, atau proses panjang?
  • Berapa concurrency aman berdasarkan koneksi dan throughput database?
  • Apakah ada antrean untuk memisahkan request sinkron dari pekerjaan berat?
  • Apakah biaya dibandingkan dengan pola puncak, idle, transfer data, observability, dan layanan pendukung?
  • Apakah deployment memiliki rollback, migrasi database aman, dan strategi graceful shutdown?
  • Apakah tim memiliki dashboard, alert, trace, serta runbook untuk throttling, backlog, dan retry gagal?
  • Apakah arsitektur memungkinkan hybrid atau migrasi bertahap jika pola penggunaan berubah?

Kesimpulannya: pilih serverless ketika elastisitas dan minimnya biaya idle lebih penting untuk workload singkat dan bursty. Pilih container selalu aktif ketika latensi konsisten, proses panjang, koneksi persisten, atau kontrol concurrency menjadi kebutuhan utama. Evaluasi berdasarkan perilaku workload dan kesiapan operasi, bukan hanya harga awal atau tren arsitektur.