GNU/Linux >> Tutoriales Linux >  >> Linux

Administrar el inicio usando systemd

Mientras configuraba un sistema Linux recientemente, quería saber cómo asegurarme de que las dependencias para los servicios y otras unidades estuvieran en funcionamiento antes de que comiencen esos servicios y unidades dependientes. Específicamente, necesitaba más conocimiento sobre cómo systemd administra la secuencia de inicio, especialmente para determinar el orden en que se inician los servicios en lo que es esencialmente un sistema paralelo.

Es posible que sepa que SystemV (el predecesor de systemd, como expliqué en el primer artículo de esta serie) ordena la secuencia de inicio nombrando los scripts de inicio con un prefijo SXX, donde XX es un número del 00 al 99. SystemV luego usa el orden de clasificación por nombre y ejecuta cada script de inicio en secuencia para el nivel de ejecución deseado.

Pero systemd usa archivos unitarios, que pueden ser creados o modificados por un administrador de sistemas, para definir subrutinas no solo para la inicialización sino también para el funcionamiento normal. En el tercer artículo de esta serie, expliqué cómo crear un archivo de unidad de montaje. En este quinto artículo, demuestro cómo crear un tipo diferente de archivo de unidad:un archivo de unidad de servicio que ejecuta un programa al inicio. También puede cambiar ciertos ajustes de configuración en el archivo de la unidad y usar el diario systemd para ver la ubicación de sus cambios en la secuencia de inicio.

Preparación

Asegúrate de haber eliminado rhgb y quiet del GRUB_CMDLINE_LINUX= línea en /etc/default/grub archivo, como mostré en el segundo artículo de esta serie. Esto le permite observar el flujo de mensajes de inicio de Linux, que necesitará para algunos de los experimentos de este artículo.

El programa

En este tutorial, creará un programa simple que le permitirá observar un mensaje durante el inicio en la consola y luego en el diario systemd.

Cree el programa shell /usr/local/bin/hello.sh y agregue el siguiente contenido. Desea asegurarse de que el resultado sea visible durante el inicio y que pueda encontrarlo fácilmente cuando busque en el diario systemd. Utilizará una versión del programa "Hola mundo" con algunas barras alrededor, para que se destaque. Asegúrese de que el archivo sea ejecutable y tenga propiedad de usuario y grupo por root con 700 permisos de seguridad:

#!/usr/bin/bash
# Simple program to use for testing startup configurations
# with systemd.
# By David Both
# Licensed under GPL V2
#
echo "###############################"
echo "######### Hello World! ########"
echo "###############################"

Ejecute este programa desde la línea de comandos para verificar que funciona correctamente:

[root@testvm1 ~]# hello.sh 
###############################
######### Hello World! ########
###############################
[root@testvm1 ~]#

Este programa puede crearse en cualquier lenguaje de secuencias de comandos o compilado. El hello.sh El programa también podría ubicarse en otros lugares según la estructura jerárquica del sistema de archivos de Linux (FHS). Lo coloco en el /usr/local/bin directorio para que pueda ejecutarse fácilmente desde la línea de comandos sin tener que anteponer una ruta cuando escribo el comando. Encuentro que muchos de los programas de shell que creo deben ejecutarse desde la línea de comando y por otras herramientas como systemd.

El archivo de la unidad de servicio

Cree el archivo de unidad de servicio /etc/systemd/system/hello.service con el siguiente contenido. Este archivo no necesita ser ejecutable, pero por seguridad, necesita propiedad de usuario y grupo por root y permisos 644 o 640:

# Simple service unit file to use for testing 
# startup configurations with systemd.
# By David Both
# Licensed under GPL V2
#

[Unit]
Description=My hello shell script

[Service]
Type=oneshot
ExecStart=/usr/local/bin/hello.sh

[Install]
WantedBy=multi-user.target

Verifique que el archivo de la unidad de servicio funcione según lo esperado al ver el estado del servicio. Cualquier error sintáctico aparecerá aquí:

[root@testvm1 ~]# systemctl status hello.service 
● hello.service - My hello shell script
     Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
     Active: inactive (dead)
