Skip to content
Blog DevOps

Bagaimana Saya Mempromosikan Config Antar Environment Tanpa Menimpa Kunci yang Mendekripsi Production

By Romi Fadli, S.Kom. 21 min read Environments & Deployment
On this page

Sebuah rilis sudah siap dan sudah diuji. Perubahan yang sama kini harus sampai ke mesin yang dipakai orang sungguhan — tapi tidak semuanya, sebab file setelan di kedua mesin itu serupa bentuknya dan berbeda maknanya.

Menyalin file-nya adalah langkah yang paling terpikirkan. Ia juga cara menyerahkan password database mesin uji ke sistem yang hidup, sekaligus menimpa satu-satunya kunci yang membuat data bertahun-tahun tetap terbaca.

Pendahuluan

Tiap program yang berguna perlu diberi tahu beberapa perkara yang tak bisa ia simpulkan sendiri: di mana database-nya, password apa yang dipakai, berapa permintaan per menit yang boleh dilayani. Itulah setelan-nya, dan ia disimpan di file konfigurasi — daftar sederhana berisi nama dan nilai, yang tinggal di luar program itu sendiri.

Pemisahan itu disengaja, dan justru itulah yang membuat seluruh isi tulisan ini mungkin sekaligus berbahaya. Karena setelannya di luar, satu hasil build program bisa berjalan di beberapa tempat dengan perilaku berbeda di masing-masing. Karena setelannya di luar pula, memindahkan program dari satu tempat ke tempat lain berarti memindahkan — atau justru dengan sengaja tidak memindahkan — file tersebut.

A. Apa itu environment, dan kenapa jumlahnya lebih dari satu

Environment adalah satu salinan sistem yang utuh dan berjalan: programnya, database-nya, file-nya, setelannya. Kebanyakan project berakhir dengan lebih dari satu, dan semuanya tak bisa saling tukar.

Development tempat pekerjaan baru dikerjakan. Lazimnya jalan di laptop, memuat data karangan, dan merusaknya cuma berongkos satu sore.

Staging adalah gladi bersih. Ia menjalankan persis rilis yang sebentar lagi naik, di atas data yang menyerupai aslinya — kerap dipulihkan dari backup terkini. Tujuannya membuat kegagalan terjadi di depan orang yang sanggup memperbaikinya, bukan di depan user. Aksesnya lebih longgar: lebih banyak orang bisa masuk, dump diambil, eksperimen berlangsung.

Production yang diandalkan user sungguhan. Ia memegang satu-satunya salinan data yang berarti, dan kekeliruan di sana diukur dengan insiden, bukan dengan sore yang terbuang. Aksesnya paling ketat, perubahannya paling jarang, dan tiap perubahan itu dicatat.

Perbedaan terakhir itu — akses longgar di staging, data sungguhan di production — adalah seluruh alasan kenapa batas di antara keduanya wajib ada. Kalau kredensial yang bocor di staging juga membuka production, memisahkan keduanya tak menghasilkan apa pun.

B. Apa sebenarnya yang berpindah saat rilis naik

Rilis adalah versi software tertentu yang sudah rampung: dibangun sekali, diberi label, lalu dijalankan apa adanya ke mana pun ia pergi. Yang dibangun itu disebut artefak — salinan program yang sudah terbungkus. Memindahkan rilis dari satu environment ke environment berikutnya disebut mempromosikan-nya.

Yang membuat promosi lebih rumit ketimbang menyalin file: rilis bukan satu benda. Ia punya dua paruh, dan keduanya berpindah dengan cara berbeda.

Paruh pertama adalah artefak beserta setelannya. Artefaknya sendiri sungguh identik di mana-mana — memang itu gunanya dibangun sekali. Setelannya tidak: sebagian menggambarkan rilisnya, sebagian lagi menggambarkan tempat ia berjalan.

Paruh kedua adalah skema database — struktur tabel yang diharapkan program. Saat rilis baru menambah fitur, kerap ia butuh kolom atau tabel baru. Artefaknya membawa kode yang memakai kolom itu, tapi tak membawa kolomnya. Harus ada yang mengubah database-nya secara terpisah, dan langkah itu disebut migration skema.

