La solución de vi­r­tua­li­za­ción Docker ha cambiado ra­di­ca­l­me­n­te a lo largo de la última década cómo se crea, comparte y opera el software. Ahora, las apli­ca­cio­nes in­di­vi­dua­les se vi­r­tua­li­zan con Docker, a di­fe­re­n­cia de las máquinas virtuales (VM) que se uti­li­za­ban an­te­rio­r­me­n­te. Por tanto, un Docker Container es un co­n­te­ne­dor de software o apli­ca­cio­nes.

El concepto de co­n­te­ne­dor de software se basa en los co­n­te­ne­do­res físicos, como los que tra­n­s­po­r­tan los barcos. Los co­n­te­ne­do­res como unidad es­ta­n­da­ri­za­da de la logística han hecho posibles las cadenas co­me­r­cia­les modernas. Un co­n­te­ne­dor puede ser tra­n­s­po­r­ta­do en cualquier barco, camión o tren diseñado para ello, algo que funciona no­r­ma­l­me­n­te con in­de­pe­n­de­n­cia del contenido del co­n­te­ne­dor. En el exterior, el co­n­te­ne­dor está dotado de in­te­r­fa­ces es­ta­n­da­ri­za­das. Esto es muy similar al caso de los Docker Container.

Dominios web
Compra y registra tu dominio ideal
  • Gratis SSL Wildcard para tra­n­s­fe­re­n­cias de datos más seguras
  • Gratis registro privado para más pri­va­ci­dad

¿Qué es un Docker Container?

¿Qué es exac­ta­me­n­te un Docker Container? Dejemos que nos lo explique el creador de Docker:

Cita

“Los co­n­te­ne­do­res son una unidad es­ta­n­da­ri­za­da de software que permiten a los de­sa­rro­lla­do­res aislar una app de su entorno” (Tra­du­c­ción de IONOS)

Cita original: “Co­n­tai­ne­rs are a sta­n­da­r­di­zed unit of software that allows de­ve­lo­pe­rs to isolate their app from its en­vi­ro­n­me­nt”. – Fuente: https://www.docker.com/why-docker

Al igual que muchos co­n­te­ne­do­res físicos pueden re­mo­n­tar­se a una única es­pe­ci­fi­ca­ción, es posible crear el número de co­n­te­ne­do­res Docker que se quiera a partir de una sola imagen. De esta manera, los co­n­te­ne­do­res Docker sientan las bases para servicios es­ca­la­bles y los entornos de apli­ca­ción re­pro­du­ci­bles. Podemos crear un Container desde una Image así como guardar Co­n­tai­ne­rs exi­s­te­n­tes en una nueva Image. Dentro del co­n­te­ne­dor se inician, pausan y concluyen los procesos.

Un Docker Container, a di­fe­re­n­cia de lo que ocurre con la vi­r­tua­li­za­ción mediante máquinas virtuales (VM), no contiene un sistema operativo propio (OS, por sus siglas en inglés). En cambio, todos los co­n­te­ne­do­res que se ejecutan en un mismo host de Docker se remiten al mismo núcleo del sistema operativo. Al utilizar Docker en un host de Linux, se utilizará el núcleo de Linux co­rre­s­po­n­die­n­te, pero si el software de Docker no funciona con un sistema Linux, se utiliza una re­pro­du­c­ción mínima del sistema Linux mediante hi­pe­r­vi­sor o máquina virtual.

En la ejecución, se asigna a cada co­n­te­ne­dor una cierta cantidad de recursos del sistema, entre los que se en­cue­n­tran la memoria de trabajo, los núcleos de la CPU, la memoria de gran capacidad y los di­s­po­si­ti­vos de red (virtuales). Té­c­ni­ca­me­n­te, se limita el acceso de un Docker Container a los recursos del sistema mediante los llamados cgroups (Control Groups). Para crear pa­r­ti­cio­nes de los recursos del núcleo y para limitar los procesos entre sí se utilizan los llamados na­me­s­pa­ces del núcleo.

