Docker is an open-source platform that makes it possible for software developers to Build, Ship, and Run any App anywhere.

Docker is an open-source platform that makes it possible for software developers to Build, Ship, and Run any App anywhere. In a high-level explanation, a dockerized App can be likened to a flower growing in a bucket whereby the bucket contains all that the flower needs to survive notwithstanding the environment. It basically places (containerizes) the App in a container that houses all that the App needs to run.

This article assumes that the reader already understands how to use Laravel, our business is how to dockerize it.

This article was originally published on medium

Requirements

To get a Laravel app running on Docker, the following must be put in place:

  • Laravel installed on your local machine. See the official documentation for steps to installing Laravel.
  • Docker installed on your local machine. Check here to install the appropriate distribution for your operating system
  • Docker Compose installed on your local machine.
  • Knowledge of the command line, if you're a Linux user, you should check out this Linux tutorial to understand basic Linux commands

If you've met the requirements above, then you're ready and good to go. Let's dive deep into the main business.

The sample project used in this article can be cloned here

Getting started

Before we proceed, it's important we explain some basic concepts that we'll be using in this tutorials. Most prominent among them are images and containers.

Images: The filesystem and metadata needed to run containers. They can be thought of as an application packaging format that includes all of the dependencies to run the application, and default settings to execute that application. The metadata includes defaults for the command to run, environment variables, labels, and healthcheck command.

Containers: An instance of an isolated application. A container needs the image to define its initial state and uses the read-only filesystem from the image along with a container specific read-write filesystem. A running container is a wrapper around a running process, giving that process namespaces for things like filesystem, network, and PIDs.

Stackoverflow.com

Another analogy that explains images and containers is class and objects in object-oriented programming. An image is a class while a container is an object of the class.

Explaining further; in order to run a Laravel app on our local machine, we install LEMP, WAMP, XAMP, or MAMP as the case may be as well as phpmyadmin if it's needed. These pieces of software are bundled as images in docker environment. This means, to dockerize a Laravel app, we need to create an image that contains each of those packages, or we create different images for each of them and internetwork them inside the docker container

In this article, we'll not be creating images from scratch, we are going to pull already built images from docker hub. Below are the images we are going to pull from docker hub to enable us dockerize our app:

Click on each of the images to read details about them. Having established the necessary theories about what we are going to do, let us begin the main processes of dockerizing the app following the steps below.

Step 1 - Create the Laravel Project to be dockerized

Create the Laravel project that you want to dockerized. Open your terminal and cd into the root of the project.

ned@Ned-PC:/var/www/html/laravel-dockerization$

Step 2- Create the docker-compose.yaml file

$ touch docker-compose.yaml

Next up, edit the docker-compose.yaml file as follows:

version: '3'
services:
  core_services:
    image: creativitykills/nginx-php-server
    container_name: core
    ports:
      - "44678:80"
    volumes:
      - ./:/var/www
    networks: 
      - backend
  
  mysql:
    container_name: db_mysql
    image: mysql:5.7
    ports:
      - "33062:3306"
    environment:
      - "MYSQL_ROOT_PASSWORD=${DB_PASSWORD}"
    volumes:
      - "./db/mysql/data:/var/lib/mysql"
      - "./db/mysql/initdb.d:/docker-entrypoint-initdb.d"
    networks: 
      - backend
  pma:
    container_name: pma
    image: phpmyadmin/phpmyadmin
    ports:
      - "44679:80"
    environment:
      - "PMA_HOST=db_mysql"
      - "MYSQL_DATABASE=${DB_DATABASE}"
      - "MYSQL_ROOT_PASSWORD=${DB_PASSWORD}"
    networks: 
      - backend

networks:
  backend:
    driver: bridge

You can view the snippet above on GitHub Gist for the line numbers.

We’ve finished what we need to do, but before we summarize, let me explain what is happening in the yaml file above.

On line 1, we specified the version of docker-compose to be used. On line 2, we declared the beginning of the definition of our services, services here literally means images.

On line 3, we declared the first service name core_services; note that the name is up to you to decide. This service would be an image comprising the Nginx and PHP interpreter and other dependencies such as composer.

On line 4, we specified the name of the image to be pulled from the docker hub. On line 5, we specified the container name, that is the name for any instance of the core_services image. Note that the name is up to you to decide.

On line 6–7, we specified the port through which we can access the app on the browser. It means we can access the app by visiting 0.0.0.44678 on the browser. On line 8–9, we mapped the directory of our project to /var/wwwinside the docker container. What it means is, copy the entire project located in /var/www/html/laravel-dockerization to /var/www inside the container. With this, any change we make externally in the project directory will be reflected inside the docker container and vice versa.

On line 10–11, we specified the network to be used by the container so that any other container connected to the same network and communicate with each other. If you do not specify a network, docker creates one an attach the container to it. It’s advisable you specify the network to be used so that you can have full control of its behavior.

