Creating a PHP (Laravel) Application with MySQL using Docker and Docker Compose.

Creating a PHP (Laravel) Application with MySQL using Docker and Docker Compose.

Before we embark on this exciting journey, I'd like to assume you already have a basic understanding of PHP, Laravel and Docker – from installing it to understanding its fundamental usage. However, if you don't have it installed, head on to Docker to download it.

Why do we need Docker?

Docker is a powerful tool that addresses several challenges in software development and deployment, making it an essential component of modern software development practices. Here are some key reasons why Docker is widely used:

  1. Dependency Management: Docker eliminates "it works on my machine" issues by ensuring that all dependencies required by an application are included within the container. This reduces conflicts and compatibility problems.

  2. Fast Deployment: With Docker, you can create an image once and deploy it to different environments. This reduces the time and effort required for deployment, enabling faster development cycles and quicker time-to-market.

  3. Isolation and Portability: Docker allows you to package applications and their dependencies into a standardized unit called a "container." Containers provide isolation, ensuring that an application runs consistently regardless of the environment. This makes it easier to move applications between different environments, from development to production, without worrying about compatibility issues.

  4. Efficiency: Docker enables efficient utilization of system resources. Since containers share the host OS kernel, they are lightweight and start quickly. This makes it possible to run multiple containers on a single host, improving resource utilization and reducing overhead.

  5. Consistency: Containers ensure consistent application behaviour across different development, testing, and production environments. Developers can be confident that what works on their local machine will work the same way in other environments.

So, let's get started with the main business. Fire up your Docker desktop and head to your terminal of choice. Create a directory where you intend to keep your project and make sure to navigate to that directory using the trusty cd command in your terminal.

Quick note: No need to worry about having PHP or Composer installed on your machine; Docker is all you need right now.

Let's Dive into Laravel Installation using Composer

docker run -it --rm -w /var/www/html -v "$(pwd)":/var/www/html composer create-project Laravel/Laravel .

Feeling a bit lost? Let's break down this command:

  • We're running a Composer image interactively (-it).

  • After installing Laravel, the image will be removed (--rm).

  • Our working directory within the container is set to /var/www/html (-w).

  • We're binding the current directory on your host machine to the working directory within the container (-v).

Now, behold! You've got yourself a fully-fledged Laravel project tucked neatly into a folder. Let's now shift our focus to crafting a Dockerfile.

Create a Dockerfile at the root directory of your project with these contents, check the comments for an explanation.

# Use the official PHP 8.2 with Apache base image
FROM php:8.2-apache

# Set the working directory within the container to /var/www/html
WORKDIR /var/www/html

# Copy the composer.json and composer.lock files from the host to the container
COPY composer.json composer.lock /var/www/html/

# Update package lists and install necessary packages
RUN apt-get update && \
    apt-get install -y \
    zip \
    unzip \
    git \
    libpq-dev \
    && docker-php-ext-install \
    pdo_mysql \
    && docker-php-ext-enable \
    pdo_mysql

# Copy Composer binary from another image layer to this image
COPY --from=composer /usr/bin/composer /usr/bin/composer

# Copy all files from the host's current directory to the container's /var/www/html directory
COPY . /var/www/html

# Set an environment variable to allow Composer to run as superuser
ENV COMPOSER_ALLOW_SUPERUSER=1

# Run Composer to install project dependencies, ignoring platform requirements
RUN composer install --ignore-platform-reqs

# Change ownership of certain directories to the www-data user (Apache)
RUN chown -R www-data:www-data /var/www/html/storage \
&& chown -R www-data:www-data /var/www/html/bootstrap/cache

# Enable the Apache rewrite module and update Apache's default site configuration
RUN a2enmod rewrite && \
    sed -i 's!/var/www/html!/var/www/html/public!g' /etc/apache2/sites-available/000-default.conf

# Expose port 80 for incoming HTTP traffic
EXPOSE 80

# Start the Apache web server in the foreground
CMD ["apache2-foreground"]

With PHP and Composer in the bag, it's time to bring in MySQL for the database and phpMyAdmin for database visualization. Enter Docker Compose to the scene!

Create your docker-compose.yml file on the root directory of the project.

Here are the contents of docker-compose.yml, see the comments for what they do

version: "3.9"

# Define the services that make up the application
services:

    # Webserver service configuration
    webserver:
        build: .
        container_name: webserver
        ports:
            - 8000:80
        volumes:
          - ./:/var/www/html  # Mount the current directory to the container's /var/www/html
          - /var/www/html/vendor  # Mount the /var/www/html/vendor directory
        depends_on:
            - db  # Depend on the db service

    # Database service configuration
    db:
        image: mysql:latest
        container_name: db
        volumes:
            - data:/var/lib/mysql  # Mount a volume for MySQL data storage
        ports:
            - 3306:3306  # Expose port 3306 for MySQL
        environment:
            - MYSQL_ROOT_PASSWORD=mysecret  # Set MySQL root password
            - MYSQL_DATABASE=todos  # Create a database named "todos"

    # Artisan service configuration
    artisan:
        build: .
        container_name: artisan
        entrypoint: ["php", "/var/www/html/artisan"]  # Set the entrypoint to run artisan commands
        volumes:
            - ./:/var/www/html  # Mount the current directory to the container's /var/www/html
            - /var/www/html/vendor  # Mount the /var/www/html/vendor directory

    # PhpMyAdmin service configuration
    phpmyadmin:
        image: phpmyadmin:latest
        container_name: phpmyadmin
        depends_on:
            - db  # Depend on the db service
        ports:
            - 8001:80  # Expose port 8001 for PhpMyAdmin
        environment:
            - PMA_ARBITRARY=1
            - PMA_HOST=mysql

# Define a named volume for MySQL data storage
volumes:
    data:

Connecting Your App to the Database

Now comes the thrilling part – connecting your app to the database. Since Docker Compose places all services on the same network, you can effortlessly use the name of your MySQL service as the host in your .env file:

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=todos
DB_USERNAME=root
DB_PASSWORD=mysecret

Your DB_HOST now points to the MySQL service's name – which is 'db'.

Launching Your Containers

With everything in place, let's launch those containers:

docker-compose up -d // -d is to run the container in detached mode

That's it! With all services up and running, let's perform migrations using the artisan service:

docker-compose run --rm artisan migration

And to access your app, head over to the mapped port, such as localhost:8000.

For managing your database data, phpMyAdmin's got your back on port 8001:

Remember, the server name is 'db', and your container name and credentials align with those you set for MySQL.

Conclusion

Congratulations, you've successfully navigated the Docker waters to set up PHP (Laravel), MySQL, phpMyAdmin, and Artisan, without the need to install them directly on your local machine. Docker and Docker Compose are the wizards making it all possible.

Feel free to drop your questions in the comment section. If you're keen on learning about pushing this setup to production using cloud services like AWS, let me know in the comments as well.

And that's a wrap on our Docker-powered journey! 🚀🐳