Conceptos y Comandos Básicos en Docker

Advertencia
Este artículo se actualizó por última vez el 2021-09-14, el contenido puede estar desactualizado.

Antes de comenzar, quería contarles que hay una promoción en DigitalOcean donde te dan un crédito de USD 100.00 durante 60 días para que puedas probar los servicios que este Proveedor Cloud ofrece. Lo único que tienes que hacer es suscribirte a DigitalOcean con el siguiente botón:

DigitalOcean Referral Badge

O a través del siguiente enlace: https://bit.ly/digitalocean-itsm


En artículos anteriores vimos de manera muy superficial, alguna teoría sobre Docker, instalarlo en distribuciones Linux e instalar la interfaz gráfica más completa que podemos conseguir: Portainer

En esta oportunidad, vamos a ahondar más a fondo los conceptos de Docker, además de los comandos básicos que nos ayudarán a comprender esta fabulosa y popular herramienta.


La idea de lo que ahora llamamos “tecnología de contenedores” surgió por primera vez en el año 2000 como FreeBSD jail, una tecnología que permite la partición de un sistema FreeBSD en varios subsistemas o “jaulas” (jails). Las jaulas se desarrollaron como entornos seguros que un administrador de sistemas podía compartir con distintos usuarios dentro o fuera de una empresa.

En 2001, se introdujo en Linux la implementación de un entorno aislado, a través del proyecto VServer de Jacques Gélinas. Una vez que se estableció para múltiples espacios de usuario controlados en Linux, comenzó a tomar forma lo que hoy es un contenedor de Linux.

Cada vez se combinaron más tecnologías, con mayor rapidez, para hacer realidad este enfoque. Los grupos de control (cgroups) son una función del kernel que controla y limita el uso de los recursos para un proceso o grupo de procesos. Además, los cgroups utilizan systemd, un sistema de inicialización que configura el espacio de usuario y gestiona sus procesos, para proporcionar mayor control de estos procesos aislados. Ambas tecnologías, que agregan más control a Linux, también fueron el marco para que los entornos pudieran tener éxito al permanecer separados.

En 2008, Docker apareció en escena con su tecnología de contenedores que lleva el mismo nombre. La tecnología Docker incorporó una serie de conceptos y herramientas nuevos: una interfaz de línea de comandos sencilla para ejecutar y diseñar imágenes nuevas en capas, un daemon de servidor, una biblioteca de imágenes en contenedores prediseñadas y el concepto de un servidor de registros. Estas tecnologías combinadas permitieron que los usuarios diseñaran rápidamente nuevos contenedores en capas y los compartieran con otros sin ninguna dificultad.


Es un motor que añade una capa de abstracción adicional a la virtualización que permite utilizar el kernel de Linux y las funciones de este, como Cgroups y namespaces, para segregar los procesos, de modo que puedan ejecutarse de manera independiente.

Los contenedores ejecutan instancias de las imágenes. Al ejecutar una imagen se crea un contenedor.

A partir de una única imagen podríamos ejecutar varios contenedores. Por ejemplo, sería posible tener una misma aplicación ejecutándose en varios contenedores, y usar balanceadores de carga para distribuir los accesos y ofrecer servicios con más garantías y menos carga de peticiones en cada contenedor.

Como las imágenes no cambian, las modificaciones realizadas durante la ejecución de un contenedor no serán persistentes al detenerlo y volver a ejecutarlo. Pero es posible crear una nueva imagen, una nueva versión, con los cambios realizados. Y si algo va mal podríamos volver de forma sencilla a una versión anterior del contenedor.

El propósito de los contenedores es esta independencia: la capacidad de ejecutar varios procesos y aplicaciones por separado para hacer un mejor uso de su infraestructura y, al mismo tiempo, conservar la seguridad que tendría con sistemas separados, además de solucionar un problema muy común en infraestructura, el famoso: "En mi máquina funciona", problema que soluciona haciendo que los contenedores e imágenes sean consistentes y se ejecuten no importando el sistema operativo del host.

/images/como-instalar-docker-linux/containers-vs-virtual-machines.jpg

Permite compartir una aplicación, o un conjunto de servicios, con todas sus dependencias en varios entornos. Docker también automatiza la implementación de la aplicación (o conjuntos combinados de procesos que constituyen una aplicación) en este entorno de contenedores.

