Docker: Configurando un contenedor como un servicio de Linux


Introducción

Existen múltiples formas de orquestar la gestión, iniciación, despliegue, etc. de los contenedores de Docker. Incluso Docker ofrece su propia herramienta y modo de ejecución.  También hay herramientas de terceros para orquestar los contenedores, entre ellas encontramos: Kubernetes, Rancher, Apache Mesos, etc.

El demonio de Docker ofrece un sencillo mecanismo para arrancar, parar y consultar el estado de los contenedores desplegados en el sistema. En esta publicación veremos como desplegar contenedores como servicios de Linux utilizando Docker + systemd sin necesidad de utilizar herramientas de terceros.

El tutorial muestra concretamente como desplegar Portainer como un servicio de Linux.

docker as a linux systemd service

Contenedor existente previamente

El método más sencillo para desplegar un contenedor como un servicio es crear un contenedor de Docker con un nombre concreto y mapear cada una de las operaciones de Docker (start y stop) con comandos de servicio de systemd.

Una vez creado un contenedor empleando el comando anterior, podemos arrancar, parar y reiniciar el contenedor empleando comandos estándar de docker indicando el nombre que le hemos proporcionado al contenedor (docker stop portainer, docker start portainer, docker restart portainer).

El siguiente paso es crear un fichero systemd unit de configuración con la descripción del servicio en el directorio /etc/systemd/system/. Siguiendo la metodología para desplegar un contenedor con una imagen estándar de Portainer, crearemos un fichero con el nombre portainer.service con el siguiente contenido.

El fichero unit define un nuevo servicio y mapea los comandos docker start and docker stop a las secuencias service start y stop de systemd.

El fichero describe también dependencias hacia network-online target y docker socket. Es decir, si docker socket no se inicia, este servicio tampoco lo hará. Además se define una dependencia hacia docker.service de modo que este servicio únicamente se iniciará después de que el anterior complete su secuencia de inicio.

Ahora ya podremos arrancar y parar el servicio empleando los comandos correspondientes:

Además, también podremos instalar el servicio para que se arranque al iniciar el sistema empleando el siguiente comando:

Crear contenedor (si aplica) al iniciar el servicio

Podemos mejorar el fichero unit anterior para que cree el contenedor si este no existe, esto nos permitirá ahorrarnos el primer paso de creación manual del contenedor  (docker run…) y podrá utilizarse para desplegar contenedores simplemente creando el fichero unit con el descriptor del servicio systemd.

Simplemente hemos añadido una línea adicional con una instrucción ExecStartPre. Los ficheros unit tienen determinadas limitaciones cuando se configuran comandos de ejecución en las definiciones de  ExecStart, ExtecStop, ExecStartPre…. Para evitar estas limitaciones, encapsulamos el comando que realmente queremos ejecutar dentro de un comando “/bin/bash -c” que nos permitirá tener un poco más de flexibilidad.

El primero de los comandos (docker container inspect) comprueba la existencia de un contenedor con el mismo nombre en la misma máquina. Si este comando lanza un error (el contenedor no existe), la siguiente instrucción se ejecutará creando así el contenedor. Este último comando es el mismo que hemos empleado en el ejemplo anterior para crear el contenedor de forma manual.

Es importante destacar que el comandodocker run debe de ejecutarse con el argumento -d (detached), de no hacerlo así, el comando no devolverá el control a systemd y por tanto el siguiente paso (ExecStart) no se ejecutará, por lo que el servicio systemd nunca se activará.

Dependiendo de la existencia previa o no del contenedor, la secuencia de inicio del servicio se comportará de un modo u otro:

  • Ya existe un contenedor de nombre portainer:
    • ExecStartPre no devolverá ningún error en la ejecución del primer comando, por (docker container inspect…), por lo que no ejecutará el siguiente (docker run…).
    • ExecStart se ejecutará normalmente y el proceso se enganchará al contenedor.
  • No existe un contenedor de nombre portainer:
    • ExecStartPre devolverá un error en la ejecución del primer comando (docker container inspect…), con lo que ejecutará el segundo de ellos creando y arrancando el contenedor docker en modo detached devolviendo el control a systemctl.
    • ExecStart ejecutará  igualmente (docker start) que no tendrá ningún efecto puesto que el contenedor ya está arrancado, pero se enganchará al contenedor.

Conclusión

En este tutorial hemos mostrado una forma sencilla de desplegar contenedores Docker como servicios systemd de Linux. Esto es una alternativa “barata” a Kubernetes o cualquier otra herramienta de orquestación aunque no tiene la misma fiabilidad y sólo debería de emplearse en casos muy específicos.

Dejar un Comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *