Docker compose en un proyecto


Como desarrolladores nos interesará crear Docker Compose complejos, que serán parte de un proyecto (puede ser cualquier tipo de proyecto: Java, Python, Go, etc.) que haremos paso a paso.

Índice

Nota sobre este artículo: Aunque este artículo está concebido para ser independiente por si ya sabes algo de Docker Compose, también está pensado para ser continuación del anterior artículo sobre Docker Compose

Git: el código de en este artículo también lo puedes encontrar en https://github.com/Invarato/docker/tree/main/articulos-jarroba/docker-compose-en-un-proyecto

Ejemplo de proyecto utilizando Docker compose

Para este ejemplo voy a hacer un “proyecto web PHP” (es decir, un servidor PHP que procesa datos de nuestra base de datos y los muestra en la web; esto es parecido al primer artículo sobre Docker básico, si vienes de él, pero en vez de usar la imagen de “httpd” usaremos la de “php” que es parecida, pero con PHP instalado), que nos mostrará una web donde veremos los datos del a base de datos y podremos insertar nuevos; también quiero inicializar la base de datos con una tabla y unos datos de ejemplo; además, quiero desarrollar en condiciones, por lo que queremos visibilidad de los datos de la base de datos con un administrador web.

Primero, diseñaremos un primer esbozo de la arquitectura que vamos a realizar, con las imágenes Docker que usaremos (añado su documentación oficial para poder consultarla):

Para realizar este proyecto crearé una carpeta “miDockerCompose”; inmediatamente dentro, crearemos el fichero “compose.yml”, una primera carpeta “scripts” (dentro crearemos un fichero “init.sql” para inicializar la base de datos) y una segunda carpeta “www” (dentro crearemos un fichero “index.php” para crear nuestra web con la conexión con la base de datos). Tendrá un aspecto somo la siguiente captura:

Empezaremos rellenando el fichero “compose.yml”, para ello necesitamos saber qué añadir dentro (consultaríamos la documentación oficial, pero en este artículo te lo cuento yo). Te voy a enseñar el aspecto que tendrá la arquitectura completa que montaremos y, tras este dibujo, te paso a explicar uno a uno (explicaré lo nuevo, lo que ya se explicó anteriormente solo te pongo el valor), que te recomiendo que revises a la par con el contenido de “compose.yml” que te he puesto detrás:

Servicio para la base de datos que he llamado “bd” configurado con:

  • Imagen Docker: mysql:8.3.0
  • Container name: miMySQL-container
  • Restart: always (para cuando le ocurra algo al contenedor, como caerse por un error, que se vuelva a levantar automáticamente; este parámetro no es necesario y por defecto es “no”, pero quería ponértelo para que lo)
  • Ports: 3306:3306
  • Environment variable: (esta vez sí podremos contraseña root “contrseniaroot” y crearemos una base de datos “mibd” con usuario “usuariobd” y contraseña “contraseniabd”):
    • MYSQL_ROOT_PASSWORD: contrseniaroot
    • MYSQL_USER: usuariobd
    • MYSQL_PASSWORD: contraseniabd
    • MYSQL_DATABASE: mibd
  • Volumes:
    • Volumen con nombre “bd-data” (ojo, esto es igual que antes, pero le he cambiado de nombre con respecto al anterior ejemplo, puesto que el sufijo “-data” es más claro y está más extendido su uso entre la comunidad) apuntando a/var/lib/mysql”.
    • Bind Mount apuntando en nuestro ordenador local a la ruta “./scripts/init.sql” (“.” de la ruta relativa con respecto al “compose.yml”, la carpeta “scripts” y al fichero “init.sql”) y este anterior apuntando a “/docker-entrypoint-initdb.d/init.sql” de dentro del contenedor (concomeos esta última ruta por la documentación oficial).

Servicio para el administrador de la base de datos que he llamado “adminer” configurado con:

  • Imagen Docker: adminer:4.8.1
  • Container name: adminer-container
  • Restart: always
  • Depends on: bd (aquí pondremos una dependencia al anterior servicio “bd”, puesto que, si no se ha levantado la base de datos, el administrador de la base de datos no tiene nada que administrar, además, si fuera sumamente dependiente tendríamos errores al no encontrar la base de datos).
  • Ports: 8081:8080 (podría haber dejado “8080:8080”, pero quería cambiártelo para que vieras la diferencia de puerto local y puerto del contenedor en este artículo 😉 )

