Bagaimana cara mengatur variabel lingkungan dalam layanan systemd?

162

Saya memiliki sistem Arch Linux dengan systemd dan saya telah membuat layanan saya sendiri. Layanan konfigurasi di /etc/systemd/system/myservice.serviceterlihat seperti ini:

[Unit]
Description=My Daemon

[Service]
ExecStart=/bin/myforegroundcmd

[Install]
WantedBy=multi-user.target

Sekarang saya ingin memiliki set variabel lingkungan untuk /bin/myforegroundcmd. Bagaimana aku melakukan itu?

lfagundes
sumber

Jawaban:

197

Waktu berubah dan begitu juga praktik terbaik.

Cara terbaik saat ini untuk melakukannya adalah menjalankan systemctl edit myservice, yang akan membuat file override untuk Anda atau membiarkan Anda mengedit yang sudah ada.

Dalam instalasi normal ini akan membuat direktori /etc/systemd/system/myservice.service.d, dan di dalam direktori itu membuat file yang namanya berakhir dengan .conf(biasanya, override.conf), dan dalam file ini Anda dapat menambah atau menimpa bagian mana pun dari unit yang dikirimkan oleh distribusi.

Misalnya, dalam file /etc/systemd/system/myservice.service.d/myenv.conf:

[Service]
Environment="SECRET=pGNqduRFkB4K9C2vijOmUDa2kPtUhArN"
Environment="ANOTHER_SECRET=JP8YLOc2bsNlrGuD6LVTq7L36obpjzxd"

Perhatikan juga bahwa jika direktori ada dan kosong, layanan Anda akan dinonaktifkan! Jika Anda tidak bermaksud memasukkan sesuatu ke dalam direktori, pastikan itu tidak ada.


Untuk referensi, cara lama adalah:

Cara yang disarankan untuk melakukan ini adalah membuat file /etc/sysconfig/myserviceyang berisi variabel Anda, dan kemudian memuatnya EnvironmentFile.

Untuk detail selengkapnya, lihat dokumentasi Fedora tentang cara menulis skrip systemd .

Michael Hampton
sumber
4
Saya kira sysconfigjalurnya khusus untuk Fedora tetapi pertanyaannya adalah tentang Arch Linux. Jawaban paluh lebih menarik menurut saya
Ludovic Kuty
1
/etc/sysconfigkhusus untuk Fedora. AFAIR Arch Linux mendorong untuk memiliki file konfigurasi di suatu tempat paket khusus daripada di /etclokasi khusus Fedora. Seperti /etc/myservice.conf, meskipun menggunakan file tambahan sepertinya tidak tepat di sini.
Michał Górny
5
Tidak tidak Tidak. / etc / sysconfig tidak direkomendasikan. Tidak disarankan, bersama dengan / etc / default / * dari debian, karena tidak ada gunanya, dan namanya tidak ada artinya dan hanya masuk akal untuk alasan kompatibilitas ke belakang (semua / etc adalah tentang konfigurasi sistem, bukan hanya / etc / sysconfig, dan / etc / defaults adalah untuk mengganti, bukan default). Cukup letakkan definisi langsung di file unit, atau jika tidak memungkinkan, dalam file lingkungan yang memiliki lokasi spesifik paket (seperti yang disarankan komentar Michał).
zbyszek
1
@FrederickNord Itu hanya variabel = pasangan nilai, seperti DJANGO_SETTINGS_MODULE=project.settings, satu per baris.
Michael Hampton
1
@MichaelHampton Bisakah Anda menambahkan tautan dokumentasi untuk "cara terbaik saat ini"?
jb.
77

Jawabannya tergantung pada apakah variabel seharusnya konstan (yaitu, tidak seharusnya dimodifikasi oleh pengguna mendapatkan unit) atau variabel (seharusnya ditetapkan oleh pengguna).

Karena ini adalah unit lokal Anda, batasnya cukup buram dan bagaimanapun cara kerjanya. Namun, jika Anda mulai mendistribusikannya dan akan berakhir /usr/lib/systemd/system, ini akan menjadi penting.

Nilai konstan

Jika nilainya tidak perlu diubah per instance, cara yang disukai adalah menempatkannya Environment=langsung di file unit:

[Unit]
Description=My Daemon

[Service]
Environment="FOO=bar baz"
ExecStart=/bin/myforegroundcmd

[Install]
WantedBy=multi-user.target

Keuntungannya adalah bahwa variabel disimpan dalam satu file dengan unit. Oleh karena itu, file unit lebih mudah untuk dipindahkan antar sistem.

Nilai variabel

Namun, solusi di atas tidak berfungsi dengan baik ketika sysadmin seharusnya mengubah nilai variabel lingkungan secara lokal. Lebih khusus lagi, nilai baru perlu ditetapkan setiap kali file unit diperbarui.

