How To Set Up Laravel App on Docker, With NGINX and MySQL

How To Set Up Laravel App on Docker, With NGINX and MySQL

Deploying Your Laravel App on Docker, With NGINX and MySQL ... The original post was also uploaded here: Laravel Application on Docker.

Now Shall We

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.

Now Onto The Good Stuff..

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:

  1. Windows 10 Pro Users: Docker Desktop
  2. Windows 10 Version that's below Windows 10 Pro Users: Docker Toolbox .This is because of Docker Desktop system requirements. Docker Toolbox leverages on the functionalities of VirtualBox.
  3. Linux Users: Docker CentOS . You can choose your Linux distribution (if its not CentOS) from the side menu on the screen that comes up and follow those distribution specific instructions.
First Step: Creating your 'docker-compose' file

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.

So, for starters, our laravel app container will be defined 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"

Overview of the Above Code:

  1. version - Feel free to change this to your choosing
  2. container_name - You'll use this name to refer to your container. i.e. if you'd want to close your container, you'll use this name to refer to it specifically. Also feel free to change it to your choosing.
  3. build - Used to build an image from a Dockerfile. Has the following additional options:
  • Context - Docker uses this context (basically, where your laravel files reside) to reference any files within it. In this case, the ./ refers to the root laravel folder assuming that the docker-compose file is stored in your laravel root folder.
  • dockerfile: docker images are built from Dockerfiles, which often contain additional commands that should be run inside the container. In this case, the dockerfile we use to build our appcontainer. Also note that we have used development/app.dockerfile .This means that our docker file is located in a 'development' folder on the root of our laravel app.
  1. volumes - Volumes are used by docker containers to share files between the host machine and the docker container that is running. The left hand side of the full colon represents our host machine and the right hand side represents our docker container. In this case, we're sharing all data in the storage folder on our laravel app with the docker container mounted at /var/www/storage
  2. env_file - This defines our laravel's .env file, in our case env.prod that we'll use to input docker container specific environment variables as we'll see later on this post.
  3. environment - This defines the environment variables that will be set on our docker machine. In this case, if we can execute a bash command inside our linux container and reference the environment variables we define here, i.e. echo $DB_HOST will print out: database

Our NGINX Container will be defined as follows:

# 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:

  1. container_name - Again, the name of your container, which you can choose to change.
  2. build - Definition same as above. Here you can see that we define this container's dockerfile as web.dockerfile.
  3. volumes - Definition same as above. Here we share our laravel's logs folder with nginx's logs folder.
  4. ports - Here, we define the port in the host machine that our docker container will be listening on and the port on the virtual network created by docker during container deployment. This can be easily visualised by understanding that the left side of the colon defines the host machines, therefore ports on the host machine and the right side of the colon the docker container, therefore the ports on the docker container.

Our MySQL Container will be defined as follows:

