Saltar al contenido

Archivos de categoría Docker

Automatización de despliegues en Docker

Docker Orchestration: Automatización de despliegues en Docker

Automatización de despliegues en DockerLa automatización de despliegues en Docker también se denomina “Docker Orchestration. Hasta ahora hemos visto que con Docker puede ejecutar una sola instancia de la aplicación con un simple comando “Docker run” (ver Práctica con el comando run de Docker). Por ejemplo, para ejecutar una aplicación basada en node js, está en el comando docker run node js. Pero esa es solo una instancia de la aplicación en un host docker. Si aumenta el número de usuarios y esa instancia ya no puede manejar la carga, se implementa una instancia adicional ejecutando el comando docker run varias veces. Es algo que tiene que hacerse manualmente. Se debe vigilar de cerca la carga y el rendimiento de la aplicación e implementar instancias adicionales. Y si un contenedor fallara, hay que ser capaz de detectarlo y ejecutar el comando docker run nuevamente para implementar otra instancia de esa aplicación. 

Pero, si el host falla y es inaccesible entonces los contenedores alojados en ese host también se vuelven inaccesibles. Para resolver este problema se necesitaría un ingeniero dedicado que pueda sentarse y monitorear el funcionamiento y el estado de los contenedores y tomar las medidas necesarias para remediar la situación. Pero cuando se tienen implementadas grandes aplicaciones con decenas de miles de contenedores, ese no es un enfoque práctico.

Solución

Por lo tanto, para la automatización de despliegues en Docker se pueden crear scripts para ayudar a abordar estos problemas hasta cierto punto. La orquestación de contenedores es solo una solución para eso. Es una solución que consiste en un conjunto de herramientas y scripts que pueden ayudar a alojar contenedores en un entorno de producción. Por lo general, una solución de orquestación de contenedores consta de múltiples hosts Docker que pueden alojar contenedores de esa manera. Si uno falla, se podría acceder a través de los demás. Una solución de organización de contenedores le permite implementar fácilmente cientos o miles de instancias de una aplicación con un solo comando. Este es un comando utilizado para el enjambre Docker. 

Analizaremos el comando en sí un poco. Algunas soluciones de orquestación pueden ayudar a aumentar automáticamente el número de instancias. Cuando los usuarios aumentan y reducir el número de instancias cuando la demanda disminuye. Algunas soluciones incluso pueden ayudar a agregar automáticamente hosts adicionales para admitir la carga del usuario y no solo a agrupar y escalar las soluciones de orquestación de contenedores. También brinda soporte para la creación de redes avanzadas entre los contenedores en diferentes hosts, así como equilibrar la carga de las solicitudes entre los diferentes contenedores. También brindan soporte para compartir almacenamiento entre el host, así como soporte para la gestión de la configuración y la seguridad dentro del clúster.

Aplicaciones para la automatización de despliegues en Docker

Hoy en día, existen múltiples soluciones de orquestación de contenedores. Docker tiene Docker Swarm, Google los Kubernetes o Apache ofrece MESOS. Aunque Docker Swarm es realmente fácil de configurar y de iniciar, carece de algunas de las funciones avanzadas de escalado automático requeridas para aplicaciones complejas de gran producción. Por otro lado, MESOS es bastante difícil de configurar y de iniciar, pero admite muchas características avanzadas. Kubernetes podría decirse que es el más popular de todos, aunque es un poco difícil de configurar y de iniciar, ofrece muchas opciones para personalizar las implementaciones y tiene soporte de muchos proveedores diferentes, ya que es compatible con todos los proveedores de servicios en la nube como GCP, Azure y AWS. Es uno de los proyectos mejor clasificados en github.

Docker Swarm

Docker Swarn tiene muchos conceptos que cubrir y requiere su propio artículo, pero vamos a explicar rápidamente algunos de los detalles básicos para tener una breve idea de lo que es.

Puede combinar varias máquinas Docker en un solo cluster. Se encarga de la automatización de despliegues en Docker distribuyendo sus servicios y sus instancias de aplicación en hosts separados para una alta disponibilidad y para el equilibrio de carga en diferentes sistemas y hardware. Para configurar Docker Swarm primero se debe tener varios hosts con Docker instalados. Luego se debe designar a uno de ellos para que sea el gerente, el maestro o el administrador de Swarm y otros como esclavos o trabajadores. Una vez hecho se ejecuta el comando “docker swarm init” en el administrador de Swarm para que se inicialice el administrador. La respuesta de ese comando proporcionará la orden que se debe ejecutar en los trabajadores para unirse al administrador.

Después de unirse al cluster, los trabajadores (también conocidos como nodos) estarán listos para crear servicios e implementarlos en el clúster de Swarm ejecutando el comando “docker run” específico. Esto crea una nueva instancia de contenedor de la aplicación.

Automatización

Para ejecutar varias instancias de un servidor sería ejecutar el comando “docker run” en cada nodo de trabajo, pero eso no es lo ideal, ya que podría tener que iniciar sesión en cada nodo. Además tendría que configurar el balanceo de carga y monitorizar el estado de cada instancia. Por último, si las instancias fallan, hay que reiniciarlas a mano. Sería una tarea imposible, por lo que la orquestación de Docker Swarm hace todo esto por nosotros. 

El componente clave de la orquestación de Swarm es el servicio Docker. Es una o más instancias de una sola aplicación o servicio que se ejecuta en todo el sitio, en todos los nodos en el clúster de Swarmbre. Se podría crear un servicio Docker para ejecutar varias instancias de un servidor web en todos los nodos de trabajo en mi clúster de Swarm. Para esto, se ejecuta el comando “docker service create –replicas=<N> web-server” en el nodo del administrador.

Como se ve, se especifica el nombre de la imagen y se usa la opción “replicas” para especificar el número de instancias del servidor web que deben ejecutarse. Con esto se obtienen instancias del servidor web distribuidas en los diferentes nodos de trabajo. El comando “service” de creación del servicio es similar al comando de ejecución “run”, ya que tiene las mismas opciones, como la variable de entorno -e, -p para publicar puertos, la opción de red para conectar el contenedor a una red, etc. 

Kubernetes

Con Docker se puede ejecutar una sola instancia de una aplicación usando comando “docker run”. Con Kubernetes, usando su linea de comando, conocida como control de kube. Ayuda a la automatización de despliegues en Docker ejecutando mil instancias de la misma aplicación con un solo comando (kubectl run –replicas=1000 web-server). Puede ampliarlo hasta dos mil o que lo haga automáticamente. Las instancias y la infraestructura se pueden ampliar y reducir en función de la carga del usuario. Se pueden acumular instancias de la aplicación de forma continua, una a una con un solo comando. Si algo sale mal, puede ayudar a revertir las imágenes con un solo comando. Kubernetes puede ayudar a probar nuevas características de una aplicación actualizando solo un porcentaje de las instancias creando nodos de prueba. 

La arquitectura Kubernetes proporciona soporte para muchas redes diferentes y el almacenamiento cubre cualquier estructura de almacenamiento. Admite muchas variedades de mecanismos de autenticación y autorización. Los principales proveedores de servicios en la nube tienen soporte nativo para Kubernetes. Los usuarios de Kubernetes usan Docker para alojar aplicaciones en forma de contenedores. Pero no tiene por qué ser Docker, ya Kubernetes admite como nativos otros como un Rocket o CRI-O.

Arquitectura

La arquitectura de Kubernetes es un clúster compuesto por un conjunto de nodos. Un nodo es una máquina física o virtual en la que se instala un conjunto de herramientas de Kubernetes. El nodo es una máquina de trabajo y es donde Kubernetes lanzará los contenedores. Si el nodo en el que el la aplicación se está ejecutando falla, la aplicación deja de funcionar, por lo que se debe tener más de 1 nodo. Un clúster es un conjunto de nodos agrupados, si un nodo falla, la aplicación es accesible desde otros nodos.

El responsable de administrar este clúster es quien tiene la información sobre el resto de los miembros. Es quien monitoriza los nodos y quien mueve la carga de trabajo. Este es el master. El master es un nodo con los componentes de control de Kubernetes instalados. El master vigila los nodos en el clúster y es responsable de la orquestación real de los contenedores.

Cuando se instala Kubernetes en un sistema, en realidad está instalando los siguientes componentes: un servidor API, un servidor etcd, un servicio kubelet, un motor de tiempo de ejecución de contenedores (Docker), un grupo de controladores y el planificador (Scheduler).

Componentes

