En estos días estuve investigando un poco más como funciona Docker, tratando de entender la arquitectura y aprendiendo a usar los comandos de Docker.
Docker vs Maquinas Virtuales
El concepto de maquinas virtuales para desarrollar aplicaciones es bastante conocido. Desarrollamos una aplicación PHP en nuestra maquina (digamos que usamos Windows), pero nuestro server, el que va a hostear la aplicación, corre en Linux. Entonces creamos una maquina virtual en nuestra maquina para probar como funciona en ese sistema operativo.
Reproducir estos ambientes es pesado, ya que deberíamos armar una maquina virtual, exportarla y compartirla con el equipo de desarrollo. Imagínense el problema si somos un equipo de desarrollo en diferentes lugares del mundo y necesitamos compartirnos una VM que pesa 20 Gb, aún más si necesitamos hacer cambios a esos ambientes para replicar los cambios hechos en el ambiente de producción.
Algo un poco mas avanzado que podemos hacer es usar Vagrant para crear maquinas virtuales y Ansible, para provisionar maquinas virtuales instalando lo que necesitamos (dependencias y nuestra aplicación). De esta forma con unos pocos archivos en nuestro repositorio tenemos la configuración de nuestro ambiente reproducible por todo el equipo. El problema de mantener la misma configuración que el ambiente de producción sigue, pero un poco mas sencillo, es cuestión de modificar nuestros scripts de Ansible y todo debería seguir funcionando.
Podemos ver en el gráfico como desde nuestros scripts de Ansible podemos tanto provisionar nuestra maquina virtual, como así también los ambientes de producción o staging.
El concepto de Docker va un poco mas allá. Que pasa si pudiésemos con un solo archivo crear una imagen que sea la misma que corra en nuestra VM de desarrollo, en staging, en producción, en la maquina de un tester, etc.
Acá entran en juego un poco de terminología de Docker:
Imagen: Es el conjunto de binarios, bibliotecas y mi aplicación. Todo esto se arma en un paquete que va a correr en un container.
Container: Es el lugar donde va a correr la aplicación con todas sus dependencias. Es un lugar aislado, nada de afuera lo modifica. Es decir, si corremos un container en un Linux Debian en producción, pero en nuestra máquina tenemos otra distribución va a funcionar igual, ya que todas las dependencias están dentro del container.
Dockerfile: Es el archivo que tiene la receta de como crear la imagen.
Docker Hub: Es el lugar donde se guardan las imágenes de Docker
Tenemos un Dockerfile en nuestro proyecto, a partir de ese archivo creamos una imagen que publicamos en Docker Hub. Esta imagen tiene nuestra aplicación + dependencias.
Queremos instalar nuestra aplicación en producción, corremos un container con la imagen que esta en Docker Hub, lo mismo si queremos instalarla en staging o en un cluster.
Comandos Basicos de Docker
Los comandos que más vamos a utilizar en Docker son:
docker run <imagen>
Este comando nos permite correr un container con una imagen que tengamos localmente o que exista en Docker Hub.
docker start <nombre|id>
Nos permite arrancar un container que ya tengamos en nuestra maquina que este apagado, ya sea usando el nombre que le asignamos al crearlo, o el id autogenerado.
docker stop <nombre|id>
Nos permite parar un container que tengamos corriendo en nuestra maquina, nuevamente por nombre o id.
docker ps [-a incluye los containers apagados]
Este comando nos lista los containers que tengamos ejecutando en nuestra maquina, si agregamos la opción -a, nos muestra todos los containers que están en el registro, que alguna vez corrimos y nunca borramos.
Tengan cuidado porque cada vez que ejecutan docker run se genera un nuevo container y creanme que pueden llegar a tener muchísimos.
docker rm [nombre|id]
Este comando nos permite borrar los containers creados en nuestra maquina, tanto por nombre, como por id.
Manos a la obra
Vamos a empezar a usar estos comandos y para esto vamos a usar una imagen que esta en Docker Hub que se llama tutum/hello-world .
Para ejecutar el container en nuestra maquina vamos a la consola de Docker y ejecutamos:
docker run -d tutum/hello-world
La opción -d nos crea pone a correr el container en background, si no ponemos esa opción nuestra consola va a quedar freezada y vamos a tener que abrir otra para interactuar con Docker.
Luego ejecutamos
docker ps
Ya tenemos nuestro primer container corriendo :). Fíjense que dice Ports 80/tcp, entonces el container esta escuchando en ese puerto.
Esta imagen tiene una página donde nos muestra el nombre de nuestro container, intentemos entonces accederla.
Desde nuestro navegador ponemos en la URL a la IP de la maquina de Docker, en mi caso http://10.0.0.100.
Lo que nos va a pasar es que no vemos nada, el navegador nos dice ‘Connection Refused’.
Lo que nos pasa es que si bien el container esta escuchando al puerto 80, no tenemos un mapeo hecho de ese puerto del container, con el de la máquina de Docker. Para esto tenemos que agregar algunos parametros al comando run.
docker run -p 8080:80 tutum/hello-world
La opción -p nos permite mapear un puerto de la máquina de Docker, al container. En este caso, el puerto 8080 de la máquina de Docker se mapea al 80 del container.
Ahora si accedemos a la IP de la maquina de Docker, en mi caso http://10.0.0.100:8080 y vemos algo así:
Si ahora ejecutamos nuevamente docker ps vamos a ver lo siguiente
Vemos entonces que en la columna PORTS nuestro puerto esta mapeado.
Ahora vamos a parar el container.
docker stop 0bd2b89a3b5a
Cuando ejecutamos este comando el container se para, si queremos acceder en el navegador no vamos a ver nada y si ejecutamos nuevamente el comando docker ps, no vamos a ver nada.
La unica forma que podamos ver los contenedores parados es con el comando;
docker ps -a
Ahora si quisieramos podriamos arrancar uno de estos containers apagados con el comando
docker start 0bd2b89a3b5a
Este comando pone en funcionamiento el container que habíamos apagado antes. Para ver si esta funcionando ejecutamos
docker ps
Vemos que todas los comandos los ejecutamos usando el id del container, sería mas cómodo usar el nombre. Para poder ponerle un nombre al container utilizamos la opción –name
docker run --name hello-world -p 8080:80 tutum/hello-world
Lo último que nos queda hacer es eliminar un container, para eso ejecutamos.
docker rm hello-world
Tengan en cuenta que para borrar un container antes tiene que estar parado.
Espero les sirva, nos leemos!