Staging Environment Toolkit
On this page
- Sekilas
- Yang dikerjakannya
- A. Membangun environment rilis dari tiga sumber
- B. Me-restore data dalam urutan yang dibutuhkan tiap store
- C. Menyelaraskan kredensial dengan data yang di-restore
- D. Menjalankan migration rilis terhadap data production
- E. Menyesuaikan salinan dengan network-nya sendiri
- F. Menyajikan antarmuka tiap versi, dan membuktikannya
- G. Membuang satu versi tanpa sisa
- H. Menjadi gerbang kesehatan
- Cara saya memakainya
- Keputusan rancangan
- Spesifikasi
- Terkait
- Istilah
Staging Environment Toolkit adalah tiga perintah yang membangun salinan penuh production untuk satu rilis, siap diuji, lalu membuangnya lagi tanpa meninggalkan apa pun. Satu perintah merakit environment dari tiga sumber — konfigurasi di repository, image yang sudah dipublikasikan di registry, dan data di backup terbaru — me-restore dan menyalakan semuanya dalam urutan yang dibutuhkan, menjalankan migration database rilis itu terhadap data tersebut, lalu memeriksa bahwa semuanya healthy. Production tak pernah ditulisi; database-nya hanya dibaca lewat backup.
Dulu membangun environment staging berarti lima perintah dengan urutan tetap, dan melewatkan salah satunya menghasilkan kegagalan yang tak tampak seperti langkah yang terlewat. Toolkit ini ada supaya satu-satunya yang perlu saya putuskan adalah versi mana yang diuji.
Sekilas
| Tools | gen-staging-env, remove-staging, health-check |
|---|---|
| Bentuk | Script command-line yang berbagi satu library pembantu |
| Bahasa | Bash, yang mengendalikan Docker Compose, nginx, dan perintah restore milik toolkit backup |
| Status | Stabil, berjalan di production |
| Kode sumber | Privat |
Yang dikerjakannya
A. Membangun environment rilis dari tiga sumber
File compose dan konfigurasi service diambil dari template development di repository, image ditarik dari registry untuk versi persis yang diuji, dan datanya berasal dari backup terbaru, bukan dari database yang sedang hidup. Hasilnya environment yang menjalankan apa yang akan dirilis, terhadap jenis data yang akan ia hadapi, tanpa satu query pun ke database production. Image setiap modul ditarik, bukan hanya milik aplikasi, karena tiap modul membawa versinya sendiri dan versi yang belum pernah ditarik pernah membuat start gagal di server; nama image-nya dibaca dari file compose tiap modul. Pull dijalankan sebagai user yang memanggil perintah, bukan sebagai root. Stack lalu dinyalakan tanpa build apa pun, dalam urutan tetap — database dan cache dulu, storage kedua, sisanya sesudah itu — jadi Compose tak pernah bisa jatuh ke build image lokal.
B. Me-restore data dalam urutan yang dibutuhkan tiap store
Kebutuhan tiap store saling bertolak belakang. Data directory database hanya bisa diganti selagi stack mati; object storage hanya bisa di-mirror ke server yang sedang berjalan; user telemetry hanya bisa dibuat di mesin pencari yang sudah menyala. Jadi perintah ini me-restore database dulu, menyalakan stack sendiri, me-mirror objek dan menerapkan policy akses bucket yang tak ikut tersalin oleh mirror, membuat user telemetry, lalu menunggu setiap service melapor healthy. Sebelum langkah yang membutuhkannya, ia menunggu storage dan mesin pencari berjalan, ditambah jeda bagi database untuk memutar ulang log-nya. Restore database yang gagal dideteksi per modul dari output restore, jadi kegagalan satu modul tak pernah tertutup keberhasilan modul lain.
C. Menyelaraskan kredensial dengan data yang di-restore
Data hasil restore membawa kredensial tempat ia dibuat, jadi sebelum apa pun dinyalakan, toolkit ini menyelaraskan konfigurasi setiap service dengan data yang di-restore. Tanpa langkah itu, database tampak sehat sementara service yang bergantung padanya restart terus karena login ditolak.
D. Menjalankan migration rilis terhadap data production
Inilah langkah yang menjadi alasan staging ada. Begitu data di-restore, migration aplikasi dijalankan terhadapnya, disusul migration skema milik certificate authority, dan kegagalan salah satunya menghentikan flow dengan exit code non-nol dan pesan tegas untuk tidak mempromosikan rilis itu. Pesannya membedakan dua sebab: bila restore database sudah gagal sebelumnya, persoalannya ada di rantai backup, bukan di migration rilis ini, dan ia menunjuk ke perintah untuk menampilkan rantai tersebut.
E. Menyesuaikan salinan dengan network-nya sendiri
Environment staging tinggal di network-nya sendiri, di samping development dan production, jadi setiap alamat di konfigurasi hasil salinan harus berubah. Toolkit membaca network yang harus dipakainya dari state jaringan yang tercatat, bukan dari konstanta, ikut menulis ulang rentang trusted proxy, lalu memastikan tak ada satu pun alamat development tersisa di hasilnya. Network-nya harus sudah ada sebelum satu file pun ditulis, jadi kegagalan tak meninggalkan folder setengah jadi, dan gateway staging yang sama dengan milik development ditolak. Penggantian mencocokkan teks secara literal, bukan sebagai regular expression, karena titik pada alamat akan bertindak sebagai wildcard dan garis miring pada rentang alamat merusak substitusi; yang disentuh hanya jenis file konfigurasi, dan folder data, dependency, serta version control dilewati. Salinan rollback yang ditinggalkan tool lain dikecualikan dari salinan, sesudah satu sisa seperti itu membuat pemeriksaan alamat development selalu gagal. Setiap modul mendapat nama project-nya sendiri, jadi menyalakannya tak bisa menggantikan container development, beserta path data-nya sendiri — dulu restore menulis ke path staging sementara database membaca path development yang kosong, dan setiap tabel hilang walau restore melapor sukses. File konfigurasi yang dibaca certificate authority dibuat bisa dibaca oleh user di dalam image-nya, sertifikat CA yang dibutuhkan untuk menandatangani wajib tiba atau generate gagal, dan folder data mesin pencari dibuat lalu diserahkan ke user-nya lebih dulu.
F. Menyajikan antarmuka tiap versi, dan membuktikannya
Setiap versi mendapat snapshot antarmukanya sendiri, di-mount untuk web server, dengan link current yang membuat konfigurasi web server tetap statis: berpindah versi cukup memindahkan link, tanpa menyunting apa pun. Mount-nya dibuktikan dengan membaca halaman index antarmuka lewat mount itu, bukan dengan memercayai perintah mount. Konfigurasi web server dirender ulang dari template-nya di setiap generate, setelah salinan render lama bertahan melewati template yang sudah diperbaiki dan setiap tautan presigned storage mengembalikan error signature. Banner menyebut versi yang sedang diuji. Mount atau langkah web server yang gagal tak membuang environment-nya; ia mencetak opsi yang hanya mengulang bagian itu, opsi yang sama yang dipakai sesudah reboot.
G. Membuang satu versi tanpa sisa
Satu versi staging tersebar di beberapa tempat: folder project-nya, datanya di bawah direktori data bersama, container-nya, antarmuka yang di-mount, dan konfigurasi web server-nya. Perintah penghapus membersihkan semuanya, menghapus data milik root lewat container berumur pendek supaya tak butuh hak lebih tinggi, dan bisa menampilkan setiap versi beserta keadaan tiap bagiannya, termasuk data yang tertinggal dari versi yang foldernya sudah hilang. Penghapusan meminta saya mengetik ulang versinya, atau menerima flag untuk otomasi, dan bisa menyimpan data sambil membuang sisanya. Container ditemukan lewat label project di file environment versi itu, bukan lewat pencocokan nama. Antarmuka di-unmount sebelum apa pun dihapus, dan proses berhenti bila unmount gagal, karena menghapus lewat mount yang masih hidup berarti menghapus antarmuka itu sendiri. Bila versi yang dihapus adalah versi current, link dan banner dipindah ke versi lain yang masih ter-mount, atau dikosongkan. Web server hanya disentuh bila link atau banner berubah, dan itu pun dengan reload.
H. Menjadi gerbang kesehatan
Health check membaca status kesehatan yang sudah didefinisikan file compose tiap service, bukan menduplikasi probe-nya, dan mencoba ulang sampai semuanya healthy atau percobaannya habis. Ia keluar dengan kode yang bisa dipakai pipeline rilis, jadi deployment tak pernah dinyatakan berhasil hanya karena container-nya sekadar menyala. Nol container dihitung gagal, dan argumen yang salah keluar dengan 2.
Cara saya memakainya
&&
Urutannya bukan soal selera. Setiap langkah yang terlewat dulu gagal dengan caranya sendiri yang menyesatkan:
| Langkah yang terlewat | Gejalanya |
|---|---|
| Restore database | service crash-loop karena tabel tak ada, dan halaman home 502 |
| Restore object storage | dokumen dan gambar 404 saat diuji |
| Setup telemetry | setiap log dan trace dibuang diam-diam, tanpa tanda apa pun di antarmuka |
| Langkah | Keadaan stack yang dibutuhkan | Alasan |
|---|---|---|
| Restore database | mati | ia mengganti data directory database |
| Restore object storage | berjalan | ia me-mirror ke storage server yang hidup |
| Setup telemetry | berjalan | ia membuat user lewat mesin pencari yang hidup |
Keputusan rancangan
Satu perintah, karena urutannya tak kasatmata dan kesalahannya senyap. Perintah ini mengerjakan lebih dari sekadar generate: ia menyalakan container dan menulis konfigurasi web server, dan justru itulah yang membuatnya lengkap. Bila satu tahap gagal, environment yang sudah dibangun dibiarkan di tempatnya dan langkah-langkah yang tersisa dicetak; generate untuk versi yang sama ditolak selama folder-nya masih ada, jadi melanjutkan berarti menjalankan langkah-langkah itu. Tahap yang tanpanya rilis tak bisa diuji — menarik image dan kedua migration — berakhir dengan exit code non-nol supaya pipeline ikut berhenti. Tahap lainnya — modul yang gagal menyala, restore storage, setup telemetry, dan health check terakhir — dikumpulkan dalam satu ringkasan yang saya baca sebelum pengujian apa pun dimulai. Ringkasan itu sendiri mencetak versi image sungguhan tiap modul, setelah variable yang belum diisi pernah menghentikan script tepat di sana, ketika semua kerja berat sudah berhasil.
Database production tak pernah jadi sumber, dan hanya artefak rilis sungguhan yang diuji. Data berasal dari backup, jadi membangun staging sesering apa pun tak membebani production sama sekali — dan setiap kali membuktikan backup-nya bisa di-restore. Satu-satunya yang dibaca langsung dari production adalah file runtime yang tak dipegang backup maupun version control, yang dijelaskan di bawah. Image ditarik dari registry untuk versi yang diuji. Opsi memakai image lokal hanya untuk laptop saya, karena di server ia akan menguji image yang mungkin tak pernah dipublikasikan, dan lolosnya tak membuktikan apa pun tentang rilis.
Setiap nilai khas environment dibaca, bukan ditebak, lalu diperiksa. Versi lebih awal menyimpan gateway development sebagai konstanta; ketika network development dipindah, penggantiannya diam-diam tak menemukan apa pun, staging terbit membawa alamat development, dan service-nya tak bisa menjangkau database-nya. Kini gateway dibaca dari state jaringan yang tercatat, hasilnya dipindai untuk mencari alamat development, dan environment staging yang masih memuatnya dihapus, bukan dipakai. Prinsip yang sama berlaku untuk file: file data yang dilacak version control dikembalikan dengan menanyakan daftarnya ke version control, dan state runtime yang tak ada di version control — misalnya daftar pencabutan milik certificate authority — disalin dari path yang saya sebut lebih dulu, bila tidak dari production, bila tidak dari development, sambil mengganti tujuan yang keliru berbentuk direktori dan memakai container berumur pendek bila tujuannya milik root. Tanpanya, mount diam-diam berubah jadi direktori kosong dan penandatanganan gagal tiga service jauhnya. Daftar pencabutan yang hilang dilaporkan sebagai peringatan; sertifikat CA yang dibutuhkan untuk menandatangani yang hilang menghentikan generate. Gateway staging dibaca tanpa fallback sama sekali, karena fallback lamanya milik skema yang sudah tak ada dan akan salah tanpa sepatah kata.
Menghapus sama lengkapnya dengan membangun, dan hanya satu versi yang hidup sekaligus. Semua versi berbagi nama project, nama container, dan port yang sama, jadi versi kedua akan diam-diam mengambil alih yang pertama; perintah ini menolak membangun selagi versi lain masih ada. Penghapusan membersihkan setiap tempat satu versi tinggal, tanpa hak lebih tinggi, dan daftarnya memperlihatkan data yatim serta mount yang hilang sebelum menjadi misteri. Perubahan web server diterapkan dengan reload, yang mempertahankan koneksi situs lain, dan baru sesudah uji konfigurasinya lolos, karena web server yang mati akibat konfigurasi rusak tak akan menyala lagi dengan sendirinya. Ujinya membedakan konfigurasi rusak dari konfigurasi yang tak bisa diuji karena kurang hak, dan web server yang sedang mati dinyalakan, bukan di-reload.
Penjaga kecil terhadap kejutan yang mahal. Nama versi tak boleh memuat pemisah path, rujukan ke folder induk, atau wildcard, jadi tak ada operasi yang bisa lolos keluar dari folder staging, dan versi yang disebut dua kali ditolak. Generate tanpa data mencetak peringatan bahwa database-nya kosong, beserta gejala yang akan muncul dan langkah untuk mengisinya. Path data dan backup disusun dari nama project, jadi sama di laptop saya maupun di server.
Spesifikasi
| Masukan | Nomor versi; opsional backup tertentu dan path ke file state runtime |
|---|---|
| Keluaran | Folder project per versi, datanya, antarmuka yang di-mount, dan konfigurasi web server-nya |
| Yang di-restore | Empat database PostgreSQL dan object storage beserta policy aksesnya; store telemetry dan cache sengaja dimulai kosong |
| Migration | Migration aplikasi dan migration skema certificate authority, dijalankan terhadap data hasil restore; kegagalan menghentikan flow |
| Gerbang kesehatan | Membaca health check milik tiap service; jumlah percobaan dan jeda bisa diatur; exit code 0 bila healthy, 1 bila percobaan habis, 2 untuk argumen salah |
| Keamanan | Database production hanya dibaca lewat backup-nya; satu versi pada satu waktu; tahap yang gagal menyimpan environment-nya; web server di-reload hanya sesudah uji konfigurasinya lolos |
| Prasyarat | Docker; hak lebih tinggi hanya untuk mount antarmuka dan konfigurasi web server |
Terkait
Kenapa setiap environment di satu mesin butuh network, state, dan identitas sendiri.
Prosedur rilis tempat toolkit ini membangun langkah staging.
Setiap script mengikuti bentuk yang saya berikan pada setiap tool kecil, dalam varian Bash-nya.
Toolkit yang backup-nya menjadi sumber setiap environment staging.
Kenapa tiap versi staging membawa banner dan konfigurasi web server-nya sendiri di bawah domain yang sama.
Kenapa telemetry staging butuh user sendiri, yang dibuat di akhir generate.
Istilah
- Staging
- Environment yang meniru production, lengkap dengan salinan datanya, untuk menguji rilis sebelum tayang.
- Bind mount
- Direktori atau file di host yang dibuat tersedia di path lain, di sini untuk menyajikan antarmuka satu versi.
- Trusted proxy
- Rentang alamat yang dipercaya service ketika ia melaporkan alamat client aslinya.
- Daftar pencabutan
- File yang dipublikasikan certificate authority berisi sertifikat yang sudah tak berlaku.
- Health check
- Pemeriksaan otomatis bahwa service sudah menyala dan bisa menjawab.
- Pipeline
- Rangkaian otomatis langkah build, uji, dan deployment yang berhenti ketika salah satunya gagal.
Technologies covered
- Bash
- Docker Compose
- nginx