Canary deploy pada CodeIgniter 4 adalah cara merilis versi baru ke sebagian kecil trafik lebih dulu, lalu mengamati gejala masalah sebelum seluruh instance ikut menerima rilis. Pendekatan ini berguna ketika aplikasi PHP berjalan di beberapa VM, pod, atau container, karena kegagalan rilis sering tidak muncul saat build, tetapi baru terlihat saat trafik nyata, query database aktual, atau integrasi eksternal dipakai pengguna.

Jika tujuan Anda adalah deteksi dini masalah, rollback cepat, dan evaluasi insiden yang rapi, maka fokusnya bukan hanya pada proses deploy. Anda perlu menyiapkan artefak yang konsisten, migrasi database yang aman untuk versi lama dan baru, health check yang benar-benar berguna, smoke test yang singkat, log terstruktur, metrik minimum, serta aturan jelas kapan canary dihentikan. Di akhir, postmortem ringan membantu tim belajar tanpa membuat proses terlalu berat.

Mengapa canary deploy cocok untuk CodeIgniter 4

Pada aplikasi PHP tradisional, banyak tim masih melakukan deploy ke semua instance sekaligus. Masalahnya, satu perubahan kecil dapat memicu error yang hanya muncul pada kombinasi data tertentu: kolom baru yang belum ada di semua node, cache schema yang belum sinkron, dependency eksternal melambat, atau perubahan konfigurasi yang tidak kompatibel.

Canary deploy mengurangi radius kegagalan. Anda mendorong satu artefak baru ke satu atau dua instance, mengarahkan sebagian kecil trafik ke sana, lalu mengecek:

  • apakah aplikasi bisa boot dengan benar,
  • apakah koneksi database dan cache normal,
  • apakah endpoint penting tetap sehat,
  • apakah error rate, latency, dan log anomali meningkat.

Jika hasilnya baik, trafik dinaikkan bertahap. Jika buruk, instance canary dikeluarkan dari pool tanpa menyentuh mayoritas pengguna.

Arsitektur rilis sederhana di beberapa instance atau container

Untuk implementasi minimal, Anda tidak perlu platform yang kompleks. Pola sederhananya:

  1. Build satu artefak aplikasi untuk semua environment.
  2. Push artefak ke registry atau server artefak.
  3. Deploy ke canary instance lebih dulu.
  4. Jalankan health check dan smoke test.
  5. Alihkan 1-10% trafik melalui load balancer atau ingress.
  6. Pantau metrik minimum selama beberapa menit.
  7. Jika aman, lanjutkan ke seluruh instance.
  8. Jika gagal, keluarkan canary dari rotasi dan rollback cepat.

Poin pentingnya adalah immutability: jangan membangun aplikasi langsung di server produksi jika bisa dihindari. Artefak yang sama harus bisa dipromosikan dari staging ke produksi agar perbedaan perilaku lebih kecil.

Contoh alur rilis

  • Versi N berjalan di 5 container.
  • Versi N+1 dideploy ke 1 container canary.
  • Ingress mengarahkan 5% trafik ke canary.
  • Tim memantau error 5xx, waktu respons, dan log exception.
  • Jika stabil, rilis dinaikkan ke 50%, lalu 100%.

Persiapan artefak dan konfigurasi rilis

1. Bangun artefak yang konsisten

Untuk CodeIgniter 4, praktik aman biasanya mencakup:

  • install dependency saat build, bukan di server produksi,
  • pastikan file env atau variabel environment dipisahkan dari artefak,
  • hindari perubahan manual di instance produksi,
  • sertakan informasi versi atau commit SHA untuk pelacakan.

Contoh langkah build yang umum:

composer install --no-dev --optimize-autoloader
php spark optimize
# opsional: jalankan test dan static check di pipeline sebelum artefak dipublish

Perintah optimasi dapat berbeda sesuai kebutuhan aplikasi Anda. Yang penting, artefak hasil build dapat dijalankan identik di semua node.

2. Simpan identitas rilis

