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!
Hola Ricardo,
Por casualidad di con tu post en Google pues tengo días tratando de integrar Docker con un proyecto hecho en Laravel. No soy DevOps para nada, así que tu guía me ayudó bastante a entender qué era lo que faltaba para poder hacer la integración (probé con Laradock pero me pareció muy “bloated”, así que me aventuré a tratar de hacer mi propia imagen y además mi intención es aprender, no tiene gracia usar algo sin entender bien cómo funciona). Sólo quería darte las gracias por tan detallada guía.
Para otros usuarios que puedan leer esto: en mi caso, lo hice con Docker Toolbox en Windows 10 (Home, por eso no instalé una versión más actual de Docker) y funcionó a la perfección. Sólo asegúrense de colocar sus proyectos dentro de C:\Users, de lo contrario se toparán con problemas de permisos.
¿Cómo se hace el docker run —rm –v $(pwd):/app composer install en windows?