Docker Ep 9: The Building Blocks β Detailed Structure of a Dockerfile
Alex now knows the basics, but itβs time to get their hands dirty by writing an actual Dockerfile.
The FROM
Instruction: Choosing the Foundation
The first thing Alex learns is the FROM
instruction, which sets the base image for their container. Itβs like choosing the foundation for a house.
- Purpose:
- The
FROM
instruction initializes a new build stage and sets the Base Image for subsequent instructions.
- The
- Choosing a Base Image:
- Alex decides to use a Python base image for their application. They learn that
python:3.9-slim
is a lightweight version, saving space and reducing the size of the final image.
- Alex decides to use a Python base image for their application. They learn that
FROM python:3.9-slim
Example: Think of FROM
as picking the type of bread for your sandwich. Do you want white, whole wheat, or maybe something gluten-free? Your choice sets the tone for the rest of the recipe.
The LABEL
Instruction: Adding Metadata (Optional)
Next, Alex discovers the LABEL
instruction. While optional, itβs a good practice to include metadata about the image.
- Purpose:
- The
LABEL
instruction adds metadata like version, description, or maintainer information to the image.
- The
- Example:
- Alex decides to add a maintainer label:
LABEL maintainer="alex@example.com"
Story Note: This is like writing your name on a sandwich wrapper, so everyone knows who made it and whatβs inside.
The RUN
Instruction: Building the Layers
The RUN
instruction is where Alex can execute commands inside the image, such as installing dependencies.
- Purpose:
- The
RUN
instruction runs any commands in a new layer on top of the current image and commits the results.
- The
- Example:
- To install the Flask framework, Alex writes:
RUN pip install flask
They also learn to combine commands to reduce layers:
RUN apt-get update && apt-get install -y curl
Story Note: Imagine slicing tomatoes and cheese for your sandwich and placing them carefully on top. Each ingredient (command) adds a layer of flavor.
The COPY
and ADD
Instructions: Bringing in Ingredients
Now, Alex needs to bring their application code into the container, which is where the COPY
and ADD
instructions come into play.
- COPY:
- The
COPY
instruction copies files or directories from the host filesystem into the containerβs filesystem.
- The
- ADD:
- The
ADD
instruction is similar toCOPY
but with additional features, like extracting compressed files.
- The
- Example:
- Alex copies their application code into the container:
COPY . /app
Story Note: This is like moving ingredients from your fridge (host) to the counter (container) where youβre preparing the sandwich.
The WORKDIR
Instruction: Setting the Workspace
Alex learns that setting a working directory makes it easier to manage paths within the container.
- Purpose:
- The
WORKDIR
instruction sets the working directory for subsequent instructions.
- The
- Example:
- Alex sets the working directory to
/app
:
- Alex sets the working directory to
WORKDIR /app
Story Note: This is like setting up a designated area on your counter where youβll assemble your sandwichβkeeping everything organized.
The CMD
and ENTRYPOINT
Instructions: The Final Touch
Finally, Alex learns how to define the default command that will run when the container starts.
- CMD:
- Provides defaults for an executing container, but can be overridden.
- ENTRYPOINT:
- Configures a container that will run as an executable, making it difficult to override.
- Example:
- Alex uses
CMD
to specify the command to start their Flask app:
- Alex uses
CMD ["python", "app.py"]
Story Note: Think of CMD
as the final step in making your sandwichβlike deciding to add a toothpick to hold everything together before serving.
Below is an example Dockerfile of a flask application,
# Use an official Python runtime as a parent image FROM python:3.9-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"]
Breakdown of the Dockerfile:
- FROM python:3.9-slim:
- This line specifies the base image. In this case, it uses a slim version of Python 3.9, which is lightweight and sufficient for a simple Flask application.
- WORKDIR /app:
- This sets the working directory inside the container to
/app
. All subsequent commands will be run inside this directory.
- This sets the working directory inside the container to
- COPY . /app:
- This copies everything from your current directory on the host machine into the
/app
directory inside the container.
- This copies everything from your current directory on the host machine into the
- RUN pip install βno-cache-dir -r requirements.txt:
- This installs the necessary Python packages listed in the
requirements.txt
file. The--no-cache-dir
option reduces the image size by not caching the downloaded packages.
- This installs the necessary Python packages listed in the
- EXPOSE 80:
- This makes port 80 available for external access. Itβs where the Flask application will listen for incoming requests.
- ENV NAME World:
- This sets an environment variable
NAME
to βWorldβ. You can access this variable in your Python code.
- This sets an environment variable
- CMD [βpythonβ, βapp.pyβ]:
- This tells the container to run the
app.py
file using Python when it starts.
- This tells the container to run the
Example Flask Application (app.py
):
To complete the example, hereβs a simple Flask application you can use:
from flask import Flask import os app = Flask(__name__) @app.route('/') def hello(): name = os.getenv('NAME', 'World') return f'Hello, {name}!' if __name__ == "__main__": app.run(host='0.0.0.0', port=80)
Example requirements.txt
:
And hereβs the requirements.txt
file listing the dependencies for the Flask app:
Flask==2.0.3
Building and Running the Docker Image:
- Build the Docker image using the Dockerfile:
docker build -t my-flask-app .
2. Run the Docker container:
docker run -p 4000:80 my-flask-app
- This maps port 4000 on your host machine to port 80 in the container.
Open your browser and go to http://localhost:4000
, and you should see βHello, World!β displayed on the page.
You can customize the ENV NAME
in the Dockerfile or by passing it as an argument when running the container:
docker run -p 4000:80 -e NAME=Alex my-flask-app
This will display βHello, Alex!β instead.