Primeros pasos con las herramientas de Docker

Docker Tools es un conjunto de herramientas que se viene empleando hace relativamente poco tiempo en multiples proyectos. En este post voy a explicar brevemente, de la forma mas concistente que pueda, cada una de las herrramientas que provee Docker.

¿Qué es Docker?

Docker es una herramienta que permite realizar una virtualización ligera, llamada contenerización.  Virtua que?, conte que?.  La virtualización basada en contenedores, también llamada virtualización del SO (Linux Containers), es una aproximacón en la cual la capa de virtualización se ejecuta como una aplicación del SO.

Es decir que los conetnedores tienen aislamiento y asignación de recursos similares a las maquinas virtuales, pero un enfoque arquitectónico diferencte que les permite se mucho mas portatil y eficiente. Veamos la siguiente figura:

Máquina virtual vs. Contenedores

Cuando usamos una máquina virtual (por ejemplo VirtualBox, VMWare, etc…), cada una de estas necesita un SO virtualizado. Luego en esta máquina virtual desplegamos nuestro servicio o aplicación. Esto se ve en la arquitectura de la imagen anterior como Guest OS. De esta manera se consumen más recursos de hardware por ende minimizano la memoria ram física real que tenemos en nuestro pc, entre otros recursos más.

En una infraestructura de contenedores, como Docker, tenemos el motor de Docker (llamado Docker Engine), el cual corre de forma nativa en sistemas operativos Linux, es mucho menos costosa a nivel de recursos la virtualización que realizamos.

El objetivo primario es simplificar la infraestructura de las aplicaciones para su despliegue y distribución.  Podemos resumir las siguientes ventajas y desventajas…

Ventajas:

  • Comsumen menos recursos de harware.
  • Las instancias arrancan mas rápido.
  • Perfecto para automatizar en entornos de integración continua.
  • Escalabilidad.
  • Alta disponibilidad.

Desventajas:

  • Como mencione antes, solo puede usarse de forma nativa en entornos Unix.
  • Las imagenes solo pueden estar basadas en versiones de Linux modernas (kernel mínimo de 3.8).
  • Seguridad, porque?. El contenedor deja la seguridad en manos del SO y, por lo tanto, se debe encapsular al usuario.

Bueno, ahora vamos de a poco a meternos en el tema Docker en sí. Meta guacha 2.0.

Imágenes y Contenedores

Docker maneja básicamente dos conceptos: imágenes y contenedores.

Las imágenes se pueden ver como un componente estático dado que no son mas que un SO con un conjunto de aplicaciones empaquetadas, mientras que un contenedor es la instanciación o ejecución de una imagen, pudiendo ejecutarse varios contenedores a partir de la misma imagen.

Docker Hub

Es un repositorio de imágenes pre-configuradas listas para usar. Dispone de las imagenes oficiales de postgresql, redis, mysql, ubuntu, mongodb, etc… además de muchas imágenes que van creando otros usuarios. Ustedes se pueden crear una cuenta y subir las suyas.

Con el siguiente comando nos bajamos una imagen de un linux llamada Alpine. Es una de las mas livianas.

docker pull alpine
Using default tag: latest
latest: Pulling from library/alpine
0a8490d0dfd3: Pull complete
Digest: sha256:dfbd4a3a8ebca874ebd2474f044a0b33600d4523d03b0df76e5c5986cb02d7e8
Status: Downloaded newer image for alpine:latest

Ahora ejecutamos el siguiente comando para ver las imagenes que tenemos descargadas.

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest 88e169ea8f46 2 weeks ago 3.984 MB

Como podemos observar vemos que tenemos descargada la imagen de Alpine. En este caso es la ultima dado que al no especificar ninguna por defecto obtiene la última.

Docker Engine

Es la herramienta principal de Docker. Es lo que nos permitio descargar la imagen desde Docker Hub como también ejecutar el conteiner. Vamos a ejecutar la imagen que bajamos dentro de un contenedor.

docker run -i -t alpine:latest /bin/sh
/ #

Como se puede observar ejecutamos en contenedor y entramos al mismo. Nos queda el prompt de nuestro Alpine. En este caso lo ejecute con dos parametros i y t. i nos permite ejecurta e interactuar con el mismo, t permite asignar un TTY a la consola del contendor. Existen muchas opciones mas.

Luego para salir de este Alpine ejecutamos exit. Volvemos a nuestro host local. Con el siguiente comando vamos a ver el contenedor que hemos ejecutado.

docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
9a8a63be587c        alpine:latest       "/bin/sh"           7 minutes ago       Exited (0) 5 minutes ago                       nauseous_euler

Podemos obsercar el estado del mismo, en este caso hemos salido del mismo. Para volver a entrar ejecumos el siguiente comando con el parametro attach.

docker attach 9a8a63be587c
You cannot attach to a stopped container, start it first

Ups!, que paso? para poder entrar al contenedor tenemos que arracarlo asi que…

docker start 9a8a63be587c
9a8a63be587c

En este caso estamos usando el id asignado a nuestro contenedor desde el momento que lo creamos. Ahora si…estmos adentro de nuestro contenedor.

docker attach 9a8a63be587c
/ #

Si queremos borrar nuestro contenedor solo ejecutamos docker rm id_contendor. Lo mismo para borrar nuestras imagenes pero con rmi, docker rmi id_imagen.

Docker Compose

Hasta acá tenemos lo básico de Docker. Veamos esto de Compose. Cuando tenemos aplicaciones web o tenemos varios microservicios corriendo donde unos dependen de otros y tenemos que hacer a mano la ejecución de cada contenedor se vuelve un bodrio. Para existe Compose, que necesitamos? solo crear un archivo de configuración para el. Luego Compose se encagara de crear todos los contenedores y los links entre ellos.

Vamos con un ejemplo clasico de una web. Que cosas tenemos:

  • Un servidor web (apache, nginx, etc…)
  • Un servidor de aplicación (jboss, bla bla)
  • Una base de datos (mysql, oracle bleb le)

Si usamos solo el engine de Docker deberiamos ejecutar lo siguiente:

docker run -d --name baseDatos -p 3306:3306 mysql
docker run -d --name servidoraplicacion -p 8080:8080 -l baseDatos:bbdd jboss/wildfly
docker run -d --name servidorweb -p 80:80 -l servidoraplicacion:app ngnix

Como vemos tenemos desplegados los contenedores en los puertos correspondientes. No hace falta bajar la imagen primero dado que si no esta Docker la trae desde el Hub.

Esto se vuelve pesado si tenemos que ir arrancando cada uno de los contenedores que foman parte de nuestra arquitectura. Esto hace muy dificil la gestion de muchos contenedores dado que tenemos que contemplar puertos, enlaces,  volúmenes, etc etc.

Para simplicar esto creamos el archivo que mencione antes en el cual vamos a describir todo esto. Creamos un archio .yml (YAML).

bbdd:
image: mysql
expose:
- 3306

app1:
image: jboss/wildfly
links:
- bbdd:redis
expose:
- 8080
volumes:
- ./webapps:/opt/jboss/wildfly/standalone/deployments/

app2:
image: jboss/wildfly
links:
- bbdd:redis
expose:
- 8080
volumes:
- ./webapps:/opt/jboss/wildfly/standalone/deployments/

web:
build: ./
links:
- app1:app1
- app2:app2
ports:
- 80:80

Lo primero que observamos es la definición de los cuatro contenedores:

  1. Un contenedor llamado bbdd que contiene una instalación de MySQL y expone el puerto 3306 en la red interna de docker.
  2. Un contenedor llamado app1 que contiene un WildFly y expone el puerto 8080 en la red interna de Docker, un enlace con el contenedor bbdd y el volúmen para que WildFly lea nuestro WAR.
  3. Un contenedor llamado app2 que contiene un WildFly y expone el puerto 8080 en la red interna de Docker, un enlace con el contenedor bbdd y el volúmen para que WildFly lea nuestro WAR.
  4. Un contenedor llamado web que construye un contenedor a traves de un Dockerfile, expone el puerto 80 en la red interna de Docker y lo mapea al puerto 80 de la máquina anfitrión y 2 enlaces con el contenedor app1 y app2.

Lo que nos queda luego es ejecutar docker-compose en donde tenemos ubicado nuestro archivo yml.

Docker Swarm

Es una herramienta que nos permite crear un cluster o agupamiento de maquinas docker. Veamos la siguiente figura para comprender mejor.

Swarm Master: El responsable de todo el clúster y gestiona los recursos de varios hosts Docker.

