¿Cómo configurar la variable de entorno en el servicio systemd?

162

Tengo un sistema Arch Linux con systemd y he creado mi propio servicio. El servicio de configuración en se /etc/systemd/system/myservice.serviceve así:

[Unit]
Description=My Daemon

[Service]
ExecStart=/bin/myforegroundcmd

[Install]
WantedBy=multi-user.target

Ahora quiero tener una variable de entorno establecida para /bin/myforegroundcmd. ¿Cómo puedo hacer eso?

lfagundes
fuente

Respuestas:

197

Los tiempos cambian y las mejores prácticas también.

La mejor forma actual de hacerlo es ejecutar systemctl edit myservice, lo que creará un archivo de anulación para usted o le permitirá editar uno existente.

En instalaciones normales, esto creará un directorio /etc/systemd/system/myservice.service.d, y dentro de ese directorio creará un archivo cuyo nombre termina en .conf(típicamente override.conf), y en este archivo puede agregar o anular cualquier parte de la unidad enviada por la distribución.

Por ejemplo, en un archivo /etc/systemd/system/myservice.service.d/myenv.conf:

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

¡Tenga en cuenta también que si el directorio existe y está vacío, su servicio estará deshabilitado! Si no tiene la intención de poner algo en el directorio, asegúrese de que no exista.


Como referencia, la antigua forma era:

La forma recomendada de hacer esto es crear un archivo /etc/sysconfig/myserviceque contenga sus variables y luego cargarlas EnvironmentFile.

Para obtener detalles completos, consulte la documentación de Fedora sobre cómo escribir un script systemd .

Michael Hampton
fuente
44
Supongo que la sysconfigruta es específica de Fedora, pero la pregunta es sobre Arch Linux. La respuesta de paluh es más interesante, creo
Ludovic Kuty
1
/etc/sysconfiges específico de Fedora. AFAIR Arch Linux estaba presionando para tener los archivos de configuración en algún lugar específico del paquete en /etclugar de esa ubicación específica de Fedora. Me gusta /etc/myservice.conf, aunque el uso de archivos adicionales no parece la forma correcta aquí.
Michał Górny
55
No no no. / etc / sysconfig no se recomienda. Se desaconseja, junto con / etc / default / * de debian, porque no tienen sentido, y los nombres no tienen sentido y tienen sentido solo por razones de compatibilidad con versiones anteriores (todo / etc se trata de la configuración del sistema, no solo / etc / sysconfig y / etc / defaults son para anulaciones, no los valores predeterminados). Simplemente coloque las definiciones directamente en el archivo de la unidad, o si no es posible, en un archivo de entorno que tenga una ubicación específica del paquete (como sugiere el comentario de Michał).
zbyszek
1
@FrederickNord Es solo variable = pares de valores, como DJANGO_SETTINGS_MODULE=project.settingsuno por línea.
Michael Hampton
1
@MichaelHampton ¿Podría agregar un enlace de documentación para "la mejor manera actual"?
jb.
77

La respuesta depende de si se supone que la variable es constante (es decir, no debe ser modificada por el usuario que obtiene la unidad) o variable (se supone que debe ser establecida por el usuario).

Como es su unidad local, el límite es bastante borroso y de cualquier manera funcionaría. Sin embargo, si comenzaste a distribuirlo y terminaría en /usr/lib/systemd/system, esto sería importante.

Valor constante

Si el valor no necesita cambiar por instancia, la forma preferida sería colocarlo como Environment=, directamente en el archivo de la unidad:

[Unit]
Description=My Daemon

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

[Install]
WantedBy=multi-user.target

La ventaja de esto es que la variable se mantiene en un solo archivo con la unidad. Por lo tanto, el archivo de la unidad es más fácil de mover entre sistemas.

Valor variable

Sin embargo, la solución anterior no funciona bien cuando se supone que sysadmin cambia el valor de la variable de entorno localmente. Más específicamente, el nuevo valor debería establecerse cada vez que se actualiza el archivo de la unidad.

