¿Cómo puede un indicador de servicio systemd que está listo, para que otros servicios puedan esperar a que esté listo antes de comenzar?

8

Tengo un montón de servicios (por ejemplo C0, C1... C9) que solo deberían comenzar después de que un servicio Shaya completado su inicialización y esté completamente en ejecución y listo para los otros servicios. ¿Cómo arreglo eso con systemd?

En los servicios de pedidos con activación de ruta y destino en systemd , se supone que el servicio Stiene un mecanismo para escribir algún tipo de archivo indicador. Supongamos aquí, en cambio, que tengo control total sobre el programa que Sejecuta el servicio , y puedo agregar mecanismos systemd si es necesario.

JdeBP
fuente

Respuestas:

7

Uno no necesariamente necesita esto.

Si los Cservicios necesitan esperar Sa estar listos para poder abrir una conexión de socket, entonces uno no necesariamente necesita hacer esto. Por el contrario, uno puede aprovechar la apertura temprana del socket de escucha por parte de los administradores de servicios.

Varios sistemas, incluido el s6 de Laurent Bercot , mi conjunto de herramientas nosh y systemd, tienen formas en las que se puede abrir un socket de escucha desde el principio, lo primero en la configuración del servicio. Todos ellos implican algo más que el programa de servicio que abre los sockets de escucha, y el programa de servicio, cuando se invoca, recibe los sockets de escucha como descriptores de archivo ya abiertos.

Con systemd, específicamente, uno crea una unidad de socket que define el socket de escucha. systemd abre la unidad de socket y la configura para que el subsistema de red del kernel escuche las conexiones; y lo pasa al servicio real como un descriptor de archivo abierto cuando se trata de generar los procesos que manejan las conexiones al socket. (Puede hacer esto de dos maneras, como inetdpodría hacerlo , pero una discusión de los detalles de los servicios Accept=trueversus Accept=falseestá más allá del alcance de esta respuesta).

El punto importante es que uno no necesariamente necesita más pedidos que eso. El núcleo agrupa las conexiones del cliente en una cola hasta que se inicializa el programa de servicio y está listo para aceptarlas y hablar con los clientes.

Cuando uno lo hace, los protocolos de preparación son la cosa.

systemd tiene un conjunto de protocolos de preparación que comprende, servicio específico por servicio con la Type=configuración en la unidad de servicio. El protocolo de preparación particular de interés aquí es el notifyprotocolo de preparación. Con él, se le dice a systemd que espere mensajes del servicio, y cuando el servicio está listo, envía un mensaje que señala la preparación. systemd retrasa la activación de los otros servicios hasta que se marca la disponibilidad.

Hacer uso de esto implica dos cosas:

  • Modificando el código de Smodo que llame a algo como la función de Pierre-Yves Ritschard notify_systemd()o la función de Cameron T Norman notify_socket().
  • Configuración de la unidad de servicio para el servicio con Type=notifyy NotifyAccess=main.

La NotifyAccess=mainrestricción (que es la predeterminada) se debe a que systemd necesita saber para ignorar mensajes de programas traviesos (o simplemente defectuosos), porque cualquier proceso en el sistema puede enviar mensajes al socket de notificación de systemd.

Uno usa el código de Pierre-Yves Ritschard o Cameron T Norman de preferencia porque no excluye la posibilidad de tener este mecanismo en UbuntuBSD, Debian FreeBSD, FreeBSD real, TrueOS, OpenBSD, etc. que el código proporcionado por los autores systemd excluye.

Una trampa para evitar es el systemd-notifyprograma. Tiene varios problemas importantes, uno de los cuales es que los mensajes enviados con él pueden terminar siendo descartados sin procesar por systemd. El problema más importante en este caso es que no se ejecuta como el proceso "principal" del servicio, por lo que hay que abrir las notificaciones de preparación para el servicio Sa cada proceso en el sistema NotifyAccess=all.

Otra trampa para evitar es pensar que el forkingprotocolo es más simple. No lo es. Hacerlo correctamente implica no bifurcar y salir del padre hasta que (por una cosa) se estén ejecutando todos los subprocesos de trabajo del programa. Esto no coincide con la abrumadora mayoría de los demonios que se bifurcan.

Otras lecturas

JdeBP
fuente
1
Según man systemd.service(5), NotifyAccess=allaceptará mensajes de todos los miembros del grupo de control del servicio , lo que no implica ningún proceso malicioso en el sistema. Esto es lo suficientemente seguro para la mayoría de los casos de uso. Además, su preocupación por la portabilidad a otros sistemas operativos no es relevante para OP, ya que ya estamos aquí en el tema de Systemd.
Amir
1

En referencia a la página del manual para systemd.service(5), específicamente la sección sobre Tipo = , cada tipo de servicio tiene una forma diferente para que Systemd determine que está listo para ofrecer funcionalidad a otros servicios:

  • Si Type=simple, sus canales de comunicación deben instalarse antes de que se inicie el demonio (por ejemplo, los sockets configurados por systemd, mediante la activación de sockets).

  • Si Type=forking, se espera que el proceso principal salga cuando se complete el inicio y se configuren todos los canales de comunicación.

  • Si Type=dbus, se espera que el demonio adquiera un nombre en el bus D-Bus, en ese momento systemd procederá a iniciar las unidades de seguimiento.

  • Si Type=notify, se espera que el demonio envíe un mensaje de notificación a través de sd_notify(3)o una llamada equivalente cuando haya terminado de iniciarse. systemd procederá a iniciar las unidades de seguimiento después de que se haya enviado este mensaje de notificación.

Para la última opción (enviar un mensaje a través de sd_notify), puede usar la systemd-notifyutilidad y recordar concederle acceso NotifyAccess=all.

Dado que tiene control sobre el servicio S, puede elegir la mejor opción para su caso de uso, o simplemente la que sea más fácil de implementar.

Amir
fuente
1

Me gusta esto:

S.service

[Unit]
Description=My main Service

[Service]
Type=notify
ExecStart=/usr/bin/myBinary

Servicio C0.

[Unit]
Description=Dependent service number 0
PartOf=S.service

C1.service

[Unit]
Description=Dependent service number 1
PartOf=S.service

Servicio C9.

[Unit]
Description=Dependent service number 9
PartOf=S.service

Donde / usr / bin / myBinary realiza una llamada sd_notify READY = 1 cuando se completa la inicialización.

Según cómo desee que se comporte la dependencia, puede usar PartOf, Requiere o BindsTo u otros .

code_monk
fuente