Los co­n­te­ne­do­res de Docker se comunican con el exterior a través de la red. Para ello, se habilitan los puertos en los que escuchan de­te­r­mi­na­dos servicios, que suelen ser se­r­vi­do­res web o de bases de datos. Los propios co­n­te­ne­do­res se controlan en el re­s­pe­c­ti­vo host de Docker a través de la API de Docker. Los co­n­te­ne­do­res, entre otras cosas, pueden iniciarse, detenerse y eli­mi­nar­se. El cliente de Docker pro­po­r­cio­na una interfaz de línea de comandos (Command Line Interface, CLI) con los comandos co­rre­s­po­n­die­n­tes.

¿En qué se di­fe­re­n­cian Docker Container y Docker Image?

La dualidad de los conceptos Docker Container y Docker Image suele generar confusión; cosa que no sorprende, ya que se trata del dilema de la gallina y el huevo. Un co­n­te­ne­dor se crea a partir de una imagen, pero un co­n­te­ne­dor también puede guardarse como imagen nueva. Veamos en detalle qué di­fe­re­n­cias hay entre estos conceptos.

Una Docker Image es una re­pre­se­n­ta­ción inerte. La imagen solo ocupa algo de espacio en el disco duro; nada más. En cambio, el co­n­te­ne­dor de Docker es una instancia “viva”. Un Docker Container en ejecución tiene co­m­po­r­ta­mie­n­to: el co­n­te­ne­dor in­ter­ac­túa con el entorno. Además, el co­n­te­ne­dor tiene un estado que cambia con el tiempo y que ocupa una cantidad variable en la memoria de trabajo.

Quizás conozcas los conceptos de “clase” y “objeto” de la pro­gra­ma­ción orientada a objetos (OOP). La relación entre Docker Container y Docker Image es de cierta manera co­m­pa­ra­ble con la relación que existe entre objeto y la clase a la que pertenece. La clase solo existe; y a partir de ella se pueden crear varios objetos de la misma clase. La clase como tal se carga a partir de un archivo de código fuente. En el universo Docker hay un patrón similar. A partir de una unidad de código fuente, el “Do­c­ke­r­fi­le”, se crea un patrón del que se ori­gi­na­rán de nuevo varias in­s­ta­n­cias:

Código fuente Patrón Instancia
Concepto Docker Do­c­ke­r­fi­le Docker Image Docker Container
Analogía de pro­gra­ma­ción Código fuente clase Clase cargada Objeto in­s­ta­n­cia­do
Consejo

Llamamos a los co­n­te­ne­do­res de Docker “in­s­ta­n­cias en ejecución” de la imagen a la que pe­r­te­ne­cen. Los términos “instancia” e “in­s­ta­n­ciar” son ab­s­tra­c­tos. Si no estás muy enterado del tema, utiliza una regla mne­mo­té­c­ni­ca. Sustituye me­n­ta­l­me­n­te “in­s­ta­n­ciar” por “moldear”. Aunque no haya relación entre estas palabras, en in­fo­r­má­ti­ca existe un alto grado de co­rre­la­ción entre sus si­g­ni­fi­ca­dos. Imagínate el principio de esta manera: igual que cuando hacemos galletas iguales con un mismo molde a partir de una masa, in­s­ta­n­cia­mos muchos objetos del mismo tipo a partir de una misma plantilla. La in­s­ta­n­cia­ción es por tanto el momento en el que creamos un objeto a partir de un patrón.

¿Cómo se crea un co­n­te­ne­dor de Docker?

