Jak ustawić zmienną środowiskową w usłudze systemd?

162

Mam system Arch Linux z systemd i stworzyłem własną usługę. Usługa konfiguracji /etc/systemd/system/myservice.servicewygląda następująco:

[Unit]
Description=My Daemon

[Service]
ExecStart=/bin/myforegroundcmd

[Install]
WantedBy=multi-user.target

Teraz chcę mieć zmienną środowiskową dla /bin/myforegroundcmd. W jaki sposób mogę to zrobić?

lfagundes
źródło

Odpowiedzi:

197

Czasy się zmieniają, podobnie jak najlepsze praktyki.

Prąd najlepszym sposobem na to jest do uruchomienia systemctl edit myservice, który utworzy plik override dla ciebie lub pozwalają edytować istniejący.

W normalnych instalacjach utworzy to katalog /etc/systemd/system/myservice.service.d, a wewnątrz tego katalogu utworzy się plik, którego nazwa kończy się na .conf(zazwyczaj override.conf), aw tym pliku możesz dodać lub zastąpić dowolną część jednostki dostarczoną przez dystrybucję.

Na przykład w pliku /etc/systemd/system/myservice.service.d/myenv.conf:

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

Pamiętaj również, że jeśli katalog istnieje i jest pusty, usługa zostanie wyłączona! Jeśli nie zamierzasz umieszczać czegoś w katalogu, upewnij się, że nie istnieje.


Dla porównania, stary sposób to:

Zalecanym sposobem jest utworzenie pliku /etc/sysconfig/myservicezawierającego zmienne, a następnie załadowanie ich EnvironmentFile.

Aby uzyskać szczegółowe informacje, zobacz dokumentację Fedory dotyczącą pisania skryptu systemowego .

Michael Hampton
źródło
4
Myślę, że sysconfigścieżka jest specyficzna dla Fedory, ale pytanie dotyczy Arch Linux. Myślę, że odpowiedź paluh jest bardziej interesująca
Ludovic Kuty
1
/etc/sysconfigjest specyficzny dla Fedory. AFAIR Arch Linux nalegał, aby pliki konfiguracyjne były gdzieś specyficzne dla pakietu, a nie w /etclokalizacji specyficznej dla Fedory. Na przykład /etc/myservice.confużycie dodatkowego pliku nie wydaje się tutaj dobrym rozwiązaniem.
Michał Górny,
5
Nie nie nie. / etc / sysconfig nie jest zalecany. Jest odradzane, wraz z / etc / default / * z debiana, ponieważ są bezcelowe, a nazwy są bez znaczenia i mają sens tylko z powodów kompatybilności wstecznej (wszystkie / etc dotyczy konfiguracji systemu, a nie tylko / etc / sysconfig, a / etc / defaults służy do przesłonięcia, a nie do ustawień domyślnych). Po prostu umieść definicje bezpośrednio w pliku jednostki, a jeśli nie jest to możliwe, w pliku środowiska, który ma specyficzne położenie pakietu (jak sugeruje komentarz Michała).
zbyszek
1
@FrederickNord To tylko zmienna = pary wartości, na przykład DJANGO_SETTINGS_MODULE=project.settingsjedna na linię.
Michael Hampton
1
@MichaelHampton Czy możesz dodać link do dokumentacji „aktualny najlepszy sposób”?
jb.
77

Odpowiedź zależy od tego, czy zmienna ma być stała (to znaczy nie powinna być modyfikowana przez użytkownika otrzymującego jednostkę), czy zmienna (powinna być ustawiona przez użytkownika).

Ponieważ jest to twoja jednostka lokalna, granica jest dość rozmyta i tak czy inaczej zadziałałoby. Jeśli jednak zaczniesz go rozpowszechniać i skończy się /usr/lib/systemd/systemto, stanie się to ważne.

Stała wartość

Jeśli wartość nie musi być zmieniana dla każdej instancji, preferowanym sposobem byłoby umieszczenie jej jako Environment=, bezpośrednio w pliku jednostki:

[Unit]
Description=My Daemon

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

[Install]
WantedBy=multi-user.target

Zaletą tego jest to, że zmienna jest przechowywana w jednym pliku z jednostką. Dlatego plik jednostki jest łatwiejszy do przenoszenia między systemami.

Zmienna wartość

Jednak powyższe rozwiązanie nie działa dobrze, gdy sysadmin ma lokalnie zmieniać wartość zmiennej środowiskowej. Mówiąc dokładniej, nowa wartość musiałaby być ustawiona przy każdej aktualizacji pliku jednostki.

