Introducción a Docker: Guía Esencial para el Despliegue de Aplicaciones

Instalación y Configuración de Docker

Preparación del Entorno en CentOS

Para comenzar con Docker en sistemas basados en CentOS, es necesario instalar algunas utilidades del sistema y configurar los repositorios de Docker.

sudo yum install -y yum-utils device-mapper-persistent-data lvm2

Una vez instaladas las herramientas, configure el repositorio oficial de Docker CE (Community Edition). Es recomendable usar un espejo (mirror) para acelerar las descargas, por ejemplo, los de Aliyun si aplican para su región:

sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo

A continuación, actualice el caché de Yum para que los nuevos repositorios estén disponibles:

sudo yum makecache fast

Instalación de Docker Engine

Ahora puede proceder a instalar el motor de Docker y sus componentes principales. Esto incluye el demonio de Docker, la interfaz de línea de comandos (CLI), containerd.io y los plugins de buildx y compose:

yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verificación y Gestión del Servicio Docker

Después de la instalación, es fundamental iniciar y configurar el servicio Docker para su uso. También se recomienda habilitar el inicio automático para que Docker se ejecute cada vez que el sistema se reinicie:

# Iniciar el servicio Docker
systemctl start docker

# Detener el servicio Docker
systemctl stop docker

# Reiniciar el servicio Docker
systemctl restart docker

# Habilitar el inicio automático de Docker al arrancar el sistema
systemctl enable docker

# Verificar que Docker se está ejecutando correctamente
docker ps

Si el comando docker ps se ejecuta sin errores y muestra un encabezado de tabla, Docker está correctamente instalado y funcionando.

Configuración de Espejos para Acelerar la Descarga de Imágenes

Para mejorar la velocidad al descargar imágenes de Docker Hub, se pueden configurar espejos de registro. Esto se hace editando o creando el archivo /etc/docker/daemon.json. Las URLs de los espejos pueden variar; es recomendable buscar las más eficientes para su ubicación geográfica:

# Eliminar un archivo de configuración existente si lo hubiera para evitar duplicados
sudo rm -f /etc/docker/daemon.json

# Crear o sobrescribir el archivo de configuración con los espejos deseados
sudo tee /etc/docker/daemon.json <<-'EOF'
{
    "registry-mirrors": [
        "https://docker.mirrors.ustc.edu.cn",
        "https://registry.docker-cn.com"
    ]
}
EOF

# Recargar la configuración del demonio Docker para aplicar los cambios
systemctl daemon-reload

# Reiniciar Docker para que la nueva configuración de espejos tenga efecto
systemctl restart docker

Conceptos Fundamentales de Docker

Docker simplifica el despliegue de aplicaciones empaquetándolas en unidades estandarizadas llamadas imágenes, las cuales luego se ejecutan como contenedores. Una imagen es un paquete ligero, autónomo y ejecutable que contiene todo lo necesario para ejecutar una aplicación: el código, el tiempo de ejecución, bibliotecas del sistema, herramientas, variables de entorno y archivos de configuración. Un contenedor es una instancia en ejecución de una imagen, proporcionando un entorno aislado para la aplicación.

Despliegue de un Contenedor de Base de Datos (MySQL)

El despliegue de una base de datos como MySQL con Docker es notablemente sencillo. Con un solo comando, puede iniciar un servidor MySQL en un contenedor aislado:

docker run -d \
  --name servicio_mysql_despliegue \
  -p 3308:3306 \
  -e TZ=America/Mexico_City \
  -e MYSQL_ROOT_PASSWORD=miContrasenaFuerte \
  mysql:8.0

Desglose de los parámetros del comando:

  • docker run -d: Crea un nuevo contenedor y lo inicia en modo "detached", es decir, en segundo plano.
  • --name servicio_mysql_despliegue: Asigna un nombre único al contenedor para una fácil identificación.
  • -p 3308:3306: Configura el mapeo de puertos. El puerto 3308 del host se enlaza con el puerto 3306 (puerto predeterminado de MySQL) dentro del contenedor, permitiendo el acceso externo.
  • -e TZ=America/Mexico_City: Establece una variable de entorno para la zona horaria del contenedor.
  • -e MYSQL_ROOT_PASSWORD=miContrasenaFuerte: Define la contraseña del usuario 'root' para la instancia de MySQL dentro del contenedor.
  • mysql:8.0: Especifica la imagen de Docker a utilizar, en este caso, la versión 8.0 de MySQL.

