Docker Básico
Qué es docker
Es una herramienta que permite crear contenedores portátiles y de poco peso que puedan encapsular nuestra aplicación y correr bajo una máquina que posea Docker instalado. Esto independientemente del sistema operativo de la máquina
Principalmente el objetivo detrás de todo este esquema, es facilitar la portabilidad y el despliegue de nuestras aplicaciones, minimizando las dependencias requeridas sujetas al sistema operativo en la que se encuentre la misma.
Básicamente, Docker lleva el concepto de “encapsulamiento” proporcionado por las VM (Virtual Machines) a otro nivel. La gran diferencia radica en que teniendo nuestra aplicación en máquinas virtuales, dichas máquinas necesitan emular todo un sistema operativo por completo, independientemente de si usemos o nos todos los componentes del mismo.
Si tomamos en consideración todo lo que ello implica, nos encontraremos con una gran sobrecarga y gasto de recursos. Docker, por el contrario, utiliza solo contenedores y los mismos comparten un mismo sistema operativo y no sólo eso, sino que además por su forma modular, pueden compartir librerías entre sí, eliminando el “overhead” que implica emular todo un sistema operativo por cada parte que queramos encapsular de nuestra aplicación.
En la imagen previa, podemos apreciar la arquitectura entre un sistema compuesto por VM’s vs uno construido en con Docker.
Instalar Docker
Comencemos a trabajar, para ello debemos instalar Docker.
Podemos descargar Docker desde la página oficial
https://docs.docker.com/engine/installation/
Instalamos Docker y el set de herramientas que trae.
Abrimos Kitematic y luego le damos click Docker CLI, enseguida se nos abrirá una consola donde ejecutaremos nuestros comandos de docker.
Principalmente, Docker está dividido en dos grandes partes, el deamon y el client. Veamos un poco acerca de ellos.
Docker Daemon
Se refiere al proceso demonizado o “background process” que corre dentro de la capa de “Docker Engine” o “Motor de Docker”. El mismo se encarga de escuchar y ejecutar las instrucciones provenientes de un cliente (más adelante lo veremos con detalle). Estos comandos sirven para crear/detener/eliminar los contenedores donde se alojarán nuestras aplicaciones.
Docker Client
Contiene todos los comandos necesarios para manipular nuestros contenedores de docker. Estas instrucciones están expuesta al usuario y nos permitirá manejar toda nuestra infraestructura.
Comencemos
Correr contenedor “Hello Word”
Para crear contenedores de docker se usa el comando docker run
$ docker run busybox echo hello world
Con este comando creamos un contenedor con la imagen busybox que imprima un “hello word” dentro. Luego, el contenedor se detiene, se puede asemejar como a una especie de “job”.
Imagen de Docker
Pero si nos preguntamos, qué son docker images. Simple, una imagen de docker se pueden asemejar a una plantilla base que se usa para crear el contenedor, en la misma, están las dependencias que necesitan nuestras aplicaciones.
Actualmente hay muchas imágenes públicas, por ejemplo, imágenes de Apache, Nginx, Java, entre otras, las cuáles se pueden descargar y utilizar para crear nuestro contenedor. De esta forma, fácilmente podemos crear un contenedor con un servidor de apache ya configurado usando una imagen ya pública.
Para listar nuestras imágenes usamos el comando.
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
busybox latest efe10ee6727f 2 weeks ago 1.13MB
Actualmente no tenemos ninguna creada. La de busybox fue traída del registro público de imágenes de docker.
Correr un contenedor interactivo
$ docker run -t -i alpine /bin/bash
-t: Aloca una tty
-i: Nos comunicamos con el contenedor de modo interactivo.
Ahora podremos ejecutar comandos en la consola dentro de nuestro contenedor.
Al salir del modo interactivo, el contenedor se detendrá. Intentalo con Ctrl+C o Command + C.
Correr un contenedor en modo “background”
Vimos cómo crear un contenedor para ejecutar una instrucción o interactuar con él, teniendo en cuenta que el mismo se detiene luego de finalizar la acción, pero qué sucede si deseamos crear un pequeño servidor web. Nos interesa que el contenedor quede corriendo de fondo aceptando nuestras peticiones.
Creemos un servidor web simple usando python 3.5
$ docker run -d -p 1234:1234 python:3.5 python -m http.server 1234
Este comando crea un servidor de python usando SimpleHTTPServer en el puerto 1234. El argumento-p 1234:1234 le indica a docker que tiene que hacer un port forwarding del puerto 1234 del host al 1234 del contenedor.
El flag -d le indica a Docker que debe crear ese contenedor en segundo plano, es decir, que aunque nos salgamos, el contenedor seguirá corriendo. Esto es importante porque nos va permitir ejecutar comandos dentro del contenedor mientras corre.
Ya tenemos nuestro primer servidor de python, para abrirlo
En Linux:
$ google-chrome localhost:1234
En Mac:
$ open "http://$(docker-machine ip default):1234"
Entrar a modo interactivo sobre un contenedor en segundo plano
Digamos que deseamos entrar a nuestro contenedor y ejecutar algunos comandos en la consola. Para ello usamos el comando
$ docker exec -ti <container-id> /bin/bash
Pero cómo obtenemos el id del contenedor. Simple
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
c4489be8b466 python:3.5 "python -m http.se..." 48 seconds ago Up 46 seconds 0.0.0.0:4321->4321/tcp
Esto nos listará los contenedores que actualmente están corriendo.
Copiamos el id de nuestro contenedor de python y ejecutamos el comando. En este caso sería
$ docker exec -ti c4489be8b466 /bin/bash
Se nos abrirá una consola y ahí podemos ejecutar cualquier instrucción que deseemos.
Verifiquemos la versión de python del contenedor. Ejecutamos
python -V
>>Python 3.5.3
Salimos del contenedor con el comando exit
Ciclo de vida de un contenedor
Ya sabemos como crear un contenedor, pero Docker ofrece mucho más opciones a la hora de manejar nuestros contenedores. A un contenedor podemos crearlo, pararlo y eliminarlo. Para ello, tenemos a nuestra disposición una serie de comandos como docker create, start, stop, kill y rm.
Nota: docker create equivale a docker run -d pero con la diferencia que nosotros mismos debemos arrancar el contenedor usando docker start, algo que ya docker run -d hace.
Creemos un nuevo contenedor con otro servidor python en el puerto 8002
$ docker create -P --expose=8002 python:3.5 python -m http.server
Listamos nuestros contenedores, pero agregamos el flag -a, esto nos listarás los contenedores que hemos corrido, creado o los que ya no están corriendo.
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS ..
2c349246e9f3 python:3.5 "python -m http.se..." 30 seconds ago Created ..
c4489be8b466 python:3.5 "python -m http.se..." 5 minutes ago Up 5 minutes 0.0.0.0:4321->4321/tcp ..
0b785f6b937d busybox "echo hello world" 40 minutes ago Exited (0) 40 minutes ago
Arrancamos nuestro contenedor recien creado, tomando el ID del mismo, en este caso tendrá el estatus created
$ docker start 2c349246e9f3
Ahora, si queremos detener el contenedor para que no siga corriendo. Tenemos dos opciones, kill o stop.
$ docker kill a842945e2414(envía SIGKILL)$ docker stop a842945e2414(envía SIGTERM)
Si lo que deseamos es reiniciar nuestro contenedor, utilizamos docker restart contenedor_id
$ docker restart a842945e2414
Ahora, si necesitamos eliminarlo, usamos el comando docker rm contenedor_id
$ docker rm a842945e2414
Nota: Para eliminar nuestro contenedor, el mismo debe estar detenido.
Crear nuestra primera imagen de Docker
Ya vimos el uso que se le dan a las imágenes de Docker. Hasta el momento solo hemos utilizados imágenes públicas ya creadas, pero qué sucede si necesitamos crear nuestra propia imagen para manejar nuestra aplicación y todas las dependencias especiales que pudiéramos requerir.
Para hacer una propia, debemos crear un archivo llamado Dockerfile. En el cual, debemos definir secuencialmente todas las instrucciones necesarias para correr nuestro contenedor, como: instalación de dependencias, creación de directorios, creación de variable de entornos, entre otros.
Vamos a crear una imagen para solamente definir una variable de entorno.
Comenzamos:
$ mkdir primera_imagen && cd primera_imagen
$ touch Dockerfile
Abrimos nuestro Dockerfile y escribimos:
FROM alpine
ENV bar=foo
NOTA: Para la creación del Dockerfile, Docker nos proporciona una serie de comandos, en este ejemplo, utilizamos ENV y FROM, pero hay muchos más disponibles. Pueden revisarlos acá en la documentación oficial:
https://docs.docker.com/engine/reference/builder/
Luego para construir nuestra imagen usando el archivo Dockerfile previamente definido, utilizamos el comando build
$ docker build -t primera_imagen .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM alpine
latest: Pulling from library/alpine
88286f41530e: Pull complete
Digest: sha256:1072e499f3f655a032e88542330cf75b02e7bdf673278f701d7ba61629ee3ebe
Status: Downloaded newer image for alpine:latest
---> 7328f6f8b418
Step 2/2 : ENV bar foo
---> Running in 2fc17f32ae5c
---> 4c9f74bc4a67
Removing intermediate container 2fc17f32ae5c
Successfully built 4c9f74bc4a67
Successfully tagged primera_imagen:latest
Para verificar que tenemos nuestra imagen, las listamos.
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
primera_imagen latest 4c9f74bc4a67 22 minutes ago 3.97MB
python 3.5 56b15234ac1d 10 days ago 684MB
busybox latest efe10ee6727f 2 weeks ago 1.13MB
alpine latest 7328f6f8b418 5 weeks ago 3.97MB
Deberíamos tener nuestra imagen creada.