Tambahkan identitas rilis ke environment, misalnya:

APP_VERSION=2026.06.13-abc123
APP_ENV=production
CANARY_ENABLED=true

Nilai ini berguna untuk:

  • ditampilkan pada endpoint health/debug internal,
  • ditulis ke log terstruktur,
  • memudahkan korelasi antara lonjakan error dan versi rilis.

3. Pisahkan konfigurasi sensitif

Jangan menanam secret di image atau repository. Gunakan secret manager, variable environment, atau mekanisme deployment platform Anda. Masalah autentikasi ke database, Redis, atau layanan pihak ketiga sering terlihat seperti bug aplikasi, padahal sumbernya konfigurasi.

Migrasi database aman untuk canary

Bagian paling berisiko dalam rilis biasanya bukan kode PHP, tetapi perubahan skema database. Untuk canary deploy, skema harus kompatibel untuk versi lama dan baru selama masa transisi.

Prinsip migrasi aman

  • Perluas dulu, hapus belakangan: tambah kolom/indeks baru lebih aman daripada langsung menghapus kolom lama.
  • Hindari perubahan destruktif saat cutover: rename kolom, drop kolom, atau mengubah tipe secara agresif sebaiknya dipisahkan ke rilis berikutnya.
  • Aplikasi lama dan baru harus bisa berjalan bersama selama rollout bertahap.
  • Backfill data dilakukan terpisah jika butuh waktu lama.

Contoh strategi expand-and-contract

Misalnya Anda ingin memindahkan penyimpanan status dari status ke order_status.

  1. Rilis 1: tambahkan kolom order_status, aplikasi baru menulis ke kedua kolom.
  2. Backfill data lama di background.
  3. Rilis 2: semua pembacaan pindah ke order_status.
  4. Rilis 3: kolom lama baru dihapus setelah stabil.

Pendekatan ini lebih aman daripada mengganti skema sekaligus, karena rollback aplikasi masih mungkin dilakukan tanpa segera merusak data.

Contoh migrasi CodeIgniter 4

<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class AddOrderStatusColumn extends Migration
{
    public function up()
    {
        $this->forge->addColumn('orders', [
            'order_status' => [
                'type'       => 'VARCHAR',
                'constraint' => 32,
                'null'       => true,
            ],
        ]);
    }

    public function down()
    {
        $this->forge->dropColumn('orders', 'order_status');
    }
}

Catatan: method down() tidak selalu aman dipakai di produksi, terutama bila data baru sudah terisi. Untuk rollback insiden, sering kali lebih aman rollback aplikasi sambil membiarkan perubahan skema tetap ada, asalkan kompatibel.

Kapan migrasi dijalankan?

Untuk sistem multi-instance, hindari setiap container menjalankan migrasi otomatis saat startup. Itu berisiko menimbulkan race condition. Lebih aman jika pipeline atau job terpisah menjalankan migrasi sekali sebelum trafik dialihkan ke versi baru.

Health check dan smoke test yang benar-benar berguna

Health check dasar

Health check bukan sekadar endpoint yang mengembalikan 200 OK. Endpoint ini sebaiknya memverifikasi dependensi minimum agar instance benar-benar layak menerima trafik.

Contoh komponen health check:

  • proses aplikasi bisa boot,
  • koneksi database berhasil,
  • cache atau session store aktif bila wajib,
  • versi aplikasi terbaca.

Contoh controller sederhana di CodeIgniter 4:

<?php

namespace App\Controllers;

use CodeIgniter\RESTful\ResourceController;
use Config\Database;

class HealthController extends ResourceController
{
    public function live()
    {
        return $this->respond([
            'status' => 'ok',
            'version' => env('APP_VERSION'),
        ]);
    }

    public function ready()
    {
        try {
            $db = Database::connect();
            $db->query('SELECT 1');

            return $this->respond([
                'status' => 'ready',
                'version' => env('APP_VERSION'),
            ]);
        } catch (\Throwable $e) {
            return $this->respond([
                'status' => 'failed',
                'version' => env('APP_VERSION'),
            ], 503);
        }
    }
}

