Python 3 런타임으로 마이그레이션

Python 2.7 지원은 2024년 1월 31일에 종료되었습니다. 기존 Python 2.7 애플리케이션을 계속 실행하고 트래픽을 받을 수 있습니다. 그러나 App Engine에서 지원 종료 날짜 이후에 런타임을 사용하는 애플리케이션의 재배포를 차단할 수 있습니다. 이 페이지의 가이드라인을 수행하여 지원되는 최신 Python 버전으로 마이그레이션하는 것이 좋습니다.

Python 3 런타임으로 마이그레이션하면 최신 언어 기능을 사용하고 관용적인 코드로 이동성이 향상된 앱을 빌드할 수 있습니다. Python 3 런타임은 Python Software Foundation에서 제공하는 최신 버전의 오픈소스 Python 인터프리터를 사용합니다. Python 3 런타임에 빌드된 앱은 Python의 패키지 및 프레임워크(requirements.txt 파일에서 종속 항목을 선언하여 C 코드를 사용하는 패키지 및 프레임워크 포함)로 이루어진 풍부한 생태계를 사용할 수 있습니다.

런타임 마이그레이션 프로세스 개요

런타임 마이그레이션에 다음과 같은 점진적 방법을 사용하는 것이 좋습니다. 이 방법은 프로세스 전반에 걸쳐 작동 및 테스트 가능한 애플리케이션을 유지보수합니다.

  1. Python 3과 호환되도록 앱을 업그레이드하세요.

    이 업그레이드에 유용한 몇 가지 솔루션이 있습니다. 예를 들어 Six, Python-Future 또는 Python-Modernize를 사용합니다.

    이 런타임 마이그레이션 프로세스 단계에 대한 자세한 내용은 Python Software Foundation 문서 사이트의 Porting Python 2 Code to Python 3(Python 2 코드를 Python 3로 포팅)을 참조하세요.

  2. 앱에서 사용하는 모든 App Engine 번들 서비스에 대해 다음 구현 전략 중 하나를 선택하세요.

    1. Python 2 앱의 기존 번들 서비스를 번들되지 않은 Google Cloud 서비스, 타사 서비스 또는 기타 권장 대체 서비스로 마이그레이션합니다.

    2. Python 3 앱에서 기존 번들 서비스를 사용합니다. 이 방식을 사용하면 나중에 마이그레이션 주기 시 번들되지 않은 서비스로 유연하게 전환할 수 있습니다.

    각 서비스를 마이그레이션한 후 앱을 테스트해야 합니다.

  3. Python 3 런타임용 App Engine 구성 파일을 준비합니다. 다음을 포함하여 여러 중요한 변경사항이 app.yaml의 구성 설정에 영향을 미칩니다.

    • 이제 앱은 스레드로부터 안전한 것으로 간주됩니다. 애플리케이션이 스레드 안전이 아닌 경우 app.yamlmax_concurrent_requests를 1로 설정해야 합니다. 이 설정으로 인해 스레드 안전 앱에 필요한 것보다 더 많은 인스턴스가 생성될 수 있으며 불필요한 비용이 발생할 수 있습니다.
    • app.yaml 파일이 더 이상 요청을 스크립트로 라우팅하지 않습니다. 대신 인앱 라우팅이 포함된 웹 프레임워크를 사용하고 app.yaml의 모든 script 핸들러를 업데이트하거나 삭제해야 합니다. Flask 프레임워크로 이 작업을 수행하는 방법의 예시는 GitHub의 App Engine 마이그레이션 가이드 코드 샘플을 참조하세요.

      이 구성 파일과 다른 구성 파일 변경에 대한 자세한 내용은 구성 파일 섹션을 참조하세요.

  4. 2세대 런타임에서 앱 로그는 더 이상 요청 로그 내에 중첩되지 않습니다. 로그 탐색기에서 요청 및 앱 로그의 중첩된 뷰를 표시하려면 추가 단계가 필요합니다. 자세한 내용은 Cloud Logging으로 마이그레이션을 참조하세요.

  5. Python 3 환경에서 업그레이드된 앱을 테스트 및 배포합니다.

    모든 테스트를 통과한 후 업그레이드된 앱을 App Engine에 배포하되 트래픽이 새 버전으로 자동 라우팅되지 않도록 합니다. 트래픽 분할을 사용하여 Python 2 런타임의 앱에서 Python 3 런타임의 앱으로 트래픽을 천천히 마이그레이션합니다. 문제가 발생하면 문제가 해결될 때까지 모든 트래픽을 정식 버전으로 라우팅하세요.