El servidor API actúa como interfaz para Kubernetes. La línea de comandos hablan con el servidor API para interactuar con el clúster de componentes. El etcd es un almacén de valores de clave distribuido utilizado por Kubernetes para almacenar todos los datos utilizados para administrar el clúster. Cuando hay múltiples nodos y múltiples maestros en un clúster, etcd almacena toda esa información en todos los nodos en el clúster de manera distribuida. etcd es responsable de implementar registros dentro del clúster para garantizar que no haya conflictos entre los maestros.

El planificador es responsable de distribuir el trabajo de los contenedores en múltiples nodos, busca contenedores recién creados y los asigna a los nodos. Los controladores son el cerebro detrás de la orquestación, son responsables de responder cuando los contenedores de nodos caen. Los controladores toman decisiones para abrir nuevos contenedores en esos casos. El motor de tiempo de ejecución (runtime container) es el software que se utiliza para ejecutar contenedores, que, en nuestro en caso, es Docker. Finalmente kubelet es el agente que se ejecuta en cada nodo del clúster y es el responsable de asegurarse de que los contenedores se estén ejecutando en las nodos tal como se esperaba.

La utilidad de línea de comando conocida como kube, ya que también es una herramienta de control. kube se usa para implementar y administrar aplicaciones en un clúster de Kubernetes, para obtener información relacionada con el clúster, para obtener el estado de los nodos en el clúster y muchas otras cosas. 

El comando “kubectl run <aplicacion>” se usa para implementar una aplicación en el clúster, el comando “kubectl cluster-info” se usa para ver información sobre el clúster y el comando “kubectl get nodes” muestra los nodos que forman parte del clúster.

Hay más información en la web de Kubernetes: https://kubernetes.io/es/

 

0 Seguir leyendo →

Docker en windows

Docker windowsDocker en Windows

Como hemos visto en Docker Compose, los contenedores comparten el núcleo del sistema operativo y, como resultado, no podemos tener un contenedor de Windows ejecutándose en el host de Linux o viceversa. Tenemos que tener esto en cuenta, ya que es un concepto muy importante y la mayoría de los principiantes tienden a tener un problema con él. 

Opciones ara Windows

Hay dos opciones para ejecutar Docker en Windows. Una de ellas es utilizar la caja de herramientas Docker y otra es el escritorio de Docker para Windows.

Docker Linux sobre Windows

Este fue el soporte original para Docker en Windows. Si se tiene un portátil con Windows sin acceso a ningún sistema Linux, lo que se hace es instalar un software de virtualización. Hay varias opciones como Oracle VitualBox o VMWear workstation. Se implementa una máquina virtual Linux con Ubuntu o Debian y luego se instala Docker en la máquina virtual. Realmente no tiene mucho que ver con Windows ya que no se pueden crear imágenes Docker basadas en Windows ni ejecutar contenedores Docker basados en Windows. 

Docker para Windows nos proporciona un conjunto de herramientas para facilitar las anteriores tareas. Se denomina Docker toolbox y contiene un conjunto de herramientas como la Máquina Virtual de Oracle, el motor Docker, la máquina Docker Composer y una interfaz de usuario llamada Katematic. Esto ayuda a comenzar, de una forma rápida con Docker en Windows. La máquina virtual implementa una máquina virtual liviana llamada Boot to Docker que ya tiene Docker internamente.

Los requisitos necesarios son un Sistema operativo Windows 7 de 64 bits o superior y que la virtualización esté habilitada en el sistema. Recuerde que Docker toolbox es una solución heredada para todos los sistemas de Windows que no cumplen con los requisitos para ejecutar la opción de Docker para Windows.

Otra opción es eliminar Oracle Virtualbox y usar la tecnología de virtualización nativa de Windows llamada Microsoft Hyper V. También creará automáticamente un sistema Linux debajo, pero esta vez encima de Microsoft Hyper V en lugar de Oracle Virtual Box. Debido a esta dependencia de Hyper v, esta opción solo es compatible con Windows 10 Enterprise Professional Edition o en Windows Server 2016 porque ambos sistemas operativos vienen con soporte Hyper V de forma predeterminada.

Docker Desktop

Hasta ahora no hemos hablado de aplicaciones Windows o imágenes de Windows o contenedores de Windows. Las dos opciones que acabamos de comentar ayudan a ejecutar un contenedor de Linux en un host de Windows. Por primera vez, ahora se puede empaquetar aplicaciones de aplicaciones de Windows en contenedores de Docker de Windows y ejecutarlas en el host de Docker de Windows usando Docker Desktop para Windows. 

Para hacerlo se debe configurar explícitamente Docker para que Windows cambie a usar contenedores de Windows. Con esto se pueden crear imágenes basadas en Windows y ejecutar contenedores de Windows en un servidor de Windows tal como lo haría con contenedores de Linux en un sistema Linux. También se pueden crear aplicaciones de contenedor de imágenes de Windows y compartirlas a través de la tienda Dockers.

A diferencia de Linux, hay dos tipos de contenedores en Windows. El primero es un contenedor de servidor de Windows que funciona exactamente como los contenedores de Linux. El núcleo del sistema operativo se comparte con el sistema operativo subyacente para permitir una mejor frontera de seguridad entre los contenedores y permitir que coexistan núcleos con diferentes versiones y configuraciones. La segunda opción se conoce como aislamiento Hiper V. Cada contenedor se ejecuta dentro de una máquina virtual altamente optimizada que garantiza el aislamiento completo del núcleo entre los contenedores y el host subyacente.

Imagenes

En el mundo Linux se tenían varias imágenes base para sistemas Linux como Ubuntu, Debian fedora Alpine, etc. Eso es lo que especifica al principio del archivo acoplable. En Windows se tienen solo dos opciones: Windows Server Core y Nano Server. Un Nano Server es una opción de implementación para Windows Server que se ejecuta en una fracción del tamaño del sistema operativo completo. Puede pensar que es como la imagen de Alpine en Linux. Los contenedores de Windows son compatibles con Windows Server 2016, Nano Server y Windows 10 Professional y Enterprise Edition. Recuerde que en Windows 10 Professional y Enterprise Edition solo admite contenedores aislados de Hyper V, lo que significa que cada contenedor implementado se implementa en una máquina virtual altamente optimizada.

Hay que señalar que las dos opciones no pueden coexistir en el mismo host de Windows. Si se inició con Docker Toolbox y se quiere migrar a Hyper V hay una migración y una guía disponibles en la página de documentación de Docker.

Para más información puede acudir a https://docs.docker.com/docker-for-windows/

Docker en Mac

Para Mac es similar a Docker en Windows. Docker Toolbox o una opción de escritorio.

Docker Toolbox es un Docker Linux usando Virtualbox en Mac. Simplemente ejecuta contenedores Linux en una caja de herramientas Docker en Mac OS. Contiene un conjunto de herramientas como Oracle Virtualbox, un motor Docker, Docker Compose y una interfaz de usuario llamada kitematic. Cuando se descarga e instala el ejecutable de la caja de herramientas Docker, se instala la caja virtual ligera llamada boot Docker que ya tiene Docker. Esto requiere Mac OS 10.8 o más reciente.

Otra opción más nueva llamada desktop, es como wn Windows, se saca Oracle Virtualbox y se utiliza la tecnología de virtualización de Hyper Kit durante el proceso de instalación para Mac. Creará automáticamente un sistema Linux debajo, pero esta vez se crea en un Hyperkit en lugar de Oracle Virtualbox. Esto requiere Mac OS Sierra 10.12 o más reciente y el hardware de Mac debe ser de 2010 o un modelo más nuevo.

Recuerde que todo esto es para poder ejecutar el contenedor Linux en Mac, no hay imágenes o contenedores basados en Mac.

0 Seguir leyendo →

Redes en Docker

Creación de redes en Docker.

Redes de DockerCuando se instala Docker, se crean tres redes: Bridge, none y host. Bridge es la red predeterminada a la que se conecta un contenedor. Si se desea asociar el contenedor con cualquier otra red, hay que especificar la información de la red utilizando el parámetro de línea de comando “–network”: “docker run –network=none ubuntu” o “docker run –network=host ubuntu”

La red “bridge” es una red interna privada creada por Docker en el host. Todos los contenedores se conectan a esta red de forma predeterminada y obtienen una dirección IP interna,  generalmente en el rango 172.17.0.*. Los contenedores pueden acceder entre sí utilizando esta IP interna. Para acceder a cualquiera de estos contenedores desde el mundo exterior hay que mapear los puertos de estos contenedores a los puertos de nuestro host tal como vimos en Opciones del comando run de Docker