Docker, al ejecutar este comando, primero buscará la imagen mysql:8.0 localmente. Si no la encuentra, la descargará de Docker Hub. Luego, crea y ejecuta un nuevo contenedor basado en esa imagen.

Comandos Docker Esenciales para la Gestión de Contenedores e Imágenes

La siguiente tabla resume los comandos más comunes utilizados en la gestión de Docker:

Comando Descripción
docker pull <imagen[:tag]> Descarga una imagen (y su etiqueta específica) de un registro remoto (como Docker Hub) al host local.
docker push <imagen[:tag]> Sube una imagen local a un registro remoto.
docker images Muestra una lista de todas las imágenes disponibles en el sistema local.
docker rmi <imagen> Elimina una o varias imágenes locales.
docker run <opciones> <imagen> Crea y ejecuta un nuevo contenedor a partir de una imagen especificada.
docker stop <contenedor> Detiene la ejecución de un contenedor especificado.
docker start <contenedor> Inicia un contenedor que previamente ha sido detenido.
docker rm <contenedor> Elimina uno o varios contenedores detenidos.
docker ps [-a] Lista los contenedores en ejecución. Usar -a para listar todos los contenedores (activos e inactivos).
docker logs <contenedor> Muestra los logs de salida estándar y error estándar de un contenedor.
docker exec -it <contenedor> <comando> Ejecuta un comando dentro de un contenedor en ejecución (-it para una sesión interactiva de terminal).
docker save -o <archivo.tar> <imagen> Exporta una imagen a un archivo TAR en el sistema de archivos local.
docker load -i <archivo.tar> Importa una imagen desde un archivo TAR al sistema local de Docker.
docker inspect <objeto> Proporciona información detallada en formato JSON sobre un objeto Docker (imagen, contenedor, volumen, red).

Configuración de Inicio Automático para Contenedores

Además de configurar el servicio Docker para que inicie automáticamente, también es posible configurar contenedores individuales para que se reinicien automáticamente en caso de fallo o al reiniciar el sistema anfitrión:

# Habilitar el inicio automático del servicio Docker (ya cubierto)
systemctl enable docker

# Configurar un contenedor específico para que se reinicie siempre
docker update --restart=always mi_contenedor_backend

Persistencia de Datos con Volúmenes de Docker

Los contenedores, por naturaleza, son entornos aislados y efímeros. Si un contenedor se elimina, todos sus datos internos se pierden. Para solucionar esto y permitir la gestión externa de configuraciones, Docker introduce el concepto de volúmenes.

Entendiendo los Volúmenes

Un volumen es el mecanismo preferido por Docker para la persistencia de datos generados y utilizados por los contenedores. Los volúmenes facilitan la vinculación de un directorio o archivo del sistema anfitrión (host) con un directorio o archivo dentro del contenedor. Esto asegura que los datos de la aplicación permanezcan disponibles incluso si el contenedor se destruye y se recrea.

Consideremos, por ejemplo, un servidor Nginx. Sus archivos estáticos (en /usr/share/nginx/html) y sus configuraciones (en /etc/nginx) son cruciales. Sin volúmenes, cualquier cambio o la eliminación del contenedor resultaría en la pérdida de estos datos. Al usar volúmenes, estos directoiros del contenedor se "montan" desde directorios específicos del host, permitiendo una gestión y persistencia sencillas.

Tipos de Montajes de Volúmenes

Existen dos tipos principales de montajes de volúmenes en Docker:

  • Volúmenes Nombrados (Named Volumes): Son completamente gestionados por Docker. Se crean y se les da un nombre, y Docker se encarga de su ubicación física en el host. Son ideales para almacenar datos de bases de datos o información que no necesita una ruta específica en el host.
  • Montajes de Enlace (Bind Mounts): Permiten mapear un archivo o directorio arbitrario en el sistema del host directamente a una ubicación dentro del contenedor. Son muy útiles para desarrollo (cuando se edita código en el host y se quiere ver los cambios en el contenedor al instante) o para configurar archivos específicos del host.

Comandos para Volúmenes Nombrados

Comando Descripción
docker volume create <nombre> Crea un nuevo volumen gestionado por Docker con el nombre especificado.
docker volume ls Lista todos los volúmenes nombrados presentes en el sistema.
docker volume rm <nombre> Elimina uno o más volúmenes nombrados.
docker volume inspect <nombre> Muestra información detallada sobre un volumen específico, incluyendo su ubicación en el host.
docker volume prune Elimina todos los volúmenes que no están siendo utilizados por ningún contenedor.

