¿Cómo crear un servicio systemd virtual para detener / iniciar varias instancias juntas?

12

Planeo alojar varias instancias de la misma aplicación web para clientes que usan systemd. I gustaría ser capaz de stopy startcada instancia de cliente usando systemd, así como el tratamiento de toda la colección de instancias de los clientes como un único servicio que puede ser detenido e iniciado juntos.

systemdparece proporcionar los bloques de construcción que necesito usar PartOf, y los archivos de la unidad de plantilla, pero cuando dejé el servicio principal, el servicio al cliente secundario no se detuvo. ¿Cómo puedo hacer que esto funcione con systemd? Esto es lo que tengo hasta ahora.

El archivo de unidad de los padres, app.service:

[Unit]
Description=App Web Service

[Service]
# Don't run as a deamon (because we've got nothing to do directly)
Type=oneshot
# Just print something, because ExecStart is required
ExecStart=/bin/echo "App Service exists only to collectively start and stop App instances"
# Keep running after Exit start finished, because we want the instances that depend on this to keep running
RemainAfterExit=yes
StandardOutput=journal

Un archivo de plantilla de unidad llamado [email protected], utilizado para crear instancias de clientes:

[Unit]
Description=%I Instance of App Web Service

[Service]
PartOf=app.service
ExecStart=/home/mark/bin/app-poc.sh %i
StandardOutput=journal

Mi app-poc.shscript (Prueba de concepto que solo se imprime en un archivo de registro en un bucle):

#!/bin/bash
# Just a temporary code to fake a full daemon.
while :
do
  echo "The App PoC loop for $@"
  sleep 2;
done

Como prueba de concepto, tengo los archivos de la unidad systemd ~/.config/systemd/user.

Luego inicio el padre y una instancia basada en la plantilla (después systemctl --user daemon-reload):

systemctl --user start app
systemctl --user start [email protected]

Al usarlo journalctl -f, puedo ver que ambos comenzaron y que la instancia del cliente continúa ejecutándose. Ahora espero que cerrar el padre detendrá al hijo (porque lo usé PartOf), pero no lo hace. Además, iniciar al padre tampoco inicia al hijo como se esperaba.

systemctl --user stop app

¡Gracias!

(Estoy usando Ubuntu 16.04 con systemd 229).

Mark Stosberg
fuente
1
"PartOf = Configura dependencias similares a Requiere =, pero se limita a detener y reiniciar las unidades". Si desea comenzar a trabajar, ¿no necesita usarlo Requires=en su lugar?
sourcejedi

Respuestas:

10

Necesitas mover la linea

PartOf=app.service

fuera de [Service]y dentro de la [Unit]sección, y añadir a la [Unit]de app.servicela lista de clientes para empezar, por ejemplo,

[email protected] [email protected]

o como dijo sourcejedi en los comentarios, Requires=lo mismo. Puede mantener los PartOfservicios podrían dejar de empezar con la mano que no están en la lista anterior, al igual que systemctl --user start [email protected].

meuh
fuente
Confirmé que tenías razón PartOf. Gracias. Voy a manejar los "Deseos" a través de un enlace simbólico, que se convierte en la única acción que debo tomar para activar un nuevo cliente con systemd. Para mi caso de prueba: `ln -s /home/mark/.config/systemd/user/[email protected] / home / mark / .config / systemd / user / app.service.wants / unity @ foo.service`
Mark Stosberg
13

Aprendí que para eso sirven las "Unidades de destino" del sistema. Al usar una Unidad objetivo, obtengo los beneficios que quiero sin necesidad de crear la [Service]sección falsa que tenía arriba. Un archivo de ejemplo de trabajo "Unidad de destino" tiene este aspecto:

# named like app.target
[Unit]
Description=App Web Service

# This collection of apps should be started at boot time.
[Install]
WantedBy=multi-user.target

Entonces cada instancia cliente debe incluir PartOfen la [Unit]sección (como fuera puntiagudo por @meuh), y también debe tener una [Install]sección para que enabley disablefuncionará en el servicio específico:

# In a file name like [email protected]
[Unit]
Description=%I Instance of App Web Service
PartOf=app.target

[Service]
ExecStart=/home/mark/bin/app-poc.sh %i
Restart=on-failure
StandardOutput=journal

# When the service runs globally, make it run as a particular user for added security
#User=myapp
#Group=myapp

# When systemctl enable is used, make this start when the App service starts
[Install]
WantedBy=app.target

Para mostrar la instancia del cliente y hacer que se inicie cuando se inicia el destino, se utiliza este comando de habilitación de una sola vez:

 systemctl enable app

Ahora, en este punto, puedo usar stopy startseguir app@customerpara una instancia específica, o puedo usar start appy stop appdetener todas las aplicaciones juntas.

Mark Stosberg
fuente
¿Qué tal el estado? No puedo encontrar una manera simple de obtener el estado de todos los servicios que la aplicación quería. Sé cómo puedo escribir eso, pero ...
Tommi Kyntola
1
Me refiero a obtener el estado de las aplicaciones en ese grupo objetivo sin enumerar todas las que forman parte de él, comodines o no, preferiblemente usando ese nombre de grupo y sin importarle de qué está hecho.
Tommi Kyntola
2
No es tan simple. ¿A qué paquete pertenecería ese script? Tendría que modificarse cada vez que se agrega un nuevo componente. Olvídese de eso y la implementación / mantenimiento se vuelve loco. Lo que obviamente deseo es simplemente agregar un nuevo paquete con la configuración partOf que indique su presencia en ese grupo y no luego modificar algún script persistente. Y luego detenerse y comenzar ese objetivo funciona como antes. Esto funciona, pero el estado parece estar fuera de ese alcance. Ni siquiera puedo encontrar una manera de obtener una lista de unidades que están presentes en tiempo de ejecución en un objetivo. Este caso de uso no está cubierto por systemd.
Tommi Kyntola
2
@TommiKyntola Aquí hay un bash one-liner que no necesita actualizar a medida que cambian las dependencias objetivo:systemctl status $(systemctl list-dependencies --plain otp.target)
Mark Stosberg
2
@TommiKyntola Estoy de acuerdo en que systemdpodría mejorar la usabilidad aquí. He abierto una solicitud de función para sugerir un estado mejorado para los objetivos.
Mark Stosberg