Direktori Layanan dengan systemd - Menempatkan Data Runtime, State, dan Cache di Tempat yang Tepat
Sebuah layanan kecil memerlukan tempat untuk menyimpan Unix socket, database, metadata yang diunduh, dan mungkin file log-nya sendiri. Membuat satu direktori yang dapat ditulis lalu meletakkan semuanya di sana memang tampak praktis, tetapi cara ini menyembunyikan beberapa janji yang berbeda. File mana yang boleh hilang ketika layanan berhenti? Mana yang harus bertahan setelah reboot? Mana yang dapat dibuat ulang? Siapa yang membuat direktori sebelum proses tanpa privilege dimulai?
Di sistem Debian yang menggunakan systemd, pertanyaan-pertanyaan ini sering kali dapat dinyatakan di dalam unit itu sendiri. Directive seperti RuntimeDirectory= dan StateDirectory= memungkinkan service manager membuat direktori standar dengan ownership dan permission yang disengaja. tmpfiles.d tetap berguna, tetapi untuk batas yang berbeda: objek filesystem yang lifecycle-nya tidak bergantung pada satu layanan, atau aturan yang memerlukan cleanup berdasarkan umur dan operasi yang lebih kompleks.
Ini bukan berarti setiap mkdir harus dihapus. Ini adalah cara untuk menentukan siapa yang memiliki lifecycle sebuah direktori sebelum memilih mekanismenya.
Klasifikasikan data sebelum membuat path
Nama direktori seharusnya mengikuti arti datanya, bukan sekadar program yang menulisnya. Untuk system service, systemd.exec(5) memetakan lima pengaturan unit ke bagian filesystem yang umum:
| Tujuan | Pengaturan unit | Path sistem | Environment variable |
|---|---|---|---|
| Data runtime volatil | RuntimeDirectory= | /run/ | $RUNTIME_DIRECTORY |
| State aplikasi persisten | StateDirectory= | /var/lib/ | $STATE_DIRECTORY |
| Data cache yang dapat dibuat ulang | CacheDirectory= | /var/cache/ | $CACHE_DIRECTORY |
| Log milik layanan | LogsDirectory= | /var/log/ | $LOGS_DIRECTORY |
| Konfigurasi yang dikelola administrator | ConfigurationDirectory= | /etc/ | $CONFIGURATION_DIRECTORY |
Perbedaan ini memengaruhi pemulihan. Socket di bawah /run hanya bermakna ketika ada proses yang siap menjawabnya. Database antrean di bawah /var/lib mungkin menjadi catatan pekerjaan yang belum selesai. Metadata remote yang disimpan di /var/cache seharusnya dapat dibuat ulang dari sumber lain. Menganggap ketiganya sekadar “file aplikasi” membuat keputusan penghapusan dan backup menjadi berbahaya tanpa alasan.
Log membutuhkan keputusan tersendiri. Layanan yang hanya menulis ke standard output dan standard error mungkin sudah ditangani journal sehingga tidak memerlukan LogsDirectory=. Demikian pula, ConfigurationDirectory= bukan berarti daemon seharusnya menulis ulang konfigurasinya sendiri. Konfigurasi di bawah /etc biasanya menjadi wilayah administrator; directive ini terutama membuat path yang diharapkan menjadi eksplisit.
Mengapa /run memerlukan pemilik lifecycle
Debian Policy bagian 9.1.4 menyatakan bahwa /run biasanya dibersihkan saat boot, umumnya karena lokasi ini merupakan filesystem sementara. Karena itu, software tidak boleh berasumsi bahwa direktori yang dibuat kemarin masih ada setelah restart. Path lama /var/run dipertahankan sebagai pengalihan kompatibilitas, bukan sebagai rumah persisten yang terpisah.
Setup script yang dijalankan root dapat membuat ulang direktori, tetapi urutan, ownership, penanganan error, dan kebijakan penghapusannya kemudian berada di tempat lain, terpisah dari layanan. Baris ExecStartPre=/usr/bin/mkdir ... juga terasa janggal ketika layanan berjalan tanpa privilege: perintah itu tidak dapat membuat path, atau justru menerima privilege tinggi yang tidak diperlukan proses utama.
RuntimeDirectory=example-worker memberi PID 1 instruksi yang lebih sempit. Sebelum memulai proses, systemd membuat /run/example-worker. Secara default, direktori terdalam dimiliki akun yang diatur melalui User= dan Group=. Ketika layanan berhenti, direktori runtime tersebut secara default dihapus. Ownership, urutan startup, dan aturan cleanup biasa melekat pada unit yang sama.
Unit kecil dengan area tulis yang eksplisit
Worker fiktif berikut menggunakan penyimpanan runtime, state, dan cache. /usr/bin/sleep sengaja menjadi placeholder yang tidak berbahaya agar syntax unit dapat diperiksa tanpa memasang aplikasi:
[Unit]
Description=Example background worker
[Service]
Type=simple
User=example-worker
Group=example-worker
ExecStart=/usr/bin/sleep infinity
RuntimeDirectory=example-worker
RuntimeDirectoryMode=0750
StateDirectory=example-worker
StateDirectoryMode=0750
CacheDirectory=example-worker
CacheDirectoryMode=0750
Akun yang disebut dalam contoh ini hanya ilustrasi; buat system account khusus melalui sistem operasi atau proses package management, lalu ganti perintah placeholder dengan executable sebenarnya. Contoh ini meminta systemd menyiapkan path berikut:
/run/example-workeruntuk socket atau objek runtime volatil lainnya;/var/lib/example-workeruntuk state persisten yang diperlukan demi ketepatan operasi;/var/cache/example-workeruntuk data yang dapat dibuat ulang oleh worker.
Environment variable yang sesuai berisi path absolut. Aplikasi dapat membacanya jika memang dirancang demikian sehingga asumsi path tidak perlu diduplikasi. Software lama yang mengharapkan path konvensional tetap dapat menggunakannya; environment variable berguna, tetapi tidak wajib.
Directive mode penting karena default yang didokumentasikan adalah 0755. Mode 0750 masuk akal ketika hanya akun layanan dan grupnya yang boleh melintasi direktori, tetapi angka ini bukan nilai keamanan universal. Web server, backup agent, atau proses monitoring mungkin memerlukan akses grup. Pilih mode berdasarkan pembaca dan penulis yang sebenarnya, bukan sekadar menyalin angka yang terlihat paling ketat.
Stop bukan berarti menghapus semuanya
Perbedaan terpenting antara pengaturan ini adalah perilaku penghapusannya. Direktori terdalam yang dibuat oleh RuntimeDirectory= dihapus ketika unit berhenti, kecuali RuntimeDirectoryPreserve= mengubah kebijakan tersebut. Bahkan ketika preservation diaktifkan, /run pada sistem biasanya tetap volatil saat reboot.
Direktori yang dibuat melalui StateDirectory=, CacheDirectory=, LogsDirectory=, dan ConfigurationDirectory= tidak dihapus hanya karena layanan berhenti. Hal ini diperlukan untuk state, tetapi juga berarti “menonaktifkan layanan” bukan operasi penghapusan data.
Systemd menyediakan interface cleanup eksplisit untuk kelas resource yang dikelola ini. Sebagai contoh, cache milik unit yang sudah dihentikan dapat dihapus dengan:
sudo systemctl clean --what=cache example-worker.service
Dokumentasi systemctl(1) mengharuskan unit berhenti untuk operasi ini. Memilih state, logs, configuration, atau all membawa konsekuensi yang jauh lebih besar. Perintah bernama clean tidak otomatis aman; periksa deklarasi direktori unit dan backup terlebih dahulu.
Tempat tmpfiles.d masih diperlukan
Nama tmpfiles lebih sempit daripada kemampuan tool-nya. Menurut tmpfiles.d(5), aturannya dapat membuat file, direktori, pipe, dan link; menyesuaikan ownership dan mode; serta melakukan penghapusan berdasarkan waktu. Mekanisme ini umum dipakai untuk path volatil dan sementara, tetapi sebenarnya merupakan mekanisme filesystem deklaratif yang bersifat umum.
Manual yang sama memberikan garis pemisah yang berguna: utamakan RuntimeDirectory= dan pengaturan unit terkait untuk direktori milik satu layanan karena lifecycle-nya terikat langsung pada unit tersebut. Gunakan tmpfiles.d ketika objek memiliki lifecycle mandiri atau memerlukan aturan yang lebih kompleks.
Cleanup cache berdasarkan umur adalah salah satu contohnya. Misalkan unit mengelola /var/cache/example-worker, sedangkan file hasil impor di bawah satu child directory boleh dibersihkan setelah 14 hari:
# /etc/tmpfiles.d/example-worker.conf
d /var/cache/example-worker/imports 0750 example-worker example-worker 14d -
Akun tersebut harus sudah ada ketika aturan diterapkan. Baris d membuat direktori saat diperlukan, menyesuaikan mode dan ownership yang ditentukan, serta membuat isinya tunduk pada cleanup berdasarkan umur ketika nilai umur disertakan. “Lebih tua dari 14 hari” tidak hanya bergantung pada satu timestamp sederhana: manual menjelaskan bagaimana waktu akses, modifikasi, perubahan status, dan timestamp direktori ikut diperhitungkan. Jika pola akses atau pemindaian backup berpengaruh, baca semantics tersebut sebelum memilih umur.
Operasinya juga penting. systemd-tmpfiles(8) membedakan --create, --clean, dan --remove. Membuat path yang dideklarasikan tidak berarti cleanup berdasarkan umur sudah dijalankan, dan cleanup tidak sama dengan menerapkan aturan penghapusan eksplisit. Uji satu file konfigurasi secara sengaja alih-alih menjalankan perintah penghapusan luas terhadap setiap aturan yang terpasang.
Risiko migrasi yang layak diperhatikan
Ownership yang sudah ada dapat berubah secara rekursif
Untuk kelas direktori tulis yang dikelola selain konfigurasi, systemd memeriksa ownership terhadap User= dan Group=. Dokumentasi memperingatkan bahwa ketika ownership direktori terdalam yang sudah ada tidak cocok, systemd dapat mengubah ownership secara rekursif di bawahnya. Ini berguna untuk direktori khusus satu layanan dan berbahaya untuk tree yang dipakai bersama. Inventarisasi isi yang ada sebelum mengonversi path lama.
DynamicUser= mengubah model perlindungan
Dengan DynamicUser=, systemd melindungi direktori state, cache, dan log persisten dari user ID yang didaur ulang dengan memakai lokasi host-side yang terlindungi dan menyajikan path standar yang diharapkan kepada layanan. Interaksi ini menjadi alasan untuk memakai pengaturan direktori terkelola, tetapi bukan alasan untuk menambahkan DynamicUser=yes tanpa pertimbangan. Akses database, Unix socket, supplementary group, dan file di luar path terkelola mungkin semuanya perlu dirancang ulang.
Pembuatan direktori bukan kebijakan yang lengkap
Directive ini tidak menentukan apa yang masuk ke backup, merotasi file log aplikasi, menetapkan quota penyimpanan, mengenkripsi data, atau membuat semua path filesystem lain tidak dapat diakses. Directive tersebut dapat bekerja bersama opsi sandboxing seperti ProtectSystem=, tetapi membuat tiga direktori dengan ownership yang tepat tidak membuktikan bahwa proses hanya dapat menulis di sana.
Perbedaan versi juga penting. Pengaturan direktori inti sudah tersedia selama bertahun-tahun, sementara flag opsional dan fitur cleanup yang lebih baru memiliki persyaratan versi lebih tinggi. Periksa manual lokal dan systemctl --version sebelum memakai directive yang disalin dari dokumentasi online terkini.
Verifikasi deklarasi dan hasil runtime
Sebelum memasang unit, systemd-analyze verify dapat melaporkan directive yang tidak dikenal, dependency wajib yang hilang, dan masalah executable:
systemd-analyze verify ./example-worker.service
Hasil verifikasi yang bersih hanya mengonfirmasi kelas pemeriksaan yang dilakukan tool tersebut. Hasil itu tidak membuktikan bahwa worker memahami path, state-nya dapat dipulihkan, atau permission yang dipilih cocok untuk setiap proses yang bekerja sama.
Setelah instalasi dan start yang terkontrol, periksa property yang dinormalisasi systemd sekaligus filesystem-nya:
systemctl show example-worker.service \
--property=RuntimeDirectory,StateDirectory,CacheDirectory
stat -c '%A %U:%G %n' \
/run/example-worker \
/var/lib/example-worker \
/var/cache/example-worker
Setelah itu, uji lifecycle alih-alih berasumsi: hentikan layanan dan pastikan artifact runtime menghilang sementara state yang diperlukan tetap ada; jalankan lagi dan pastikan path runtime kembali. Jangan menjalankan eksperimen tersebut pada state produksi sebelum ekspektasi penghapusan dan pemulihan didokumentasikan.
Aturan sederhana untuk memilih mekanisme
Jika satu layanan membutuhkan direktori konvensional dan lifecycle-nya mengikuti unit tersebut, mulailah dengan directive direktori systemd yang sesuai. Cara ini menjaga path, ownership, mode, urutan startup, dan perilaku penghapusan biasa tetap dekat dengan proses yang bergantung padanya. Jika objek filesystem dimiliki beberapa layanan, harus tetap ada secara mandiri, memerlukan cleanup berdasarkan umur, atau membutuhkan jenis di luar kelas direktori tersebut, pertimbangkan aturan tmpfiles.d yang terfokus.
Tujuannya bukan mengurangi baris dengan mengorbankan hal lain. Tujuannya adalah deklarasi yang lebih jujur: socket bersifat sementara, state bertahan, cache dapat diganti, konfigurasi menjadi wilayah administrasi, dan cleanup hanya terjadi di bawah kebijakan yang menyebutkan apa yang boleh hilang.
Referensi
- systemd project / Debian Manpages -
systemd.exec(5), systemd 257.13 - systemd project / Debian Manpages -
tmpfiles.d(5), systemd 257.13 - systemd project / Debian Manpages -
systemd-tmpfiles(8), systemd 257.13 - Debian Project - Debian Policy Manual 4.7.4.1, section 9.1.4:
/runand/run/lock - systemd project / Debian Manpages -
systemd-analyze(1), systemd 257.13 - systemd project / Debian Manpages -
systemctl(1), systemd 257.13