Otra forma de acceder a los contenedores externamente es asociar el contenedor a la red “host”. Esto elimina cualquier aislamiento de red entre el host de Docker y el contenedor de Docker. Es decir, si tuviera que ejecutar un servidor web en el Puerto 5000 en un contenedor web, es automáticamente accesible en el mismo puerto externamente sin requerir ninguna asignación de puertos, ya que el contenedor web utiliza la red “host”. Esto también significaría que, a diferencia de antes, ahora no podrá ejecutar varios contenedores web en el mismo host en el mismo puerto, ya que los puertos ahora son comunes a todos los contenedores en la red host con ninguna red. Con la red “none” los contenedores no se asocian a ninguna red y no tienen acceso a la red externa u otros contenedores. Se ejecutan en una red aislada. 

Aislamiento de redes

Acabamos de ver que la red “bridge” es la predeterminada y usa el rango 172.17. 0.*, por lo tanto, todos los contenedores asociados a esta red predeterminada podrán  comunicarse entre sí. Pero, ¿qué sucede si deseamos aislar los contenedores dentro del host de Docker, por ejemplo, los dos primeros contenedores web en la red interna 172 y otros contenedores en una red interna diferente como 182. Por defecto Docker solo crea una red “bridge” interna, pero podríamos crear nuestra propia red interna usando el comando “docker network create” y especificar que el controlador es “bridge”, la subred  y el nombre personalizado de la red aislada: “docker network create –driver bridge –subnet 182.18.0.0/16 mi-red-aislada”. Con el comando “docker network ls” para enumerar todas las redes. 

Información

¿cómo podemos ver la configuración de red y la dirección IP asignada a un contenedor existente? con el comando “docker inspect <nombre del contenedor>” para ver una sección sobre la configuración de la red. Allí puede ver el tipo de red que el contenedor está conectado, su dirección IP interna, la dirección MAC y otros… 

Los contenedores pueden comunicarse entre sí utilizando sus nombres. Por ejemplo, en caso de tener un servidor web y un contenedor de base de datos MySQL ejecutándose en el mismo nodo, ¿cómo puedo hacer que mi servidor web acceda a la base de datos en el contenedor de la base de datos? Una cosa que podría hacer es usar la dirección IP interna asignada al contenedor MySQL que podría ser la 172.17.0.3, pero eso no es muy bueno porque no se garantiza que el contenedor obtenga la misma IP cuando el sistema se reinicie. La forma correcta de hacerlo es usar el nombre del contenedor.

Todos los contenedores en un host Docker pueden resolverse entre sí con el nombre del contenedor. Docker tiene un servidor DNS incorporado que ayuda a los contenedores a resolverse entre sí utilizando el nombre del contenedor. Hay que tener en cuenta que el servidor DNS incorporado siempre se ejecuta en la dirección 127.0.0.11

Entonces, ¿cómo implementa Docker las redes? ¿Cuál es la tecnología detrás de esto? ¿Cómo se aislan los contenedores dentro de la red de usuarios de Docker host?

Docker usa Nombre de espacios de red. Eso crea un espacio de nombres separado para cada contenedor. Luego usa pares virtuales de Ethernet para conectar contenedores. Pero eso es para un uso avanzado.

En la página de Docker se puede encontrar más información: https://docs.docker.com/network/

0 Seguir leyendo →

Sistema de archivos de Docker

Sistema de archivos de Docker

docker sistema de archivosEn la Práctica básica con Docker vimos los elementos básicos de Docker, así que ahora vamos a hablar sobre como se almacenan esos elementos: los controladores de almacenamiento y el sistema de archivos de Docker. Vamos a ver dónde y cómo Docker almacena datos y cómo administra los sistemas de archivos de los contenedores. 

Comencemos con cómo Docker almacena datos en el sistema de archivos local. Cuando se instala Docker en un sistema, crea una estructura de carpetas en var/lib/docker  que tiene varias carpetas debajo: aufs, containers, image, volumes, etc. 

Aquí es donde Docker almacena todos sus datos de manera predeterminada. Cuando digo datos, me refiero a archivos relacionados con imágenes y contenedores que se ejecutan en el host de Docker. Por ejemplo, todos los archivos relacionados con contenedores se almacenan en la carpeta de contenedores y los archivos relacionados con imágenes se almacenan en la carpeta de imágenes. Y todos los volúmenes creados por los contenedores Docker se crean en la carpeta de volúmenes.

Almacenamiento de las capas

En Creación de imágenes para Docker hemos visto que cuando Docker construye imágenes, las construye en una arquitectura en capas. Cada línea de instrucciones en el archivo Docker crea una nueva capa en la imagen Docker con solo los cambios de la capa anterior. Por ejemplo, para esta composición: 

FROM ubuntu
RUN apt-get update && apt-get -y install python
RUN pip install flask flask-mysql
COPY . /opt/source-code
ENTRYPOINT FLASK_APP=/opt/source-code/app.py flask run 

La primera capa es un sistema operativo Ubuntu base seguido de la segunda instrucción que crea una segunda capa que instala todos los paquetes APT. Y luego, la tercera instrucción crea una tercera capa que con los paquetes de Python seguida de la cuarta capa que copia el código fuente. Y finalmente, la quinta capa que actualiza el punto de entrada de la imagen.

Ya que cada capa solo almacena los cambios de la capa anterior, también se refleja en el tamaño. Si miras en el sistema de archivos de Docker, la base de la imagen, tiene alrededor de 120 megabytes de tamaño. Los paquetes apt que se instalan son de alrededor de 300 MB y luego las capas restantes son más pequeñas.

Veamos una segunda aplicación:

FROM ubuntu
RUN apt-get update && apt-get -y install python
RUN pip install flask flask-mysql
COPY app2.py /opt/source-code
ENTRYPOINT FLASK_APP=/opt/source-code/app2.py flask run

Esta aplicación tiene un archivo acoplable diferente pero es muy similar a nuestra primera aplicación ya que usa la misma imagen base que un ubuntu y usa las mismas dependencias de python y flask, pero usa un código fuente diferente para crear una aplicación diferente y también un punto de entrada diferente. Cuando ejecuto el comando docker build para crear una nueva imagen para esta aplicación, cómo las tres primeras capas de ambas aplicaciones son las mismas, Docker no va a construir las tres primeras capas. Reutiliza las mismas tres capas que creó para la primera aplicación del caché y solo crea las dos últimas capas con las nuevas fuentes y el nuevo punto de entrada. De esta manera Docker construye imágenes más rápido y ahorra espacio en disco. 

Esto también es aplicable si actualiza su código de aplicación. Cada vez que actualiza su código de aplicación, Docker reutiliza todas las capas anteriores de la memoria caché y reconstruye rápidamente la imagen de la aplicación actualizando el último código fuente, lo que nos ahorra mucho tiempo durante las reconstrucciones y actualizaciones. 

Capas en una aplicación propia

Si leemos las capas de abajo hacia arriba podemos entenderlo mejor. En la base tendremos la capa de ubuntu, encima los paquetes, luego las dependencias y luego el código fuente de la aplicación y por último el punto de entrada. Todas estas capas se crean cuando ejecutamos el comando “docker build” para formar la imagen final de Docker. Una vez que se completa la compilación, no se puede modificar el contenido de estas capas, por lo que son de solo lectura y solo puede modificarlas iniciando una nueva compilación. 

Cuando se ejecuta un contenedor basado en la imagen creada (usando el comando “docker run”), Docker crea un contenedor basado en estas capas y crea una nueva capa en la parte superior. En esta capa sí es capaz de escribir y se utiliza para almacenar datos creados por el contenedor, como los archivos de registro de las aplicaciones o cualquier archivo temporal generado por el contenedor o simplemente cualquier archivo modificado por el usuario en ese contenedor. La vida de esta capa es solo mientras el contenedor esté vivo. Cuando el contenedor se destruye esta capa y todos los cambios almacenados en él también se destruyen. Recuerde que todos los contenedores creados con esta imagen comparten la misma capa de imagen.  

Si tuviera que iniciar sesión en el contenedor recién creado y decide crear un nuevo archivo llamado temp.txt, crearía ese archivo en la capa contenedor que es de lectura y escritura. Acabamos de decir que los archivos en la capa de imagen son de solo lectura, lo que significa que no puede editar nada en esas capas.

Ejemplo

Tomemos un ejemplo de nuestro código de aplicación. Ya que compilamos nuestro código Python en la imagen, el código es parte de la capa de imagen y, como tal, es de solo lectura. Esto no significa que no pueda modificar el código dentro del contenedor creado. Antes de crear la capa del contenedor, Docker crea automáticamente una copia del archivo en la capa de lectura y escritura, por lo que es posible modificar el archivo en la capa de lectura y escritura. Todas las modificaciones futuras se realizarán en esta copia del archivo en la capa de lectura y escritura. Esto se llama mecanismo de copia en escritura. Esto significa que los archivos en estas capas no se modificarán en la imagen, por lo que la imagen permanecerá igual todo el tiempo hasta que reconstruya la imagen usando el comando de construcción de docker.

