Docker, aplicaciones en cualquier parte

De Apuntes
Saltar a: navegación, buscar

Docker es una herramienta que puede empaquetar una aplicación y sus dependencias en un contenedor virtual que se puede ejecutar en cualquier servidor Linux. Es muy flexible y portable, de manera que se puede desplegar en infraestructura física, nube privada, nube pública, etc. Actualmente es soportado también por sistemas como Windows y macOS.

Arquitectura de una máquina virtual
Arquitectura de un contenedor Docker, donde el kernel del anfitrión es compartido
Es virtualización a nivel de sistema operativo que, a diferencia de otros tipos como emulación (VirtualBox), paravirtualización (Xen), o virtualización completa (VMWare, KVM), comparten el kernel o núcleo del anfitrión lo que lo hace más ligero.

Instalación

La documentación oficial incluye instrucciones de instalación de Docker Community para macOS, Windows, Linux o servicios en la nube[1].

Ubuntu

La forma más sencilla de instalar Docker en Ubuntu es añadiendo un repositorio de paquetes. Puede consultar la guía de instalación para instrucciones en otras distribuciones Linux[2].

Instale los paquetes necesarios para utilizar apt sobre HTTPS:

$ sudo apt install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

Agrege la llave GPG de Docker:

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

Añada el repositorio:

$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

Nota: la extensión $(lsb_release -cs) ejecuta el comando entre paréntesis y, en este caso, devuelve el nombre de la distribución, de manera que completa la línea de acuerdo a la versión de Ubuntu instalada. En el caso de Linux Mint debe sustituirlo manualmente por el nombre de la distribución Ubuntu base.

Actualice la lista de paquetes e instale los paquetes necesarios:

$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io

Compruebe que Docker se ejecuta correctamente:

$ sudo docker run hello-world

Debería visualizar una salida como la siguiente:

 Unable to find image 'hello-world:latest' locally
 latest: Pulling from library/hello-world
 1b930d010525: Pull complete 
 Digest: sha256:4fe721ccc2e8dc7362278a29dc660d833570ec2682f4e4194f4ee23e415e1064
 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/

Si desea ejecutar docker como un usuario no administrador, añada su usuario al grupo docker:

$ sudo usermod -aG docker <usuario>

Luego cierre su sesión y vuelva a abrirla.


Uso

Básicamente, un contenedor no es más que un proceso en ejecución, con algunas características de encapsulación adicionales aplicadas para mantenerlo aislado del host y de otros contenedores. Uno de los aspectos más importantes del aislamiento de contenedores es que cada contenedor interactúa con su propio sistema de archivos privado; este sistema de archivos es proporcionado por una imagen Docker. Una imagen provee todo lo necesario para ejecutar una aplicación, como código o binarios, runtimes, dependencias, etc. Una imagen podría proporcionar un ambiente Debian o un servidor LAMP, por ejemplo.

Muchos proyectos mantienen una imagen oficial y también los usuarios aportan sus propias imágenes. El repositorio o registro oficial es Docker Hub[3], el cuál será utilizado a menos que se especifique otro diferente. También es posible crear una imagen propia tomando una imagen existente como base. Esta se define en un archivo llamado Dockerfile.

Docker-layers.png

Las imágenes están construidas por varias capas que pueden ser compartidas, por ejemplo varias imágenes pueden estar basadas en Debian por lo que la respectiva capa será descargada una sola vez y utilizada por todas las imágenes que lo requieran.

Sobre la imagen se ejecuta el contenedor y es aquí dónde se escriben los cambios, por ejemplo los archivos que se suban a un Nextcloud o los datos que se inserten en un MariaDB. Si se borra el contenedor, todos los datos contenidos se borrarán también. Para resguardar los datos se utilizan volúmenes persistentes que se montan dentro del contenedor pero cuyo sistema de archivos está fuera de este.

Ejemplo 1: linux con consola interactiva

Continuando con el ejemplo, si se desea descargar una versión mínima de Debian Buster, ejecute:

$ docker pull debian:buster-slim

En este ejemplo el nombre de la imagen es debian y se ha indicado una etiqueta buster-slim. Las etiquetas permiten indicar variantes o versiones específicas de la imagen. Si no se indica alguna etiqueta, docker buscará la etiqueta lastest que, como su nombre lo sugiere, corresponde a la más reciente versión de la imagen.

Una vez descargada la imagen, es posible ejecutar un contenedor. Por ejemplo:

$ docker run --rm -it debian:buster-slim bash

Donde:

  • --rm indica que el contendor debe borrarse una vez que haya finalizado su ejecución. Útil cuando solo queremos ejecutar un ejemplo y no algo permanentemente.
  • -it indica que sea una sesión interactiva y que además proporcione una consola.
  • debian:debian-slim corresponde al nombre de la imagen y la etiqueta.
  • bash indica el programa a ejecutar.

