Mastering Docker: A Step-by-Step Guide for Beginners — Part 2

Kent Edoloverio
8 min readNov 10, 2023

--

The first article discusses containers and their functionality, including running multiple images inside them, as well as removing containers you can check it here. In this article we will creating an image using a Dockerfile. Once the image is built, it can be pushed to a central registry for deployment in other environments. Additionally, you’ll gain insight into image layers and how Docker efficiently stores images and runs containers using copy-on-write technology and the union file system.

https://www.booleanworld.com

Setup your Environment

You can use either Docker Personal and Play with Docker while following in this article

What is Docker Image

Docker Images play a crucial role in defining the application environment. They act as comprehensive blueprints that are virtually unchangeable, ensuring consistency and reliability. However, when containers are instantiated from these images, they can be modified to cater specifically to the needs of an application.

Docker Images fall into two primary categories: Official Base Images and Customized Images. The former is readily available for download through registries like Docker Hub, serving as a foundation for various applications. On the other hand, Customized Images leverage base images to create tailored environments that align precisely with specific application requirements.

Often likened to snapshots of an entire ecosystem, Docker Images encapsulate all indispensable components such as libraries, binaries, configuration files.

Difference between Image and Containers

Docker containers are powerful entities that encapsulate self-contained, fully functional software applications or services. These containers leverage Docker images as templates, which contain a precise set of instructions necessary to execute the application within the container.

While Docker images serve as repositories for sharing and reusing these templates, it is in creating and destroying containers throughout an application’s lifecycle where their true value shines. Containers provide a dynamic environment that can easily adapt to changing requirements and facilitate seamless deployment and scaling processes.

For this example, we will use a simple Flask Application. We’ll start by creating an image of the application and then running it inside a Docker image.

Create a Python Application (Without Docker)

To create a file named “app.py”, run this command in the terminal.

echo 'from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
return "hello world!"

if __name__ == "__main__":
app.run(host="0.0.0.0")' > app.py

This is a simple Python application that uses Flask to expose an HTTP web server on port 5000. (The default port for Flask is 5000.) It’s important to note that the concepts we’ll be discussing can be applied to applications written in any programming language, so don’t worry if you’re not too familiar with Python or Flask. To run the simple flask application use this command:

python app.py

Create and Build the Docker Image

To create an empty Dockerfile, use the command ‘touch Dockerfile’. The Dockerfile is a text document that contains all the necessary commands to build an image.

touch Dockerfile

Insert the following content inside the Dockerfile. I’ll explain its purpose later on:

FROM python:3.6.1-alpine
RUN pip install --upgrade pip
RUN pip install flask
CMD ["python","app.py"]
COPY app.py /app.py

If you are using Play With Docker, you can locate the Dockerfile by clicking on the Editor button in the instance window.

Now, let’s break down the commands in the Dockerfile line by line.

  • FROM python:3.6.1-alpine

This is the starting point for your Dockerfile. Every Dockerfile typically starts with a FROM line that is the starting image to build your layers on top of. In this case, you are selecting the python:3.6.1-alpine base layer because it already has the version of Python and pip that you need to run your application. The alpine version means that it uses the alpine distribution, which is significantly smaller than an alternative flavor of Linux. A smaller image means it will download (deploy) much faster, and it is also more secure because it has a smaller attack surface.

Here you are using the 3.6.1-alpine tag for the Python image. Look at the available tags for the official Python image on the Docker Hub. It is best practice to use a specific tag when inheriting a parent image so that changes to the parent dependency are controlled. If no tag is specified, the latest tag takes effect, which acts as a dynamic pointer that points to the latest version of an image.

For security reasons, you must understand the layers that you build your docker image on top of. For that reason, it is highly recommended to only use official images found in the Docker Hub, or noncommunity images found in the Docker Store. These images are vetted to meet certain security requirements, and also have very good documentation for users to follow. You can find more information about this Python base image and other images that you can use on the Docker store.

For a more complex application, you might need to use a FROM image that is higher up the chain. For example, the parent Dockerfile for your Python application starts with FROM alpine, then specifies a series of CMD and RUN commands for the image. If you needed more control, you could start with FROM alpine (or a different distribution) and run those steps yourself. However, to start, it's recommended that you use an official image that closely matches your needs.

  • RUN pip install flask