Lo que sucede cuando nos deshacemos del contenedor, es que todos los datos almacenados en la capa del contenedor también se eliminan. El cambio que hicimos en la aplicación Python y el nuevo archivo temporal que creamos también se eliminarán.

Montaje de volumen del sistema de archivos de Docker

Si estuviéramos trabajando con una base de datos y quisiéramos preservar los datos creados por el contenedor, podríamos agregar un volumen persistente al contenedor. Para hacer esto primero crear un volumen usando el comando “docker volume create <nombre>”. Entonces, si ejecutamos el comando “docker volume create data_volume”, se crea una carpeta llamada “data_volume” en el directorio “var/lib/docker/volumes”.  Luego, cuando ejecuto el contenedor Docker usando el comando “docker run”, podría montar este volumen dentro de la capa de lectura y escritura de los contenedores usando la opción -v: “docker run -v data_volume:/var/lib/mysql mysql”

Con esto, Docker creará un nuevo contenedor y montará el volumen de datos que creamos, en la carpeta var/lib/mysql dentro del contenedor para que todos los datos escritos por la base de datos se almacenen en el volumen creado en el host Docker. Incluso si el contenedor se destruye, los datos siguen activos. Si no se ejecuta el comando “docker volume create” para crear el volumen antes del comando “docker run”, por ejemplo, “docker run -v data_volume2:/var/lib/mysql mysql”, Docker creará automáticamente un volumen denominado data_volume2 y lo montará en el contenedor. Esto se llama montaje de volumen.

Montaje de unión del sistema de archivos de Docker

Si ya tuviéramos nuestros datos en otra ubicación, por ejemplo, en un almacenamiento externo del host Docker llamado /data y nos gustaría almacenar datos de la base de datos en ese volumen y no en la carpeta predeterminada. En ese caso, ejecutaríamos un contenedor con el comando “docker run -v”. Pero en este caso proporcionaremos la ruta completa a la carpeta que nos gustaría montar. Es decir /data/mysql, por lo que creará un contenedor y montará la carpeta en el contenedor. Esto se llama montaje de unión.

Por lo tanto, hay dos tipos de montajes para el sistema de archivos de Docker: un montaje de volumen y un montaje de volumen. El montaje de volumen monta un volumen del directorio de volúmenes y el montaje de unión monta un directorio desde cualquier ubicación en el host Docker. Un último punto a tener en cuenta es que -v es un estilo antiguo. La nueva forma es usar la opción de montaje –mount. Esta es la forma preferida, ya que es más detallado. Por lo tanto, debe especificar cada parámetro en un formato de clave igual a valor: “docker run –mount type=bind,source=/data/mysql,target=/var/lib/mysql mysql”

Controladores

Quien es el responsable de realizar esta operaciones: Mantener la arquitectura en capas, crear una capa de escritura, mover archivos a través de capas, copiar y escribir, etc. Son los controladores de almacenamiento. Dockery utiliza controladores de almacenamiento para habilitar la arquitectura en capas. Algunos de los controladores de almacenamiento comunes son AUFS, ZFS, BTRFS, Device mapper, Overlay, Overlay2… Depende del sistema operativo subyacente que se utilice, por ejemplo, con Ubuntu el controlador de almacenamiento predeterminado es un AUFS, mientras que ese tipo de controlador no está disponible en otros sistemas operativos como fedora o centOS. En esos casos, el Device Mapper puede ser una mejor opción. Docker elegirá el mejor controlador disponible en función del sistema operativo. 

Más información en la página de Docker https://docs.docker.com/storage/

0 Seguir leyendo →

Motor de Docker

Motor de Docker

Estructura del motor de Docker
En este artículo, veremos más de cerca la arquitectura de Docker, cómo ejecuta aplicaciones en contenedores aislados y cómo funciona internamente cuando hicimos la Práctica con el comando run de Docker. Cuando hablamos del motor Docker, simplemente nos referimos a un host con Docker instalado en él. Cuando se instala Docker en un host de Linux, en realidad está instalando tres competencias diferentes.

El demonio Docker: Es un proceso en segundo plano que gestiona los objetos Docker, como los volúmenes y las redes de los contenedores de imágenes.

El servidor REST API: Es la interfaz API que los programas pueden usar para hablar con el demonio y proporcionar instrucciones. Se puede crear nuestras propias herramientas utilizando esta API de REST.

La CLI de docker: Es la interfaz en línea de comandos que hemos estado utilizando hasta ahora para realizar acciones como ejecutar un contenedor, detener contenedores que destruyen imágenes, etc. Utiliza la API de REST para interactuar con el demonio de Docker. 

Algo que hay que tener en cuenta aquí es que la CLI de Dockers no necesita estar necesariamente en el mismo host. Podría estar en otro sistema como una computadora portátil y aún puede funcionar con un motor Docker remoto. Solo hay que usar la opción -H en el comando docker y especificar la dirección remota del motor Docker y el puerto (“docker -H=remote-docker-engine:2375 <comandos>”)

Estructura

Docker usa el espacio de nombres para aislar los espacios de trabajo, procesos, comunicaciones, sistemas de tiempo de Unix… cada uno crea en su propio espacio de nombres, proporcionando así aislamiento entre los contenedores. 

Cada vez que se inicia un sistema Linux, comienza con un proceso con el ID 1. Este es el proceso raíz y comienza todos los demás procesos en el sistema. Cuando el sistema se detiene por completo, tenemos un puñado de procesos que están ejecutándose. Esto se puede ver ejecutando el comando “ps” para enumerar todos los procesos en ejecución. Los IDs de procesos son únicos y dos procesos no pueden tener el mismo proceso ID. 

Si creamos un contenedor, que es básicamente un sistema secundario dentro del sistema actual, el sistema secundario debe pensar que es un sistema independiente y que tiene su propio conjunto de procesos empezando con un ID 1, pero sabemos que no existe un aislamiento sólido entre los contenedores y el host subyacente. Así, los procesos que se ejecutan dentro del contenedor son, de hecho, procesos que se ejecutan en el host subyacente. Y sabemos que dos procesos no pueden tener el mismo ID de proceso.

Aquí es donde entra en juego el espacio de nombres. Cada proceso puede tener múltiples id de proceso asociadas. Por ejemplo, cuando los procesos comienzan en el contenedor, en realidad es solo otro conjunto de procesos en el sistema Linux base y obtiene el siguiente ID de proceso disponible. Sin embargo, también obtienen otro ID de proceso comenzando con 1 en el espacio de nombres del contenedor, que solo es visible dentro del contenedor, por lo que el contenedor tiene su propio árbol de proceso raíz y, por lo tanto, es un sistema independiente.

Relación con un sistema real

Digamos que ejecutamos un contenedor nginx. Sabemos que nginx y su contenedor ejecutan un servicio nginx. Si enumeramos todos los servicios dentro del contenedor docker con un comando como “docker exec <ID> ps -eaf”, veríamos que el servicio nginx se ejecuta con una ID de proceso de 1. Este es el proceso del servicio dentro del espacio de nombres del contenedor.  Si enumeramos los servicios en el host de Docker con una orden “ps -eaf”, veremos el mismo servicio pero con una ID de proceso diferente que indica que todos los procesos se están ejecutando en el mismo host pero separados en sus propios contenedores usando el espacio de nombres.

Por lo que vemos, el Docker subyacente y los contenedores comparten los mismos recursos del sistema, como la CPU y la memoria. La cantidad de recursos dedicados al host y a los contenedores y cómo Docker administra y comparte los recursos entre los contenedores de manera predeterminada es sin restricciones. Por lo tanto, un contenedor puede terminar utilizando todos los recursos en el host subyacente. 

Pero hay una manera de restringir la cantidad de CPU o memoria que un contenedor puede usar. Docker usa tres grupos o grupos de control para restringir la cantidad de recursos de hardware asignados a cada contenedor. Esto se puede hacer proporcionando la opción –cpus en el comando “run”. Un valor de punto cinco asegurará que el contenedor no ocupe más del 50 por ciento de la CPU en cualquier momento dado. Lo mismo ocurre con la memoria. Establecer un valor de 100 megabytes para la opción –memory limita la cantidad de memoria que el contenedor puede usar a 100 megabytes.

“docker run –cpus=.5 –memory=100m ubuntu”

Puedes ver más información en la página https://docs.docker.com/config/containers/resource_constraints/

 

0 Seguir leyendo →

Docker Compose

Docker Compose

