Local E-commerce Made Easy: Harnessing Docker and Docker Compose

bablubablu
8 min read

In a previous article, we discussed deploying our e-commerce application on a single Google Compute Engine VM. Now, let's go back to the early 2000s when the flexibility of Docker was the new frontier! Before considering Kubernetes for production, we'll focus on getting our e-commerce example running locally using Docker for efficient testing and development.

To simplify the setup, we'll use Cloud Shell to run our Docker environment. Cloud Shell has Docker and Docker Compose already installed, so you don't need to handle any local installations or configurations. This allows us to quickly get your e-commerce application running and tested without any hassle.

For our e-commerce setup, we'll use the KodeKloudHub Learning App E-commerce repository, which provides a PHP Apache-based application for this purpose.

Now, let's create a Dockerfile for our e-commerce application. This file will specify how to build our Docker image, including the base image, required extensions, and the location of our application code.


FROM php:7.4-apache
# Install the mysqli extension, which is required for database interaction
RUN docker-php-ext-install mysqli

# Copy all files from the current directory (where the Dockerfile is located)
# into the /var/www/html/ directory of the Apache server inside the container.
# This makes our e-commerce application code available to the web server.
COPY . /var/www/html/

This Dockerfile sets up a basic PHP 7.4 Apache environment, installs the mysqli extension needed for database connectivity, and copies our application code into the web server's document root. Next, we'll see how to build a container.

Building and Running the Docker Container

With our Dockerfile ready, we can build the Docker image and run a container from it. This will package our application and its dependencies into a portable unit.

First, we will go to the directory where we saved our Dockerfile and the e-commerce application code (after cloning the KodeKloudHub repository). Then, use the docker build command to create our image:

$ docker build -t ecommerce-app .
  • docker build: This command starts the process of building the image.

  • -t ecommerce-app: This tags the image with the name ecommerce-app. You can choose any name you prefer.

  • .: This indicates the build context, meaning Docker will search for the Dockerfile in the current directory.

$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
ecomapp      latest    4f4440e2d28f   7 seconds ago   459MB

Once the image is built, we will run a container from it using the docker run command:

$ docker run -d -p 8080:80 --name ecommerce-container ecommerce-app
  • docker run: This command creates and starts a new container from an image.

  • -p 8080:80: This is important for accessing our application. It maps port 8080 on our host machine (Cloud Shell in this case) to port 80 inside the container (where Apache is listening).

  • -d: Run in detached mode.

  • --name ecommerce-container: This gives a recognizable name to our running container, making it easier to manage later.

  • ecommerce-app: This is the name of the image we just built.

$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                  NAMES
3ea54a5de75f   ecomapp   "docker-php-entrypoi…"   56 seconds ago   Up 56 seconds   0.0.0.0:8080->80/tcp   ecomcontainer

After running this command, our e-commerce application should be accessible through the Cloud Shell's "Web Preview" feature on port 8080. However, we will see a connection error right away. This happens because our e-commerce application needs a database, and right now, we've only started the PHP Apache server. We need a way to run multiple containers together and manage their networking. This is where Docker Compose is useful.

connection error

Using Docker Compose for Multi-Container Applications

Docker Compose is a tool that helps you define and run applications with multiple Docker containers. With Compose, you use a YAML file to set up your application's services, networks, and storage. This lets you start your whole application stack with just one command.

Let's create a docker-compose.yml file in the root of our e-commerce project. This file will define two services: our app (the PHP Apache application) and a db service (a MariaDB database).

version: '3.3'
services:
  app:
    # Build the Docker image from the Dockerfile in the current directory.
    build: .
    ports:
      # Map port 8080 on your host machine to port 80 in the container.
      - "8080:80"
    environment:
      # Pass database connection details to the application as environment variables.
      DB_HOST: db
      DB_USER: ecomuser
      DB_PASSWORD: ecompassword
      DB_NAME: ecomdb
    # Ensure the 'db' service is started before the 'app' service.
    depends_on:
      - db

  # The 'db' service for your MariaDB database.
  db:
    image: mariadb:10.6 
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: verysecretrootpassword
      MYSQL_DATABASE: ecomdb
      MYSQL_USER: ecomuser
      MYSQL_PASSWORD: ecompassword
    volumes:
      # Mount the SQL script to initialize the database on first startup.
      - ./assets/db-load-script.sql:/docker-entrypoint-initdb.d/init.sql
      # Use a volume to persist database data.
      - db_data:/var/lib/mysql

# volume for database persistence.
volumes:
  db_data:

version: '3.3': This line sets the Docker Compose file format version.

services:: This is the main section where we define all the individual containers (services) that make up our application. Each service operates in its own isolated environment.

