Visual Studio에서 Linux의 Python 코드를 원격으로 디버그하기

이 문서에서는 원격 Linux 컴퓨터에서 Python 코드 디버깅을 지원하는 데 필요한 Visual Studio 설치를 구성하는 방법을 알아보세요. 이 연습은 Visual Studio 2019 버전 16.6을 기반으로 합니다.

Visual Studio는 Windows 컴퓨터에서 Python 애플리케이션을 로컬 및 원격으로 시작하고 디버그할 수 있습니다. 또한 debugpy 라이브러리를 사용하여 CPython 이외의 다른 오퍼레이션 체제, 디바이스 또는 Python 구현에서 원격 디버깅을 지원합니다.

Visual Studio 2019 버전 16.4 및 이전 버전에서는 ptvsd 라이브러리를 사용합니다. Visual Studio 2019 버전 16.5 및 이후 버전에서는 debugpy 라이브러리가 ptvsd를 대체합니다. debugpy를 사용하는 경우 디버그되는 Python 코드는 Visual Studio에서 연결할 수 있는 디버그 서버를 호스팅합니다. 이 호스팅을 수행하려면 서버를 가져오고 활성화하는 약간의 수정을 코드에 더해야 합니다. TCP 연결을 허용하도록 원격 컴퓨터에 네트워크 또는 방화벽 구성을 조정해야 할 수도 있습니다.

필수 조건

  • Python 워크로드에 대한 지원과 함께 설치된 Visual Studio. 자세한 내용은 Visual Studio에서 Python 지원 설치를 참조하세요.

  • Mac OSX 또는 Linux와 같은 운영 체제에서 Python을 실행하는 원격 컴퓨터.

  • 원격 디버깅의 기본값으로, 해당 컴퓨터의 방화벽에서 열려 있는 포트 5678(인바운드).

Linux 컴퓨터 설정

쉽게 Azure에서 Linux 가상 머신을 만들고 Windows에서 원격 데스크톱을 사용하여 Linux 가상 머신에 액세스할 수 있습니다. Python은 기본적으로 설치되므로 가상 머신에 대한 Ubuntu는 편리합니다. 다른 구성이 있는 경우, 다른 Python 다운로드 위치에 대한 Python 인터프리터 설치를 참조하세요.

방화벽 구성

원격 디버깅을 지원하려면 원격 컴퓨터의 방화벽에서 인바운드 포트 5678을 열어야 합니다.

Azure 가상 머신에 대한 방화벽 규칙을 만드는 방법에 대한 자세한 내용은 다음 문서를 참조하세요.

디버그할 스크립트 준비

다음 단계에 따라 Linux에서 Python 코드를 디버깅하기 위한 스크립트를 준비합니다.

  1. 원격 컴퓨터에서 guessing-game.py 라는 Python 파일을 다음 코드와 함께 만듭니다.

    import random
    
    guesses_made = 0
    name = input('Hello! What is your name?\n')
    number = random.randint(1, 20)
    print('Well, {0}, I am thinking of a number between 1 and 20.'.format(name))
    
    while guesses_made < 6:
        guess = int(input('Take a guess: '))
        guesses_made += 1
        if guess < number:
            print('Your guess is too low.')
        if guess > number:
            print('Your guess is too high.')
        if guess == number:
            break
    if guess == number:
        print('Good job, {0}! You guessed my number in {1} guesses!'.format(name, guesses_made))
    else:
        print('Nope. The number I was thinking of was {0}'.format(number))
    
  2. 환경에 debugpy 패키지를 설치하려면 pip3 install debugpy 명령을 사용하세요.

    참고 항목

    문제 해결에 필요한 경우를 대비하여 설치된 debugpy 버전을 기록하는 것이 좋습니다. 또한 debugpy 목록에 사용 가능한 버전도 표시됩니다.

  3. 다른 코드보다 앞서 guessing-game.py 파일의 맨 위에 다음 코드를 추가하여 원격 디버깅을 활성화합니다. (엄격한 요구 사항은 아니지만, listen 함수를 호출하기 전에 생성된 모든 백그라운드 스레드를 디버그할 수는 없습니다.)

    import debugpy
    debugpy.listen(('0.0.0.0', 5678))
    
  4. 파일을 저장하고 프로그램을 실행합니다.

    python3 guessing-game.py
    

    프로그램과 상호 작용하기 때문에, 백그라운드에서 listen 함수에 대한 호출이 실행되고, 들어오는 연결을 기다립니다. 원하는 경우, wait_for_client 한 후 함수를 호출하여 listen 함수가 프로그램을 디버거가 연결될 때까지 차단할 수 있습니다.

게다가 listenwait_for_client 함수와 함께, debugpy는 헬퍼 함수를 breakpoint제공합니다. 이 함수는 디버거가 연결된 경우 프로그래밍 방식의 중단점 역할을 합니다. 또 다른 함수, is_client_connected1는, 디버거가 연결된 경우 True 를 반환합니다. 다른 debugpy 함수를 호출하기 전에 이 결과를 검사할 필요가 없습니다.

Python 도구에서 원격으로 연결

다음 단계에서는 중단점을 설정하여 원격 프로세스를 중지하는 방법을 보여줍니다.

  1. 로컬 컴퓨터에 원격 파일의 복사본을 만들고 Visual Studio에서 엽니다. 파일의 위치는 중요하지 않지만, 이름은 원격 컴퓨터의 스크립트 이름과 일치해야 합니다.

  2. (선택 사항) 로컬 컴퓨터에서 debugpy에 IntelliSense를 사용하려면 Python 환경에 debugpy 패키지를 설치합니다.

  3. 디버그>프로세스에 연결을 선택합니다.

  4. 프로세스에 연결 대화상자에서, 연결 형식Python 원격 (debugpy)으로 설정합니다.

  5. 연결 대상 필드에서, 명령을 tcp://<ip_address>:5678입력합니다.

    • tcp:// 는 연결 형식을 TCP(Transmission Control Protocol)로 지정합니다.
    • <ip_address> 은 원격 컴퓨터의 IP 주소로, 명시적 주소 또는 myvm.cloudapp.net같은 이름일 수 있습니다.
    • :5678 는 원격 디버깅 포트 번호입니다.
  6. 해당 컴퓨터에서 사용할 수 있는 debugpy 프로세스 목록을 채우려면 입력 키를 선택하세요.

    Screenshot that shows how to enter the connection target to see a list of available debugpy processes.

    이 목록을 채운 후 원격 컴퓨터에서 다른 프로그램을 시작하게 되는 경우 새로 고침 단추를 선택합니다.

  7. 디버그할 프로세스를 선택한 다음 연결을 선택하거나 프로세스를 더블클릭합니다.

  8. 스크립트가 원격 컴퓨터에서 계속 실행되는 동안 Visual Studio에서 디버깅 모드로 전환하여 모든 일반적인 디버깅 기능을 제공합니다.

    중단점을 if guess < number: 줄에 설정한 다음, 원격 컴퓨터로 전환하고 다른 guess를 입력합니다. 로컬 컴퓨터의 Visual Studio가 해당 중단점에서 중지하고 로컬 변수 등을 보여 줍니다.

    Screenshot that shows how Visual Studio pauses debugging when a breakpoint is hit.

  9. 디버깅을 중지하면 Visual Studio가 프로그램에서 분리됩니다. 프로그램은 원격 컴퓨터에서 계속 실행됩니다. 또한 debugpy는 계속 디버거 연결을 수신 대기하므로 언제든지 프로세스에 다시 연결할 수 있습니다.

연결 문제 해결하기

