Building Docker images is a fundamental skill for anyone working with Docker. In this section, we will cover the following topics:

  1. Introduction to Docker Images
  2. Creating a Dockerfile
  3. Building an Image from a Dockerfile
  4. Best Practices for Writing Dockerfiles
  5. Practical Example
  6. Exercises

  1. Introduction to Docker Images

Docker images are the blueprints for Docker containers. They contain everything needed to run an application, including the code, runtime, libraries, environment variables, and configuration files.

Key Concepts:

  • Image Layers: Docker images are built in layers, with each layer representing a set of file changes.
  • Base Image: The starting point for building a Docker image, often an official image from Docker Hub.
  • Dockerfile: A text file that contains a series of instructions on how to build a Docker image.

  1. Creating a Dockerfile

A Dockerfile is a script containing a series of instructions on how to build a Docker image. Each instruction in a Dockerfile creates a new layer in the image.

Basic Dockerfile Instructions:

  • FROM: Specifies the base image.
  • RUN: Executes a command in the container.
  • COPY or ADD: Copies files from the host to the container.
  • CMD or ENTRYPOINT: Specifies the command to run when the container starts.
  • EXPOSE: Defines the port on which the container will listen.

Example Dockerfile:

# Use an official Python runtime as a parent image
FROM python:3.8-slim

# Set the working directory in the container
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY . /app

# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]

  1. Building an Image from a Dockerfile

To build a Docker image from a Dockerfile, use the docker build command.

Command Syntax:

docker build -t <image_name>:<tag> <path_to_dockerfile>

Example:

docker build -t my-python-app:latest .

This command builds an image named my-python-app with the tag latest from the Dockerfile in the current directory.

  1. Best Practices for Writing Dockerfiles

  • Use Official Base Images: Start with official images from Docker Hub to ensure security and stability.
  • Minimize Layers: Combine multiple RUN commands into a single command to reduce the number of layers.
  • Leverage Caching: Order instructions from least to most frequently changing to take advantage of Docker's layer caching.
  • Clean Up: Remove unnecessary files and packages to keep the image size small.

  1. Practical Example

Let's create a simple Python web application and build a Docker image for it.

Step-by-Step Guide:

  1. Create a Project Directory:

    mkdir my-python-app
    cd my-python-app
    
  2. Create a Python Application:

    • app.py:
    from flask import Flask
    app = Flask(__name__)
    
    @app.route('/')
    def hello():
        return "Hello, World!"
    
    if __name__ == "__main__":
        app.run(host='0.0.0.0', port=80)
    
    • requirements.txt:
    Flask==1.1.2
    
  3. Create a Dockerfile:

    FROM python:3.8-slim
    WORKDIR /app
    COPY . /app
    RUN pip install --no-cache-dir -r requirements.txt
    EXPOSE 80
    CMD ["python", "app.py"]
    
  4. Build the Docker Image:

    docker build -t my-python-app:latest .
    
  5. Run the Docker Container:

    docker run -p 4000:80 my-python-app:latest
    
  6. Access the Application: Open a web browser and navigate to http://localhost:4000 to see the "Hello, World!" message.

  1. Exercises

Exercise 1: Create a Dockerfile for a Node.js Application

  1. Create a simple Node.js application.
  2. Write a Dockerfile to build an image for the application.
  3. Build and run the Docker container.

Exercise 2: Optimize a Dockerfile

  1. Take the provided Dockerfile and optimize it by reducing the number of layers and cleaning up unnecessary files.

Solutions

Solution 1:

  1. Node.js Application:

    • app.js:
    const express = require('express');
    const app = express();
    
    app.get('/', (req, res) => {
        res.send('Hello, World!');
    });
    
    app.listen(3000, () => {
        console.log('Server is running on port 3000');
    });
    
    • package.json:
    {
        "name": "my-node-app",
        "version": "1.0.0",
        "main": "app.js",
        "dependencies": {
            "express": "^4.17.1"
        }
    }
    
  2. Dockerfile:

    FROM node:14
    WORKDIR /app
    COPY . /app
    RUN npm install
    EXPOSE 3000
    CMD ["node", "app.js"]
    
  3. Build and Run:

    docker build -t my-node-app:latest .
    docker run -p 4000:3000 my-node-app:latest
    

Solution 2:

  1. Original Dockerfile:

    FROM python:3.8-slim
    WORKDIR /app
    COPY . /app
    RUN pip install --no-cache-dir -r requirements.txt
    EXPOSE 80
    CMD ["python", "app.py"]
    
  2. Optimized Dockerfile:

    FROM python:3.8-slim
    WORKDIR /app
    COPY requirements.txt requirements.txt
    RUN pip install --no-cache-dir -r requirements.txt
    COPY . /app
    EXPOSE 80
    CMD ["python", "app.py"]
    

Conclusion

In this section, we covered the basics of building Docker images, including creating a Dockerfile, building an image, and best practices for writing Dockerfiles. We also provided a practical example and exercises to reinforce the concepts. In the next section, we will explore managing Docker images, including tagging and pushing images to Docker Hub.

© Copyright 2024. All rights reserved