Haciendo del Desarrollo y la Arquitectura Web, ciencia y pasión.

Docker, Capitulo 1

Hoy os voy a hablar de Docker. Llevaba muchisimo tiempo queriendo escribir sobre esta tecnología, pues vamos allá. Docker es relativamente joven, de 2013, en muy pocos años se ha convertido en el estandar de facto, con un gran futuro por delante. Se trata de una tecnología que facilita de un modo extremo ciertos procesos, los despliegues, el testeo, la escalabilidad, la resiliencia (me encanta esta palabra ;^)), etc. Vamos a definir qué es Docker, pero antes vamos a decir lo que no es. Docker no es virtualización, como puedan ser VMware, KVM o VirtualBox. Antes de continuar con lo que es o lo que no es, vamos a dar un paso pasa atras para entender en qué consiste la virtualizacion.

Tradicionalmente el departamento de sistemas de una empresa tenía toda su infrastructura, como suelen decir fuera, on-premise, o in situ, en el CPD. La virtualización vino a dar respuestas y soluciones a problemas a la hora de permitir múltiples sistemas operativos corriendo en la misma máquina . La virtualización no es más que la creación, a traves de software de una emulacion de un recurso hardware. El hipervisor explota la capacidad de ciertos ordenadores, no todos lo permiten, de virtualizar los recursos, el procesador, la memoria, los interfaces de red, etc, de la máquina. Se debe elegir que proporción queremos que se asigne a la máquina virtualizada, cantidad de GB de memoria o nucleos de CPU. De este modo permiten ejecutar más de un sistema operativo en la misma máquina simultaneamente sobre ese hardware virtualizado.

 

Docker_structure

Si miramos la imagen que representa el servidor anfitrión y que dispone de un software de virtualización, en seguida veremos que hay varias partes repetidas, con un desperdicio de recursos. Básicamente la idea consiste en separar la parte de emulación del hardware y delegarlo al sistema operativo anfitrion. El kernel de Linux se encargará con ayuda de los Spacenames y los grupos de control o Cgroups, de permitir la ejecucion de procesos aislados y completamente segregados.  Y esta es la clave detrás del concepto de contenedor de Docker. Al haber aislado desde el sistema operativo puedo ejecutar n programas iguales sin que colisionen sus espacios de memoria o sus hilos de ejcución. El aislamiento de estos procesos nos permitirá, por ejemplo,  la ejecución por ejemplo de 2 o más servidores Apache corriendo en la misma máquina.

Pero vamos a meternos en harina, crearemos algunos contenedores y despues hablaremos del Dockerfile. Para empezar necesitaremos Docker instalado en la máquina, no tiene mucha ciencia. Basta con instalar la versión Community edition, la 18.06 en el momento de escribir este artículo.


daniel@dockerserver:~$ docker --version
Docker version 18.06.1-ce, build e68fc7a

El primer comando que vamos a aprender es como pedir ayuda. Con el parametro --help podemos leer documentación dentro de cualquier comando. Por ejemplo para ver que hace el comando ps haríamos:


daniel@dockerserver:~$ docker ps --help

Usage:	docker ps [OPTIONS]

List containers

Options:
  -a, --all             Show all containers (default shows just running)
  -f, --filter filter   Filter output based on conditions provided
      --format string   Pretty-print containers using a Go template
  -n, --last int        Show n last created containers (includes all states) (default -1)
  -l, --latest          Show the latest created container (includes all states)
      --no-trunc        Don't truncate output
  -q, --quiet           Only display numeric IDs
  -s, --size            Display total file sizes

Así que ya hemos visto para qué vale ps, nos lista los contenedores. Perfecto, este comando lo usaremos mucho. La opción -a nos indicará que queremos ver los que estan parados y en ejecucion. La opción -q nos indicará que solo necesitamos los ids. También nos vendrá bien más adelante.


