Gravando um arquivo de unidade systemd com um caminho executável definido pelo ambiente

17

Estou escrevendo um arquivo de unidade systemd para um aplicativo Java e gostaria de controlar a versão do Java usada para iniciá-lo. Meu arquivo de serviço (simplificado) é

[Service]
Type=simple
EnvironmentFile=%h/Documents/apps/app/app-%i/app.cfg
ExecStart=${JAVA_HOME}/bin/java ${JAVA_OPTS} -jar %h/Documents/apps/app/app-%i/myapp.jar
SuccessExitStatus=143

Ao tentar iniciá-lo, recebo um erro de volta

Apr 28 12:43:37 rombert systemd[1613]: [/home/robert/.config/systemd/user/[email protected]:7] Executable path is not absolute, ignoring: ${JAVA_HOME}/bin/java ${JAVA_OPT
Apr 28 12:43:37 rombert systemd[1613]: [email protected] lacks both ExecStart= and ExecStop= setting. Refusing.

Eu sei que JAVA_HOMEestá definido corretamente; se eu mudar a ExecStartlinha para começar /usr/bin/javae depois adicionar algo como -DsomeOption=${JAVA_HOME}eu posso ver bem.

A solução óbvia é criar um script de wrapper, mas acho que isso acaba com o ponto de usar um arquivo de serviço.

Como posso definir JAVA_HOME para meu aplicativo Java usando um arquivo de unidade?

Robert Munteanu
fonte
Por que o script wrapper anula exatamente o propósito de usar um arquivo de serviço? Você ainda obtém o sequenciamento e o rastreamento de dependência do sistemad, o monitoramento etc. Basicamente, o systemd troca a programação de forma livre que tínhamos com o SysVinit em favor da lógica DTRT incorporada . Quando "a coisa certa" é algo que o systemd não faz, você precisa colocá-lo fora do systemd, como em um script de shell.
Warren Young
@ WarrenYoung - porque de repente começo a gerenciar scripts de shell novamente. No meu caso, não gerenciar um script de shell é mais útil que os outros bits.
Robert Munteanu
Eu realmente não vejo o problema. Você passa seus dias se preocupando com todos os executáveis ​​que você precisa gerenciar também? :)
Warren Young
3
From systemd.service (5): "Observe que o primeiro argumento (ou seja, o programa a ser executado) pode não ser uma variável." Isso explica por que $ {JAVA_HOME} não é expandido no início do caminho dos aplicativos, mas é quando usado em algum momento posterior.
Wieland
@ WarrenYoung - prefiro um único invólucro do que o binário. Eu entendo que isso não é um problema para todos, mas é para mim :-)
Robert Munteanu

Respostas:

12

Na seção "Linhas de comando" no systemd.service (5):

Observe que o primeiro argumento (ou seja, o programa a ser executado) pode não ser uma variável.

Eu sugeriria o uso do especificador de instância %i(você pode ler mais sobre isso em systemd.unit (5)), mas (agora estamos de volta em systemd.service (5)):

o primeiro argumento da linha de comando (ou seja, o programa a ser executado) pode não incluir especificadores.

Eu acho que a melhor opção neste momento é realmente criar um script de shell que envolva a execução do binário java, como sugerido por Warren Young, ou você pode executar um shell diretamente como no exemplo das linhas de comando do shell na seção "Command Lines" systemd.service (5), que possui o seguinte exemplo:

ExecStart=/bin/sh -c 'dmesg | tac'

para que você possa fazer (não testado):

ExecStart=/bin/sh -c '${JAVA_HOME}....'
Wieland
fonte
2

Outra opção semelhante é usar /usr/bin/env:

ExecStart=/usr/bin/env "${JAVA_HOME}/bin/java" -jar ...

Dessa forma, você pode omitir 'aspas em todo o comando, o que é útil se você precisar aninhar itens entre aspas.

PS. Como uma observação lateral, é importante colocar nomes de variáveis ​​entre {chaves }nos arquivos Systemd, caso contrário eles não serão reconhecidos corretamente.

MarSoft
fonte