Cross-network remote debugging for Python
Project description
Azure Debug Relay for Python
AzDebugRelay - a Python module for cross-network remote debugging in Visual Studio Code.
AzDebugRelay uses debugpy and Azure Relay service to create a debugging tunnel between 2 machines:
- You local Visual Studio Code debugger in
listen
mode. - You remote code in
attach
mode.
Both machines can be isolated behind NAT or virtual networks - all they need is to be able to connect to Azure Relay resource. Azure Relay carries a secure tunnel, just as if these machines were in the same VPN.
The debugging tunnel is handled by Azure Relay Bridge utility which is downloaded and installed automatically by AzDebugRelay. Azure Relay Bridge can maintain secure TCP and UDP tunnels for different purposes. AzDebugRelay is a collection of helpers for VS Code and Python that makes easier to use Azure Relay Bridge for debugging remote code.
We currently use a private fork of Azure Relay Bridge repo.
Requirements
- Python 3.6+
- debugpy
Azure Relay Bridge tool is a .NET Core application, so you may need to install apt-transport-https
and other .NET Core 3.1 Runtime prerequisites on Linux and Windows.
You don't have to install .NET Runtime itself - Azure Relay Bridge builds are self-contained.
Supported Operating Systems
- Ubuntu 18+
- Debian 10+
- macOS 10+
- Windows 10
Usage
Before you start debugging with AzDebugRelay, there are 3 places you configure it:
- Azure Portal.
- Local machine where you run Visual Studio Code and its Python debugger.
- Remote machine where you run the same code files that open locally in VS Code.
In Azure Portal
- Create Azure Relay resource. Better make one in a region closest to your location.
- Once created, switch to the resource, and select
Hybrid Connections
option in the vertical panel. - Add a hybrid connection (
+ Hybrid Connection
button), give it a memorable name (e.g.test
🙂) - this is your Relay Name. - Switch to that new hybrid connection, then select
Shared Access Policies
in the vertical panel. - Add a new policy with
Send
andListen
permissions. - Once created, copy its
Primary Connection String
, this is your Connection String.
Azure CLI version
Choose your name instead of mydebugrelay1
for an Azure Relay resource, and your custom name for Hybrid Connection instead of debugrelayhc1
.
az group create --name debugRelayResourceGroup --location westus2
az relay namespace create --resource-group debugRelayResourceGroup --name mydebugrelay1 --location westus2
az relay hyco create --resource-group debugRelayResourceGroup --namespace-name mydebugrelay1 --name debugrelayhc1
az relay hyco authorization-rule create --resource-group debugRelayResourceGroup --namespace-name mydebugrelay1 --hybrid-connection-name debugrelayhc1 --name sendlisten --rights Send Listen
az relay hyco authorization-rule keys list --resource-group debugRelayResourceGroup --namespace-name mydebugrelay1 --hybrid-connection-name debugrelayhc1 --name sendlisten
Last command will show you something like this:
{
"keyName": "sendlisten",
"primaryConnectionString": "Endpoint=sb://mydebugrelay1.servicebus.windows.net/;SharedAccessKeyName=sendlisten;SharedAccessKey=REDACTED1;EntityPath=debugrelayhc1",
"primaryKey": "REDACTED1",
"secondaryConnectionString": "Endpoint=sb://mydebugrelay1.servicebus.windows.net/;SharedAccessKeyName=sendlisten;SharedAccessKey=REDACTED2;EntityPath=debugrelayhc1",
"secondaryKey": "REDACTED2"
}
Use primaryConnectionString
or secondaryConnectionString
value as your Connection String.
Relay Name would be the one you choose instead of debugrelayhc1
.
You cannot share the same hybrid connection between multiple active debug sessions unless running between same 2 machines via different ports.
Locally and Remotely
Create .azrelay.json
file in your workspace directory or whatever directory will be "current" (next to remote_server_demo.py
files),
and set 2 variables:
AZRELAY_CONNECTION_STRING
to your Connection String.AZRELAY_NAME
to your Relay Name.
For example:
{
"AZRELAY_CONNECTION_STRING": "Endpoint=sb://vladkol-relay.servicebus.windows.net/;SharedAccessKeyName=default;SharedAccessKey=REDACTED;EntityPath=test",
"AZRELAY_NAME": "test"
}
.azrelay.json
is added in .gitignore
, and won't be committed.
Alternatively, you can assign these 2 variables as environment variables.
Locally in Visual Studio Code
This step must be done before launching the remote code.
- Open
remote_server_demo.py
and put a breakpoint indo_work()
function. - Start debugging in your local Visual Studio Code in
Python: Listen
configuration.
If you are doing this on tops of your own code:
- Configure
.vscode/tasks.json
with tasks as in this repo's.vscode/tasks.json
. These tasks take care of launching and stopping Azure Relay Bridge when needed. - Configure
.vscode/launch.json
withPython: Listen
configuration as in this repo's.vscode/launch.json
.
Notice how the debugger maps paths on the local and the remote machines.
If your code has a different structure remotely, you may need to provide more sophisticated path mappings. Here is that piece in .vscode/launch.json
:
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "."
}
]
It tells VS Code that the workspace directory locally is mapped to the "current" directory remotely.
When the debugger looks goes through a file remotely, it needs to find the corresponding file in your local VS Code workspace.
When debugging remote_server_demo.py
, the debugger maps ./remote_server_demo.py
remotely to ${workspaceFolder}/remote_server_demo.py
locally.
Remote Machine
- Clone the repo.
- Start
python3 remote_server_demo.py --debug=attach
.
Terminal session you start #2 in must have the repo's directory as current directory - for a reason of mapping local and remote directories.
If everything works as it's supposed to, you will hit a breakpoint in your local Visual Studio Code.
AzDebugRelay API
remote_server_demo.py
shows how you can use AzDebugRelay with your code.
azdebugrelay package contains DebugRelay class that install and launches Azure Relay Bridge:
from azdebugrelay import DebugRelay, DebugMode
access_key_or_connection_string = "AZURE RELAY HYBRID CONNECTION STRING OR ACCESS KEY"
relay_name = "RELAY NAME" # your Hybrid Connection name
debug_mode = DebugMode.Connect # or DebugMode.WaitForConnection if connecting from another end
hybrid_connection_url = "HYBRID CONNECTION URL" # can be None if access_key_or_connection_string is a connection string
host = "127.0.0.1" # local hostname or ip address the debugger starts on
port = 5678 # any available port that you can use within your machine
debug_relay = DebugRelay(access_key_or_connection_string, relay_name, debug_mode, hybrid_connection_url, host, port)
debug_relay.open()
# attach to a remote debugger (usually from remote server code) with debug_mode = DebugMode.Connect
debugpy.connect((host, port))
# Debug, debug, debug
# ...
# ...
debug_relay.close()
access_key_or_connection_string
- SAS Policy key or Connection String for Azure Relay Hybrid Connection. Must haveSend
andListen
permissionsrelay_name
- name of the Hybrid Connectiondebug_mode
- debug connection mode.DebugMode.WaitForConnection
when starting in listening mode,DebugMode.Connect
for attaching to a remote debugger.hybrid_connection_url
- Hybrid Connection URL. Required when access_key_or_connection_string as an access key, otherwise is ignored and may be None.host
- Local hostname or ip address the debugger starts on,127.0.0.1
by defaultport
- debugging port,5678
by default
Troubleshooting
Why using Azure Relay Bridge which is a .NET Core application that we have to install and use via subprocess
calls?
Reasons:
- Azure Relay has SDKs for .NET, Java, and Node. No Python SDK or examples.
- Azure Relay Bridge does a lot of things we have to implement otherwise. It is a great tool that can help you connecting different networks for many purposes: for RDP, SSH and other protocols over TCP or UDP.
A private fork we are currently using is only to provide .NET Core 3.1 builds of the most recent code. There is a pending pul-requests: one and two.
Known issues
When VS Code starts debugging in
listen
mode, Azure Relay Bridge doesn't close if the debugging session was stopped without another side connected and attached (azbridge
keeps running and connected).
Reason: VS Code doesn't launch necessary postDebugTask
if no debugging was actually started/attached.
Just starting in listen
mode doesn't count.
Workaround: Currently only one - killing azbridge
process manually. Better solution is in progress.
On macOS, there may be a situation when Azure Relay Bridge (
azbridge
) cannot connect when creating a local forwarder (-L
option).
Reason: .NET Core wants you to add your Computer Name to /etc/hosts
file.
Workaround: Make necessary edits of /etc/hosts
file:
- Look for your computer's name in
Settings → Sharing
. - Open
/etc/hosts
in a text editor in sudo mode (VS Code can save it later in sudo mode). - Add the following line (replace
your-computer-name
with your computer's name). Save the file.
127.0.0.1 your-computer-name
I launched the debugger as described and nothing happened
Reason: you probably didn't put a breakpoint in your VS Code locally. Make sure that breakpoint is in a place that your server process actually runs through.
I do everything right, but thing works
Reason: Stop all debugging sessions (if any). Kill all azbridge
processes. Try again.
Doesn't help? File an issue! Thank you!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for azure_debug_relay-0.1.1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 4de3451b135e64f90e15bdd10c75b69d5b4cbf0c402d31b9f2d32fb44b402846 |
|
MD5 | 95393bfc8fd1c95a79a4c584f67f7c08 |
|
BLAKE2b-256 | 70f8d7632eeeb2d03803c794a69549e769bef1cc96594fa6ad4a3f0bc7031044 |