daniel@dockerserver:~$ docker ps -a
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS                     PORTS               NAMES
34e5be65b8e8        ubuntu-vim            "vim"                    5 hours ago         Exited (0) 5 hours ago                         zealous_boyd
52546b2d4154        ubuntu-vim            "vim"                    5 hours ago         Exited (0) 5 hours ago                         gallant_pare
ded1f5b9285b        ubuntu                "bash"                   6 hours ago         Up 6 hours                                     ecstatic_beaver
de5d8c4a5261        ubuntu                "vim"                    6 hours ago         Created                                        elastic_shannon
2a2c0188e8f5        ubuntu                "bash"                   6 hours ago         Exited (0) 6 hours ago                         gallant_poitras
50229418aa45        ubuntu                "bash"                   6 hours ago         Exited (0) 6 hours ago                         sharp_curran
1752a6f169ff        alpine                "sh"                     6 hours ago         Exited (127) 6 hours ago                       stoic_mahavira
a70cc1715623        alpine                "sh"                     6 hours ago         Exited (0) 6 hours ago                         nifty_jones
a8091b9e806d        alpine                "sh"                     7 hours ago         Exited (0) 6 hours ago                         ecstatic_bhabha
dfd0b420e080        jenkins/jenkins:lts   "/sbin/tini -- /usr/…"   3 days ago          Exited (143) 3 days ago                        quizzical_keller

Ahora vamos arrancar un contenedor, así, a las bravas. Deliberadamente voy a arrancar uno que no tengo. Empezaremos con un clasico Hola Mundo, luego subiremos el nivel usando algo más complejo, una Debian o un entorno node, ya veremos. Así que iré a la consola y ejecutaré:


docker run hola-mundo

 daniel@dockerserver:~$ docker run hola-mundo
Unable to find image 'hola-mundo:latest' locally
latest: Pulling from library/hola-mundo
708e56201c93: Pull complete 
Digest: sha256:7fd85c550d60b54cf950722d75b0307ebb374329833b87b03d8a0eb59efdb487
Status: Downloaded newer image for hola-mundo:latest

¡Hola de DockerCon EU 2015 (Barcelona)!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hola-mundo" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

Si os fijais en la primera linea, como no tenía el contenedor (o realmente la imagen) del hola-mundo, pues ha cascado con todo éxito y entonces se lo ha bajado del Docker Hub. Docker hub es como un gran repositorio de imagenes disponibles listas para descargar. También podia habérmelo descargado para después arrancarlo haciendo un pull, asi:


 daniel@dockerserver:~$ docker pull hola-mundo:latest
latest: Pulling from library/hola-mundo
Digest: sha256:7fd85c550d60b54cf950722d75b0307ebb374329833b87b03d8a0eb59efdb487
Status: Image is up to date for hola-mundo:latest
docker.io/library/hola-mundo:latest

Y efectivamente me dice, tronco, up to date. Pues es que ya lo tenía, perfect. Vamos a ver lo que hay, vamos a hacer un listado de contenedores:


 daniel@dockerserver:~$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Mmm..., pues parece que no hay nada, ¿que esta pasando?, pues que el listado de contenedores por defecto solo muestra los que están corriendo, parece ser que el contenedor de hello-world arranca, devuelve una salida y termina. Mmmm... vale.


 daniel@dockerserver:~$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
ee9d69ad7a5b        hola-mundo          "/hello"            10 minutes ago      Exited (0) 10 minutes ago                       romantic_taussig

Efectivamente ahi tenemos contenedor con estado exited. Y bueno, si hay un contenedor, ¿habrá una imagen que ha producido ese contenedor? eeeefectivamente, lanzamos un image ls y veremos:


 daniel@dockerserver:~$docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hola-mundo          latest              aa08f42dc496        21 months ago       1.86kB

Notar ue aquí no haría falta por que las imágenes no tienen estado. ¿Y oiga, podria lanzar otro contenedor de esa imagen? por supuesto:


 daniel@dockerserver:~$docker run aa08f42dc496

¡Hola de DockerCon EU 2015 (Barcelona)!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hola-mundo" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

 daniel@dockerserver:~$docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
875dc9853f63        aa08f42dc496        "/hello"            55 seconds ago      Exited (0) 54 seconds ago                       epic_haslett
ee9d69ad7a5b        hola-mundo          "/hello"            29 minutes ago      Exited (0) 6 minutes ago                        romantic_taussig