sql
-- artefaknya membawa kode yang membaca kolom ini;
-- kolomnya sendiri tidak ikut terbawa
ALTER TABLE documents ADD COLUMN signed_at timestamptz;

Kedua paruh ini gagal dengan cara yang berbeda, dan saya terjegal keduanya. Config yang ikut berpindah padahal semestinya tidak menjatuhkan production, atau lebih buruk. Migration skema yang tak pernah jalan sama sekali tak menjatuhkan apa pun — ia menunggu, tanpa suara, sampai tiba permintaan yang membutuhkan kolom yang tak ada.

Itu sebabnya tulisan ini membahas keduanya sekaligus. Keduanya bukan dua topik yang kebetulan berdekatan, melainkan dua paruh dari satu peristiwa yang sama: memindahkan rilis ke tempat yang dipakai orang. Menyelesaikan salah satunya saja meninggalkan separuh perjalanan yang belum tertangani.

C. Apa taruhannya kalau ini keliru

Kebanyakan kekeliruan konfigurasi berujung gangguan layanan. Service tak sanggup menggapai database-nya, health check gagal, ada yang menyadarinya dalam hitungan menit, lalu nilai sebelumnya dikembalikan. Tidak enak, dan sepenuhnya bisa dipulihkan.

Satu di antaranya tidak begitu. Di antara setelan itu ada kunci yang dipakai mengenkripsi kolom database tertentu — data pribadi, di sistem ini. Kunci itu bukan password yang disodorkan service ke pihak lain; ia yang membuat nilai tersimpan sebelumnya bisa dibaca sama sekali. Timpa ia di production, maka tiap nilai yang terenkripsi memakai kunci lama jadi tak terbaca. Mengembalikan kunci lama memulihkan aksesnya, tapi cuma bila kunci itu masih ada. Kalau penimpaannya sudah tersimpan dan file sebelumnya sudah lenyap, datanya ikut lenyap.

Jadi kedua jenis kegagalan itu tak sebanding. Kebanyakan berongkos menit. Satu bersifat permanen, dan ia tampak persis seperti yang lain sampai detik ada yang mencoba membaca catatan lama.

Ketaksetaraan itulah yang membentuk seluruh isi di bawah. Tool yang memperlakukan semua setelan sama saja adalah tool yang cepat atau lambat, dan tanpa suara, akan melakukan kekeliruan yang mahal.

D. Kenapa ini bukan sekadar menyalin file

Saya mengukurnya, sebab saya ingin tahu bentuk persoalannya yang sebenarnya sebelum menulis apa pun. Selama ini yang saya punya cuma firasat bahwa menyalin file itu berbahaya; firasat bukan dasar yang cukup untuk memutuskan apa yang boleh dan tak boleh berpindah.

Konfigurasi service utamanya memuat 136 kunci, baik di staging maupun production. Bukan mirip — identik, kunci demi kunci. Dan dari 136 itu, kira-kira 21 bernilai berbeda, disengaja, dan wajib tetap begitu.

Ke-21 itu terbagi dua kelompok. Pertama rahasia: password database, kunci penanda tangan, dan kunci enkripsi yang dibahas di atas. Kedua alamat: hostname, URL service, daftar proxy yang dipercaya tiap environment. Tiap environment punya jaringannya sendiri, jadi tiap alamat berbeda.

Menyalin file-nya menetapkan ke-136-nya. Ia benar untuk 115 yang memang harus ikut, sekaligus menimpa 21 yang semestinya tidak — mengarahkan production ke database staging, dengan password staging, memercayai jaringan staging.

ini
# kunci sama, posisi sama, nilai yang tak boleh sekali-kali tertukar
DB_HOST=db.staging.internal        # staging
DB_HOST=db.production.internal     # production
APP_ENCRYPTION_KEY=...             # berbeda, dan mendekripsi kolom tersimpan

E. Lima hambatan yang menghalangi

