Ejecutando systemd en un contenedor

Llevamos mucho tiempo siguiendo el tema del uso de systemd en contenedores. En 2014, nuestro ingeniero de seguridad Daniel Walsh escribió un artículo Ejecutando systemd dentro de un contenedor Docker, y un par de años después, otro, que se llamó Ejecutando systemd en un contenedor sin privilegios, en el que afirmó que la situación no había mejorado mucho. En particular, escribió que “desafortunadamente, incluso dos años después, si buscas en Google “sistema Docker”, lo primero que aparece es el mismo artículo de siempre. Así que es hora de cambiar algo”. Además, ya hemos hablado de conflicto entre Docker y los desarrolladores de systemd.

Ejecutando systemd en un contenedor

En este artículo mostraremos qué ha cambiado con el tiempo y cómo Podman puede ayudarnos en este asunto.

Hay muchas razones para ejecutar systemd dentro de un contenedor, como por ejemplo:

  1. Contenedores multiservicio – mucha gente quiere sacar sus aplicaciones multiservicio de las máquinas virtuales y ejecutarlas en contenedores. Por supuesto, sería mejor dividir dichas aplicaciones en microservicios, pero no todos saben cómo hacerlo todavía o simplemente no tienen tiempo. Por lo tanto, ejecutar aplicaciones como servicios iniciados por systemd desde archivos unitarios tiene mucho sentido.
  2. Archivos de unidad Systemd – La mayoría de las aplicaciones que se ejecutan dentro de contenedores se crean a partir de código que anteriormente se ejecutaba en máquinas virtuales o físicas. Estas aplicaciones tienen un archivo unitario que fue escrito para estas aplicaciones y comprende cómo deben iniciarse. Por lo tanto, es mejor iniciar servicios utilizando métodos compatibles, en lugar de piratear su propio servicio de inicio.
  3. Systemd es un administrador de procesos. Gestiona servicios (cierra, reinicia servicios o elimina procesos zombies) mejor que cualquier otra herramienta.

Dicho esto, existen muchas razones para no ejecutar systemd en contenedores. El principal es que systemd/journald controla la salida de contenedores y herramientas como Kubernetes o OpenShift Se espera que los contenedores escriban registros directamente en stdout y stderr. Por lo tanto, si va a administrar contenedores a través de herramientas de orquestación como las mencionadas anteriormente, debería considerar seriamente el uso de contenedores basados ​​en systemd. Además, los desarrolladores de Docker y Moby a menudo se han opuesto firmemente al uso de systemd en contenedores.

La llegada de Podman

Nos complace informar que la situación finalmente ha avanzado. El equipo responsable de ejecutar contenedores en Red Hat decidió desarrollar tu propio motor de contenedores. el tiene un nombre Podman y ofrece la misma interfaz de línea de comandos (CLI) que Docker. Y casi todos los comandos de Docker se pueden usar en Podman de la misma manera. A menudo llevamos a cabo seminarios, que ahora se llaman Cambiando Docker a Podman, y la primera diapositiva pide escribir: alias docker=podman.

Muchos lo hacen.

Mi Podman y yo no estamos de ninguna manera en contra de los contenedores basados ​​en systemd. Después de todo, Systemd es el subsistema de inicio de Linux más utilizado y no permitir que funcione correctamente en contenedores significa ignorar cómo miles de personas están acostumbradas a ejecutar contenedores.

Podman sabe qué hacer para que systemd funcione correctamente en un contenedor. Necesita cosas como montar tmpfs en/run y/tmp. A ella le gusta tener habilitado el entorno "en contenedores" y espera permisos de escritura en su parte del directorio cgroup y en la carpeta /var/log/journald.

Cuando inicia un contenedor en el que el primer comando es init o systemd, Podman configura automáticamente tmpfs y Cgroups para garantizar que systemd se inicie sin problemas. Para bloquear este modo de inicio automático, use la opción --systemd=false. Tenga en cuenta que Podman solo usa el modo systemd cuando ve que necesita ejecutar un comando systemd o init.

Aquí hay un extracto del manual:

hombre podman correr
...

–systemd=verdadero|falso

Ejecutando un contenedor en modo systemd. Habilitado de forma predeterminada.

Si ejecuta un comando systemd o init dentro de un contenedor, Podman configurará los puntos de montaje tmpfs en los siguientes directorios:

/ejecutar, /ejecutar/lock, /tmp, /sys/fs/cgroup/systemd, /var/lib/journal

Además, la señal de parada predeterminada será SIGRTMIN+3.

Todo esto permite que systemd se ejecute en un contenedor cerrado sin modificaciones.

NOTA: systemd intenta escribir en el sistema de archivos cgroup. Sin embargo, SELinux evita que los contenedores hagan esto de forma predeterminada. Para habilitar la escritura, habilite el parámetro booleano container_manage_cgroup:

setsebool -P contenedor_manage_cgroup verdadero

Ahora mire cómo se ve Dockerfile para ejecutar systemd en un contenedor usando Podman:

# cat Dockerfile

FROM fedora

RUN dnf -y install httpd; dnf clean all; systemctl enable httpd

EXPOSE 80

CMD [ "/sbin/init" ]

Eso es todo

Ahora montamos el contenedor:

# podman build -t systemd .

Le decimos a SELinux que permita que systemd modifique la configuración de Cgroups:

# setsebool -P container_manage_cgroup true

Por cierto, mucha gente se olvida de este paso. Afortunadamente, esto sólo es necesario hacer una vez y la configuración se guarda después de reiniciar el sistema.

Ahora simplemente iniciamos el contenedor:

# podman run -ti -p 80:80 systemd

systemd 239 running in system mode. (+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=hybrid)

Detected virtualization container-other.

Detected architecture x86-64.

Welcome to Fedora 29 (Container Image)!

Set hostname to <1b51b684bc99>.

Failed to install release agent, ignoring: Read-only file system

File /usr/lib/systemd/system/systemd-journald.service:26 configures an IP firewall (IPAddressDeny=any), but the local system does not support BPF/cgroup based firewalling.

Proceeding WITHOUT firewalling in effect! (This warning is only shown for the first loaded unit using IP firewalling.)

[  OK ] Listening on initctl Compatibility Named Pipe.

[  OK ] Listening on Journal Socket (/dev/log).

[  OK ] Started Forward Password Requests to Wall Directory Watch.

[  OK ] Started Dispatch Password Requests to Console Directory Watch.

[  OK ] Reached target Slices.

…

[  OK ] Started The Apache HTTP Server.

Eso es todo, el servicio está en funcionamiento:

$ curl localhost

<html  xml_lang="en" lang="en">

…

</html>

NOTA: ¡No intentes esto en Docker! Allí todavía hace falta bailar con una pandereta para lanzar este tipo de contenedores a través del demonio. (Se requerirán campos y paquetes adicionales para que todo esto funcione sin problemas en Docker, o deberá ejecutarse en un contenedor privilegiado. Para obtener más detalles, consulte статье.)

Un par de cosas interesantes más sobre Podman y systemd

Podman funciona mejor que Docker en archivos unitarios systemd

Si es necesario iniciar los contenedores cuando se inicia el sistema, simplemente puede insertar los comandos Podman apropiados en el archivo de unidad systemd, que iniciará el servicio y lo monitoreará. Podman utiliza el modelo fork-exec estándar. En otras palabras, los procesos contenedores son hijos del proceso Podman, por lo que systemd puede monitorearlos fácilmente.

Docker utiliza un modelo cliente-servidor y los comandos de Docker CLI también se pueden colocar directamente en un archivo unitario. Sin embargo, una vez que el cliente Docker se conecta al demonio Docker, (el cliente) se convierte en un proceso más que procesa stdin y stdout. A su vez, systemd no tiene idea de la conexión entre el cliente Docker y el contenedor que se ejecuta bajo el control del demonio Docker y, por lo tanto, dentro de este modelo, systemd fundamentalmente no puede monitorear el servicio.

Activando systemd a través de socket

Podman maneja correctamente la activación a través del socket. Debido a que Podman usa el modelo fork-exec, puede reenviar el socket a sus procesos de contenedor hijo. Docker no puede hacer esto porque utiliza un modelo cliente-servidor.

El servicio varlink que utiliza Podman para comunicarse con clientes remotos en contenedores en realidad se activa a través de un socket. El paquete cockpit-podman, escrito en Node.js y parte del proyecto cockpit, permite a las personas interactuar con los contenedores Podman a través de una interfaz web. El demonio web que ejecuta cockpit-podman envía mensajes a un socket varlink que systemd escucha. Luego, Systemd activa el programa Podman para recibir mensajes y comenzar a administrar contenedores. Activar systemd a través de un socket elimina la necesidad de un demonio en ejecución constante al implementar API remotas.

Además, estamos desarrollando otro cliente Podman llamado podman-remote, que implementa la misma CLI de Podman pero llama a varlink para ejecutar contenedores. Podman-remote puede ejecutarse sobre sesiones SSH, lo que le permite interactuar de forma segura con contenedores en diferentes máquinas. Con el tiempo, planeamos habilitar podman-remote para que sea compatible con MacOS y Windows junto con Linux, de modo que los desarrolladores de esas plataformas puedan ejecutar una máquina virtual Linux con Podman varlink ejecutándose y tener la experiencia completa de que los contenedores se ejecutan en la máquina local.

SD_NOTIFY

Systemd permite diferir el lanzamiento de servicios auxiliares hasta que se inicie el servicio contenedorizado que requieren. Podman puede reenviar el socket SD_NOTIFY al servicio en contenedor para que el servicio notifique a systemd que está listo para operar. Y nuevamente, Docker, que utiliza un modelo cliente-servidor, no puede hacer esto.

los planes

Planeamos agregar el comando podman generate systemd CONTAINERID, que generará un archivo de unidad systemd para administrar un contenedor específico especificado. Esto debería funcionar tanto en modo raíz como sin raíz para contenedores sin privilegios. Incluso hemos visto una solicitud para un tiempo de ejecución systemd-nspawn compatible con OCI.

Conclusión

Ejecutar systemd en un contenedor es una necesidad comprensible. Y gracias a Podman, finalmente tenemos un tiempo de ejecución de contenedor que no entra en conflicto con systemd, pero que lo hace fácil de usar.

Fuente: habr.com

Añadir un comentario