Introducción a Docker

Antes de decir lo que es Docker es casi mejor decir qué hace, que al final es lo que nos interesa.

Docker maneja contenedores aislados que contienen un código determinado. Ese código se independiza de tal forma que hasta permite que el contenedor que lo contiene sea compartido entre diferentes máquinas.

Ahora vamos a lo que no es.

Los contenedores de docker no son máquinas virtuales ya que lo que contiene son piezas de software y no máquinas completas.

Un contenedor de docker es una unidad de software que está autocontenida en el contenedor que contiene todo lo necesario para ejecutar ese código incluyendo código, configuración, procesos, dependencias o red e incluso puede contener la parte del sistema operativo necesaria para ejecutar la aplicación concreta.

Lo primero que necesitamos es instalar docker en nuestro sistema operativo, si es windows o mac tenemos que bajarnos el programa de docker.com y si es linux o bien seguimos las intrucciones de la web de docker o instalamos de los repositorios, de todos modos es bastante sencilla su instalación y no requiere nada en especial.

Una imagen es cualquier fichero que tiene lo suficiente del sistema operativo para hacer lo que necesitamos. Pensad que en una máquina virtual completa instalaríamos todo el sistema operativo, aquí no es necesario con lo que la imagen resultante será más pequeña que una máquina virtual completa.

Para ver las imagenes que tenemos en nuestro sistema, si es que tenemos alguna sería con el comando

docker images

Este comando nos va a dar entre otros valores el repositorio, la etiqueta y el ID de la imagen.

El repositorio es de donde viene la imagen, la etiqueta nos indica la versión, es muy probable que veáis como versión latest, y el ID de imagen es el identificativo interno de Docker.

Para referirnos a una imagen podemos hacerlo con el nombre y la etiqueta, o en caso de no tener tendríamos que hacerlo a través del ID de imagen.

Para ejecutar un contenedor usaremos el comando

docker run -ti repositorio:versión bash

Donde -ti significa terminal interactive

Y si en otra pantalla en paralelo ponéis

docker ps

Podréis ver los contenedores que estáis ejecutando. Es importante cuando lo ejecutéis que os deis cuanta que el ID del contenedor no es el mismo ID que el de la imagen, son números diferentes que no se pisan. Una cosa es la imagen y otra el contenedor.

Ahora bien, si ejecutamos un contenedor y luego lo cerramos todos los cambios que hayamos hechos se perderán, así que tenemos que hacer una copia del contenedor en imagen.

Para convertir un contenedor en imagen mientras el contenedor está activo haremos un

docker commit <id_contenedor>
docker tag xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx mi-etiqueta

Al hacer el commit nos creará una imagen con una etiqueta larguísima, así que habrá que renombrarla por algo más fácil de recordar.

Para borrar una imagen

docker image rm etiqueta

En este punto ya sabemos arrancar una instancia, pero necesitamos saber como ejecutar algo en un contendor, que al final esa es la razón para la que utilizar contenedor .

El contendor va a parar cuando termine lo que estamos ejecutando, para ello

docker run imagen proceso

Poniendo un ejemplo añadiendo comandos al bash como parámetro

edu@thinkpad ~ # docker run -ti nginx:latest bash -c "sleep 3; ls -lisa; echo ya termine"
total 72
 2 4 drwxr-xr-x 33 root root 4096 Apr 21 12:58 .
 2 4 drwxr-xr-x 33 root root 4096 Apr 21 12:58 ..
67 0 -rwxr-xr-x 1 root root 0 Apr 21 12:58 .dockerenv
27 4 drwxr-xr-x 2 root root 4096 Oct 20 2016 bin
70 4 drwxr-xr-x 2 root root 4096 Sep 12 2016 boot
 2 0 drwxr-xr-x 5 root root 360 Apr 21 12:58 dev
11 4 drwxr-xr-x 58 root root 4096 Apr 21 12:58 etc
73 4 drwxr-xr-x 2 root root 4096 Sep 12 2016 home
31 4 drwxr-xr-x 10 root root 4096 Oct 21 22:31 lib
29 4 drwxr-xr-x 2 root root 4096 Oct 20 2016 lib64
72 4 drwxr-xr-x 2 root root 4096 Oct 20 2016 media
75 4 drwxr-xr-x 2 root root 4096 Oct 20 2016 mnt
74 4 drwxr-xr-x 2 root root 4096 Oct 20 2016 opt
 1 0 dr-xr-xr-x 292 root root 0 Apr 21 12:58 proc