Estas herramientas desarrolladas a partir de los contenedores de Linux, lo que hace a Docker fácil de usar y único, otorgan a los usuarios un acceso sin precedentes a las aplicaciones, la capacidad de implementar rápidamente y control sobre las versiones y su distribución.

Las imágenes pueden almacenarse localmente o remotamente en un repositorio conocido como registro, donde están disponibles por nombre y normalmente en diferentes versiones etiquetadas, por ejemplo ubuntu:latest o mysql:5.7.

Es posible crear un registro privado, pero también pueden usarse registros públicos. El más utilizado es Docker Hub, un repositorio en la nube para crear, probar, guardar y distribuir imágenes. También proporciona a los usuarios un espacio para crear repositorios privados, automatizar funciones de compilación, crear webhooks o compartir espacios de trabajo.

Los archivos creados dentro de un contenedor no persisten entre ejecuciones. Docker proporciona dos mecanismos para que un contenedor almacene archivos en el host y persistan después de detenerlo:

  • Volúmenes (volumes): Son el mecanismo preferido para mantener la persistencia de datos. Es posible definir volúmenes en modo «sólo lectura». Y volúmenes que pueden compartirse por más de un contenedor, algunos en modo «lectura/escritura» y otros en modo «sólo lectura».

  • Puntos de montaje (bind mounts): Tienen una funcionalidad limitada en comparación con los volúmenes. Permiten montar un archivo o directorio de la máquina huésped en el contenedor. Son muy efectivos, pero se basan en el sistema de archivos del host y la estructura específica del directorio disponible.


El enfoque Docker para la creación de contenedores se centra en la capacidad de tomar una parte de una aplicación, para actualizarla o repararla, sin necesidad de tomar la aplicación completa. Además de este enfoque basado en los microservicios, puede compartir procesos entre varias aplicaciones de la misma forma que funciona la arquitectura orientada al servicio (SOA).

Cada archivo de imagen de Docker se compone de una serie de capas. Estas capas se combinan en una sola imagen. Una capa se crea cuando la imagen cambia. Cada vez que un usuario especifica un comando, como ejecutar o copiar, se crea una nueva capa, además de reutilizarlas para construir nuevas imágenescontenedores, lo cual hace mucho más rápido, eficiencia y tamaño durante el proceso de construcción. Cada vez que se produce un cambio nuevo, básicamente, usted tiene un registro de cambios incorporado: control completo de sus imágenes de contenedor.

Ya que toda imagen tiene capas, en caso de que esa imagen no se adapte a las necesidades actuales, se puede restaurar a una versión anterior. Esto es compatible con un enfoque de desarrollo ágil y permite hacer realidad la integración e implementación continuas (CI/CD) desde una perspectiva de las herramientas.

En el pasado, solía demorar días desarrollar un nuevo hardware, ejecutarlo, proveerlo y facilitarlo. Y el nivel de esfuerzo y sobrecarga era extenuante. Los contenedores basados en Docker pueden reducir el tiempo de implementación a segundos. Al crear un contenedor para cada proceso, puede compartir rápidamente los procesos similares con nuevas aplicaciones. Y, debido a que un SO no necesita iniciarse para agregar o mover un contenedor, los tiempos de implementación son sustancialmente inferiores. Además, con la velocidad de implementación, puede crear y destruir la información creada por sus contenedores sin preocupación, de forma fácil y rentable.


Se recomienda utilizar virtualización cuando la aplicación necesite el 100% del hardware a su disposición, tales como: base de datos, storage, aplicaciones stateful. Por otra parte, se recomienda usar contenedores cuando queremos fraccionar y aprovechas los recursos del hardware, por ejemplo, si tuvieramos un servidor (físico o virtual) con 16 GB de Memoria RAM, podriamos ejecutar 16 contenedores que usen 1GB de RAM cada uno, o en aplicaciones stateless.

Los contenedores son efímeros, esto significa que si por alguna razón el contenedor se reinicia, muere, se corrompe o se cierra manualmente, todo lo que hemos trabajado en el y sus archivos se perderán. Más adelante veremos como persistir esos datos para que se mantengan.


