We can find so many Docker images in Docker Hub site. However, sometimes we cannot find a service that we want to use as part of our application or our team decided that the application will be dockerizing for ease of shipping and deployment.
Preparing
Now, let’s containerize a simple Web application that I have built using the Python Flask framework. The repository can be found in this link: https://github.com/WisnuAnggoro/simple-flask-web.
To create a Docker image, simply start by thinking what we might do if we want to deploy the application manually. First thing first, we setup an operating system, like Ubuntu
, then update the source repositories using the apt-get update
command, then install python
and the dependencies, then copy over the source code of our application to a location like /opt
and then finally run the Web server using the Flask
command.
Creating Dockerfile
To build Docker image, we have to write a text file named Dockerfile
. The file contains a set of instruction
and argument
format.
In Dockerfile
, everything on the left in capital leters is an instruction, for instance: FROM
, RUN
, and COPY
. Each of these instruction performs a specific action when creating the image. Everything on the right is an argument
to those instructions.
Based on the previous discussion about setting up things when we deploy application manually, we can transfer it to Dockerfile
as follow:
# Setup Ubuntu
FROM ubuntu
# Update Ubuntu Source Repositories
RUN apt-get update
# Install Python3 and Python3-Pip
RUN apt-get install -y python3 python3-pip
# Install Python Dependencies that is used in the code
COPY requirements.txt /tmp/requirements.txt
RUN python3 -m pip install -r /tmp/requirements.txt
# Run the web server
COPY app.py /opt/app.py
ENTRYPOINT FLASK_APP=/opt/app.py flask run --host=0.0.0.0
Here’s the explanation of the content of Dockerfile
we have written previously:
- The first line is
FROM ubuntu
which defines what the base OS should be used for this container. All Docker images must be based of another image, either an OS or another image that was created before. We can find official releases of all operating systems on Docker Hub. (Note that all Docker files must start with a from instruction). - The
RUN
instruction instructs Docker to run a particular command on those base images, whichbash
command in Ubuntu. - The
COPY
instruction copies files from the local system onto the Docker image. In this case, we copyrequirements.txt
file from local system to the image base OS so that it can install all dependencies stated in therequirements.txt
file from the image. - Finally,
ENTRYPOINT
instruction allows us to specify a command that will be run when the image is run as a container.
Building An Image
After completing the Dockerfile
, we can build an image by running the following command:
docker build . -f Dockerfile -t simple-flask-web
NOTE: -f
option is optional in case we create Dockerfile
with different name and -t
option is tag
which gives a name and tag to built image.
When Docker builds an image, it create this in a layered architecture. Each line of instruction creates a new layer in the Docker image with just the changes from the previous layer. It is also reflected in the size. To find out the size of each layer, we can docker history
followed by the image name as follow:
docker history simple-flask-web:latest
The output will be similar like follows:
IMAGE CREATED CREATED BY SIZE COMMENT
cea1c9b31a0c 7 seconds ago /bin/sh -c #(nop) ENTRYPOINT ["/bin/sh" "-c… 0B
63aebe23c639 9 seconds ago /bin/sh -c #(nop) COPY file:d9405d0b92ad5bd0… 325B
da157dc5d2bb 10 seconds ago /bin/sh -c python3 -m pip install -r /tmp/re… 4.38MB
3b4201da9273 14 seconds ago /bin/sh -c #(nop) COPY file:2f57c6de4d15d496… 5B
d5d4f906fdd3 16 seconds ago /bin/sh -c apt-get install -y python3 python… 314MB
a582710508ab 59 seconds ago /bin/sh -c apt-get update 27.1MB
d70eaf7277ea 4 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 4 months ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
<missing> 4 months ago /bin/sh -c [ -z "$(apt-get indextargets)" ] 0B
<missing> 4 months ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 811B
<missing> 4 months ago /bin/sh -c #(nop) ADD file:435d9776fdd3a1834… 72.9MB
As we can see in the last line, that is the Ubuntu layer which size is 72.9MB
and with reposities update size is 27.1MB
. Also, for python
and dependencies installation, the size is 314MB
. In total, the image size is 419MB
which can be found using docker images
command.
Pushing The Image To DockerHub
When we have our image ready, we can push it to DockerHub so it can be used by others. To do that, we can run docker push
command followed by the image name.
However, we have to rebuild the docker image and tag the docker name with the DockerHub account. My DockerHub account is wisnuanggoro
so I will tag the image with my account as follows:
docker build . -f Dockerfile -t wisnuanggoro/simple-flask-web
After that, we can run docker push
command followed by image name. I will push the previous built image to my DockerHub repository with the following command:
docker push wisnuanggoro/simple-flask-web
NOTE: If you get the error like this denied: requested access to the resource is denied
, you must login first using docker login
command.
Source links:
Best practices for writing Dockerfiles
Docker Credentials Store