A logo showing the text blog.marcnuri.com
English
Inicio»Operaciones»Docker: Configurando un contenedor como un servicio de Linux

Entradas Recientes

  • Fabric8 Kubernetes Client 7.2.0 está disponible!
  • Conectarse a un servidor MCP con JavaScript y AI SDK
  • Conectarse a un servidor MCP con JavaScript y LangChain.js
  • El Futuro de las Herramientas para Desarrolladores en la era de la IA
  • Conectarse a un servidor Model Context Protocol (MCP) con Java y LangChain4j

Categorías

  • Antiguo
  • Front-end
  • Go
  • Herramientas
  • Industria y negocios
  • Inteligencia Artificial
  • Java
  • JavaScript
  • Operaciones
  • Personal
  • Proyectos personales

Archivos

  • mayo 2025
  • abril 2025
  • marzo 2025
  • febrero 2025
  • enero 2025
  • diciembre 2024
  • noviembre 2024
  • agosto 2024
  • junio 2024
  • mayo 2024
  • abril 2024
  • marzo 2024
  • febrero 2024
  • enero 2024
  • diciembre 2023
  • noviembre 2023
  • octubre 2023
  • septiembre 2023
  • agosto 2023
  • julio 2023
  • junio 2023
  • mayo 2023
  • abril 2023
  • marzo 2023
  • febrero 2023
  • enero 2023
  • diciembre 2022
  • noviembre 2022
  • octubre 2022
  • agosto 2022
  • julio 2022
  • mayo 2022
  • marzo 2022
  • febrero 2022
  • enero 2022
  • diciembre 2021
  • noviembre 2021
  • octubre 2021
  • septiembre 2021
  • agosto 2021
  • julio 2021
  • diciembre 2020
  • octubre 2020
  • agosto 2020
  • junio 2020
  • mayo 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • noviembre 2019
  • octubre 2019
  • julio 2019
  • diciembre 2018
  • agosto 2018
  • julio 2018
  • junio 2018
  • mayo 2018
  • marzo 2018
  • febrero 2018
  • noviembre 2017
  • octubre 2017
  • agosto 2017
  • julio 2017
  • enero 2017
  • julio 2016
  • enero 2016
  • diciembre 2015
  • noviembre 2015
  • diciembre 2014
  • marzo 2014
  • febrero 2011
  • junio 2008
  • mayo 2008
  • abril 2008
  • enero 2008
  • junio 2007
  • mayo 2007
  • abril 2007
  • marzo 2007

Docker: Configurando un contenedor como un servicio de Linux

2018-02-23 en Operaciones etiquetado DevOps / Docker / Linux / Portainer / Servicio / Systemctl / systemd por Marc Nuri | Última actualización: 2021-03-19
English version

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
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.

1docker run -d --name portainer --privileged -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v /opt/docker/data/portainer:/data portainer/portainer

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.

1[Unit]
2Description=Portainer container
3After=docker.service
4Wants=network-online.target docker.socket
5Requires=docker.socket
6
7[Service]
8Restart=always
9ExecStart=/usr/bin/docker start -a portainer
10ExecStop=/usr/bin/docker stop -t 10 portainer
11
12[Install]
13WantedBy=multi-user.target

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:

1systemctl start portainer
2systemctl stop portainer

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

1systemctl enable portainer.service

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.

1[Unit]
2Description=Portainer container
3After=docker.service
4Wants=network-online.target docker.socket
5Requires=docker.socket
6
7[Service]
8Restart=always
9ExecStartPre=/bin/bash -c "/usr/bin/docker container inspect portainer 2> /dev/null || /usr/bin/docker run -d --name portainer --privileged -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v /opt/docker/data/portainer:/data portainer/portainer"
10ExecStart=/usr/bin/docker start -a portainer
11ExecStop=/usr/bin/docker stop -t 10 portainer
12
13[Install]
14WantedBy=multi-user.target

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.

Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Navegador de artículos
Inyección de campos desaconsejada “Field injection is not recommended” – Spring IOCSpring Bean Scopes: Guía para comprender los distintos scopes (ámbitos) de un Spring Bean
© 2007 - 2025 Marc Nuri