Ejecutando un servicio en docker

Trabajando con imágenes

Vamos a descargar una imagen del sistema operativo GNU/Linux Ubuntu del registro público docker hub. Normalmente el nombre de las imágenes tienen la forma usuario/nombre:etiqueta, si no indicamos la etiqueta será latest. Por ejemplo el nombre de una imagen puede ser nuagebec/ubuntu:15.04.

$ sudo docker pull ubuntu 
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
50aff78429b1: Pull complete 
f6d82e297bce: Pull complete 
275abb2c8a6f: Pull complete 
9f15a39356d6: Pull complete 
fc0342a94c89: Pull complete 
Digest: sha256:ec0e4e8bf2c1178e025099eed57c566959bb408c6b478c284c1683bc4298b683
Status: Downloaded newer image for ubuntu:latest

Como podemos ver cada imagen está formado por distintas capas, que corresponden a parte del sistema de fichero que forman la imagen. Las capas que son comunes a varias imágenes se comparten entre ellas y por lo tanto no es necesario bajarlas. Vamos a bajar otra imagen:

$ sudo docker pull ubuntu:14.04

Y podemos ver todas las imágenes que tenemos en nuestro equipo local con:

$ sudo docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
ubuntu                latest              00fd29ccc6f1        4 weeks ago         111MB
ubuntu                14.04               67759a80360c        4 weeks ago         221MB
debian                latest              da653cee0545        4 weeks ago         100MB

Para obtener información sobre una imagen, ejecutamos el siguiente comando:

$ sudo docker inspect ubuntu

Para buscar imágenes en docker hub, podemos utilizar la siguiente instrucción:

$ sudo docker search ubuntu

Si queremos buscar laas imágenes oficiales:

$ sudo docker search --filter "is-official=true" debian

Y para borrar una imagen:

$ sudo docker rmi ubuntu:14.04

«Dockerizando» un servidor web apache

Como primera aproximación para crear una imagen desde la que podamos crear un contenedor que ofrezca un servicio, como, por ejemplo, un servidor web, daremos los siguientes pasos:

  • Crearemos un contenedor desde una imagen base, por ejemplo desde la imagen «debian».
  • Accederemos a ese contenedor e instalaremos un servidor web apache2.
  • Crearemos una nueva imagen a partir de este contenedor que nos permitirá crear nuevos contenedores con el servidor web instalado.

Hay que indicar que el método que vamos a utilizar no es el que se usa habitualmente y que en estudiaremos el procedimiento habitual para conseguirlo, que será generar directamente una imagen con un servidor web instalado utilizando para ello un fichero Dockerfile y el comando docker build. De todas maneras vamos a mostrar este ejemplo que nos puede ser muy útil para seguir profundizando en el funcionamiento de docker.

En primer lugar vamos a crear un contenedor desde la imagen “debian” que habíamos descargado, para ello vamos a ejecutar la siguiente instrucción:

$ sudo docker run -i -t --name primero debian /bin/bash
root@61ee5966f2f3:/# apt-get update
root@61ee5966f2f3:/# apt-get install apache2
root@61ee5966f2f3:/# exit

Como vemos, hemos instalado en el contenedor el servidor web apache. Cuando se crea un contenedor, su sistema de fichero estará formado por la unión de las capas de la imagen origen, es decir tendrá el sistema de ficheros que tiene la imagen. Estas capas que forman el sistema de archivo del contenedor tienen dos características: son compartidas entre contenedores distintos que se crean desde la misma imagen, y son de sólo lectura, no se pueden modificar, por lo que cuando se crea un nuevo contenedor se añade una nueva capa de lectura y escritura donde se va registrando la creación de los nuevos ficheros y la modificación y borrado de los ficheros existentes.

Por lo tanto el sistema de archivo de nuestro contenedor estará formado por dos capas: la original de la imagen “debian”, y una segunda donde hemos creado los ficheros al instalar el servidor web. Cuando salimos de un contenedor de una sesión interactiva el contenedor se detiene (stop). Podemos ver la lista de contenedores detenidos:

$ sudo docker ps -a
 CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
 61ee5966f2f3        debian              "/bin/bash"         8 minutes ago       Exited (0) 3 seconds ago                       primero

Una vez que hemos hecho una modificación en nuestro contenedor, si queremos utilizar el servidor web deberíamos crear una nueva imagen a partir del contenedor, para ello:

$ sudo docker commit -m "Añadido apache" -a "José Domingo" 61ee5966f2f3 jose/apache:v1

Con commit vamos a crear una nueva imagen en nuestro repositorio local, se indica un comentario con la opción -m, el autor con la opción -a, el identificador del contenedor y finalmente el nombre de la nueva imagen.

$ sudo docker images
 REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
 jose/apache         v1                  81aeeac1781a        9 seconds ago       193.3 MB
 debian              latest              9a02f494bef8        12 days ago         125.1 MB
 ...

Creando un nuevo contenedor con el servidor web

Ahora vamos a crear un nuevo contenedor a partir de la imagen que hemos creado anteriormente: jose/apache:v1. Este contenedor lo vamos a crear usando la opción -d que nos permite «demonizar» el contenedor, es decir que se ejecute indefinidamente, para ello:

$ sudo docker run -d -p 8000:80 --name segundo jose/apache:v1 /usr/sbin/apache2ctl -D FOREGROUND
85218e86d6e48f360eb1c6e49c62da5e4ffd94b00308734e84f0253e72ec647d

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
85218e86d6e4        jose/apache:v1      "/usr/sbin/apache2ctl"   3 seconds ago       Up 2 seconds        0.0.0.0:8000->80/tcp   segundo

Vemos que el contenedor se está ejecutando, además con la opción -p mapeamos un puerto del equipo donde tenemos instalado el docker engine, con un puerto del contenedor. Además la instrucción que se ejecuta en el contenedor es la que nos permite ejecutar apache2 en segundo plano. En este caso podemos ver que accediendo al puerto 8000 de nuestro servidor docker accederemos al puerto 80 del contenedor.

Podemos ver los procesos que se están ejecutando en nuestro contenedor:

$ sudo docker top segundo
 UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
 root                11211               7597                0                   20:49               ?                   00:00:00            /bin/sh /usr/sbin/apache2ctl -D FOREGROUND
 root                11227               11211               0                   20:49               ?                   00:00:00            /usr/sbin/apache2 -D FOREGROUND
 www-data            11228               11227               0                   20:49               ?                   00:00:00            /usr/sbin/apache2 -D FOREGROUND
 www-data            11229               11227               0                   20:49               ?                   00:00:00            /usr/sbin/apache2 -D FOREGROUND

Para para nuestro contenedor:

$ sudo docker stop segundo

Y lo podemos volver a ejecutar:

$ sudo docker start segundo

O en un solo paso:

$ sudo docker restart segundo

Y finalmente para borrar el contenedor:

$ sudo docker stop segundo
$ sudo docker rm segundo