PyCharm - (반대로) 원격 프로세스가 PyCharm에 디버그 연결하는 방법
일반적으로 비주얼 스튜디오에 익숙한 사용자라면 원격 디버깅이라고 할 때 이전에 설명했던,
PyCharm - 원격 프로세스를 디버그하는 방법
; https://www.sysnet.pe.kr/2/0/12798
방식의 디버깅만을 연상하게 됩니다. 그런데, 특이하게도 PyCharm의 경우 프로세스 측에서 PyCharm IDE로 디버그 연결을 하는 방법을 함께 제공합니다.
이런 문제는, 아마도 지난 글에 소개한,
파이썬 - WSGI를 만족하는 최소한의 구현 코드 및 PyCharm에서의 디버깅 방법
; https://www.sysnet.pe.kr/2/0/12794
simple_app과 같은 앱을 uwsgi/gunicorn 등에서 호스팅할 때 디버깅을 원한다면 사용할 수 있는 대안으로써 제공되는 듯합니다. 이와 관련된 공식 문서도 있지만,
debugging a uwsgi python application using pycharm
; https://stackoverflow.com/questions/21257568/debugging-a-uwsgi-python-application-using-pycharm
Remote debugging with the Python remote debug server configuration
; https://www.jetbrains.com/help/pycharm/remote-debugging-with-product.html#remote-debug-config
역시 문서보다 ^^ 직접 상황에 따른 예를 드는 것이 더 이해가 빠르겠지요. 이번에도 "
PyCharm - 원격 프로세스를 디버그하는 방법"에서처럼 원격 PC가 있어야겠지만 어차피 설정은 모두 동일하므로 WSL 환경에서 테스트를 하겠습니다.
즉, 원격 PC(또는 WSL)에서 실행한 uwsgi/gunicorn 프로세스에서 호스팅되는 Python App을 디버깅하는 것을 가정하고 설명하겠습니다. ^^
우선, PyCharm IDE에서 새로운 "Run/Debug Configuration"을 "Python Debug Server" 유형으로,
포트와 IDE host name을 입력해 생성합니다. (이 글에서는 WSL 환경이므로 localhost와 임의의 포트를 지정했습니다. 일반적인 상황이라면, "0.0.0.0"으로 AnyIP로 설정하는 것이 속 편합니다.)
그런 다음, 화면에 나오는 텍스트에서 지시하는 것처럼, 원격 PC(또는 WSL) 측에서 "pydevd-pycharm~=212.4746.96" 구성 요소를 설치하고,
$ pip install pydevd-pycharm~=212.4746.96
원격 PC에서 실행할 스크립트에는 적절한 디버그 시점을 선택해 아래의 스크립트를 추가합니다. (즉, 아래의 경우는 시작하자마자 PyCharm IDE의 디버그 서버로 접속을 시도합니다.)
# main.py
import pydevd_pycharm
pydevd_pycharm.settrace('localhost', port=14444, stdoutToServer=True, stderrToServer=True)
HELLO_WORLD = b"Hello world!\n"
def simple_app(environ, start_response):
"""Simplest possible application object"""
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return [HELLO_WORLD]
def get_wsgi_application():
return simple_app
application = get_wsgi_application()
자, 이걸로 준비 작업은 끝입니다. 이제 PyCharm은 해당 Configuration의 설정으로 디버그를 시작하면 콘솔 화면에 "Waiting for process connection..."이라고 뜨며 연결을 대기합니다.
이후, WSL 측에서 파이썬 스크립트를 실행하면,
$ python main.py
Could not connect to localhost: 14444
Traceback (most recent call last):
File "/home/testusr/.local/lib/python3.8/site-packages/_pydevd_bundle/pydevd_comm.py", line 507, in start_client
s.connect((host, port))
ConnectionRefusedError: [Errno 111] Connection refused
Traceback (most recent call last):
File "main.py", line 7, in <module>
pydevd.settrace('localhost', port=14444, stdoutToServer=True, stderrToServer=True)
File "/home/testusr/.local/lib/python3.8/site-packages/pydevd.py", line 2696, in settrace
_locked_settrace(
File "/home/testusr/.local/lib/python3.8/site-packages/pydevd.py", line 2777, in _locked_settrace
py_db.connect(host, port) # Note: connect can raise error.
File "/home/testusr/.local/lib/python3.8/site-packages/pydevd.py", line 1353, in connect
s = start_client(host, port)
File "/home/testusr/.local/lib/python3.8/site-packages/_pydevd_bundle/pydevd_comm.py", line 507, in start_client
s.connect((host, port))
ConnectionRefusedError: [Errno 111] Connection refused
(원격 호스트라면 오류 없이 잘 실행이 되었겠지만) 아쉽게도 WSL 환경이라 "Could not connect to localhost - ConnectionRefusedError: [Errno 111] Connection refused" 오류가 발생하는군요. ^^
이에 대한 원인은 WSL 2에서의 네트워크의 특징 때문입니다.
WSL 2의 네트워크 통신 방법
; https://www.sysnet.pe.kr/2/0/12347
즉, WSL 인스턴스 측에서 윈도우 호스트 측으로의 "localhost" 접속이 불가능하므로, PyCharm 측의 "IDE host name" 바인딩을 "localhost"가 아닌 "/etc/resolv.conf"에서 보이는 IP로,
$ cat /etc/resolv.conf
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 172.23.208.1
대체해야 합니다. (혹은 그냥 위에서도 언급했지만 "0.0.0.0"으로 설정합니다.) 물론, 스크립트에서도 변경해주고,
pydevd_pycharm.settrace('172.23.208.1', port=14444, stdoutToServer=True, stderrToServer=True)
다시 실행하면 이번에는 정상적으로 디버깅 연결이 됩니다.
그런데, 디버깅 연결까지는 되었지만 아마도 화면에는 여전히 다음과 같은 식의 오류가 발생할 수 있습니다.
/mnt/d/workshop2/python-wsgi-app/main.py can't be found in project. You can continue debugging, but without the source. To fix that you can do one of the following:
소스 코드를 찾을 수 없다는 것인데, 어쩔 수 없습니다. "Run/Debug Configuration"에서 다음과 같이 매핑을 추가해야 합니다.
이후 다시 디버깅 연결을 하면 콘솔에는 다음의 메시지와 함께,
Starting debug server at port 14,444
Use the following code to connect to the debugger:
import pydevd_pycharm
pydevd_pycharm.settrace('172.23.208.1', port=14444, stdoutToServer=True, stderrToServer=True)
Waiting for process connection…
Connected to pydev debugger (build 212.4746.96)
정상적으로 main.py가 열리고 pydevd.settrace 호출 다음 라인에서 실행이 멈춰 있는 것을 확인할 수 있습니다.
참고로 PyCharm IDE는 디버깅 서버를 한 번 열어 두면 계속 접속을 받아들이도록 동작합니다. 따라서, 클라이언트 측에서 프로세스를 종료해 연결이 끊겨도 PyCharm 측의 디버그 서버를 다시 실행할 필요 없이 그냥 클라이언트 측 프로세스만 다시 시작해 pydevd.settrace 호출을 하면 PyCharm 디버그 서버에 연결이 됩니다.
이로 인해 다음의 소스 코드를,
import pydevd
pydevd.settrace('172.23.208.1', port=14444, stdoutToServer=True, stderrToServer=True)
HELLO_WORLD = b"Hello world!\n"
def simple_app(environ, start_response):
"""Simplest possible application object"""
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return [HELLO_WORLD]
def get_wsgi_application():
return simple_app
application = get_wsgi_application()
이전에는 gunicorn으로 실행하는 경우,
$ gunicorn -b 0.0.0.0:18090 main
PyCharm에서 (
F5 실행) 디버깅을 할 수 없었지만 이제는 pydevd.settrace의 호출로 인해 디버깅이 가능해졌습니다.
(그건 그렇고, 이건 Visual Studio에도 도입을 했으면... 하는 희망이 생기는군요. ^^)
참고로, 아래와 같이 코딩해도 되지만,
import pydevd_pycharm
pydevd_pycharm.settrace('172.23.208.1', port=14444, stdoutToServer=True, stderrToServer=True)
이렇게 해도 정상적으로 동작을 했습니다.
import pydevd
pydevd.settrace('172.23.208.1', port=14444, stdoutToServer=True, stderrToServer=True)
이에 대한 이력은 잘 모르겠지만 pydevd는 예전 모듈인 것 같고, 하위 호한으로 인해 남겨둔 듯합니다. (혹시 이에 대한 차이를 아시는 분은 덧글 부탁드립니다. ^^)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]