Python 2 앱을 Python 3로 변환하는 방법의 예시는 추가 리소스를 참조하세요.

Python 2 런타임과 Python 3 런타임의 주요 차이점

Python 2와 Python 3 런타임에는 다음과 같은 차이점이 있으며 런타임 마이그레이션 시 필요한 변경사항 대부분은 이 차이점에서 비롯됩니다.

메모리 사용량 차이

2세대 런타임은 1세대 런타임에 비해 메모리 사용량 기준이 더 높습니다. 이는 여러 다른 기본 이미지 버전, 두 세대 간의 메모리 사용량 계산 방법의 차이와 같은 여러 요인 때문에 발생합니다.

2세대 런타임은 애플리케이션 프로세스가 사용하는 항목의 합계 및 메모리에 동적으로 캐시된 애플리케이션 파일 수에 따라 인스턴스 메모리 사용량을 계산합니다. 메모리 사용량이 높은 애플리케이션에서 메모리 한도 초과로 인한 인스턴스 종료가 발생하지 않도록 방지하려면 메모리가 많은 더 큰 인스턴스 클래스로 업그레이드하세요.

CPU 사용량 차이

2세대 런타임은 인스턴스 콜드 스타트 시 CPU 사용량 기준이 더 높을 수 있습니다. 그 결과 애플리케이션의 확장 구성에 따라 애플리케이션이 CPU 사용률을 기준으로 확장되도록 구성된 경우 예상한 것보다 높은 인스턴스 수와 같은 의도치 않은 부작용이 발생할 수 있습니다. 이러한 문제를 방지하기 위해서는 애플리케이션 확장 구성을 검토 및 테스트해서 인스턴스 수를 적정 수준으로 유지해야 합니다.

요청 헤더 차이

1세대 런타임에서는 밑줄이 사용된 요청 헤더(예: X-Test-Foo_bar)를 애플리케이션에 전달할 수 있습니다. 2세대 런타임에는 호스트 아키텍처에 Nginx가 도입되었습니다. 이러한 변경사항으로 인해 2세대 런타임은 밑줄(_)이 있는 헤더를 자동으로 삭제하도록 구성됩니다. 애플리케이션 문제를 방지하기 위해서는 애플리케이션 요청 헤더에 밑줄을 사용하지 않아야 합니다.

Gunicorn 작업자 차이점

Python 3+ 런타임의 경우 Gunicorn 작업자 수는 메모리 사용량에 직접적인 영향을 미칩니다. 메모리 사용량 증가는 작업자 수 증가와 정비례합니다. 메모리 소비를 줄이려면 Gunicorn 작업자 수를 줄이는 것이 좋습니다. Gunicorn 작업자 수 구성에 대한 안내는 진입점 권장사항을 참조하세요.

Python 2와 Python 3 사이의 호환성 문제

Python 3이 2008년 처음 출시되었을 당시 여러 가지 이전 버전과 호환되지 않는 변경사항이 언어에 도입되었습니다. 이러한 변경사항 중 일부는 print 문을 print() 함수로 바꾸는 등 사소한 코드 업데이트를 수행해야 합니다. 기타 변경사항은 바이너리 데이터, 텍스트, 문자열을 처리하는 방법을 업데이트하는 등 코드를 대폭 업데이트해야 할 수 있습니다.

Python 표준 라이브러리를 비롯한 널리 사용되는 여러 오픈소스 라이브러리도 Python 2에서 Python 3으로 이동할 때 변경되었습니다.

Python 3 런타임의 App Engine 번들 서비스

마이그레이션과 복잡성을 줄이기 위해 App Engine 표준 환경에서는 Python 3 런타임에서 Memcache와 같은 여러 기존 번들 서비스 및 API에 액세스할 수 있습니다. Python 3 앱은 언어 관용구 라이브러리를 통해 번들 서비스 API를 호출할 수 있으며 Python 2 런타임에서와 같이 동일한 기능에 액세스할 수 있습니다.

기존 번들 서비스와 비슷한 기능을 제공하는 Google Cloud 제품을 사용할 수도 있습니다. 번들되지 않은 Google Cloud 제품으로 마이그레이션하는 것이 좋습니다. 지속적인 개선과 새로운 기능을 활용할 수 있기 때문입니다.

이미지 처리, 검색, 메시징과 같은 Google Cloud에서 별도의 제품으로 제공되지 않는 번들 서비스의 경우 Google에서 권장하는 타사 제공업체 또는 다른 해결 방법을 사용할 수 있습니다.