Para entender cómo se construye un Docker Container, es útil echar un vistazo a la me­to­do­lo­gía de la “App de 12 factores”. Se trata de una re­co­pi­la­ción de doce pri­n­ci­pios fu­n­da­me­n­ta­les para crear y operar software orientado a servicios. Tanto Docker como la app de doce factores nacieron en 2011. La app de doce factores ayuda a los de­sa­rro­lla­do­res a crear apps de software como servicio siguiendo es­tá­n­da­res de­te­r­mi­na­dos. Entre ellos se en­cue­n­tran:

  • Utilizar formatos de­cla­ra­ti­vos que au­to­ma­ti­cen la co­n­fi­gu­ra­ción y ahorren tiempo y costes a los de­sa­rro­lla­do­res nuevos en el proyecto
  • Tener en cuenta el sistema operativo y ga­ra­n­ti­zar la máxima po­r­ta­bi­li­dad entre planos de ejecución
  • Favorecer el de­s­plie­gue en pla­ta­fo­r­mas en la nube (evitar se­r­vi­do­res y ad­mi­ni­s­tra­ción de sistemas)
  • De­sa­rro­llo y pro­du­c­ción unitarios para permitir un de­s­plie­gue continuo ágil
  • Es­ca­la­bi­li­dad sin tener que modificar las he­rra­mie­n­tas, la ar­qui­te­c­tu­ra o las prácticas de de­sa­rro­llo

La co­n­s­tru­c­ción de un co­n­te­ne­dor de Docker se orienta por estos pri­n­ci­pios. Un Docker Container reúne los co­m­po­ne­n­tes que cubrimos a co­n­ti­nua­ción en detalle:

  1. Sistema operativo de co­n­te­ne­do­res y sistema de archivos de unión
  2. Co­m­po­ne­n­tes y co­n­fi­gu­ra­ción de software
  3. Variables de entorno y co­n­fi­gu­ra­ción de tiempo de ejecución
  4. Puertos y volumen
  5. Procesos y logs

Sistema operativo de co­n­te­ne­do­res y sistema de archivos de unión

A di­fe­re­n­cia de las máquinas virtuales, un Docker Container no tiene un sistema operativo propio. En lugar de eso, todos los co­n­te­ne­do­res que se ejecutan en un host acceden a un núcleo de Linux co­m­pa­r­ti­do. En el co­n­te­ne­dor solo se incluye una capa de ejecución mínima, en la que suele estar la im­ple­me­n­ta­ción de la bi­blio­te­ca estándar C así como un Shell de Linux para ejecutar procesos. He aquí un resumen de los co­m­po­ne­n­tes de la Image oficial de “Alpine Linux”:

Kernel de Linux Bi­blio­te­ca estándar C Comandos de Unix
Del host musl libc BusyBox

Una Docker Image consiste en un montón de capas, en inglés “layers”, de sistema de archivos de solo lectura. Una capa describe los cambios en el sistema de archivos a la capa inferior. Uti­li­za­n­do un sistema de archivos de unión especial como overlay2, las capas se su­pe­r­po­nen y se unifican en una su­pe­r­fi­cie coherente. Cuando se crea un Docker Container a partir de una Image, se añade otra capa de escritura a las capas de solo lectura. Todos los cambios en el sistema de archivos se in­co­r­po­ran a la capa es­cri­bi­ble mediante el pro­ce­di­mie­n­to “copy on write”.

Co­m­po­ne­n­tes y co­n­fi­gu­ra­ción de software

A partir del sistema operativo de co­n­te­ne­do­res mínimo, se instalan otros co­m­po­ne­n­tes de software en un co­n­te­ne­dor Docker. Luego, se suelen realizar otros pasos de in­s­ta­la­ción y co­n­fi­gu­ra­ción. Para la in­s­ta­la­ción se utilizan las rutas ha­bi­tua­les:

  • Mediante gestores de paquetes del sistema como apt, apk, yum, brew, etc.
  • Mediante gestores de paquetes de lenguajes de pro­gra­ma­ción como pip, npm, composer, gem, cargo, etc.
  • Mediante la co­m­pi­la­ción en el co­n­te­ne­dor con make, mvn, etc.

He aquí algunos ejemplos para co­m­po­ne­n­tes de software tí­pi­ca­me­n­te in­s­ta­la­dos en Docker Container:

Ámbito de apli­ca­ción Co­m­po­ne­n­tes de software
Lenguaje de pro­gra­ma­ción PHP, Python, Ruby, Java, Ja­va­S­cri­pt
He­rra­mie­n­tas de de­sa­rro­llo node / npm, React, Laravel
Sistemas de bases de datos MySQL, Postgres, MongoDB, Redis
Se­r­vi­do­res web Apache, nginx, lighttpd
Caches y proxis Varnish, Squid
Sistemas de gestión de contenido WordPress, Magento, Ruby on Rails

Variables de entorno y co­n­fi­gu­ra­ción de tiempo de ejecución

Siguiendo la me­to­do­lo­gía de las app de doce factores, la co­n­fi­gu­ra­ción de un co­n­te­ne­dor Docker se almacena en variables de entorno. Por co­n­fi­gu­ra­ción, nos referimos a todos los valores que cambian entre los di­fe­re­n­tes entornos, como el sistema de de­sa­rro­llo frente al de pro­du­c­ción. Esto suele incluir nombres de host y cre­de­n­cia­les de la base de datos.

Los valores de las variables de entorno influyen en el co­m­po­r­ta­mie­n­to del co­n­te­ne­dor. Para que las variables de entorno estén di­s­po­ni­bles dentro de un co­n­te­ne­dor, se utilizan pri­n­ci­pa­l­me­n­te dos vías:

1. De­fi­ni­ción en Do­c­ke­r­fi­le

Una variable de entorno se declara en un Do­c­ke­r­fi­le con la in­s­tru­c­ción ENV. Ahí es posible asignar un valor por defecto, que se utilizará si la variable de entorno está vacía al iniciar el co­n­te­ne­dor.

2. Pasar variable al iniciar el co­n­te­ne­dor

Para acceder en el co­n­te­ne­dor a una variable de entorno que no ha sido declarada en el Do­c­ke­r­fi­le, pasamos la variable al iniciar el co­n­te­ne­dor. Esto sirve para las variables in­di­vi­dua­les a través de pa­rá­me­tros de la línea de comandos. Además, es posible pasar el archivo llamado Env, que define varias variables de entorno y sus valores.

Este es el patrón para pasar una variable de entorno al iniciar el co­n­te­ne­dor:

docker run --env <env-var> <image-id></image-id></env-var>

En muchas variables de entorno merece la pena pasar un archivo Env:

docker run --env-file /path/to/.env <image-id></image-id>
Nota

Con el comando “docker inspect”, las variables de entorno de los co­n­te­ne­do­res pueden mostrar su valor, por lo que hay que tener cuidado al utilizar datos sensibles en variables de entorno.

Al iniciar el co­n­te­ne­dor desde una imagen, se pueden tra­n­s­fe­rir los pa­rá­me­tros de co­n­fi­gu­ra­ción, entre los que se en­cue­n­tran las ca­n­ti­da­des asignadas de los recursos del sistema, que por lo demás son ili­mi­ta­dos. Además, también se utilizan los pa­rá­me­tros de inicio para definir los puertos y volúmenes del co­n­te­ne­dor (más in­fo­r­ma­ción en la siguiente sección). Los pa­rá­me­tros de inicio pueden incluso so­bre­s­cri­bir los valores pre­via­me­n­te asignados en el Do­c­ke­r­fi­le. A co­n­ti­nua­ción, reunimos varios ejemplos.

Asignar un núcleo CPU y 10 megabytes de al­ma­ce­na­mie­n­to al Docker Container al iniciarlo:

docker run --cpus="1" --memory="10m" <image-id></image-id>

Liberar los puertos definidos en el Do­c­ke­r­fi­le al iniciar el co­n­te­ne­dor:

docker run -P <image-id></image-id>

Mapear el puerto TCP 80 del host de Docker al puerto 80 del co­n­te­ne­dor de Docker:

docker run -p 80:80/tcp <image-id></image-id>

Puertos y volumen