From line 13–23, we created the mysql service. We specified the version to be used as 5.7, this can change depending on your choice. We specified the MYSQL_ROOT_PASSWORD. This ${DB_PASSWORD} means replace this with the value of DB_PASSWORD in the .env file. So, ensure that the value of DB_PASSOWRD in the .env is the password you want to use for mysql.

On line 20–21, we mapped the volume from the external directory to the mysql container volume. This volume ensures that data stored in the database is persisted in the local machine even if the container is deleted. You can name the directory what you choose to but ensure you create it before the mapping.

On line 22, we mapped the external directory ./db/mysql/initdb.d to docker-entrypoint-initdb.d. This volume is used importing data into the database. For instance, if you want to import an SQL dump into the database, you need to copy the sql dump file into the db/mysql/initdb.d and then access the file from docker-entrypoint-initdb.d inside the db_mysql container. We’ll demonstrate how to do this later in this article. You can read more about docker-entrypoint here.

From line 24–34, we created the phpmyadmin service with the container name pma. We exposed port 44679 which means we can visit 0.0.0.44679 to access the phpmyadmin. On line 30, you’ll notice we specified the PMA_HOST as the MySQL container db_mysql, this is very important. If the PMA_HOST is not in sync with the mysql container, you’ll not be able to access your phpmyadmin as the PMA_HOST will default to localhost. We also specified the MYSQL_ROOT_PASSWORD and MYSQL_DATABASE to use the DB_PASSWORD and DB_DATABASE in the .env respectively. Note that MYSQL_DATABASE is optional.

From line 36–38, we defined the backend network that we connected the containers in the previous lines. When docker encounters the backend network, it will look for where it’s defined and create it first before continuing. Next up, we confirm that the correct values are passed to the docker-compose from the .env by running the command below:

$ docker-compose config

The output of the command above should look like the screenshot below: docker compose image

Below is the look of my .env file:

...
DB_CONNECTION=mysql
DB_HOST=db_mysql
DB_PORT=3306
DB_DATABASE=laravel_dockerization
DB_USERNAME=root
DB_PASSWORD=secret
...

Notice that the DB_HOST is set to the name of the mysql container defined in the docker-compose.yaml. Step 3 — Building the containers Now that we have set up the docker-compose.yaml, we are set to build the containers. Run the command below at the root of your project:

$ docker-compose up -d

The command above will build the containers and start them up. The flag -d starts the containers in interactive mode, the processes run in the background and you’ll be able to run other commands subsequently, but if you’d want to monitor the build processes, then just run:

$ docker-compose up

Next up, we need to give full permission to the storage directory so that docker would be able to read and write to it, run the command below:

$ sudo chmod 777 -R ./storage

Now, if the docker-compose command above build successfully, we confirm that the containers are running as supposed by running the command below:

$ docker ps

The output of the command above should look like the screenshot shown below:

Docker ps screenshot
Docker ps screenshot

The screenshot above shows that all the containers are running as expected. If all of the three containers are not running, it means something has gone wrong. You need to drop the containers and rebuild again removing the -d flag. This will make the build process verbose and you can easily point what went wrong and get it fixed. To do this run the command below:

$ docker-compose down && docker-compose up

Now head to the browser and visit http://0.0.0.0:44678 and voila! our app is up!

Screenshot of the running app
Screenshot of the running app

To view the phpmyadmin dashboard visit http://0.0.0.0:44679:

screenshot of phpmyadmin dashboard
screenshot of phpmyadmin dashboard

Note: if the database is not created automatically, you can manually create it using the PMA GUI or via MySQL command line.

Step 4 — Interacting with the containers

Now that the app is up and running, you might want to interact with the container to performs actions like running migration or importing SQL dump into the database. To enter the container, run the command below:

$ docker exec -it core bash

Remember that core is the name of the php and nginx container. You can now run migration inside the container.

$ php artisan migrate

To enter the mysql container, run:

$ docker exec -it db_mysql bash

How to import SQL dump file into the database

There are situations that you might want to import large SQL dump file into your database, in this case, you might want to do it via the terminal. Follow the steps below to achieve that:

  • First copy the SQL dump into db/mysql/initdb.d
  • Enter the mysql container using docker exec -it db_mysql bash
  • run cd docker-entrypoint-initdb.d
  • $ ls // to be sure the sql file is there
  • $ mysql -h localhost -u root -p <database_name> < sql_dump_file.sql
  • You’ll be prompted to enter your mysql password.

Note that, in copying the SQL dump file to the initdb.d, if you don’t have root access on your machine, you might want to use sudo command to copy the file via the terminal as shown below:

$ sudo cp /path/to/sql_dump_file.sql db/mysql/initdb.d

The screenshot below shows the steps above being applied.

import sql

Conclusion

In this article, we have demonstrated how to dockerize a Laravel application using already built docker images from the docker hub. We tried to explain what goes on in the docker-compose.yaml and also showed how we can interact with the containers and as well as showed how we can import SQL dump into the MySQL container.

Should you have any contribution or question for me, reach out here or via twitter.