Swarm Nodes: Cada nodo del clúster debe ser accesible por el Master. Cada nodo ejecuta un agente de modo que registra el daemon Docker referenciado, supervisa, y actualiza el backend descubrimiento con el estado del nodo.

Swarm Discovery: Por defecto, Swarm utiliza un servicio de descubrimiento, basado en Docker Hub, utilizando un token para descubrir los nodos que forman parte de un clúster. También soporta otros servicios de descubrimiento como ETCD, Consul, y Zookeeper.

Swarm Strategy: cuenta con múltiples estrategias para la clasificación de los nodos. Cuando se ejecuta un nuevo contenedor, Swarm decide colocarlo en el nodo con el ránking más alto calculado para su estrategia elegida. Tiene múltiples estrategias para la clasificación de los nodos:

  • binpack: nodo con mayor número de contenedores de funcionamiento.
  • spread: nodo con menor número de contenedores de funcionamiento (por defecto).
  • random: aleatorio.

Swarm Networking: es totalmente compatible para el nuevo modelo de red (overlay) de docker 1.9.

Con esto tenemos una idea sobre Swarm. Ahora vamos a hacer un poco de bife.

Docker Machine

Bueno, mencione antes hay que creaer un sistema de descubrimiento por defecto por lo que hace falta crear un identificador para el cluster. Para eso ejecutamos esto:

token=$(docker run swarm create)
Unable to find image 'swarm:latest' locally
latest: Pulling from library/swarm
3ff891db6a4d: Pulling fs layer
5e01cc94d2a7: Pulling fs layer
692b182fbe11: Pulling fs layer
692b182fbe11: Verifying Checksum
692b182fbe11: Download complete
5e01cc94d2a7: Download complete
3ff891db6a4d: Verifying Checksum
3ff891db6a4d: Download complete
3ff891db6a4d: Pull complete
5e01cc94d2a7: Pull complete
692b182fbe11: Pull complete
Digest: sha256:e20f9cf2b432451f99100acceac562a2b6620aad1301f8679e8ff8afe0360bef
echo $token
5d70a153a0b26d9d3ec33ad728043241

Con esto guardamos el id en la variable token. Ahora vamos a crear los nodos y por supuerto al capo, al master.

docker-machine create -d virtualbox --swarm --swarm-master --swarm-discovery token://$token swarm-master
Running pre-create checks...
(swarm-master) Default Boot2Docker ISO is out-of-date, downloading the latest release...
(swarm-master) Latest release for github.com/boot2docker/boot2docker is v1.12.6
(swarm-master) Downloading /Users/nhesusrz/.docker/machine/cache/boot2docker.iso from https://github.com/boot2docker/boot2docker/releases/download/v1.12.6/boot2docker.iso...
(swarm-master) 0%....10%....20%....30%....40%....50%....60%....70%....80%....90%....100%
Creating machine...
(swarm-master) Copying /Users/nhesusrz/.docker/machine/cache/boot2docker.iso to /Users/nhesusrz/.docker/machine/machines/swarm-master/boot2docker.iso...
(swarm-master) Creating VirtualBox VM...
(swarm-master) Creating SSH key...
(swarm-master) Starting the VM...
(swarm-master) Check network to re-create if needed...
(swarm-master) Waiting for an IP...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with boot2docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Configuring swarm...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env swarm-master

Con esto cramos primero la maquina swam-master con la configuración de swarm master por defecto.

Ahora hacemos los nodos:

docker-machine create -d virtualbox --swarm --swarm-discovery token://$token swarm-node-01
Running pre-create checks...
Creating machine...
(swarm-node-01) Copying /Users/nhesusrz/.docker/machine/cache/boot2docker.iso to /Users/nhesusrz/.docker/machine/machines/swarm-node-01/boot2docker.iso...
(swarm-node-01) Creating VirtualBox VM...
(swarm-node-01) Creating SSH key...
(swarm-node-01) Starting the VM...
(swarm-node-01) Check network to re-create if needed...
(swarm-node-01) Waiting for an IP...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with boot2docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Configuring swarm...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env swarm-node-01
docker-machine create -d virtualbox --swarm --swarm-discovery token://$token swarm-node-02
Running pre-create checks...
Creating machine...
(swarm-node-02) Copying /Users/nhesusrz/.docker/machine/cache/boot2docker.iso to /Users/nhesusrz/.docker/machine/machines/swarm-node-02/boot2docker.iso...
(swarm-node-02) Creating VirtualBox VM...
(swarm-node-02) Creating SSH key...
(swarm-node-02) Starting the VM...
(swarm-node-02) Check network to re-create if needed...
(swarm-node-02) Waiting for an IP...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with boot2docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Configuring swarm...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env swarm-node-02

