Alur deployment berdasarkan perubahan frontend dan backend
Alur deployment berdasarkan perubahan frontend dan backend. Ilustrasi konsep · Klik untuk memperbesar.
Contoh antarmuka pemberitahuan versi baru dengan perlindungan perubahan belum tersimpan
Contoh antarmuka pemberitahuan versi baru dengan perlindungan perubahan belum tersimpan. Ilustrasi konsep · Klik untuk memperbesar.

Untuk aplikasi React dan Django yang masih kecil, aset frontend bisa disimpan di bucket S3 privat di belakang CloudFront. Satu instance EC2 kecil bisa menjalankan reverse proxy, Django, worker Celery, dan Redis. PostgreSQL juga bisa ditempatkan di sana, selama susunan ini memenuhi kebutuhan pemulihan aplikasi.

Susunan ini belum tentu paling murah untuk setiap aplikasi. Biayanya bergantung pada region, trafik, penyimpanan, alamat IPv4 publik, backup, serta waktu yang dibutuhkan untuk mengelolanya. Sebelum memilih layanan, tentukan berapa lama aplikasi boleh tidak tersedia dan berapa banyak data yang boleh hilang.

Berbagi satu instance EC2

Menempatkan API, worker, broker, dan database dalam satu mesin mengurangi jumlah layanan yang perlu dikelola, tetapi semuanya berebut sumber daya yang sama. Impor data dalam jumlah besar bisa menghabiskan memori dan mengganggu request lain. Disk penuh dapat menghentikan PostgreSQL sekaligus Redis. Jika tipe instance memakai kredit CPU, pantau sisa kreditnya bersama persentase penggunaan CPU.

Batasi memori proses, mulai dengan concurrency worker yang kecil, dan sisakan kapasitas untuk PostgreSQL. PostgreSQL serta Redis sebaiknya hanya mendengarkan koneksi lokal atau jaringan privat. Keduanya tidak perlu terbuka ke internet. Gunakan TLS di reverse proxy, batasi port masuk, dan pertimbangkan Systems Manager agar akses operasional tidak bergantung pada SSH yang terbuka luas.

RDS menangani patch database, pengelolaan backup, dan sebagian proses pemulihan. Dukungan failover lintas zona bergantung pada konfigurasi yang dipilih, bukan otomatis tersedia karena memakai layanan terkelola. Biaya RDS lebih tinggi daripada menjalankan PostgreSQL di mesin yang sudah ada, tetapi bisa lebih rendah daripada biaya memelihara prosedur pemulihan sendiri. Apa pun pilihannya, simpan backup di luar instance dan uji proses restore.

Tentukan build dari dependensi monorepo

Filter direktori saja tidak cukup untuk menentukan komponen yang perlu di-build. Perubahan lockfile di root bisa mengubah dependensi frontend tanpa menyentuh direktori frontend. API client hasil generator dapat berubah akibat perubahan skema Django. Dockerfile bersama atau skrip rilis mungkin memengaruhi kedua aplikasi.

Tuliskan aturan dependensi sebelum mengoptimalkan pipeline:

