How to containerize an application using docker?
Published: 2022-02-07 05:24:39



## What is Docker?

Docker is a platform where we can run, build, test and deploy our application within containers. The Build, Test, Execute and Deploy steps were/are usually done on local machines but using containers instead proves to be more beneficial especially in the long run. Using Docker CLI we can Build Docker images as well Pull/Push Docker images to the Open Docker registry. Run/Stop Docker Containers and much more!

## What are Containers?

We can create containers using Docker CLI. Containers are nothing but running instances of docker images which we can modify and run them. You can think of Containers as a very Lean Virtual machine, because the images are usually based on Operating Systems and only contain the required packages/ software. 

Therefore you can interact with Container in a similar fashion you do with a Virtual machine, you will be able to see the output of the code in the terminal, and if its a web app, you can expose a port where you will be able to see the webpage just like it runs on localhost:port when you run these applications locally. It can be a little confusing hence I would not recommend containerizing an app until you understand how the app works, a brief idea would also suffice, and know how to debug and know what's wrong will help a lot.

## What are Docker Images?

TLDR; Docker images are a lean way of running an OS which can be as small as 35mb like Alpine with just the software you need to build and run the application.

Lets first ask the question, What is the need for an Docker Image? 

There are several desktop and Mobile OS used by millions of users, and when developing applications we need to make sure to include most of them if not all. Before the inception of Docker, Say you are a developer who created a desktop app for windows on your windows machine, and now you want to make it available to your friends using linux. You have a few options here, 

1. Install Linux on your machine - you would probably dual boot windows with a linux based Distribution
2. Create a Virtual machine using software like Virtual Box or VMware
3. Use a Virtual machine from a Cloud provider.
4. Get a new machine altogether for Linux Development 

Option 1 is time consuming as you would need to restart every time you want to test your application on particular OS.

Option 2 has been a decent option for quite a long time, there are certain drawbacks as you would be essentially running two OS, so you machine - Laptop or Desktop would need the resources for it to be usable.

Option 3 is a cheaper option than actually buying new hardware, but there are extra steps whenever you want to download the code to run it else where, and to create more instances to scale you would need to first prepare the new environment again.

Option 4 is costly because of cost of hardware, also costly to scale and needs to be setup manually, also costly to replace if something goes down.

## What is a Dockerfile?