# 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:

  1. container_name - Refer to above.
  2. image - In this case, we haven't defined a dockerfile to base our container build on, but rather an image. Our docker container will therefore be built from the image we've defined in this case mysql5:7image. 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.
  3. volumes - Still the same concept, except that now we've defined dbdata from our host machine that will map to /var/lib/mysql on the docker container.
  4. environment - Same concept as defined above, except that in this case, our mysql database will be initialized with the variables we have set. Therefore, our container after build, will automatically have a database named 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.prodfile so as not to collide our current .env file settings with our container file settings.
  5. ports - same as above, except that our mysql container will be listening on port 8991 on the host machine and 3306 (mysql's default port) on the container's network.

Defining your named volumes

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:80

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

# redis

cache:
image: redis:3.0-alpine

volumes:
dbdata:

Second Step: Defining our Dockerfiles.

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.

Defining our 'app' dockerfile (laravel_app)

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-fpm

COPY 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.phar

COPY . /var/www

RUN chown -R www-data:www-data
/var/www/storage
/var/www/bootstrap/cache

RUN 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 mcrypt

RUN mv .env.prod .env

RUN php artisan optimize

Overview of the Above Code

  1. From php:7.2-fpm - This means will be building our container from an image, php:7.2-fpm. Also, you can change this version to meet your development environment needs.
  2. COPY - In the first copy command, we copy our 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/databasefolder 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.
  3. WORKDIR - We set the working directory to /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.
  4. RUN - Here, we install all the dependencies that will be needed by laravel, including composer and the dependencies needed by composer. Please note the 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.
  5. COPY . /var/www - At this point we copy all our folder contents into /var/www folder in the docker container.
  6. RUN - In the final run commands, we clear our application cache and other cache and install the mysql driver that laravel uses to make connections to the database. Afterwards, we rename our .env.prodfile 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

Defining our 'web' dockerfile

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-alpine

ADD 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

Almost there...

So, to counter-check, you need to already have the following files:

  1. Root folder - docker-compose.yml and .env.prod
  2. development folder:
  • .dockerignore
  • app.dockerfile
  • web.dockerfile
  • vhost.conf

If so, then you're almost done, but first, some prerequisites:

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
Drum Rolls...

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!


Develop this one fundamental skill if you want to become a successful developer

Throughout my career, a multitude of people have asked me&nbsp;<em>what does it take to become a successful developer?</em>

Throughout my career, a multitude of people have asked me what does it take to become a successful developer?

It’s a common question newbies and those looking to switch careers often ask — mostly because they see the potential paycheck. There is also a Hollywood level of coolness attached to working with computers nowadays. Being a programmer or developer is akin to being a doctor or lawyer. There is job security.

But a lot of people who try to enter the profession don’t make it. So what is it that separates those who make it and those who don’t? 

Read full article here

Tutorial Laravel 6 with Docker and Docker-Compose

Tutorial Laravel 6 with Docker and Docker-Compose

In this tutorial, we'll learn how we can use Docker and Compose to develop and run Laravel 6 applications locally.

Following this tutorial you'll learn:

Docker is a container tool that allows developers and organizations to build, run and deploy applications using containers.

Containers are standalone images that allow developers to package applications with their dependencies. They share the kernel and resources of the system where they are running and they provide isolated environments for applications.

Docker makes use of Dockerfile files to define and describe the parts and dependencies used by an application and also commands to run in order to install the dependences and start the application.

Installing Docker

start by installing Docker for your type of operating system by following one of these official guides:

Docker-Compose

The official documentation describes docker-compose as:

A tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see the list of features.

Using docker-compose involves three easy steps:

1.     First, you need to define/describe your app environment with a Dockerfile so you can reproduce it anywhere.

2.     Second, you need to create docker-compose.yml file and define the services required for running your application so you can run them in multi containers.

3.     Finally you just need to run docker-compose up and Compose will start all the services for your application

Creating a Laravel 6 Project

Let's create a Laravel 6 project and then see how we can dockerize it using Docker.

We have different ways to create Laravel projects: Using Composer (PHP package manager), cloning from GitHub or downloading using cURL.

Using Composer

If you have Composer installed on your system then you can simply use the following command to create a Laravel project:

composer create-project --prefer-dist laravel/laravel laravelproject

Using GitHub Repository

Alternatively if you don't want to install Composer in your system, you can also clone Laravel from GitHub using:

git clone https://github.com/laravel/laravel.git laravelproject

Installing the Project Dependencies

Now, let's install the project's dependencies. If you have Composer installed, simply run the following command from within your project's root folder:

composer install 

If you don't have Composer installed on your system. You can also use Docker with the official composer image to install dependencies by running the following command from the project's root folder:

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

The --rm switch tells Docker to automatically clean up and remove the container after doing the task and exiting.

This is what the docs says about that:

By default a container’s file system persists even after the container exits. This makes debugging a lot easier (since you can inspect the final state) and you retain all your data by default. But if you are running short-term foreground processes, these container file systems can really pile up. If instead you’d like Docker to automatically clean up the container and remove the file system when the container exits, you can add the --rm flag:

The command: -v $(pwd):/app instructs Docker to mount the current directory on the host system retrieved using $(pwd) to /app in the container . What app? Simply because the composer tool in the container will look for a composer.json file in this folder.

After finishing the installation, either way, you'll find a vendor folder with all dependencies inside your Laravel project.

Creating a Dockerfile

Now, after installing Docker and Docker-compose and creating the laravel project let's create a Dockerfile to define our environmenet. Create a new Dockerfile file in the application directory and start by adding the first command:

FROM php:7.2.2-fpm

This tells Docker to download and use the php:7.2.2-fpm image.

Next add:

RUN apt-get update -y && apt-get install -y libmcrypt-dev openssl
RUN docker-php-ext-install pdo mcrypt mbstring

This run apt-get to install the dependencies and extensions required by Laravel.

If you want to install Composer you can also add this command:

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

Now, add:

WORKDIR /app
COPY . /app

This tells Docker to set the working directory in the container to /app and copy the files (Laravel project) in our current folder (in our system host) to the /app folder in the container.

If you didn't install the dependencies using the previous methods you can install them using:

RUN composer install

Now, let's run the artisan serve command using the CMD command:

CMD php artisan serve --host=0.0.0.0 --port=8000

This will serve the Laravel project from the port 8000.

Finally, you need to expose the port 8000 from the container using:

EXPOSE 8000

This is the complete Dockerfile:

FROM php:7.2.2-fpm
RUN apt-get update -y && apt-get install -y libmcrypt-dev openssl
RUN docker-php-ext-install pdo mcrypt mbstring
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN docker-php-ext-install pdo mcrypt mbstring
WORKDIR /app
COPY . /app
RUN composer install

CMD php artisan serve --host=0.0.0.0 --port=8000
EXPOSE 8000

Building the Docker Image

After creating the Dockerfile, you are ready to create the actual Docker image. From your terminal, navigate to the root folder of your project and run:

docker build -t my-laravel-image .

Using the -t switch we can specify the tag/name of the Docker image. For the dot means the current folder will be used as the context for the image to be built. Remember the COPY command in the Dockerfile? its source folder is this folder so we can copy the files from our project to the container file-system.

Running the Docker Image Using Docker

After building the Docker image (my-laravel-image) you can now run it using:

docker run -p 8000:8000 my-laravel-image

You can now access your application from your browser at http://localhost:8000.

Running the Docker Image Using Docker-compose

Docker-compose is more useful to build multi containers for the same application but it can also used to run a single Docker image. So first, create a docker-compose.yml file in the root folder of your project and add the following content:

web:
image: my-laravel-image
ports:
- 8000:8000
volumes:
- ./:/app

We are defining a volume using ./:/var/www which simply mount our current directory ./ in the host system to /app in the container. This will allow you to work on the source code in your project's folder and changes will be reflected in the container file-system (in our case /app) without executing any other commands.

Conclusion

In this tutorial, we've seen how to use Docker to create a development environment with Laravel 6. We've seen how to use both Docker and docker-compose to run our Laravel 6 application in an isolated container.

Originally published  at techiediaries.com on 06 Sep 2019

=========================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

How To Set Up Laravel, Nginx, and MySQL with Docker Compose

Deploying a Laravel app in Kubernetes on Google Cloud

Docker Basics: Docker Compose

Laravel 6 CRUD Application Tutorial

Laravel 6 Image Upload Tutorial

How to use form validation in Laravel 6