Jadi tujuannya sempit sekaligus canggung: memindahkan konfigurasi satu rilis dari staging ke production sedemikian rupa sehingga production berakhir menjalankan rilis baru, sementara kedua puluh satu nilai tadi tetap persis di tempatnya. Tanpa seorang manusia membaca 136 baris lalu memutuskannya satu per satu, sebab itu pekerjaan yang tak seorang pun mengerjakannya dengan benar dua kali berturut-turut.

Gampang dirumuskan, canggung dibangun. Ada lima hambatan yang berdiri antara kalimat itu dan promosi yang berani saya jalankan tanpa membacanya lebih dulu, dan dua yang terakhir punya sifat yang tak dimiliki tiga yang pertama: keduanya lolos tiap pemeriksaan otomatis lalu menagih utangnya belakangan.

Hambatan pertama: kedua file itu tampak bisa saling tukar, dan justru itulah yang membuatnya berbahaya. Kunci sama, jumlah sama, urutan sama — tak ada apa pun saat keduanya dibuka berdampingan yang menyiratkan seperlima nilainya menanggung beban dengan cara yang tak ditanggung selebihnya. Seandainya kedua environment memegang konfigurasi yang kelihatan berbeda, tak seorang pun akan terpikir menyalin yang satu menimpa yang lain; kemiripannya itulah yang membuat jalan pintasnya menggoda. Alternatif yang ditolak justru yang paling kentara — menyalin file — yang memang memindahkan 115 kunci yang seharusnya pindah, sekaligus diam-diam menyeret 21 yang seharusnya tidak.

Hambatan kedua: rahasia baru sama sekali tak boleh disalin dari staging. Ketika rilis memperkenalkan kredensial yang sebelumnya tak ada, nilai yang duduk di staging adalah nilai yang keliru untuk dipromosikan — bukan karena bentuknya cacat, melainkan karena memakainya bersama berarti satu kebocoran di staging ikut membuka production. Staging adalah tempat eksperimen dilakukan, tempat dump diambil, tempat akses diberikan lebih longgar; memang itu gunanya. Dua alternatif harus dicoret: menyalin nilai staging, dan membiarkan tool mengarang nilainya sendiri. Keduanya berakhir pada kredensial yang tak seorang pun memilihnya dengan sengaja, jadi tool-nya menolak melanjutkan dan menyebut nama kuncinya.

Hambatan ketiga: konfigurasi bersarang tak bisa dibandingkan baris per baris. Ubah indentasinya, tukar urutan kuncinya, pindahkan satu nilai ke baris baru — perbandingan berbasis baris melaporkan selisih besar yang sama sekali tak bermakna, sementara perubahan sungguhan yang terkubur jauh di dalam satu seksi bersarang cuma satu baris yang tampak seperti kebisingan yang sama. Diff berbasis baris itulah alternatif yang ditolak di sini, dan penggantinya adalah menyusuri kedua dokumen lalu membandingkan nilai pada key path yang bersesuaian, sehingga reformat jadi tak kelihatan — dan memang begitu semestinya.

Hambatan keempat: migration skema bisa diam-diam tak pernah berjalan. Satu service menerapkan skemanya lewat init script, dan image database menjalankan script semacam itu hanya ketika data directory-nya kosong. Untuk environment yang sungguh-sungguh baru, itu benar. Staging bukan environment baru — ia dilahirkan segar lalu di-restore dari backup production, justru supaya rilisnya digladi resik di atas data yang realistis, dan begitu restore itu selesai, direktorinya tak lagi kosong. Container naik, health check lolos, dan skema lama tetap sah sempurna untuk tiap kolom yang sudah dikenal service itu. Mengandalkan init script adalah alternatif yang gagal, dan ia gagal dengan cara tak mengerjakan apa pun.