Rute yang umum:

$routes->get('/health/live', 'HealthController::live');
$routes->get('/health/ready', 'HealthController::ready');

Live check dipakai untuk mengetahui proses masih hidup. Ready check dipakai load balancer atau orchestrator untuk menentukan apakah instance layak menerima trafik.

Smoke test setelah deploy

Smoke test sebaiknya singkat, deterministik, dan menyentuh alur kritis. Jangan membuatnya panjang seperti regression test penuh.

Contoh smoke test minimum:

  • GET halaman utama atau endpoint API publik,
  • login ke akun uji khusus,
  • akses satu endpoint yang membaca database,
  • buat request tulis sederhana bila aman,
  • verifikasi respons bukan 5xx dan format JSON valid.

Contoh skrip shell sederhana:

set -e

BASE_URL="$1"

curl -fsS "$BASE_URL/health/ready" > /dev/null
curl -fsS "$BASE_URL/api/ping" | grep -q 'ok'

HTTP_CODE=$(curl -s -o /tmp/home.out -w "%{http_code}" "$BASE_URL/")
[ "$HTTP_CODE" = "200" ]

Smoke test idealnya dijalankan ke instance canary secara langsung, bukan ke domain publik yang masih bisa diarahkan ke versi lama.

Metrik dasar, log terstruktur, dan alert minimum

Metrik minimum yang wajib dipantau

Anda tidak perlu observability yang rumit untuk memulai canary deploy. Minimal, pantau:

  • error rate: proporsi respons 5xx atau exception aplikasi,
  • latency: p95 atau setidaknya rata-rata waktu respons,
  • throughput: jumlah request per menit,
  • resource: CPU, memori, restart container,
  • DB symptoms: timeout koneksi, deadlock, query lambat.

Tanpa angka dasar ini, Anda sulit membedakan masalah canary dari kebisingan normal.

Log terstruktur

Saat rilis gagal, log teks bebas sering sulit dicari. Lebih baik catat event penting dalam format konsisten, misalnya JSON atau key-value yang mudah diparsing.

Contoh pseudocode logging:

log_message('error', json_encode([
    'event' => 'checkout_failed',
    'release' => env('APP_VERSION'),
    'request_id' => service('request')->getHeaderLine('X-Request-Id'),
    'user_id' => $userId ?? null,
    'message' => $e->getMessage(),
]));

Field yang berguna:

  • event,
  • release,
  • request_id,
  • path,
  • user_id bila relevan dan aman,
  • exception_class,
  • message.

Hindari menulis data sensitif seperti password, token mentah, atau payload pribadi lengkap.

Alert minimum

Untuk rilis awal, alert minimum biasanya cukup:

  • error 5xx meningkat signifikan dibanding baseline,
  • health check canary gagal,
  • latency endpoint kritis naik tajam,
  • restart container berulang,
  • query timeout meningkat.

Jika Anda belum punya sistem alert canggih, bahkan notifikasi dari pipeline plus pemeriksaan dashboard manual selama jendela rilis lebih baik daripada rilis buta.

Aturan kapan canary dihentikan

Canary deploy gagal bukan berarti seluruh sistem harus runtuh dulu. Anda perlu kriteria penghentian yang jelas sebelum rilis dimulai. Contoh indikator untuk menghentikan canary:

  • health check readiness gagal berulang,
  • smoke test gagal pada endpoint kritis,
  • muncul exception baru yang sebelumnya tidak ada,
  • error rate pada canary konsisten lebih buruk dari versi stabil,
  • latency meningkat cukup besar hingga memengaruhi pengguna,
  • terlihat gejala migrasi data tidak kompatibel,
  • integrasi pihak ketiga mulai timeout hanya pada versi baru.

Jangan menunggu semua metrik memburuk. Satu gejala serius pada alur bisnis utama, seperti checkout atau autentikasi, biasanya cukup untuk menghentikan rollout.

