Monday, October 12, 2020

Wing Tips: Debug Docker Compose Containerized Python Apps with Wing Pro

This Wing Tip describes how to configure Docker Compose so that Python code running on selected container services can be debugged with Wing Pro. This makes it easy to develop and debug containerized applications written in Python.

Prerequisites

To get started, you will need to Install Docker Compose.

You will also need an existing Docker Compose project that uses Python on at least one of the container services. If you don't already have one, it is easy to set one up as described in Getting Started with Docker Compose. However, if you use that example you will need to change to the official Python docker image and not the 'alpine' image, which contains a stripped down build of Python that cannot load Wing's debugger core. This is easy to do, by changing from FROM python:3.7-alpine to FROM:python:3.8 in the Dockerfile. You will also need to remove the RUN apk add line from the Dockerfile. This is not needed with the official Python docker image.

Configuration

To set up your Docker Compose project so it can be used with Wing's Python debugger, you will need to add some volume mounts to each container that you want to debug. These mount Wing's debugger support and cause Python to initiate debug whenever it is run on the container.

1. Prepare sitecustomize

The first step is to make a new directory sitecustomize in the same directory as your docker-compose.yml file and then add a file named __init__.py to the directory with the following contents:

from . import wingdbstub

This is the hook that will cause Python on the containers to load Wing's debugger. It is loaded by Python's Site-specific configuration hook.

2. Configure wingdbstub.py

Next, you need to configure a copy of wingdbstub.py to place into this sitecustomize directory. This module is provided by Wing as the way to start debug of any Python code that is launched from outside of the IDE, as is the case here since your code is launched in the container by docker-compose up.

You can find the master copy of wingdbstub.py at the top level of your Wing installation (or on macOS in Contents/Resources inside WingPro.app). If you don't know where this is, it is listed as the Install Directory in Wing's About box.

You will need to make copy of this file to your sitecustomize package directory and then make two changes to it:

  • Set WINGHOME='/wingpro7'
  • Set kHostPort='host.docker.internal:50005'

3. Inspect Your Installation

In order to figure out what volume mounts you need to add to your docker-compose.yml file, you first need to determine:

(1) The full path of your Wing installation on the host system, which is given in Wing's About box. This is the same place that you found wingdbstub.py earlier.

(2) The location of the site customization site-packages on each container that you want to debug. This is where you will mount the sitecustomize directory from your host system. You can determine this value by starting Python on the container and inspecting it. For example, for a service in docker-compose.yml that is called web, you can start Python on the container interactively like this:

docker run -i compose_web python -i -u

Note that the Docker image name is the same as the Docker Compose service name but with compose_ prepended.

Then type or paste in the following lines of code:

>>> import os, sys, site
>>> v = sys.version_info[:2]
>>> print(os.path.join(site.USER_BASE, 'lib', 'python{}.{}'.format(*v), 'site-packages'))

Make a note of the path that this prints; you will need it in the next step below.

4. Add Mounted Volumes

Now you can now add your volume mounts in the docker-compose.yml file. You will be mounting the Wing installation directory at /wingpro7 (this must match the WINGHOME set earlier in your copy of wingdbstub.py) and your sitecustomize package directory inside the above-determined site-packages.

For example on Windows you might add the following in docker-compose.yml for each service that you want to debug:

volumes:
  - "C:\Program Files (x86)\Wing Pro 7.2:/wingpro7"
  - "./sitecustomize:/root/.local/lib/python3.8/site-packages/sitecustomize"

On macOS this might instead be:

volumes:
  - /Applications/WingPro.app/Contents/Resources:/wingpro7
  - ./sitecustomize:/root/.local/lib/python3.8/site-packages/sitecustomize

And on Linux it might be:

volumes:
  - /usr/lib/wingpro7:/wingpro7
  - ./sitecustomize:/root/.local/lib/python3.8/site-packages/sitecustomize

Example

Here's an example of these added volumes in context, within the docker-compose.yml that is used in Getting Started with Docker Compose:

version: "3.8"
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
      - ./sitecustomize:/root/.local/lib/python3.8/site-packages/sitecustomize
      - /Applications/WingPro.app/Contents/Resources:/wingpro7
    environment:
      FLASK_ENV: development
  redis:
    image: "redis:alpine"

Note that we're only debugging the web service and not Python code running on the redis service.

Starting Debug

Now you can start your cluster and debug your containerized Python code in Wing Pro.

To do that, first make sure Wing is listening for outside debug connections, by clicking on the bug icon bugicon in the lower left of Wing's window and enabling Accept Debug Connections.

If you are using the Flask example from Getting Started with Docker Compose (or any code that spawns multiple processes that you wish to debug) then you will also need to open Project Properties from the Project menu and set Debug Child Processes under the Debug/Execute tab to Always Debug Child Processes.

Then start your cluster with docker-compose up. Your application will start and the containers you've configured for debug should attempt to connect to Wing Pro. Wing will initially reject the connection and display a dialog for each container you are trying to debug:

/images/blog/docker-compose/connection-rejection.png

Click Accept and then stop docker-compose up by pressing Ctrl-C and restart it. The second time you start your cluster, the containers should manage to connect successfully to Wing's debugger, because you've accepted the randomly generated security token used by each container.

You can now set breakpoints, step through code, and view and interact with data in the debug process using Stack Data, Debug Console, and other tools in Wing. For more information on Wing's capabilities, see the Tutorial in Wing's Help menu or take a look at the Quick Start Guide.

Trouble-Shooting

If you can't get the debugger to connect, try setting kLogFile in your copy of wingdbstub.py to "<stderr>". This will log debugger diagnostics to the output from docker-compose up and will indicate whether the debugger is failing to load or failing to connect to the IDE. You can email this output to support@wingware.com for help.

To inspect other problems, including whether your added file mounts are working correctly, you can start a shell in selected Docker containers after docker-compose up with docker-compose <service> <cmd>. For example to start an interactive shell for the service web defined in docker-compose.yml:

docker-compose web bash

Future Directions

Part of our focus in Wing Pro 8 is to extend and improve Wing's support for containerized development. This includes automating container and cluster configuration. As of the date of this article, a subset of that functionality, for working with a single container, is available in our early access program. Future releases will extend this to support Docker Compose and possibly also other container orchestration systems. If you have requests for specific types of support for containerized development, please email us.



That's it for now! We'll be back soon with more Wing Tips for Wing Python IDE.

As always, please don't hesitate to email support@wingware.com if you run into problems, have any questions, or have topic suggestions for future Wing Tips!



from Planet Python
via read more

No comments:

Post a Comment

TestDriven.io: Working with Static and Media Files in Django

This article looks at how to work with static and media files in a Django project, locally and in production. from Planet Python via read...