En el artículo Creación de imágenes para Docker  vimos como crear imágenes para Docker, pero si necesitáramos configurar una aplicación compleja que ejecuta múltiples imágenes, la mejor manera de hacerlo es usar Docker Compose. Con Docker Compose podríamos crear un archivo de configuración en formato yaml y juntar los diferentes servicios y las opciones específicas para ejecutarlos de una sola vez. Como todos los cambios se almacenan en un archivo de configuración, la ejecución solo es aplicable a contenedores en un único host de Docker. Docker Compose no se instala de forma predeterminada, por lo que hay que instalarlo según indica en la documentación https://docs.docker.com/compose/install/

Vamos a usar la misma aplicación que todos usan para demostrar Docker. Es una aplicación simple pero completa desarrollada por Docker para demostrar las diversas características disponibles en la ejecución de una pila de aplicaciones en Docker. Así que primero nos familiarizamos con la aplicación porque estaremos trabajando con la misma aplicación en diferentes secciones durante el resto de entradas. Puede acceder a su repositorio en https://github.com/dockersamples/example-voting-app 

Ejemplo

Es una aplicación de votación que proporciona una interfaz para que un usuario vote y otra interfaz para mostrar los resultados. La aplicación consta de varios componentes, como la aplicación de votación, que es una aplicación web desarrollada en Python para proporcionar al usuario una interfaz para elegir entre dos opciones: gato o perro.

Cuando realiza una selección, el voto se almacena en redis, una base de datos en la memoria. El voto es procesado por el “worker”, que es una aplicación escrita en .net. y que toma el nuevo voto y actualiza la base de datos persistente que es un SQL de Postgres que tiene una tabla con el número de votos para cada categoría. El resultado de la votación se muestra en una interfaz web que es otra aplicación web desarrollada en nodeJS que lee el recuento de votos de la base de datos PostgreSQL y lo muestra al usuario.

La aplicación está construida con una combinación de diferentes servicios, diferentes herramientas de desarrollo y múltiples plataformas, como Python, NodeJS, .Net, etc. Vamos a ver como esta aplicación se usará para mostrar cómo configurar una pila de aplicaciones completa que consiste en diversas competencias en Docker, nos permite mantener a un lado los servicios y las pilas. Veremos cómo podemos crear esta pila de aplicaciones en un solo motor Docker primero con los comandos de ejecución de Docker y luego con Docker-compose. Supongamos que todas las imágenes de las aplicaciones ya están compiladas y están disponibles en el repositorio de Docker. 

Construcción

Comencemos con la capa de datos primero, ejecutando el comando “docker run -d –name=redis redis” para iniciar una instancia de redis, agregando el parámetro -d para ejecutar este contenedor en segundo plano y también lo nombraremos, ya que es importante. En segundo lugar implementaremos la base de datos PostgreSQL ejecutando el comando “docker run -d –name=db postgres:9.4“ también con la opción -d y nombrándolo. A continuación comenzaremos con los servicios de la aplicación. Así que ejecutamos una aplicación front-end para la interfaz de votación ejecutando una instancia de imagen de la aplicación de votación con “docker run -d –name=vote -p 5000:80 voting-app” y asignando un nombre.

Ya que se trata de un servidor web, tiene una instancia que se ejecuta en el puerto 80 y que publicaremos en el puerto 5000 en el sistema host para que podamos acceder a él desde un navegador. A continuación, implementaremos la aplicación web de resultados que muestra los resultados al usuario. Para esto, implementamos un contenedor utilizando la imagen de la aplicación de resultados y publicamos el puerto 80 al puerto 5001 en el host para que de esta manera, podemos acceder a la interfaz de usuario web de la aplicación resultante en un navegador: “docker run -d –name=result -p 5001:80 result-app”. Finalmente implementamos el “worker” ejecutando “docker run -d –name=worker worker“

Ahora todo esta ejecutándose, pero no puede funcionar, ya que no hemos vinculado entre sí los diferentes contenedores. No hemos dicho a la aplicación web que use la instancia de redis, ya que podría haber múltiples redis ejecutándose. Y tampoco  hemos dicho al “worker” que use la base de datos PostgresSQL. Es por esto que usamos enlaces. 

Enlaces

Link es una opción de línea de comando que se puede usar para vincular dos contenedores. Por ejemplo, el servicio web de la aplicación de votación depende del servicio de “redis”, así que agregamos una opción link al ejecutar el contenedor de la aplicación de votación para vincularlo al contenedor de redis: “docker run -d –name=vote -p 5000:80 –link redis:redis voting-app”. Es por esto por lo que renombramos todos los contenedores, que en realidad está creando una entrada en el archivo /etc/hosts en el contenedor de la aplicación de votación que agrega una entrada con el nombre de host “redis” y una IP interna del contenedor de “redis”.

De manera similar, tendremos que agrega un enlace para que la aplicación resultante se comunique con la base de datos, ya que internamente intenta conectarse a la base de datos de un host llamado “db”. Así que agregamos una opción de enlace para referir la base de datos con el nombre db: “docker run -d –name=result -p 5001:80 –link db:db result-app”. Finalmente, la aplicación “worker”necesita acceso tanto a la redis como a la base de datos de Postgres, por lo que agregamos dos enlaces a la aplicación de trabajo, un enlace para vincular redis y el otro enlace para vincular la base de datos postgres: “docker run -d –name=worker –link db:db –link redis:redis worker”.

El uso de enlaces de esta manera está en desuso y el soporte se puede eliminar en el futuro. Esto se debe a que los conceptos avanzados y más nuevos de Docker-swarm y la creación de redes admiten mejores formas de lograr lo que acabamos de hacer aquí con enlaces, pero es bueno ver la base, ya que es fácil generar un docker para componer archivos a partir de estos conceptos.

Creación de fichero Yalm para Docker Compose

Para ello creamos un diccionario de nombres de contenedores. Vamos a usar los mismos nombres que usamos en los comandos de ejecución de Docker. Cogemos todos los nombres y creamos una clave con cada uno de ellos:

redis:
db:
vote:
result:
worker:

Luego, debajo de cada elemento, especificamos qué imagen usar. La clave es “image” y el valor es el nombre de la imagen que se usará a continuación. 

redis:
    image: redis
db:
    image: postgres:9.4
vote:
    image: voting-app
result:
    image: result-app
worker:
    image: worker

Inspeccione los comandos y vea cuáles son las otras opciones utilizadas. Publicamos puertos, así que vamos a mover esos puertos debajo de los contenedores respectivos. Entonces creamos una propiedad llamada “ports” y enumeramos todos los puertos que le gustaría publicar debajo de ella. 

redis:
    image: redis
db:
    image: postgres:9.4
vote:
    image: voting-app
    ports:
        – 5000:80
result:
    image: result-app
    ports:
        – 5001:80
worker:
    image: worker
Finalmente también ponemos los enlaces, por lo que a cualquier contenedor que requiera de enlaces debe tener una propiedad “links” y el listado de enlaces. Si el enlace y el nombre del host es el mismo, podemos crear el enlace sin los dos puntos: poner “redis” es similar a poner “redis:redis”.

redis:
    image: redis
db:
    image: postgres:9.4
vote:
    image: voting-app
    ports:
        – 5000:80
    links:
        – redis
result:
    image: result-app
    ports:
        – 5001:80
    links:
        – db
worker:
    image: worker
    links:
        – redis
        – db

Ya hemos terminado con nuestro archivo de compilación, solo hay que ejecutarlo con la orden “docker-compose up”. 

Mejoras

De los 5 componentes, 2 de ellas son imágenes que sabemos que ya están disponibles en Docker Hub (redis y postgres), pero las tres restantes son de nuestra propia aplicación. No tienen porque estar compilados y disponibles en el registro de Docker. Podemos enseñar a Docker como ejecutar la construcción en lugar de intentar extraer una imagen. Para esto podemos reemplazar la linea “image” con una línea “build” y especificar la ubicación de un directorio que contiene un archivo de Docker con instrucciones para compilar la imagen:

image: voting-app → build: ./vote
image: result → build: ./result
image: worker → build: ./worker

En este ejemplo, para la aplicación de votación, todo el código de la aplicación está en una carpeta llamada “vote” que contiene todo el código de la aplicación y el archivo Docker. Cuando se ejecute el comando “docker-compose up”, primero compilará las imágenes con un nombre temporal y luego usará esas imágenes para ejecutar contenedores utilizando las opciones que especificó antes.  

Ahora veremos diferentes versiones de Docker compose file. Esto es importante porque puede ver a Docker componer archivos en diferentes formatos en diferentes lugares y preguntarse por qué algunos se ven diferentes. Docker compose evolucionó con el tiempo y ahora admite muchas más opciones que al principio. Por ejemplo, esta es la versión recortada del archivo compuesto Docker que utilizamos anteriormente:

redis:
    image:redis
db:
    image: postgres:9.4