Un Docker Container contiene una apli­ca­ción aislada del mundo exterior. Para sacarle partido, debe poder in­ter­ac­tuar con el entorno. Por eso, existen maneras de in­te­r­ca­m­biar datos entre host y container así como entre múltiples co­n­te­ne­do­res. Así, las in­te­r­fa­ces es­ta­n­da­ri­za­das permiten utilizar un co­n­te­ne­dor en distintos entornos.

Co­mu­ni­car­se desde el exterior con los procesos en ejecución dentro de un container es posible mediante puertos en red liberados. Aquí se utilizan los pro­to­co­los estándar TCP y UDP. A modo de ejemplo, ima­gi­ne­mos un co­n­te­ne­dor de Docker que contiene un servidor web y escucha al puerto 8080 TCP. Además, el Do­c­ke­r­fi­le de la Docker Image contiene la línea 'EXPOSE 8080/tcp'. Iniciamos el co­n­te­ne­dor con 'docker run -P' y accedemos al servidor web desde la dirección 'http://localhost:8080'.

Los puertos sirven para la co­mu­ni­ca­ción con los servicios que están eje­cu­tá­n­do­se dentro del co­n­te­ne­dor. No obstante, en muchos casos tiene sentido utilizar un archivo co­m­pa­r­ti­do entre el co­n­te­ne­dor y el sistema host para in­te­r­ca­m­biar datos. Con esta finalidad, Docker conoce distintos tipos de volumen:

  • Volúmenes con nombre: re­co­me­n­da­dos
  • Volúmenes anónimos: se pierden al eliminar el co­n­te­ne­dor
  • Bind mounts: hi­s­tó­ri­ca­me­n­te relativos y no re­co­me­n­da­dos; pe­r­fo­r­ma­n­te
  • Tmpfs mounts: están en la memoria de trabajo; solo en Linux

Hay di­fe­re­n­cias sutiles entre tipos de volúmenes. Elegir el tipo adecuado depende si­g­ni­fi­ca­ti­va­me­n­te del uso que se le vaya a dar. Ex­pli­car­los en mayor detalle se saldría del alcance de este artículo.

Procesos y logs

Un co­n­te­ne­dor Docker suele en­ca­p­su­lar una apli­ca­ción o servicio. El software que se ejecuta dentro del co­n­te­ne­dor forma un conjunto de procesos en ejecución. Los procesos de un co­n­te­ne­dor Docker están aislados de los procesos de otros co­n­te­ne­do­res o del sistema host. Dentro de un Docker Container, los procesos pueden iniciarse, detenerse y enu­me­rar­se. Se controlan a través de la línea de comandos o a través de la API de Docker.

Los procesos en ejecución emiten co­n­ti­nua­me­n­te in­fo­r­ma­ción de estado. Siguiendo la me­to­do­lo­gía de la app de doce factores, se utilizan los flujos de datos estándar STDOUT y STDERR para la salida. La salida de estos dos flujos de datos puede leerse con el comando 'docker logs'. Como al­te­r­na­ti­va, puede uti­li­zar­se el llamado co­n­tro­la­dor de registro. El co­n­tro­la­dor de registro estándar escribe los logs en formato JSON.

¿Cómo y dónde se utilizan los Docker Co­n­tai­ne­rs?

Ac­tua­l­me­n­te, Docker se utiliza en todos los ámbitos del ciclo vital del software, entre ellos, el de­sa­rro­llo, el testeo y las ope­ra­cio­nes. Los co­n­te­ne­do­res en ejecución en un anfitrión Docker se dirigen mediante la API de Docker. El cliente Docker se hace cargo de los comandos de la línea de comandos: se utilizan he­rra­mie­n­tas es­pe­cia­les de or­que­s­ta­ción para controlar las aso­cia­cio­nes de Docker Co­n­tai­ne­rs.

El patrón fu­n­da­me­n­tal en la uti­li­za­ción de Docker Container es algo así:

  1. El anfitrión Docker se relaciona con la imagen Docker del Registry.
  2. Se crea e inicia el co­n­te­ne­dor de Docker a partir de la imagen.
  3. La apli­ca­ción que contiene el co­n­te­ne­dor se ejecuta hasta que se para o elimina el co­n­te­ne­dor.

