Infraestructura LAMP con Docker Compose

Aprende cómo arrancar un entorno LAMP con Docker Compose con unos sencillos pasos y explicada de la mejor manera para que construyas tus aplicaciones sobre un sistema distribuido bien organizado.

Por supuesto, mi intención además de proponerte la idea, es proporcionarte las herramientas para que puedas ejecutar el entorno LAMP sobre Docker, usando Docker Compose, sin importar en que sistema operativo o distribución estés utilizándolo, a fin de cuentas, se trata de contenedores y obviamente, será ejecutado sobre Docker.

No hace falta que lo diga, pero esta infraestructura que te enseñaré a montar, también lo puedes ejecutar en cualquier proveedor de nube, bien pudiendo ser los más populares y potentes: AWS, Azure, GCP y otros donde se pueda tener acceso a un VPS.

Si te ha llamado la atención este contenido, pero aún no conoces mucho sobre Docker, igualmente, con toda honestidad te recomiendo el excelente Curso de introducción a Docker. Sin embargo, si ya dominas algunas cosas y deseas continuar explorando soluciones más complejas, por supuesto, no debes faltar en el Curso de docker para desarrolladores del experto en la materia, el Ing. Pablo Chico.

¿Por qué un sistema LAMP con Docker?

Porque pienso de verdad, que es la manera de ir escalando en tu nivel de conocimiento y de acción para desarrollar software.

Usualmente, si escribes tu código en un sistema operativo GNU/Linux, entonces utilizaras el mismo Stack, me refiero a LAMP, donde instalas todos los recursos (paquetes) de forma independiente para levantar un proyecto sobre el servidor Apache. Por otro lado, si escribes tu código sobre Windows, tendrás opciones populares cómo: WAMP Server, XAMPP, etc.

Para cualquier caso, XAMPP es funcional tanto para GNU/Linux como para Windows.

Así que, si eres un PHPero, necesitarás un Stack mínimo que contenga Apache, PHP y MySQL/MariaDB para ejecutar tu aplicación.

Por ende, las pilas:

  • LAMP (Linux, Apache, MySQL y PHP)
  • WAMP (Windows, Apache, MySQL y PHP)
  • XAMPP (X = Cualquier OS, Apache, MariaDB/MySQL, PHP y Perl)

Ahora en día, estamos inmersos en un avance incontrolable en la tecnología, que no permite que descanses si quieres mantenerte actualizado. Es cuando miramos que muchos sistemas, para desplegarlos en producción o en un punto de desarrollo, se mantienen sistemas homogéneos y estructurados, es donde la infraestructura como código toca a la puerta, brindando facilidades en automatización de procesos.

Si estás usando Docker, perfectamente puedes prescindir de las pilas anteriormente mencionadas, excepto, LAMP, que es el término general donde se representa cada servicio. Pero tú dirás ¿pero en Linux? a lo cual te diré que sí, porque el contenedor donde se instalarán los servicios será en GNU/Linux, en una distro que te brindará rapidez y optimización para con los recursos a instalar.

Propongo Docker por la contenerización, de formar un ambiente donde tu aplicación no falle por temas de dependencias y que tengas un script fiable a la mano que siempre puedas correr. Además, te familiarizas más con los sistemas GNU/Linux y shell scripting.

Aprende DevOps para acelerar tus desarrollos de Software
Aprende las herramientas más importantes para acelerar el desarrollo de software: Jenkins, Docker, Kubernetes, Vagrant y más.
Comenzar gratis ahora

¿Cuál es la diferencia entre Docker y Docker Compose?

En principio, se sabe que docker utiliza un fichero para automatizar la creación de imágenes que se lee desde un archivo Dockerfile. Siendo este un documento de texto que contiene todos los comandos que un usuario puede llamar desde línea de comandos para ensamblar una imagen.

Docker Compose es una herramienta que permite definir y ejecutar aplicaciones Docker compuesto por varios contenedores, simplificando el uso de Docker y entender de una manera sencilla.

