Krishnan's Personal Website


Home | About | Blog | Interesting Reads | Tutorials | Skills | Personal Projects | Books | Fun | Connect with me


Containerizing my website with Docker


Published On: Jul 29 2023
Written By: Krishnan Sethuraman
Category: DevOps


Recently I fixed my old macbook pro and have started using it as my personal computer. Before the mac I used to do most of my programming on an old laptop running LAMP. With the mac joining my arsenal, I wanted the flexibility of using both my mac and the old linux laptop, without going through the struggles of configuring the local environment on my mac. So using Docker was the obvious choice. 

So this weekend I decided to containerize my website with docker. 

Taking a DB dump

Before starting I took a DB dump of the mysql database from my Linux machine and downloaded it on the mac. 

$ mysqldump -u username -p dbname > dbdump.sql

Git branching

I also took a separate branch from the develop branch just to be on the safer side so that if I mess things up I can always ignore this branch and move on. 

$ git branch feature/containerizing
$ git checkout feature/containerizing

The file and directory structure of my website looks like the below screenshot. I have not included all the files and directories of the Laravel framework but instead the ones that matter to this article. 

So the first step was to create two files Dockerfile and docker-compose.yml. I usually work on the docker-compose.yml file first. It gives me a template on what my setup would look like. 

Creating docker-compose.yml

For my website I will create an app container for the website, a database container for the blog and newsletter database and phpmyadmin container. I am not a big fan of phpmyadmin and instead love using client tools like DBeaver as I love to write sql queries. However I personally think that including phpmyadmin is a good practice as in projects where you work with multiple developers, phpmyadmin helps in standardising the tools that need to be supported. 

In the near future, I will also be implementing caching especially in the blog section and hence decided to include a memcached container for this purpose. 

I wanted my the DB data to be persistent and hence included a volume for the DB container. 

With all being said my docker-compose.yml looks like this. 

version: '3'
services:
  app:
    build: .
    volumes:
      - ./laravel-app:/var/www/html
    ports:
      - 8082:80
    environment:
      - APP_ENV=local
      - APP_DEBUG=true
    networks:
      - mywebsite
    depends_on:
      - db    

  db:
    image: mysql:8.0
    container_name: mywebsitedb
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: mywebsitedb
      MYSQL_ROOT_PASSWORD: password
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - mywebsitedb:/var/lib/mysql
    ports:
      - 8083:3306
    networks:
      - mywebsite
          

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: pma
    links:
      - db
    environment:
      PMA_HOST: db
      PMA_PORT: 3306
      PMA_ARBITRARY: 1
    restart: always
    ports:
      - 8081:80
    networks:
      - mywebsite

  memcached:
    container_name: memcached
    image: memcached:latest
    ports:
        - "11211:11211"
    networks:
      - mywebsite

networks:
    mywebsite:
        driver: bridge
volumes:
    mywebsitedb:
        driver: local

I already had mysql installed on both my mac and my linux machine so I decided to use a different post binding for mysql so that I do not have to worry about turning on and off the mysql running on the host. 

Creating Dockerfile

With that being sorted I now started working on my Dockerfile so that my containers have the configuration and packages that my project requires. 

My Dockerfile looks like this. Most of the steps are pretty straight forward. I am installing a specific version of php and apache along with all associated packages. I also want to run composer install so that I have all my packages installed. In the past I have run composer install on the host machine but I strongly feel this is the best way to do things. 

FROM composer:2.4 as build

FROM php:7.4-apache-buster as dev

ENV APP_ENV=dev
ENV APP_DEBUG=true
ENV COMPOSER_ALLOW_SUPERUSER=1

RUN apt-get update && apt-get install -y zip
RUN docker-php-ext-install pdo pdo_mysql

COPY ./laravel-app /var/www/html/
COPY --from=build /usr/bin/composer /usr/bin/composer
RUN composer install --prefer-dist --no-interaction

COPY docker/apache/000-default.conf /etc/apache2/sites-available/000-default.conf

RUN php artisan config:cache && \
    php artisan route:cache && \
    chmod 777 -R /var/www/html/storage/ && \
    chown -R www-data:www-data /var/www/ && \
    a2enmod rewrite

As I am running Laravel I need a2enmod rewrite so I also included a basic apache virtual host config for this purpose. It looks like this and other than the <Directory /var/www/> section everything else is boilerplate code. 

<VirtualHost *:80>

  ServerAdmin webmaster@localhost
  DocumentRoot /var/www/html/public/

  <Directory /var/www/>
    AllowOverride All
    Require all granted
  </Directory>

  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

Firing up the containers

Before firing up the containers I wanted to clear the old containers that I created while testing my configuration. 

$ docker system prune -a

The above command is not safe as it wipes out all volume data. I ran it because I was sure that my mac did not have any volume or image I might need. With all old volumes being deleted I fired up my containers. As it's the first, I am also included the build flag. 

$ docker-compose up -d –build

After the running I wanted to make sure that all containers were up and running. 

$ docker-compose ps

With all containers up and running I made the changes to the env file and opened up the website at http://localhost:8082

Restoring the database

With everything up and running the final step is to restore the DB dump file to the database. For this I ran the following command. 

$ docker exec -i db-container-name mysql -uroot -ppassword dbname < dbdump.sql

I queried the database to ensure that the restoring the DB dump was a success. 

Encountered an issue

During development I experimented with mysql8.0 and mysql5.7. As the volume name was the same this created a complication and the DB container kept restarting. So I had to run the following command to fix it. However, ideally deleting the specific volume would have been suffice. 

$ docker system prune -a

Things to note

  1. As per this configuration, the host name is be db.
  2. Though the DB port binding is 8084 the env file can have the default port which is 3306. As all containers are inside their own network we need not use the external port. However I used the external port while configuring the DB connection in DBeaver.  

You can refer to the files in the following Git repo. 

containerizing-laravel-app

P.S. This article assumes that Docker and docker compose is already installed on your computer.