Servicio para la base de datos que he llamado “bd” configurado con:

  • Imagen Docker: php:8-apache
  • Container name: php_web-container
  • Restart: always
  • Depends on: bd
  • Ports: 8000:80
  • Volumes:
    • Bind Mount apuntando en nuestro ordenador local a la ruta “./www” (a diferencia del anterior “Bind Mount” que apuntaba a un fichero “init.sql”, aquí quiero que sea todo el contenido de la carpeta el que se pase, puesto que es posible que queramos poner muchos ficheros web: HTML, CSS, JavaScript, PHP, etc.) y este anterior apuntando a “/var/www/html” de dentro del contenedor.
  • Command: bash -c «docker-php-ext-install mysqli && apache2-foreground» (este PHP viene “virgen de módulos” para que le instalemos los que nos sean necesarios; en este caso me es necesario el módulo de “mysqli” para realizar la conexión con la base de datos MySQL del otro contenedor, por lo que lo instalo usando el comando “docker-php-ext-install”. Lo que te voy a contar a continuación lo veremos con detalle más adelante, pero ya te adelanto para que lo vayas entendiendo y juntando las piezas: “Command” ejecuta el comando que nosotros queramos sobrescribiendo el última capa “CMD” del “Dockercompose” de la imagen “php:8-apache” que si tienes curiosidad es “CMD [«apache2-foreground»]”, de ahí que termine el comando con “apache2-foreground”, esto hace que el contenedor sea Daemon, sino sería un efímero y se cerraría inmediatamente al terminar, tal y como vimos en el primer artículo; por cierto, esta manera de instalar módulos a PHP con “command” no es la más recomendable, sería mejor crear un nuevo “Dockercompose”, pero para este ejemplo lo vamos a hacer así).

Todos estos servicios estarán conectados entre sí por una red (network) por defecto (se llama “default”) que gestiona Docker y existe siempre (la puedes ver en el dibujo anterior). Nosotros no tenemos que hacer nada, porque al no especificarla en el “compose.yml”, se asume que queremos que cada servicio se conecte a ésta.

En resumen, el Docker Compose completo “compose.yml” que tendremos con todo esto es:

version: '3.8'


services:

  bd:
    image: mysql:8.3.0
    container_name: miMySQL-container
    restart: always
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: contrseniaroot
      MYSQL_USER: usuariobd
      MYSQL_PASSWORD: contraseniabd
      MYSQL_DATABASE: mibd
    volumes:
      - bd-data:/var/lib/mysql
      - ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql

  adminer:
    image: adminer:4.8.1
    container_name: adminer-container
    restart: always
    depends_on:
      - bd
    ports:
      - '8081:8080'

  web:
    image: php:8-apache
    container_name: php_web-container
    restart: always
    depends_on:
      - bd
    ports:
      - "8000:80"
    volumes:
      - ./www:/var/www/html
    command: bash -c "docker-php-ext-install mysqli && apache2-foreground"

volumes:
  bd-data:

Ahora crearemos el contenido de “init.sql” dentro de la carpeta “scripts”, donde tendremos con “CREATE” una tabla que he llamado “mi_tabla”, con una columna “id” como clave primaria, una columna “nombre” y una columna “apellido”; además, crearé un “INSERT” con algunos datos:

CREATE TABLE IF NOT EXISTS mi_tabla (
    id SERIAL PRIMARY KEY,
    nombre VARCHAR(255) NOT NULL,
    apellido VARCHAR(255) NOT NULL
);

INSERT INTO mi_tabla (nombre, apellido) VALUES('Juan', 'Sanchez'), ('Laura', 'Lopez'), ('Paco', 'Diaz');

Antes de crear el fichero “index.php”, abriremos una terminal y vamos a levantar nuestro “compose.yml” llamando al comando (tenemos que estar dentro de la carpeta “miDockerCompose”):

docker compose up

Vamos a probar que “adminer” se haya levantado bien (también podríamos echar un vistazo a los logs del contenedor, pero así es más visual). Para ello abrimos cualquier navegador web que tengamos instalado (yo usare Chrome) y en la barra de direcciones escribiremos: http://localhost:8081/  (el puerto “8081” es el local que pusimos antes en el servicio de “adminer” en “compose.yml”)