Ya hice un artículo el cual puedes consultar aquí, si ya lo tienes instalalo, puedes omitir ese paso.

Para ejecutar nuestro primer contenedor, vamos a bajar y ejecutar la imagen de prueba hello world

1
docker run hello-world

En caso de que la imagen no se encuentre en nuestro computador, se la va a descargar desde DockerHub, y nos encotraremos con la siguiente salida:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete 
Digest: sha256:7d91b69e04a9029b99f3585aaaccae2baa80bcf318f4a5d2165a9898cd2dc0a1
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

DockerHub es un repositorio compartido donde puedes subir imagenes personalizadas y bajar imagenes de distintos proveedores u otros usuarios, donde podremos encontrar desde servidores web hasta imagenes super pequeñas para ejecutar nuestras aplicaciones. Proporciona funcionalidades de creación automática de imágenes que es compatible con servicios como GitHub y BitBucket. Tambien proporciona webhooks para notificar via HTTP a servicios cuando hay una nueva imagen creada.

Para ver los contenedores que se encuentra actualmente en ejecucion:

1
docker ps
1
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Al ejecutar este comando, no veremos contenedores ejecutándose, por lo que, para todos los contenedores aún si no están corriendo:

1
docker ps -a
1
2
CONTAINER ID   IMAGE                        COMMAND                  CREATED         STATUS                     PORTS     NAMES
65678e8575bb   hello-world                  "/hello"                 7 minutes ago   Exited (0) 7 minutes ago             affectionate_carver

Podemos observar entre las columnas:

CONTAINER ID: es un id aleatorio que va a identificar al contenedor.
IMAGE: es la imagen que utiliza el contenedor.
COMMAND: es el comando con el cual arranca el contenedor, comunmente llamado ENTRYPOINT.
CREATED: tiempo de creación del contenedor.
STATUS: estatus del contenedor.
NAMES: nombre del contenedor, si no especificamos alguno, Docker lo colocará automáticamente.

Para solo descargar la imagen:

1
docker pull ubuntu:xenial

Puedes notar que hay una pequeña diferencia en relación al comando anterior donde utilizamos la imagen hello-world, después del signo de los dos puntos le estamos indicando a Docker que nos descargue esa imagen con el tag (etiqueta) indicando, en este caso una imagen Ubuntu con el tag de la versión Xenial, lo cual es muy util a la hora de escoger las imagenes basadas en su funcionalidad, tamaño, versión, etc. Si colocamos el tag latest, nos descargará la última versión de la imagen.

Para listar las imagenes almacenadas:

1
docker images

Para crear un contenedor a partir de una imagen:

1
docker create ubuntu:xenial

Para arrancar un contenedor:

1
docker start Nombre_O_ID_del_Contenedor

Para detener un contenedor:

1
docker stop Nombre_O_ID_del_Contenedor

Lo que hace el comando docker stop es enviar una señal de apagado ordenado SIGTERM (kill -15)

Para eliminar un contenedor:

1
docker rm Nombre_O_ID_del_Contenedor

Para eliminar un contenedor de manera forzada:

1
docker rm -f Nombre_O_ID_del_Contenedor

Para ejecutar un contenedor en segundo plano y asignarle un nombre específico:

1
docker run -d --name nginx nginx:latest

Para poder entrar al contenedor nginx y ejecutar comandos:

1
docker exec -it nginx bash

-it: entramos al contenedor en modo interactivo (a la shell del contenedor)
bash: shell del contenedor. Dependiendo de la imagen, es común que sea sh en vez de bash

Para ejecutar un comando ad-hoc (es decir, sin entrar al contenedor):

1
docker exec nginx ls -l /usr+

Con el siguiente comando podremos ver información interesante de nuestro contenedor (omitiré un poco la salida para facilitar la lectura):

1
docker inspect nginx

Como vemos, se pueden obtener datos esenciales del contenedor en formato JSON: Su nombre, dirección IP, etc.

Supongamos que queremos filtrar y obtener solamente la dirección IP del contenedor:

1
docker inspect nginx | grep IPAddress
1
2
3
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAddress": "172.17.0.2",

