Introduction

Welcome to the world of Docker! This comprehensive guide is your first step into containerization. We will explore the absolute fundamentals: Dockerfiles, Images, and Containers. Understanding these three core components is the key to mastering Docker. By the end of this guide, you will not only have built and run your first containerized application but also grasped the “why” behind each step.

We’ll build a simple Python Flask web application from scratch, package it into a portable Docker image, and run it as an isolated container on your local machine.

Prerequisites

  • Docker Installed: You need Docker running on your system. You can get it from Docker Desktop for Windows/Mac or by installing Docker Engine on Linux.
  • Text Editor: Any text editor like VS Code, Sublime Text, or even Notepad will work.
  • Basic Command-Line Knowledge: You should be comfortable opening a terminal or command prompt and running basic commands.

Part 1: A Deep Dive into the Core Concepts

Before we start building, let’s solidify our understanding of the foundational concepts.

What is a Dockerfile?

A Dockerfile is a text-based script of instructions that is used to create a container image. Think of it as a recipe for your image. Each instruction in the Dockerfile creates a new “layer” in the image. This layered approach is what makes Docker images efficient and fast to build and distribute.

  • It’s a Blueprint: It specifies the base operating system, the application code, the dependencies, the environment variables, and the command to run when the container starts.
  • It’s Version Controllable: Since it’s just a text file, you can check it into your source control (like Git) right alongside your application code, ensuring a reproducible build process.

What is a Docker Image?

A Docker Image is the result of a docker build command executed on a Dockerfile. It’s a read-only template that contains everything needed to run an application: the code, a runtime (like Python), system tools, libraries, and settings.

  • It’s a Snapshot: An image is a snapshot of a configured environment. It’s portable and can be shared among teams or deployed to any machine running Docker.
  • It’s Layered: Images are composed of a stack of layers, each corresponding to an instruction in the Dockerfile. When you change an instruction, only that layer and subsequent layers need to be rebuilt, making builds fast.
  • It’s Stored in a Registry: Images are typically stored in a registry, like Docker Hub (public) or a private registry, from where they can be pulled to run as containers.

What is a Docker Container?

A Docker Container is a runnable, live instance of a Docker image. It’s where your application actually runs. When you run an image, you create a container.

  • It’s an Isolated Process: A container runs in isolation from other processes on the host machine. It has its own private filesystem, network interface, and process space.
  • It’s Lightweight: Unlike a Virtual Machine (VM), a container does not bundle a full operating system. It shares the host machine’s kernel, making it incredibly lightweight and fast to start.
  • It’s Ephemeral (by default): The filesystem inside a container is temporary. Any changes made inside the container are lost when the container is removed, unless you use volumes to persist data.

Part 2: Building Your First Image - A Step-by-Step Guide

Let’s apply these concepts by containerizing a simple Python Flask web application.

Step 1: Create the Application Files

First, create a new directory for your project and cd into it.

mkdir my-python-app
cd my-python-app

Inside this directory, create the following three files.

1. index.py This is our simple web application. It listens for web requests and responds with “Hello World!”.

from flask import Flask
app = Flask(__name__)

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

if __name__ == "__main__":
    # The host='0.0.0.0' is crucial. It tells the app to listen on all network interfaces,
    # making it accessible from outside the container.
    app.run(host="0.0.0.0", port=int("5000"), debug=True)

2. requirements.txt This file declares the Python packages our application needs. For this app, it’s just flask.

flask

3. Dockerfile This is the recipe for our image. Create a file named Dockerfile (no extension) and add the following content. We’ll break down each line.

# 1. Set the base image
FROM python:3.9-alpine

# 2. Set the working directory inside the container
WORKDIR /app

# 3. Copy the dependency file and install dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt

# 4. Copy the rest of the application code
COPY . .

# 5. Expose the port the app runs on
EXPOSE 5000

# 6. Define the command to run the application
CMD ["python", "./index.py"]

Your directory should now look like this: image

Step 2: A Detailed Breakdown of the Dockerfile

  • FROM python:3.9-alpine: Every Dockerfile must start with a FROM instruction. It specifies the base image. We are using python:3.9-alpine, which is an official Python image based on the very lightweight Alpine Linux distribution. This keeps our final image size small.
  • WORKDIR /app: This sets the working directory for any subsequent RUN, CMD, COPY, and ADD instructions. If the directory doesn’t exist, it will be created. This is better than chaining RUN cd /app for every command.
  • COPY requirements.txt .: This copies the requirements.txt file from our host machine’s build context into the /app directory inside the image. We copy only this file first to take advantage of Docker’s layer caching (see Dockerfile Best Practices).
  • RUN pip install -r requirements.txt: The RUN instruction executes commands inside the image. Here, we are installing our Python dependencies. This creates a new layer that will be cached as long as requirements.txt doesn’t change.
  • COPY . .: This copies the rest of our application’s source code (in this case, index.py) into the /app directory.
  • EXPOSE 5000: This instruction informs Docker that the container listens on the specified network port at runtime. It’s primarily for documentation and does not actually publish the port.
  • CMD ["python", "./index.py"]: This provides the default command to execute when a container is run from this image. Our application will be started by running python ./index.py.

Step 3: Build the Docker Image

Now, let’s build the image. Make sure you are in the my-python-app directory in your terminal.

docker build --tag my-python-app .
  • docker build: The command to build an image.
  • --tag my-python-app: This applies a memorable name (a “tag”) to our image.
  • .: This is the build context. It tells Docker to use the current directory as the context, from which it can find the Dockerfile and the files to be copied.

You will see Docker stepping through each instruction in your Dockerfile, creating a layer at each step. image

Part 3: Running Your First Container

With our image built, we can now create and run a container from it.

Step 1: Run the Container

Execute this command:

docker run --name python-app -p 5000:5000 my-python-app

Let’s break down this command:

  • docker run: The command to create and start a container.
  • --name python-app: Assigns a human-readable name to the container for easy reference. If you don’t provide one, Docker will assign a random name.
  • -p 5000:5000: This is the port mapping. It maps port 5000 on your host machine to port 5000 inside the container. This is what allows you to access the application from your host’s browser. The format is -p <host-port>:<container-port>.
  • my-python-app: The name of the image to run.

Your terminal will now be attached to the container’s output, showing the logs from the Flask application. image

Step 2: Verify in Your Browser

Open your web browser and navigate to http://localhost:5000. You should see the “Hello World!” message from your app! image

Part 4: Interacting with Your Container

Here are a few essential commands for managing your container. You’ll need to open a new terminal window for these, as your first one is attached to the container’s logs.

  • List Running Containers:

    docker ps
    

    This will show you your python-app container, its ID, status, and port mapping.

  • View Logs: If you had run the container in detached mode (-d), you could view its logs with:

    docker logs python-app
    
  • Stop the Container:

    docker stop python-app
    
  • Remove the Container: You can only remove a stopped container.

    docker rm python-app
    

Conclusion

Congratulations! You have successfully built a custom Docker image and run it as a container. This is the fundamental workflow of Docker development.

In this comprehensive guide, you learned:

  • The distinct roles of Dockerfiles, images, and containers.
  • How to write a clear and efficient Dockerfile with step-by-step instructions.
  • The importance of each Dockerfile command (FROM, WORKDIR, COPY, RUN, EXPOSE, CMD).
  • How to build a tagged image from a Dockerfile.
  • How to run a container, including the critical concept of port mapping.
  • Basic commands to manage and interact with your running container.

This foundation is crucial as you move on to more advanced topics like persisting data with volumes, managing multi-container applications with Docker Compose, and orchestrating deployments with Swarm or Kubernetes.