Debugging Python application in Docker using VSCode

Recently I learned docker for a project purpose liked it very much. You might be thinking come on ashok who doesn’t like Docker. True. So I built a docker image for my product and deployed it successfully in production successfully using docker-compose. Started using docker also in the development environment everything looked great until I sat to fix a bug.

You can say ashok what’s the big deal use VSCode. You are right to do that I need to install ptvsd package it means I need to add this in requirement.txt and I don’t want to because the production Docker image also will have it. Not only that even application execution command also changes with respect to production one.

I am sure by this time you will say “Ok ok Ashok we got what you are saying. Now tell me the solution”. Let me share with you the solution which I used and felt appropriate. I used the Docker multistage build concept for this.

What is Docker multi-stage build?

A multistage build allows you to use multiple images to build a final product. In a multistage build, you have a single Dockerfile, but can define multiple images inside it to help build the final image.

For more details go to this link.

Let me share the Example code for the application and docker side so that you will understand better.

A simple hello world application.

# app.py
from flask import Flask
app = Flask(__name__)

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

requirement.txt will be like this

Flask

Dockerfile looks like this

FROM python:3.9-alpine as base

RUN mkdir /app
WORKDIR /app

COPY ./requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt

COPY ./ /app/

########### START NEW IMAGE : DEBUG ###################
FROM base as debug
RUN pip install ptvsd

CMD python -m ptvsd --host 0.0.0.0 --port 5678 --wait --multiprocess -m flask run -h 0.0.0.0 -p 5000

########### START NEW IMAGE: PRODUCTION ###################
FROM base as prod

CMD flask run -h 0.0.0.0 -p 5000

Let me explain a bit about the Dockerfile.

We are preparing our image from python:3.9-alpine image and calling it as base like a variable. So we can use the variable at later stages.
Then we created a directory called app and made it a working directory.
Then we copied our requirements.txt to the app folder in docker.
Next, we are installing the python packages mentioned in the requirements.txt.
Then copying all our code to the app folder in docker.

Now we kind of created a base image. Now according to our requirements, we will create the appropriate docker image. Let’s say we want a docker image for code debugging purposes then image at debug stage we build or we want it for production then the prod stage.

If you can see when we want an image for debugging purposes we need to specify in the docker build command debug build stage. Then it uses whatever image was created for the base stage and then installs ptvsd package and then the command which facilitate the remote debugging option through the 5678 port.

If we want the image for production purposes it will use whatever image was created for the base stage and adds a flask command to it because of this we don’t need to install unnecessary packages and we don’t need irrelevant commands to run the application.

Now let’s come to the main section. How to do debug.

  1. build debug stage image
  2. run that docker image(container)
  3. start debug at VSCode
  4. Do the debugging, fix the bug and save the world 😁.

To build debug stage image run this command

sudo docker build --target debug -t flask-hello-world:debug .

To run the docker image(container)

sudo docker run -p 5000:5000 -p 5678:5678 flask-hello-world:debug

Now you don’t see any log don’t worry because we didn’t start remote debug so it is waiting for that.

Now in VSCode open app.py.
Then go to Run and Debug section which is on the left page.
Click on Run and Debug button.
You will get multiple options in that choose Remote Attach option.
Now you will get the hostname option nothing needs to be changed so just press enter.
Then port option will come nothing need to be changed so press enter.

Now you can see the terminal there you will find a log that will assure you that the application started with what hostname on what port.

Add breakpoint and start debugging. All the best for your bug fix.

If you want to build the image for production purposes then run the below command.

sudo docker build --target prod -t flask-hello-world:prod .

To run the production docker image(container)

sudo docker run -p 5000:5000 flask-hello-world:prod

Note: For educational purpose wrote this article that’s why running the application using flask command. Use gunicorn or something similar package to run your application production.

You will find the code here.

Peace. Happy Coding.

Related Post

Leave a Reply

Your email address will not be published. Required fields are marked *