DEV Community

Isaac kumi
Isaac kumi

Posted on

Dockerizing a Flask Application: A Multi-Stage Dockerfile Approach

The provided code sets up a basic Flask application and Dockerfile for containerizing the application. Here's a brief comment on each part of the code:

# project setup
mkdir app
cd app
touch app.py
touch Dockerfile
touch requirements.txt
Enter fullscreen mode Exit fullscreen mode

This snippet creates the necessary files and directories for the project. It creates a directory named app, then creates files app.py, Dockerfile, and requirements.txt within the app directory.

Flask
Enter fullscreen mode Exit fullscreen mode

The requirements.txt file lists the dependencies required by the Flask application. In this case, it only includes the Flask dependency.

from flask import Flask
import os

app = Flask(__name__)

@app.route('/')
def hello():
    return f'Hello, World!'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
Enter fullscreen mode Exit fullscreen mode

The app.py file contains a simple Flask application. It imports the Flask module, creates an instance of the Flask class, defines a route for the root URL ("/"), and returns a "Hello, World!" message when the route is accessed.


# Stage 1: Build stage
FROM python:3.9 as builder

WORKDIR /app

# Copy requirements.txt to the container
COPY requirements.txt .
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools wheel

# Install dependencies
RUN pip install --no-cache-dir --upgrade -r requirements.txt

# Copy the application source code
COPY . .
RUN rm requirements.txt

# Stage 2: Production stage
FROM builder

WORKDIR /app

# Copy the installed dependencies from the previous stage
COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages

# Copy the application source code from the previous stage
COPY --from=builder /app/* .

# Expose port 5000
EXPOSE 5000

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

Enter fullscreen mode Exit fullscreen mode

The Dockerfile is divided into two stages. The first stage, named builder, sets up the build environment. It copies the requirements.txt file, upgrades pip, and installs the required dependencies. Then, it copies the application source code and removes the requirements.txt file.

The second stage, which is the production stage, inherits from the builder stage. It sets the working directory, copies the installed dependencies from the builder stage, and copies the application source code. The EXPOSE instruction exposes port 5000, and the CMD instruction specifies the command to run the application.

conclusion

One of the key benefits of using a multi-stage Dockerfile approach is the ability to create more optimized and efficient Docker images.

Smaller Image Size and Enhanced Security: With multi-stage builds, you can significantly reduce the size of the final Docker image. By separating the build environment from the runtime environment, only the necessary artifacts and dependencies are included in the production stage. This optimization results in smaller image sizes, which reduces disk space usage and speeds up image transfers. Additionally, by excluding build tools and dependencies from the final image, you minimize potential security risks. This approach ensures that only the essential components required for running the application are present, reducing the attack surface and enhancing the overall security posture of your containerized Flask application.

By incorporating a multi-stage Dockerfile into your containerization process, you can reap the benefits of smaller image sizes, improved security, and faster builds. This approach is particularly advantageous for large-scale projects and scenarios where optimizing image size and reducing security vulnerabilities are critical factors.

Top comments (6)

Collapse
 
curhvyn profile image
Michael Kevin Alabi-Duobu

Brilliant write up Isaac. Quick question, Are there any specific considerations to keep in mind when using a multi-stage Docker-file for Flask applications or other types of applications?

Collapse
 
isaackumi profile image
Isaac kumi

Thanks Michael, below are the considerations to keep in mind when using multi-stage build with docker.

  1. Understand the Docker multi-stage build concept: Familiarize yourself with the concept of multi-stage builds in Docker and how they work. It's essential to understand the different stages and their purposes in order to design an effective Dockerfile.

  2. Identify build dependencies: Determine the specific dependencies required for the build stage of your application. These dependencies may include compilers, development libraries, or build tools. Make sure to include only what is necessary for the build process and remove any unnecessary artifacts to keep the final image size minimal.

  3. Isolate sensitive information: Ensure that any sensitive information, such as credentials or API keys, is handled carefully during the build process. Avoid hardcoding sensitive information into the Dockerfile or any intermediate stages. Instead, use environment variables or secure secret management tools to pass them during runtime.

  4. Optimize image size: One of the main advantages of using a multi-stage Dockerfile is reducing the image size. Remove unnecessary artifacts, temporary files, and build dependencies in later stages to keep the final image as small as possible. This can improve deployment speed and reduce resource consumption.

  5. Separate development and production environments: Consider separating the development and production stages in your Dockerfile. Development stages can include additional tools and features that are useful during the development process but not required in the production environment. This helps minimize the attack surface and improves the efficiency of the production image.

  6. Test the resulting image: Once you have created your Docker image using the multi-stage approach, thoroughly test it to ensure the application behaves as expected in the containerized environment. Verify that all the necessary dependencies are included, and the application runs smoothly.

  7. Keep Docker best practices in mind: Adhere to general Docker best practices when working with Dockerfiles, such as using a minimal base image, leveraging caching, and properly configuring container settings.

Collapse
 
curhvyn profile image
Michael Kevin Alabi-Duobu

Thanks.

Thread Thread
 
isaackumi profile image
Isaac kumi

welcome

Collapse
 
pixelfixer_ profile image
Nooa L.

I think you've made a mistake here. By doing 'FROM builder', you're continuing off from the previous build stage. The build stage change is essentially just semantic. It's the same image with the same layers included.

If you comment out the two lines where you 'COPY --from=builder', start the container with the -it option and dig around the filesystem, you'll see that all of the files are still included there. There was no need to copy them as build artifacts, because you're still using the same image.

The proper way to do this would be to do 'FROM scratch' or some other image, and then copy the artifacts from the previous build stage.

A multi-stage build also doesn't make much sense here, since we're not actually building anything, and all the dependencies from the previous image are still required to run the application (pip, libraries and python).

This would only make sense if we were building the flask application into a static binary, after which we wouldn't need pip or python anymore and we could use an image without those dependencies as the next build stage.

Collapse
 
josephquayson profile image
Joseph Quayson

Love it !