W tym przypadku należy użyć dodatkowego pliku. Jak - zazwyczaj zależy od zasad dystrybucji.

Jednym szczególnie interesującym rozwiązaniem jest użycie /etc/systemd/system/myservice.service.dkatalogu. W przeciwieństwie do innych rozwiązań ten katalog jest obsługiwany przez systemd i dlatego nie zawiera ścieżek specyficznych dla dystrybucji.

W takim przypadku /etc/systemd/system/myservice.service.d/local.confumieścisz taki plik, który doda brakujące części pliku jednostkowego:

[Service]
Environment="FOO=bar baz"

Następnie systemd łączy dwa pliki podczas uruchamiania usługi (pamiętaj, aby systemctl daemon-reloadpo zmianie jednego z nich). A ponieważ ta ścieżka jest używana bezpośrednio przez systemd, nie używasz EnvironmentFile=do tego.

Jeśli wartość ma zostać zmieniona tylko w niektórych systemach, których dotyczy problem, możesz połączyć oba rozwiązania, zapewniając domyślną wartość bezpośrednio w jednostce i lokalne zastąpienie w innym pliku.

Michał Górny
źródło
systemctl daemon-reloadto polecenie przeładowania systemd
Dmitry Buzolin
EnvironmentFile=jest lepszy, gdy wartości są tajne, takie jak hasła. Zobacz moją odpowiedź, aby poznać szczegóły.
Don Kirkby
17

Odpowiedzi Michaela i Michała są pomocne i odpowiadają na pierwotne pytanie, jak ustawić zmienną środowiskową dla usługi systemowej. Jednak jednym z powszechnych zastosowań zmiennych środowiskowych jest konfigurowanie poufnych danych, takich jak hasła, w miejscu, które przypadkowo nie zostanie przydzielone do kontroli źródła za pomocą kodu aplikacji.

Jeśli dlatego chcesz przekazać zmienną środowiskową do usługi, nie używaj jej Environment=w pliku konfiguracji urządzenia. Użyj EnvironmentFile=i wskaż inny plik konfiguracyjny, który jest możliwy do odczytania tylko przez konto usługi (i użytkowników z dostępem root).

Szczegóły pliku konfiguracyjnego jednostki są widoczne dla każdego użytkownika za pomocą tego polecenia:

systemctl show my_service

Wstawiam plik konfiguracyjny /etc/my_service/my_service.confi tam umieszczam swoje sekrety:

MY_SECRET=correcthorsebatterystaple

Następnie w pliku jednostki serwisowej użyłem 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

Sprawdziłem, że ps auxenie widzę tych zmiennych środowiskowych, a inni użytkownicy nie mają do nich dostępu /proc/*/environ. Oczywiście sprawdź w swoim systemie.

Don Kirkby
źródło
8

Michael dał jedno czyste rozwiązanie, ale chciałem zaktualizować zmienną env ze skryptu. Niestety wykonywanie poleceń bash w pliku jednostki systemowej nie jest możliwe. Na szczęście możesz uruchomić bash wewnątrz ExecStart:

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

Zauważ, że to ustawienie nie obsługuje bezpośrednio linii poleceń powłoki. Jeśli mają być używane wiersze poleceń powłoki, muszą zostać jawnie przekazane do pewnego rodzaju implementacji powłoki.

Przykład w naszym przypadku to:

[Service]
ExecStart=/bin/bash -c "ENV=`script`; /bin/myforegroundcmd"
użytkownik1830432
źródło
7
Nie zadziała to z wielu powodów (chyba że jest to usługa „jednorazowa”, co jest raczej bezcelowe). Udało mi się dostać do następujących prac: /bin/bash -a -c 'source /etc/sysconfig/whatever && exec whatever-program'. W -azapewnia środowisko jest eksportowana do sub-procesu (chyba, że chcesz poprzedzić wszystkie zmienne whateverz export)
Otheus
dlaczego to nie zadziała? Powinien zawsze wyzwalać całe polecenie, które obejmuje wykonanie skryptu, prawda?
user1830432
Może ExecStart=/usr/bin/env ENV=script /bin/myforegroundcmdw tym przypadku jest trochę lepsze rozwiązanie.
kstep
@Otheus: Świetna odpowiedź, zapisana w dzień, gdy musiałem utworzyć plik Tomcat 8 Unit.
Daniel
1
Istnieje sposób na wykonanie polecenia bash „w” pliku usługi systemowej. Zobacz ten link: coreos.com/os/docs/latest/…
Mark Lakata