Dockerfile is used to build an image as per your needs. So there are already several images on [docker hub](http://hub.docker.com) which you can explore, we use these images as a base for our docker container, then we will most like have to customise it to our requirements. Within a Dockerfile we can add instructions which choose a base image, install packages/ dependencies, copy files from host machine to container file system, expose ports which will help us connect to the container from our local machine. Let me tell you why Dockerfile is powerful. Say you move the code to another computer, and need to run the app locally, you would need to install a lot of things, Now imagine you move a Dockerfile to that computer and just install docker there. Now you just have to build the image and run it, all the dependencies will be installed in the build phase and application will run. Once you are done you can shut it down, and the resources will be freed, best part, localmachine will be untouched and no softwares will be left behind taking up space. 

## How do I get Docker?

You can install docker on your machine by following the docs here - [Install Docker](https://docs.docker.com/get-started/#download-and-install-docker). This step is required to proceed if docker is not installed on your machine. The next steps are optional.

Few Tips from my experience:

1. Use Vscode as your code editor, it not only has plugins which aid in Dockerfile syntax highlighting but also a panel to view and manage your docker images, containers and more. You can install it from the Extension marketplace.
2. Remote - Containers extension is also useful as it can build and run docker containers, and also open them within Vscode, how cool is that? This is helpful especially for debugging, For eg: When I wanted to change config files for nginx which are stored within the containers, I could use Vscode to do that, there are other ways but I feel this is the most beginner friendly

With the above ready as per your need you should be ready to go forward.

## How to get started containerizing your application?

So typically for most applications there will be certain steps you need to complete in order to be able to run it, they are:

1. Download dependencies/ required packages
2. Build the application (If required)
3. Run the application

For eg: In Javascript, we use node package manager(npm) for most of these steps namely, 

1. `npm install`→ installs the dependencies from the package.json file
2. `npm run build` → Builds the application (Typically frontend applications have a build step to create a production build)
3. `npm run start` → Will start your application and it’ll most likely listen on a port, you can go to this port from your browser on localhost:port and if you are serving some web page here, you should be able to view it.

In Python you would use Pip which is like a npm equivalent package manager for Python, 

1. `pip install -r requirements.txt` → This command will install all the dependencies from the requirements.txt
2. & 3. The Build and Run steps will depend on the python framework your application is using.

Once we have identified these steps, we can additionally make sure that our application is working fine locally, because if it doesn’t work locally, it probably wont in a docker container. 

## Writing your Dockerfile

We start by creating a file named Dockerfile within our project root. Note there is no file extension.

We then open this file and get started. This is the contents of my Dockerfile for a FastAPI application which is a Python framework for web servers, similarly to Django/Flask. 

Note: Even though you make not be using the same setup and config, it should be similar.

```docker
FROM alpine:latest
RUN apk add py3-pip python3-dev build-base libffi-dev
WORKDIR /code
COPY . /code/

WORKDIR /code
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
ENTRYPOINT [ "uvicorn" ]
CMD ["index:app", "--host", "0.0.0.0", "--workers", "4"]
```

```docker
FROM alpine:latest
```

The FROM command chooses the base image, we choose Alpine OS as its one of the lean and easy to use ones, you can read more about it [here](https://hub.docker.com/_/alpine)

```docker
RUN apk add py3-pip python3-dev build-base libffi-dev
```

The RUN command in a dockerfile is used to run commands within a container, in simple words we are passing these commands to the terminal in our alpine os. apk is basically and apt-get for alpine OS. We are installing these dependencies which are usually found in most linux distributions, but as alpine is lean, we have to most of it if we require it. How I found out I need it is there were errors which didn’t build the container, upon checking the errors on stackoverflow, I easily found out the dependencies required. You can use an ubuntu base image which shouldn’t have issues with this, but you still might require something additional so a good thing to know.

```docker
 
WORKDIR /code
COPY . /code/
```

With the WORKDIR command we are setting out work directory which will be used by other next commands such as COPY. If this directory doesn't exist it will be created. Also do note the difference between /code which is absolute path where it checks for /code in root directory, and ./code which looks up the code directory within the current working directory.

The COPY command is used to copy files from our host machine to the container filesystem so that the code and requirements.txt can be used by the next commands. The commands usually depend on the previous command so if there’s some error, you should check the commands preceding the one which raised the error.

```docker

RUN pip install --upgrade pip
RUN pip install -r requirements.txt
```

We again use the RUN command to use pip, here I upgrade pip to the latest version then use pip install to install the requirements. Upgrading pip is a good practice because sometimes certain packages can have issues when using with an older version of pip.

```docker
ENTRYPOINT [ "uvicorn" ]
CMD ["index:app", "--host", "0.0.0.0", "--workers", "4"]
```

Finally, I will be using uvicorn to run and host my application, this can be different for you, for eg: if using javascript, it could be node. ENTRYPOINT allows the container to be run as executable, which means it will keep running which we want, usually if the task has finished, either with a success or error code, the container will stop running. The CMD passes arguments to our entrypoint, you can also use CMD on its own, but using an ENTRYPOINT is recommended, also the Last ENTRYPOINT defined in the container will keep running in the container overshadowing all previous ones.

## How do I build a Docker Image?

Okay, so after we have created our dockerfile, we are ready to build our docker image. 

```docker
docker -t container_name build .
```

docker build command is used to build our image, we provide the location of Dockerfile using . (period), -t flag helps us provide a nice name to our container, which would be a random name if omitted. you can use the —help flag after docker command or docker [commands] to view the help and available flags within the terminal

```docker
docker run -p 8000:8000 container_name
```

docker run command is used to run the container, which will be listening on a port within the container, now if we want to be able to access it, we use the -p flag to port forward the port to our local machine, syntax is -p host_port:container_port, where the container port will be determined by where your application is listening at. 

After this, we can go to http://localhost:8000 or what you have set and be able to see our application, we have successfully build and run our application and I feel you deserve a pat on your back. For any changes to be reflected, you would have to follow these steps again namely build and run. Also note that if you do any changes within the container, they will not persist, you will have to mount a folder on the host in the container filesystem for that, which is out of scope for this tutorial but you can read about [here](https://docs.docker.com/engine/reference/builder/#volume)