Niveles de ejecución y targets de systemd

Introducción

Los niveles de ejecución (runlevels) definen el estado operativo del sistema: qué servicios están activos, si hay interfaz gráfica, si se admiten múltiples usuarios, etc. En los sistemas modernos con systemd los runlevels clásicos han sido reemplazados por targets, que ofrecen mayor flexibilidad.

Runlevels clásicos (SysV init)

El sistema SysV tradicional definía 7 niveles de ejecución (0-6):


Runlevel  Descripción
────────  ──────────────────────────────────────────
0         Apagado del sistema (halt)
1         Modo monousuario (single user, sin red, solo root)
2         Multiusuario sin red (Debian/Ubuntu lo usa como multiusuario normal)
3         Multiusuario con red, sin entorno gráfico (Red Hat / Fedora)
4         Sin uso estándar (reservado para uso personalizado)
5         Multiusuario con red y entorno gráfico
6         Reinicio del sistema (reboot)
          

La definición exacta de los runlevels 2, 3 y 4 variaba según la distribución. En Debian/Ubuntu los niveles 2-5 eran equivalentes (multiusuario con gráficos). En Red Hat/Fedora el nivel 3 era sin gráficos y el 5 con gráficos.

Targets de systemd

systemd reemplaza los runlevels por targets, que son unidades (.target) que agrupan servicios con dependencias entre sí. Son más expresivos y flexibles que los runlevels.

Equivalencias entre runlevels y targets:


Runlevel  Target systemd               Descripción
────────  ───────────────────────────  ────────────────────────────
0         poweroff.target              Apagar el sistema
1         rescue.target                Modo monousuario / rescate
2, 3, 4   multi-user.target            Multiusuario, sin gráficos
5         graphical.target             Multiusuario con entorno gráfico
6         reboot.target                Reiniciar el sistema
          

Los symlinks de compatibilidad existen en /lib/systemd/system/runlevel*.target.

Consultar y cambiar el target

Ver el target por defecto:


$ systemctl get-default
graphical.target
          

Cambiar el target por defecto (persiste tras reinicio):


# systemctl set-default multi-user.target    # sin entorno gráfico por defecto
# systemctl set-default graphical.target     # con entorno gráfico por defecto
          

Internamente crea o modifica el enlace simbólico /etc/systemd/system/default.target apuntando al target elegido.

Cambiar el target en la sesión actual (sin reiniciar):


# systemctl isolate multi-user.target    # cambia al target ahora
# systemctl isolate rescue.target        # entra en modo rescate ahora
# systemctl isolate graphical.target     # activa el entorno gráfico ahora
          

isolate activa el target indicado y detiene todos los servicios que no pertenecen a él. Solo funciona con targets que tienen AllowIsolate=yes en su definición.

Targets especiales


emergency.target    ← mínimo absoluto: solo shell root, sin servicios, sin montajes
rescue.target       ← monousuario: shell root + sistemas de ficheros montados
multi-user.target   ← multiusuario completo sin gráficos
graphical.target    ← multiusuario con entorno gráfico
network.target      ← la red está disponible
sysinit.target      ← inicialización básica del sistema completada
          

Entrar en modo de emergencia desde GRUB:

Editar la línea del kernel en GRUB y añadir:


systemd.unit=emergency.target
          

telinit — Compatibilidad con SysV

El comando clásico telinit N para cambiar de runlevel sigue funcionando en sistemas con systemd por compatibilidad. Internamente se traduce al systemctl isolate correspondiente.


# telinit 3     ← equivale a: systemctl isolate multi-user.target
# telinit 0     ← equivale a: systemctl poweroff
# telinit 6     ← equivale a: systemctl reboot
          

Cómo aplica systemd los targets

Cuando systemd activa un target, no ejecuta una lista fija de servicios — lee ficheros de unidad (.target, .service, .mount…) y resuelve un grafo de dependencias.

Fichero de unidad de un target:

Los targets se definen en /lib/systemd/system/*.target. Por ejemplo, multi-user.target:


$ cat /lib/systemd/system/multi-user.target
[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes
          
  • Requires= → dependencias obligatorias: si falla basic.target, falla este también.
  • After= → orden de arranque: este target arranca después de los listados.
  • Conflicts= → unidades incompatibles: activar este target detiene las listadas.
  • AllowIsolate=yes → permite usar systemctl isolate con este target.

Directorios .wants/ y .requires/:

Los servicios se asocian a un target mediante symlinks en los directorios /lib/systemd/system/<target>.wants/. Cuando se activa el target, systemd carga automáticamente todos los servicios enlazados en ese directorio.


$ ls /lib/systemd/system/multi-user.target.wants/
getty.target  remote-fs.target  systemd-ask-password-wall.path  ...

$ ls /etc/systemd/system/multi-user.target.wants/
cron.service  nginx.service  ssh.service  ...
          

Los symlinks en /lib/ son los que pone el paquete. Los de /etc/ son los que crea el administrador con systemctl enable.

Cómo un servicio declara su target:

En el fichero .service, la sección [Install] indica en qué target quiere participar el servicio:


# /lib/systemd/system/ssh.service (fragmento)
[Unit]
Description=OpenBSD Secure Shell server
After=network.target

[Service]
ExecStart=/usr/sbin/sshd -D

[Install]
WantedBy=multi-user.target
          

Cuando se ejecuta systemctl enable ssh, systemd crea el symlink /etc/systemd/system/multi-user.target.wants/ssh.service. Cuando se ejecuta systemctl disable ssh, lo elimina.

Inspeccionar las dependencias de un target:


$ systemctl list-dependencies multi-user.target
multi-user.target
● ├─cron.service
● ├─dbus.service
● ├─getty.target
● │ └─getty@tty1.service
● ├─nginx.service
● ├─ssh.service
● └─basic.target
●   ├─sysinit.target
●   └─...

$ systemctl list-dependencies --reverse ssh.service
ssh.service
└─multi-user.target
  └─graphical.target
          

La opción --reverse muestra qué targets dependen del servicio dado, útil para entender en qué punto del arranque se activa.

Ver el estado completo del arranque:


$ systemctl list-units --type=target          # targets activos
$ systemctl list-units --type=target --all    # todos (activos e inactivos)
$ systemctl status multi-user.target          # estado detallado del target
          

Secuencia de targets durante un arranque normal:


local-fs-pre.target
    │
local-fs.target          ← sistemas de ficheros locales montados
    │
swap.target              ← swap activada
    │
sysinit.target           ← hostname, reloj, udev, selinux...
    │
basic.target             ← sockets, timers, paths básicos
    │
multi-user.target        ← servicios de red y sistema
    │
graphical.target         ← gestor de pantalla (si procede)
          

Cada target garantiza que los anteriores están listos antes de activarse, gracias a las directivas After= y Requires= encadenadas.

Para el examen LPIC-1: conocer los targets equivalentes a runlevels 0, 1, 3, 5 y 6, los comandos get-default, set-default e isolate, y cómo systemctl enable/disable gestiona los symlinks en los directorios .wants/.

Modo rescate, chroot y recuperación de contraseñas

Índice de la sección

Índice del curso