Docker Compose permite un tipo de sintaxis más elaborado y fácil de entender, por tal razón le ha ganado la batalla en la composición de scripts para contenerización. El fichero de instrucciones que utiliza Docker Compose es docker-compose.yml haciendo diferencia con el fichero .Dockerfile .

Práctica de una pila LAMP

Una pila es un conjunto de servicios o paquetes instalables y que se relacionan entre si. En el siguiente diagrama se puede observar una pila LAMP (GNU/Linux, Apache, MySQL y PHP).

Como descripción básica, nos damos cuenta que en primera instancia tenemos 3 contenedores que conforman un sistema distribuido, todos ellos interconectados entre sí. Cada contenedor tiene servicios distintos que hará que la pila LAMP se constituye por medio de una red local.

Servidor web:

  • El contenedor tiene por nombre www.
  • Se expone el puerto 80.

Servidor de base de datos:

  • El contenedor tiene por nombre db.
  • Se expone el puerto 3306.

Servidor del sistema gestor de base de datos gráfico:

  • El contenedor tiene por nombre phpmyadmin.
  • Se expone el puerto 8000.

Se procede a descargar el repositorio docker-lamp que se encuentra en GitHub, de la siguiente manera.

git clone https://github.com/jersonmartinez/docker-lamp.git

En mi caso lo he dejado en Documents/OpenWebinars/docker-lamp.
La estructura del proyecto es la siguiente:

Ahora bien, hagamos una inspección rápida de los ficheros de configuración que tenemos en este repositorio.
En este sencillo dockerfile logramos observar que requerimos una imagen de php en la versión 8.0.0 con apache.

Se define un argumento donde se modifica una variable de entorno DEBIAN_FRONTED a modo no interactivo, que lo define para que los demás paquetes de instalación en consecuencia que soliciten una confirmación de algo en ejecución, lo omitan.

Las demás ejecuciones ya son para instalar extensiones de comunicación con docker, php y mysqli, así como las actualizaciones del repositorio de paquetes y por último la instalación de librerías y dependencias.