Hambatan kelima: container yang hijau bukan rilis yang terverifikasi. Sesudah promosi dan migration, semuanya melapor sehat, dan itu bernilai persis seluas cakupan pemeriksaannya. Health check bertanya kepada sebuah komponen apakah ia sedang berjalan; ia tak sanggup bertanya apakah sistemnya bekerja. Alternatif yang berhenti saya andalkan adalah health check itu sendiri, dan penggantinya bukan sebuah pemeriksaan — melainkan memakai sistemnya: menandatangani satu dokumen dari ujung ke ujung, yang dalam satu tindakan melewati certificate authority, signing service, object store, database, dan revocation list sekaligus, lalu gagal dengan ribut bila salah satunya salah konfigurasi.

Kelimanya terbelah dengan rapi. Tiga yang pertama soal apa yang boleh berpindah dan bagaimana mengenali apa yang berubah — ketiganya diselesaikan di dalam tool promosinya, dan ketiganya gagal selagi kita menonton. Dua yang terakhir soal apa yang terjadi sesudah tool itu melapor berhasil, dan keduanya masuk satu golongan yang layak diberi nama: kegagalan yang menunggu. Tak satu pun muncul saat deploy, keduanya selamat melewati tiap pemeriksaan otomatis, dan pada saat gejalanya tiba, perubahan yang menyebabkannya sudah bukan lagi perubahan yang terbaru. Itulah kenapa urutannya berarti — tiga yang pertama bisa diverifikasi dengan membaca keluaran, dua yang terakhir cuma bisa diverifikasi dengan benar-benar memakainya.

Metode

A. Promosikan strukturnya, bukan isinya

Inilah jawaban atas hambatan pertama: dua file yang tampak bisa saling tukar padahal tidak.

Wawasan yang membuat ini bisa dikerjakan: yang sesungguhnya perlu dibawa rilis bukan nilai, melainkan struktur.

Rilis baru menambah kunci, membuang kunci, dan mengubah makna sebagian kunci yang tidak sensitif. Ia tak perlu mengubah password atau alamat milik production — keduanya milik environment, bukan milik rilis.

Maka tool-nya membandingkan kedua file lalu mempromosikan tepat tiga golongan:

Kunci yang baru di rilis itu, ditambahkan dengan nilai bawaan dari rilisnya supaya service tak menyala sambil mencari setelan yang tak ada.

Kunci yang dibuang, dihapus supaya konfigurasi tak menumpuk setelan yang tak lagi dibaca siapa pun.

Nilai tidak sensitif yang berubah — batas laju, umur cache, sakelar fitur. Ketiganya bagian dari apa rilis itu.

Selebihnya dibiarkan persis seperti yang dipegang production. Hasilnya file dengan bentuk milik rilis dan identitas milik environment.

bash
classify() {                 # tiap kunci jatuh ke tepat satu golongan
  case "$1" in
    *PASSWORD|*SECRET|*_TOKEN|*PASSPHRASE) echo secret ;;   # tak pernah dipromosikan
    *_HOST|*_URL|TRUSTED_*)                echo address ;;  # tak pernah dipromosikan
    *)                                     echo plain ;;    # dipromosikan bila berubah
  esac
}

B. Rahasia baru adalah penghentian, bukan penyalinan

Inilah jawaban atas hambatan kedua, dan satu-satunya tempat tool-nya sengaja berhenti ketimbang memutuskan.

Saat rilis baru memperkenalkan rahasia — kunci penanda tangan baru, kredensial untuk service yang sebelumnya tak ada — tool-nya menolak melanjutkan. Ia tak menyalin nilainya dari staging, dan tak mengarang sendiri.

Menyalinnya berarti staging dan production berbagi kredensial. Itu lebih buruk dari kedengarannya: staging adalah environment tempat eksperimen terjadi, tempat dump diambil, tempat akses diberikan lebih longgar. Kebocoran di staging yang sekaligus membuka production menghapus seluruh gunanya punya batas.

Maka tool-nya berhenti, menyebut nama kuncinya, dan meminta operator menetapkannya di production dengan sengaja. Ini gangguan, dan disengaja. Alternatifnya adalah tool yang diam-diam menggerus pemisah antara environment tempat kekeliruan itu murah dan environment tempat ia tidak.