Ahora el listado de los contenedores tiene dos registros con distintos ids. Cool. Como este contenedor no da para mucho más, vamos a ser algo más ambiciosos. Voy instalarme una Debian, pero antes de hacerlo, voy a introducir el concepto de ejecución en modo interactivo, -i y -t de terminal tty y ademas voy a pedir qué comando quiero ejecutar, en este caso una shell, por ejemplo bin/bash, asi:



daniel@dockerserver:~$ docker run -it debian bin/bash
root@c22f72d6b37e:/# ls
bin  boot  dev	etc  home  lib	lib64  media  mnt  opt	proc  root  run  sbin  srv  sys  tmp  usr  var
root@c22f72d6b37e:/bin# apt-get update
Get:1 http://security.debian.org/debian-security buster/updates InRelease [65.4 kB]
Get:2 http://deb.debian.org/debian buster InRelease [121 kB]                       
Get:3 http://deb.debian.org/debian buster-updates InRelease [51.9 kB]              
Get:4 http://security.debian.org/debian-security buster/updates/main amd64 Packages [234 kB]
Get:5 http://deb.debian.org/debian buster/main amd64 Packages [7906 kB]
Get:6 http://deb.debian.org/debian buster-updates/main amd64 Packages [7868 B]
Fetched 8387 kB in 3s (2738 kB/s)                        
Reading package lists... Done
root@c22f72d6b37e:/bin# apt-get install vim 
Reading package lists... Done
Building dependency tree   
[....]

Efectivamente en medio minuto tenemos una Debian a la que podemos entrar para dejarla a nuestro gusto. Le he hecho un update y he instalado un editor vim que no viene por defecto. Ahora mientras nuestro contenedor de id c22 sigue corriendo, desde otra terminal puedo ejecutar un comando, por ejemplo el vim que me instalé antes, con el nombre del archivo que quiero editar.


daniel@dockerserver:~$ docker exec -it c22f72d6b37e  vim wazaaaa.txt

si vuelvo a mi primera terminal, ya hay un archivo wazaaaa.txt y puedo abrirlo


root@c22f72d6b37e:/# ls
bin  boot  dev	etc  home  lib	lib64  media  mnt  opt	proc  root  run  sbin  srv  sys  tmp  usr  var	wazaaaa.txt
root@c22f72d6b37e:/# cat wazaaaa.txt 
Mooola esto de Docker oyes

Peeerfecto, ahora imaginemos que me he salido y quiero volver a seguir trabajando sobre el contenedor para ir haciendo mejoras, por ejemplo un segundo editor más sencillo para aquellos que no conozcan vim


daniel@dockerserver:~$ docker start -ai c22f72d6b37e
root@c22f72d6b37e:/# vim    
root@c22f72d6b37e:/# apt-get install joe
Reading package lists... Done
Building dependency tree     
[...]

Y una vez terminado las operaciones sobre el contenedor podemos hacer una nueva version de la imagen que lleva vim y joe, con un commit:


daniel@dockerserver:~$ docker container commit c22f72d6b37e
sha256:b47786e1708bb9d6fc98c212e3640d74eb8634dffb40f6314b5f45762a6f5b23
daniel@dockerserver:~$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
                                        b47786e1708b        7 seconds ago       168MB
jenkins/jenkins     lts                 f669140ba6ec        5 days ago          711MB
ubuntu              latest              9140108b62dc        2 weeks ago         72.9MB
httpd               latest              417af7dc28bc        3 weeks ago         138MB
debian              latest              f6dcff9b59af        4 weeks ago         114MB
alpine              latest              a24bb4013296        4 months ago        5.57MB
daniel@dockerserver:~$ docker image tag b47786e1708b debian-editors:v1.0
daniel@dockerserver:~$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
debian-editors      v1.0                b47786e1708b        About a minute ago   168MB
ubuntu-vim          latest              f25420e3eb13        25 hours ago         167MB

Tras el commit vemos que efectivamente se ha generado una nueva version de mi imagen, del cual podria generar nuevos contenedores. Esta versión no tiene ni nombre ni version. Así que con el comando tag podemos darle un nombre para ser facilmewnrte localizable y poder taguear versiones. Despues de taguearlo ya tenemos una nueva imagen con el nombre debian-editors y que esta en su version 1.0.