How to write Dockerfile for beginners
Published: 2024-07-12 11:10:36

Before diving into what a Dockerfile is, let's conduct a small experiment.

Remember the Python image we pulled in the previous section? Let's first enter the container using the `docker run -it` command. Recall what the -`it` parameter does—it assigns a pseudo-TTY to the container, allowing the user to interact with it.

Once inside the container, let's list all the files in the current directory:

```
[root@novita ~]# docker run -it python:3.7 /bin/bash
root@692f87774bf7:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@692f87774bf7:/# 
```

Next, we'll create a file named `hello.py` and then list all the files again:

```
root@692f87774bf7:/# touch hello.py
root@692f87774bf7:/# ls
bin  boot  dev  etc  hello.py  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
```

You can see that `hello.py` has been created. Now, let's exit the container and re-enter the interactive mode of the container using the same 

```
docker run -it python:3.7 /bin/bash
```
 command, followed by listing all the files:

```
root@692f87774bf7:/# exit
exit
[root@novita ~]# docker run -it python:3.7 /bin/bash
root@65c767655e8a:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@65c767655e8a:/#
```

You've probably noticed the issue by now. Curiously, the file we created earlier has vanished.

This is because actions performed inside the container do not modify the image. The container merely adds a writable layer on top of the image, much like tracing paper over a calligraphy template—no matter what you write on the tracing paper, it doesn't affect the layer beneath. This feature is somewhat akin to a VM snapshot.

But here's the problem: if actions within the container don't modify the image, how can we achieve rapid deployment without repeating certain operations before each code deployment? That would be quite inefficient.

Suppose you're the tech lead of a social media platform, and suddenly there's a viral event that requires scaling up to hundreds of cloud servers quickly to handle massive user traffic. Your team has chosen Docker for application deployment. If you were to deploy using the above method, users might be disappointed. By the time your service recovers, they might have already moved on.

Enter the Dockerfile. As per Docker's official introduction:

Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using `docker build`, users can create an automated build that executes several command-line instructions in succession.

In simpler terms, a Dockerfile is a text document containing all the commands needed to build an image from the command line. It's like an instruction manual for assembling a set of blocks into the desired image.

Let's explore how to construct a Dockerfile from a practical scenario, using a Python image as an example. On my local machine (outside the container), there's a Python script named `Optimal_Hotel_Matching.py`. It's a web scraping program. Let's try running it inside the container:

As expected, there's an error because the `requests` HTTP library, which is a third-party module, isn't installed in the Python image. This is a typical scenario where we need to set up a dependent environment. Let's attempt to solve this using a Dockerfile.

Before writing our first Dockerfile, let's familiarize ourselves with some common Dockerfile instructions:

- FROM: Specifies the base image for the build.
- MAINTAINER: Describes the creator of the image with their name and email.
- RUN: One of the most crucial commands in a Dockerfile. It executes commands in the container and commits the results.
- CMD: Specifies the command to be executed when the container starts.
- COPY: Copies files from the host into the container's filesystem.
- WORKDIR: Sets the working directory for any RUN, CMD, or ENTRYPOINT commands that follow in the Dockerfile.

Now, let's write our first Dockerfile to address our needs: installing the requests library and running the script.

Here's a possible solution:

```
FROM python:3.7                             # Specify the base image as python:3.7
MAINTAINER ultra "tech@novita.ai"     # Image creator's name and email
RUN pip install requests                  # Install the requests library
WORKDIR /dockerfileTest                   # Set the working directory
COPY . .                                  # Copy the current directory contents into the container
CMD ["python", "Optimal_Hotel_Matching.py"]  # Command to run the script
```

Save the above content in a file named `Dockerfile` in your project directory.

Now, let's build the image using the `docker build` command, tagging it for easy identification:

```
[root@novita dockerfileTest]# docker build --tag python:requests .
Sending build context to Docker daemon  5.12 kB
...
```

After the build completes, use the `docker images` command to verify:

```
[root@novita dockerfileTest]# docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
python                  requests            04f20acdc288        2 hours ago         926 MB
...
```

You can see that a new Python image tagged as `requests` has been generated locally. Try running this image to see if the `No module named 'requests'` error persists, and if the program runs as expected.

```
[root@novita dockerfileTest]# docker run python:requests
Computing distance between 116.368816,39.866464  and  116.438946,39.921624
Computing distance between 116.370910,39.869603  and  116.438946,39.921624
...
```

Success! The container ran the code and exited.

Is this the end?

In this piece, I've shared a simple application of Dockerfiles. In actual enterprise production environments, Dockerfiles tend to be more complex.