연결 관련 문제를 해결하려면 다음 사항을 검토하세요.

  • Python 원격(debugpy)연결 형식에 선택했는지 확인합니다.

  • 원격 코드의 시크릿과 연결 대상 의 시크릿이 정확히 일치하는지 확인합니다.

  • 원격 컴퓨터의 IP 주소가 연결 대상 의 IP 주소와 일치하는지 확인합니다.

  • 원격 컴퓨터의 원격 디버깅 포트가 열려 있고 연결 대상에 :5678같은 포트 접미사가 연결 대상에 포함되어 있는지 확인합니다.

    다른 포트를 사용하려면, listen 함수 호출에서 debugpy.listen((host, port))와 같이 포트 번호를 지정합니다. 이 경우 방화벽에서 해당 특정 포트를 열어야 합니다.

  • 원격 컴퓨터에 설치된 디버그피 버전( pip3 list 명령에서 반환됨)이 PTVS(Visual Studio Python Tools) 버전과 일치하는지 확인합니다.

    다음 표에는 유효한 버전 쌍이 나와 있습니다. 필요에 따라 원격 컴퓨터에서 debugpy 버전을 업데이트합니다.

    Visual Studio Python 도구 debugpy
    2019 16.6 1.0.0b5 1.0.0b5
    2019 16.5 1.0.0b1 1.0.0b1

참고 항목

Visual Studio 2019 버전 16.0~16.4는 debugpy가 아닌 ptvsd를 사용했습니다. 이 연습에서 이러한 버전에 대한 프로세스는 유사하지만 함수 이름은 다릅니다. Visual Studio 2019 버전 16.5는 debugpy를 사용하지만 함수 이름은 ptvsd에서와 동일합니다. listen 대신 enable_attach를 사용합니다. wait_for_client 대신 wait_for_attach를 사용합니다. breakpoint 대신 break_into_debugger를 사용합니다.

레거시 디버깅에 ptvsd 3.x 사용하기

ptvsd 3.x 레거시 디버거는 Visual Studio 2017 버전 15.7 및 이전 버전의 기본값입니다.

Visual Studio 구성에 따라, 원격 디버깅에 ptvsd 3.x를 사용해야 할 수 있습니다.

  • Python 2.6, 3.1에서 3.4까지 또는 IronPython을 사용한 Visual Studio 2017 버전 15.7 및 이전 버전
  • Python 2.6, 3.1에서 3.4까지 또는 IronPython을 사용한 Visual Studio 2019 버전 16.5 및 이후 버전
  • 이전 4.x 버전들

구성에서 이전 버전 시나리오를 구현하는 경우 Visual Studio에 오류가 표시됩니다. 디버거는 이 Python 환경을 지원하지 않습니다.

원격 디버깅 설정

ptvsd 3.x를 사용하여 원격 디버깅을 준비하려면 다음 단계를 완료합니다.

  1. 실행 중인 스크립트에 대한 액세스를 제한하는 데 사용되는 비밀을 설정합니다.

    Ptvsd 3.x에서 enable_attach 함수를 사용하려면 첫 번째 인수로 "시크릿"을 전달해야 합니다.

    • 원격 디버거를 결합할 때, enable_attach(secret="<secret>") 명령을 사용하여 시크릿을 입력합니다.

    누구나 enable_attach(secret=None) 명령을 사용하여 연결할 수 있게 할 수 있지만, 이 옵션은 권장되지 않습니다.

  2. 연결 대상 URL을 tcp://<secret>@<ip_address>:5678형식으로 만듭니다.

    • tcp:// 연결 형식을 TCP로 지정합니다.
    • <secret> 는 Python 코드의 enable_attach 함수와 함께 전달된 문자열입니다.
    • <ip_address> 은 원격 컴퓨터의 IP 주소로, 명시적 주소 또는 myvm.cloudapp.net같은 이름일 수 있습니다.
    • :5678 는 원격 디버깅 포트 번호입니다.

TCPS 프로토콜을 사용하여 보안 연결

기본적으로 ptvsd 3.x 원격 디버그 서버에 대한 연결은 시크릿로만 보호되고 모든 데이터는 일반 텍스트로 전달됩니다. 보다 안전한 연결을 위해 ptvsd 3.x는 TCP 프로토콜 또는 TCPS의 보안 형식을 사용하여 SSL을 지원합니다.