[root@testvm1 ~]#

Puede ejecutar este tipo de servicio "oneshot" varias veces sin problemas. El tipo oneshot está diseñado para servicios en los que el programa iniciado por el archivo de la unidad de servicio es el proceso principal y debe completarse antes de que systemd inicie cualquier proceso dependiente.

Hay siete tipos de servicios, y puede encontrar una explicación de cada uno (junto con las otras partes de un archivo de unidad de servicio) en la página del comando man systemd.service(5). (También puede encontrar más información en los recursos al final de este artículo).

Tan curioso como soy, quería ver cómo podría ser un error. Entonces, eliminé la "o" del Type=oneshot línea, por lo que parecía Type=neshot y ejecutó el comando de nuevo:

[root@testvm1 ~]# systemctl status hello.service
● hello.service - My hello shell script
     Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
     Active: inactive (dead)

May 06 08:50:09 testvm1.both.org systemd[1]: /etc/systemd/system/hello.service:12: Failed to parse service type, ignoring: neshot
[root@testvm1 ~]#

Estos resultados me dijeron con precisión dónde estaba el error y facilitaron mucho la resolución del problema.

Más recursos de Linux

  • Hoja de trucos de los comandos de Linux
  • Hoja de trucos de comandos avanzados de Linux
  • Curso en línea gratuito:Descripción general técnica de RHEL
  • Hoja de trucos de red de Linux
  • Hoja de trucos de SELinux
  • Hoja de trucos de los comandos comunes de Linux
  • ¿Qué son los contenedores de Linux?
  • Nuestros últimos artículos sobre Linux

Solo tenga en cuenta que incluso después de restaurar el hello.service archivo a su forma original, el error persistirá. Aunque un reinicio borrará el error, no debería tener que hacerlo, así que busqué un método para borrar errores persistentes como este. He encontrado errores de servicio que requieren el comando systemctl daemon-reload para restablecer una condición de error, pero eso no funcionó en este caso. Los mensajes de error que se pueden corregir con este comando siempre parecen tener una declaración a tal efecto, por lo que sabe cómo ejecutarlo.

Sin embargo, se recomienda que ejecute systemctl daemon-reload después de cambiar un archivo de unidad o crear uno nuevo. Esto notifica a systemd que se han realizado los cambios y puede evitar ciertos tipos de problemas con la gestión de servicios o unidades alterados. Continúe y ejecute este comando.

Después de corregir el error ortográfico en el archivo de la unidad de servicio, un simple systemctl restart hello.service borrado el error. Experimente un poco introduciendo otros errores en hello.service archivo para ver qué tipo de resultados obtiene.

Iniciar el servicio

Ahora está listo para iniciar el nuevo servicio y verificar el estado para ver el resultado. Aunque probablemente hizo un reinicio en la sección anterior, puede iniciar o reiniciar un servicio único tantas veces como desee, ya que se ejecuta una vez y luego se cierra.

Continúe e inicie el servicio (como se muestra a continuación) y luego verifique el estado. Dependiendo de cuánto haya experimentado con los errores, sus resultados pueden diferir de los míos:

[root@testvm1 ~]# systemctl start hello.service 
[root@testvm1 ~]# systemctl status hello.service
● hello.service - My hello shell script
     Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
     Active: inactive (dead)

May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
[root@testvm1 ~]#

Observe en la salida del comando de estado que los mensajes systemd indican que hello.sh Se inició el script y se completó el servicio. También puede ver el resultado del script. Esta visualización se genera a partir de las entradas de diario de las invocaciones más recientes del servicio. Intente iniciar el servicio varias veces y luego ejecute el comando de estado nuevamente para ver a qué me refiero.

También debe mirar el contenido de la revista directamente; hay varias maneras de hacer esto. Una forma es especificar el identificador del tipo de registro, en este caso, el nombre del script de shell. Esto muestra las entradas del diario para reinicios anteriores, así como la sesión actual. Como puede ver, he estado investigando y probando este artículo desde hace algún tiempo:

[root@testvm1 ~]# journalctl -t hello.sh
<snip>
-- Reboot --
May 08 15:55:47 testvm1.both.org hello.sh[840]: ###############################
May 08 15:55:47 testvm1.both.org hello.sh[840]: ######### Hello World! ########
May 08 15:55:47 testvm1.both.org hello.sh[840]: ###############################
-- Reboot --
May 08 16:01:51 testvm1.both.org hello.sh[840]: ###############################
May 08 16:01:51 testvm1.both.org hello.sh[840]: ######### Hello World! ########
May 08 16:01:51 testvm1.both.org hello.sh[840]: ###############################
-- Reboot --
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
[root@testvm1 ~]#

Para ubicar los registros systemd para el hello.service unidad, puede buscar en systemd. Puede utilizar G+Intro a la página hasta el final de las entradas del diario y luego desplácese hacia atrás para ubicar las que le interesan. Use -b opción para mostrar solo las entradas del inicio más reciente:

[root@testvm1 ~]# journalctl -b -t systemd
<snip>
May 10 10:37:49 testvm1.both.org systemd[1]: Starting SYSV: Late init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Started SYSV: Late init script for live image..
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:37:50 testvm1.both.org systemd[1]: Starting D-Bus System Message Bus...
May 10 10:37:50 testvm1.both.org systemd[1]: Started D-Bus System Message Bus.

Copié algunas otras entradas del diario para darle una idea de lo que podría encontrar. Este comando arroja todas las líneas del diario relacionadas con systemd:109 183 líneas cuando escribí esto. Eso es una gran cantidad de datos para clasificar. Puede utilizar la función de búsqueda del buscapersonas, que suele ser less , o puede usar el grep incorporado rasgo. El -g (o --grep= ) opción utiliza expresiones regulares compatibles con Perl:

[root@testvm1 ~]# journalctl -b -t systemd -g "hello"
[root@testvm1 ~]# journalctl -b -t systemd -g "hello"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:01:01 EDT. --
May 10 10:37:49 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
[root@testvm1 ~]#

Podrías usar el estándar GNU grep comando, pero eso no mostraría los metadatos de registro en la primera línea.

Si no desea ver solo las entradas del diario correspondientes a su hello servicio, puede reducir un poco las cosas especificando un intervalo de tiempo. Por ejemplo, comenzaré con la hora de inicio de 10:54:00 en mi VM de prueba, que fue el comienzo del minuto de donde provienen las entradas anteriores. Tenga en cuenta que --since= la opción debe ir entre comillas y que esta opción también se puede expresar como -S "<time specification>" .

La fecha y la hora serán diferentes en su host, así que asegúrese de usar las marcas de tiempo que coincidan con las horas de sus diarios:

[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:00"
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=54 op=LOAD
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=55 op=LOAD
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd"'
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/"'
May 10 10:56:00 testvm1.both.org NetworkManager[840]: <error> [1589122560.0633] dhcp4 (enp0s3): error -113 dispatching events
May 10 10:56:00 testvm1.both.org NetworkManager[840]: <info>  [1589122560.0634] dhcp4 (enp0s3): state changed bound -> fail
<snip>

El since la especificación omite todas las entradas antes de ese tiempo, pero todavía hay muchas entradas después de ese tiempo que no necesita. También puede usar el until opción para recortar las entradas que vienen un poco después de la hora que te interesa. Quiero el minuto completo en que ocurrió el evento y nada más:

[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:35" --until="2020-05-10 10:55:00"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:04:59 EDT. --
May 10 10:54:35 testvm1.both.org systemd[1]: Reloading.
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=27 op=UNLOAD
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=26 op=UNLOAD
<snip>
ay 10 10:54:35 testvm1.both.org audit: BPF prog-id=55 op=LOAD
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd>
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/>
lines 1-46/46 (END)

Si hubiera mucha actividad en este período de tiempo, podría reducir aún más el flujo de datos resultante mediante una combinación de estas opciones:

[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:35" --until="2020-05-10 10:55:00" -t "hello.sh"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:10:41 EDT. --
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
[root@testvm1 ~]#

Sus resultados deben ser similares a los míos. Puede ver en esta serie de experimentos que el servicio se ejecutó correctamente.

Reiniciar—finalmente

Hasta ahora, no ha reiniciado el host donde instaló su servicio. Así que hazlo ahora porque, después de todo, este tutorial se trata de ejecutar un programa al inicio. Primero, debe permitir que el servicio se inicie durante la secuencia de inicio:

[root@testvm1 ~]# systemctl enable hello.service 
Created symlink /etc/systemd/system/multi-user.target.wants/hello.service → /etc/systemd/system/hello.service.
[root@testvm1 ~]#

Tenga en cuenta que el enlace se creó en /etc/systemd/system/multi-user.target.wants directorio. Esto se debe a que el archivo de la unidad de servicio especifica que el servicio es "deseable" por multi-user.target .

Reinicie y asegúrese de observar el flujo de datos durante la secuencia de inicio para ver el mensaje "Hola mundo". Espera… ¿no lo viste? Bueno, yo tampoco. Aunque pasó muy rápido, vi el mensaje de systemd de que estaba iniciando hello.service .

Mire el diario desde el último arranque del sistema. Puedes usar el less herramienta de búsqueda del buscapersonas para encontrar "Hola" o "hola". Eliminé muchas líneas de datos, pero dejé algunas de las entradas de diario circundantes, para que pueda tener una idea de cómo se ven localmente las entradas relacionadas con su servicio:

[root@testvm1 ~]# journalctl -b
<snip>
May 10 10:37:49 testvm1.both.org systemd[1]: Listening on SSSD Kerberos Cache Manager responder socket.
May 10 10:37:49 testvm1.both.org systemd[1]: Reached target Sockets.
May 10 10:37:49 testvm1.both.org systemd[1]: Reached target Basic System.
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Modem Manager...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Network Manager...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Avahi mDNS/DNS-SD Stack...
May 10 10:37:49 testvm1.both.org systemd[1]: Condition check resulted in Secure Boot DBX (blacklist) updater being skipped.
May 10 10:37:49 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
May 10 10:37:49 testvm1.both.org systemd[1]: Started irqbalance daemon.
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=irqbalance comm="systemd" exe="/usr/lib/sy>"'
May 10 10:37:49 testvm1.both.org systemd[1]: Starting LSB: Init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Hardware Monitoring Sensors...
<snip>
May 10 10:37:49 testvm1.both.org systemd[1]: Starting NTP client/server...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting SYSV: Late init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Started SYSV: Late init script for live image..
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=livesys-late comm="systemd" exe="/usr/lib/>"'
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd>"'
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/>
May 10 10:37:50 testvm1.both.org audit: BPF prog-id=28 op=LOAD
<snip>

Puede ver que systemd inició el hello.service unidad, que ejecutó el hello.sh shell script con la salida registrada en el diario. Si pudo detectarlo durante el arranque, también habría visto el mensaje systemd que indica que estaba iniciando el script y otro mensaje que indica que el servicio se realizó correctamente. Al observar el primer mensaje de systemd en el flujo de datos anterior, puede ver que systemd inició su servicio poco después de alcanzar el objetivo básico del sistema.

Pero también me gustaría ver el mensaje que se muestra al inicio. Hay una manera de hacer que eso suceda:agregue la siguiente línea al [Service] sección del hello.service archivo:

StandardOutput=journal+console

El hello.service el archivo ahora se ve así:

# Simple service unit file to use for testing 
# startup configurations with systemd.
# By David Both
# Licensed under GPL V2
#

[Unit]
Description=My hello shell script

[Service]
Type=oneshot
ExecStart=/usr/local/bin/hello.sh
StandardOutput=journal+console

[Install]
WantedBy=multi-user.target

Después de agregar esta línea, reinicie el sistema y observe el flujo de datos a medida que se desplaza hacia arriba en la pantalla durante el proceso de inicio. Deberías ver el mensaje en su pequeño cuadro. Una vez completada la secuencia de inicio, puede ver el diario del inicio más reciente y ubicar las entradas para su nuevo servicio.

Cambiando la secuencia

Ahora que su servicio está funcionando, puede ver dónde comienza en la secuencia de inicio y experimentar para cambiarlo. Es importante recordar que la intención de systemd es iniciar la mayor cantidad de servicios y otros tipos de unidades en paralelo dentro de cada uno de los objetivos principales:basic.target , multi-user.target y graphical.target . Debería haber visto las entradas del diario para el arranque más reciente, que debería parecerse a mi diario en el resultado anterior.

Tenga en cuenta que systemd inició su servicio de prueba poco después de llegar al sistema básico de destino. Esto es lo que especificó en el archivo de la unidad de servicio en el WantedBy línea, por lo que es correcto. Antes de cambiar nada, enumere el contenido de /etc/systemd/system/multi-user.target.wants directorio, y verá un enlace simbólico (suave) al archivo de la unidad de servicio. El [Install] La sección del archivo de la unidad de servicio especifica qué objetivo iniciará el servicio y ejecutar systemctl enable hello.service El comando crea el enlace en el directorio "objetivo deseado" apropiado:

hello.service -> /etc/systemd/system/hello.service

Ciertos servicios deben iniciarse durante el basic.target , y otros no necesitan iniciarse a menos que el sistema esté iniciando el graphical.target . El servicio de este experimento no se iniciará en el basic.target —suponga que no necesita que comience hasta que graphical.target . Así que cambia el WantedBy línea:

WantedBy=graphical.target

Asegúrese de deshabilitar el hello.service y vuelva a habilitarlo para eliminar el enlace anterior y agregar el nuevo en graphical.targets.wants directorio. He notado que si olvido deshabilitar el servicio antes de cambiar el objetivo que lo quiere, puedo ejecutar systemctl disable comando, y los enlaces se eliminarán de ambos directorios "objetivo quiere". Luego, solo necesito volver a habilitar el servicio y reiniciar.

Una preocupación con el inicio de servicios en graphical.target es que si el host arranca en multi-user.target , este servicio no se iniciará automáticamente. Eso puede ser lo que desea si el servicio requiere una interfaz de escritorio GUI, pero también puede no ser lo que desea.

Mire las entradas del diario para el graphical.target y el multi-user.target usando el -o short-monotonic opción que muestra segundos después del inicio del kernel con precisión de microsegundos:

[root@testvm1 ~]# journalctl -b -o short-monotonic

Algunos resultados para multi-user.target :

[   17.264730] testvm1.both.org systemd[1]: Starting My hello shell script...
[   17.265561] testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
<SNIP>
[   19.478468] testvm1.both.org systemd[1]: Starting LSB: Init script for live image....
[   19.507359] testvm1.both.org iptables.init[844]: iptables: Applying firewall rules: [  OK  ]
[   19.507835] testvm1.both.org hello.sh[843]: ###############################
[   19.507835] testvm1.both.org hello.sh[843]: ######### Hello World! ########
[   19.507835] testvm1.both.org hello.sh[843]: ###############################
<SNIP>
[   21.482481] testvm1.both.org systemd[1]: hello.service: Succeeded.
[   21.482550] testvm1.both.org smartd[856]: Opened configuration file /etc/smartmontools/smartd.conf
[   21.482605] testvm1.both.org systemd[1]: Finished My hello shell script.

Y algunos resultados para graphical.target :

[   19.436815] testvm1.both.org systemd[1]: Starting My hello shell script...
[   19.437070] testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
<SNIP>
[   19.612614] testvm1.both.org hello.sh[841]: ###############################
[   19.612614] testvm1.both.org hello.sh[841]: ######### Hello World! ########
[   19.612614] testvm1.both.org hello.sh[841]: ###############################
[   19.629455] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   19.629569] testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   19.629682] testvm1.both.org systemd[1]: hello.service: Succeeded.
[   19.629782] testvm1.both.org systemd[1]: Finished My hello shell script.

A pesar de tener el graphical.target "quiero" en el archivo de la unidad, el hello.service la unidad funciona aproximadamente 19,5 o 19,6 segundos después de iniciarse. Pero hello.service comienza en aproximadamente 17,24 segundos en el multi-user.target y 19,43 segundos en el objetivo gráfico.

¿Qué significa esto? Mire el /etc/systemd/system/default.target Enlace. El contenido de ese archivo muestra que systemd primero inicia el objetivo predeterminado, graphical.target , que luego extrae el multi-user.target :

[root@testvm1 system]# cat default.target
#  SPDX-License-Identifier: LGPL-2.1+
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes
[root@testvm1 system]#

Si inicia el servicio con graphical.target o el multi-user.target , el hello.service la unidad funciona aproximadamente a los 19,5 o 19,6 segundos desde el inicio. Según esto y los resultados del diario (especialmente los que usan la salida monotónica), sabe que ambos objetivos están comenzando en paralelo. Mire una cosa más de la salida del diario:

[   28.397330] testvm1.both.org systemd[1]: Reached target Multi-User System.
[   28.397431] testvm1.both.org systemd[1]: Reached target Graphical Interface.

Ambos objetivos terminan casi al mismo tiempo. Esto es coherente porque graphical.target extrae el multi-user.target y no puede terminar hasta el multi.user target es alcanzado, es decir, terminado. Pero  hola.servicio termina mucho antes que esto.

Lo que todo esto significa es que estos dos objetivos se inician prácticamente en paralelo. Si explora las entradas del diario, verá varios objetivos y servicios de cada uno de esos objetivos principales que comienzan en su mayoría en paralelo. Está claro que el multi-user.target no necesita completarse antes del graphical.target empieza. Por lo tanto, el simple uso de estos objetivos primarios para secuenciar el inicio no funciona muy bien, aunque puede ser útil para garantizar que las unidades se inicien solo cuando sean necesarias para el graphical.target. .

Antes de continuar, revierte el hello.service archivo de unidad a WantedBy=multi-user.target (si no lo está ya).

Asegúrese de que un servicio se inicie después de que la red esté en funcionamiento

Un problema común de la secuencia de inicio es garantizar que una unidad se inicie después de que la red esté en funcionamiento. El artículo de Freedesktop.org Ejecución de servicios después de que la red esté activa dice que no hay un consenso real sobre cuándo una red se considera "activa". Sin embargo, el artículo proporciona tres opciones, y la que satisface las necesidades de una red completamente operativa es network-online.target . Solo tenga en cuenta que network.target se usa durante el apagado en lugar del inicio, por lo que no le servirá de nada cuando intente secuenciar el inicio.

Antes de realizar cualquier otro cambio, asegúrese de examinar el diario y verificar que hello.service unidad comienza mucho antes que la red. Puede buscar network-online.target en el diario para comprobar.

Su servicio realmente no requiere el servicio de red, pero puede usarlo como avatar para uno que lo requiera.

Porque configurar WantedBy=graphical.target no garantiza que el servicio se iniciará después de que la red esté en funcionamiento, necesita otra forma de asegurarse de que así sea. Afortunadamente, hay una manera fácil de hacer esto. Add the following two lines to the [Unit] section of the hello.service archivo de unidad:

After=network-online.target                                                                             
Wants=network-online.target

Both of these entries are required to make this work. Reboot the host and look for the location of entries for your service in the journals:

[   26.083121] testvm1.both.org NetworkManager[842]: <info>  [1589227764.0293] device (enp0s3): Activation: successful, device activated.
[   26.083349] testvm1.both.org NetworkManager[842]: <info>  [1589227764.0301] manager: NetworkManager state is now CONNECTED_GLOBAL
[   26.085818] testvm1.both.org NetworkManager[842]: <info>  [1589227764.0331] manager: startup complete
[   26.089911] testvm1.both.org systemd[1]: Finished Network Manager Wait Online.
[   26.090254] testvm1.both.org systemd[1]: Reached target Network is Online.
[   26.090399] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=NetworkManager-wait-online comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? termina>"'
[   26.091991] testvm1.both.org systemd[1]: Starting My hello shell script...
[   26.095864] testvm1.both.org sssd[be[implicit_files]][1007]: Starting up
[   26.290539] testvm1.both.org systemd[1]: Condition check resulted in Login and scanning of iSCSI devices being skipped.
[   26.291075] testvm1.both.org systemd[1]: Reached target Remote File Systems (Pre).
[   26.291154] testvm1.both.org systemd[1]: Reached target Remote File Systems.
[   26.292671] testvm1.both.org systemd[1]: Starting Notify NFS peers of a restart...
[   26.294897] testvm1.both.org systemd[1]: iscsi.service: Unit cannot be reloaded because it is inactive.
[   26.304682] testvm1.both.org hello.sh[1010]: ###############################
[   26.304682] testvm1.both.org hello.sh[1010]: ######### Hello World! ########
[   26.304682] testvm1.both.org hello.sh[1010]: ###############################
[   26.306569] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   26.306669] testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   26.306772] testvm1.both.org systemd[1]: hello.service: Succeeded.
[   26.306862] testvm1.both.org systemd[1]: Finished My hello shell script.
[   26.584966] testvm1.both.org sm-notify[1011]: Version 2.4.3 starting

