El vicio de Docker

¿Alguna vez os habéis encontrado una tecnología/gadget tan versátil que os gustaría que estuviera todo ahí? Algo así como el smartphone actual, donde si pudieras, pedirías que con pulsar una sola tecla en la pantalla, que te preparase el café, te leyese las noticias, te pusiera en la cama la ropa que te vas a poner ese día, y que además te teletransportase al trabajo. Pues eso mismo me pasó a mi con Docker.
Docker
En una entrevista de trabajo que tuve, me hablaron de Docker por primera vez, como una forma de virtualizar aplicaciones, pero que no requería un OS completo de por medio. Para mi eso era toda una novedad, ya que me habría venido muy bien para virtualizar los servidores que uso para CoResponde en vez de tener que configurármelos uno a uno con todos los pequeños detalles. Solo crear una imagen de Docker especifica con todas las configuraciones, y lanzar contenedores de esa imagen en otros servidores. Más fácil imposible.
Desde el momento que empecé a enredar más a fondo con ello, vi que si pudiera, Dockerizaria hasta las sartenes de la cocina. Su potencia me sorprendió, y el rendimiento era extremadamente bueno.
Debo deciros antes de nada, que Docker está pensado para ejecutar en máquinas Linux, por lo cual si queréis hacerlo desde Windows o Mac, debereis usar una máquina virtual intermedia para poder funcionar. Pero por supuesto, esto no tiene ningún sentido, ya que si vamos a usar Docker, es porque no queremos hacer una virtualización tradicional. Así que si quieres usar Docker, ya estás tardando en pasarte a Linux.
Ahora entramos en la chicha de Docker. Esto es como funcionaría una máquina virtual tradicional, con su sistema operativo completo corriendo por encima de un sistema de virtualización o hypervisor elegido.

stackvm

Solo con el sistema operativo completo, el rendimiento se reduce dramáticamente. Sin embargo, este es el stack de Docker.

stackdocker

Con esto nos quitamos de en medio todo el sistema operativo de la máquina virtual, y el motor de Docker usará los recursos nativos de la máquina, junto con los binarios y librerías estrictamente necesarios para poder correr las aplicaciones que se requiera.
El funcionamiento de Docker es relativamente sencillo: Se basa en imágenes que contienen todo lo necesario para ejecutar una, o varias aplicaciones, y lanzar contenedores (Instancias) de estas imagenes. Contruir una imagen propia es tan sencillo como crear un fichero de texto plano llamado Dockerfile en el cual, se configura la aplicación que quieras correr.
Ej. de fichero Dockerfile

