Variáveis ​​dinâmicas nos arquivos da unidade de serviço systemd

14

Existe uma maneira de atribuir dinamicamente variáveis ​​de ambiente em um arquivo de unidade de serviço systemd?

Temos uma máquina com 4 GPUs e queremos ativar várias instâncias de um determinado serviço por GPU. Por exemplo:

  • gpu_service @ 1: 1.service
  • gpu_service @ 2: 1.service
  • gpu_service @ 3: 1.service
  • gpu_service @ 4: 1.service
  • gpu_service @ 1: 2.service
  • gpu_service @ 2: 2.service
  • gpu_service @ 3: 2.service
  • gpu_service @ 4: 2.service
  • ad nauseam

Portanto, 1: 1, 2: 1 etc. são efetivamente o% i no arquivo da unidade de serviço.

Para que o serviço se ligue a uma GPU específica, o executável do serviço verifica uma determinada variável de ambiente, por exemplo:

USE_GPU=4

Existe uma maneira de eu levar% i dentro do arquivo da unidade de serviço e executá-lo através de alguma função (shell) para derivar o número da GPU e, em seguida, posso definir a variável de ambiente USE_GPU de acordo?

Mais importante, eu não quero o trabalho de escrever vários /etc/systemd/system/gpu_service@x:y.service/local.confarquivos, apenas para poder gerar mais instâncias.

Kal
fonte

Respostas:

10

Se você for cuidadoso, poderá incorporar uma pequena sequência de scripts bash como seu comando exec no arquivo de serviço da instância. Por exemplo

ExecStart=/bin/bash -c 'v=%i; USE_GPU=$${v%:*} exec /bin/mycommand'

O $$na string se tornará único $no resultado passado para o bash, mas o mais importante será que não será ${...}interpolado pelo systemd. (As versões anteriores do systemd não documentavam o uso de $$, portanto não sei se era suportado).

meuh
fonte
Acabei fazendo algo assim. :)
Kal
1
Chamar a bash -cpara iniciar um programa a partir do arquivo da unidade? Ligar exec? É como usar uma empilhadeira em cima de uma empilhadeira (talvez com outra empilhadeira em cima), porque a primeira empilhadeira tem problemas para realmente fazer a empilhadeira.
David Tonhofer
Infelizmente, você não pode usar um ExecStartPre para escrever um arquivo env e, em seguida, usá-lo, aparentemente ele deve ser escrito antes, para que algo assim funcione. Ou um script de wrapper para fazer a divisão :) A outra opção bizarra seria criar outro serviço para configurar o ambiente. arquivo, não tenho certeza como isso funciona com modelos tho: stackoverflow.com/a/42841480/32453
rogerdpack
8

Não construído de maneira. Você precisa fazer essas coisas antes do início do serviço. Uma maneira seria colocá-lo em um arquivo de ambiente.

[Service]
# Note you need to escape percentage sign
ExecStartPre=/bin/sh -c "my_awesome_parser %%i > /run/gpu_service_%i"
EnvironmentFile=/run/gpu_service_%i
ExecStart=...
Umut
fonte
4

Parece que você pode realmente definir variáveis ​​de ambiente dentro de um arquivo de unidade systemd ...

Por sugestões dos comentaristas, aqui está a solução:

Usando variáveis ​​de ambiente em unidades systemd

Diretiva ambiente

systemd possui uma diretiva Ambiente que define variáveis ​​de ambiente para processos executados. É necessária uma lista separada por espaços de atribuições de variáveis. Esta opção pode ser especificada mais de uma vez, caso em que todas as variáveis ​​listadas serão definidas. Se a mesma variável for definida duas vezes, a configuração posterior substituirá a configuração anterior. Se a sequência vazia for atribuída a esta opção, a lista de variáveis ​​de ambiente será redefinida, todas as atribuições anteriores não terão efeito. As diretivas de ambiente são usadas em unidades integradas do sistema Container Linux, por exemplo, em etcd2 e flanela.

Com o exemplo abaixo, você pode configurar seu daemon etcd2 para usar criptografia. Basta criar um /etc/systemd/system/etcd2.service.d/30-certificates.confdrop-in para o etcd2.service:

[Service]
# Client Env Vars
Environment=ETCD_CA_FILE=/path/to/CA.pem
Environment=ETCD_CERT_FILE=/path/to/server.crt
Environment=ETCD_KEY_FILE=/path/to/server.key
# Peer Env Vars
Environment=ETCD_PEER_CA_FILE=/path/to/CA.pem
Environment=ETCD_PEER_CERT_FILE=/path/to/peers.crt
Environment=ETCD_PEER_KEY_FILE=/path/to/peers.key

Em seguida, execute sudo systemctl daemon-reloade sudo systemctl restart etcd2.serviceaplique novos ambientes ao etcd2 daemon.

Texto citado extraído do seguinte URL: https://coreos.com/os/docs/latest/using-environment-variables-in-systemd-units.html

CyberK
fonte
2
Embora isso possa teoricamente responder à pergunta, seria preferível incluir aqui as partes essenciais da resposta e fornecer o link para referência.
Stephen Rauch
1
Embora seu comentário possa teoricamente melhorar minhas respostas futuras no stackexchange, seria preferível que você incluísse as partes essenciais da resposta em seu comentário, em vez de apenas comentar para apontar como alguém pode ser incompetente :)
CyberK
1
Bem-vindo ao Stack Exchange! Obrigado pelo comentário, você me fez sorrir. Também obrigado por reservar um tempo para editar sua resposta. Estamos tentando criar algo que terá valor ao longo do tempo, e as respostas apenas do link simplesmente não envelhecem muito bem.
Stephen Rauch
Se você adicionar Environment=ABC=%i, defina esse env. variável "na totalidade de% i". Eu acho que você poderia criar um invólucro para retirar as "coisas além da citação" que você não deseja e chamar o executável real. Mas se você está fazendo um invólucro, você poderia até mesmo passar %icomo um argumento para que ex:ExecStart=my_wrapper %i
rogerdpack
0

É feio e não é exatamente o que você solicitou, nem permite a inicialização automática, mas para os seguidores é possível fazer algo usando o ambiente systemctl :

$ sudo systemctl set-environment USE_GPU=4 # add it to the env. variables for future services
$ sudo systemctl start gpu_service@4:2.service

Apenas tentando listar todas as maneiras possíveis :)

rogerdpack
fonte