Docker is trending in both software development and IT for a long time. In this blog, I am going to explain what is docker? why should you use docker? and how to dockerize your Laravel or any other application.
What is Docker
Docker is an open-source tool designed to create, run, and deploy your application in an easy way. In a way, it is the same as a Virtual Machine, but unlike Virtual Machine, rather than creating a whole virtual operating system, Docker allows applications to use the same kernel
Why Docker?
Let say, I have added a new feature to the project, and it works fine in my local machine, but it does not work in production. For instance, with that new feature, I have installed dependencies, and I forgot to install those dependencies in production. Docker's purpose is to solve this problem.
What is Dockerfile?
Dockerfile is a configuration file that contains a collection of commands and instructions which that will be automatically executed in sequence in the docker environment for building a new docker image. The file is written in YAML markup language.
What is Docker Image?
Docker image is a file that contains source code, libraries, dependencies, tools, and other files needed to run an application. A docker image is described in a text file called a Dockerfile
, which has a simple, well-defined syntax.
What is Docker Container?
Docker container is a running instance of a docker image.
You can understand better the container, image, and Dockerfile with the help of the following image.
How to dockerize an application for development
Let's assume that you are planning to dockerize your Laravel and Vuejs application. If that is the case then you need to have two docker file. One docker file to run our Vuejs application, and the other docker file to run our Laravel application. Here is my Dockerfile
to run the Laravel application.
Dockerfile.dev
#choose the OS as a basae image
FROM php:7.3-fpm
#add metadata
LABEL version="1.0.0"
#install required dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
locales \
libzip-dev \
zip \
jpegoptim optipng pngquant gifsicle \
unzip \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
#specify the directory where the source code must be copied
WORKDIR /var/www
#Copy from the host machine to image
COPY composer.json composer.lock ./
COPY . .
#copy environment file
COPY ./.env.example ./.env
RUN composer install
RUN php artisan key:generate --ansi
RUN php artisan storage:link
COPY ./script/php_script.sh /tmp
RUN chmod +x /tmp/php_script.sh
ENTRYPOINT ["sh","/tmp/php_script.sh"]
#choose the port to communicate with a container
EXPOSE 8000
Following is the docker file to run Vuejs
Dockerfile.node
FROM node:14.15.0
WORKDIR /var/www
COPY package.json ./
RUN npm install
COPY . .
CMD npm run watch -- --watch-poll
and of course, you need to have another container for your database, and for the database, you do not need to have a Dockerfile. You will use an image of MySQL from docker hub.
To run the above docker files with a single command we will use docker-compose
.
What is Docker Compose?
You have two custom Dockerfiles to create an image from it and MySQL image from the docker hub. So you need to create, run, network, and volume all the images one by one, and as a developer, it is a headache to up and down all three images one by one.
Docker-compose solves this problem. Simply, by running only one command.
Docker-compose is a simple yet powerful tool that is used to run multiple containers as a single service. For example, suppose you have an application which requires nodejs to run Vuejs, PHP to run Laravel, and mysql as a database service. In this case by docker-compose, you can create one single file (docker-compose.yml ) which will create all the containers as a single service without starting each separately.
docker-compose.yml
version: "3.8"
services:
server:
build: .
container_name: server
ports:
- "${HTTP_PORT}:8000"
volumes:
- ./:/var/www/
- /var/www/vendor
depends_on:
- mysql
links:
- mysql
mysql:
image: mysql
container_name: mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_USER: ${DB_USERNAME}
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
ports:
- ${MYSQL_PORT}:3306
volumes:
- ./mysql/init.sql:/data/application/init.sql
- mysql_data:/var/lib/mysql
client:
build:
context: .
dockerfile: ./Dockerfile.node
container_name: client
volumes:
- ./:/var/www/
- /var/www/node_modules
depends_on:
- "server"
volumes:
mysql_data:
Simply run docker-compose up -d
to up, and docker-compose down
to down the services.
Setup Production Environment
In production, you do not need to have Nodejs and composer installed. all you need is to have PHP and Nginx or Apache to serve my application. However, you may need a Nodejs to run and build your Vuejs application, and a composer to install PHP dependencies. to avoid and reduce the image package size. you will have to use Docker multi-stage
feature.
What is Docker Multi-Stage?
Normally each docker file contains one FROM
statement as a base. Using Multi-Stage, you can have multiple FROM
, and each of them begins a new stage of the build. you can selectively copy artifacts from one stage to another, leaving behind everything you dont want in the final image. following is theDockerfile.prod
file.
#Client App
FROM node:14.15.0 as vuejs
LABEL authors="Nimat Razmjo
RUN mkdir -p /app/public
COPY package.json webpack.mix.js package-lock.json /app/
COPY resources/ /app/resources/
WORKDIR /app
RUN npm install && npm run prod
#Server Dependencies
FROM composer:2.0.8 as vendor
WORKDIR /app
COPY database/ database/
COPY composer.json composer.json
COPY composer.lock composer.lock
RUN composer install \
--ignore-platform-reqs \
--no-interaction \
--no-plugins \
--no-scripts \
--prefer-dist
#Final Image
FROM php:7.4-apache as base
#install php dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libpng-dev \
libonig-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
locales \
libzip-dev \
zip \
jpegoptim optipng pngquant gifsicle \
unzip \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
# change the document root to /var/www/html/public
RUN sed -i -e "s/html/html\/public/g" \
/etc/apache2/sites-enabled/000-default.conf
# enable apache mod_rewrite
RUN a2enmod rewrite
WORKDIR /var/www/html
COPY . /var/www/html
COPY --from=vendor /app/vendor/ /var/www/html/vendor/
COPY --from=vuejs /app/public/js/ /var/www/html/public/js/
COPY --from=vuejs /app/public/css/ /var/www/html/public/css/
COPY --from=vuejs /app/mix-manifest.json /var/www/html/mix-manifest.json
RUN pwd && ls -la
RUN php artisan key:generate --ansi && php artisan storage:link && php artisan config:cache && php artisan route:cache
# these directories need to be writable by Apache
RUN chown -R www-data:www-data /var/www/html/storage \
/var/www/html/bootstrap/cache
# copy env file for our Docker image
# COPY env.docker /var/www/html/.env
# create sqlite db structure
RUN mkdir -p storage/app \
&& touch storage/app/db.sqlite
VOLUME ["/var/www/html/storage", "/var/www/html/bootstrap/cache"]
EXPOSE 80
And following is docker-compose.production.yml
file to run all instance as a single service.
docker-compose.production.yml
version: "3.8"
services:
server:
build:
context: .
dockerfile: Dockerfile.prod
target: base
container_name: server
env_file:
- ./.env
depends_on:
- mysql
links:
- mysql
ports:
- 80:80
networks:
- back-tier
mysql:
image: mysql
container_name: mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_USER: ${DB_USERNAME}
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
ports:
- ${MYSQL_PORT}:3306
volumes:
- ./mysql/init.sql:/data/application/init.sql
- mysql_data:/var/lib/mysql
networks:
- back-tier
volumes:
mysql_data:
networks:
back-tier:
to run the application in production mode simply run docker-compose -f docker-compose.production.yml up -d
to up the service, and docker-compose -f docker-compose.production.yml down
to down the service
The source code is available on here
Note: I warmly welcome If you would like to contribute to the above project on Github.
Thanks for reading, If you enjoyed this article, share it with your friends and colleagues! Or, if you have any feedback in general, let me know :)