Verá que el promp cambia, indicando que ahora está ejecutando comandos en el contenedor. Cuando desee salir, ejecute el comando exit. La sesión se cerrará y el contenedor será eliminado.

Ejemplo 2: servidor web

Algo muy común es ejecutar varios contenedores, uno con un servidor web y otro con una base de datos; donde se expongan uno o más puertos para acceder servicios desde otros hosts; y donde se definan volúmenes persistentes que se monten en el contenedor, esto para evitar que se pierdan datos en caso de eliminar un contenedor.

El primer paso es definir una red exclusiva para los contenedores. Si no se creara una red, los contenedores utilizarían la red predeterminada, llamada bridge, sin embargo esta tiene menos características y no ofrece un aislamiento seguro.

docker network create website-net

El servidor de base de datos se ejecutará en su propio contenedor, aparte del servidor web. De la siguiente manera:

docker run -d --name websitedb \
--network website-net \
-e MYSQL_ROOT_PASSWORD=h4ckm3 \
-e MYSQL_USER=userdb \
-e MYSQL_PASSWORD=us3r \
-e MYSQL_DATABASE=webdb \
-v websitedbvolume:/var/lib/mysql \
mariadb:latest

Donde:

  • -d indica que el contenedor se ejecutará en segundo plano regresando el control al promp.
  • --name define un nombre. Si no se definiera, docker creará algún jocoso nombre.
  • --network indica a cuál red, previamente definida, debe conectarse. Puede conectarse a más de una red.
  • -e permite asignar valores a las variables de entorno definidas en el contenedor por los desarrolladores.
  • -v monta un volumen dentro del contenedor. En este caso se definió el volumen websitedbvolume, el cual es gestionado por docker.
  • mariadb:latest corresponde al nombre de la imagen y la etiqueta. Si la imagen no existiera, docker la descargará.

Puede confirmar la creación del volumen y la información del mismo, de la siguiente manera:

docker volume ls
docker volume inspect websitedbvolume

Las bases de datos del contenedor se guardarán aparte, en este volumen, en la ruta indicada en Mountpoint en la salida del segundo comando.

Antes de crear el contenedor para el servidor web, crearemos un directorio que usaremos como volumen, mostrando otra forma de gestionar los volúmenes en docker.

mkdir public_html

El contendedor del servidor web se ejecuta de la siguiente manera:

docker run -d --name website \
--network website-net \
-p 8080:80 \
-v $(pwd)/public_html:/var/www/html \
php:apache

Donde:

  • -d indica que el contenedor se ejecutará en segundo plano regresando el control al promp.
  • --name define un nombre. Si no se definiera, docker creará algún jocoso nombre.
  • --network indica a cuál red, previamente definida, debe conectarse. Puede conectarse a más de una red.
  • -p indica que puerto de expondrá. A la izquierda se indica un puerto libre del host, y a la derecha el puerto a exponer del contenedor.
  • -v monta un volumen dentro del contenedor. En este caso se indicó la ruta a un directorio. $(pwd) indica la ruta actual, de manera que genere la dirección a una ruta absoluta.
  • php:apache corresponde al nombre de la imagen y la etiqueta. Si la imagen no existiera, docker la descargará.

Todo lo que se coloque en el directorio public_html será publicado por el servidor web, pues dicho directorio está mondado, en el contenedor, como /var/www/html, que es la ruta predeterminada del servicio web. Pruebe creando un archivo index.html u index.php.

Para ver el sitio web, abra un navegador web e ingrese a la dirección http://xx.xx.xx.xx:8080.

Donde:

  • xx.xx.xx.xx es la dirección IP o nombre de dominio de su servidor. Sustituya por localhost si está probando en su computadora.
  • 8080 es el puerto con el que se mapeó el puerto 80 del contenedor.

En este punto debería tener en ejecución dos contenedores, llamados websitedb y website. Puede corroborarlo así:

docker ps

Si desea correr comandos en la terminal de un contenedor que esté en ejecución, puede hacerlo de la siguiente manera:

docker exec -it website bash

Donde:

  • -it indica que abra una sesión interactiva y que además proporcione una consola.
  • website corresponde al nombre del contenedor, el cual fue definido cuando se creó.
  • bash indica el programa a ejecutar.

Si desea eliminar los contenedores ejecute los siguientes pasos.

docker stop website
docker rm website
docker stop websitedb
docker rm websitedb

El volumen del contenedor websitedatabase seguirá existiendo y almacenando los archivos de la o las bases de datos. Si desea borrarlo ejecute:

docker volume rm websitedbvolume

De una manera similar, puede eliminar la red que se definió para dichos contenedores.

docker network rm website-net

Docker Compose, aplicaciones complejas con un simple comando

Docker Compose es una herramienta que permite definir uno o más contenedores Docker en un único archivo de configuración y ejecutarlo con un simple comando. Es una excelente alternativa para definir y correr aplicaciones complejas.

Básicamente se debe definir un archivo, en formato YAML[4], y guardarlo con el nombre docker-compose.yml [5].

Luego, en el directorio con el archivo docker-compose.yml, ejecutar:

docker-compose up -d

Donde:

  • -d indica que el contenedor se ejecutará en segundo plano regresando el control al promp.

Instalación en Ubuntu

La instalación de Docker Compose consiste en descargar el binario de la aplicación y darle permisos de ejecución [6].

Se descarga docker-compose en el directorio /usr/local/bin:

sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

Se le da permisos de ejecución:

sudo chmod +x /usr/local/bin/docker-compose

Para corroborar que funciona correctamente, ejecute:

docker-compose --version

Ejemplo 2.1: servidor web con un solo comando

El ejemplo 2: servidor web, se condensa en este archivo docker-compose.yml:

version: '3'

services:      
  website:
    image: php:apache
    container_name: website
    ports:
      - 8081:80
    volumes:
      - ./public_html:/var/www/html
    networks:
      - website-net

  websitedb:
    image: mariadb:latest
    container_name: websitedb
    volumes:
      - websitedbvolume:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: "h4ckm3"
      MYSQL_USER: 'userdb'
      MYSQL_PASSWORD: 'us3r'
      MYSQL_DATABASE: 'webdb'
    networks:
      - website-net
      
networks:
  website-net:
  
volumes:
  websitedbvolume:

Consideraciones:

  • Los servicios corresponden a cada uno de los contenedores.
  • Si no se indica un nombre en container_name, docker-compose le asignará un nombre.
  • Las redes y volúmenes se deben declarar, en este caso al final del archivo.
  • En este caso se utilizó el puerto 8081 del host, esto para evitar conflicto en caso de que todavía el ejemplo 2 esté en ejecución.

Guarde los cambios y, como se había indicado, ejecute:

docker-compose up -d

Puede monitorear la ejecución de los contenedores con:

docker-compose logs -f

Presione Control+C para volver al promp.

Para ver los contenedores existentes ejecute:

docker-compose ps

Si desea ejecutar comandos dentro de un contenedor, puede hacerlo de la siguiente manera:

docker-compose exec website bash

Donde:

  • website corresponde al nombre del servicio.
  • bash indica el programa a ejecutar.

Para eliminar los contenedores ejecute, siempre dentro del directorio con el archivo docker-compose.yml, los siguientes comandos:

docker-compose stop
docker-compose rm

Consulte la documentación para conocer más comandos [7].

Portainer, aplicaciones complejas con un simple clic

Portainer es un gestor web de dockers. A pesar de la sencillez de esta herramienta, incluye gran cantidad de funciones.

Instalación

Esta solución consta de dos elementos: servidor y agente. Pero en el caso de un solo nodo, solo es necesario instalar el servidor, o sea en modo standalone.

Se instala como un contenedor docker más [8]. Primero se crea el volumen de datos persistentes, donde se almacenará todo lo relacionado con Portainer.

docker volume create portainer_data

Luego, se crea el contenedor de Portainer

docker run -d \
--name mi_portainer \
-p 8000:8000 -p 9000:9000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer

Abra en el navegador la dirección: http://xx.xx.xx.xx:9000

Donde:

  • xx.xx.xx.xx es la dirección IP o nombre de dominio de su servidor. Sustituya por localhost si está probando en su computadora.

Si todo está correctamente configurado, se le pedirá que defina el nombre de usuario de la cuenta administrativa y una contraseña:

Portainer setup admin.png

Una vez realizado este paso, se le preguntará a qué tipo de ambiente conectarse. Este es un escenario standalone, así que escoja la opción Local y presione Connect.

Portainer docker enviroment.png

La interfaz es muy intuitiva. Si usted siguió este tutorial, podrá entender la mayoría de las opciones disponibles, sin mayor dificultad.

Portainer-dashboard.png

Véase también

Referencias

  1. https://docs.docker.com/install/
  2. https://docs.docker.com/install/
  3. https://hub.docker.com
  4. https://es.wikipedia.org/wiki/YAML
  5. https://docs.docker.com/compose/compose-file
  6. https://docs.docker.com/compose/install/
  7. https://docs.docker.com/compose/reference/
  8. https://www.portainer.io/installation/