This confirms that the hello.service unit started after the network-online.target . This is exactly what you want. You may also have seen the "Hello World" message as it passed by during startup. Notice also that the timestamp is about six seconds later in the startup than it was before.

The best way to define the start sequence

This article explored Linux startup with systemd and unit files and journals in greater detail and discovered what happens when errors are introduced into the service file. As a sysadmin, I find that this type of experimentation helps me understand the behaviors of a program or service when it breaks, and breaking things intentionally is a good way to learn in a safe environment.

As the experiments in this article proved, just adding a service unit to either the multi-user.target or the graphical.target does not define its place in the start sequence. It merely determines whether a unit starts as part of a graphical environment or not. The reality is that the startup targets multi-user.target and graphical.target —and all of their Wants and Requires—start up pretty much in parallel. The best way to ensure that a unit starts in a specific order is to determine the unit it is dependent on and configure the new unit to "Want" and "After" the unit upon which it is dependent.

Resources

There is a great deal of information about systemd available on the internet, but much is terse, obtuse, or even misleading. In addition to the resources mentioned in this article, the following webpages offer more detailed and reliable information about systemd startup.

  • The Fedora Project has a good, practical guide to systemd. It has pretty much everything you need to know in order to configure, manage, and maintain a Fedora computer using systemd.
  • The Fedora Project also has a good cheat sheet that cross-references the old SystemV commands to comparable systemd ones.
  • For detailed technical information about systemd and the reasons for creating it, check out Freedesktop.org's description of systemd.
  • Linux.com's "More systemd fun" offers more advanced systemd information and tips.

