Build applications & publish images with Docker in easy way

Raju Ahmed Shetu
intelligentmachines
7 min readJan 11, 2020

--

Motivation

Sharing code is one of most important aspect when it comes to working in a team. This actually leads to the inception of version control systems i.e. Github, Gitlab, Bitbucket etc. As a result, we can share the code easily but another problem arises when it comes to running the code in one’s pc or laptop. There are several challenges.

  • What if there is an os related dependency
  • There might be issues with the installation of a specific dependency on the os that one has
  • There might be lots of instruction and pre-requisite to follow to get up and running with the code

What if we can send the application built from the codebase into a CD or Floppy disk? That way, we can share the CD or Floppy to anyone and one can run the application from that CD or Floppy. Yet again, CD or Floppy are backdated, right? So what’s new then? It’s an Image.

Why Docker?

As I said earlier if Image is the new thing, what it has to do it with Docker. So, Docker is platform that can run image inside a container in one’s pc. Wait wait wait, from where does this container dude come? Let me tell you in detail. This figure has been taken from here.

Docker Platform Architecture

So let’s get started on each:

  • Infrastructure: This is simply the hardware that we have in our pc.
  • Host Operating System: This layer actually contains the files that are needed to run our laptop’s operating system.
  • Docker: The platform that will be installed on our host operating system that will help us to run images inside a container.
  • Container Layer: Docker platform can’t run image directly. It can store images in our host system using local storage. When it needs to run a image then Docker boots up a container and run that image inside it. Whenever we end up using it, Docker can simply stop the container and remove it if we want, but the images remain in the storage for further usage. So the rectangle above docker layer are actually containers running on docker and each text inside the rectangle are the images. i.e: App A, App B etc.

Still, Image is not explained. Simply think image as any operating system’s CD that we used to buy to install OS into our laptop. After installing we would have many programs in our pc just like browser, media player etc. But in this case, an Image actually contains only one specific application with all the dependencies installed in a bare minimum operating system.

So how is this image built?

Image are built using a set of instructions sequentially that are written in a file named Dockerfile. But, what’s the process? I will build a simple image using django application. Let’s go step by step. (I am using a mac so some of the commands might not work in windows)

1. Install Docker

Download Docker from here and install. After installing, run this in terminal

➜  ~ docker version

This should give the output like

Client: Docker Engine - Community
Version: 19.03.5
API version: 1.40
Go version: go1.12.12
Git commit: 633a0ea
Built: Wed Nov 13 07:22:34 2019
OS/Arch: darwin/amd64
Experimental: false
...

2. Initialize Django Application

Run this commands one by one in terminal. This will create virtualenvand start djangoproject and list all our dependencies.

pip3 install virtualenv                  # create virtualenv
mkdir docker_demo # create folder
cd docker_demo # go into folder
virtualenv venv -p /usr/bin/python3 # intialize venv
source venv/bin/activate # activate venv
pip install django #install dependency
pip install gunicorn #install dependency
django-admin startproject docker_demo . # start project
pip freeze > requirements.txt . # get requirements

3. Write Dockerfile

Now create a file named Dockerfile (remember, no extension) in project directory. And copy and paste these lines in Dockerfile.

FROM python:3
RUN mkdir /code
WORKDIR /code

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "docker_demo.wsgi"]

4. Directory Structure

At this point, the directory should look this

.
├── Dockerfile
├── docker_demo
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
└── requirements.txt

Explanation of each line in Dockerfile

FROM python:3

Every image has to contain a bare bone operating system files to run the application. Many a times, developers tries to install various dependency in a running container using a base image and take a snapshot which makes that another image where those dependencies are already installed.

But, how does this python:3 images comes? Well, Docker as an organization itself published some common images for developers to use. Here python is the name of image and 3 being the version of it. By default the nomenclature rule is like <name>:<version> if no version is specified, then by default latest is used.

I talked about base image before. Here python is not a base image. Actually pythonimage is based on alpine image which actually is the bare minimum operating system. So Docker organization actually install python3inside a container running alpine image, take a snap and later publish it. So now if we use python:3 image then we don’t have to install python every time as it’s been installed already.

So now, from where we get it. By default, Docker search for the image in our local storage in laptop or pc, if not found then it pull the image from docker image registry named dockerhub which is actually a platform just like github from where instead of code, we get pre built images.

RUN mkdir /code

We will create a directory named code under / directory when we will be building this image

WORKDIR /code

We are setting /code directory as our default destination for all the copy, move functionality.

COPY requirements.txt .

We are copying our local requirements.txt files into our images . directory. Here . means /code directory actually as we set /code as our WORKDIR.

RUN pip install -r requirements.txt

We are installing all the requirements for our application.

COPY . .

Now we copy all our local repository code to our images work directory

CMD ["gunicorn", "--bind", "0.0.0.0:8000", "docker_demo.wsgi"]

This line starting with CMD is the command that will run when we start a container using the image that we will build. This actually shows we will be getting our application running on port 8000 on the container when it will be fired.

Moment of Truth

Enough chit chat. It’s time to build the image. Just making sure we make some changes to docker_demo/settings.py The following line will have to be replaced.

ALLOWED_HOSTS = []

to

ALLOWED_HOSTS = ['*']

Now open up the terminal, make sure you are in your project directory and run the following command to build

docker build . -t docker-demo-image

Here docker-demo-image is the name of the image. If the build is successful, then run the following command to start off a container using the image

docker run -p 5000:8000 docker-demo-image

This command actually run docker-demo-image using docker and port forwards your machine’s 5000 port to container’s 8000port. As if you see dockerfile you can see we hosted our app on 8000 port. If all goes fine you should be seeing this when you go to http://localhost:5000/

Django Application Home Screen

Yay ! ! Our built image is now running in a docker container.

Optimization

When we ran build command in docker we might be seeing some like this.

Sending build context to Docker daemon 42.87MB

But from where this 42.87MB comes from where our project is merely a boilerplate code. Actually COPY . . command copies all the project files that were on local to our image include venv/ folder which is unnecessary as we are installing our dependencies by RUN pip install -r requirements.txt So now let’s remove venv/ folder from our built image. We can do it in two ways.

  1. We can manually copy which files to use or which is not

2. We can create a file name .dockerignore just like .gitignore in our project directory and put the names of the files into it which we want to discard.

Let’s use step 2 as step 1 would be tough to maintain. Create .dockerignore in project directory and copy Paste this following code in .dockerignore file.

venv/

Now run the build command again usingdocker build . -t docker-image-demo

You might be seeing something like this Sending build context to Docker daemon 26.62KB

Cool, right ? We just make our image slimmer by removing unnecessary dependency installation file.

Publication

As we can build image and run it, why not sharing it to the world ? Make sure you signed up in here, if you don’t have any account in dockerhub

  • Login from your terminal using docker login command. You might be asked your username and password.
  • Docker won’t let you push to it’s image registry unless the name of the image is in this format <docker-hub-username>/<name-of-image>
  • So build the image like docker build . -t <docker-hub-username>/docker-image-demo where <name-of-image> is docker-image-demo and replace <docker-hub-username> will be your docker hub username.
  • Now push like this docker push <docker-hub-username>/docker-image-demo

Last Words

You can find the all the codes in github and my built image in dockerhub

I wanted to go in details as much as I can. This is just the starting of the docker. You can achieve fascinating things with docker which will streamline your application development and deployment as well.

If you have any questions, drop a comment here.

--

--