Los volúmenes nombrados también pueden ser creados automáticamente por Docker si se especifican en el comando docker run sin una ruta de host explícita.

Montaje de Directorios o Archivos del Host (Bind Mounts)

La sintaxis para montar directorios o archivos locales desde el host a un contenedor es la siguiente:

# Montar un directorio del host a un directorio del contenedor
-v /ruta/absoluta/en/el/host:/ruta/absoluta/en/el/contenedor

# Montar un archivo del host a un archivo del contenedor
-v /ruta/absoluta/en/el/host/config.ini:/ruta/absoluta/en/el/contenedor/config.ini

Ejemplo de Despliegue de MySQL con Bind Mounts

Para asegurar que los datos y la configuración de MySQL persistan, incluso si el contenedor se elimina, se pueden utilizar montajes de enlace. Aquí se muestra cómo configurar el contenedor de MySQL para usar directorios locales para datos, scripts de inicialización y configuración:

docker run -d \
  --name servidor_bd_persistencia \
  -p 3308:3306 \
  -e MYSQL_ROOT_PASSWORD=miContrasenaFuerte \
  -e TZ=America/Mexico_City \
  -v ~/docker/mysql/data:/var/lib/mysql \
  -v ~/docker/mysql/initdb:/docker-entrypoint-initdb.d \
  -v ~/docker/mysql/conf:/etc/mysql/conf.d \
  mysql:8.0

En este ejemplo:

  • ~/docker/mysql/data en el host se mapea a /var/lib/mysql en el contenedor para la persistencia de datos.
  • ~/docker/mysql/initdb se mapea a /docker-entrypoint-initdb.d, permitiendo ejecutar scripts SQL personalizados en el primer arranque del contenedor.
  • ~/docker/mysql/conf se mapea a /etc/mysql/conf.d para configurar MySQL con archivos de configuración personalizados.

Es importante que las rutas en el host (ej. ~/docker/mysql/data) sean rutas absolutas o relativas al directorio actual de ejecución, y comiencen con / o ./ para ser reconocidas como bind mounts.

Creación de Imágenes Personalizadas con Dockerfile

Un Dockerfile es un archivo de texto que contiene una serie de instrucciones para construir una imagen de Docker personalizada. Permite automatizar el proceso de creación de imágenes, asegurando que las aplicaciones y sus dependencias se empaqueten de manera consistente.

Instrucciones Fundamentales en un Dockerfile

A continuación, se describen las instrucciones más comunes utilizadas en un Dockerfile:

Instrucción Descripción Ejemplo
FROM <imagen_base>[:<tag>] Especifica la imagen base sobre la cual se construirá la nueva imagen. Cada Dockerfile debe comenzar con esta instrucción. FROM openjdk:17-jdk-slim
LABEL <clave>="<valor>" Añade metadatos a la imagen, como información del autor o versión. LABEL maintainer="desarrollador <dev@example.com>"
ENV <clave> <valor> Establece variables de entorno que estarán disponibles dentro del contenedor en tiempo de ejecución. ENV APP_PORT=8080
ARG <nombre_variable>[=<valor_predeterminado>] Define variables que se pueden pasar al comando docker build. No persisten en el contenedor final. ARG VERSION_API=1.0.0
COPY <origen> <destino> Copia archivos o directorios desde el contexto de construcción del host (donde se ejecuta docker build) a la imagen. COPY ./mi_aplicacion.jar /app/app.jar
RUN <comando> Ejecuta comandos en una nueva capa sobre la imagen actual. Útil para instalar paquetes o configurar el entorno dentro de la imagen. RUN apt-get update && apt-get install -y vim
WORKDIR <ruta> Establece el directorio de trabajo para las instrucciones RUN, CMD, ENTRYPOINT, COPY y ADD que le sigan. WORKDIR /app
EXPOSE <puerto> Informa a Docker que el contenedor espera escuchar conexiones en el puerto de red especificado. No publica el puerto automáticamente. EXPOSE 8080
VOLUME <ruta_contenedor> Crea un punto de montaje para un volumen nombrado, útil para la persistencia de datos. VOLUME /var/log/mi-app
ENTRYPOINT ["<ejecutable>", "<param1>"] Define el comando principal que se ejecutará cuando el contenedor se inicie. Los argumentos de docker run se adjuntan a este comando. ENTRYPOINT ["java", "-jar", "app.jar"]
CMD ["<ejecutable>", "<param1>"] Proporciona el comando predeterminado para un contenedor en ejecución. Puede ser anulado completamente por los argumentos del comando docker run. CMD ["echo", "Hola Docker!"]

