Build applications & publish images with Docker in easy way
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.
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 virtualenv
and start django
project 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 python
image is based on alpine
image which actually is the bare minimum operating system. So Docker organization actually install python3
inside 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 8000
port. 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/
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.
- 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>
isdocker-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.