bash
if [ "$(classify "$key")" = secret ] && ! exists_in "$target" "$key"; then
  echo "BLOCKED: new secret '$key' is not set in production yet"
  exit 1
fi

C. Membandingkan format bersarang lewat kunci, bukan baris

Inilah jawaban atas hambatan ketiga: membedakan perubahan sungguhan dari sekadar hasil reformat.

Tak semua service memakai format konfigurasi yang sama. Sebagian membaca file kunci-nilai sederhana; sebagian lagi membaca dokumen terstruktur yang bersarang.

Untuk yang bersarang, perbandingan berbasis baris adalah tool yang keliru. Memformat ulang dokumen — indentasi berbeda, urutan kunci berubah, sebuah nilai pindah ke baris baru — melahirkan perbedaan besar yang tak bermakna apa-apa. Sementara itu perubahan sungguhan jauh di dalam sebuah seksi bersarang bisa cuma satu baris yang tenggelam sebagai derau di antara perubahan format tadi.

Maka dokumen terstruktur dibandingkan lewat jalur kunci: tool-nya menelusuri kedua dokumen, menyusun himpunan jalur yang dimuat masing-masing, lalu membandingkan nilai pada jalur yang bersesuaian. Perubahan urutan dan format jadi tak kasat mata, dan itu memang benar, sebab keduanya memang bukan perubahan.

bash
# bandingkan lewat makna, bukan baris: ratakan dulu jadi jalur kunci
jq -r 'paths(scalars) | join(".")' staging.json | sort > /tmp/a
jq -r 'paths(scalars) | join(".")' production.json | sort > /tmp/b
comm -3 /tmp/a /tmp/b        # cuma perbedaan struktural sungguhan yang tersisa

D. Dry run dulu, dan cadangan tiap file yang ditulisnya

Tool-nya punya mode yang melaporkan tiap perubahan yang akan ia buat lalu tak membuat satu pun, dan memakainya lebih dulu bukan pilihan dalam cara kerja saya. Promosi konfigurasi bukan langkah yang bentuknya ingin Anda temukan sambil ia berjalan.

Saat ia memang menulis, ia mencadangkan tiap file lebih dulu, lengkap dengan penanda waktu. Cadangan itulah yang membuat kasus terburuk di alinea sebelumnya masih bisa dilewati: bila sebuah nilai terpromosikan padahal semestinya tidak, file sebelumnya masih ada di disk.

bash
# Cuma melapor kecuali --apply diminta, dan tak pernah menulis tanpa salinan.
if ! $apply; then
  printf '%s %s -> %s\n' "$key" "$current" "$incoming"   # tampilkan, jangan ubah
  return 0
fi

cp -p "$target" "$target.$(date +%Y%m%d-%H%M%S).bak"       # jalan pulang, per file
write_key "$target" "$key" "$incoming"

E. Migration yang tak pernah jalan

Inilah jawaban atas hambatan keempat, dan yang pertama dari dua hambatan yang gagal dengan cara menunggu.

Mempromosikan konfigurasi baru separuh dari memindahkan rilis. Separuh lainnya adalah skema database, dan di sanalah bersarang kegagalan yang takkan saya duga.

Service yang ditulis dalam bahasa yang jalur start-nya tak punya framework migration otomatis bisa menerapkan skemanya lewat script inisialisasi. Image database menjalankan script semacam itu cuma saat direktori datanya masih kosong. Untuk environment yang benar-benar baru, itu justru tepat.

yaml
volumes:
  # jalan cuma saat inisialisasi pertama — tak pernah lagi begitu datanya ada
  - ./config/schema.sql:/docker-entrypoint-initdb.d/schema.sql:ro

Staging bukan environment yang benar-benar baru. Ia dilahirkan segar lalu dipulihkan dari backup production, justru supaya rilisnya digladi bersih terhadap data yang menyerupai aslinya. Begitu pemulihan itu selesai, direktori datanya tak lagi kosong.

Jadi perubahan skemanya tak akan pernah diterapkan. Dan cara kegagalan itu menampakkan diri adalah yang paling buruk:

