I was recently debugging one Python application which ran in a Docker container. At some point, I’d like to debug the app in the container. At first, I was wondering if there is a way to run a Python application with a debug agent like you can do with Java, so that the agent listened in a port for incoming connections from a remote debugger. Unfortunately I didn’t find a convenient way how I could remotely debug my Python app. I found an article which describes how you can debug a Python application remotely with PyCharm IDE and SSH. If I understood correctly, PyCharm can deploy your application to a Docker container via SSH, then do some magic which is called “remote interpreter”, so as a result, you can debug the application from your local PyCharm installation. Looks like this feature is available only in a commercial PyCharm version, but I had only a community edition.

So, I gave up and decided just to connect to an SSH server which is run in a Docker container, and run my Python application manually with a debugger. Yes, hardcore only.

One of the simplest solution is just to have a switch between “production” and “debug” modes. That looked to me as a pretty simple and straightforward solution, but the problem is that you can’t run both SSH server and a Python application in the same Docker container. Or, at least I don’t know how to do that. If you add multiple CMD instructions to a Dockerfile, only one is going to be executed. It might be possible to have two Dockerfiles. The first one could run the application, and the second one could start an SSH server. But I didn’t want to have two Dockerfiles which had a common part which setup environment for the application.

So, if such a switch can’t be added to a Dockerfile, let’s add a wrapper bash-script which has this logic. We can use an environment variable to switch between modes.

Here is a Dockerfile, wrapper.sh and a simple Python application which I used:

#!/usr/bin/python
print("like most of life's problems, this one can be solved with bending")
view raw app.py hosted with ❤ by GitHub
FROM ubuntu
RUN apt-get update
# we're going to run a Python application
RUN apt-get install -y python3.5
# configure an SSH server in case we want to debug something
RUN apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:changeme' | chpasswd
RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile
EXPOSE 22
# add our stuff
ADD . /var/stuff
WORKDIR /var/stuff
CMD [ "bash", "wrapper.sh" ]
view raw Dockerfile hosted with ❤ by GitHub
#!/bin/bash
if [ "x${DEBUG}" = "xyes" ]; then
echo "ssh is ready, we'are waiting for you ..."
/usr/sbin/sshd -D
else
python3 app.py
fi
view raw wrapper.sh hosted with ❤ by GitHub

The following command builds a Docker image:

docker build --tag vivaldi .

Here is how you can run the application in a regular mode:

docker run vivaldi

And here is how you can switch to an SSH server which runs in the same Docker container:

docker run -p 8022:22 -e DEBUG=yes vivaldi

Here we forward a local port 8022 to 22 port in our container. Then, we can connect to the running container via SSH:

$ ssh root@localhost -p 8022

Good luck!

Links: