Este post es un tutorial paso a paso para configurar laravel con docker-compose para poder corre nuestras aplicaciones dockerizadas en un ambiente local.

Asumiré que ya tienen los siguientes requisitos:

  • Ubuntu 18.04 (o cualquier sistema operativo linux basado en debian)
  • Docker

Les recomiendo leer todo el post antes de ponerse manos a la obra para entender bien el proceso 🙂

Descargar E Instalar Laravel

Normalmente lo que haríamos para instalar laravel sería hacerlo con composer, no obstante, el objetivo de hacerlo con docker es desacoplar nuestra configuración lo mas que podamos de nuestro ambiente local.

Lo primero es clonar el código de laravel en un directorio que nos guste:

git clone https://github.com/laravel/laravel.git laravel-app
cd laravel-app

Luego de hacer eso usaremos la imagen de composer con el objetivo de crear un contenedor efímero que copiara los archivos de laravel en el contenedor y se asegurara de que la carpeta vendor haya sido creada.

docker run --rm -v $(pwd):/app composer install

y por ultimo nos adueñamos del directorio

sudo chown -R $USER:$USER ~/laravel-app

Creando El Docker Compose

bien lo siguiente consiste en crear el archivo docker-compose.yml que contendrá las instrucciones para nuestro contenedor:

nano laravel-app/docker-compose.yml

Este archivo definirá 3 servicios: app, servidor web, y base de datos. Y se verá algo así:

version: '3'
services:

  #PHP
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: mi-app-laravel
    container_name: app
    restart: unless-stopped
    tty: true
    environment:
      SERVICE_NAME: app
      SERVICE_TAGS: dev
    working_dir: /var/www
    networks:
      - app-network

  #Nginx
  webserver:
    image: nginx:alpine
    container_name: webserver
    restart: unless-stopped
    tty: true
    ports:
      - "80:80"
      - "443:443"
    networks:
      - app-network

  #MySQL Service
  db:
    image: mysql:5.7.22
    container_name: db
    restart: unless-stopped
    tty: true
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: el_password_root
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    networks:
      - app-network

#Redes
networks:
  app-network:
    driver: bridge

Este docker file introduce en nuestro contenedor las siguientes características:

  • app: que contiene una imagen de laravel corriendo en /var/www con base en la imagen de php:7.2-apache
  • webserver: que es el servicio que tiene nuestro nginx y expone los puertos 80 y 443
  • db: que corre una base de datos mysql:5.7.22, expone el puerto 3306. Hay que notar que deben reemplazar las variables de entorno MYSQL_DATABASE y MYSQL_ROOT_PASSWORD, con sus propios valores.
  • Finalmente la red de la aplicación esta configurada con el controlador bridge que es el que hace posible que todos los servicios en este contenedor puedan comunicarse de forma exitosa.

Persistiendo Los Datos

Bien ahora que ya tenemos nuestro docker-compose hecho es necesario añadir un poco mas de configuración, ya que si lo dejamos así como esta cada vez que el contenedor se reinicie (o se caiga) toda la data se perderá.

Para estos fines agregamos un volumen al servicio db de nuestro contenedor de la siguiente manera:

db:
  ...
    volumes:
      - dbdata:/var/lib/mysql
      - ./mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - app-network
  ...

Entonces le decimos que todo lo que esta en /var/lib/mysql será replicado en la carpeta dbdata de nuestro sistema anfitrión. ademas persistimos la configuración de la base de datos en /etc/mysql/my.cnf.

Adicionalmente podemos decirle que este volumen que acabamos de crear se monte con el driver local:

#Volumenes
volumes:
  dbdata:
    driver: local

Con esta definición la data de este volumen podrá ser usada por todos los servicios del contenedor.

Para desacoplar aun mas nuestra configuración, podemos hacer algo similar con la de php y la de nginx:

app:
  ...
  volumes:
       - ./:/var/www
       - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
  networks:
      - app-network
  ...
webserver:
  ...
  volumes:
      - ./:/var/www
      - ./nginx/conf.d/:/etc/nginx/conf.d/
  networks:
      - app-network

El resultado final deberá ser un docker-compose como este:

version: '3'
services:

  #PHP
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: mi-app-laravel
    container_name: app
    restart: unless-stopped
    tty: true
    environment:
      SERVICE_NAME: app
      SERVICE_TAGS: dev
    working_dir: /var/www
    volumes:
      - ./:/var/www
      - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
    networks:
      - app-network

  #Nginx
  webserver:
    image: nginx:alpine
    container_name: webserver
    restart: unless-stopped
    tty: true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./:/var/www
      - ./nginx/conf.d/:/etc/nginx/conf.d/
    networks:
      - app-network

  #MySQL
  db:
    image: mysql:5.7.22
    container_name: db
    restart: unless-stopped
    tty: true
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: your_mysql_root_password
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - dbdata:/var/lib/mysql/
      - ./mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - app-network

