Dependency systemd untuk Home Server - Pisahkan Requirement dari Ordering
Sebuah service harus dimulai setelah service lain. Kedengarannya seperti satu aturan, tetapi systemd mendengar setidaknya dua pertanyaan terpisah: apakah memulai unit pertama juga harus memulai unit kedua, dan jika keduanya dimulai, mana yang harus lebih dahulu? Banyak unit file yang membingungkan berawal dari menjawab hanya salah satu pertanyaan itu.
Perbedaan ini penting pada home server kecil. Sebuah proses web mungkin memakai database lokal, job indexing mungkin memerlukan direktori yang sudah di-mount, atau tugas backup mungkin menghubungi mesin remote. Menambahkan After= dapat membuat grafik boot terlihat teratur sambil tetap tidak memasukkan unit yang dibutuhkan ke dalam transaksi. Menambahkan Requires= dapat menarik kedua unit tetapi membiarkannya mulai bersamaan. Tidak satu pun directive itu, jika berdiri sendiri, berarti “tunggu sampai aplikasi sehat.”
Artikel ini menyusun mental model yang sempit untuk directive umum Wants=, Requires=, After=, dan Before=. Artikel ini juga menjelaskan batas ketika model tersebut tidak lagi cukup.
Dua sumbu, bukan satu tangga dependency
Titik awal yang paling jelas adalah memisahkan requirement dari ordering. Manual systemd.unit menjelaskan keduanya sebagai hubungan yang independen dan orthogonal.
Wants=danRequires=menjawab apakah unit lain harus ditarik ke dalam transaksi start yang sama.After=danBefore=menjawab bagaimana job harus diurutkan ketika kedua unit berada dalam transaksi tersebut.
Bayangkan sebuah bengkel, bukan antrean. Directive requirement memasukkan pekerja lain ke daftar kehadiran. Directive ordering menentukan siapa yang harus menyelesaikan langkah startup sebelum orang lain mulai. Nama dalam jadwal tidak mengundang pekerja tersebut, dan undangan tidak menentukan tempatnya dalam jadwal.
Dari sini muncul empat kasus yang berguna:
Konfigurasi dalam app.service | Hal yang dinyatakan |
|---|---|
After=store.service | Jika kedua unit dimulai, urutkan app setelah store. Ini tidak menarik store ke dalam transaksi. |
Wants=store.service | Coba mulai store juga, tetapi tidak menentukan urutan. |
Wants=store.service ditambah After=store.service | Coba mulai keduanya dan urutkan app setelah store. |
Requires=store.service ditambah After=store.service | Mulai keduanya dengan requirement yang lebih kuat, dan jangan mulai app jika aktivasi store yang diurutkan gagal. |
Wants dan Requires berbeda dalam kebijakan kegagalan
Wants= adalah hubungan yang lebih lemah. Ketika unit yang mengonfigurasinya dimulai, systemd juga mencoba memulai unit yang diinginkan. Unit tersebut yang tidak dapat ditambahkan atau gagal dimulai tidak membatalkan keseluruhan transaksi. Dokumentasi upstream menyebutnya sebagai cara yang direkomendasikan untuk menghubungkan startup satu unit dengan unit lain dalam banyak kasus.
Requires= membuat pernyataan yang lebih kuat. Memulai unit yang mengonfigurasinya juga mengaktifkan unit yang diwajibkan. Ketika digabungkan dengan After=, kegagalan mengaktifkan unit yang diwajibkan mencegah unit dependent untuk dimulai. Menghentikan atau me-restart unit yang diwajibkan secara eksplisit juga meneruskan tindakan tersebut ke unit yang mewajibkannya.
Itu tetap bukan kontrak kesehatan permanen. Manual mencatat sejumlah kasus ketika unit yang diwajibkan kemudian dapat menjadi inactive tanpa otomatis menghentikan unit yang mewajibkannya. Service yang keluar dengan bersih dan kondisi unit yang gagal termasuk di antara nuansanya. Hubungan yang lebih kuat seperti BindsTo= memang ada, tetapi menggunakannya secara otomatis dapat membuat instalasi rapuh. Pertanyaan praktisnya bukan “directive mana yang paling kuat?” melainkan “apakah service ini harus ditahan ketika aktivasi tertentu itu gagal?”
Untuk metrics collector lokal yang opsional, Wants= dapat mempertahankan service yang berguna ketika metrics tidak tersedia. Untuk aplikasi yang bahkan tidak dapat melakukan inisialisasi tanpa local store, Requires= bersama After= mungkin menggambarkan kebijakan kegagalan yang dimaksud. Grafik unit seharusnya menyatakan kebutuhan operasional yang nyata, bukan kecemasan terhadap setiap kemungkinan kegagalan.
After dan Before hanya mengurutkan unit yang berpartisipasi
Jika web.service mendeklarasikan After=store.service, systemd menunda startup web service sampai job startup store selesai, asalkan kedua job tersebut ada. Pernyataan sebaliknya, Before=web.service di unit store, menggambarkan ordering edge yang sama dari sisi lain.
Ordering juga memengaruhi shutdown. Ketika dua unit dengan hubungan ordering dihentikan bersama, systemd menerapkan urutan terbalik. Unit yang dikonfigurasi After= unit lain dihentikan sebelum unit lain tersebut. Ini berguna ketika client perlu mendapat kesempatan menutup pekerjaannya sebelum provider menghilang.
Ada satu nuansa khusus target. Menurut systemd.target, sebuah target biasanya melengkapi hubungan Wants= dan Requires= yang dikonfigurasi di target itu sendiri dengan After=. Arah sebaliknya tidak otomatis: service yang menyatakan Wants=some.target tetap perlu menambahkan directive ordering eksplisit ketika memang membutuhkannya. Default ini tidak boleh digeneralisasi menjadi “requirement selalu menyiratkan ordering.”
Sepasang unit minimal
Perhatikan dua service yang sengaja dibuat artifisial. Keduanya menjalankan sleep, bukan aplikasi nyata, sehingga contoh ini menunjukkan semantik unit tanpa mengklaim sedang mengonfigurasi database atau web stack.
# dependency-demo-store.service
[Unit]
Description=Dependency demo store
[Service]
Type=exec
ExecStart=/usr/bin/sleep infinity
# dependency-demo-web.service
[Unit]
Description=Dependency demo web process
Requires=dependency-demo-store.service
After=dependency-demo-store.service
[Service]
Type=exec
ExecStart=/usr/bin/sleep infinity
[Install]
WantedBy=multi-user.target
Memulai dependency-demo-web.service menarik store karena Requires=. Baris After= secara terpisah mengurutkan proses web setelah aktivasi store. Menghapus Requires= hanya menyisakan hubungan ordering; memulai unit web saja belum tentu memulai store. Menghapus After= mempertahankan requirement tetapi membiarkan kedua job startup berjalan tanpa urutan tersebut.
Contoh ini dapat diperiksa sebelum instalasi:
systemd-analyze verify \
dependency-demo-store.service \
dependency-demo-web.service
systemd-analyze verify berguna untuk menemukan directive yang tidak dikenal, dependency yang hilang, dan masalah unit file lainnya. Tidak adanya output bukan bukti bahwa hubungan pada level aplikasi sudah benar; tool ini lebih mampu memvalidasi grafik dan sintaks daripada maksud operator.
Sudah diurutkan bukan berarti sudah siap
Batasan terpenting tersembunyi di dalam frasa “selesai dimulai.” Untuk sebuah service unit, momen itu bergantung pada Type=. Manual systemd.service menjelaskan bahwa default Type=simple dapat dianggap sudah dimulai segera setelah proses dibuat, bahkan sebelum executable berhasil dijalankan. Type=exec menunggu sampai eksekusi binary service berhasil, tetapi tetap tidak mengetahui apakah HTTP listener sudah menerima request atau database sudah menyelesaikan recovery.
Service dengan dukungan notifikasi systemd native dapat memakai Type=notify dan mengirim READY=1 ketika inisialisasinya selesai. Desain lain menghindari race melalui socket atau bus activation. Jika keduanya tidak tersedia, aplikasi dependent sering kali memerlukan retry dengan bounded backoff. Menambahkan sleep ke ExecStartPre= hanya menebak berapa lama readiness berlangsung; cara itu dapat membuang waktu pada boot cepat dan tetap gagal pada boot lambat.
Inilah sebabnya After=mariadb.service, misalnya, tidak seharusnya diparafrasakan sebagai “database akan menerima query aplikasi ini.” Directive itu menyatakan bahwa systemd mengamati batas aktivasi yang didefinisikan MariaDB sebelum memulai startup unit dependent. Apakah batas itu cukup harus diperiksa terhadap provider service dan perilaku client yang sebenarnya.
Network menunjukkan mengapa synchronization point adalah janji lokal
Network target membuat batas ini sangat terlihat. Panduan sinkronisasi network dari proyek systemd menyatakan bahwa network.target hanya menunjukkan bahwa stack network management sudah dimulai; target itu tidak mendefinisikan konektivitas yang sudah dikonfigurasi. network-online.target secara aktif menunggu definisi “up” milik network manager, tetapi definisi tersebut bergantung pada software dan konfigurasi lokal.
Bahkan tercapainya online target adalah kejadian historis, bukan jaminan bahwa DNS, host remote, atau Internet yang lebih luas tetap dapat dijangkau. Network client yang tetap penting setelah boot masih perlu menangani konektivitas yang hilang. Menarik network-online.target dapat tepat untuk client yang tidak dapat mulai tanpa konfigurasi network awal, tetapi menerapkannya ke setiap service yang berhubungan dengan network dapat menambah penundaan tanpa menciptakan reliability yang bertahan lama.
WantedBy berkaitan dengan instalasi, bukan readiness service
WantedBy=multi-user.target pada contoh muncul di bawah [Install]. systemd tidak membaca bagian itu sebagai instruksi runtime selama aktivasi normal. Sebaliknya, systemctl enable memakainya untuk membuat symlink di direktori .wants/. Hal ini membuat target menarik service pada boot atau aktivasi target berikutnya.
Karena itu, tiga operasi mudah tertukar:
systemctl enable example.servicemengubah link enablement; command ini tidak memulai service kecuali--nowjuga diminta.systemctl start example.servicemengaktifkannya sekarang; command ini belum tentu mengaktifkan enablement untuk boot berikutnya.WantedBy=menggambarkan link yang harus dibuat saat enablement; directive ini tidak menggantikanAfter=atau protokol application readiness.
Periksa grafik sebelum mengubahnya
Hubungan efektif sebuah unit dapat berasal dari file utamanya, drop-in, link enablement, default, dan implicit dependency. Membaca satu file saja karena itu mungkin tidak lengkap. Command berikut menampilkan bagian hasil yang berbeda:
systemctl cat dependency-demo-web.service
systemctl show dependency-demo-web.service \
-p Wants -p Requires -p After -p Before
systemctl list-dependencies dependency-demo-web.service
systemd-analyze critical-chain dependency-demo-web.service
systemctl cat menampilkan unit dan drop-in-nya. systemctl show menampilkan effective property yang dipilih. list-dependencies membantu memeriksa hal yang ditarik masuk, sedangkan critical-chain berfokus pada ordering yang kritis terhadap waktu. Tidak satu pun menggantikan pengujian kasus kegagalan: mulai unit dependent ketika provider berhenti, buat aktivasi provider gagal secara aman di lingkungan pengujian, lalu amati perilaku shutdown atau restart. Service production tidak seharusnya sengaja dirusak hanya demi mengikuti tutorial.
Pilih grafik paling kecil yang tetap jujur
Lebih banyak edge tidak otomatis membuat service lebih andal. Edge tambahan dapat membuat pekerjaan yang sebenarnya bisa berjalan bersamaan menjadi serial, membuat boot bergantung pada komponen opsional, atau menyebarkan satu kegagalan ke service lain yang masih berguna. Dokumentasi upstream systemd secara eksplisit menyarankan penggunaan dependency secara hemat dan mempertimbangkan socket- atau bus-based activation jika desain tersebut sesuai.
Review praktis dapat mengajukan empat pertanyaan:
- Apakah mengaktifkan unit ini juga harus mengaktifkan unit lain? Jika ya, pilih hubungan requirement.
- Apakah unit ini tetap harus dimulai ketika aktivasi unit lain gagal? Pertanyaan ini memisahkan banyak kasus
Wants=dari kasusRequires=. - Jika keduanya berpartisipasi, apakah urutan startup atau shutdown bermakna? Jika ya, tambahkan
After=atauBefore=secara eksplisit. - Kejadian yang dapat diamati apa yang berarti provider benar-benar siap, dan apakah
Type=service-nya mewakili kejadian tersebut? Jika tidak, ordering saja tidak cukup.
Pelajaran yang berguna bukanlah sepasang directive ajaib. Pelajarannya adalah kebiasaan menyebutkan janji secara tepat. Requirement, order, enablement, dan readiness adalah janji yang berbeda. Sebuah unit file lebih mudah dipahami ketika setiap baris hanya membuat janji yang memang dapat dipenuhi systemd.
References
- systemd.unit - Unit configuration, systemd project.
- systemd.service - Service unit configuration, systemd project.
- systemd.target - Target unit configuration, systemd project.
- Running Services After the Network Is Up, systemd project.
- systemd.unit(5) - Debian bookworm, Debian Manpages.
