===== Docker =====
{{ apuntes:docker.png?200 }}
===== ¿Qué es Docker? =====
Docker es una plataforma para desarrollar, lanzar y ejecutar aplicaciones. Permite separar las aplicaciones desarrolladas de la infraestructura donde se desarrollan y trabajar así más comoda y rapidamente.
Docker permite empaquetar y lanzar una aplicación en un entorno totalmente aislado llamado contenedor. Estos contenedores se ejecutan directamente sobre el kernel de la máquina por lo que son mucho más ligeros que las máquinas virtuales. De esa manera podemos ejecutar mucho más contenedores para el mismo equipo que si éstos fueran maquina virtuales. De esta manera, podemos probar rapidamente uestra aplicación web, por ejemplo, en múltiples entornos distintos al de donde nos encontramos desarrollando. Realmente es mucho más rápido que hacerlo en una máquina virtual, puesto que reduce el tiempo de carga y el espacio requerido por cada uno de estos contenedores o máquinas.
Incluso existe un repositorio, el [[http://hub.docker.com|Hub de Docker]], donde podemos encontrar imágenes de máquinas que podemos directamente descargar y ejecutar como contenedores. De esa forma, podemos, con un solo comando, lanzar una máquina con la configuración exacta a la que necesitamos para probar nuestro sitio web, aplicación web o cualquier desarrollo en el que estemos trabajando.
En las siguienes imágenes se compara la arquitectura de Docker y la de un sistema basado en máquinas virtuales. Se puede observar que, al eliminar la necesidad de un Sistema Operativo invitado, los contenedores de Docker son mucho más ligeros. En la práctica, se puede apreciar claramente como la velocidad de creación y arranque de estos contenedores es claramente superior a la de cualquier máquina virtual, sobre todo porque en cada contenedor podremos decidir que capas instalar contando sólo con lo estrictamente necesario.
===== Instalación =====
Una vez hayamos descargado [[http://www.docker.com|Docker]] para su instalación, podemos ejecutar el siguiente comando para comprobar que todo se ha instalado y funciona correctamente. Básicamente ejecuta un contenedor llamado //hello-world// que se ejecuta sobre nuestra máquina y muestra un mensaje. Si todo sale bien significa que nuestra instalación de Docker funciona sin problemas.
santi@zenbook:$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
5b0f327be733: Pull complete
Digest: sha256:1f19634d26995c320618d94e6f29c09c6589d5df3c063287a00e6de8. . .
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
. . .
Si queremos comprobar la versión de Docker podemos hacerlo de la siguiente manera. A veces puede ser útil puesto que se pueden haber añadido nuevas características en las versiones posteriores y podemos necesitar saber en cuál estamos nosotros.
santi@zenbook:$ docker --version
Docker version 17.05.0-ce, build 89658be
===== Imágenes =====
Las imágenes son una serie de capas software que definen la estructura que tendrá el contenedor que se cree a partir de ella.
==== Crear una imagen ====
Normalmente se crean a través de un fichero donde se definen las características que se quieren añadir a dicha imagen, creando un fichero llamado ''Dockerfile'' como el siguiente. En nuestro caso hemos definido que queremos una máquina con soporte para PHP 7.0 y que disponga de un servidor web Apache. Además, se copiará el contenido de nuestra carpeta ''src'' (imaginemos que tenemos alli código PHP que queremos probar) a la carpeta ''/var/www/html'' del contenedor, que es justo la carpeta donde se copia el contenido que queremos que Apache sirva.
FROM php:7.0-apache
COPY src /var/www/html
De esta forma tendremos una carpeta cuyo contenido será el fichero ''Dockerfile'' que acabamos de crear y la carpeta ''src'' con el sitio web que queremos probar
santi@zenbook:$ ls
Dockerfile src
A continuación, ya podemos crear la imagen, ejecutando el siguiente comando, donde aprovechamos para etiquetarla como ''miapache-php'':
santi@zenbook:$ docker build -t miapache-php .
Sending build context to Docker daemon 3.584kB
Step 1/2 : FROM php:7.0-apache
7.0-apache: Pulling from library/php
aa18ad1a0d33: Already exists
29d5f85af454: Pull complete
eca642e7826b: Pull complete
3638d91a9039: Pull complete
3646a95ab677: Pull complete
628b8373e193: Pull complete
c24a2b2280ed: Pull complete
f968b84cbbbc: Pull complete
d9ca89480aeb: Pull complete
9d506ea407af: Pull complete
3e07f2e31260: Pull complete
cddaac5f61ab: Pull complete
1d7adb8f245d: Pull complete
Digest: sha256:917ceda30c70ebb682536a1f091f25a8364d61c72c3323. . .
Status: Downloaded newer image for php:7.0-apache
---> b123b973e3e8
Step 2/2 : COPY src /var/www/html
---> 8a6954795b9f
Removing intermediate container dc20be7a4288
Successfully built 8a6954795b9f
Successfully tagged miapache-php:latest
Ahora sólo nos queda lanzar un contenedor de esa imagen para ponerla en marcha. En este caso mapeamos el puerto 80 de la máquina
santi@zenbook:$ docker run -p 5555:80 miapache-php
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
[Wed Sep 20 11:25:14.857530 2017] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.10 (Debian) PHP/7.0.23 configured -- resuming normal operations
[Wed Sep 20 11:25:14.857575 2017] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
Podemos ver como ha arracando un servidor web Apache y, por el mapeo de puertos que hemos establecido, podemos visitar la URL ''http://localhost:5555'' para visitar el sitio web.
==== Listar las imágenes locales ====
Además, es posible comprobar que imágenes tenemos disponibles localmente. Serán las imágenes de las que hayamos creado contenedores en algún momento, independientemente de que éstos todavía existan.
santi@zenbook:$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
miapache-php latest 8a6954795b9f 5 minutes ago 390MB
debian latest 72ef1cf971d1 6 days ago 100MB
hello-world latest 05a3bd381fc2 7 days ago 1.84kB
==== Eliminar una imagen ====
Si no necesitamos una imagen, y ésta no tiene contenedores creados, podemos eliminarla del sistema.
santi@zenbook:$ docker rmi 05a3bd381fc2
Untagged: hello-world:latest
Untagged: hello-world@sha256:1f19634d26995c320618d94e6f29c. . .
Deleted: sha256:05a3bd381fc2470695a35f230afefd7bf978b56625. . .
Deleted: sha256:3a36971a9f14df69f90891bf24dc2b9ed9c2d20959. . .
===== Contenedores =====
==== Lanzar un contenedor ====
Como ya hemos visto un poco más arriba, el comando ''docker run'' crea y lanza un contenedor a partir de una imagen dada. En el caso anterior utilizabamos una imagen previamente preparada y descargada. A la imagen la habiamos llamado //miapache-php// y podíamos utilizar ese nombre para lanzar un contenedor. Además, al lanzar el contenedor mapeabamos el puerto 80 del contenedor con el puerto 5555 de nuestra máquina.
santi@zenbook:$ docker run -p 5555:80 miapache-php
Ahora veremos como también podemos lanzar contenedores indicando una imagen de la que todavía no disponemos (por ejemplo una imagen de [[https://hub.docker.com|Hub de Docker]] que contiene una máquina debian). Docker se encargará de descargar la imagen y, posteriormente, lanzará el contenedor. Además, al final del comando pasaremos como argumento el comando que queremos que docker ejecute sobre el contenedor en cuanto este inicie. En este caso queremos iniciar una sesión de ''bash''.
santi@zenbook:$ docker run -it debian bash
Unable to find image 'debian:latest' locally
latest: Pulling from library/debian
219d2e45b4af: Pull complete
Digest: sha256:126052225b62db18c32f03a4462a92fad2ef243c6509371edb82. . .
Status: Downloaded newer image for debian:latest
root@94565ea1af31:/#
Ahora veamos un ejemplo en el que queramos desplegar una página web en un servidor web Apache. Queremos crear un contenedor con un servidor Apache desde el que podamos comprobar que la página funciona correctamente.
Con el siguiente comando crearemos y lanzaremos un contenedor con las siguientes características:
* El contenedor tendrá asociado el nombre //miapache// que podrá ser utilizando a partir de su creación para realizar cualquier operación sobre él
* Se ha mapeado el puerto 80 del contenedor sobre el puerto 5555 de la máquina real. De esa manera, conectándonos al puerto 5555 de nuestro equipo podremos ver que ocurre en el 80 del contenedor, justamente donde estará escuchando el Apache del mismo
* Hemos mapeado la ruta actual de nuestro equipo con la ruta ''/usr/local/apache2/htdocs'' del contenedor, que es donde Apache va a ir a buscar la web que aloje. Suponemos que el directorio actual de mi máquina tengo el sitio web que quiero testear
* Indicamos que queremos crear y lanzar un contenedor utilizando la imagen del [[http://hub.docker.com|Hub de Docker]], concretamente con la versión 2.4
* También hemos indicado con la opción ''-d'' que queremos que la máquina se lance en segundo plano
santi@zenbook:$ docker run -dit --name miapache -p 5555:80 -v "$PWD":/usr/local/apache2/htdocs httpd:2.4
Unable to find image 'httpd:2.4' locally
2.4: Pulling from library/httpd
aa18ad1a0d33: Pull complete
2b28e4afdec2: Pull complete
802b6cd5ed3b: Pull complete
6f2336b7c318: Pull complete
d7c441746c9e: Pull complete
ba7f19f905f9: Pull complete
5c7522be7faf: Pull complete
Digest: sha256:a46bd62f5286321ed19875778246f3afe97df5f9e91f0dd369a6ecfa529dbe81
Status: Downloaded newer image for httpd:2.4
444c11d75d0a1e424c5c24668e62357af74de8b801e75d37c1e014e0423c9a2c
Una vez lanzado y puesto en marcha podemos ir a la dirección ''http://localhost:5555'' con nuestro navegador y visitar nuestro sitio web funcionando desde el contenedor.
Podemos ver cómo está el contenedor en ejecución con el comando ''docker ps'' que, puesto que se ha lanzado en segundo plano, sigue en funcionamiento.
santi@zenbook:$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
444c11d75d0a httpd:2.4 "httpd-foreground" 21 seconds ago Up 19 seconds 0.0.0.0:5555->80/tcp miapache
==== Listar los contenedores en ejecución ====
Si dejamos el contenedor en marcha y lanzamos el comando ''docker ps'' desde otra consola podremos ver como el contenedor se encuentra en ejecución.
santi@zenbook:$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a859e3fdc573 debian "bash" 10 seconds ago Up 7 seconds laughing_goldstine
==== Listar todos los contenedores ====
También podemos listar todos los contenedores, estén o no iniciados.
santi@zenbook:$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5248cf319258 debian "bash" 7 seconds ago Exited (0) 2 seconds ago silly_wiles
78d990ba4591 hello-world "/hello" 17 seconds ago Exited (0) 14 seconds ago vibrant_mccarthy
==== Eliminar un contenedor ====
Podemos eliminar un contenedor con el comando ''docker rm'' pasando su ''CONTAINER ID'' como argumento. También podríamos haberle asociado un nombre al crearlo para poder referirnos a él. Lo veremos más adelante.
santi@zenbook:$ docker rm 78d990ba4591
78d990ba4591
==== Detener un contenedor ====
Y ahora podemos detener y/o volver a iniciar el contenedor refiriéndonos a ella a través del nombre asociado.
santi@zenbook:$ docker stop miapache
miapache
==== Iniciar un contenedor ====
El contenedor queda ya asociado y simplemente lo tengo que lanzar con el siguiente comando cada vez que lo quiera poner en marcha
santi@zenbook:$ docker start miapache
miapache
==== Mostrar la información de un contenedor ====
También podemos mostrar toda la información sobre un contenedor existente
santi@zenbook:$ docker inspect miapache
[
{
"Id": "444c11d75d0a1e424c5c24668e62357af74de8b801e75d37c1e014e0423c9a2c",
"Created": "2017-09-20T01:15:37.043910145Z",
"Path": "httpd-foreground",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 9146,
"ExitCode": 0,
"Error": "",
"StartedAt": "2017-09-20T01:17:16.277707412Z",
"FinishedAt": "2017-09-20T01:16:36.545679645Z"
},
"Image": "sha256:d2327de7e695e105d1f764c5c291c3305cb1b2168960fab9afd2df2e71343532",
"ResolvConfPath": "/var/lib/docker/containers/444c11d75d0a1e424c5c24668e62357af74de8b801e75d37c1e014e0423c9a2c/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/444c11d75d0a1e424c5c24668e62357af74de8b801e75d37c1e014e0423c9a2c/hostname",
"HostsPath": "/var/lib/docker/containers/444c11d75d0a1e424c5c24668e62357af74de8b801e75d37c1e014e0423c9a2c/hosts",
"LogPath": "/var/lib/docker/containers/444c11d75d0a1e424c5c24668e62357af74de8b801e75d37c1e014e0423c9a2c/444c11d75d0a1e424c5c24668e62357af74de8b801e75d37c1e014e0423c9a2c-json.log",
"Name": "/miapache",
"RestartCount": 0,
"Driver": "devicemapper",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": [
"/var/www/unsitioweb:/usr/local/apache2/htdocs"
],
"ContainerIDFile": "",
. . .
. . .
. . .
Como se puede ver, la información que se muestra, es muy exhaustiva. En ocasiones, si sabemos lo que buscamos, podemos directamente aplicar un filtro para indicar que partes de la configuración queremos que se muestren.
En este caso queremos simplemente ver la configuración del contenedor (el que hemos creado anteriormente con nombre //miapache//) en lo que a la red y el montaje de volúmenes se refiere.
santi@zenbook:$ docker inspect --format "{{.NetworkSettings.Ports}}{{ .Mounts}}" miapache
map[80/tcp:[{0.0.0.0 5555}]][{bind /var/www/unsitioweb /usr/local/apache2/htdocs true }]
===== Varios =====
==== Eliminar la caché ====
Eliminará la cache del proceso de construcción de las imágenes. Asi podemos forzar que vuelva a descargar todos los ficheros y realizar todos los pasos como si fuera la primera vez.
santi@zenbook:$ docker builder prune
----
(c) 2017 Santiago Faci