vote:
    image: voting-app
    ports:
        - 5000:8080
    links:
        - redis

De hecho, esta es la versión original del archivo compuesto Docker conocido como versión 1. Esto tenía una serie de limitaciones, por ejemplo, si desea implementar contenedores en una red diferente a la red puente predeterminada. No había forma de especificar que en esta parte del archivo también se dice que tiene una dependencia u orden de inicio de algún tipo. Por ejemplo, el contenedor de la base de datos debe aparecer primero y solo entonces se debe iniciar la aplicación de votación.

Versión 2

El soporte para estos vino en la versión 2. En la versión 2 hasta el formato del archivo también cambió un poco. Ya no especifica su información de pila directamente como lo hizo antes. Todo está encapsulado en la sección “services”, así que se crea una propiedad llamada “services” en la raíz del archivo y se mueve todos los servicios debajo

version: 2
services:
    redis:
        image:redis
    db:
        image: postgres:9.4
    vote:
        image: voting-app
        ports:
            – 5000:8080
        links:
            – redis

Para la versión 2 se debe especificar la versión en la parte superior del archivo para que docker-compose sepa que formato está utilizando, ya que la versión dos se diferencia en la creación de redes: en la versión uno docker-compose conecta todos los contenedores que ejecuta a una red puente predeterminada y luego usa enlaces como lo hicimos antes. Con la versión 2 docker-compose crea automáticamente un red puente dedicada para esta aplicación y luego conecta todos los contenedores a esa nueva red. Así todos los contenedores pueden comunicarse entre sí utilizando el nombre del servicio del otro. Así que básicamente no necesita usar enlaces en la versión 2 de docker-compose. Gracias a esto puede deshacerse de todos los enlaces de la version 1:

version: 2
services:
    redis:
        image:redis
    db:
        image: postgres:9.4
    vote:
        image: voting-app
        ports:
            – 5000:8080

Dependencia

Finalmente la versión 2 también presenta una función dependiente si desea especificar un orden de inicio. Por ejemplo, digamos que la aplicación web de votación depende del servicio de redis.Por lo tanto, debe asegurarse de que el contenedor de redis se inició primero y solo entonces se debe iniciar la aplicación web de votación. Así podríamos agregar que depende de la aplicación de redis. 

version: 2
services:
    redis:
        image:redis
    db:
        image: postgres:9.4
    vote:
        image: voting-app
        ports:
            – 5000:8080
        depends_on:
            – redis

Luego viene la versión 3, que es la última y que es similar a la versión 2. Esto significa que especifica la versión en la parte superior y una sección de servicios en la que coloca todos sus servicios al igual que en la versión 2. Hay que asegurarse de especificar el número de versión como 3. Viene con soporte para Docker Swarm.

Redes en Docker Compose

Hablemos sobre las redes en Docker Compose para volver a nuestra aplicación. Como hemos visto, hasta ahora hemos estado implementando todos los contenedores en la red puente  predeterminada. Supongamos que modificamos un poco la arquitectura para contener el tráfico de las diferentes fuentes. Por ejemplo, nos gustaría separar el tráfico generado por el usuario del tráfico interno de las aplicaciones, por lo que creamos una red front-end dedicada al tráfico de los usuarios y una red back-end dedicada al tráfico dentro de la aplicación. Luego conectamos las aplicaciones de cara al usuario, que son la aplicación de votación y la aplicación de resultados, la red frontend y toda la competencia a una red interna de back-end, así que de vuelta en nuestro archivo de redacción de Docker. Tenga en cuenta que en realidad he eliminado la sección de puertos por simplicidad, todavía están allí, pero simplemente no se muestran aquí.

Lo primero que debemos hacer si vamos a usar redes es definir las redes que vamos a usar. En nuestro caso dos redes, una front-end y otra back-end, así que creamos una nueva propiedad llamada “ntworks” en el nivel raíz:

version: 2
services:
    redis:
        image:redis
    db:
        image: postgres:9.4
    vote:
        image: voting-app
    result:
        image: result
networks:
    front-end:
    back-end:

Para cada servicio hay que crear una propiedad de redes y proporcionar una lista de redes a las que se debe conectar. También debe agregar una sección para el “worker”. 

version: 2
services:
    redis:
        image:redis
        networks:
            – back-end:
    db:
        image: postgres:9.4
        networks:
            – back-end:
    vote:
        image: voting-app
        networks:
            – front-end:
            – back-end:
    result:
        image: result
        networks:
            – front-end:
            – back-end:
    worker:
        image: worker
        networks:
            – front-end:
            – back-end:
networks:
    front-end:
    back-end:

0 Seguir leyendo →

Creación de imágenes para Docker

Creación de imágenes para Docker

imagenes dockerLa creación de imágenes para Docker puede tener diversas razones, ya sea porque no puede encontrar un componente o un servicio que desea usar como parte de su aplicación en Docker. También es posible que se la aplicación que se está desarrollando se tiene que acoplar para facilitar el envío y la implementación. Hasta ahora hemos utilizado Docker con imagenes ya creadas, como se vio en la Práctica con el comando run de Docker. Ahora vamos a crear una aplicación web simple con el Framekork de Python Flask.

Para la creación de imágenes para Docker primero debemos comprender para qué estamos dockerizando o para qué aplicación estamos creando una imagen y cómo se crea la aplicación. Comience por pensar qué podría hacer si desea implementar la aplicación manualmente. Anotamos los pasos necesarios en el orden correcto y creamos una imagen para una aplicación web simple. Si tuviera que configurarlo manualmente sería estos pasos:

  1. Comenzaría con un sistema operativo como un ubuntu
  2. Luego actualizaría los repositorios de origen utilizando el comando apt. 
  3. En tercer lugar instale las dependencias usando el comando apt
  4. Luego instale las dependencias de Python usando el comando pip
  5. Copie el código fuente de mi aplicación en una ubicación como /opt
  6. Finalmente ejecute el servidor web usando el comando “flask”.

 

Creación del archivo para la imagen de Docker

Ahora que se tienen las instrucciones, vamos a crear un archivo acoplable con estos pasos. Aquí hay una descripción general rápida del proceso de creación de su propia imagen:

Primero hay que crear un archivo docker llamado “Dockerfile” y escribir las instrucciones para configurar la aplicación en él, como instalar dependencias, donde copiar el código fuente desde y hacia y cuál es el punto de entrada de la aplicación, etc:

FROM Ubuntu
RUN apt-get update
RUN apt-get install python
RUN pip install flask
RUN pip install flask-mysql
COPY . /opt/source-code
ENTRYPOINT FLASK_APP=/opt/source-code/app.py flask run

Una vez hecho, hay que construir la imagen usando el comando “docker build” y especificar el archivo docker como entrada, así como un nombre de etiqueta para la imagen: “docker build Dockerfile -t <Imagen>”

Esto creará una imagen local. Para que esté disponible en el registro público de Docker Hub, hay que ejecutar el comando “docker push <imagen>” y especificar el nombre de la imagen que acaba de crear. 

Estructura del archivo Dockerfile

El archivo Docker es un archivo de texto escrito en un formato específico que Docker puede entender. Está en formato de instrucciones y argumentos. Todo a la izquierda en MAYÚSCULAS es una instrucción. Cada uno de estos le indica a Docker que realice una acción específica mientras crea la imagen. Todo a la derecha es un argumento para esas instrucciones. La primera línea define en qué sistema operativo base se debe enfocar el contenedor. Cada imagen de Docker debe basarse en otra imagen. Un sistema operativo u otra imagen que se creó antes basada en un sistema operativo. 

Es importante tener en cuenta que todos los archivos Docker deben comenzar con una instrucción FROM, la instrucción de ejecución le indica a Docker que ejecute un comando particular en esas imágenes base. Entonces, en este punto, Docker ejecuta los comandos de actualización de apt-get para obtener los paquetes actualizados e instala las dependencias requeridas en la imagen, luego la instrucción de copia copia los archivos del sistema local en la imagen de Docker. En este caso, el código fuente de nuestra aplicación está en la carpeta actual y lo copiaré en la ubicación /opt/source-code dentro de la imagen de Docker. Y finalmente, el punto de entrada nos permite especificar un comando que se ejecutará cuando la imagen se ejecute como un contenedor. 

Cuando Docker construye las imágenes, las construye en una arquitectura en capas. Cada línea de instrucción crea una nueva capa en la imagen con solo los cambios de la capa anterior. Como cada capa solo almacena los cambios de la capa anterior, también se refleja en el tamaño. 

Tamaño de las imágenes creadas