FROM php:8.0.0-apache
ARG DEBIAN_FRONTEND=noninteractive
RUN docker-php-ext-install mysqli
RUN apt-get update \
    && apt-get install -y libzip-dev \
    && apt-get install -y zlib1g-dev \
    && rm -rf /var/lib/apt/lists/* \
    && docker-php-ext-install zip

RUN a2enmod rewrite

Finalmente, el mágico fichero de configuración de Docker Compose. En este punto, encontramos 3 bloques importantes donde se definen los servicios: www, db y phpmyadmin.

Si analizan la configuración, pueden observar que es tan sencillo que se explica solo, incluso porque antes he explicado un poco el diagrama de stack LAMP y de los servicios como tal.

version: "3.1"
services:
    www:
        build: .
        ports: 
            - "80:80"
        volumes:
            - ./www:/var/www/html
        links:
            - db
        networks:
            - default
    db:
        image: mysql:8.0
        ports: 
            - "3306:3306"
        command: --default-authentication-plugin=mysql_native_password
        environment:
            MYSQL_DATABASE: dbname
            MYSQL_USER: root
            MYSQL_PASSWORD: test
            MYSQL_ROOT_PASSWORD: test 
        volumes:
            - ./dump:/docker-entrypoint-initdb.d
            - ./conf:/etc/mysql/conf.d
            - persistent:/var/lib/mysql
        networks:
            - default
    phpmyadmin:
        image: phpmyadmin/phpmyadmin
        links: 
            - db:db
        ports:
            - 8000:80
        environment:
            MYSQL_USER: root
            MYSQL_PASSWORD: test
            MYSQL_ROOT_PASSWORD: test 
volumes:
    persistent:

Se utiliza la versión de Docker Compose que pueda darle ejecución a la versión 3.1. La versión 1.21 de docker compose viene bien. Para conocer este dato, solo deben escribir:

docker-compose -v

Los contenedores se catalogan como servicios. En esta configuración son 3 y la explicación de cada uno de los bloques es la siguiente:

www

Si hay algo que falta por comentar, por supuesto, serán los atributos:

  • build tiene como valor ., indicando que se construirá el .dockerfile sobre este contenedor.
  • ports mantendrá el mismo puerto con que el servicio internamente está corriendo, que es el 80 y será expuesto hacia afuera.
  • volumes aplica una definición de donde sincronización entre 2 directorios o volumen de datos, para ello, se sincroniza ./www con /var/www/html que se encuentra en el contenedor.
  • links selecciona con quien podrá verse y compartir recursos en red, para ello es necesario que el servidor web esté conectado al sistema gestor de base de datos que se encuentra en el contenedor db.
  • networks define el nombre de la red por la cual estará conectados los contenedores, para este caso default.
www:
    build: .
    ports: 
        - "80:80"
    volumes:
        - ./www:/var/www/html
    links:
        - db
    networks:
        - default

Es habitual que cambiemos el puerto 80 que se expone, pues muchas veces tenemos aplicaciones que ya están corriendo sobre ese puerto. Esto, para tenerlo en cuenta si algo falla.

db

Este bloque tiene un poco más de atributos que revisar, como los siguientes:

  • En primera instancia comentar que no tiene un atributo build, dando a entender que no es necesario instalar paquetes desde un .dockerfile como si lo necesitaba el contenedor www.
  • image selecciona la de mysql en la versión 8.0.
  • ports mantendrá el mismo puerto con que el servicio internamente está corriendo, que es el 3306 y será expuesto hacia afuera.
  • command es funcional para que se inserte un comando, donde dicha instrucción será para habilitar la autenticación con la contraseña nativa de MySQL.
  • environment es la aplicación de variables de entorno, donde este contenedor recibe 4 con el prefijo de MYSQL_ donde escribe el nombre de la base de datos, nombre de usuario y contraseña.
  • volumes aplica una definición de donde sincronización entre ficheros, así como la configuración y donde corre el demonio de MySQL.
  • networks define el nombre de la red por la cual estará conectados los contenedores, para este caso default, donde ya se puede conocer con el contenedor www.
db:
    image: mysql:8.0
    ports: 
        - "3306:3306"
    command: --default-authentication-plugin=mysql_native_password
    environment:
        MYSQL_DATABASE: dbname
        MYSQL_USER: root
        MYSQL_PASSWORD: test
        MYSQL_ROOT_PASSWORD: test 
    volumes:
        - ./dump:/docker-entrypoint-initdb.d
        - ./conf:/etc/mysql/conf.d
        - persistent:/var/lib/mysql
    networks:
        - default

Desde esta configuración, ya se importa la base de datos que se encuentra en dump\dbname.sql.

Es habitual que cambiemos el puerto 3306 que se expone, pues muchas veces tenemos aplicaciones que ya están corriendo sobre ese puerto. Esto, para tenerlo en cuenta si algo falla.

Se hace notar que en esta configuración tampoco existe el atributo links para conectar con algún otro contenedor. Esto pasa porque el contenedor donde se encuentra el sistema gestor de base de datos, en este caso MySQL, quedará aislado, proveyendo seguridad en su acceso y ortogándoselo a los que realmente necesitan hacer consultas, tal como el contenedor de www y phpmyadmin.

phpmyadmin

Este bloque tiene menos atributos y básicos como los que ya hemos abordado:

  • image selecciona la de phpmyadmin/phpmyadmin.
  • ports es diferente a los demás, porque internamente el servicio está funcionando con el puerto 80, pero al exponerlo se necesita hacer con otro, puesto que colisionaría con el puerto que tiene expuesto el contenedor del servidor web www. De este modo, se expone el puerto 8000.
  • links se conecta con el contenedor db para tener acceso al sistema gestor de base de datos.
  • environment es la aplicación de variables de entorno, donde este contenedor recibe 3 con el prefijo de MYSQL_ donde escribe el nombre de usuario, contraseña y contraseña de root.
phpmyadmin:
    image: phpmyadmin/phpmyadmin
    links: 
        - db:db
    ports:
        - 8000:80
    environment:
        MYSQL_USER: root
        MYSQL_PASSWORD: test
        MYSQL_ROOT_PASSWORD: test

Todos los volúmenes se encuentran en modo persistente, por lo que no hay pérdida de datos y de enlace.

Este es el .sql que se importa a MySQL.

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

CREATE TABLE `Data` (
  `id` int(11) NOT NULL,
  `name` varchar(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `Data` (`id`, `name`) VALUES (1, 'OpenWebinars Article'), (2, 'Crashell'), (3, 'Jerson Martinez'), (4, 'Antonio Moreno');

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Este trozo de código php ubicado en www/index.php es para comprobar que el sitio carga bien y que existe correcta conectividad con la base de datos y que este puede dar lectura.

<html>
    <head>
        <title>Welcome to LAMP Infrastructure</title>
        <meta charset="utf-8"> 
        <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
    </head>
    <body>
        <div class="container-fluid">
            <?php
                echo "<h1>¡Hola, OpenWebinars te da la bienvenida!</h1>";

                $conn = mysqli_connect('db', 'root', 'test', "dbname");
                $query = 'SELECT * From Data';
                $result = mysqli_query($conn, $query);

                echo '<table class="table table-striped">';
                echo '<thead><tr><th></th><th>id</th><th>name</th></tr></thead>';
                while($value = $result->fetch_array(MYSQLI_ASSOC)){
                    echo '<tr>';
                    echo '<td><a href="#"><span class="glyphicon glyphicon-search"></span></a></td>';
                    foreach($value as $element){
                        echo '<td>' . $element . '</td>';
                    }

                    echo '</tr>';
                }
                echo '</table>';

                $result->close();
                mysqli_close($conn);
            ?>
        </div>
    </body>
</html>

A partir de este punto, necesitaremos ejecutar todas las instrucciones de consola con privilegios elevados, para ello, un sudo su y poner su contraseña bastará.

Para lanzar la configuración, se ejecuta la siguiente instrucción:

docker-compose up -d

Como pueden observar en la imagen, los datos resultantes son de que se crean 3 contenedores, en principio con los prefijos docker-lamp_ que es el directorio donde se encuentra, procediendo a concatenar el nombre del contenedor y el sufijo _1 con resultado done.

Para verificar los contenedores corriendo desde Docker Compose:

docker-compose ps

Es notable un mayor detalle del estado como de los puertos expuestos, así como el protocolo y la dirección local ::1, 127.0.0.1 o localhost por medio de 0.0.0.0. El comando docker-compose ps es equivalente a docker ps, solo que este último con mayor detalle.

Con esto bastará para hacer la prueba en un navegador y escribir 127.0.0.1, equivalente a 127.0.0.1:80 y localhost:80 con el puerto opcional.

Se procede a realizar una consulta a la dirección 127.0.0.1:8000 para llegar al contenedor phpmyadmin. El usuario y la contraseña han sido definidos en el .yml.

  • Usuario: root
  • Contraseña: test

Se comprueba que el acceso ha sido correcto. Por otro lado, se verifica que la base de datos se encuentra bien importada con su respectiva tabla y datos dentro, listo para ser gestionada.

A partir de acá, el proyecto que vayas a trabajar, tendrá que colocarlo dentro del directorio www/, por ejemplo, si tu proyecto se llama Crashell, lo revisarás en tu navegador como 127.0.0.1/Crashell.

Para detener el stack con Docker Compose, es de esta manera:

docker-compose stop

Así de fácil.

Aumenta la productividad de tu equipo de desarrollo
Desarrolla el talento de tu empresa 3 veces más rápido con formaciones prácticas y avanzadas de Cloud Computing y DevOps.
Solicitar más información

Conclusión

Conceptualmente y prácticamente ha sido abordado el tema, de una forma meticulosa para que puedas arrancar con tus proyectos web de una forma más profesional, dando el siguiente paso con Docker utilizando Docker Compose y con ello, personalizando tu stack LAMP para tus desarrollos.

Ya con esto, podemos firmar que en producción también funciona.

Las cookies nos permiten ofrecer nuestros servicios. Al utilizar nuestros servicios, aceptas el uso que hacemos de las cookies. Más Información