Prinsip praktis: jika tim tidak yakin apakah canary aman untuk dilanjutkan, anggap belum aman. Menunda rollout lebih murah daripada memperluas dampak insiden.

Rollback cepat tanpa memperparah insiden

Rollback yang baik adalah rollback yang sederhana, cepat, dan minim perubahan baru. Saat insiden berlangsung, hindari improvisasi seperti edit file langsung di server, menjalankan patch manual tanpa jejak, atau mengubah banyak komponen sekaligus.

Langkah rollback yang aman

  1. Keluarkan instance canary dari rotasi trafik.
  2. Bekukan rollout agar tidak ada node lain menerima versi baru.
  3. Pastikan versi stabil masih sehat.
  4. Rollback aplikasi ke artefak terakhir yang diketahui baik.
  5. Jangan langsung rollback skema database kecuali benar-benar perlu dan aman.
  6. Pantau sistem sampai metrik kembali normal.
  7. Simpan bukti insiden: log, timestamp, commit, hasil smoke test.

Mengapa rollback database sering bukan langkah pertama

Rollback database dapat memperparah situasi jika:

  • data baru sudah ditulis oleh aplikasi baru,
  • perubahan skema bersifat destruktif,
  • rollback memerlukan lock panjang,
  • versi lama sebenarnya masih kompatibel dengan skema baru.

Karena itu, desain migrasi yang kompatibel jauh lebih penting daripada bergantung pada rollback skema.

Contoh pseudocode alur pipeline

deploy_canary()
run_db_migration_once()
wait_until_ready(canary)
run_smoke_test(canary)
shift_traffic(canary, 5%)
observe_metrics(10m)

if metrics_bad or smoke_test_failed:
    remove_from_rotation(canary)
    rollback_app(canary)
    mark_release_failed()
else:
    rollout_remaining_instances()

Detail implementasi fungsi di atas tergantung platform Anda, tetapi logikanya sebaiknya tetap sederhana dan mudah diaudit.

Checklist operasional sebelum, saat, dan sesudah rilis

Sebelum rilis

  • Artefak build sukses dan identitas versi tercatat.
  • Perubahan konfigurasi sudah direview.
  • Migrasi database ditinjau untuk kompatibilitas maju-mundur.
  • Health check dan smoke test siap dijalankan.
  • Dashboard metrik dan log sudah disiapkan.
  • Rollback plan untuk aplikasi sudah jelas.
  • Penanggung jawab rilis dan jalur eskalasi sudah ditetapkan.

Saat canary aktif

  • Pastikan hanya sebagian kecil trafik masuk ke canary.
  • Periksa readiness dan smoke test awal.
  • Pantau 5xx, latency, restart, dan log exception.
  • Bandingkan canary dengan instance stabil, bukan melihat angka mentah saja.
  • Catat waktu perubahan trafik: 1%, 5%, 25%, 100%.

Jika rilis gagal

  • Hentikan kenaikan trafik.
  • Keluarkan canary dari rotasi.
  • Rollback aplikasi ke versi stabil.
  • Verifikasi metrik kembali normal.
  • Jangan ubah banyak hal sekaligus saat investigasi awal.

Setelah rilis atau rollback

  • Simpan timeline kejadian.
  • Kumpulkan contoh request gagal dan log terkait.
  • Catat keputusan yang diambil selama insiden.
  • Jadwalkan postmortem ringan maksimal 24-48 jam setelah kejadian.

Contoh konfigurasi routing trafik canary

Implementasi trafik canary biasanya ada di load balancer, API gateway, ingress, atau service mesh. Karena detailnya berbeda-beda, berikut contoh konfigurasi konseptual yang umum:

upstream app_stable {
    server app-v1-1:8080;
    server app-v1-2:8080;
    server app-v1-3:8080;
}

upstream app_canary {
    server app-v2-1:8080;
}

# pseudocode weighted routing
route / {
    95% -> app_stable
    5%  -> app_canary
}