The RUN command executes commands needed to set up your image for your application, such as installing packages, editing files, or changing file permissions. In this case, you are installing Flask. The RUN commands are executed at build time and are added to the layers of your image.

  • CMD ["python","app.py"]

CMD is the command that is executed when you start a container. Here, you are using CMD to run your Python applcation.

There can be only one CMD per Dockerfile. If you specify more than one CMD, then the last CMD will take effect. The parent python:3.6.1-alpine also specifies a CMD (CMD python2). You can look at the Dockerfile for the official python:alpine image.

You can use the official Python image directly to run Python scripts without installing Python on your host. However, in this case, you are creating a custom image to include your source so that you can build an image with your application and ship it to other environments.

  • COPY app.py /app.py

This line copies the app.py file in the local directory (where you will run docker image build) into a new layer of the image. This instruction is the last line in the Dockerfile. Layers that change frequently, such as copying source code into the image, should be placed near the bottom of the file to take full advantage of the Docker layer cache. This allows you to avoid rebuilding layers that could otherwise be cached. For instance, if there was a change in the FROM instruction, it will invalidate the cache for all subsequent layers of this image. You'll see this little later in this lab.

It seems counter-intuitive to put this line after the CMD ["python","app.py"] line. Remember, the CMD line is executed only when the container is started, so you won't get a file not found error here.

Build the Docker Image

To build a docker image use this command:

docker image build -t python-hello-world .

Pass the parameter -t to name your image python-hello-world

Note: You need to copy the entire command till the ‘ . ‘ at the end of command — this is required and indicates the root path

Build and use the parameter -t to name the image

To verify and show your latest image you can use this command:

docker image ls
It will display the most current image you’ve generated

Run the Docker Image

To run the docker image you can use this command bellow:

docker run -p 5001:5000 -d python-hello-world

The -p flag allows you to map a port from inside the container to your host. In this case, the Python app is being mapped from port 5000 inside the container to port 5001 on your host. If port 5001 is already in use by another application on your host, consider using a different value like 5002.

Navigate to http://localhost:5001 in a browser to see the results.

You should see “hello world!” in your browser.

The image is successfully running

To view the logs of the container, simply access your Docker Personal and navigate to the containers. Click on the recently created container and select “Logs” to see its output. You can also view inside the terminal by simply typing this command:

docker container logs [container id]

The Dockerfile is used to create reproducible builds for your application. A common workflow is to have your CI/CD automation run docker image build as part of its build process. After images are built, they will be sent to a central registry where they can be accessed by all environments (such as a test environment) that need to run instances of that application. In the next section, you will push your custom image to the public Docker registry, which is the Docker Hub, where it can be consumed by other developers and operators.

Push to a Central Registry

To get started, go to Docker Hub’s website and sign up for a free account.

Authenticate your access to the Docker registry by logging in using the docker login command on your terminal.

In my situation, if you install and login to Docker Desktop, it will automatically login; otherwise, it will prompt you for your username.

Tag the image with your username.

The Docker Hub naming convention is to tag your image with [dockerhub username]/[image name]. To do this, tag your previously created image python-hello-world to fit that format.

docker tag python-hello-world [dockerhub username]/python-hello-world

After correctly tagging the image, run the docker push command to upload it to the Docker Hub registry:

It will automatically upload your image into Docker Hub

In your browser, look for your image on Docker Hub.

To view your uploaded image, go to Docker Hub and select your profile.

Now that your image is on Docker Hub, other developers and operators can use the docker pull command to deploy your image to other environments.

Remember: Docker images are a convenient solution for managing application dependencies. They eliminate the concern of environment drift and remove the need for multiple provisioning steps. With Docker, all you have to do is install it in one simple step and you’re good to go.

Leave a 👏 and comment and let me know what you think.

Do you enjoy what you’ve read so far? Join and buy me a ko-fi

© All Rights Reserved Kent Edoloverio

--

--

Kent Edoloverio

I’m an undergraduate student who enjoys blogging, 3D modeling, and programming.