#Redes
networks:
  app-network:
    driver: bridge
#Volumenes
volumes:
  dbdata:
    driver: local

Dockerfile

Ahora para que el docker-compose que recién creamos funcione como es debido debemos crear un Dockerfile que definirá la imagen que nombramos como mi-app-laravel. y configura php ademas todos los directorios y puertos de la app.

FROM php:7.2-fpm

# Copiar composer.lock y composer.json
COPY composer.lock composer.json /var/www/

# Configura el directorio raiz
WORKDIR /var/www

# Instalamos dependencias
RUN apt-get update && apt-get install -y \
    build-essential \
    mysql-client \
    libpng-dev \
    libjpeg62-turbo-dev \
    libfreetype6-dev \
    locales \
    zip \
    jpegoptim optipng pngquant gifsicle \
    vim \
    unzip \
    git \
    curl

# Borramos cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Instalamos extensiones
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd

# Instalar composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# agregar usuario para la aplicación laravel
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www

# Copiar el directorio existente a /var/www
COPY . /var/www

# copiar los permisos del directorio de la aplicación
COPY --chown=www:www . /var/www

# cambiar el usuario actual por www
USER www

# exponer el puerto 9000 e iniciar php-fpm server
EXPOSE 9000
CMD ["php-fpm"]

Configurar PHP

vamos a crear un directorio que sera usado por los volúmenes que definimos en el servicio de la app:

mkdir laravel-app/php

y vamos a crear en ese directorio una configuración en el archivo local.ini

nano laravel-app/php/local.ini

incluyo algunas configuraciones básicas, pero ustedes pueden darse gusto añadiendo las suyas:

upload_max_filesize=100M
post_max_size=100M

Configurar Nginx

De igual forma, creamos el directorio y el archivo de configuración que establecimos en el volumen del servicio webserver

mkdir -p laravel-app/nginx/conf.d
nano laravel-app/nginx/conf.d/app.conf

y agregamos la siguiente configuración:

server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/public;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}

noten que root apunta a /var/www/public que es el publico directorio de laravel 🙂

Configurar MySQL

repetimos una formula similar para MySQL

mkdir laravel-app/mysql
nano laravel-app/mysql/my.cnf

Y agregamos nuestra configuración al archivo:

[mysqld]
general_log = 1
general_log_file = /var/lib/mysql/general.log

Correr El Contenedor

Ahora es el momento! hay que correr el contenedor. Pero primero oficialicemos las variables de entorno propias de la app en el archivo .env:

cp .env.example .env

Y le configuramos las variables que hacen sentido con nuestro contenedor:

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=usuario_laravel
DB_PASSWORD=el_password_de_la_app

Y finalmente el mágico:

docker-compose up -d

Si todo fue bien la correr el comando docker -ps, tendríamos que ver:

CONTAINER ID        NAMES               IMAGE                             STATUS              PORTS
c31b7b3251e0        db                  mysql:5.7.22                      Up 2 seconds        0.0.0.0:3306->3306/tcp
ed5a69704580        app                 mi-app-laravel                    Up 2 seconds        9000/tcp
5ce4ee31d7c0        webserver           nginx:alpine                      Up 2 seconds        0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp

Si esto es correcto, podemos correr dos comandos mas para adicionar seguridad a nuestra aplicación:

docker-compose exec app php artisan key:generate
docker-compose exec app php artisan config:cache

Con lo cual agregamos una llave de encripción generada para este contenedor, y cacheamos nuestra configuración.

Migrando Datos

Ahora que ya tenemos todo montado, ya deberíamos ser capaces de ver nuestra aplicación en el navegador. Y para migrar la data necesitamos crear un usuario para la base de datos. Entonces primero accedemos a la base de datos asi:

docker-compose exec db bash

Y luego accedemos al cliente de mysql:

mysql -u root -p <su password>

una vez dentro vemos las bases de datos que estan creadas:

$ show databases;

+--------------------+
| Database           |
+--------------------+
| information_schema |
| laravel            |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

y vemos que esta la que nombramos laravel. Entonces creamos el usuario corriendo las siguientes consultas:

mysql> GRANT ALL ON laravel.* TO 'usuario_laravel'@'%' IDENTIFIED BY 'su_password_de_la_base_de_datos';
mysql> FLUSH PRIVILEGES;
mysql> EXIT;

y finalmente nos salimos del contenedor:

root@c31b7b351f1:/# exit

Ahora a correr las migraciones:

docker-compose exec app php artisan migrate

Y ahora por fin! a seguir desarrollando con nuestra app laravel ya dockerizada!

 

Tagged in:

, , ,