Ejemplo de Dockerfile para una Aplicación Java

Este Dockerfile demuestra cómo construir una imagen para una aplicación Java empaquetada como un archivo JAR, utilizando una imagen base de OpenJDK para un entorno optimizado:

# Utiliza una imagen base oficial de OpenJDK 17 en su versión "slim" para reducir el tamaño
FROM openjdk:17-jdk-slim

# Añade metadatos a la imagen para identificarla
LABEL autor="TuNombre"
LABEL version="1.0"
LABEL descripcion="Aplicación Java Spring Boot para API REST"

# Define el directorio de trabajo dentro del contenedor
WORKDIR /app

# Copia el archivo JAR de la aplicación desde el contexto de construcción al contenedor
# Se asume que el JAR se llama 'api-backend.jar' en el directorio 'target' del host
ARG JAR_FILE=target/api-backend.jar
COPY ${JAR_FILE} app-backend.jar

# Expone el puerto en el que la aplicación Java escuchará las conexiones
EXPOSE 8080

# Define el comando que se ejecutará cuando se inicie el contenedor
# Esto iniciará la aplicación Java con el JAR copiado
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app-backend.jar"]

Proceso de Construcción de la Imagen

Para construir la imagen a partir del Dockerfile (asumiendo que el Dockerfile y el JAR de la aplicación se encuentran en el directorio actual o en la ruta especificada por JAR_FILE), utilice el siguiente comando:

docker build -t mi-api-backend:1.0 .

  • -t mi-api-backend:1.0: Asigna un nombre (mi-api-backend) y una etiqueta de versión (1.0) a la imagen.
  • .: Indica el "contexto de construcción", que es la ruta donde Docker buscará el Dockerfile y los archivos a copiar. En este caso, el directorio actual.

Conectividad de Contenedores con Redes Docker

Para que los contenedores que forman parte de una misma aplicación o ecosistema puedan comunicarse entre sí (por ejemplo, una aplicación backend con una base de datos), Docker proporciona un sistema de redes internas. Esto permite que los contenedores se encuentren y se conecten de manera segura sin exponer puertos directamente al host.

Comandos para la Gestión de Redes Docker

Comando Descripción
docker network create <nombre_red> Crea una nueva red definida por el usuario.
docker network ls Lista todas las redes Docker disponibles en el sistema.
docker network rm <nombre_red> Elimina una o más redes definidas por el usuario.
docker network prune Elimina todas las redes que no están siendo utilizadas por ningún contenedor.
docker network connect <nombre_red> <id_o_nombre_contenedor> Conecta un contenedor existente a una red específica.
docker network disconnect <nombre_red> <id_o_nombre_contenedor> Desconecta un contenedor de una red específica.
docker network inspect <nombre_red> Muestra información detallada sobre una red, incluyendo los contenedores conectados a ella.

Ejemplo de Comunicación entre Contenedores Usando una Red Definida por el Usuario

Consideremos una aplicación Java (mi_app_backend) que necesita conectarse a una base de datos MySQL (servicio_bd_persistencia). Ambas pueden conectarse a la misma red para comunicarse por sus nombres:

# 1. Crear una nueva red para el proyecto
docker network create red_mi_proyecto

# 2. Conectar el contenedor de la base de datos a la red
# Asumimos que 'servicio_bd_persistencia' ya fue creado.
docker network connect red_mi_proyecto servidor_bd_persistencia

# 3. Conectar el contenedor de la aplicación Java a la misma red
# Si se crea la app ahora: docker run --name mi_app_backend --network red_mi_proyecto ...
# Si ya existe:
docker network connect red_mi_proyecto mi_app_backend

# 4. Verificar la comunicación desde dentro del contenedor de la aplicación
docker exec -it mi_app_backend bash
# Dentro del contenedor de la aplicación, ahora se puede hacer ping al servicio de la base de datos por su nombre:
ping servidor_bd_persistencia

Dentro de una red definida por el usuario, Docker proporciona resolución de DNS, permitiendo que los contenedores se refieran entre sí por sus nombres de servicio (o nombres de contenedor si no están en Docker Compose) en lugar de direcciones IP.

Orquestación Multi-Contenedor con Docker Compose

Cuando un proyecto involucra múltiples servicios interconectados (como una base de datos, un backend de API, un front end web y un servidor de caché), gestionarlos individualmente con comandos docker run puede volverse complejo y propenso a errores. Docker Compose simplifica enormemente este proceso, permitiendo definir y ejecutar aplicaciones multi-contenedor completas utilizando un solo archivo de configuración en formato YAML, típicamente llamado docker-compose.yml.