Si nos fijamos en la imagen base de Ubuntu, tiene un tamaño de alrededor de 120mb. Los paquetes apt que instalo son alrededor de 300mb y las capas restantes son pequeñas. Puede ver esta información si ejecuta el comando “docker history <imagen>” seguido del nombre de la imagen. Cuando ejecuta el comando “docker build” se pueden ver los diversos pasos involucrados y el resultado de cada tarea. Todas las compilaciones de capas se almacenan en caché, por lo que la arquitectura en capas lo ayuda a reiniciar la compilación de Docker desde ese paso en particular en caso de que falle o si agrega nuevos pasos en el proceso de compilación, no tendría que comenzar de nuevo.

Docker almacena en caché todas las capas construidas, por lo que, en caso de que falle un paso en particular, puede solucionar el problema y volver a ejecutar “docker build”. Se reutilizarán las capas anteriores de la memoria caché y continuará para construir las capas restantes. Lo mismo es cierto si tuviera que agregar pasos adicionales en el archivo acoplable. De esta manera, reconstruir su imagen es más rápido y no tiene que esperar a que un acoplador reconstruya la imagen completa cada vez. Esto es útil especialmente cuando actualiza el código fuente de su aplicación, ya que puede cambiar con más frecuencia solo las capas por encima de las capas actualizadas que deben reconstruirse.

Acabamos de ver una serie de productos en contenedores, como herramientas de  desarrollo de bases de datos, sistemas operativos, etc. Pero eso no es todo. La creación de imágenes para Docker puede contener casi todas las aplicaciones en contenedores, incluso las más simples, como navegadores o utilidades como aplicaciones curl como Spotify Skype, etc.

0 Seguir leyendo →

Resumen de comandos de Docker

Resumen de comandos de Docker:

Puedes descargar un resumen de comandos de Docker en imágen para imprimir, está en inglés:
Comandos de docker
Descarga imagen parte 1
Descarga imagen parte 2
También puedes ir a la página de documentación de Docker:
Documentación de Docker

docker version muestra la versión y detalles de docker

docker --help muestra ayuda

docker COMANDO --help …del comando de docker COMANDO

docker ps Lista contenedores (sólo los activos)

docker ps -a Lista contenedores (todos)

docker ps -l Lista el último contenedor creado (activo o no)

docker run IMAGEN COMANDO Crea un contenedor a partir de IMAGEN, ejecuta COMANDO en él y muestra el resultado (y termina)

docker run -t -i IMAGEN COMANDO …de forma interactiva (-i mantiene STDIN abierto y -t asigna un terminal virtual en el contenedor)

docker run -d IMAGEN COMANDO …en modo daemon (sin mostrar el resultado)

docker run -P IMAGEN COMANDO …mapea los puertos necesarios (se muestran en un ps)

docker run -p 80:1080 IMAGEN COMANDO …mapea los puertos indicados

docker run --name="NOMBRE" IMAGEN COMANDO …con el nombre NOMBRE

docker stop CONTENEDOR detiene CONTENEDOR

docker start CONTENEDOR inicia CONTENEDOR

docker restart CONTENEDOR reinicia CONTENEDOR

docker rm COMTENEDOR borra CONTENEDOR

docker images Lista imágenes

docker rmi IMAGEN borra IMAGEN

docker create IMAGEN Crea un contenedor a partir de IMAGEN (admite muchos de los modificadores de run, como -itP o --name)

docker exec -it CONTENEDOR bash inicia sesión interactiva bash en CONTENEDOR (-i mantiene STDIN abierto y -t asigna un terminal virtual en el contenedor)

docker logs CONTENEDOR Muestra un log con las salidas de CONTENEDOR

docker logs -t CONTENEDOR …con timestamp

docker top CONTENEDOR muestra los procesos en el contenedor

docker commit CONTENEDOR REPO crea imagen REPO a partir de CONTENEDOR

0 Seguir leyendo →

Práctica con el comando run de Docker

Práctica con el comando run de Docker

En esta demostración, veremos algunas de las opciones avanzadas del comando “run” que vimos en el artículo Opciones del comando run de Docker.

