Production Promotion Toolkit
On this page
- Sekilas
- Yang dikerjakannya
- A. Membangun production satu kali
- B. Mempromosikan struktur rilis, bukan nilainya
- C. Menolak menyalin rahasia
- D. Menerapkan perubahan skema yang dibawa image
- E. Memperlihatkan setiap perubahan sebelum melakukannya
- Cara saya memakainya
- Keputusan rancangan
- Spesifikasi
- Terkait
- Istilah
Production Promotion Toolkit adalah tiga perintah yang membawa rilis yang sudah teruji ke production tanpa pernah menimpa apa yang menjadi milik production. Satu perintah membangun environment production pertama kali; satu mempromosikan konfigurasi tiap rilis dari staging; satu lagi menerapkan perubahan skema database yang dibawa image rilis itu. Bersama-sama, satu rilis bisa mengubah apa yang memang perlu diubahnya, dan tak lebih.
Mempromosikan rilis tampak seperti menyalin file dari staging ke production. Kenyataannya tidak, karena kedua environment berbagi strukturnya tapi tidak berbagi rahasia maupun alamatnya — dan salah satu rahasia itu adalah kunci yang membuat data terenkripsi bertahun-tahun tetap terbaca.
Sekilas
| Tools | gen-production-env, promote-to-production, migrate-ca |
|---|---|
| Bentuk | Script command-line yang berbagi satu library pembantu |
| Bahasa | Bash, dengan Python tertanam untuk perbandingan per kunci pada JSON bersarang, yang mengendalikan Docker dan rsync |
| Status | Stabil, berjalan di production |
| Kode sumber | Privat |
Yang dikerjakannya
A. Membangun production satu kali
Environment production pertama dibangun dari template development, dengan setiap alamat ditulis ulang untuk network milik production, yang dibaca dari state jaringan yang tercatat tanpa fallback — fallback lama menunjuk ke subnet yang sudah tak ada, dan production akan lahir di sana tanpa satu peringatan pun. Penulisan ulangnya mencocokkan teks secara literal dan menolak selesai bila masih ada alamat development tersisa. Generator bisa menyalin folder data, tapi hanya dari sumber yang sedang berhenti; prosedur yang saya ikuti mengisi production lewat toolkit backup. Setiap modul mendapat nama project dan path data milik production sendiri, jadi menyalakannya tak bisa menggantikan container development, dan network-nya harus sudah ada sebelum apa pun ditulis. Konfigurasi yang dibaca certificate authority dibuat bisa dibaca user di dalam image-nya, dan sertifikat CA serta daftar pencabutannya tetap tiba walau data dilewati. Generator sengaja tidak menyalakan stack — menyalakan production adalah keputusan operator — dan mencetak dua langkah yang wajib menyusul, salah satunya membuat user telemetry lewat mode tersendiri. Begitu production ada, ia tak pernah dibangun ulang; sejak saat itu ia hanya berubah lewat promosi.
B. Mempromosikan struktur rilis, bukan nilainya
Untuk setiap service, promosi membandingkan konfigurasi staging dengan production, kunci demi kunci, baik di file environment maupun JSON bersarang. Kunci yang ditambahkan ikut ditambahkan, kunci yang dihapus ikut dihapus, dan nilai non-sensitif yang berubah — batas, timeout, feature flag — diperbarui. Rahasia dan alamat tetap memakai nilai milik production. Kunci yang terhapus dibuang per baris, jadi komentar dan urutannya selamat. Versi image tiap modul ikut dipromosikan, tapi dari file compose hanya baris image: yang pindah, tak pernah file utuhnya: production pernah tertinggal di nama image lama yang baru ketahuan ketika migration gagal, dan file utuh membawa fallback alamat per environment yang tak boleh menyeberang. Di JSON, label deployment environment dihitung sebagai milik production, jadi telemetry production tak pernah berlabel development lalu bercampur dengannya. Alamat dikenali lewat pola nama, termasuk bentuk tunggalnya, setelah satu alamat internal pernah diperlakukan sebagai nilai biasa dan membawa alamat development ke production. Kredensial storage dijaga tetap sama di setiap file yang memuatnya, setelah gambar yang rusak di production ternyata error signature dengan setiap objek masih utuh.
C. Menolak menyalin rahasia
Ketika staging punya rahasia yang belum dimiliki production, promosi tidak menyalinnya. Semua perubahan lain tetap diterapkan, tapi run itu menyebut setiap rahasia yang diblokir dan diakhiri dengan kegagalan sampai ada orang yang menetapkan nilai milik production dengan tangan. Kunci alamat baru disalin, diperingatkan di tempatnya muncul, lalu didaftar ulang di ringkasan, karena peringatan di tengah output panjang mudah terlewat — satu base URL baru pernah membawa alamat development ke production dan halaman status aplikasi menjawab 503. Modul yang ada di staging tapi belum ada di production dilaporkan dan mengakhiri run dengan kode berbeda: promosi tak membuat service, dan dulu ia pernah menulis kunci untuk service yang tak dimiliki production lalu menyebut hasilnya sudah selaras. Rahasia yang diblokir lebih diutamakan daripada modul yang hilang, dan ringkasannya membedakan "tak ada perubahan" dari "tak ada perubahan, tapi ada modul yang hilang".
D. Menerapkan perubahan skema yang dibawa image
Sebagian service tak memigrasi database-nya sendiri saat menyala; skemanya dibuat oleh init script yang hanya dijalankan database ke data directory yang masih kosong. Begitu environment berisi data hasil restore, script-script itu tak pernah jalan lagi. Perintah migration menerapkan setiap migration yang tertunda secara eksplisit, mengambil migration-nya dari dalam image service, dan mencatatnya di tabel migration yang memang sudah dipakai service itu, dengan versi yang diambil dari prefix timestamp tiap nama file, sama seperti migrator milik service itu sendiri. Tiap migration berjalan sebagai satu transaksi yang berhenti di error pertama, jadi tak ada yang setengah diterapkan, dan satu modul berhenti di migration gagal pertamanya karena migration sesudahnya biasanya bergantung padanya. Prefix container dibaca dari file environment, bukan ditebak dari nama folder. Database-nya harus sudah berjalan, sementara container aplikasi cukup ada, karena yang dibutuhkan hanya file-nya. Service yang image-nya tak membawa migration dilewati, dengan permintaan agar image-nya di-build ulang, dan run menghitungnya sebagai kegagalan.
E. Memperlihatkan setiap perubahan sebelum melakukannya
Promosi maupun migration punya dry run yang mencetak persis apa yang akan berubah tanpa mengubah apa pun; dry run migration bahkan tak membuat tabel pencatatnya. Setiap file compose, file environment, dan konfigurasi JSON yang ditulis ulang promosi di-backup lebih dulu, di samping aslinya, dengan timestamp. Di akhir ia mencetak langkah berikutnya, ditutup dengan menandatangani satu dokumen, karena container yang hijau tak membuktikan penandatanganan bekerja.
Cara saya memakainya
&&
| Jenis kunci | Contoh | Yang dilakukan promosi |
|---|---|---|
| Rahasia | key aplikasi, password database, private key, token | mempertahankan nilai production; rahasia baru memblokir promosi |
| Alamat | host database, URL service, origin, trusted proxy | mempertahankan nilai production; alamat baru disalin dan didaftar ulang untuk diperiksa |
| Struktur | kunci baru atau terhapus, rate limit, masa berlaku, feature flag | dipromosikan |
| Exit code | Arti |
|---|---|
| 0 | production sudah selaras dengan rilisnya |
| 1 | setidaknya satu rahasia baru diblokir dan harus ditetapkan dengan tangan |
| 2 | ada service di staging yang belum ada di production; promosi tak membuat service baru. Bila ada rahasia yang juga diblokir, 1 yang menang |
Keputusan rancangan
Ia mempromosikan struktur, karena nilainyalah yang membuat production menjadi production. Ketika saya membandingkan dua environment milik satu service, keduanya punya 136 kunci yang persis sama, dan 21 di antaranya wajib tetap bernilai berbeda. Menyalin file-nya akan memberi production password database milik staging, alamat milik staging, dan — paling parah — application key milik staging, yang mendekripsi kolom-kolom yang bertahun-tahun lalu dienkripsi dengan kunci milik production. Karena itu promosi hanya memindahkan apa yang sah diubah oleh rilis, memahami konfigurasi datar maupun bersarang, dan memperlihatkan diff-nya lewat dry run sebelum menulis apa pun, dengan backup untuk setiap file yang disentuhnya.
Ia tak pernah menyalin rahasia, sekalipun itu praktis. Rahasia yang hanya ada di staging adalah kredensial baru yang dibutuhkan rilis. Menyalinnya membuat staging dan production berbagi kredensial, sehingga kebocoran dari environment yang penjagaannya lebih longgar langsung menjadi kebocoran production. Promosi mengenali rahasia lewat daftar eksplisit dan pola nama, menolak membawanya menyeberang, lalu gagal dengan suara keras supaya keputusannya diambil oleh orang, sekali, dengan sengaja. Daftar itu dijaga selaras dengan daftar yang dirotasi tool rotasi, jadi kunci bernama tak lazim, seperti kunci enkripsi aplikasi, ikut tertangkap.
Migration ikut di dalam image, jadi server tak butuh apa pun selain image-nya. Kegagalan yang dicegahnya senyap: service dengan skema usang tetap menyala, lolos health check di kolom-kolom lama, dan baru gagal ketika ada request yang menyentuh kolom baru. Mengambil migration dari dalam image berarti migration selalu cocok dengan biner yang memakainya, dan server tanpa source code pun tetap bisa menerapkannya. Mencatatnya di tabel migration yang sudah dimiliki service, bukan tabel buatan saya sendiri, menjaga satu riwayat saja dan membuat menjalankan ulang perintahnya tak berbahaya.
Production dibangun sekali, dengan hati-hati, dan tak pernah lagi. Bootstrap menolak menyalin data dari sumber yang container-nya masih berjalan, karena menyalin file database yang hidup berisiko menghasilkan salinan korup; jalur amannya, dan jalur yang saya pakai, membangun konfigurasi saja, lalu mengisi data lewat backup dan restore. Ia tak pernah menyalakan stack sendiri, dan ia menyebut langkah telemetry sebagai wajib, karena ketika langkah itu pernah terlewat setiap service tetap sehat sementara dua hari log, trace, dan error aplikasi terbuang. Sesudah hari pertama itu, production hanya berubah lewat promosi, jadi setiap perubahannya sudah melewati staging dan dry run.
Spesifikasi
| Masukan | Versi staging yang dipromosikan; opsional tujuan lain dan satu service saja |
|---|---|
| Keluaran | Konfigurasi dan versi image production yang diperbarui, backup bertimestamp untuk setiap file yang berubah, dan migration yang diterapkan serta tercatat di tabel migration milik service |
| Format | File environment dan konfigurasi JSON bersarang, dibandingkan kunci demi kunci |
| Keamanan | Dry run untuk promosi maupun migration; rahasia dan alamat dipertahankan; rahasia baru tak pernah disalin; backup sebelum menulis ulang konfigurasi; tak ada data yang disalin dari sumber yang sedang berjalan; migration dalam satu transaksi |
| Prasyarat | Docker, rsync, dan Python 3 di host; migration ikut di dalam image service |
Terkait
Cerita tentang kunci yang mendekripsi production, dan kenapa konfigurasi dipromosikan berdasarkan strukturnya.
Prosedur rilis tempat perintah-perintah ini menjadi langkah promosi dan migration.
Setiap script mengikuti bentuk yang saya berikan pada setiap tool kecil, dalam varian Bash-nya.
Toolkit yang mengisi environment production baru dengan data pertamanya.
Toolkit yang membangun environment staging, titik berangkat setiap promosi.
Toolkit yang memberi production network-nya sebelum bootstrap.
Kenapa daftar rahasia di sini dijaga selaras dengan daftar yang dirotasi.
Kenapa label environment di telemetry tetap milik production.
Istilah
- Promosi
- Memindahkan rilis yang lolos pengujian dari satu environment ke environment berikutnya, di sini dari staging ke production.
- Rahasia
- Nilai konfigurasi yang memberi akses atau mendekripsi data, seperti password, key, atau token.
- Dry run
- Menjalankan perintah supaya ia melaporkan apa yang akan berubah tanpa mengubah apa pun.
- Migration skema
- Perubahan struktur database yang berversi, diterapkan berurutan dan dicatat supaya hanya berjalan sekali.
- Init script
- Script yang dijalankan container database saat menyala dengan data directory kosong, dan tak pernah lagi.
- Bootstrap
- Membuat environment untuk pertama kali, sebelum proses rutin mana pun bisa merawatnya.
Technologies covered
- Bash
- Docker