Python 런타임

개요

Python 런타임은 애플리케이션 코드 및 종속 항목을 설치하고 애플리케이션을 실행하는 소프트웨어 스택입니다. 표준 런타임은 app.yaml에서 runtime: python으로 선언됩니다.

runtime: python
env: flex

가변형 환경의 런타임은 Docker를 사용해 구축합니다. Python 런타임은 Ubuntu 16.04를 기반으로 합니다. Python 런타임의 소스 코드는 GitHub에서 공개적으로 사용 가능합니다.

인터프리터

기본 인터프리터는 Python 2.7.9입니다. runtime_config 설정을 통해 애플리케이션의 app.yaml에서 Python 2를 사용할지 Python 3를 사용할지 지정할 수 있습니다.

runtime: python
env: flex

runtime_config:
    python_version: 3

다음과 같은 인터프리터 버전이 지원됩니다.

  • Python 2.7.9(2로 지정)
  • Python 3.7.2(3으로 지정)

종속 항목

런타임에서는 애플리케이션 소스 디렉터리에서 requirements.txt 파일을 찾아 pip를 사용해 애플리케이션을 시작하기 전에 종속 항목을 설치합니다. 패키지 선언 및 관리에 대한 자세한 내용은 Python 라이브러리 사용을 참조하세요.

Python을 통한 C 라이브러리 사용

C 확장이 필요한 Python 패키지의 사용을 설정하려면 현재 Python 버전의 헤더와 다음 Ubuntu 패키지를 시스템에 미리 설치해야 합니다.

이 패키지가 있어야 가장 널리 사용되는 Python 라이브러리를 설치할 수 있습니다. 추가로 운영체제 수준의 종속 항목이 필요한 애플리케이션인 경우 이 런타임을 기반으로 커스텀 런타임을 사용해 적절한 패키지를 설치해야 합니다.

애플리케이션 시작

런타임은 app.yaml에서 정의된 entrypoint를 사용하여 애플리케이션을 시작합니다. 이 진입점에서 환경 변수 PORT로 정의된 포트에서 HTTP 요청에 응답하는 프로세스를 시작할 것입니다.

대부분의 웹 애플리케이션은 Gunicorn, uWSGI, Waitress 같은 WSGI 서버를 사용합니다.

이러한 서버 중 하나를 사용하려면 먼저 애플리케이션의 requirements.txt에 종속 항목으로 추가해야 합니다. 런타임은 entrypoint가 호출되기 전에 모든 종속 항목이 설치되도록 보장합니다.

Flask==0.10.1
gunicorn==19.3.0

Flask 애플리케이션에 gunicorn을 사용하는 entrypoint의 예:

entrypoint: gunicorn -b :$PORT main:app

Django 애플리케이션에 gunicorn을 사용하는 entrypoint의 예시:

entrypoint: gunicorn -b :$PORT mydjangoapp:wsgi

권장되는 WSGI 서버는 Gunicorn이지만 다른 WSGI 서버를 사용해도 무방합니다. Flask에 uWSGI를 사용하는 진입점의 예를 들면 다음과 같습니다.

entrypoint: uwsgi --http :$PORT --wsgi-file main.py --callable app

WSGI 서버 없이 요청을 처리할 수 있는 애플리케이션에서는 python 스크립트만 실행하면 됩니다.

entrypoint: python main.py

위에 제시된 기본 entrypoint 예시는 시작점으로 사용하기 위한 것이며 사용자의 웹 애플리케이션에서 작동할 수도 있습니다. 하지만 대부분의 애플리케이션은 WSGI 서버를 추가로 구성해야 합니다. entrypoint의 모든 설정을 지정하는 대신 app.yaml이 위치한 프로젝트 루트 디렉터리에 gunicorn.conf.py 파일을 생성하고 entrypoint에서 다음과 같이 지정합니다.

entrypoint: gunicorn -c gunicorn.conf.py -b :$PORT main:app

Gunicorn 문서에서 Gunicorn의 모든 구성 값을 확인할 수 있습니다.

작업자

Gunicorn은 요청을 처리하기 위해 작업자를 사용합니다. 기본적으로 Gunicorn은 동기 작업자를 사용합니다. 이 작업자 클래스는 모든 웹 애플리케이션과 호환되지만 각 작업자는 한 번에 하나의 요청만 처리할 수 있습니다. 기본적으로 gunicorn은 이러한 작업자 중 하나만 사용합니다. 따라서 인스턴스가 제대로 활용되지 못하여 고부하 시 애플리케이션의 지연 시간이 증가하는 경우가 생길 수 있습니다.