Lo que nos queda es conectarnos desde nuestro cliente docker al docker engine de la nueva maquina. Para ellos necesitamos declarar las variables de entorno adecuadas de la máquina a la que nos conectaremos. En este caso accedemos al nodo master y vemos la información de docker en el mismo.

eval "$(docker-machine env --swarm swarm-master)"
docker info
Containers: 4
Running: 4
Paused: 0
Stopped: 0
Images: 3
Server Version: swarm/1.2.5
Role: primary
Strategy: spread
Filters: health, port, containerslots, dependency, affinity, constraint
Nodes: 3
swarm-master: 192.168.99.100:2376
└ ID: P7UF:FLOG:6PAM:62IX:HRUL:SJXO:GT2Q:F2CW:KKUI:YKL7:ER3S:UJ5J
└ Status: Healthy
└ Containers: 2 (2 Running, 0 Paused, 0 Stopped)
└ Reserved CPUs: 0 / 1
└ Reserved Memory: 0 B / 1.021 GiB
└ Labels: kernelversion=4.4.41-boot2docker, operatingsystem=Boot2Docker 1.12.6 (TCL 7.2); HEAD : 5ab2289 - Wed Jan 11 03:20:40 UTC 2017, provider=virtualbox, storagedriver=aufs
└ UpdatedAt: 2017-01-11T04:54:05Z
└ ServerVersion: 1.12.6
swarm-node-01: 192.168.99.102:2376
└ ID: TWDP:HF7L:TMJR:GJRZ:QV6H:WMHX:GB76:MC4Q:LY3L:5TH5:5K5Z:KMXJ
└ Status: Healthy
└ Containers: 1 (1 Running, 0 Paused, 0 Stopped)
└ Reserved CPUs: 0 / 1
└ Reserved Memory: 0 B / 1.021 GiB
└ Labels: kernelversion=4.4.41-boot2docker, operatingsystem=Boot2Docker 1.12.6 (TCL 7.2); HEAD : 5ab2289 - Wed Jan 11 03:20:40 UTC 2017, provider=virtualbox, storagedriver=aufs
└ UpdatedAt: 2017-01-11T04:53:55Z
└ ServerVersion: 1.12.6
swarm-node-02: 192.168.99.103:2376
└ ID: F5I6:77JS:R77L:GYZT:HXR3:X4EM:NGTA:YDZL:DWK4:GYCJ:O5XT:VZOE
└ Status: Healthy
└ Containers: 1 (1 Running, 0 Paused, 0 Stopped)
└ Reserved CPUs: 0 / 1
└ Reserved Memory: 0 B / 1.021 GiB
└ Labels: kernelversion=4.4.41-boot2docker, operatingsystem=Boot2Docker 1.12.6 (TCL 7.2); HEAD : 5ab2289 - Wed Jan 11 03:20:40 UTC 2017, provider=virtualbox, storagedriver=aufs
└ UpdatedAt: 2017-01-11T04:54:03Z
└ ServerVersion: 1.12.6
Plugins:
Volume:
Network:
Swarm:
NodeID:
Is Manager: false
Node Address:
Security Options:
Kernel Version: 4.4.41-boot2docker
Operating System: linux
Architecture: amd64
CPUs: 3
Total Memory: 3.063 GiB
Name: 8733171cf2a2
Docker Root Dir:
Debug Mode (client): false
Debug Mode (server): false
WARNING: No kernel memory limit support

Por último, empleando el comando docker-machine podremos observar las máquinas que hemos creado anteriormente.

docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
swarm-master * (swarm) virtualbox Running tcp://192.168.99.100:2376 swarm-master (master) v1.12.6
swarm-node-01 - virtualbox Running tcp://192.168.99.102:2376 swarm-master v1.12.6
swarm-node-02 - virtualbox Running tcp://192.168.99.103:2376 swarm-master v1.12.6