71 4 drwx------ 2 root root 4096 Oct 20 2016 root
45 4 drwxr-xr-x 3 root root 4096 Oct 20 2016 run
26 4 drwxr-xr-x 2 root root 4096 Oct 20 2016 sbin
69 4 drwxr-xr-x 2 root root 4096 Oct 20 2016 srv
 1 0 dr-xr-xr-x 13 root root 0 Apr 21 12:58 sys
68 4 drwxrwxrwt 2 root root 4096 Oct 21 22:31 tmp
20 4 drwxr-xr-x 15 root root 4096 Oct 21 22:31 usr
43 4 drwxr-xr-x 15 root root 4096 Oct 21 22:31 var
ya termine

También podemos dejar los contenedores corriendo en background, lo que en docker se llama dettach container, en este caso el conenedor correrá hasta que se mate o se salga de él (control +q).

Con control + q el contenedor se quedará corriendo en background, para volver a el tendremos que hacer un

docker attach CONTAINER_ID

Aquí me gustaría que tuieráis una cosa en cuenta cuando leyerais mucha de la documentación porque lleva a confusión, para hacer un dettach del contenedor en mucha documentación pone que es con control + p , pero realmente es control + q, al menos en Ubuntu que es donde yo siempre lo he usado para hacer pruebas.

Otra cosa es que cuando vais a hacer un docker attach hay que poner el Container_ID que es el número largo, el primero que os da con un docker ps y no el nombre del contenedor, es una tontería pero os podéis tirar un rato ahí intentando ver por qué no os funciona si no sabéis esto.

Ahora vamos a intentar hacer más de una cosa a la vez en el contenedor. Para ello iniciamos con docker run -ti imagen comado y luego añadiremos la segunda consola con docker exec NAMES comando.

Esto desglosado es de la siguiente manera

edu@thinkpad ~ # docker run -ti nginx:latest bash
root@2cc2b836fb2a:/# 

EN OTRA CONSOLA

edu@thinkpad ~ # docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
2cc2b836fb2a        nginx:latest        "bash"              35 seconds ago      Up 34 seconds       80/tcp, 443/tcp     boring_knuth
edu@thinkpad ~ # docker exec -ti boring_knuth bash
root@2cc2b836fb2a:/# 

En este caso hemos utilizado exec para añadir una consola al docker run del principio, para añadir comandos, en este caso hemos añadido también bash.

Si creáis un fichero en una consola lo veréis en la otra obviamente.

Pero claro, cuando ejecutamos algo en docker lo normal no es ejecutar un bash sino algún comando concreto, así que vamos a ejecutar

docker run --name nginx -ti -d nginx:latest bash -c "ouch hola"

Esto nos va a dar error porque el comando ouch no existe, así que lo que haremos será

docker logs nginx

Y nos dirá que el comando no existe:

bash: ouch: command not found

Ojo que le hemos puesto –name para indicar un nombre al contenedor y no hemos dejado que lo bautice directamente docker.

Para borrar un contenedor poder hacer

docker kill nombre

y luego

docker rm nombre

El primero matará el contenedor y el segundo borrará logs y demás, porque si queremos usar el mismo nombre nos dirá que ese contenedor ya existe, aunque no sea así y eso es porque no hemos hecho el rm del mismo.

Por defecto los contenedores pueden usar toda la memoria que tenga el sistema disponible, sin limitación, y claro, no es lo más óptimo, o al menos no es lo ideal.

Podemos limitar la cantidad de memoria asignada a un contenedor de forma similar a como lo haríamos en otro tipo de paravirtualiación como por ejemplo LXC.

Para limitar la memoria usaremos el comando:

docker run --memory 200m -ti nginx:latest bash

En este caso hemos limitado a 200 megas la imagen nginx:latest

Para ver que realmente hemos limitado en otra consola pondremos

docker stats infallible_bell
CONTAINER           CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/O           PIDS
infallible_bell     0.00%               504KiB / 200MiB     0.25%               6.41kB / 0B         0B / 0B             1

Por supuesto también podemos limitar la CPU, y aquí docker nos ofrece dos posibilidades:

  1. Limitar de forma relativa
  2. Limitar de forma absoluta

Limitar de forma relativa significa limitar de forma relativa a otros contenedores y absoluta es en función de la CPU del sistema.

Si usamos la forma relativa

docker run --memory 200m --cpu-shares 1024 -ti nginx:latest bash

En este caso le hemos dado 1024 acciones (shares) y si tenemos otra máquina con otros 1024 tendrá el 50% de la capacidad, si hubiera 10 todas iguales un 10%, pero si estuvuera con otra que tuviera 2048 entonces tendría un 33%, es decir, depende del total en el sistema, esto es útil para un laboratorio o cosas así.