There is also a series of deeply technical articles for Linux sysadmins by Lennart Poettering, the designer and primary developer of systemd. These articles were written between April 2010 and September 2011, but they are just as relevant now as they were then. Much of everything else good that has been written about systemd and its ecosystem is based on these papers.

  • Rethinking PID 1
  • systemd for Administrators, Part I
  • systemd for Administrators, Part II
  • systemd for Administrators, Part III
  • systemd for Administrators, Part IV
  • systemd for Administrators, Part V
  • systemd for Administrators, Part VI
  • systemd for Administrators, Part VII
  • systemd for Administrators, Part VIII
  • systemd for Administrators, Part IX
  • systemd for Administrators, Part X
  • systemd for Administrators, Part XI

Linux
  1. Cómo administrar los servicios de Systemd con Systemctl en Linux

  2. Reemplazo de rc.local en sistemas systemd Linux

  3. Uso de Logrotate en Linux para administrar archivos de registro (con ejemplos)

  4. ¿Cómo usar Systemd para reiniciar un servicio cuando está inactivo?

  5. Systemd:Uso de After y Requires

Comandos Systemctl para administrar el servicio Systemd

Uso de las funciones de systemd para proteger los servicios

Cómo usar el comando Systemctl para administrar los servicios de Systemd

Cómo configurar la ejecución automática de un script de Python usando Systemd

¿Cómo escribir un script de inicio para Systemd?

Forma correcta de usar Ubuntu systemctl para controlar Systemd