En primer lugar ejecutamos “docker run ubuntu cat /etc/*release*” para ver la versión de ubuntu ejecutándose. Si se quiere ejecutar otra versión de este sistema operativo en particular hay que ir al sitio https://hub.docker.com/, entrar en el repositorio que se está usando (ubuntu) y ver la lista de etiquetas compatibles. Si no especifica ninguna etiqueta se asumirá que es la última etiqueta. Es por eso que se descargó la última imagen. Si desea ejecutar otra versión hay que agregar la etiqueta al nombre de la imagen: docker run ubuntu:17.10 cat /etc/*release. La etiqueta puede ser cualquiera de las listadas para la misma versión (“17.10”, “artful-20170728”, “artful” o “devel”). De esta manera, se pueden etiquetar las imágenes de manera diferente para diferentes propósitos, ya sea para versionar, etc.

Lo siguiente es ver los modos de conexión y desconexión. Vamos a ejecutar un contenedor ubuntu y mantenerlo vivo por 15 segundos usando el comando sleep. Por lo tanto, ejecutará el comando de suspensión durante 15 segundos y va a salir del contenedor. Si se ejecuta, no se ve ninguna respuesta y aunque se haga Ctrl+C no sale de la ejecución. Así que estamos atascados hasta que el sistema operativo o el contenedor salgan automáticamente cuando pasen los 15 segundos. Si tuviera que ejecutar una aplicación real en lugar del comando de prueba sleep, no se podría salir. La consola solo mostraría la salida de esa aplicación que esta ejecutándose. Esta es la forma predeterminada en que se ejecuta un contenedor. Tampoco importa, ya que se puede usar otro terminal para acceder al host docker y ejecutar una parada haciendo “docker stop <ID>” y luego volver al terminal original. 

Segundo plano

Pero la próxima vez que se ejecute el contenedor, se puede ejecutar en segundo plano o en un modo separado, especificando el parámetro -d: “docker run -d ubuntu sleep 1500”.  Al ejecutarlo mostrará el ID (largo) del contenedor, el mismo que aparece cuando se ejecuta el comando “docker ps” pero solo los primeros caracteres. Si se quiere llevar el proceso ejecutado en segundo plano de nuevo a primer plano, se ejecuta el comando “docker attach <ID>”, pero volveremos  a la misma situación de que nada funciona realmente.

Para esta práctica con el comando run de Docker vamos a ver otro ejemplo con una imagen llamada “timer” que simplemente imprime la hora actual en la pantalla cada segundo: “docker run timer”. Esto funciona infinitamente, el contenedor nunca se detiene ya que se ejecuta en primer plano. Si se ejecuta en segundo plano con el parámetro -d se puede ver con el comando “ps” que se está ejecutando pero no se puede ver el resultado. Si se ejecuta el comando “docker attach”  con el ID del contenedor, se podrá ver de nuevo la salida que genera.

Práctica con el comando run de Docker para ejecutar Jenkins

Practica con el comando run de Docker y JenkinsExploremos algunas imágenes adicionales de Docker en hub.docker.com. Algo que me interesa es Jenkins, un sistema de compilación que no conozco y me gustaría probar. Jenkins es un servidor de entrega e integración continua. Supongamos que he trabajado antes y solo quiero jugar con él para tener experiencia práctica. Entonces, en lugar de seguir las instrucciones de instalación e instalar muchas dependencias en mi ordenador, lo que me gustaría hacer es simplemente ejecutar Jenkins como un contenedor y jugar con él. EN el repositorio se pueden ver instrucciones sobre cómo ejecutar jenkins. El nombre es solo “jenkins”, lo que ayuda a comenzar simplemente ejecutando un contenedor con “jenkins”: “docker run jenkins”. Con esto se deberían extraer las imágenes y capas de jenkins y ejecutar una instancia de jenkins.

Ejecución

Jenkins es un servidor web. Lo que esperamos es que una vez que implementemos este contenedor en particular, esperamos ir a un sitio web con un navegador y acceder a la interfaz de usuario web. Ahora, ¿cómo puedo acceder? si voy a la interfaz de usuario de mi host y muestro el host donde se ejecuta Docker haciendo “docker ps” se puede ver que se está ejecutando jenkins y está en el puerto 8080 y 50000. Como Jenkins tiene un interfaz de usuario web, se puede ir a la IP interna y el puerto.

También se puede asignar un puerto a mi host acoplable y acceder usando la IP externa. Para acceder utilizando la IP interna debe estar dentro del host docker. Para saber cual es usar el comando “docker inspect <ID>”. Entre toda la información mostrada, abajo, en la sección de “Networks”, verá el apartado “bridge” y la dirección IP.  Así que pongo esa dirección y el puerto 8080 para ir a la página de jenkins. Una vez ejecutándose Jenkins en el navegador, solo hay que seguir las instrucciones. Se puede observar que después de ejecutar el comando “docker run” hay un usuario administrador creado y una contraseña generada automáticamente, por lo que usaría esta contraseña para desbloquear e iniciar configuraciones en los jenkins.

Cómo acceder externamente

Si abro una página web y voy a la IP de mi host docker que es 192.168.1.14 y si intento acceder al servidor jenkins usando esa dirección IP, no podré acceder . Esto se debe a que ese puerto o servicio en particular no está escuchando en el host del acoplador. Para hacer eso, se debe agregar un mapeo de puertos. Pero no se puede agregar una asignación de puertos mientras el servicio se está ejecutando. Por esto, debe detenerse y ejecutar el mismo comando de ejecución de Docker, pero mapeando desde el puerto 8080 hasta el puerto en el host docker: “docker run -p 8080:8080 jenkins” Ahora debería poder ver la página de jenkins. 

De esta forma no es necesario saber cómo instalar dependencias o seguir muchas instrucciones o instalar binarios, etc. Para comenzar con jenkins o cualquier aplicación como esa es tan fácil como ejecutar el contenedor docker para esa aplicación en particular.

Asignación de volúmenes

Hemos realizado la asignación de puertos, pero también podemos asignar un volumen. Esto es para que jenkins almacene los datos en un directorio en el host. De manera predeterminada, jenkins almacenará los datos en nuestra página de inicio de jenkins dentro del contenedor de jenkins. Si desea guardar esa información, debe asignar un volumen. 

Para demostrar esto, instalar los complementos sugeridos de jenkins y hacer algunos cambios de configuración en mi Jenkins como puede ser un cambio de configuración y una contraseña, etc. Aunque instalar complementos también son cambios de configuración. Por lo tanto, quiero ver que los cambios que se realizan, si se detiene el contenedor y se reinicia o elimina y, de nuevo, ejecuta otra instancia de jenkins y aún se podrá ver los cambios realizados. Para conservar esos datos en los contenedoresse tiene que mapear el volumen. Para ello voy a crear un directorio local llamado my-jenkins-data. Lo que haré es, cuando ejecute el contenedor Docker la próxima vez, asignar ese volumen a la ubicación donde el contenedor docker de jenkins almacena sus datos de manera predeterminada que está en la home de jenkins: “docker run -p 8080:8080 -v /root/my-jenkins-data:/var/jenkins_home jenkins”

Es posible que exista un problema de permisos, por lo que es necesario especificar en la ejecución otro usuario con la opción -u:  “docker run -p 8080:8080 -v /root/my-jenkins-data:/var/jenkins_home -u root jenkins”.

Esto es todo en esta práctica con el comando run de Docker.

0 Seguir leyendo →

Opciones del comando run de Docker

Opciones del comando run de Docker run:

Docker runAunque en el artículo de Primeros pasos con Docker  vimos el comando “run”, en este artículo vamos a estudiar sus opciones. En primer lugar vamos a usar el comando “docker run redis” para ejecutar el contenedor que ejecuta un servicio redis, pero una versión anterior, por ejemplo, la 4.0. Para eso hay que especificar la versión separada por dos puntos. Esto se llama una etiqueta o tag: “docker run redis:4.0”.

Si no se especifica ninguna etiqueta, se considerará que la etiqueta predeterminada es la última que depende de los autores de ese software. Para saber información sobre versiones y qué es lo último hay que acudir a  https://hub.docker.com/. Hay que buscar una imagen y se verán todas las etiquetas compatibles en su descripción. Cada versión del software puede tener múltiples etiquetas cortas y largas asociadas. 

inputs o entradas

Supongamos que tengo una aplicación simple que cuando se ejecuta solicita mi nombre y al ingresar mi nombre imprime un mensaje de bienvenida. Si tuviera que dockerizar esta aplicación y ejecutarla como un contenedor de docker, no esperaría el mensaje. Simplemente imprime lo que se supone que imprime la aplicación en la salida estándar. Esto se debe a que, por defecto, el contenedor acoplable no escucha una entrada estándar. Aunque esté conectado a su consola, no puede leer ninguna entrada suya. No tiene una terminal para leer las entradas. Se ejecuta en un modo no interactivo.

Si se desea proporcionar una entrada se debe asignar la entrada estándar del host al contenedor utilizando el parámetro -i. El parámetro -i es para el modo interactivo y cuando se ingresa mi nombre imprime la salida esperada, pero todavía falta algo cuando ejecutamos la aplicación. Nos pidió nuestro nombre, pero cuando se ejecuta falta ese mensaje, aunque parece haber aceptado la entrada. Esto se debe a que el mensaje de solicitud se muestra en la terminal y no nos hemos conectado a la terminal de contenedores. Para esto, hay que usar la opción -t también. Entonces, con la combinación de -i y -t ahora estamos conectados al terminal, así como al modo interactivo en el contenedor: “docker -it run <imagen>”

Asignación de puertos

Otra de las opciones del comando run de Docker es la que permite la asignación de puertos o la publicación de puertos en los contenedores. Volvamos al ejemplo en el que estamos en una aplicación web simple en un contenedor Docker. El host donde está instalado Docker se llama host Docker. Cuando ejecutamos una aplicación web en un contenedor se asigna una dirección IP dentro del host Docker (por ejemplo la 172.17.0.2) y escucha en un puerto determinado.

Se podría utilizar la IP del contenedor pero es una IP interna y sólo es accesible dentro del host del contenedor. Si se abre un navegador desde el host del contenedor se puede acceder, pero si abre un navegador desde el host del ordenador que ejecuta Docker no se podrá. Para acceder hay que ir a la IP del host de Docker y haber mapeado el puerto dentro del contenedor docker a un puerto libre en el host Docker. 

Ejemplo

Supongamos que vamos a crear 3 contenedores de una aplicación web que escucha por el puerto 5000 y dos contenedores mysql que escuchan por el puerto 3306. Cada uno de los 5 contenedores tendrá su IP dentro del host de Docker, pero las tres webs escucharan por el puerto 5000 y las dos bases de datos por el puerto 3306. Para acceder a cada contenedor desde fuera del host Docker hay que mapear los puertos de cada una de las aplicaciones. Para esto se utiliza el parámetro -p de esta manera: “docker run -p <puerto docker>:<puerto aplicacion< <imagen>”. En el ejemplo de las 5 aplicaciones sería:

  • web1: docker run -p 80:5000 webapp
  • web2: docker run -p 8000:5000 webapp
  • web3: docker run -p 8001:5000 webapp
  • mysql1: docker run -p 3306:3306 mysql
  • mysql2: docker run -p 8306:3306 mysql

Con esto, para que el usuario pueda acceder a cualquier aplicación dockerizada, solo tiene que ir a la IP del host de Docker y el puerto mapeado. Por lo tanto, puede ejecutar tantas aplicaciones como quiera y asignarlas a tantos puertos como desee. Y, por supuesto, no puede asignar al mismo puerto en el host Docker más de una vez.

Persistencia de los datos en el contenedor de Docker.

Supongamos que queremos ejecutar un contenedor mysql. Cuando se crean bases de datos y tablas, los archivos de datos se almacenan en location /var/lib/mysql dentro del contenedor docker. El contenedor de Docker tiene su propio sistema de archivos aislado y cualquier cambio en cualquier archivo ocurre dentro del contenedor. Si se vuelcan muchos datos en la base de datos, y se elimina el contenedor mysql también se eliminan todos los datos en su interior. Si se desea conservar los datos, lo mejor es asignar un directorio de fuera del contenedor.

Podemos crear un directorio llamado /opt/datadir y asignarlo a /var/lib/mysql dentro del contenedor docker usando la opción -v y especificando el directorio en el host docker seguido de dos puntos y el directorio dentro del contenedor docker. De esta manera, cuando se ejecute el contenedor de Docker, montará implícitamente el directorio externo en una carpeta dentro del contenedor de Docker. De esta manera, todos los datos ahora se almacenarán en el volumen externo en el directorio /opt/data y, por lo tanto, permanecerán incluso si elimina el contenedor: “docker run -v /opt/datadir:/var/lib/mysql mysql”

El comando “docker ps” está bien para obtener detalles básicos sobre contenedores, como sus nombres y el ID, pero si queremos ver detalles adicionales sobre un contenedor específico, es mejor usar el comando “docker inspect” y proporcionar el nombre del contenedor o el ID. Devuelve todos los detalles de un contenedor en formato JSON, como el estado, los montajes, los datos de configuración, la configuración de red, etc. Recuerde usarlo cuando sea necesario para encontrar detalles en un contenedor.

Finalmente, para ver los registros de un contenedor que se ejecuta en segundo plano se utiliza el comando “docker logs” con el ID. del contenedor o el nombre

0 Seguir leyendo →