Como definir variável de ambiente no serviço systemd?

162

Eu tenho um sistema Arch Linux com systemd e criei meu próprio serviço. O serviço de configuração se /etc/systemd/system/myservice.serviceparece com isso:

[Unit]
Description=My Daemon

[Service]
ExecStart=/bin/myforegroundcmd

[Install]
WantedBy=multi-user.target

Agora eu quero ter uma variável de ambiente definida para o /bin/myforegroundcmd. Como faço isso?

lfagundes
fonte

Respostas:

197

Os tempos mudam e as melhores práticas também.

A melhor maneira atual de fazer isso é executar systemctl edit myservice, o que criará um arquivo de substituição para você ou permitirá que você edite um existente.

Em instalações normais, isso criará um diretório /etc/systemd/system/myservice.service.de, dentro desse diretório, criará um arquivo cujo nome termina em .conf(normalmente override.conf) e, nesse arquivo, você poderá adicionar ou substituir qualquer parte da unidade enviada pela distribuição.

Por exemplo, em um arquivo /etc/systemd/system/myservice.service.d/myenv.conf:

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

Observe também que, se o diretório existir e estiver vazio, seu serviço será desativado! Se você não pretende colocar algo no diretório, verifique se ele não existe.


Para referência, o caminho antigo era:

A maneira recomendada de fazer isso é criar um arquivo /etc/sysconfig/myserviceque contenha suas variáveis ​​e carregá-las EnvironmentFile.

Para detalhes completos, consulte a documentação do Fedora sobre como escrever um script systemd .

Michael Hampton
fonte
4
Eu acho que o sysconfigcaminho é específico para o Fedora, mas a pergunta é sobre o Arch Linux. A resposta por paluh é mais interessante eu acho
Ludovic Kuty
1
/etc/sysconfigé específico do Fedora. O AFAIR Arch Linux estava pressionando por ter os arquivos de configuração em algum lugar específico do pacote e não no /etclocal específico do Fedora. Como /etc/myservice.conf, embora o uso de arquivo extra não pareça o caminho certo aqui.
Michał Górny
5
Não não não. / etc / sysconfig não é recomendado. É desencorajado, junto com / etc / default / * do debian, porque eles não fazem sentido e os nomes não fazem sentido e fazem sentido apenas por razões de compatibilidade com versões anteriores (todo / etc é sobre configuração do sistema, não apenas / etc / sysconfig e / etc / defaults são para substituições, não para os padrões). Basta colocar as definições diretamente no arquivo da unidade, ou se não for possível, em um arquivo de ambiente que tenha um local específico do pacote (como sugere o comentário de Michał).
Zbyszek
1
@FrederickNord É apenas variável = pares de valores, como DJANGO_SETTINGS_MODULE=project.settingsum por linha.
Michael Hampton
1
@MichaelHampton Você poderia adicionar o link da documentação para "melhor maneira atual"?
jb.
77

A resposta depende se a variável deve ser constante (ou seja, não deve ser modificada pelo usuário obtendo a unidade) ou variável (que deve ser definida pelo usuário).

Como é a sua unidade local, o limite é bastante desfocado e de qualquer forma funcionaria. No entanto, se você começar a distribuí-lo e ele acabar /usr/lib/systemd/system, isso se tornará importante.

Valor constante

Se o valor não precisar mudar por instância, a maneira preferida seria colocá-lo como Environment=, diretamente no arquivo da unidade:

[Unit]
Description=My Daemon

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

[Install]
WantedBy=multi-user.target

A vantagem disso é que a variável é mantida em um único arquivo com a unidade. Portanto, é mais fácil mover o arquivo da unidade entre os sistemas.

Valor variável

No entanto, a solução acima não funciona bem quando o sysadmin deve alterar o valor da variável de ambiente localmente. Mais especificamente, o novo valor precisaria ser definido toda vez que o arquivo da unidade for atualizado.