Untuk kasus ini, file tambahan harus digunakan. Bagaimana - biasanya tergantung pada kebijakan distribusi.

Salah satu solusi yang sangat menarik adalah dengan menggunakan /etc/systemd/system/myservice.service.ddirektori. Tidak seperti solusi lain, direktori ini didukung oleh systemd sendiri dan karenanya tidak ada jalur distribusi khusus.

Dalam hal ini, Anda menempatkan file seperti /etc/systemd/system/myservice.service.d/local.confitu menambahkan bagian-bagian yang hilang dari file unit:

[Service]
Environment="FOO=bar baz"

Setelah itu, systemd menggabungkan kedua file ketika memulai layanan (ingat untuk systemctl daemon-reloadsetelah mengubah salah satu dari mereka). Dan karena jalur ini digunakan langsung oleh systemd, Anda tidak menggunakannya EnvironmentFile=untuk ini.

Jika nilainya seharusnya diubah hanya pada beberapa sistem yang terpengaruh, Anda dapat menggabungkan kedua solusi, memberikan default langsung di unit dan override lokal di file lain.

Michał Górny
sumber
systemctl daemon-reloadadalah perintah untuk memuat ulang systemd
Dmitry Buzolin
EnvironmentFile=Lebih baik bila nilainya rahasia seperti kata sandi. Lihat jawaban saya untuk detailnya.
Don Kirkby
17

Jawaban oleh Michael dan Michał sangat membantu dan menjawab pertanyaan awal tentang cara mengatur variabel lingkungan untuk layanan systemd. Namun, satu penggunaan umum untuk variabel lingkungan adalah untuk mengkonfigurasi data sensitif seperti kata sandi di tempat yang tidak akan secara tidak sengaja mendapatkan komitmen untuk mengontrol sumber dengan kode aplikasi Anda.

Jika itu sebabnya Anda ingin meneruskan variabel lingkungan ke layanan Anda, jangan gunakan Environment=dalam file konfigurasi unit. Gunakan EnvironmentFile=dan arahkan ke file konfigurasi lain yang hanya dapat dibaca oleh akun layanan (dan pengguna dengan akses root).

Detail file konfigurasi unit dapat dilihat oleh setiap pengguna dengan perintah ini:

systemctl show my_service

Saya menaruh file konfigurasi /etc/my_service/my_service.confdan menaruh rahasia saya di sana:

MY_SECRET=correcthorsebatterystaple

Kemudian dalam file unit layanan saya, saya menggunakan EnvironmentFile=:

[Unit]
Description=my_service

[Service]
ExecStart=/usr/bin/python /path/to/my_service.py
EnvironmentFile=/etc/my_service/my_service.conf
User=myservice

[Install]
WantedBy=multi-user.target

Saya memeriksa bahwa ps auxetidak dapat melihat variabel lingkungan itu, dan pengguna lain tidak memiliki akses ke /proc/*/environ. Periksa sistem Anda sendiri, tentu saja.

Don Kirkby
sumber
8

Michael memberikan satu solusi bersih tetapi saya ingin mendapatkan variabel env yang diperbarui dari skrip. Sayangnya menjalankan perintah bash tidak dimungkinkan dalam file unit systemd. Untungnya Anda dapat memicu bash di dalam ExecStart:

http://www.dsm.fordham.edu/cgi-bin/man-cgi.pl?topic=systemd.service&sect=5

Perhatikan bahwa pengaturan ini tidak secara langsung mendukung baris perintah shell. Jika baris perintah shell akan digunakan, mereka harus diteruskan secara eksplisit ke implementasi shell.

Contoh dalam kasus kami adalah:

[Service]
ExecStart=/bin/bash -c "ENV=`script`; /bin/myforegroundcmd"
pengguna1830432
sumber
7
Ini tidak akan berfungsi karena berbagai alasan (kecuali itu layanan "sekali pakai", yang agak tidak berguna). Saya berhasil mendapatkan berikut untuk pekerjaan: /bin/bash -a -c 'source /etc/sysconfig/whatever && exec whatever-program'. The -amemastikan lingkungan diekspor ke sub-proses (kecuali jika Anda ingin awalan semua variabel whateverdengan export)
Otheus
mengapa itu tidak berhasil? Itu harus selalu memicu seluruh perintah yang mencakup mengeksekusi skrip, bukan?
user1830432
Mungkin ExecStart=/usr/bin/env ENV=script /bin/myforegroundcmdsolusi yang sedikit lebih baik dalam hal ini.
kstep
@Otheus: Jawaban yang bagus, disimpan pada hari ketika saya harus membuat file Tomcat 8 Unit.
Daniel
1
ADA cara untuk menjalankan perintah bash "dalam" file layanan systemd. Lihat tautan ini: coreos.com/os/docs/latest/…
Mark Lakata