Selama pengerjaan, sistem dijalankan melalui mix phx.server dari dalam folder proyek, dan cara itu tidak layak digunakan pada server produksi. Topik ini membahas mix release, yaitu mekanisme Mix untuk menghasilkan artefak yang dapat dijalankan di server target tanpa menginstal Elixir di sana. Pembahasan mencakup isi artefaknya, konfigurasi runtime yang dibaca saat sistem dimulai, serta satu skrip kecil untuk menguji keputusan konfigurasinya lebih dahulu.
mix release dan sistem terkandung#
Perintah MIX_ENV=prod mix release menghasilkan sistem terkandung (self-contained), yaitu satu direktori yang memuat kode aplikasi terkompilasi, seluruh dependensinya, dan runtime Erlang/OTP itu sendiri. Server target cukup menerima direktori tersebut, sedangkan Elixir tidak perlu diinstal di servernya karena runtime sudah dibawa bersama artefak. Mode produksi yang disetel oleh MIX_ENV=prod sekaligus mematikan perkakas pengembangan, misalnya halaman debug dan pemuatan kode panas, sehingga artefaknya berperilaku layaknya sistem produksi.
# tidak-dijalankan
# kompilasi produksi memakan waktu menit-menit sehingga melebihi batas blok eksekusi
# buat artefak rilis produksi dari folder proyek
cd ~/papan-suhu
MIX_ENV=prod mix releaseArtefak tersimpan di _build/prod/rel/papan_suhu, dan bagian akhir jalurnya mengikuti nama proyek. Isinya berpola sama untuk setiap rilis:
| Isi artefak | Fungsi |
|---|---|
bin/papan_suhu | berkas perintah utama, misalnya start, stop, rpc, dan version |
erts-<versi>/ | runtime Erlang/OTP yang disertakan bersama artefak |
lib/ | aplikasi OTP hasil kompilasi beserta seluruh dependensinya |
releases/ | berkas boot dan catatan konfigurasi per versi rilis |
Sistem dimulai dari berkas perintah utamanya, dan variabel lingkungan wajib disetel lebih dahulu karena konfigurasinya dibaca pada saat start:
# tidak-dijalankan
# variabel lingkungan produksi disetel sebelum sistem dimulai
export SECRET_KEY_BASE=$(mix phx.gen.secret)
export PHX_HOST=contoh.local
PHX_SERVER=true _build/prod/rel/papan_suhu/bin/papan_suhu startPerintah start menjalankan sistem di depan, sehingga lognya mengalir ke terminal, sedangkan perintah daemon melepaskannya ke latar. Variabel PHX_SERVER=true membuat endpoint memulai server HTTP saat sistem berjalan, sedangkan PHX_HOST menyatakan host yang digunakan URL halamannya.
Artefak yang sama juga melayani pemeriksaan tanpa memulai sistem penuh. bin/papan_suhu version mencetak versi rilisnya, bin/papan_suhu rpc menjalankan ekspresi di dalam sistem yang sedang berjalan, dan bin/papan_suhu eval mengeksekusi ekspresi sekali jalan. Perintah-perintah tersebut menggunakan runtime dari folder erts-<versi>/, sehingga tetap berfungsi pada server tanpa instalasi Elixir.
Konfigurasi runtime#
Berkas konfigurasi di folder config/ dievaluasi pada waktu kompilasi, sedangkan config/runtime.exs dievaluasi setiap kali sistem dimulai. Perbedaan waktu evaluasi itu menentukan tempat setiap jenis nilai. Nilai yang tetap untuk semua lingkungan boleh berada di config.exs, sedangkan nilai yang berbeda antara pengembangan dan produksi, misalnya host, port, dan kredensial, dibaca dari variabel lingkungan di runtime.exs. Proyek hasil phx.new sudah menyertakan pola ini, dan bentuk intinya adalah sebagai berikut:
# tidak-dijalankan
# dievaluasi setiap kali sistem rilis dimulai, bukan saat kompilasi
if config_env() == :prod do
host = System.fetch_env!("PHX_HOST")
port = String.to_integer(System.get_env("PORT") || "4000")
kunci = System.fetch_env!("SECRET_KEY_BASE")
config :papan_suhu, PapanSuhuWeb.Endpoint,
url: [host: host],
http: [ip: {0, 0, 0, 0, 0, 0, 0, 0}, port: port],
secret_key_base: kunci,
server: true
endDua fungsi pembaca variabel memegang peran berbeda. System.fetch_env!/1 melempar error dengan pesan yang jelas apabila variabelnya tidak ada, sehingga sistem yang kuncinya hilang gagal cepat pada saat start dan bukan berjalan dengan kunci kosong. System.get_env/2 menerima nilai default, sehingga cocok untuk nilai yang tidak wajib disetel, misalnya port 4000. Kunci server: true pada konfigurasi endpoint memainkan peran yang sama dengan variabel PHX_SERVER=true pada perintah start di atas.
Perbedaan waktu evaluasinya juga menjelaskan cara memutakhirkan sistem. Perubahan pada config.exs menuntut artefak dibangun ulang, sedangkan perubahan nilai yang dibaca di runtime.exs cukup ditangani dengan penghidupan ulang sistem tanpa kompilasi apa pun. Nilai barunya langsung terbaca pada start berikutnya.
Skrip utuh: ceklis konfigurasi runtime#
Pola pembacaan di atas dapat diuji tanpa membuat rilis lebih dahulu. Skrip berikut membaca ketiga variabel dengan nilai default, lalu mencetak keputusannya. Pada lingkungan tanpa variabel lingkungan, ketiga default yang tercetak, sedangkan pada server produksi nilai aslinya yang tercetak:
# runtime_ceklis.exs — membaca konfigurasi runtime dengan nilai default
host = System.get_env("PHX_HOST", "contoh.local")
server = System.get_env("PHX_SERVER", "false")
kunci = System.get_env("SECRET_KEY_BASE", "")
IO.puts("PHX_HOST: #{host}")
status_server = if server == "true", do: "aktif", else: "nonaktif"
IO.puts("PHX_SERVER: #{status_server}")
if kunci == "" do
IO.puts("SECRET_KEY_BASE: belum diset, start produksi akan gagal")
else
IO.puts("SECRET_KEY_BASE: sudah diset, panjang #{String.length(kunci)} karakter")
endPHX_HOST: contoh.local
PHX_SERVER: nonaktif
SECRET_KEY_BASE: belum diset, start produksi akan gagalKeluaran di atas dihasilkan pada lingkungan tanpa variabel lingkungan, sehingga ketiga default terlihat. Baris terakhirnya menyatakan akibat yang sama dengan mekanisme System.fetch_env!/1 pada runtime.exs, yaitu sistem produksi menolak berjalan tanpa kuncinya. Uji coba semacam ini berbiaya rendah dan dilakukan sebelum artefak dibangun. Keputusan yang sudah tercetak dengan benar di sini tinggal dipindahkan ke runtime.exs.
Kesalahan umum#
Kesalahan pertama adalah menyimpan SECRET_KEY_BASE di berkas konfigurasi yang ikut terkompilasi. Kredensial itu tercetak ke dalam artefak dan berpindah bersamanya, sehingga setiap pihak yang memegang artefak memegang kuncinya. Nilai semacam ini harus dibaca dari variabel lingkungan di runtime.exs, dan System.fetch_env!/1 memastikan start gagal cepat apabila variabelnya belum disetel.
Kesalahan kedua adalah mengira config/prod.exs dibaca ulang saat sistem dimulai. Berkas itu dievaluasi saat kompilasi, sehingga perubahan variabel lingkungan setelah artefak dibangun tidak berpengaruh pada nilai yang ditetapkan di sana. Nilai yang berbeda per lingkungan ditempatkan di runtime.exs, sedangkan config/prod.exs hanya memuat nilai produksi yang tetap.
Kesalahan ketiga adalah melupakan penyalakan server. Sistem yang dimulai tanpa PHX_SERVER=true atau server: true tampak sehat pada lognya, tetapi tidak ada halaman yang dibalas, karena endpoint tidak memulai server HTTP. Pemeriksaannya sederhana, yaitu membuka halaman sistem setelah start, persis seperti bukti pada titik pemeriksaan P4.
Ringkasan#
MIX_ENV=prod mix releasemenghasilkan sistem terkandung berisi kode terkompilasi, dependensi, dan runtime Erlang/OTP, sehingga server target tidak perlu menginstal Elixir.config/runtime.exsdievaluasi setiap kali sistem dimulai, sehingga host, port, dan kredensial dibaca dari variabel lingkungan di sana, bukan di berkas konfigurasi yang terkompilasi.System.fetch_env!/1membuat start gagal cepat apabila kredensial wajib hilang, sedangkanSystem.get_env/2dengan default cocok untuk nilai yang tidak wajib disetel, seperti port.- Sistem dimulai dari berkas
bin/papan_suhudenganstartdi depan ataudaemondi latar, danPHX_SERVER=truememastikan server HTTP ikut berjalan.
Referensi#
- Mix.Tasks.Release — Hexdocs Mix — dokumentasi resmi mix release beserta isi artefaknya.
- Panduan mix release — Hexdocs Phoenix — langkah rilis untuk proyek Phoenix lengkap dengan variabel lingkungannya.
- Deployment overview — Hexdocs Phoenix — pilihan deployment umum bagi aplikasi Phoenix.
- Modul System — Hexdocs Elixir — System.get_env/2 dan System.fetch_env!/1 pada konfigurasi runtime.