Tidak Semua Backend Service Di-deploy dengan Cara yang Sama

Sebuah tim bersiap untuk mendeploy fitur baru. Pembaruan API service berjalan mulus — beberapa detik rolling replacement, zero downtime. Lalu tibalah giliran worker. Seseorang menekan tombol deploy, dan tiba-tiba semua job pemrosesan gambar yang antri menghilang. Tim menatap log dengan bingung. "Ini berhasil untuk API. Kenapa worker-nya malah rusak?"

Skenario ini terjadi di tim engineering setiap minggu. Akar masalahnya hampir tidak pernah karena bug di alat deployment. Ini adalah kesalahpahaman tentang jenis backend service apa yang sedang mereka tangani.

Backend service terlihat serupa di permukaan. Semuanya berjalan di server, semuanya punya kode, semuanya perlu pembaruan. Tapi cara mereka menerima pekerjaan, cara mereka menyimpan state, dan cara mereka menangani interupsi sangat berbeda. Pipeline CI/CD yang bekerja sempurna untuk satu jenis bisa diam-diam menghancurkan data untuk jenis lainnya.

API Service: Selalu Mendengarkan, Selalu Tersedia

Jenis backend yang paling umum adalah API service. Ia mendengarkan di sebuah port, menunggu permintaan masuk dari aplikasi mobile, website, atau service lain, dan mengembalikan respons. Pengguna langsung merasakannya ketika API service mati — aplikasi berhenti memuat, halaman menampilkan error, transaksi gagal.

Diagram alir berikut mengklasifikasikan backend service berdasarkan cara mereka menerima pekerjaan dan memetakan setiap tipe ke strategi deployment yang direkomendasikan.

flowchart TD A[Bagaimana service menerima pekerjaan?] --> B[Request] A --> C[Queue] A --> D[Schedule] A --> E[Stream] B --> F[API Service] F --> G[Rolling / Blue-Green / Canary] C --> H[Worker] H --> I[Graceful shutdown setelah job selesai] D --> J[Scheduler] J --> K[Cegah overlap, gunakan distributed locks] E --> L[Consumer] L --> M[Commit offset saat graceful shutdown]

API service harus selalu siap. Mereka harus menangani lonjakan traffic dengan menaikkan dan menurunkan jumlah instance. Mereka harus menerima koneksi baru sementara koneksi lama masih diproses. Dan yang kritis, mereka harus diperbarui tanpa menjatuhkan permintaan yang sedang berlangsung.

Untuk CI/CD, ini berarti strategi deployment itu penting. Deployment stop-and-start sederhana akan memutus pengguna aktif. Rolling update, blue-green deployment, atau canary release menjadi diperlukan. Pipeline perlu memverifikasi bahwa instance baru lolos health check sebelum instance lama dihapus.

Worker: Memproses Job, Bukan Request

Worker tidak berbicara langsung dengan pengguna. Mereka mengambil tugas dari antrian, memprosesnya satu per satu, dan menyimpan hasilnya. Resizing gambar, pengiriman email, pembuatan laporan — ini adalah job worker. Pengguna mengunggah foto dan mendapatkan respons segera, tapi proses resizing terjadi di belakang layar.

Karena worker tidak melayani request langsung, mereka bisa diperbarui dengan lebih lembut. Perhatian utamanya adalah tidak menjatuhkan job yang aktif. Pipeline worker yang baik menunggu job saat ini selesai sebelum mematikan proses lama. Jika sistem antrian mendukungnya, worker dapat memberi sinyal bahwa ia akan berhenti, menyelesaikan tugasnya saat ini, lalu keluar. Instance baru mulai, mengambil job berikutnya dari antrian, dan sistem berlanjut tanpa kehilangan pekerjaan.

Kesalahan yang dilakukan tim adalah memperlakukan worker seperti API service. Mereka mematikan proses segera saat deployment, dan semua job yang sedang berlangsung hilang. Antrian tidak tahu bahwa job gagal di tengah jalan — ia hanya tidak pernah melihat sinyal penyelesaian. Job tersebut menghilang ke dalam lubang hitam.

Kubernetes Deployment diharapkan berjalan selamanya dan menggunakan readiness probe untuk mengelola traffic. Sebaliknya, Job berjalan hingga selesai dan menggunakan restartPolicy: Never:

# API service - berjalan selamanya, perlu readiness probe
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
      - name: api
        image: myapp/api:latest
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
---
# Worker - menjalankan satu job, lalu keluar
apiVersion: batch/v1
kind: Job
metadata:
  name: image-resize-job
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: worker
        image: myapp/worker:latest
        command: ["process-queue"]