Estructura y Ejemplo de un Archivo docker-compose.yml

Un archivo docker-compose.yml describe la arquitectura de la aplicación, incluyendo todos los servicios, redes y volúmenes necesarios. A continuación, se presenta un ejemplo de un archivo para un stack común de aplicación web que incluye MySQL, una aplicación Java Spring Boot y Nginx:

version: '3.8'

services:
  # Servicio de base de datos MySQL
  db_app:
    image: mysql:8.0
    container_name: mi_db_mysql
    environment:
      MYSQL_ROOT_PASSWORD: superClaveSegura!
      TZ: America/Bogota
    ports:
      - "3306:3306" # Mapeo de puerto host:contenedor
    volumes:
      - ./infra/mysql/data:/var/lib/mysql # Persistencia de datos
      - ./infra/mysql/conf:/etc/mysql/conf.d # Configuraciones personalizadas
    networks:
      - red_aplicacion_global

  # Servicio de la aplicación Java Spring Boot (backend)
  backend_api:
    build:
      context: ./src/backend-java # Ruta al Dockerfile de la aplicación Java
      dockerfile: Dockerfile
      args:
        JAR_FILE: target/api-backend.jar # Nombre del JAR dentro del contexto de construcción
    container_name: mi_api_backend
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://db_app:3306/nombre_db?serverTimezone=America/Bogota
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: superClaveSegura!
      TZ: America/Bogota
    ports:
      - "8080:8080" # Mapeo de puerto host:contenedor para la API
    depends_on:
      - db_app # Asegura que la base de datos se inicie antes que el backend
    networks:
      - red_aplicacion_global

  # Servicio del servidor web Nginx (proxy inverso y servidor de estáticos)
  frontend_web:
    image: nginx:1.20.2
    container_name: mi_nginx_frontend
    ports:
      - "80:80" # Mapeo de puerto HTTP estándar
      - "443:443" # Mapeo de puerto HTTPS
    volumes:
      - ./infra/nginx/html:/usr/share/nginx/html # Archivos estáticos
      - ./infra/nginx/conf.d:/etc/nginx/conf.d # Configuraciones de sitios virtuales
      - ./infra/nginx/nginx.conf:/etc/nginx/nginx.conf # Configuración principal de Nginx
    networks:
      - red_aplicacion_global
    depends_on:
      - backend_api # Nginx puede depender del backend si lo proxy

networks:
  red_aplicacion_global:
    name: red-de-despliegue

En este ejemplo, la estructura de directorios esperada en el host, relativa al docker-compose.yml, podría ser:

  • ./src/backend-java/: Contiene el Dockerfile y los archivos para construir la imagen del backend.
  • ./infra/mysql/data/: Para la persistencia de datos de MySQL.
  • ./infra/mysql/conf/: Para archivos de configuración personalizados de MySQL.
  • ./infra/nginx/html/: Para archivos estáticos servidos por Nginx.
  • ./infra/nginx/conf.d/: Para configuraciones específicas de Nginx (ej., virtual hosts).
  • ./infra/nginx/nginx.conf: Archivo de configuración principal de Nginx.

Comandos Clave de Docker Compose

Una vez que el archivo docker-compose.yml está configurado, la gestión de la aplicación multi-contenedor se simplifica a unos pocos comandos:

Comando Descripción
docker compose up [-d] [--build] Construye (si es necesario) y levanta todos los servicios definidos en el archivo. -d ejecuta los contenedores en segundo plano (detached mode). --build fuerza la reconstrucción de las imágenes de los servicios que especifican un Dockerfile.
docker compose down [--volumes] [--remove-orphans] Detiene y elimina los contenedores, redes y, opcionalmente, volúmenes creados por Compose.
docker compose ps Muestra el estado de los contenedores de los servicios.
docker compose logs [servicio] Muestra los logs de salida de uno o todos los servicios.
docker compose stop [servicio] Detiene los servicios especificados (o todos si no se especifica ninguno) sin eliminarlos.
docker compose start [servicio] Inicia los servicios detenidos.
docker compose restart [servicio] Reinicia los servicios.
docker compose exec <servicio> <comando> Ejecuta un comando arbitrario dentro de un servicio en ejecución.

Etiquetas: Docker docker-compose contenedores despliegue linux

Publicado el 10-10 03:59