Container-nya menyala. Health check lolos. Skema lama sepenuhnya sah untuk tiap kolom yang sudah dikenal service, jadi apa pun yang tak menyentuh kolom baru berperilaku normal. Kegagalannya menunggu sampai sebuah permintaan mencapai kode yang mengharapkan kolom yang tak ada — mungkin saat peninjauan rilis, mungkin sesudah promosi, di production.

Dua jalur: direktori data kosong menuju kotak hijau bertuliskan skema diterapkan, dan direktori yang dipulihkan dari backup menuju kotak bergaris merah bertuliskan skema dilewati, yang tetap berujung pada health check hijau.
Script inisialisasi cuma jalan di atas direktori data yang kosong. Begitu staging dipulihkan dari backup production, perubahan skemanya dilewati — dan health check, yang cuma menanyakan apakah service-nya berjalan, tetap hijau.

Perbaikannya: langkah migration terpisah yang eksplisit. Yang membuatnya andal adalah dari mana ia membaca migration-nya: dari dalam image container yang sedang berjalan, bukan dari repository. Migration-nya ikut bepergian bersama biner yang membutuhkannya, jadi server yang sudah menarik image baru otomatis punya migration yang bersesuaian — tak ada celah bagi keduanya untuk berbeda pendapat. Migration yang sudah diterapkan dicatat di skemanya sendiri, sehingga menjalankan langkahnya dua kali tak berbahaya.

F. Container hijau bukan rilis yang terverifikasi

Inilah jawaban atas hambatan kelima, dan inilah yang memampatkan tenggang menunggunya jadi nol.

Sesudah promosi dan migration, semuanya melapor sehat. Itu bernilai persis seluas cakupan pemeriksaannya.

Maka langkah terakhirnya sama sekali bukan pemeriksaan — melainkan memakai sistemnya. Dalam kasus saya: tanda tangani satu dokumen, dari ujung ke ujung. Tindakan tunggal itu menggerakkan certificate authority, service penanda tangan, penyimpan objek, database, dan daftar pencabutan, lalu gagal dengan lantang bila salah satunya salah konfigurasi.

bash
# Bukan health check: satu transaksi sungguhan lewat tiap komponen.
doc=$(upload_sample)                       # object store + database
sig=$(sign "$doc") || die "signing failed" # CA + signing service
verify "$doc" "$sig" || die "verification failed"   # revocation list
echo "release verified end to end"

Health check menanyai sebuah komponen apakah ia sedang berjalan. Menandatangani dokumen menanyai sistemnya apakah ia bekerja. Keduanya pertanyaan berbeda, dan cuma yang kedua yang dipedulikan siapa pun.

Hasil dan Pembahasan

A. Kenapa jumlah kunci yang identik justru kasus berbahaya

Alasan persoalan ini gampang dikerjakan keliru: kedua file-nya tampak bisa saling tukar. Kunci sama, jumlah sama, urutan sama. Tak ada apa pun dari membukanya berdampingan yang menyiratkan bahwa seperlima nilainya menanggung beban dengan cara yang tak berlaku bagi empat perlima sisanya.

Andai staging dan production punya konfigurasi yang kasat mata berbeda — himpunan kunci berbeda, struktur berbeda — takkan ada yang terpikir menyalin yang satu menimpa yang lain. Justru kemiripannya yang membuat jalan pintas itu menggoda.

Saya kini memperlakukan itu sebagai tanda bahaya secara umum: ketika dua hal identik secara struktur dan berbeda secara makna, perbedaannya tak kasat mata dan karena itu mahal. Pertahanannya: buat tool-nya yang menyandikan perbedaan itu, sebab manusia yang membaca file-nya takkan melihatnya.

B. Kegagalan yang menunggu

Hambatan keempat dan kelima sedari awal diperkenalkan sepasang. Layak ditengok kenapa pasangan itu memang berpasangan, sebab alasannya struktural, bukan kebetulan.