TCPS 프로토콜을 사용하도록 ptvsd 3.x를 구성하려면 다음 단계를 사용합니다.

  1. 원격 컴퓨터에서, openssl 명령을 사용하여 키와 자체 서명된 인증서에 대해 별도의 파일을 생성합니다.

    openssl req -new -x509 -days 365 -nodes -out cert.cer -keyout cert.key
    
    • openssl 프롬프트에서, 명령 이름에 연결하는 데 사용하는 호스트 이름 또는 IP 주소를 입력합니다.

    자세한 내용은, Python 모듈 설명서의 자체 서명된 인증서ssl 참조하세요. Python 설명서에 설명된 명령은 결합된 단일 파일만 생성합니다.

  2. 코드에서, enable_attach 함수 호출을 수정하여 certfilekeyfile 인수를 포함할 때 파일 이름을 값으로 사용합니다. 이러한 인수는 표준 ssl.wrap_socket Python 함수와 동일한 의미를 갖습니다.

    ptvsd.enable_attach(secret='my_secret', certfile='cert.cer', keyfile='cert.key')
    

    로컬 컴퓨터의 코드 파일에서도 동일한 변경 작업을 수행할 수 있습니다. 이 코드는 실제로 실행되지 않으므로 반드시 필요한 것은 아닙니다.

  3. 원격 컴퓨터에서 Python 프로그램을 다시 시작하여 디버깅을 준비합니다.

  4. Visual Studio가 설치된 Windows 컴퓨터에서 Trusted Root CA에 인증서를 추가하여 채널을 보호합니다.

    1. 로원격 컴퓨터의 인증서 파일을 로컬 컴퓨터에 복사합니다.

    2. 먼저 패널 제어 를 열고 Windows 도구의>컴퓨터 인증서 관리로 갑니다.

    3. certlm [인증서 - 로컬 컴퓨터] 대화상자에서, Trusted Root Certification Authorities 노드를 확장하고, 인증서를 우클릭하고 모든 작업>가져오기를 선택합니다.

    4. 원격 컴퓨터에서 복사한 .cer 파일을 찾아 선택합니다.

    5. 대화 상자 프롬프트를 계속 진행하여 가져오기 프로세스를 완료합니다.

  5. Visual Studio의 연결 프로세스는 Python 도구에서 원격으로 연결에서 설명한 대로 반복합니다.

    이 인스턴스의 경우, tcps:// 프로토콜로서 연결 대상 (또는 Qualifier)에 대해 정의합니다.

    Screenshot that shows how to specify TCPS as the remote debugging transport with SSL.

연결 문제 해결하기

연결 시도 중에 Visual Studio에 문제가 발생할 수 있습니다. 다음 시나리오를 검토하고 필요에 따라 적절한 조치를 취합니다.

  • SSL을 통해 연결하면 Visual Studio에서 잠재적인 인증서 문제에 경고를 표시합니다.

    작업: 메시지를 무시하고 계속 진행할 수 있습니다.

    주의

    채널은 여전히 도청에 대해 암호화되어 있지만 메시지 가로채기(man-in-the-middle) 공격에 노출될 수 있습니다.

  • Visual Studio는 원격 인증서가 신뢰할 수 없는 경고를 표시합니다 .

    문제: 인증서가 Trusted Root CA에 제대로 추가되지 않았습니다.

    작업: Windows 컴퓨터의 Trusted Root CA에 인증서를 추가하는 단계를 다시 검사하고 연결을 재시도합니다.

    Screenshot of the warning that says the remote SSL certificate isn't trusted.

  • Visual Studio는 원격 인증서 이름이 호스트 이름와 일치하지 않음 경고를 표시합니다.

    문제: 인증서의 공통 이름 에 대한 적절한 호스트 이름 또는 IP 주소가 지정되지 않았습니다.

    작업: TCPS와 연결 보호단계를 재검사합니다. 인증서를 만들 때 올바른 공통 이름 을 사용하고 연결을 다시 시도해야 합니다.

    Screenshot of the warning that says the remote SSL certificate doesn't match the hostname.