Variables dinámicas en archivos de unidad de servicio systemd

14

¿Hay alguna manera de asignar dinámicamente variables de entorno en un archivo de unidad de servicio systemd?

Tenemos una máquina que tiene 4 GPU, y queremos activar múltiples instancias de un determinado servicio por GPU. P.ej:

  • 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
  • hasta el hastío

Entonces, 1: 1, 2: 1, etc. son efectivamente el% i en el archivo de la unidad de servicio.

Para que el servicio se una a una GPU particular, el ejecutable del servicio verifica una determinada variable de entorno, por ejemplo:

USE_GPU=4

¿Hay alguna manera de tomar% i dentro del archivo de la unidad de servicio y ejecutarlo a través de alguna función (shell) para obtener el número de GPU, y luego puedo configurar la variable de entorno USE_GPU en consecuencia?

Lo que es más importante, no quiero la molestia de escribir varios /etc/systemd/system/gpu_service@x:y.service/local.confarchivos solo para poder activar más instancias.

Kal
fuente

Respuestas:

10

Si tiene cuidado, puede incorporar una pequeña secuencia de script bash como su comando exec en el archivo de servicio de instancia. P.ej

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

El $$en la cadena se convertirá en un solo $en el resultado pasado a bash, pero lo más importante será ${...}que interceptará systemd. (Las versiones anteriores de systemd no documentaban el uso de $$, por lo que no sé si era compatible entonces).

meuh
fuente
Terminé haciendo algo así. :)
Kal
1
¿Llamar a bash -cpara iniciar un programa desde el archivo de la unidad? Llamar exec? Esto es como usar una carretilla elevadora encima de una carretilla elevadora (tal vez con otra carretilla elevadora en la parte superior) porque la primera carretilla elevadora tiene problemas en realidad.
David Tonhofer
Desafortunadamente, no puede usar un ExecStartPre para escribir un archivo env, luego usarlo, aparentemente tiene que estar escrito de antemano, por lo que algo como esto funcionaría. O una secuencia de comandos de envoltura para hacer la división :) La otra opción extraña sería crear otro servicio para configurar el entorno. archivo, no estoy seguro de cómo funcionaría eso con las plantillas: stackoverflow.com/a/42841480/32453
rogerdpack
8

No construido en el camino. Debe hacer estas cosas antes de que comience su servicio. Una forma sería ponerlo en un archivo de entorno.

[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
fuente
4

Parece que realmente puede establecer variables de entorno dentro de un archivo de unidad systemd ...

Según las sugerencias de los comentaristas, aquí está la solución:

Usar variables de entorno en unidades systemd

Directiva medioambiental

systemd tiene una directiva de entorno que establece variables de entorno para procesos ejecutados. Se necesita una lista separada por espacios de asignaciones de variables. Esta opción se puede especificar más de una vez, en cuyo caso se establecerán todas las variables enumeradas. Si la misma variable se configura dos veces, la configuración posterior anulará la configuración anterior. Si la cadena vacía se asigna a esta opción, la lista de variables de entorno se restablece, todas las asignaciones anteriores no tienen efecto. Las directivas de entornos se utilizan en unidades incorporadas de sistema de Contenedor Linux, por ejemplo en etcd2 y franela.

Con el siguiente ejemplo, puede configurar su demonio etcd2 para usar el cifrado. Simplemente cree /etc/systemd/system/etcd2.service.d/30-certificates.confdrop-in para 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

Luego ejecute sudo systemctl daemon-reloady sudo systemctl restart etcd2.serviceaplique nuevos entornos al demonio etcd2.

Texto citado tomado de la siguiente URL: https://coreos.com/os/docs/latest/using-environment-variables-in-systemd-units.html

CyberK
fuente
2
Si bien esto puede responder teóricamente a la pregunta, sería preferible incluir aquí las partes esenciales de la respuesta y proporcionar el enlace para referencia.
Stephen Rauch
1
Si bien su comentario puede mejorar teóricamente mis futuras respuestas en stackexchange, sería preferible que haya incluido las partes esenciales de la respuesta en su comentario en lugar de solo comentar para señalar cuán incompetente puede ser alguien :)
CyberK
1
¡Bienvenido a Stack Exchange! Gracias por el comentario, me hiciste sonreír. También gracias por tomarse el tiempo para editar su respuesta. Estamos tratando de construir algo que tenga valor con el tiempo, y las respuestas de enlace solo no envejecen muy bien.
Stephen Rauch
Si lo agrega Environment=ABC=%i, establece ese entorno. variable "a la totalidad de% i". Supongo que podrías hacer un envoltorio para quitar las "cosas más allá de la cita" que no quieres, y se llama ejecutable real. Pero si está haciendo una envoltura, incluso podría pasar %icomo argumento, por ejemplo:ExecStart=my_wrapper %i
rogerdpack
0

Es feo y no es exactamente lo que pediste, ni permite el inicio automático, pero para los seguidores es posible hacer algo usando el entorno 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

Solo trato de enumerar todas las formas posibles :)

rogerdpack
fuente