Hemos aquí dos ejemplos de uso de Docker Container:

Uso de Docker Container en entorno de de­sa­rro­llo local

Uno de los usos más populares de Docker Container es el de­sa­rro­llo de software. No­r­ma­l­me­n­te son equipos de es­pe­cia­li­s­tas los que de­sa­rro­llan un software. Para ello, se utiliza una colección de he­rra­mie­n­tas de de­sa­rro­llo de­no­mi­na­da toolchain, es decir, cadena de he­rra­mie­n­tas. Cada he­rra­mie­n­ta está di­s­po­ni­ble en una versión es­pe­cí­fi­ca, y toda la cadena funciona úni­ca­me­n­te si las versiones son co­m­pa­ti­bles entre sí. Además, la co­n­fi­gu­ra­ción de las he­rra­mie­n­tas debe ser correcta.

Docker se utiliza para ga­ra­n­ti­zar la co­he­re­n­cia del entorno de de­sa­rro­llo. Se crea una imagen Docker que contiene toda la cadena de he­rra­mie­n­tas co­rre­c­ta­me­n­te co­n­fi­gu­ra­da. Cada de­sa­rro­lla­dor del equipo extrae la imagen Docker en la máquina local e inicia un co­n­te­ne­dor desde ella. El de­sa­rro­llo se realiza entonces dentro del co­n­te­ne­dor. Si hay un cambio en la toolchain, la imagen se actualiza de manera ce­n­tra­li­za­da.

Uso de Docker Container en aso­cia­cio­nes or­que­s­ta­das

En los centros de datos de los pro­vee­do­res de hosting y pro­vee­do­res PaaS (“Platform-as-a-Service”), se utilizan redes de co­n­te­ne­do­res Docker. Equi­li­bra­do­res de carga, se­r­vi­do­res web, se­r­vi­do­res de bases de datos, etc. Cada servicio se ejecuta en su propio Docker Container. Un solo co­n­te­ne­dor puede úni­ca­me­n­te soportar una de­te­r­mi­na­da carga. Un software de or­que­s­ta­ción supervisa el co­n­te­ne­dor así como su estado y su ocupación. A medida que la carga aumenta, el or­que­s­ta­dor inicia co­n­te­ne­do­res adi­cio­na­les. Este enfoque permite que los servicios se amplíen rá­pi­da­me­n­te como respuesta a las co­n­di­cio­nes ca­m­bia­n­tes.

Ventajas e in­co­n­ve­nie­n­tes de la vi­r­tua­li­za­ción con co­n­te­ne­do­res de Docker

Las ventajas de vi­r­tua­li­zar con Docker deben co­n­si­de­rar­se sobre todo con respecto al uso de máquinas virtuales (VM). En co­m­pa­ra­ción con las máquinas virtuales, los Docker Container son mucho más ligeros. Pueden iniciarse con mayor rapidez y consumir menos recursos. Las imágenes en las que se basan los Docker Container también son órdenes de magnitud más pequeñas. Mientras que las imágenes de las máquinas virtuales suelen tener un tamaño de entre cientos de MB y varios GB, las imágenes de Docker empiezan con unos pocos MB.

Sin embargo, la vi­r­tua­li­za­ción de co­n­te­ne­do­res con Docker también tiene algunas de­s­ve­n­ta­jas. Como un co­n­te­ne­dor no tiene un sistema operativo propio, el ai­s­la­mie­n­to de los procesos que se ejecutan en él no es del todo perfecto. Usar un gran número de co­n­te­ne­do­res conlleva una gran co­m­ple­ji­dad. Además, Docker es un sistema evo­lu­cio­na­do, y la pla­ta­fo­r­ma Docker hace demasiado, por lo que cada vez se hacen más esfuerzos para dividir los co­m­po­ne­n­tes in­di­vi­dua­les.

Ir al menú principal