구성 파일

App Engine 표준 환경의 Python 3 런타임에서 앱을 실행하려면 App Engine에서 사용하는 구성 파일의 일부를 변경해야 할 수도 있습니다.

동적 콘텐츠 요청을 라우팅하는 데 필요한 웹 프레임워크

Python 2 런타임에서 app.yaml 파일에 URL 핸들러를 만들어 특정 URL 또는 URL 패턴이 요청될 때 실행할 앱을 지정할 수 있습니다.

Python 3 런타임에서 앱은 app.yaml에서 URL 핸들러를 사용하는 대신 Flask 또는 Django와 같은 웹 프레임워크를 사용하여 동적 콘텐츠 요청을 라우팅해야 합니다. 정적 콘텐츠의 경우 앱의 app.yaml 파일에서 URL 핸들러 생성을 계속할 수 있습니다.

정적 콘텐츠만 포함된 앱

App Engine에서 정적 웹앱을 호스팅할 때는 app.yaml 파일에서 URL을 정적 파일에 매핑하도록 핸들러를 지정합니다.

Python 2에서 요청이 app.yaml 파일에 지정된 핸들러와 일치하지 않으면 App Engine이 404 오류 코드를 반환합니다.

Python 3에서 요청이 핸들러와 일치하지 않으면 App Engine이 main.py 파일을 찾고 main.py 파일이 없으면 5xx 오류를 반환합니다. 정적 콘텐츠만 포함된 App Engine 앱은 main.py 파일이 필요하지 않으므로 앱 로그의 인스턴스 시작 오류와 함께 대부분의 사용자에게 이 오류가 표시됩니다.

정적 핸들러가 일치하지 않을 때 404 오류를 반환하는 동일 동작을 유지하고 로그에서 오류를 방지하기 위해서는 다음 중 하나를 수행하면 됩니다.

  • 빈 디렉터리를 가리키는 포괄 정적 핸들러를 app.yaml 파일에 추가합니다.
  • main.py 파일에서 404 오류를 반환하는 간단한 동적 앱을 추가합니다.

두 옵션 중 하나를 사용하는 예시:

app.yaml

empty/와 같이 루트 앱 디렉터리에 빈 디렉터리를 만듭니다. app.yaml 파일의 핸들러 섹션에서 맨 끝에 다른 모든 URL 패턴을 포착하는 새 핸들러를 만들고 static_filesupload 요소empty 디렉터리를 지정합니다.

  handlers:
  - url:
    .
    .
    .
  - url: /(.*)$
    static_files: empty/\1
    upload: empty/.*$

main.py

main.py 파일을 만들고 404 오류를 반환하도록 다음 코드를 추가합니다.

  def app(env, start_response):
    start_response('404 Not Found', [('Content-Type','text/html')])
    return [b"Not Found"]

테스트

dev_appserver에 의존하지 않고 Python에 관용적인 테스트 방법을 사용하는 것이 좋습니다. 예를 들어 venv를 사용하여 격리된 로컬 Python 3 환경을 만들 수 있습니다. 모든 표준 Python 테스트 프레임워크를 사용하여 단위 테스트, 통합 테스트, 시스템 테스트를 작성할 수 있습니다. 서비스의 개발 버전을 설정하거나 많은 Google Cloud 제품에서 사용할 수 있는 로컬 에뮬레이터를 사용할 수도 있습니다.

선택적으로 Python 3을 지원하는 dev_appserver의 미리보기 버전을 사용할 수 있습니다. 이 테스트 기능에 대한 자세한 내용은 로컬 개발 서버 사용을 참조하세요.

배포

appcfg.py를 통한 배포는 Python 3에서 지원되지 않습니다. 대신 gcloud 명령줄 도구를 사용하여 앱을 배포합니다.

로깅

Python 3 런타임의 로깅은 Cloud Logging의 로깅 표준을 따릅니다. Python 3 런타임에서 앱 로그는 더 이상 요청 로그와 번들로 묶이지 않고 다른 레코드로 분리됩니다. Python 3 런타임에서 로그를 읽고 쓰는 방법에 대한 자세한 내용은 로깅 가이드를 참조하세요.

추가 마이그레이션 리소스

App Engine 앱을 독립형 Cloud 서비스 또는 Python 3 런타임으로 마이그레이션하는 방법에 대한 자세한 내용은 다음 App Engine 리소스를 참조하세요.