Cuando se abra “adminer” nos pedirá una serie de datos, que son los que configuramos previamente en nuestro servicio “bd” (gracias al “network default” podemos usar como “IP” el “nombre del servicio”, es decir, que en el campo “servidor” de “adminer” no pondremos algo tal como “http://192.1.2.3…”, sino solo el nombre del servicio “bd” y Docker por debajo, con su “network default”, nos hace muy fácil el trabajo):

Dentro, podremos ver que hemos creado bien nuestra tabla en nuestra base de datos MySQL:

Y si entramos en la tabla, en la pestaña “Visualizar contenido” podremos ver nuestros datos cargados sin haber hecho nosotros nada, es decir, que todo funcionando como la seda:

¿Por qué no hemos rellenado el “index.php” antes? Esto lo hemos hecho, porque el servidor “Httpd” dentro de la imagen de PHP nos permite añadir en caliente ficheros web.

A continuación, queremos crear nuestra página web, que se compondrá de las siguientes partes:

  • Lectura de la base de datos: donde pintará en nuestra web todo el listado con los datos, pero solo los nombres y apellidos.
  • Grabación en la base de datos: un formulario donde se nos pida el nombre y el apellido.
  • Refresco automático: para ver los datos al instante.

Es decir, queremos ver lo siguiente:

Ahora sí, crearemos el fichero con el contenido de “index.php” dentro de la carpeta “www” con el siguiente contenido que cumple lo que queríamos (aquí no se va a explicar el código PHP, puesto que en este artículo lo que nos interesa es ver cómo trabajar en un proyecto cualquiera con “compose.yml”):

<?php
$server = 'bd';
$username = 'usuariobd';
$password = 'contraseniabd';
$dbname = 'mibd';

$conn = new mysqli($server, $username, $password, $dbname);
if ($conn->connect_error) {
    die("Fallo de conexión a la base de datos: " . $conn->connect_error);
}

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $name = $_POST['nombre'];
    $value = $_POST['apellido'];

    $query = "INSERT INTO mi_tabla (nombre, apellido) VALUES (?, ?)";
    $stmt = $conn->prepare($query);
    $stmt->bind_param('ss', $name, $value);
    $stmt->execute();
    echo "¡Nuevo registro añadido a la tabla mi_tabla de la base de datos!<br>";
}

$query = "SELECT * FROM mi_tabla";
$result = $conn->query($query);

echo "<b>Registros guardados en mi_tabla:</b><br>";
echo "Nombre | Apellido<br>";
echo "==============<br>";

if ($result->num_rows > 0) {
    while($row = $result->fetch_assoc()) {
        echo $row['nombre']. " | " . $row['apellido'] . "<br>";
    }
} else {
    echo "0 resultados encontrados en la tabla mi_tabla";
}

$conn->close();
?>

<!DOCTYPE html>
<html lang="es">
<body>
<br><br><br>
<b>Nuevo registro a guardar en mi_tabla:</b>
<form method="post" action="index.php">
    <label for="nombre">Nombre:</label><br>
    <input type="text" id="nombre" name="nombre"><br>
    <label for="apellido">Apellido:</label><br>
    <input type="text" id="apellido" name="apellido"><br>
    <input type="submit" value="Submit">
</form>

</body>
</html>

Al igual que hicimos con “adminer”, las credenciales de la base de datos son las que pusimos en el “compose.yml”:

Terminamos abriendo otra pestaña de tu navegador y ve a: http://localhost:8000/

Y puedes probar qué funciona:

¡Enhorabuena, has hecho la labor de Devops! Pero recuerda, este artículo no es de Devops (aunque se lo recomiendo a todo el mundo técnico), sino para que tengas una compresión más completa de todo el ciclo de vida del proyecto (seas desarrollador, de devops, manager, etc.) ; además, si eres desarrollador, para que puedas realizar pruebas de tus proyectos lo más próximo posible a sistemas (cercanas al “entorno de producción”, pero sin necesidad de realizar las pruebas directamente en “producción” XD ).

Continúa el curso de Docker

Puedes continuar con la siguiente parte de este curso en:

Comparte esta entrada en:
Safe Creative #1401310112503
Docker compose en un proyecto por "www.jarroba.com" esta bajo una licencia Creative Commons
Reconocimiento-NoComercial-CompartirIgual 3.0 Unported License.
Creado a partir de la obra en www.jarroba.com

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies

ACEPTAR
Aviso de cookies