Perubahan skema yang tak diterapkan tidak gagal saat deploy; ia gagal saat sebuah permintaan tertentu tiba. Kunci enkripsi yang tertimpa tidak gagal saat promosi; ia gagal saat ada yang mencoba membaca data lama.

Pada keduanya, saat kerusakan terjadi dan saat ia ditemukan terpisah — berjam-jam, atau selama apa pun jeda sebelum ada yang menyusuri jalur itu. Keterpisahan itulah yang membuat keduanya mahal. Begitu gejalanya muncul, perubahan yang menyebabkannya bukan lagi perubahan terkini, dan penyelidikannya bermula di tempat yang keliru.

Inilah alasan memilih verifikasi yang menggerakkan sistem ketimbang memeriksanya. Menandatangani dokumen sesudah promosi memampatkan jeda itu jadi nol: apa pun yang rusak, rusaknya sekarang, di depan orang yang baru saja mengubah sesuatu — bukan Selasa depan di depan seorang user.

C. Apa yang sengaja tak dikerjakan tool-nya

Ia tidak memutuskan apa yang tergolong rahasia. Daftar itu dirawat tangan, dan ia jenis daftar eksplisit yang sama dengan yang saya pakai untuk rotasi kredensial, dengan alasan yang sama: aturan yang menyimpulkan sensitivitas dari nama sebuah kunci cepat atau lambat akan keliru, dan ongkos kekeliruannya tak setara.

Ia juga tidak mempromosikan otomatis sebagai bagian dari sebuah pipeline. Promosi itu keputusan, bukan peristiwa. Tool-nya membuat keputusan itu murah diperiksa — itulah seluruh sumbangannya — tapi tetap harus ada yang menengok hasil dry run-nya lalu menyetujuinya.

D. Di mana ini berlaku umum

Kalau environment Anda sepenuhnya dikonfigurasi lewat secret manager, sebagian besar bahasan ini sudah diurus untuk Anda: nilainya diambil saat runtime menurut environment-nya, dan tak ada file yang perlu disalin. Itu rancangan yang lebih baik, dan saya akan memilihnya untuk sistem baru.

Yang berlaku umum terlepas dari itu adalah pembedaan yang mendasarinya. Konfigurasi memuat dua jenis muatan yang berbeda: apa rilis itu, dan di mana rilis itu berjalan. Yang pertama harus ikut berpindah. Yang kedua tak boleh. Mekanisme apa pun yang tak sanggup membedakan keduanya cepat atau lambat akan memindahkan sesuatu dari golongan kedua, dan kunci termahal di golongan itu biasanya justru yang mendekripsi data Anda.

Simpulan

Mempromosikan konfigurasi antar environment bermuara pada satu pertanyaan yang diajukan ke tiap kunci: ini bagian dari rilisnya, atau bagian dari environment-nya? Jawab itu per kunci dan mekanismenya mengikuti — promosikan struktur dan perubahan yang tak sensitif, biarkan rahasia dan alamat, berhenti sama sekali saat muncul rahasia baru.

Pelajaran yang tak saya duga menyangkut skema. Migration yang cuma jalan saat direktori data masih kosong adalah migration yang bekerja di tiap pengujian lalu diam-diam tak mengerjakan apa pun di satu environment yang justru menentukan — environment yang sedang menggladi rilis terhadap data sungguhan. Ia melapor sukses sepanjang jalan.

Dan itu membawa kedua bagian pekerjaan ini ke tempat yang sama. Sesudah promosi, container yang hijau berarti container-nya menyala. Ia tak mengatakan apa pun tentang apakah rilisnya bekerja. Tanda tangani satu dokumen.

Istilah

Konfigurasi
Setelan yang disimpan di luar program supaya satu hasil build bisa berjalan di beberapa environment.
Promosi
Memindahkan rilis, beserta perubahan konfigurasi yang dituntutnya, dari satu environment ke environment berikutnya.
Rahasia
Nilai konfigurasi yang memberi akses atau melindungi data. Dibedakan dari nilai lain oleh akibatnya, bukan oleh formatnya.
Jalur kunci
Posisi sebuah nilai di dalam dokumen bersarang, dipakai untuk membandingkan konfigurasi terstruktur berdasarkan makna, bukan baris.
Migration skema
Perubahan struktur database yang wajib diterapkan dengan sengaja, sebab database yang sudah ada takkan menerimanya cuma dengan menyala.
Dry run
Mode yang melaporkan apa saja yang akan diubah, tanpa mengubah apa pun.