FROM buildpack-deps:jessie-scm
# A few problems with compiling Java from source:
# 1. Oracle. Licensing prevents us from redistributing the official JDK.
# 2. Compiling OpenJDK also requires the JDK to be installed, and it gets
# really hairy.
RUN apt-get update && apt-get install -y --no-install-recommends \
 bzip2 \
 unzip \
 xz-utils \
 && rm -rf /var/lib/apt/lists/*
RUN echo 'deb http://httpredir.debian.org/debian jessie-backports main' > /etc/apt/sources.list.d/jessie-backports.list
# Default to UTF-8 file.encoding
ENV LANG C.UTF-8
# add a simple script that can auto-detect the appropriate JAVA_HOME value
# based on whether the JDK or only the JRE is installed
RUN { \
 echo '#!/bin/sh'; \
 echo 'set -e'; \
 echo; \
 echo 'dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"'; \
 } > /usr/local/bin/docker-java-home \
 && chmod +x /usr/local/bin/docker-java-home
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64
ENV JAVA_VERSION 8u72
ENV JAVA_DEBIAN_VERSION 8u72-b15-1~bpo8+1
# see https://bugs.debian.org/775775
# and https://github.com/docker-library/java/issues/19#issuecomment-70546872
ENV CA_CERTIFICATES_JAVA_VERSION 20140324
RUN set -x \
 && apt-get update \
 && apt-get install -y \
 openjdk-8-jdk="$JAVA_DEBIAN_VERSION" \
 ca-certificates-java="$CA_CERTIFICATES_JAVA_VERSION" \
 && rm -rf /var/lib/apt/lists/* \
 && [ "$JAVA_HOME" = "$(docker-java-home)" ]
# see CA_CERTIFICATES_JAVA_VERSION notes above
RUN /var/lib/dpkg/info/ca-certificates-java.postinst configure
ENTRYPOINT /usr/lib/jvm/java-8-openjdk-amd64/bin/java -version 

Este es el código del Dockerfile para tener un contenedor con java 8 instalado.
Lo primero que se puede observar es el siguiente texto :

FROM buildpack-deps:jessie-scm

Esto indica que estás heredando de la imagen con nombre buildpack-deps y la versión jessie-scm. Casi la totalidad de las imágenes de docker, heredan de una más básica (Existen imágenes de Ubuntu o Debian básicas muy útiles de las cuales partir), y van añadiendo funciones extra.
Todo lo demás son intrucciones de que debe tener instalado nuestra imagen, excepto la orden final, que debe ser un ENTRYPOINT o CMD que es la orden que se ejecuta cuando se inicia una instancia de esta imagen. En el ejemplo anterior simplemente se ejecuta un java -version que mostraría simplemente la versión de java. Este parámetro de entrada, puede ser sustituido por nuestro propio punto de entrada para una imagen propia.
Para convertir este archivo Dockerfile en una imagen lista para ejecutar, simplemente con ejecutar este comando desde el propio directorio donde se encuentra el Dockerfile, crearemos una imagen:

docker build -t imagen-java .

En este caso, estamos construyendo una imagen con nombre imagen-java, diciéndole que nuestro fichero Dockerfile, se encuentra en el directorio actual. Dependiendo de todo lo que hayamos definido en el fichero, tardará más o menos tiempo, y al final si ejecutamos

docker images

veremos que ya tenemos nuestra imagen lista.
A partir de aquí ya tenemos lo básico para empezar a trabajar con ella. Lo siguiente es lanzar un contenedor de docker. Un contenedor no es más que una instancia de esta imagen base, y que será equivalente a nuestra máquina virtual tradicional para poder entenderlo bien. Para lanzar un nuevo contenedor solo necesitamos el siguiente comando:

docker run --name container-java imagen-java

Y listo, ya tenemos nuestro primer contenedor creado, y veremos como por pantalla nos sale la versión de java que configuramos en nuestra imagen base.
Los contenedores de Docker se paran, en cuanto no tienen una aplicación en primer plano ejecutándose. Esto significa que si lanzas un proceso que empieza y termina, como por ej. el contenedor lanzado antes con java -version, en cuanto se acabe de ejecutar, el contenedor se para, y tendrías que volver a ejecutarlo con

docker start container-java

ya que no habría que crear otro, solamente re-arrancar el ya creado.
Por supuesto esto es algo demasiado básico que no nos sirve para nada, ya que no tenemos ninguna aplicación real corriendo.
Llegados a este punto, toca explicar que es Docker Hub. Docker Hub es una plataforma, donde almacenar y poner disponibles imágenes de Docker pre-compiladas, para poder ejecutar desde cualquier sitio. Resumiendo: Si ejecutamos

docker run tomcat:8.0

primero se comprobará que no tenemos disponible en local una imagen con ese nombre, irá a docker hub, comprobará que existe una imagen con ese nombre y versión, se la bajará a tu equipo, y después lanzará un contenedor que se quedará ejecutando un servidor tomcat versión 8. Como podéis ver la simplicidad de esto es infinita, y nos permite lanzar contenedores extremadamente rápido.
Hay que tener en cuenta, que estos contenedores son como máquina virtuales, y los puertos que usan las aplicaciones dentro, están aislados, por lo cual si queremos que estén accesibles en la máquina, debemos mapearlos. La sintaxis es la siguiente:

docker run <nombreimagen> -p <puerto externo>:<puerto del contenedor>

Un ejemplo de esto con un contenedor de tomcat sería

docker run tomcat:8.0 -p 80:8080

Con esto mapeamos el puerto por defecto del tomcat del contenedor, a nuestro puerto 80 de la máquina, por lo que con acceder a http://localhost/  estariamos ejecutando ya todo lo que contenga nuestro tomcat.
Se pueden tener múltiples contenedor de la misma imagen, por lo que tener dos servidores a la vez sería tan fácil como

docker run tomcat:8.0 -p 81:8080

y ya tendríamos otro tomcat corriendo en la misma máquina. Por supuesto, tiene que ser en un puerto distinto, ya que nuestro puerto 80 estaría ocupado por el contenedor anterior, y crearía un conflicto.

Espero que os haya servidor de ayuda, y a pesar de que no es un tutorial completo, hayáis echado un vistazo a una de las tecnologías que más me ha impresionado en los últimos tiempos.

3 comentarios

  1. Gracias, me acabas de aclarar unas cuantas dudas que tenía acerca de docker.
    Entonces, al ejecutar un contenedor docker con «docker start «, ¿el resultado es como si ejecutara una aplicación de consola? Quiero decir, no es como ejecutar un sistema operativo virtualizado, se parece más a arrancar un servidor, aunque en un entorno más controlado por así decirlo.

    1. Docker start, arranca un contenedor que ya está parado. Para lanzar uno nuevo es docker run.
      No tiene porque ser de consola (He visto incluso un Firefox dockerizado), pero si es lo más común, ya que se suele ejecutar en servidor.
      Por decirlo de alguna forma, es como llamar a una aplicación (o varias) en un entorno aislado, con todo lo necesario para que funcione, y sea replicable de una forma sencilla, sin tener que tener instalada ninguna dependencia en el propio pc. Por decirlo de alguna manera, es una virtualización ligera.

      1. Ooooh! Ya! Vamos, que no necesita ser una aplicación de consola ni una aplicacion tipo servidor, puede ser cualquiera (incluso un navegador como tu dices).
        Entonces es muchísimo mejor de lo que yo pensaba. Muchas gracias por la info. Tengo unas ganas tremendas de probarlo, como tantas otras cosas, pero ahora tengo una idea mucho más clara de lo que es Docker.
        Gracias, mil gracias.

Unirse a la conversación

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