Ejecutando docker containers.
Introducción
Qué es Docker?
Docker es una plataforma de ejecución de contenedores de aplicaciones en espacio de usuario. Para
dotar de sentido a esta afirmación en primer lugar habrá que definir qué es un contenedor (container
en términos técnicos).
Un contenedor es un entorno controlado de ejecución de un proceso. Ese entorno está compuesto de
varios elementos, básicamente todo lo necesario para que el proceso pueda ejecutarse:
• Recursos de ejecución: CPU y Memoria
• Recursos de acceso a la información: Sistema de Archivos
Existen dos conceptos centrales cuando hablamos de tecnología Docker, imágenes y containers,
pero antes vamos a hablar brevemente de los fundamentos de esta tecnología
Cosas del kernel: namespaces y cgroups
A medida que los sistemas operativos se vuelven más complejos y las plataformas de ejecución más
potentes surge la necesidad de aislar la ejecución de los procesos, de modo que dispongamos de
entornos más simples y manejables para la ejecución de los mismos.
Un sistema operativo puede ejecutar miles de procesos en un mismo entorno, en principio sin
aislamiento. Por suerte, desde la versión del Kernel 2.4.19 disponemos de la siguiente característica
namespaces
Esta característica del Kernel permite aislar recursos en un entorno separado, de este modo
podemos aislar ciertos procesos del resto de procesos en ejecución en un mismo sistema. Pero
¿cuáles son los elementos que podemos aislar y gestionar de un modo totalmente independiente?.
Pues tenemos los siguientes:
1
• Espacios de PID: En un determinado namespace podemos crear un proceso inicial, al que
se asocia el PID 1, y a partir del cual se crean todos sus hijos con PIDs dentro del mismo
namespace. De este modo todos los demás procesos del sistema operativo quedan “fuera”
del namespace, además de este modo podrá haber PID repetidos en namespaces diferentes.
• Espacios de Networking: Podemos correr procesos que utilicen puertos de red de modo
totalmente independiente y con la posibilidad de repetir los mismos puertos en namespaces
distintos.
• Espacios de Puntos de montaje: Podemos definir puntos de montaje del sistema de
archivos totalmente aislados de los puntos de montaje definidos en el sistema operativo que
actúa a modo de host
Esta característica del Kernel permite “romper” en sistema en varios “subsistemas” de modo virtual,
en el que los procesos y los recursos que utilizan están aislados y controlados de forma separada.
Disponemos de un comando para aplicar esta característica y definir namespaces. Este comando es
unshare
unshare --fork --pid --mount-proc bash
El comando anterior lanza el comando bash en un namespace separado, en el cual ese proceso tiene
PID 1
Opciones:
• --fork: lanza un proceso hijo antes de lanzar el programa bash
• --pid: deja de compartir el espacio de nombres de pid
• --mount-proc: monta el sistema de archivos /proc dentro del namespace
El nombre del comando indica “dejar de compartir”, en este caso los recursos indicados con el
anfitrión. Algunas de las opciones reconocidas por el comando y documentadas en la opción --help
del mismo son:
Modo de empleo:
unshare [opciones] <programa> [<argumento>...]
Ejecuta un programa con algunos espacios de nombres no compartidos con el padre.
Opciones:
-m, --mount[=<fichero>] deja de compartir el espacio de nombre de los montajes
-u, --uts[=<fichero>] deja de compartir el espacio de nombres UTS (nombre de
máquina, etc.)
-i, --ipc[=<fichero>] deja de compartir el espacio de nombres del System V IPC
-n, --net[=<fichero>] deja de compartir el espacio de nombres de red
-p, --pid[=<fichero>] deja de compartir el espacio de nombres de pid
-U, --user[=<fichero>] deja de compartir el espacio de nombres de usuario
2
-C, --cgroup[=<fichero>] deja de compartir el espacio de nombres de cgroup
-f, --fork crea un proceso hijo antes de lanzar <programa>
--mount-proc[=<dir>] monta primero el sistema de ficheros proc (implica --mount)
-r, --map-root-user asocia el usuario actual a root (implica --user)
--propagation slave|shared|private|unchanged
modifica la propagación del montaje en el espacio de nombres
del montaje
-s, --setgroups allow|deny controla llamadas al sistema de setgroups en el
espacio de nombres de usuario
-h, --help muestra esta ayuda y sale
-V, --version muestra información de versión y sale
Si a continuación ejecutamos:
ps aux
Obtendremos la lista de procesos siguientes:
root@base:~# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.4 19868 3576 pts/0 S 16:26 0:00 bash
root 4 0.0 0.4 38304 3212 pts/0 R+ 16:26 0:00 ps aux
lo cual ilustra a las claras que hemos entrado en un entorno aislado de ejecución en el que el
programa bash se ha lanzando en un proceso con PID 1.
Si a continuación ejecutamos
exit
y de nuevo ejecutamos
ps aux
veremos una salida mucho más extensa con todos los procesos del anfitrión
cgroups
Esta característica, actualmente en su versión 2, disponible desde la versión 4.5 del Kernel, de
Marzo de 2016.
Con los cgroups podemos definir restricciones de uso de recursos, como CPU, RAM, asociados a
determinados procesos. Esta es una característica que complementa a la posibilidad de aislamiento
abierta por los namespaces. Sigue un modelo de administración jerárquico y es posible asignar
tareas a determinados cgroups.
3
Para gestionar esta característica disponemos de comandos como cgcreate y cgexec
Necesitamos para poder utilizar estos comandos instalar el paquete cgroup-tools
apt update && apt install -y cgroup-tools
Vamos a crear un cgroup para el usuario de trabajo actual con posibilidad de aplicar restricciones de
memoria y CPU cuyo nombre sea micgroup. El usuario especificado tendrá el privilegio de añadir
tareas al cgroup.
cgcreate -a `whoami` -t `whoami` -g memory,cpu:micgroup
Las opciones indican:
• -a: usuario propietario de grupo y de todos sus archivos a excepción del archivo de tareas
“tasks”
• -t: usuario propietario del archivo de tasks
• -g: control group a añadir, especificando los controladores de restricciones y el nombre
Ahora vamos a aplicar una restricción de memoria de 1MB para ese cgroup, para ello ejecutamos
echo 1000000 >
/sys/fs/cgroup/memory/micgroup/memory.limit_in_bytes
Por último aplicamos el cgroup a un proceso, para ello usamos el comando cgexec
cgexec -g memory:micgroup bash
Comprobamos como efectivamente se está aplicando la restricción. Esto podemos verlo haciendo
uso del comando cgget
cgget -r memory.limit_in_bytes micgroup
mostraría la salida
micgroup:
memory.limit_in_bytes: 9998336
También podemos ver la restricción desde un punto de vista más práctico. Lanzamos desde la
consola bash abierta tras la ejecución del comando cgexec
apt update
el comando arroja
root@base:~# apt update
Ign:1 http://ftp.es.debian.org/debian stretch InRelease
Obj:2 http://ftp.es.debian.org/debian stretch-updates InRelease
Obj:3 http://security.debian.org/debian-security stretch/updates InRelease
Err:2 http://ftp.es.debian.org/debian stretch-updates InRelease
4
Couldn't spawn new processNo se pudo asignar memoria
Obj:4 http://ftp.es.debian.org/debian stretch Release
Err:3 http://security.debian.org/debian-security stretch/updates InRelease
Couldn't spawn new processNo se pudo asignar memoria
Err:5 http://ftp.es.debian.org/debian stretch Release.gpg
Couldn't spawn new processNo se pudo asignar memoria
síntoma claro de que la restricción de memoria de 1MB está afectando al proceso y a sus hijos.
Elementos principales de Docker
Imágenes
Una imagen es un conjunto de recursos que constituyen un paquete autocontenido de elementos
necesarios para la ejecución de un servicio. Los elementos principales que alberga la imagen son:
• Código ejecutable
• Librerías de soporte
• Archivos de configuración
• Variables de entorno
Ejemplos de imágenes podrían ser:
• Entornos de Sistemas Operativos: debian, ubuntu, fedora, centos...
• Servidores Web: Apache, Nginx...
• Servidores de Correo: Postfix...
• Servidores de Aplicaciones: Odoo, Tomcat...
• y un largo etc.
Las imágenes son elementos estáticos que se pueden distribuir y utilizar como base para ejecutar los
containers. Como se mencionó anteriormente, constan de todo lo necesario para que un proceso
pueda ejecutarse de forma aislada y con todos los recursos necesarios a su alcance.
A efectos de distribución de imágenes existen infraestructuras que le dan soporte, como por ejemplo
Dockerhub, que es un repositorio público de imágenes docker listas para descargar y usar.
Containers
Un container es una instancia en ejecución de una imagen. Para ejecutar un container necesitamos
una imagen de base para correr un proceso sobre el entorno que esta define.
Los containers se ejecutan de forma totalmente aislada sobre el host en el que se ejecutan. Varios
containers en un mismo host comparten el mismo kernel, de ahí que una de las ventajas principales
del uso de containers vs máquinas virtuales es el ahorro de recursos.
5
Asociado a un container tenemos la imagen de base a partir de la cual se ejecuta, sobre ella se
despliega un Overlay, una capa del sistema de archivos que almacena los cambios en el sistema de
archivos de la imagen base realizados por el container. Es decir, existe un elemento de “solo
lectura”, la imagen base sobre la que se ejecuta el container, y otro elemento de “lectura escritura”,
que corresponde a los cambios efectuados por el container sobre los archivos de la imagen de base.
Cuando un container se destruye, esta capa de lectura escritura desaparece, aunque permanece toda
la información disponible en la imagen de base, por este motivo docker dispone de sistemas de
gestión de la persistencia de los datos del container.
Respecto a las diferencias entre containers y máquinas virtuales, una máquina virtual necesita para
su ejecución un entorno de sistema operativo completo, un container utiliza gran parte de los
recursos de ejecución del host en el que se ejecuta.
Un container utilizará los recursos proprocionados por el propio sistema operativo anfitrión.
Como siempre, una imagen vale más que mil palabras:
Dónde puedo instalarlo?
El requisito principal para la ejecución de Docker es disponer de un Kernel compatible con éste.
A día de hoy existen versiones de Docker, tanto de la versión Enterprise como Community, para
cualquier plataforma: GNU/Linux, UNIX, Mac y Windows.
También es posible, y además constituye una de las opciones de despliegue más recurridas, el
utilizar servidores en Cloud, de distintos proveedores, como AWS (Amazon), Digital Ocean, Azure
(Microsoft), etc., para el despliegue de docker containers.
En este curso utilizaremos una distribución GNU/Linux de tipo Debian Jessie, la cual dispondrá de
un kernel compatible para la ejecución de Docker.
6
En breve veremos como instalar nuestro propia infraestructura Docker.
Cómo se administra?
Existen múltiples herramientas para administrar la tecnología Docker. En primera instancia
podemos identificar los siguientes componentes
Docker Engine
Por Docker Engine entedemos todos aquellos elementos de soporte de ejecución de docker
containers. Para poder ejecutar éstos es necesario disponer de los binarios y librerías necesarias para
la creación de imágenes, ejecución de containers y gestión de todos estos elementos.
Docker CLI
Docker CLI es el conjunto de herramientas de adminstración asociadas a Docker Engine. A través
de la línea de comandos, con el comando docker, podremos utilizar todas las capacidades integradas
en la tecnología.
Con los comandos Docker CLI podremos
• Crear y gestionar imágenes
• Crear, arrancar, detener, destruír containers
• Crear y gestionar recursos para los containers: volúmenes, networks
• Crear y gestionar clústers de nodos Docker Engine mediante Docker Swarm.
7
Docker Compose
Docker Compose es una herramienta de orquestación de servicios docker. A través de archivos de
configuración en formato YAML, .yml, declararemos todos los elementos necesarios para la
construcción de un servicio, a través de una imagen docker y de todos los recursos que necesitará el
servicio en tiempo de ejecución, mediante containers.
Docker Machine
Docker Machine es una herramienta que nos permite gestionar de forma remota plataformas de
ejecución de docker container, es decir sistemas que ejecuten Docker Engine.
8
Es una herramienta útil en entornos de producción, pues permite trabajar con múltiples providers de
Infraestructura Cloud (IaaS, Infraestructure as a Service), entre ellos encontramos a Digital Ocean,
AWS (Amazon Web Services), Azure, etc.
Mención interesante merece el driver de VitualBox, que permite ejecutar Docker Machine
utilizando como hosts maquinas virtuales en VirtualBox ejecutando Docker Engine
Docker Swarm
Swarm es la tecnología utilizada por Docker gestionar agrupaciones (clústers) de nodos.
Uno de los objetivos principales de Swam es el despliegue bajo demanda de unidades de ejecución,
en este caso docker containers. Utilizando esta tecnología podremos gestionar el despliegue de un
servicio, indicando las unidades de ejecución del mismo. Swarm se encargará de gestionar todos los
aspectos relativos a la disponibilidad y distribución de las unidades de ejecución, de forma
transparente y supervisada.
Hoy en día docker swarm está siendo desplazado por Kubernetes como tecnología de
administración de infraestructura para despliegue de containers debido al respaldo mayoritario de
los grandes proveedores como Amazon, Google o Microsoft.
9
Quién lo usa?
A día de hoy las ventajas del uso de containers son evidentes:
• Distribución de aplicaciones
• Estandarización del despliegue
• Uso eficiente de recursos de cómputo
• Consolidación de plataformas
Docker ha sido uno de los pioneros en proporcionar una solución para proporcionar una plataforma
potente, estable, versátil y madura. Muchas empresas han sabido ver el valor estratégico de su
propuesta y, o bien, se han incorporado como parterns a la propia tecnología Docker, o bien, han
desarrollado su propia tecnología al mismo fin.
Cabe destacar los siguientes socios/partners de tecnología Docker:
• RedHat
• IBM
• Google
• VMWare
• CoreOS
• Cisco
Otras corporaciones como Google o Amazon siguen de cerca las evoluciones de Docker y diseñan
productos que utilizan esta tecnología, como Google Cloud Platform o Amazon Web Services
10
Recursos
Página principal del proyecto
https://www.docker.com/
Página principal de la documentación
https://docs.docker.com/
11