The app Service (Your E-commerce Application)

  • app:: This defines our e-commerce PHP application service.

    • build: .: This tells Docker Compose to build the Docker image for this service using the Dockerfile located in the current directory (.). This is the Dockerfile we created earlier.

    • ports:: This maps network ports between the host machine (Cloud Shell in this case) and the container.

      • - "8080:80": This means that traffic on port 8080 of our host machine will be forwarded to port 80 inside the app container. Since Apache usually listens on port 80, this allows our web application to be accessed from outside the container via the Cloud Shell Web Preview URL.
    • environment:: This section allows us to define environment variables that will be available inside the app container. These are crucial for our PHP application to connect to the database.

      • DB_HOST: db: This is a key setting. Inside the Docker Compose network, services can communicate with each other using their service names as hostnames. So, db refers to the db service defined later in this file.
  • DB_USER: ecomuser, DB_PASSWORD: ecompassword, DB_NAME: ecomdb: These variables provide the credentials and database name needed for our PHP application to connect to the MariaDB database. They must match the settings configured for the db service.

  • depends_on:: This sets up a dependency between services.

    • - db: This tells Docker Compose that the app service depends on the db service. While depends_on ensuring the db container starts before the app container.

The db Service (MariaDB Database)

db:: This sets up our database service, which will run a MariaDB instance.

  • image: mariadb:10.6: This tells Docker Compose to use the mariadb image with the 10.6 tag from Docker Hub. This image includes the MariaDB database server.

  • restart: always: This policy ensures that if the db container stops for any reason (like an error or host reboot), Docker will automatically try to restart it.

  • environment:: Like the app service, these environment variables configure the MariaDB server when it starts for the first time.

    • MYSQL_ROOT_PASSWORD, MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD: These variables set the root password, the default database name ecomdb, and a specific user ecomuser with a password ecompassword for that database. These credentials are used by our app service to connect.

    • volumes:: This section is essential for managing data in Docker. Volumes allow data generated by Docker containers to be saved.

      • - ./assets/db-load-script.sql:/docker-entrypoint-initdb.d/init.sql: This is a bind mount. It mounts a local file ./assets/db-load-script.sql from our project directory into a specific directory inside the MariaDB container. The docker-entrypoint-initdb.d/ directory is special for MariaDB/MySQL images; any .sql scripts placed here will automatically run when the container starts for the first time, allowing you to pre-populate your database with schemas and initial data. Make sure this db-load-script.sql file exists in assets folder relative to our docker-compose.yml.

      • - db_data:/var/lib/mysql: This uses a named volume db_data to save the actual database files /var/lib/mysql is where MariaDB stores its data. This means that even if we stop, remove, and recreate the db container, our database data will not be lost because it's stored on the host machine in the db_data volume. Volumes Definition

  • volumes:: This top-level key is where we define named volumes that can be shared and reused by services.

    • db_data:: This line defines a named volume called db_data. Docker handles the creation and storage location of this volume on the host system. It's a best practice for saving data because it's more reliable than bind mounts for data that needs to exist beyond the container's lifecycle.

In summary, this docker-compose.yml file sets up a complete self-contained e-commerce environment with a PHP Apache application and a MariaDB database. It ensures they can communicate, data is saved, and the setup is easy to reproduce.

$ docker compose up -d

Once you run docker compose up -d our application goes live! We can then interact with our services on port 8080 as we defined. Look for the "Web Preview" button in the Cloud Shell toolbar. Clicking it will usually let you choose "Preview on port 8080" which will open a new browser tab or window showing your running application.

success

$ docker-compose ps
            Name                          Command               State          Ports        
--------------------------------------------------------------------------------------------
learning-app-ecommerce_app_1   docker-php-entrypoint apac ...   Up      0.0.0.0:8080->80/tcp
learning-app-ecommerce_db_1    docker-entrypoint.sh mariadbd    Up      3306/tcp

Conclusion: Tearing Down and Looking Ahead

We've successfully launched our multi-container application using docker compose up -d, checked its running status with docker compose ps, and learned how Docker Compose smoothly manages communication between services and data storage.

When we're ready to shut down our application and clean up all related resources, including the data stored in named volumes, we'll use the following command:

$ docker compose down -v

This complete method for defining, running, and shutting down multi-container applications with Docker Compose offers a powerful and flexible local development setup. We've learned the basics of managing our services effectively.

But what do you do when your application becomes too large for your local machine? How do you scale it, ensure it's always available, and manage it in a production setting?

In our next article, we'll make the exciting jump from local Docker Compose deployments to enterprise-level orchestration. Get ready as we explore how to host and manage your application on Kubernetes.

0
Subscribe to my newsletter

Read articles from bablu directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

bablu
bablu