Jika platform Anda tidak mendukung weighted routing, alternatif paling sederhana adalah mengarahkan trafik internal QA atau header tertentu ke canary lebih dulu. Ini bukan canary penuh, tetapi tetap berguna untuk validasi awal sebelum rollout luas.

Postmortem ringan setelah rilis gagal

Postmortem ringan bertujuan belajar cepat tanpa birokrasi berlebihan. Fokusnya bukan mencari siapa yang salah, tetapi memahami kenapa sistem, proses, atau pengujian gagal menangkap masalah lebih awal.

Isi minimal postmortem

  • Timeline: kapan deploy dimulai, kapan canary aktif, kapan gejala muncul, kapan rollback selesai.
  • Dampak: fitur mana yang terganggu, durasi, pengguna yang terdampak, apakah ada data yang salah.
  • Root cause: penyebab teknis utama dan faktor pendukung.
  • Perbaikan: apa yang dilakukan untuk memulihkan layanan.
  • Pencegahan: perubahan proses, test, observability, atau desain.

Template postmortem singkat

Judul insiden:
Rilis gagal pada canary CodeIgniter 4 - endpoint checkout error 500

Tanggal:
2026-06-13

Ringkasan:
Versi baru menyebabkan error 500 pada checkout di instance canary setelah 5% trafik dialihkan.
Canary dihentikan dan aplikasi di-rollback ke versi stabil.

Timeline:
- 10:00 deploy canary dimulai
- 10:05 migrasi database selesai
- 10:08 smoke test lolos
- 10:12 5% trafik dialihkan ke canary
- 10:15 error 500 meningkat pada /checkout
- 10:17 canary dikeluarkan dari rotasi
- 10:20 rollback aplikasi selesai
- 10:25 metrik kembali normal

Dampak:
- Sebagian kecil pengguna gagal checkout selama sekitar 5 menit
- Tidak ada indikasi kehilangan data permanen

Root cause:
- Kode baru membaca kolom baru yang belum di-backfill untuk sebagian data lama
- Smoke test tidak mencakup skenario data lama tersebut

Faktor pendukung:
- Tidak ada alert khusus untuk lonjakan error endpoint checkout
- Validasi kompatibilitas data lama kurang

Perbaikan saat insiden:
- Canary dihentikan
- Aplikasi di-rollback ke versi stabil

Tindakan pencegahan:
- Tambah smoke test untuk data lama dan baru
- Tambah alert khusus endpoint checkout
- Gunakan strategi dual-read/dual-write sementara
- Review migrasi dengan checklist kompatibilitas

Kesalahan umum yang sering terjadi

  • Menganggap health check 200 berarti aman. Padahal koneksi DB, cache, atau dependency lain belum diuji.
  • Menjalankan migrasi otomatis di semua instance. Ini berisiko konflik dan hasil tidak konsisten.
  • Rollback database terlalu cepat. Sering justru menambah risiko kehilangan data atau lock berat.
  • Tidak menyimpan identitas rilis di log. Akibatnya investigasi lebih lama.
  • Tidak punya kriteria stop. Tim akhirnya terus menaikkan trafik walau gejala awal sudah terlihat.
  • Smoke test terlalu dangkal. Lolos di endpoint ping, gagal di alur bisnis nyata.

Penutup

Untuk CodeIgniter 4: Canary Deploy dan Postmortem Ringan saat Rilis Gagal, kunci keberhasilannya bukan alat yang paling canggih, melainkan disiplin operasional yang sederhana dan konsisten. Mulailah dari artefak yang konsisten, migrasi database yang kompatibel, health check yang bermakna, smoke test singkat, metrik minimum, log terstruktur, dan aturan rollback yang jelas.

Dengan pendekatan ini, rilis gagal tidak harus berubah menjadi insiden besar. Anda bisa mendeteksi masalah lebih awal, membatasi dampak lewat canary, memulihkan layanan dengan rollback cepat, lalu menutup kejadian dengan postmortem ringan yang menghasilkan perbaikan nyata.