apps/web/**                  -> frontend
apps/api/**                  -> backend
packages/contracts/**        -> frontend + backend
pnpm-lock.yaml               -> frontend
pyproject.toml, uv.lock       -> backend
.github/workflows/**         -> frontend + backend
infra/**, scripts/release/**  -> frontend + backend

Contoh aturan ini perlu disesuaikan dengan konfigurasi CI proyek. Tambahkan input build lain yang dipakai bersama oleh Python dan JavaScript. Jika dampak suatu perubahan belum jelas, jalankan build agar hasil lama tidak ikut masuk produksi.

Untuk pull request, perbandingan terhadap merge base biasanya sesuai. Untuk deployment produksi, bandingkan dengan commit terakhir yang berhasil di-deploy untuk setiap komponen. Jangan hanya memakai commit sebelumnya pada branch. Jika deployment gagal lalu commit berikutnya mengubah komponen lain, komponen yang gagal tadi tetap harus dikirim ulang. Jalankan build lengkap jika revisi dasar tidak ditemukan atau riwayat checkout terlalu dangkal. Sediakan pemeriksaan CI ringan yang selalu berjalan agar required check tidak menggantung ketika job komponen dilewati.

Berikan kredensial sementara kepada CI

Gunakan OIDC GitHub Actions untuk menjalankan assume role AWS tanpa menyimpan access key AWS berumur panjang. Trust policy role harus membatasi audience dan subject ke repository serta environment deployment yang dituju. Aturan persetujuan rilis bisa dipasang pada environment produksi yang dilindungi.

permissions:
  contents: read
  id-token: write

concurrency:
  group: production-release
  cancel-in-progress: false

Potongan tersebut mengizinkan penerbitan token, tetapi belum memberi izin ke resource AWS. Batasi role pada prefix bucket frontend, artefak deployment, dan instance atau mekanisme deployment yang diperlukan. Jangan memasukkan secret database ke variabel build frontend. Pin action pihak ketiga ke commit SHA yang sudah ditinjau. Kode pull request yang belum dipercaya tidak boleh berjalan dalam job deployment berizin tinggi.

Jalankan promosi rilis satu per satu. Sebelum mengalihkan trafik, pastikan versi yang sedang aktif sesuai dengan versi pendahulu yang diharapkan. Dua build bisa selesai dalam urutan terbalik meskipun tahap deployment memakai lock yang sama.

Deploy artefak dengan versi tetap

Build setiap rilis sekali dan beri identitas tetap, biasanya commit SHA. Simpan artefak backend beserta manifest dependensinya dengan identitas tersebut. Di EC2, ekstrak ke direktori rilis baru. Jangan menimpa file aplikasi yang sedang berjalan satu per satu.

Jalankan pemeriksaan konfigurasi, siapkan file statis, lalu hidupkan proses aplikasi baru pada port lokal yang belum dipakai. Periksa readiness melalui proses itu, termasuk query database ringan. Setelah lolos, ganti konfigurasi upstream reverse proxy secara atomik, validasi, kemudian reload proxy. Biarkan proses lama menyelesaikan request yang masih berjalan. Pergantian symlink secara atomik hanya memilih direktori rilis; tindakan itu sendiri tidak me-restart proses dan tidak menjamin tanpa downtime.

Untuk frontend, unggah aset dengan nama berbasis hash terlebih dahulu dan simpan aset rilis sebelumnya. Publikasikan dokumen HTML setelah seluruh file yang dirujuk tersedia. Gunakan cache berumur panjang dan immutable untuk aset ber-hash, sedangkan HTML serta manifest versi harus divalidasi ulang. Pastikan pengaturan CloudFront mengikuti kebijakan tersebut: minimum TTL di atas nol bisa membuat CloudFront tetap menyimpan cache meskipun origin mengirim header no-cache. Selama cache diperbarui, tiap browser bisa menerima rilis baru pada waktu yang berbeda.

Migrasi database bisa menyulitkan rollback. Tambahkan kolom nullable atau tabel baru terlebih dahulu, deploy kode yang kompatibel dengan kedua skema, lalu lakukan backfill secara terpisah. Hapus field lama pada rilis berikutnya. Jalankan migrasi sekali dengan lock deployment dan ukur durasi lock memakai data yang realistis. Rollback kode aplikasi tidak membatalkan migrasi destruktif atau mengembalikan data yang sudah dihapus.

Beri tahu tab yang masih terbuka

Demo interaktif — tidak memuat ulang halaman ini.

Tab lama terus menjalankan JavaScript dari rilis awalnya. Publikasikan manifest versi kecil setelah promosi frontend selesai. Periksa manifest hanya ketika halaman terlihat dan browser menganggap koneksi tersedia. Status online hanya petunjuk; request tetap bisa gagal.

export function watchVersion(currentVersion, showUpdate) {
  let stopped = false;
  let pending = false;
  const controller = new AbortController();

  async function check() {
    if (stopped || pending || document.hidden || !navigator.onLine) return;
    pending = true;
    try {
      const response = await fetch('/version.json', {
        cache: 'no-store', signal: controller.signal,
      });
      if (!response.ok) return;
      const next = await response.json();
      if (!stopped && typeof next.version === 'string' &&
          next.version !== currentVersion) showUpdate(next.version);
    } catch {
      // Pemeriksaan gagal tidak boleh mengganggu pekerjaan pengguna.
    } finally {
      pending = false;
    }
  }

  const timer = setInterval(check, 60000);
  document.addEventListener('visibilitychange', check);
  window.addEventListener('online', check);
  check();
  return () => {
    stopped = true;
    controller.abort();
    clearInterval(timer);
    document.removeEventListener('visibilitychange', check);
    window.removeEventListener('online', check);
  };
}

Callback sebaiknya menampilkan notifikasi “Pembaruan tersedia” yang bisa ditutup, dengan tombol untuk memperbarui halaman. Jangan langsung memuat ulang halaman. Saat tombol diklik, periksa perubahan formulir yang belum disimpan, unggahan aktif, dan mutasi yang belum selesai. Beri pengguna pilihan untuk menyimpan draf atau tetap di halaman. Muat ulang hanya setelah pengguna memberi konfirmasi atau pekerjaannya selesai dengan aman. Format draf yang disimpan perlu memiliki versi agar kode baru dapat membacanya. Jika memakai service worker, atur juga siklus pembaruannya agar sesuai dengan proses ini.

Setelah upload berhasil, periksa hasil deployment dengan menguji request API yang memakai autentikasi, satu task dalam queue, tab browser lama, dan rollback ke artefak sebelumnya. Catat versi yang aktif.

Referensi