Technologies covered

  • Linux
  • PostgreSQL
  • Docker

About the author

Photo of Romi Fadli

Romi Fadli, S.Kom.

Solution Architect · Backend Engineer · Embedded Systems

In my day job I work as a solution architect. I design the system, lead the team that builds it — quality assurance, backend developers, a UI developer, DevOps and DevSecOps — and write the most critical modules myself, such as a certificate authority, a long-term PDF signer, and the core API of a personnel system.

I work across the whole stack that a running system actually needs. On the hardware side that means PCB design, microcontrollers, and embedded firmware — including RISC-V boards running FreeRTOS and Embassy, speaking MQTT over mutually authenticated TLS on a microcontroller with 64 KB of SRAM.

Most of my day-to-day work is software: Rust backends and API modules built on Clean Architecture, an OAuth2/OpenID Connect authorization server written from scratch, PAdES document signing, and certificate authorities — root CAs, timestamping authorities, and OCSP responders. I also build the frontends that sit on top of them.

Running all of that means infrastructure: Docker Compose stacks, networking, Mikrotik and RADIUS, observability with OpenTelemetry, and a steady habit of writing command-line tools and utilities for Linux when the existing ones do not fit.

Share

Enjoyed this?

Get an email when a new article is published.

Referenced by

A staging configuration with an orange banner and a production configuration with a green banner; ordinary keys cross over with arrows, while a locked secret is stopped halfway by a red cross and production keeps its own lock

Tiga perintah yang membawa rilis teruji ke production tanpa menimpa apa yang dimilikinya: mempromosikan struktur konfigurasi tapi mempertahankan rahasia dan alamatnya, menolak menyalin rahasia baru, dan menerapkan migration yang dibawa image.

10 min read Read more →
Three stages in a row — development, staging and production — with an image passing from development to staging, configuration promoted from staging to production, staging fed with real data restored from a production backup, and production marked done once its core function is proven

Release Workflow: Development to Production adalah prosedur yang ditempuh tiap perubahan saya dari laptop sampai production — langkah mana yang saya otomatiskan, mana yang sengaja saya serahkan ke manusia, kenapa tiap langkah berada di tempatnya, cara saya mendiagnosis rilis yang gagal, dan cara pulihnya.

9 min read Read more →

Related Articles

A stack of Markdown documents and an orange arrow leading to a browser page with a side menu, a heading, paragraphs, a dark code panel, and a table of contents with its active entry highlighted

Satu script yang mengubah dokumentasi Markdown satu project menjadi situs statis yang terbuka dari folder tanpa server maupun internet: menu, daftar isi yang mengikuti pembaca, tabel yang tak pernah terpotong, kode dengan tombol salin, dan tautan yang tetap berfungsi.

7 min read Read more →
An old layout with a grey banner and a new structure with a green banner, each holding a blue database, a purple document store, and an orange telemetry store; three coloured paths pass through one export package marked with a green check

Empat perintah yang memindahkan stack dari layout lama ke struktur baru melintasi versi mayor: export tanpa mengubah data sumber, import tiap store sesuai cara yang diizinkan versinya, verifikasi apa yang benar-benar tiba, dan rename modul sambil memindahkan datanya.

11 min read Read more →
Three image layer stacks, each with a version tag of its own colour, blue, orange, and green, each leading to an archive file banded in the same colour

Perintah yang memberi versi tersendiri pada tiap modul stack, mem-build image-nya di bawah tag yang berarti tepat satu build, menolak memakai ulang versi atau menelan compile yang gagal, dan mengarsipkan image menurut nama dan tag supaya versi tak saling menimpa.

7 min read Read more →