Basics of Docker and Docker-compose (part-2) Dockerfile.

Arjun Dahal
5 min readAug 28, 2023

--

This is the second part of Docker and Docker-compose series. Here we will learn the components of Dockerfile. What is it? How can we use it? why are we using it? and Do some practical.

What is Dockerfile?

Dockerfile is a file without any extenstions and contains the step wise instructions to execute a certain steps while creating an image. Dockerfile we will see some syntaxes in later steps.

Why are we using it?

Dockerfile can be used to package code, choose os, packages, anything that is essential for our application. This means we can instruct specifically which os do we need, what dependencies we require. This proves the point that we might want to do it every time we deploy our code the infrastructure and dependencies might not change much, but setting each time and ensuring the code works everytime is a work, we had to do it manually and was error prone now we let the dockerfile specify all the task and it will be same each time.

Parctical

I will explain you each steps from a dockerfile that I have written for one of my project you should not copy it rather understand each steps and meaning of it.. Remember the more you try you will be better at the same thing. You will use the same syntax 80% of the time so feel free to explore rest 20% of things. Here is a development file meaning this is not for production and it is just a simple file so that we can work on same environment.

FROM python:alpine3.17

WORKDIR /server

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN apk update \
&& apk add postgresql-dev gcc python3-dev musl-dev libffi-dev

RUN pip install --upgrade pip

COPY ./requirements.txt /
COPY ./entrypoint-dev.sh /

RUN pip install -r /requirements.txt

COPY ./server/ .

ENTRYPOINT ["/entrypoint-dev.sh"]

So this this file let’s start from the beginning.

FROM python:alpine3.17

This code will do few things, It will check if you have docker image of python:alpine3.17 in your local machine. If it won't find the image it will download it from the docker-hub. Here we can find any kind of images we need for our development. I could have gone with the ubuntu or basic linux os like apline which is the lightest linux and installed all the dependencies by myself like python, gcc and other stuffs there but we don't want to do that much of work. we have python image and this image contains. The thing like os, python installed already in it. the :alpine means that it is taking alpine linux as os and installs our python over it. so that if we want to install any os level software we can use alpine linux command.

WORKDIR /server

This means that create a directory if it is not present and set that as our base directory. Our directory from now will be server and we will do everything inside it.

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

This is specific to python but this can be used in your project as well. let me explain you what i am doing here, i am using ENV to set environment variable for my project this will set environment variable to my os, Python will create cypthon or semi compiled code which will create a copy of files which i don’t want so i am just saying python dont write byte code and python unbuffered so that i don’t get extra files generated by python on my server folder.


RUN apk update \
&& apk add postgresql-dev gcc python3-dev musl-dev libffi-dev

Here comes the part, alpine uses apk as package manager and we need to install postgresql-dev, gcc, python3-dev, musl-dev, libffi-dev on our os so we are doing it here, our code need these dependencies on os level.

Run can be used to run the os level command specific for os platfrom.

RUN pip install --upgrade pip

Now, we are here upgrading the pip. It is a package manager for python. This again is installed on os and we are just updating it.

COPY ./requirements.txt /
COPY ./entrypoint-dev.sh /

Copy command is used for copying files and folders from our machine to the docker image. ./ means current directory and requirements.txt means the file we want to copy. / at the end means that copy requirements.txt to workdir i.e. /server/requirements.txt. Same thing for entrypoint-dev.sh

RUN pip install -r /requirements.txt

Once the file is copied we are installing all the packages on our os, That’s why we are using the RUN command.

COPY ./server/ .

Now, This time we are copying source code which is located in our server folder and into the workdir of the docker

ENTRYPOINT ["/entrypoint-dev.sh"]

Entrypoint gets executed when the container is build and started at the first time.

Let’s see another example here we will see another dockerfile here i am using php

FROM php:8.2-fpm

RUN curl -sS https://getcomposer.org/installer | php -- \
--install-dir=/usr/local/bin --filename=composer

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

RUN apt-get update && apt-get install -y zlib1g-dev \
libzip-dev \
unzip \
default-mysql-client

RUN docker-php-ext-install pdo pdo_mysql sockets zip


WORKDIR /var/www

ADD . /var/www
RUN touch /var/www/storage/logs/laravel.log

RUN composer install

# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www

# Copy existing application directory contents
COPY . /var/www

# Copy existing application directory permissions
COPY --chown=www:www . /var/www

# Change ownership of the application directory to www user and group
RUN chown -R www:www /var/www

# Make storage/logs writable by the www user
RUN chmod -R 775 /var/www/storage/logs

# Change current user to www
USER www

CMD ["php-fpm"]
EXPOSE 9000

CMD is the command that is executed when the container is started. Expose as word suggests it is exposing the port to the host machine.

Here is another example of dockerfile for java project.

FROM gradle:5.1.1-jdk8-slim as build

# Set customizable env vars defaults.
# Set Grails version
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src

RUN gradle clean --warning-mode=all


ARG BuildMode

RUN gradle bootWar -Dgrails.env=${BuildMode}


FROM tomcat:8.5.31 as production

EXPOSE 8080

RUN rm -rf /usr/local/tomcat/webapps/ROOT

COPY --from=build /home/gradle/src/build/libs/*.war /usr/local/tomcat/webapps/ROOT.war

CMD ["catalina.sh", "run"]

Here we are using something called multistage build where we are building the images on different stages. we are using ARG as we are getting value from argument while building the docker image.

Now let’s see how we can run and build docker image here is an example to build image and run container. for more information go here.

docker build -t test:2.0 .

This command just bulds the image with the tag as test:2.0 and . means the dockerfile is located in current path.

docker build -f Dockerfile.dev 

This command -f is used if you have different file name or dockerfile is located in different path.

docker run test:2.0 -p 8080:8080 -d 

we are running our image and exposing port -p 8080 and on -d detach mode meaning once the container is started we will have normal terminal.

you can get reference for commands here.

--

--

Arjun Dahal

I love building things from scratch, doing it everyday and learning things everyday. I am sharing it with you all. This is my journey.