Docker, the new sheriff in town whose new gold boots is getting everyone excited. Why? because Docker is development bliss since it ensures consistency on both your development and production cycles therefore standardizing your environment
I assume you're as excited about docker as the rest of us and hence explains why you have stumbled upon this post. I'll be honest, docker at first, without any prior knowledge is a nightmare, as any other new concept without prior knowledge, but once you do get the hang of it, it'll save you tonnes of time you'll have otherwise invested during deployment.
This post is intended mostly for beginners who probably have little or no knowledge about docker. The motivation behind this post lies on the fact that I wasn't able to find any comprehensive Laravel Docker tutorials online. I was only able in the end to accomplish this task through continuous reading of multiple blog posts and combining all this into a massive comprehensive series of steps that I'll attempt to document in this post.
Before I start, I assume that you already have your Laravel Application ready with you. If you don't, you can jump on to the Laravel Documentation Page and build yourself an app, then head back here and continue reading.
I also assume that you already have Docker installed on your machine. In case you don't, you have the following options:
What is a docker-compose file? This is a file that defines all your multiple docker containers and all these containers can be spawned up by running a relatively simple command like:
docker-compose -f docker-compose.prod.yml up --build
Here, we'll be setting up a development environment (Which can also be used in your production environment, with a few minor changes that I'll document in my next post :-))
Create a file in your root directory and name it: docker-compose.yml
You'll be defining the containers in the next steps in this file.
In our docker-compose file, we define three containers: mysql, nginx and our laravel app.
version: '2'services:
The Application
app:
container_name: laravel_app
build:
context: ./
dockerfile: development/app.dockerfile
volumes:
- ./storage:/var/www/storage
env_file: ‘.env.prod’
environment:
- “DB_HOST=database”
- “REDIS_HOST=cache”
Overview of the Above Code:
development/app.dockerfile
.This means that our docker file is located in a ‘development’ folder on the root of our laravel app.echo $DB_HOST
will print out: database# The Web Server
web:
container_name: nginx_server
build:
context: ./
dockerfile: development/web.dockerfile
volumes:
- ./storage/logs/:/var/log/nginx
ports:
- 8990:80
Overview of the Above Code:
# The Database
database:
container_name: mysql_database
image: mysql:5.7
volumes:
- dbdata:/var/lib/mysql
environment:
- “MYSQL_DATABASE=Baly”
- “MYSQL_USER=phpmyadmin”
- “MYSQL_PASSWORD=phpmyadmin”
- “MYSQL_ROOT_PASSWORD=finallyJDBC2017.”
ports:
- 8991:3306
Overview of the Above Code:
mysql5:7
image. You can switch this mysql version with the version you’re developing with. Remember that, with reference to your laravel application, the newest versions of mysql may not work with your laravel app. This is because the newest versions MySQL use a different technique of authentication that may not be supported by either mysql or pdo php extensions. Therefore, beware when invoking mysql:latest
instead of mysql:5.7
.dbdata
from our host machine that will map to /var/lib/mysql
on the docker container.database
, a user named secret
identified by the password secret
and a root password of secret_root
. You can feel free to change these as you please. We define these settings in our env.prod
file so as not to collide our current .env
file settings with our container file settings.Copy paste the following into your docker-compose.yml file:
volumes:
dbdata:
Ensure that you need to preserve the indenting in your docker-compose.yml file to ensure that docker-compose reads it correctly. In the end, your docker-compose file should look as follows:
version: ‘2’services:
The Application
app:
container_name: laravel_app
build:
context: ./
dockerfile: development/app.dockerfile
volumes:
- ./storage:/var/www/storage
env_file: ‘.env.prod’
environment:
- “DB_HOST=database”
- “REDIS_HOST=cache”The Web Server
web:
container_name: nginx_server
build:
context: ./
dockerfile: development/web.dockerfile
volumes:
- ./storage/logs/:/var/log/nginx
ports:
- 8990:80The Database
database:
container_name: mysql_database
image: mysql:5.7
volumes:
- dbdata:/var/lib/mysql
environment:
- “MYSQL_DATABASE=Baly”
- “MYSQL_USER=phpmyadmin”
- “MYSQL_PASSWORD=phpmyadmin”
- “MYSQL_ROOT_PASSWORD=finallyJDBC2017.”
ports:
- 8991:3306# redis
cache:
image: redis:3.0-alpinevolumes:
dbdata:
In this step, we define the dockerfiles for the containers we just defined in our docker-compose file. These dockerfiles will represent a series of commands that we’ll want to run inside our docker containers.
Create a folder in your laravel app’s root directory and name it development
. Inside the folder you just created, create a file and name it app.dockerfile
(yes, without any extensions). Open this file and copy paste the following code into it:
FROM php:7.2-fpmCOPY composer.lock composer.json /var/www/
COPY database /var/www/database
WORKDIR /var/www
RUN apt-get update && apt-get -y install git && apt-get -y install zip
RUN php -r “copy(‘https://getcomposer.org/installer’, ‘composer-setup.php’);”
&& php -r “if (hash_file(‘SHA384’, ‘composer-setup.php’) === ‘a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1’) { echo ‘Installer verified’; } else { echo ‘Installer corrupt’; unlink(‘composer-setup.php’); } echo PHP_EOL;”
&& php composer-setup.php
&& php -r “unlink(‘composer-setup.php’);”
&& php composer.phar install --no-dev --no-scripts
&& rm composer.pharCOPY . /var/www
RUN chown -R www-data:www-data
/var/www/storage
/var/www/bootstrap/cacheRUN php artisan cache:clear
RUN php artisan optimize
RUN apt-get install -y libmcrypt-dev
libmagickwand-dev --no-install-recommends
&& pecl install mcrypt-1.0.2
&& docker-php-ext-install pdo_mysql
&& docker-php-ext-enable mcryptRUN mv .env.prod .env
RUN php artisan optimize
Overview of the Above Code
php:7.2-fpm
. Also, you can change this version to meet your development environment needs.composer.lock
and composer.json
from our root folder (in our host machine) to /var/www/
in the docker container. In the second copy command, we copy our database
folder in the host machine to /var/www/database
folder in the docker container. This is because, one, we’ll want to make sure that the dependencies we use in our development environment (in composer.json) will be reflected inside the container when we download dependencies and two, that we can access our migrate files inside the docker container in cases we may need to run migrate
command./var/www
which means we don’t have to cd
to this folder (move to this folder) in cases we’ll need to run bash commands.if(hash_file(‘SHA384’…
line. The hash value defined there will change with every update, and therefore if your installer fails with the message: installer corrupt, consider getting the correct hash value from: Get Hash Value./var/www
folder in the docker container..env.prod
file to .env
since this file will contain the correct environment variables specific to the docker container environment and therefore should be used by laravel. We run php artisan optimize
to remove the cached version of the .env
file.Please note that it is unnecessary to copy everything from our root folder (like vendor folder) and docker provides a .dockerignore
file which works pretty much like a .gitignore
file. Our dockeringore file will look as follows:
.git
.idea
.env
node_modules
vendor
storage/framework/cache/**
storage/framework/sessions/**
storage/framework/views/**
development
Save this file in the same folder as your app.dockerfile (development folder).
For your .env.prod
file, copy paste your .env
file and rename it to .env.prod
. In the database settings, change the DB_HOST
to match the name of your mysql container, and the password to match what you defined in your docker-compose.yml
file. If you followed all my steps without changing a thing, then your .env.prod file should resemble the following:
DB_CONNECTION=mysql
DB_HOST=mysql_database
DB_PORT=3306
DB_DATABASE=Baly
DB_USERNAME=phpmyadmin
DB_PASSWORD=phpmyadmin
In the same folder you just created (the development folder) create a web.dockerfile
. Copy paste the following to the dockerfile:
FROM nginx:1.10-alpineADD development/vhost.conf /etc/nginx/conf.d/default.conf
COPY public /var/www/public
Overview of the Above Code
We build our dockerfile from the image: nginx:1.10-alpine
. We then replace nginx’s default.conf
file with the vhost.conf
we’ll create in a sec.
We also copy our laravel app’s public directory to the public directory of nginx, that will server all our public assets.
Create a vhost.conf file in this same directory (development) and copy paste this into it:
server {
listen 80;
index index.php index.html;
root /var/www/public;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
try_files $uri /index.php?$args;
}location ~ \.php$ { 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; }
}
our php-fpm
container will be listening on port 9000 and hence app:9000
So, to counter-check, you need to already have the following files:
docker-compose.yml
and .env.prod
.dockerignore
app.dockerfile
web.dockerfile
vhost.conf
If you are using Docker Toolbox on Windows and your laravel app folder is in a folder other than C:/users
you will have trouble sharing volumes between your host machine and your docker containers. This is because any other folder that’s not C:/users
is not mounted by virtual box when your docker machine starts. Therefore, to fix this, first stop your running docker machine by running:
docker-machine stop
Then open virtualbox, right-click the machine named default and click on settings . Navigate to Shared Folders click on it, and add a new folder that defines the location of your laravel app folder. Remember to check against Auto mount. Afterwards, start your docker machine by running:
docker-machine start default
Assuming you have done everything correctly, go ahead and run the following command:
docker-compose up --build
Make sure that you are running this command inside the root folder of your laravel app. This command builds your container images and finally starts them. If everything goes according to plan, you should be able to access your laravel app running inside your container at:
0.0.0.0:8990
Replace 8990
with the port you defined in your docker-compose.yml file if you used a different port.
Also, please note that for users using Docker Toolbox, docker creates a virtual network and assigns an IP address to it. You can find this IP address by searching for docker quickstart terminal
and running it. The IP address assigned will be displayed in the terminal that pops up and you’ll be able to access your laravel app by going to:
your-docker-machine-ip:8990
And there you have it folks! You have successfully deployed your laravel app on docker! Stay tuned for my next post where I’ll be describing on how to deploy your Laravel app on a production environment.
Thanks for reading. If you liked this post, share it with all of your programming buddies!
#laravel #docker #mysql #devops #web-development #php