Para calcular la CPU que se puede utilizar de forma absoluta lo hacemos teniendo en cuenta ciclos de reloj, indicando la quota y el periodo por core, si indicamos que el periodo es 50.000 entonces 25.000 es medio core y hablaríamos del 50% de la CPU, si hubiera 4 cores sería el 12,5% de la CPU, pero el comando el mismo, es decir, es dependiente del hardware.

docker run -it --cpu-period=50000 --cpu-quota=25000 nginx:latest /bin/bash

Vamos a pasar a hablar de la red en docker. Lo primero que tenemos que saber es que Docker tiene su propia red privada dentro del host, realmente se puede dividir esa red en varias de forma no demasiado complicada.

Obviamente podemos asignar contenedores a las redes que queramos dentro del host.

Una de sus funcionalidades, quizás de las más usadas es conectar un puerto externo a uno de docker

docker run --rm -ti -p 8080:8080 -p 8081:8081 --name ubuntu ubuntu:latest bash

Y este chorizo significa ejecuta docker, luego borralo, muy recomendable hacer eso siempre para que no se quede porquería, con terminal interactiva y me mapear el puerto 8080 externo al del contendor 8080, igual con el 8081, llamas al contenedor ubuntu y lo haces a partir de la versión latest de la imagen ubuntu y ejecutas el proceso bash.

Llegados a este punto ya podemos instalar alguna imagen de docker, vamos a empezar con una que seguro que a Ángel de uGeek le hace mucha ilusión, un nextcloud, lo comento porque Ángel es un gran experto en nextcloud y un gran admirador de esta plataforma.

Lo primero será buscar un contenedor de nextcloud, vosotros pensar que casi cualquier cosa que se os ocurra ya existe, lo haremos con el comando docker search

edu@thinkpad ~ # docker search nextcloud
NAME                        DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
wonderfall/nextcloud        Nextcloud - a safe home for all your data....   179                  [OK]
nextcloud                   A safe home for all your data                   73        [OK]       
greyltc/nextcloud           Nextcloud: a safe home for all your data. ...   29                   [OK]
rootlogin/nextcloud         Nextcloud docker image running on top of N...   10                   [OK]
indiehosters/nextcloud      Docker image for Nextcloud application.         9                    [OK]
sameersbn/nextcloud         Dockerized Nextcloud                            3                    [OK]
freenas/nextcloud           Access & share your files, calendars, cont...   3                    [OK]
aheimsbakk/nextcloud        Nextcloud - a safe home for all your data....   2                    [OK]
skybosh/nextcloud           A simple Nextcloud image.                       2                    [OK]
jremy/nextcloud             A docker image for Nextcloud, compatible w...   1                    [OK]
cyphar/nextcloud            NextCloud is a fork of OwnCloud. This is a...   1                    [OK]

Vamos a instalar nextcloud directamente, no es la que tiene más votos, pero vamos a probar esa misma.

Procedemos a instalar con docker pull

edu@thinkpad ~ # docker pull nextcloud
Using default tag: latest
latest: Pulling from library/nextcloud
6d827a3ef358: Pull complete 
87fe8fbc743a: Pull complete 
f6d1a8d304ab: Pull complete 
caf3547d9b73: Pull complete 
1004db2760ff: Pull complete 
66e2d66a547e: Pull complete 
bbfaa62c234a: Pull complete 
19ce8807f4d1: Pull complete 
ccf3ec7b3529: Pull complete 
7b0fb921474a: Pull complete 
8cde875cb7f5: Pull complete 
2e8b672a5081: Pull complete 
c1b547802173: Pull complete 
688c5e582e3a: Pull complete 
41307daccaf3: Pull complete 
c6f9b9f60c06: Pull complete 
b9bfa2b24ad2: Pull complete 
8d965230aa8e: Pull complete 
9c528fa83dc0: Pull complete 
a340a28467ca: Pull complete 
Digest: sha256:087fb214ab4aac1754fee23c75662902c8b0f0707bebd29368c455b386f3483f
Status: Downloaded newer image for nextcloud:latest

Y ahora arrancamos el contenedor en el puerto 80 y el 443

docker run --name nc -p 80:80 -p 443:443 -d nextcloud

Y en este caso al abrir la web entráis como admin/admin

De todos modos todos y cada uno de estos contenedores tienen la documentación en hub.docker.com, así que entráis ahí y podréis verlo.