Writing Your First Dockerfile

  • What is Dockerfile?

A Dockerfile is a build recipe for a Docker image. It contains a series of instructions telling Docker how an image is constructed. The docker build command builds an image from a Dockerfile.

  • How do I write it and what instructions I can pass?

There are many instructions you pass to a Docker file. I would recommend you to read and understand what each of below does. In this section, we will talk about a few of the instruction sets, which really affects how you build your docker image.

  • Why do I need it?

To automate building a Docker image, you describe the building steps in a Docker manifesto called the Dockerfile. This text file uses a set of instructions to describe which base image the new container is based on, what steps need to be taken to install various dependencies and applications, what files need to be present in the image, how they are made available to a container, what ports should be exposed, and what command should run when a container starts, as well as a few other things.

  • Let’s write our first Dockerfile. The resulting image will allow you to create a container that executes the /bin/echo command. Create a text file called Dockerfile in your working directory and write the following content in it.
$ mkdir DockerDemo; cd DockerDemo
  • Create a Dockerfile with the below content
FROM ubuntu:14.04
ENTRYPOINT ["/bin/echo"]

N.B : The FROM instruction tells you which image to base the new image. Here you choose the ubuntu:14.04 image from the Official Ubuntu repository in Docker Hub. The ENTRYPOINT instruction tells you which command to run when a container based on this image is started. To build the image, use the below command.

$ docker build .
  • To see the built is successful, use the below command
$ docker images
  • Lets Run the container
$ docker run IMAGE ID Hi Docker!

N.B: In the above example we used ENTRYPOINT, now try using say /bin/date as an argument to the above command.

  • There is another instruction CMD, Lets see what it does and how its different from ENTRYPOINT.
FROM ubuntu:14.04
CMD ["/bin/echo" , "Hi Docker !"]
  • Let’s build it and run it:
$ docker build .
$ docker run CONTAINER_ID 

N.B : It looks the same, but if you pass a new executable as an argument to the docker run command, this command will be executed instead of the /bin/echo defined in the Dockerfile. That means you can override what is already defined in Dockerfile.

$ docker run CONTAINER_ID /bin/date

N.B : Remember that CMD can be overwritten by an argument to docker run, while ENTRY POINT can be overwritten only by using the --entrypoint option of docker run

Dockerize your first Python+Flash application

Scenario

Suppose you have a python flask based application running on your server and you would like to migrate it. Instead of migrating AS-IS, You would like to Dockerize it and then ship it.

$ mkdir myfirstpythonapp; cd myfirstpythonapp
  • Let’s create a python application listening on port 5000. Create a file called hello.py with the following contents:
#!/usr/bin/env python
from flask import Flask
app = Flask(__name__)
@app.route('/hi')
def hello_world():
    return 'Welcome to Container bootcamp!'
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
  • Let’s create a Dockerfile, which will copy the above file to /opt and run it.
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y python
RUN apt-get install -y python-pip
RUN apt-get clean all
RUN pip install flask
ADD hello.py /opt/hello.py
EXPOSE 5000
CMD ["python","/opt/hello.py"]
  • Let’s build the image
$ docker build -t helloworld .
  • Now let’s run the container, daemonize it by using -d option and -P to let Docker choose a port on the Docker host will be mapped to the exposed port specified in the Docker‐ file.
$ docker run -d -P helloworld

$ docker ps

Lets optimize the above Dockerfile.

  • The above Dockerfile is written badly (purposely). For example to install a few packages using multiple RUN commands. This is bad practice, as it will add unnecessary layers to the image. You also used the ADD command to copy a simple file. Instead in this example, you should use the COPY command. Let’s see how it affects your docker image size and build time.
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y \
python \
python-pip
RUN pip install flask
COPY hello.py /tmp/hello.py
  • Now build the above docker image and check the image size and number of layers.
  • Test your application

Is it really tuned? What do you think? Lets try another approach

  • Let’s further tune it by changing the base image being used, leveraging a Python integrated, Alpine Linux based image. This image already includes all the required Python dependencies, meaning that getting flask in place requires a single pip install command
FROM python:2.7.16-alpine
RUN pip install flask
COPY hello.py /tmp/hello.py