Bagaimana Saya Melayani Tiga Environment dari Satu Nama Domain yang Tak Bisa Saya Ubah
On this page
- Pendahuluan
- A. Apa sebenarnya socket itu
- B. Kenapa namanya tak bisa sekadar dibedakan
- C. Apa yang sudah ditangani pemisahan lain
- D. Apa taruhannya kalau ini keliru
- E. Apa yang wajib dibereskan proxy
- F. Empat hambatan yang menghalangi
- Metode
- A. Pisahkan di socket, bukan di nama
- B. Memilih environment berarti memilih alamat
- C. Konfigurasi yang bisa disunting version control
- D. Satu template, bukan lima file yang nyaris kembar
- E. Substitusi yang lolos uji sintaks tapi keliru
- F. Gagal sebelum menyentuh apa pun
- G. Menandai environment yang tak bisa dibedakan namanya
- Hasil dan Pembahasan
- A. Uji yang tak memeriksa apa yang dikira
- B. Header yang tak akan terpikir diperiksa
- C. Berapa ongkos satu proxy untuk tiga environment
- D. Di mana ini tidak berlaku
- Simpulan
- Istilah
Satu nama domain. Tiga environment. Satu mesin, dan satu web server di depan semuanya.
Susunan begitu terdengar mustahil, atau setidaknya jelek. Ternyata bukan keduanya — meski jalan menuju ke sana menuntut saya memutuskan persis sejauh mana version control boleh menjangkau konfigurasi yang hidup.
Pendahuluan
Saat browser meminta sebuah halaman, ada yang memutuskan program mana yang menjawab. Di mesin yang menjalankan satu aplikasi, keputusan itu sepele: calonnya cuma satu. Di mesin yang menjalankan tiga salinan aplikasi yang sama — development, staging, dan production — keputusan itu justru jadi seluruh persoalannya.
Komponen yang mengambil keputusan tersebut adalah reverse proxy: web server yang duduk di depan semuanya, menerima permintaan dari luar, lalu meneruskan tiap permintaan ke service internal yang semestinya menanganinya. Tak ada yang sampai ke aplikasinya tanpa melewatinya lebih dulu.
A. Apa sebenarnya socket itu
Karena jawaban di tulisan ini bertumpu pada satu kata yang kebanyakan artikel pakai tanpa menjelaskannya, satu alinea layak diberikan.
Mesin tak punya satu alamat jaringan; ia punya beberapa. Ada alamat loopback, yang cuma bisa digapai mesin itu sendiri — tak ada apa pun di luarnya yang bisa menempuh rute ke sana. Ada alamat di jaringan lokal, yang bisa digapai mesin-mesin di sekitarnya. Bisa ada lagi yang lain: alamat di antarmuka jaringan privat, alamat pemberian VPN.
Di atas alamat mana pun itu, sebuah program bisa mengklaim port — nomor yang memisahkan satu layanan dari layanan lain pada alamat yang sama. Lalu lintas web lazimnya memakai port 443 untuk sambungan terenkripsi.
Pasangan keduanya — satu alamat, satu port — itulah socket. Ia pintu tertentu tempat server mendengarkan. Dan inilah sifat yang menentukan: dua program berbeda sama-sama bisa mendengarkan di port 443 pada saat yang sama, asalkan keduanya mengklaimnya di alamat yang berbeda. Sistem operasi menganggapnya pintu yang terpisah, sebab memang begitu.
Itulah seluruh mekanisme yang jadi tumpuan tulisan ini. Kalau environment tak bisa dibedakan lewat nama, ia bisa dibedakan lewat pintu mana yang dilalui permintaannya.
Ada satu sifat tambahan yang baru saya hargai belakangan. Alamat loopback tak punya rute dari luar mesin — bukan karena ada aturan yang melarangnya, melainkan karena jaringan memang tak menyediakan jalan ke sana. Environment development yang mendengarkan di situ jadi tak terjangkau dari luar, dan itu berlaku sekalipun seluruh konfigurasi lain salah. Pemisahannya ditegakkan jaringan, bukan kedisiplinan.
B. Kenapa namanya tak bisa sekadar dibedakan
Pendekatan yang paling terpikirkan: tiga hostname — dev.example, staging.example, dan yang sungguhan. Kebanyakan project memang begitu, dan bagi kebanyakan project itu jawaban yang tepat.
Pilihan itu tak tersedia buat saya, dan alasannya layak disebut sebab banyak sistem internal berbagi keterbatasan yang sama.
Sistemnya melayani sebuah instansi publik, dan domainnya sudah tetap di tempat-tempat yang bukan saya kendalikan. Ia muncul di certificate yang sudah terlanjur diterbitkan — certificate adalah pernyataan bertanda tangan yang mengikat sebuah kunci publik ke nama tertentu, jadi certificate untuk satu nama tak membuktikan apa pun tentang nama lain. Ia muncul di konfigurasi yang dipegang organisasi lain yang menyambung ke sistem ini. Dan ia muncul di dokumen yang sudah ditandatangani, tempat namanya jadi bagian dari apa yang disaksikan.
Menguji dengan hostname berbeda berarti environment yang sedang menggladi bersih tidak menguji benda yang sesungguhnya dipakai production. Nama di sini bukan hal sampingan; ia bagian dari yang sedang diuji.
Jadi batasannya sudah terkunci: hostname yang sama wajib menggapai tiga environment berbeda, dan sesuatu selain nama yang harus menentukan mana.
C. Apa yang sudah ditangani pemisahan lain
Proxy bukan satu-satunya yang menjaga ketiga environment ini tetap terpisah, dan memahami apa yang bukan tanggung jawabnya justru membantu.
Tiap environment punya jaringan container sendiri dengan rentang alamatnya sendiri, sehingga service di satu environment tak bisa menggapai database di environment lain — alamatnya memang tak ketemu menyeberangi batas itu. Tiap environment menyimpan datanya di direktori sendiri di luar folder project, sehingga menyalin atau menghapus folder project mustahil menyentuh database. Dan tiap environment menyatakan identitas project-nya sendiri ke runtime container, sehingga menaikkan satu environment tak bisa membuang container milik environment lain.
Ketiga pemisahan itu ditegakkan mekanisme yang sama sekali tak berhubungan dengan proxy: definisi jaringan per environment, path data di luar folder project, dan project name yang dinyatakan eksplisit oleh tiap environment, bukan diturunkan runtime dari nama folder. Yang terakhir penting karena runtime container menganggap folder-folder bernama sama sebagai satu project, sehingga tanpa itu, menyalakan satu environment bisa menghapus container milik environment lain. Alasan ia disebut di sini bersifat pengurangan: berkatnya, proxy tinggal punya satu tugas. Ia tak perlu mencegah environment saling menggapai — itu sudah beres. Ia cuma perlu mengarahkan tiap permintaan yang masuk ke environment yang benar.
D. Apa taruhannya kalau ini keliru
Proxy sekaligus satu-satunya komponen yang pemisahannya berhenti mungkin. Instansnya cuma ada satu, dan ketiga environment bergantung padanya berbarengan.
Pemusatan itulah risikonya. Kekeliruan di konfigurasi proxy tak tinggal di dalam environment yang sedang dikerjakan. Suntingan yang buruk saat menyiapkan staging bisa menjatuhkan production, sebab keduanya dilayani proses yang sama yang membaca direktori konfigurasi yang sama — dan di mesin ini, begitu pula project-project lain yang sama sekali tak berhubungan dengan yang ini.
Itu sebabnya bagian tulisan ini yang paling akan saya bela bukan soal pengarahannya, melainkan soal siapa yang boleh mengubah konfigurasi tersebut — dan apa yang terjadi ketika jawabannya ternyata "apa pun yang menulis ulang file di direktori itu", termasuk perintah version control yang tak seorang pun menganggapnya berbahaya.
E. Apa yang wajib dibereskan proxy
Tiga hal, dan ketiganya saling tarik-menarik.
Ia wajib mengarahkan dengan benar: permintaan untuk staging tak boleh sampai ke production. Ia wajib bisa disunting: alamat berubah, dan orang harus bisa mengubahnya tanpa menurunkan ulang seluruh konfigurasi. Dan ia wajib bisa dipulihkan: saat ada yang keliru pukul dua pagi, harus ada jalan kembali ke keadaan yang diketahui.
Rancangan yang paling jelas memenuhi dua yang awal dan diam-diam memusnahkan yang ketiga. Bagian itulah yang layak dituturkan.
F. Empat hambatan yang menghalangi
Dinyatakan sebagai tujuan: melayani tiga environment dari balik satu nama domain yang sama sekali tak berwenang saya ubah, tanpa satu request pun mendarat di environment yang keliru. Namanya terkunci sebab sudah tertulis di certificate yang terlanjur diterbitkan, di konfigurasi milik organisasi lain yang bukan saya yang mengurusnya, dan di dokumen-dokumen yang sudah ditandatangani — di sana namanya bagian dari apa yang disaksikan. Selain itu semuanya masih bisa dirundingkan. Yang satu ini tidak.
Gampang dirumuskan, canggung dibangun. Ada empat hambatan yang menghalangi, dan urutannya mengikuti urutan orang benar-benar menemuinya: mula-mula kebingungan menatap tiga environment yang seragam, lalu dua hambatan soal bagaimana konfigurasinya dilahirkan, dan terakhir yang paling mahal.
Hambatan pertama: satu domain berarti tak ada yang tahu sedang menatap environment mana. Kedengarannya soal rupa belaka, dan bukan. Ketika address bar terbaca sama di ketiganya, satu-satunya yang membedakan gladi resik dari sistem sungguhan adalah ingatan orang yang menatapnya — dan ingatan itu justru gugur ketika paling dibutuhkan: larut malam, di tab ketiga. Pertolongan yang lazim, satu hostname per environment, justru tak tersedia di sini. Jadi penandanya harus disisipkan ke dalam halamannya sendiri: satu pita berwarna melintang di puncak tiap response, biru untuk development dan oranye untuk staging, menyebut nama environment-nya dan mengingatkan bahwa datanya bukan data sungguhan. Production tak diberi pita sama sekali, dan ketidaksimetrisan itu disengaja, bukan kelalaian — penanda yang bocor ke sistem sungguhan justru jadi cacat tersendiri.
Hambatan kedua: lima file konfigurasi yang nyaris kembar itu bukan strategi, melainkan janji menjaga lima hal tetap sinkron selamanya. Tiap environment punya file lengkapnya sendiri, dan varian laptop maupun server terpisah lagi. Mengukur apa yang sebenarnya berbeda cukup membuat tertegun: seluruh server block ternyata identik, beberapa definisi route identik, dua cuma berbeda satu baris. Yang sungguh-sungguh berbeda tinggal alamat yang didengarkan, satu pita, dan satu security header. Janji semacam itu patah tanpa suara — satu route block bisa raib dari satu environment saat penataan ulang dan tetap raib dalam waktu lama, sebab environment tempatnya menghilang adalah yang tak dikunjungi orang tiap hari. Menyalin file per environment adalah alternatifnya, dan justru itulah yang mengundang cacat tadi.
Hambatan ketiga: membangkitkan konfigurasi satu bahasa memakai tools dari bahasa lain melahirkan keluaran yang sah sekaligus keliru. Konfigurasi web server penuh variabel runtime yang ditulis dengan sigil yang sama dengan milik shell — $uri, $host, $remote_addr — dan semuanya dimaksudkan tiba di file akhir tanpa tersentuh. Perender yang paling terpikirkan, substitusi environment polos, menganggapnya miliknya sendiri, tak menemukan nilainya, lalu menggantinya dengan kekosongan. Yang keluar tetap sah secara sintaks. Uji konfigurasi lolos, server reload tanpa mengeluh, dan cacatnya baru muncul ketika jenis request tertentu datang. Jawabannya: berhenti berbagi sigil sama sekali. Placeholder ditulis @@NAME@@, rangkaian yang mustahil muncul di file konfigurasi yang sah — dan justru sifat itu yang membuat placeholder tak tersulih bisa dicari perender lalu ditolak pemasangannya.
Hambatan keempat: file yang sekaligus dilacak version control dan menjadi konfigurasi sistem yang hidup punya dua pemilik yang tak saling berkoordinasi. Menyimpan file aslinya di repository lalu menaruh symbolic link di direktori sistem terlihat seperti praktik yang baik — satu salinan, terlacak, disunting seperti file project mana pun. Ia bisa bekerja berbulan-bulan, sampai satu perpindahan branch menulis ulang working tree dan, lewat link itu, konfigurasi hidup proxy yang melayani production. Checkout-nya berhasil, satu endpoint mulai mengembalikan 502, dan tak ada apa pun di keluaran version control yang menyiratkan ada sesuatu di luar repository ikut berubah, sebab sepanjang yang ia tahu memang tak ada. Tugas version control adalah membuat working tree serupa dengan sebuah revisi, dan ia menunaikan tugas itu dengan sempurna — termasuk ketika working tree itu sedang menanggung beban.
Urutannya berarti dengan cara yang spesifik. Hambatan pertama ditemui orang dalam hitungan detik sejak domainnya dipakai bersama, dan sekali dibereskan ia selesai selamanya. Yang kedua dan ketiga sepasang: dua-duanya soal bagaimana sebuah file konfigurasi dilahirkan, dan yang ketiga baru mungkin ada justru karena yang kedua mendorong saya ke template sejak awal. Yang keempat ditaruh terakhir sebab dialah yang memusnahkan kemampuan memulihkan — syarat ketiga dari tiga syarat di atas — dan tak seperti yang lain, ia sama sekali tak memperkenalkan diri sebagai persoalan rancangan. Ia datang sebagai perintah rutin yang selama ini selalu aman.
Metode
A. Pisahkan di socket, bukan di nama
Inilah jawaban atas paruh routing-nya, sekaligus fondasi tempat tiga hambatan lain diselesaikan.
Kalau nama tak bisa membedakan environment, penanda lain harus bisa. Jawabannya: alamat yang didengarkan server.
Sebuah mesin lazimnya punya lebih dari satu alamat — alamat loopback yang cuma bisa digapai dirinya sendiri, alamat di jaringan lokal, alamat di antarmuka jaringan privat. Web server bisa disuruh menerima sambungan di alamat tertentu saja:
listen 127.0.0.50:443 ssl; # development — cuma tergapai dari mesin ini
listen 10.0.1.10:443 ssl; # staging — tergapai lewat jaringan privat
listen 10.0.2.10:443 ssl; # production — tergapai dari jaringan kantorTiga blok server, server_name yang sama, tiga socket berbeda. Permintaan tiba di satu alamat atau alamat lain, dan itulah yang menentukan environment mana yang menjawab. Namanya tak perlu berbeda sebab bukan nama yang sedang dicocokkan.
Ada satu sifat yang baru saya hargai belakangan: pemisahannya ditegakkan jaringan, bukan konfigurasi. Development mendengarkan di alamat yang cuma ada di dalam mesin itu. Sesalah apa pun konfigurasi di tempat lain, ia tak jadi tergapai dari luar, sebab memang tak ada rute ke sana.
B. Memilih environment berarti memilih alamat
Kalau environment dibedakan alamat, berpindah di antara keduanya lewat browser berarti membuat nama yang sama jatuh ke alamat berbeda. Di komputer kerja, itu urusan file hosts — tabel kecil yang dibaca sistem operasi sebelum bertanya ke server DNS.
Menyuntingnya dengan tangan bisa, dan mengundang kekeliruan, kebanyakan satu jenis: meninggalkan dua entri aktif sekaligus, sehingga urutan resolusi yang menentukan environment-nya dan jawabannya jadi sesuka sistem. Maka perpindahan itu dijadikan perintah, dengan tiga jaminan: tepat satu entri domain yang aktif, entri kembar di bagian lain file dinonaktifkan, dan baris milik project lain tak tersentuh.
# --- blok terkelola: tepat satu baris aktif ---
#10.0.1.10 app.example.test
#10.0.2.10 app.example.testAlamatnya tidak ditanam di dalam tools perpindahan itu. Ia dibaca dari file yang sama dengan sumber konfigurasi proxy, sehingga cuma ada satu tempat alamat itu hidup dan selebihnya mengikuti.
C. Konfigurasi yang bisa disunting version control
Inilah jawaban atas hambatan keempat — yang datang sebagai perintah biasa, bukan sebagai pertanyaan rancangan.
Konfigurasi web server tinggal di direktori sistem. Konfigurasi saya di-generate dari project, jadi susunan yang tampak paling masuk akal: simpan file aslinya di repository, lalu taruh symbolic link di direktori sistem yang menunjuk ke sana. Satu salinan, terlacak version control, disunting seperti file project lain.
Berbulan-bulan ia bekerja. Lalu saya berpindah branch.
# susunan yang tampak paling masuk akal
# ... sehingga perintah ini menulis ulang konfigurasi yang hidup
Perpindahan branch menulis ulang file di working tree. Bila ada link, salah satu file itu adalah konfigurasi hidup web server yang sedang melayani production. Checkout-nya berhasil, tanpa suara, dan meninggalkan proxy menunjuk alamat dari revisi lama. Satu endpoint mulai membalas 502, dan tak ada apa pun di keluaran version control yang menyiratkan ada sesuatu di luar repository ikut berubah, sebab sejauh yang ia tahu, memang tak ada.
Pelajarannya bukan "hati-hati dengan symbolic link". Ia lebih sempit dan lebih berguna: file yang sekaligus dilacak version control dan menjadi konfigurasi sistem yang hidup punya dua pemilik, dan keduanya tak saling berunding. Tugas version control adalah membuat working tree cocok dengan sebuah revisi. Ia akan menunaikan tugas itu dengan sempurna, termasuk ketika working tree sedang menanggung beban.
Karena itu tak ada link. Konfigurasi di direktori sistem adalah file biasa milik root, dirender dari template di project, bukan ditautkan padanya. Version control tak bisa menjangkaunya.
Ada konsekuensi yang perlu disebut terang-terangan: memulihkan konfigurasi bukan satu perintah version control. Mengembalikan template ya mengembalikan template. File hasil render tetap seperti apa adanya sampai dirender ulang.
D. Satu template, bukan lima file yang nyaris kembar
Inilah jawaban atas hambatan kedua: lima file yang wajib sepakat satu sama lain, dan langsung menyimpang begitu satu disunting sendirian.
Hasil pengukuran di atas menunjukkan bentuk perbaikannya. Kalau nyaris segalanya identik dan yang sungguh berbeda tinggal satu alamat, satu pita, dan satu security header, maka bagian yang identik itu semestinya tak disimpan lima kali.
Blok bersama pun jadi partial — potongan konfigurasi yang disimpan di file-nya sendiri lalu ditarik masuk ke tiap template saat dirender, sehingga blok yang wajib identik di mana-mana secara fisik cuma ada di satu tempat. Blok yang cuma ada sekali mustahil hilang dari salah satu di antara tiga salinan.
# dev.conf.in — satu-satunya baris yang berbeda per environment
listen @@LISTEN@@:443 ssl;
include partials/app-routes.conf; # identik di ketiganya
include partials/ca-server.conf; # identik di ketiganyaE. Substitusi yang lolos uji sintaks tapi keliru
Inilah jawaban atas hambatan ketiga, dan ia cuma ada gara-gara seksi sebelumnya — template-lah yang membuat sebuah perender jadi perlu.
Merender template berarti menyulihkan nilai ke dalam placeholder, dan tools yang paling terpikirkan untuk itu adalah utilitas substitusi environment bawaan. Ia juga, khusus dalam kasus ini, berbahaya.
Konfigurasi web server penuh variabel runtime yang memakai lambang yang sama dengan shell — $uri, $host, $remote_addr — dan semuanya justru harus lolos ke file akhir tanpa tersentuh. Substitusi yang naif tak tahu itu. Ia memperlakukannya sebagai variabel shell, tak menemukan nilainya, lalu menggantinya dengan kekosongan:
try_files $uri $uri/ /index.html; # yang dimaksud
try_files / /index.html; # sesudah substitusi naifBaris kedua tetap sah secara sintaks. Uji konfigurasi lolos. Server memuat ulang tanpa mengeluh lalu berperilaku keliru, dengan cara yang baru kelihatan saat jenis permintaan tertentu tiba.
Jawabannya: berhenti berbagi lambang dengan bahasa tujuan. Placeholder ditulis @@NAMA@@, urutan karakter yang tak pernah muncul di file konfigurasi yang sah. Sifat itulah yang membuat verifikasi jadi mungkin: sesudah render, perendernya mencari sisa @@ dan menolak memasang file-nya bila menemukan satu saja. Placeholder yang belum tersulih berubah jadi kegagalan keras, bukan kegagalan senyap.
Ini bentuk umum yang layak dikenali. Saat Anda men-generate konfigurasi satu bahasa memakai tools dari bahasa lain, sintaks keduanya cepat atau lambat akan bertabrakan — dan tabrakannya melahirkan keluaran yang sah sekaligus keliru, kombinasi terburuk yang tersedia.
F. Gagal sebelum menyentuh apa pun
Perender mengerjakan tugasnya dalam urutan yang dipilih supaya kegagalan tak meninggalkan apa pun setengah jadi: render ke lokasi sementara, verifikasi hasilnya, cadangkan file yang sekarang, pasang, jalankan uji konfigurasi milik server itu sendiri, lalu kembalikan cadangan bila ujinya gagal.
Urutannya lebih menentukan ketimbang langkah-langkahnya. Menguji sebelum memasang berarti hasil render yang rusak tak pernah sampai ke direktori sistem. Mencadangkan sebelum memasang berarti selalu ada tempat untuk kembali. Memulihkan saat gagal berarti percobaan yang gagal meninggalkan mesin persis seperti semula.
&& || G. Menandai environment yang tak bisa dibedakan namanya
Inilah jawaban atas hambatan pertama, dan satu-satunya yang menyelesaikan persoalan manusia, bukan persoalan mesin. Semua yang di atas membuat ketiga environment benar-benar terpisah. Tak satu pun membuatnya terlihat terpisah, dan address bar terbaca sama persis di ketiganya.
Penandanya disisipkan proxy itu sendiri, saat response keluar, bukan oleh aplikasinya. Penempatan itu justru intinya: aplikasi tak perlu tahu ia sedang berjalan di environment mana, mustahil salah menebaknya, dan mustahil ter-deploy ke tempat keliru dengan nilai keliru yang sudah terlanjur ikut ter-build di dalamnya. Proxy sudah tahu, sebab environment-nya ditentukan oleh socket mana request itu tiba.
# dev.conf.in — disisipkan saat keluar, oleh proxy, bukan oleh aplikasi
add_header X-App-Env "dev" always;
sub_filter_once off;
sub_filter '</body>' '
<div style="position:fixed;top:0;left:0;right:0;z-index:99999;
background:#2563eb;color:#fff;text-align:center;
font:600 13px/28px system-ui;height:28px;">
DEV · local · development environment
</div>
<div style="height:28px"></div>
</body>';Dua detail yang kurang kentara ketimbang pitanya sendiri. <div> kedua adalah pengganjal setinggi persis pitanya: elemen ber-posisi fixed dikeluarkan dari aliran dokumen, jadi tanpa pengganjal itu pitanya akan menindih apa pun yang dirender paling awal. Dan sub_filter_once off penting sebab dalam praktiknya sebuah response bisa memuat lebih dari satu penutup body; menyulih yang pertama saja menyisakan halaman-halaman yang pitanya diam-diam tak muncul.
Staging berbeda pada tiga nilai — pita oranye, kata STAGING, dan satu identifier versi yang dirender dari file kecil yang ditulis ulang generator environment tiap kali versi staging berganti. Menaruh identifier itu di file tersendiri itulah yang membuat template-nya sendiri tetap statis.
# staging.conf.in — mekanisme sama, tiga nilai berbeda
add_header X-App-Env "staging" always;
sub_filter '</body>' '
<div style="…background:#e67e22;…">
STAGING · $app_staging_version · test environment, not production data
</div>…';Production tak punya pita maupun header, dan itu keputusan, bukan kealpaan. Penanda ada untuk memberi tahu developer bahwa yang sedang ia tatap bukan yang sungguhan; di production tak ada yang perlu diperingatkan, dan pita environment yang nongol di depan user sungguhan justru jadi cacat. Header-nya ditiadakan karena alasan yang berkerabat — ia mengumumkan kepada siapa pun yang bertanya sedang berhadapan dengan sistem macam apa, dan itu keterangan yang tak punya alasan untuk disodorkan sistem sungguhan.
Layak diperhatikan apa yang seksi ini bagi bersama seksi-seksi sebelumnya. Pita adalah perbedaan antar environment, jadi menurut logika seksi D ia mestinya tinggal di template per-environment, bukan di partial bersama — dan ia memang salah satu dari tiga bagian yang sungguh berbeda saat diukur. Pengukuran itulah yang memberi tahu saya di mana ia harus ditaruh.
Hasil dan Pembahasan
A. Uji yang tak memeriksa apa yang dikira
Tiap web server menyediakan uji konfigurasi, dan ia sungguh berguna. Ia juga lebih sempit dari reputasinya.
Uji itu mengurai konfigurasi dan memvalidasi strukturnya. Ia tidak mencoba mengikat diri ke alamat yang disebut konfigurasi tersebut. Jadi konfigurasi yang mendengarkan di alamat yang sedang tak dimiliki mesin — antarmuka jaringan privat yang mati, alamat jaringan lokal yang berubah — lolos uji dengan mulus.
Kegagalannya tiba belakangan, dan cuma lewat satu tindakan tertentu. Reload mempertahankan socket yang sudah terbuka dan tak akan menyadarinya. Restart melepaskannya lalu mencoba memperolehnya kembali, dan di situlah alamat yang hilang berubah jadi kegagalan saat start. Proxy yang berminggu-minggu baik-baik saja menolak menyala, dan konfigurasi yang "lolos uji" itulah sebabnya.
Tools yang kini saya pakai melaporkan, terpisah dari uji sintaks, apakah tiap alamat yang dikonfigurasi benar-benar ada di sebuah antarmuka. Pemeriksaannya pendek, dan ia bertanya kepada mesinnya, bukan kepada konfigurasinya — cuma begitu caranya menangkap perselisihan di antara keduanya.
# Apakah tiap alamat yang didengarkan konfigurasi ini benar-benar ada sekarang?
| |
while ; do
|
&&
|| Pemeriksaan macam ini kelihatan mubazir sampai pertama kali ia menyelamatkan satu malam.
B. Header yang tak akan terpikir diperiksa
Satu detail di konfigurasi proxy bisa melahirkan kegagalan yang butuh sehari untuk dilacak tanpa tahu harus melihat ke mana.
Object storage memverifikasi permintaan unggah dengan menghitung tanda tangan atas sebagian isi permintaan — termasuk host yang ia yakini jadi alamat tujuannya. Saat proxy meneruskan permintaan, nilai itu wajib diteruskan persis seperti yang diharapkan object storage, kalau tidak tanda tangan yang ia hitung takkan cocok dengan yang dikirim klien.
Kalau keliru, kegagalannya berupa ketidakcocokan tanda tangan, dilaporkan oleh object storage. Itu bukan error proxy. Tak ada yang ganjil di log proxy. Permintaannya diteruskan dengan benar menurut segala ukuran yang dikenal proxy, dan komponen yang menolaknya berjarak tiga lapis dari baris yang menyebabkannya.
Inilah alasan meng-generate konfigurasi ketimbang menulisnya: bukan karena generate lebih elok, melainkan karena nilai semacam ini gampang terlewat oleh tangan dan mustahil terlewat dari template yang selalu memancarkannya.
C. Berapa ongkos satu proxy untuk tiga environment
Rancangan ini sengaja memusatkan risiko, jadi ia membawa pengamannya sendiri. Satu proxy melayani ketiga environment, sehingga kekeliruan konfigurasi di dalamnya bisa mengenai environment selain yang sedang dikerjakan.
Urutan render-uji-pasang-pulihkan membuat konfigurasi buruk ditolak sebelum terpasang, dan pemisahan jadi partial membuat suntingan atas perilaku bersama dikerjakan sekali, bukan tiga kali.
Saya lebih memilih itu ketimbang kegagalan alternatifnya — lima file rawatan tangan yang perlahan menyimpang — sebab penyimpangan jauh lebih sulit terdeteksi. Kekeliruan bersama langsung terlihat di mana-mana. Penyimpangan cuma terlihat di tempat yang kebetulan ada yang menengok.
D. Di mana ini tidak berlaku
Kalau tiap environment bisa diberi hostname-nya sendiri, tempuh jalan itu saja. Seluruh isi artikel ini akibat dari tidak bisa melakukannya, dan tak satu pun kerumitannya layak diadopsi secara sukarela.
Kalau environment Anda jalan di mesin terpisah, sebagian besar bahasan ini pun larut — tiap mesin dapat konfigurasi yang lempang, dan satu-satunya komponen bersama tinggal DNS.
Yang berlaku umum lebih sempit: konfigurasi yang di-generate sebaiknya diverifikasi kelengkapannya sebelum dipasang, dipasang secara atomik dengan jalan kembali, dan tak pernah dibuat terjangkau oleh tools yang tugasnya menulis ulang file. Ketiganya berlaku terlepas dari berapa hostname yang Anda punya.
Simpulan
Satu domain menggapai tiga environment bermuara pada satu gagasan: pisahkan di socket, bukan di nama. Begitu alamatnya berbeda, selebihnya — perpindahan, pengarahan, sertifikat — mengalir dari keputusan tunggal itu.
Sisa pekerjaannya menyangkut pertanyaan lain: siapa yang boleh mengubah konfigurasi yang hidup. Link menjawabnya "apa pun yang menyunting repository", yang sama saja dengan bilang "siapa pun yang menjalankan perintah version control rutin". Merender ketimbang menautkan menyempitkannya jadi satu tindakan yang disengaja, dan itulah yang membuat konfigurasi bisa dipulihkan pukul dua pagi.
Dua kegagalan dalam pekerjaan ini berbagi bentuk yang layak dibawa pulang. Substitusi yang melahirkan keluaran sah namun keliru. Uji sintaks yang memvalidasi struktur tapi bukan keterjangkauan. Keduanya lolos ujinya masing-masing. Keduanya keliru. Verifikasi yang layak dipertahankan adalah yang menanyakan apakah hasilnya lengkap dan apakah dunia yang diandaikannya sungguh ada — bukan sekadar apakah ia bisa diurai.
Istilah
- Reverse proxy
- Web server yang menerima permintaan dari luar lalu meneruskan tiap permintaan ke service internal yang semestinya menanganinya.
- Alamat dengar
- Alamat jaringan tertentu tempat server menerima sambungan. Memakai alamat berbeda untuk environment berbeda memisahkan keduanya tanpa perlu nama yang berbeda.
- Alamat loopback
- Alamat yang cuma tergapai dari mesin itu sendiri, sehingga jadi rumah yang wajar bagi environment development.
- File hosts
- Tabel lokal yang memetakan nama ke alamat, dibaca sebelum server DNS. Menyuntingnya adalah cara komputer kerja memilih environment mana yang digapai sebuah nama.
- Render template
- Menghasilkan file konfigurasi akhir dengan menyulihkan nilai ke dalam kerangka, bukan menyunting file akhirnya langsung.
- Partial
- Potongan konfigurasi yang dipakai bersama beberapa template, sehingga blok yang wajib identik di mana-mana cuma ada di satu tempat.
Technologies covered
- Bash
- Linux
- Docker
- nginx
- TLS