Para este caso, se utilizará un archivo adicional. Cómo: generalmente depende de la política de distribución.

Una solución particularmente interesante es usar el /etc/systemd/system/myservice.service.ddirectorio. A diferencia de otras soluciones, este directorio es compatible con el sistema en sí y, por lo tanto, no incluye rutas específicas de distribución.

En este caso, coloca un archivo como /etc/systemd/system/myservice.service.d/local.confese que agrega las partes faltantes del archivo de la unidad:

[Service]
Environment="FOO=bar baz"

Posteriormente, systemd combina los dos archivos al iniciar el servicio (recuerde systemctl daemon-reloaddespués de cambiar cualquiera de ellos). Y dado que systemd usa esta ruta directamente, no la usa EnvironmentFile=para esto.

Si se supone que el valor debe cambiarse solo en algunos de los sistemas afectados, puede combinar ambas soluciones, proporcionando un valor predeterminado directamente en la unidad y una anulación local en el otro archivo.

Michał Górny
fuente
systemctl daemon-reloades el comando para recargar systemd
Dmitry Buzolin
EnvironmentFile=es mejor cuando los valores son secretos como contraseñas. Vea mi respuesta para más detalles.
Don Kirkby
17

Las respuestas de Michael y Michał son útiles y responden a la pregunta original de cómo establecer una variable de entorno para un servicio systemd. Sin embargo, un uso común para las variables de entorno es configurar datos confidenciales como contraseñas en un lugar que no se comprometerá accidentalmente con el control de origen con el código de su aplicación.

Si es por eso que desea pasar una variable de entorno a su servicio, no la use Environment=en el archivo de configuración de la unidad. Úselo EnvironmentFile=y apúntelo a otro archivo de configuración que solo pueda leer la cuenta de servicio (y los usuarios con acceso de root).

Los detalles del archivo de configuración de la unidad son visibles para cualquier usuario con este comando:

systemctl show my_service

Puse un archivo de configuración en /etc/my_service/my_service.confy puse mis secretos allí:

MY_SECRET=correcthorsebatterystaple

Luego, en mi archivo de unidad de servicio, utilicé 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

Verifiqué que ps auxeno puedo ver esas variables de entorno, y otros usuarios no tienen acceso /proc/*/environ. Verifique su propio sistema, por supuesto.

Don Kirkby
fuente
8

Michael dio una solución limpia, pero quería obtener una variable env actualizada del script. Desafortunadamente, la ejecución de comandos bash no es posible en el archivo de unidad systemd. Afortunadamente, puedes activar bash dentro de ExecStart:

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

Tenga en cuenta que esta configuración no admite directamente líneas de comando de shell. Si se van a utilizar líneas de comando de shell, deben pasarse explícitamente a una implementación de shell de algún tipo.

Ejemplo en nuestro caso es entonces:

[Service]
ExecStart=/bin/bash -c "ENV=`script`; /bin/myforegroundcmd"
usuario1830432
fuente
77
Esto no funcionará por varias razones (a menos que sea un servicio de "una sola vez", que no tiene sentido). He conseguido lo siguiente para el trabajo: /bin/bash -a -c 'source /etc/sysconfig/whatever && exec whatever-program'. Esto -agarantiza que el entorno se exporte al subproceso (a menos que desee prefijar todas las variables whatevercon export)
Otheus
¿Por qué no funciona? Siempre debe activar un comando completo que incluye ejecutar el script, ¿no?
user1830432
Quizás ExecStart=/usr/bin/env ENV=script /bin/myforegroundcmdsea ​​una solución un poco mejor en este caso.
kstep
@Otheus: Gran respuesta, guardada por día cuando tuve que crear un archivo Tomcat 8 Unit.
Daniel
1
Hay una manera de ejecutar un comando bash "en" un archivo de servicio systemd. Ver este enlace: coreos.com/os/docs/latest/…
Mark Lakata