Scheduler: Waktu adalah Segalanya

Scheduler menjalankan tugas berdasarkan jadwal. Bersihkan data lama pada tengah malam. Sinkronisasi dengan sistem eksternal setiap jam. Hasilkan laporan harian pada jam 6 pagi.

Tantangan dengan scheduler adalah menghindari eksekusi duplikat. Jika scheduler menjalankan job pembersihan, dan kebetulan deployment me-restart scheduler tepat saat job dimulai, ada risiko bahwa instance baru juga memicu job yang sama. Sekarang pembersihan berjalan dua kali, berpotensi menyebabkan konflik atau masalah data.

Pipeline scheduler yang baik memastikan bahwa scheduler tidak memulai instance baru sementara instance lama masih menjalankan tugas terjadwal. Ia juga membutuhkan mekanisme untuk mendeteksi dan melewatkan eksekusi yang tumpang tindih. Beberapa tim menggunakan distributed locks atau penanda database untuk mencegah eksekusi ganda.

Consumer: Mengikuti Arus Data

Consumer mirip dengan worker, tetapi mereka memproses aliran data yang berkelanjutan. Mereka membaca dari message broker seperti Kafka atau RabbitMQ, memproses pesan secara real-time, dan sering kali perlu mempertahankan posisi mereka dalam aliran data.

Perbedaan kritisnya adalah kecepatan dan pelacakan offset. Jika seorang consumer tertinggal, ia perlu mengejar ketinggalan tanpa membebani sistem. Jika seorang consumer crash, ia harus melanjutkan dari tempat terakhir, bukan dari awal.

Untuk CI/CD, ini berarti deployment harus menangani commit offset dengan hati-hati. Mematikan consumer tanpa melakukan commit offset saat ini dapat menyebabkan pemrosesan ulang pesan yang sudah ditangani. Memulai ulang dari offset yang salah dapat melewatkan pesan atau memprosesnya dua kali. Pipeline perlu berkoordinasi dengan message broker untuk memastikan graceful shutdown dan resume.

Internal Service: Dependensi yang Tersembunyi

Internal service tidak diakses oleh aplikasi frontend. Mereka melayani service lain di dalam sistem — autentikasi, konfigurasi, logging, feature flags. Banyak service bergantung pada mereka, seringkali secara sinkron.

Bahaya dengan internal service adalah kegagalan berantai. Perubahan kecil pada service autentikasi dapat merusak setiap service yang memanggilnya. Respons lambat dari service konfigurasi dapat menyebabkan timeout di seluruh sistem.

Service ini memerlukan pengujian yang lebih ketat dan deployment yang lebih hati-hati. Pipeline harus menjalankan integration test yang mensimulasikan perilaku pemanggil nyata. Deployment harus bertahap, dengan pemantauan pada service yang bergantung. Rollback harus cepat dan andal karena radius ledakannya luas.

Checklist Praktis untuk Pipeline Backend Service

Sebelum mendesain pipeline untuk backend service apa pun, tanyakan pertanyaan-pertanyaan ini:

  • Bagaimana service ini menerima pekerjaan? (request, queue, schedule, stream)
  • Apakah bisa dihentikan dengan aman di tengah tugas? Jika ya, apa yang terjadi pada tugas tersebut?
  • Apakah ia menyimpan state di memori yang tidak bisa dipulihkan?
  • Apa yang terjadi jika dua instance menjalankan tugas yang sama secara bersamaan?
  • Service lain mana yang rusak jika service ini mati?
  • Berapa lama service ini bisa tidak tersedia sebelum pengguna atau sistem menyadarinya?

Jawabannya akan memberi tahu Anda apakah Anda memerlukan rolling update, graceful shutdown hooks, koordinasi antrian, atau dependency-aware deployment ordering.

Kesimpulan

Pipeline CI/CD bukanlah skrip satu-ukuran-untuk-semua. Ia adalah cerminan dari cara kerja service Anda sebenarnya. API service membutuhkan kesadaran koneksi. Worker membutuhkan jaminan penyelesaian job. Scheduler membutuhkan pencegahan tumpang tindih. Consumer membutuhkan manajemen offset. Internal service membutuhkan koordinasi dependensi.

Ketika Anda memahami jenis backend service yang Anda tangani, Anda berhenti menyalin template pipeline dari blog post dan mulai membangun pipeline yang melindungi data Anda, pengguna Anda, dan tidur nyenyak tim Anda.