작업자 수는 인스턴스 CPU 코어 수의 2~4배에 1을 더한 수로 설정할 것을 권장합니다. gunicorn.conf.py에서 다음과 같이 지정할 수 있습니다.

import multiprocessing

workers = multiprocessing.cpu_count() * 2 + 1

또한 대부분 I/O 바운드인 일부 웹 애플리케이션은 다른 작업자 클래스를 사용하여 성능을 개선할 수 있습니다. 작업자 클래스에 gevent나 tornado 같은 추가 종속 항목이 필요하다면 애플리케이션의 requirements.txt에 이러한 종속 항목을 선언해야 합니다.

HTTPS 및 전달 프록시

App Engine은 부하 분산기에서 HTTPS 연결을 종료하고 요청을 애플리케이션에 전달합니다. 대부분의 애플리케이션은 요청이 HTTPS를 통해 전송되었는지 여부를 알 필요는 없지만, 이 정보가 필요한 애플리케이션은 gunicorn.conf.py에서 App Engine 프록시를 신뢰하도록 Gunicorn을 구성해야 합니다.

forwarded_allow_ips = '*'
secure_scheme_headers = {'X-FORWARDED-PROTO': 'https'}

이제 Gunicorn은 대부분의 웹 프레임 워크가 요청의 표시로 사용하게 될 'https'로 설정된 wsgi.url_scheme이 안전하다는 것을 확신하게 됩니다. WSGI 서버 또는 프레임 워크에서 이를 지원하지 않는 경우 X-Forwarded-Proto 헤더의 값을 수동으로 확인하세요.

일부 애플리케이션은 사용자 IP 주소도 확인해야 합니다. 사용자 IP 주소는 X-Forwarded-For 헤더에서 제공됩니다.

gunicorn.conf.pysecure_scheme_headers 설정은 X-FORWARDED-PROTO와 같이 대문자여야 하지만, 코드에서 읽을 수 있는 헤더는 X-Forwarded-Proto와 같이 대소 문자를 혼용합니다.

런타임 확장

표준 python 런타임을 사용해 커스텀 런타임을 만들 수 있습니다. 커스텀 런타임은 Dockerfile을 통해 구성됩니다. 다음과 같이 gen-config를 사용하여 표준 Python 런타임을 기반으로 Dockerfile을 생성할 수 있습니다.

gcloud beta app gen-config --custom

원하는 대로 Dockerfile.dockerignore를 맞춤설정할 수 있습니다. 마지막으로 app.yaml에서 runtime: python 대신 runtime: custom을 지정해야 합니다. 자세한 내용은 Python 런타임 맞춤설정을 참조하세요.

환경 변수

런타임 환경에서 설정되는 환경 변수는 다음과 같습니다.

환경 변수 설명
GAE_INSTANCE 현재 인스턴스의 이름입니다.
GAE_MEMORY_MB 애플리케이션 프로세스에서 사용할 수 있는 메모리 양입니다.
GAE_SERVICE 애플리케이션의 app.yaml 파일에 지정된 서비스 이름이거나 서비스 이름이 지정되지 않은 경우 default로 설정됩니다.
GAE_VERSION 현재 애플리케이션의 버전 라벨입니다.
GOOGLE_CLOUD_PROJECT 애플리케이션과 연결된 프로젝트 ID로, Google Cloud Console에 표시됩니다.
PORT HTTP 요청을 수신할 포트

app.yaml로 추가 환경 변수를 설정할 수 있습니다.

메타데이터 서버

애플리케이션의 각 인스턴스에서 Compute Engine 메타데이터 서버를 사용해 호스트 이름, 외부 IP 주소, 인스턴스 ID, 커스텀 메타데이터, 서비스 계정 정보 등의 인스턴스 관련 정보를 쿼리할 수 있습니다. App Engine에서는 인스턴스별로 커스텀 메타데이터를 설정할 수 없지만 프로젝트 차원의 커스텀 메타데이터를 설정해 App Engine 및 Compute Engine 인스턴스에서 읽는 것은 가능합니다.

이 예시 함수는 메타데이터 서버를 사용해 인스턴스의 외부 IP 주소를 가져옵니다.

METADATA_NETWORK_INTERFACE_URL = \
    ('http://metadata/computeMetadata/v1/instance/network-interfaces/0/'
     'access-configs/0/external-ip')

def get_external_ip():
    """Gets the instance's external IP address from the Compute Engine metadata
    server. If the metadata server is unavailable, it assumes that the
    application is running locally.
    """
    try:
        r = requests.get(
            METADATA_NETWORK_INTERFACE_URL,
            headers={'Metadata-Flavor': 'Google'},
            timeout=2)
        return r.text
    except requests.RequestException:
        logging.info('Metadata server could not be reached, assuming local.')
        return 'localhost'