Supongamos que queremos exponer nuestro contenedor con nginx o cualquier otro servicio al exterior, vamos a publicar ese puerto del contenedor:

1
docker run -d -p puerto_Host:puerto_Contenedor --name nombre_contenedor nombre_imagen:tag
1
docker run -d -p 8080:80 --name nginx nginx:latest

Para comprobar que nginx se encuentra corriendo:

1
curl localhost:8080

También podemos consultar si el contenedor está vivo con: docker ps

Como dijimos anteriormente, los contenedores son efímeros, lo que significa que cualquier cambio que hayamos hecho dentro del contenedor, se perderá en caso que el contenedor se reinicie, falle o muera. Para evitar esto, vamos a comenzar a utilizar Volúmenes.

Para listar los volúmenes existentes:

1
docker volume ls

Vamos a encontrar decenas de ellos alojados en nuestra máquina. Por defecto, Docker almacena los volumenes creados en el path /var/lib/docker/volumes/

Para crear un volumen:

1
docker volume create my-volume

Podemos obtener de igual manera, información sobre el volumen, el siguiente comando devuelve cosas como MountPoint y la ruta donde Docker almacena los datos del volumen, el cual puede ser montado en contenedores:

1
docker volume inspect my-volume
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[
    {
        "CreatedAt": "2021-09-11T17:18:16-03:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-volume/_data",
        "Name": "my-volume",
        "Options": {},
        "Scope": "local"
    }
]

Para montar el volumen en el contenedor:

1
docker create -P --mount source=my-volume,target=/var/www/html --name nginx nginx:latest

-P: le indicamos que se va a montar un volumen.
--mount: le indicamos que se va a montar un volumen.
source: el volumen creado almacenado en el que se va a montar en el contenedor.
target: ruta donde se va a montar el volumen.

Si modificamos los archivos de la ruta /var/www/html dentro del contenedor con el volumen montado desde el host, los cambios serán reflejados en el volumen montado.

Para eliminar un volumen:

1
docker volume rm my-volume

Para eliminar todos los volúmenes no utilizados:

Advertencia
Utilizar este comando con precaución
1
docker volume prune

Para eliminar una imagen:

1
docker rmi nginx

Para eliminar todas las imagenes:

1
docker rmi $(docker images -a)

Para detener y eliminar un contenedor:

1
docker rm -f nginx

Para eliminar todos los contenedores que estén con estatus exited:

1
docker rm $(docker ps -a -f status=exited -q)

Para eliminar todas las imagenes, contenedores, volumenes o redes no utilizadas:

Advertencia
Utilizar este comando con precaución
1
docker system prune

Es un subsistema de red que provee Docker mediante Drivers, los siguientes:

  • Bridge: Es el driver por defecto. Los contenedores que la usen van a obtener una IP interna y se van a poder comunicar entre ellas, también en el host.

  • Host: Cualquier contenedor que esté en esta red, va a poder obtener una IP de una red real, o que elimina el aislamiento de red entre los contenedores y el host.

  • Null (none): El contenedor que se encuentre en esta red, no tendrá IP. Es útil en aquellos casos donde se ejecuten tareas o scripts en el cual el resultado deba ser almacenado en un volumen.

  • overlay: Se usa en clustering (Docker Swarm) Lo que hace es extender la red en varios hosts, de manera que, los contenedores que están en esa red van a obtener IP’s del mismo rango y se van a poder ver entre ellos.

  • Macvlan: el contenedor dentro de esta red, se le va a asignar una dirección MAC única y además va a estar en la red real, pudiendo acceder a los recursos de la misma (DHCP, DNS, etc)

Para crear una red bridge:

1
docker network create red01 bridge

Para ver la información de la red:

1
docker network inspect red01 

Para asociar un contenedor a una red específica:

1
docker create --name nginx --network red01 nginx:latest 

Para contectar y desconectar un contenedor de la red:

1
2
docker connect red01 nginx
docker disconnect red01 nginx

Para eliminar todas las redes que no estén en uso:

Peligro
Utilizar este comando con extrema precaución
1
docker network prune

Hasta aquí el artículo, espero les haya gustado, ¡hasta la próxima!


Si te pareció útil este artículo y el proyecto en general, considera brindarme un café :)

Buy me a coffeeBuy me a coffee