Nesse caso, um arquivo extra deve ser usado. Como - geralmente depende da política de distribuição.

Uma solução particularmente interessante é usar o /etc/systemd/system/myservice.service.ddiretório. Diferente de outras soluções, esse diretório é suportado pelo próprio systemd e, portanto, não possui caminhos específicos da distribuição.

Nesse caso, você coloca um arquivo assim /etc/systemd/system/myservice.service.d/local.confque adiciona as partes ausentes do arquivo de unidade:

[Service]
Environment="FOO=bar baz"

Depois, o systemd mescla os dois arquivos ao iniciar o serviço (lembre-se de systemctl daemon-reloaddepois de alterar um deles). E como esse caminho é usado diretamente pelo systemd, você não o usa EnvironmentFile=.

Se o valor deve ser alterado apenas em alguns dos sistemas afetados, você pode combinar as duas soluções, fornecendo um padrão diretamente na unidade e uma substituição local no outro arquivo.

Michał Górny
fonte
systemctl daemon-reloadé o comando para recarregar systemd
Dmitry Buzolin
EnvironmentFile=é melhor quando os valores são segredos, como senhas. Veja minha resposta para detalhes.
Don Kirkby
17

As respostas de Michael e Michał são úteis e respondem à pergunta original de como definir uma variável de ambiente para um serviço systemd. No entanto, um uso comum para variáveis ​​de ambiente é configurar dados confidenciais como senhas em um local que não será acidentalmente comprometido com o controle de origem com o código do aplicativo.

Se é por isso que você deseja passar uma variável de ambiente para o seu serviço, não use Environment=no arquivo de configuração da unidade. Use EnvironmentFile=e aponte para outro arquivo de configuração que seja legível apenas pela conta de serviço (e usuários com acesso root).

Os detalhes do arquivo de configuração da unidade são visíveis para qualquer usuário com este comando:

systemctl show my_service

Coloquei um arquivo de configuração em /etc/my_service/my_service.confe coloquei meus segredos lá:

MY_SECRET=correcthorsebatterystaple

Em seguida, no meu arquivo de unidade de serviço, usei 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

Eu verifiquei que ps auxenão consigo ver essas variáveis ​​de ambiente e outros usuários não têm acesso /proc/*/environ. Verifique seu próprio sistema, é claro.

Don Kirkby
fonte
8

Michael deu uma solução limpa, mas eu queria obter a variável env atualizada do script. Infelizmente, executar comandos bash não é possível no arquivo de unidade systemd. Felizmente, você pode ativar o bash dentro do ExecStart:

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

Observe que essa configuração não suporta diretamente linhas de comando do shell. Se as linhas de comando do shell devem ser usadas, elas precisam ser passadas explicitamente para algum tipo de implementação do shell.

Exemplo no nosso caso é então:

[Service]
ExecStart=/bin/bash -c "ENV=`script`; /bin/myforegroundcmd"
user1830432
fonte
7
Isso não funcionará por vários motivos (a menos que seja um serviço "one-shot", o que é inútil). Eu consegui o seguinte para o trabalho: /bin/bash -a -c 'source /etc/sysconfig/whatever && exec whatever-program'. Os -agarante o ambiente é exportado para o sub-processo (a menos que você deseja prefixar todas as variáveis em whatevercom export)
Otheus
por que não vai funcionar? Ele sempre deve acionar um comando inteiro, que inclui a execução do script, não é?
usar o seguinte comando
Talvez ExecStart=/usr/bin/env ENV=script /bin/myforegroundcmdseja uma solução um pouco melhor neste caso.
KPasso
@Otheus: Ótima resposta, salva por dia quando eu tive que criar um arquivo do Tomcat 8 Unit.
Daniel
1
Existe uma maneira de executar um comando bash "em" um arquivo de serviço systemd. Veja este link: coreos.com/os/docs/latest/…
Mark Lakata