Cloud Run용 Python 애플리케이션 최적화

이 가이드에서는 Python 프로그래밍 언어로 작성된 Cloud Run 서비스 최적화와 일부 최적화와 관련된 절충안을 파악하는 데 유용한 배경 정보를 설명합니다. 이 페이지의 정보는 Python에도 적용되는 일반 최적화 팁을 보완합니다.

이러한 기존 Python 웹 기반 애플리케이션의 권장사항과 최적화는 대부분 다음을 기반으로 합니다.

  • 동시 요청 처리(스레드 기반 및 비차단 I/O 모두)
  • 연결 풀링 및 중요하지 않은 함수 일괄 처리(예: 백그라운드 태스크로 trace 및 측정항목 전송)를 사용하여 응답 지연 시간 단축

컨테이너 이미지 최적화

컨테이너 이미지를 최적화하면 로드 시간과 시작 시간을 줄일 수 있습니다. 다음과 같은 방법으로 이미지를 최적화할 수 있습니다.

  • 런타임에 앱에서 필요한 항목만 컨테이너에 넣기
  • WSGI 서버 최적화

런타임에 앱에서 필요한 항목만 컨테이너에 넣기

컨테이너에 포함된 구성요소와 구성요소가 서비스 실행에 필요한지 여부를 고려합니다. 컨테이너 이미지를 최소화하는 방법에는 여러 가지가 있습니다.

  • 더 작은 기본 이미지 사용
  • 대용량 파일을 컨테이너 외부로 이동

더 작은 기본 이미지 사용

Docker Hub는 컨테이너 내 소스에서 Python을 설치하지 않기로 선택한 경우 사용할 수 있는 공식 Python 기본 이미지를 여러 개 제공합니다. Debian 운영체제를 기반으로 합니다.

Docker Hub의 python 이미지를 사용하는 경우 slim 버전을 사용하는 것이 좋습니다. 이러한 이미지는 예를 들어 애플리케이션에 수행할 필요가 없는 휠을 빌드하는 데 사용되는 많은 패키지와 함께 제공되지 않으므로 크기가 작습니다. 예를 들어 Python 이미지는 GNU C 컴파일러, 전처리기, 코어 유틸리티와 함께 제공됩니다.

기본 이미지에서 가장 큰 패키지 10개를 식별하려면 다음 명령어를 실행합니다.

DOCKER_IMAGE=python # or python:slim
docker run --rm ${DOCKER_IMAGE} dpkg-query -Wf '${Installed-Size}\t${Package}\t${Description}\n' | sort -n | tail -n10 | column -t -s $'\t'

이러한 하위 수준 패키지가 더 적기 때문에 slim 기반 이미지는 잠재적인 취약점에 대한 공격에 노출되는 영역이 더 적습니다. 이 이미지에는 소스에서 휠을 빌드하는 데 필요한 요소가 포함되어 있지 않을 수 있습니다.

Dockerfile에 RUN apt install 줄을 추가하여 특정 패키지를 다시 추가할 수 있습니다. Cloud Run에서 시스템 패키지를 사용하는 방법에 대해 자세히 알아보세요.

Debian 기반이 아닌 컨테이너에 대한 옵션도 있습니다. python:alpine 옵션을 사용하면 컨테이너가 크게 작아질 수 있지만 많은 Python 패키지에는 알파 기반 시스템을 지원하는 사전 컴파일된 휠이 없을 수 있습니다. 지원은 개선되지만(PEP-656 참조) 계속 다릅니다. 패키지 관리자, 셸 또는 기타 프로그램이 포함되지 않은 distroless base image를 사용하는 것이 좋을 수도 있습니다.

대용량 파일을 컨테이너 외부로 이동

미디어 애셋 등과 같은 대용량 파일을 기본 컨테이너에 포함할 필요가 없습니다.

Google Cloud는 Cloud Storage와 같은 호스팅 옵션을 여러 개 제공하여 이러한 대용량 항목을 저장합니다. 대규모 애셋을 이러한 서비스로 이동한 후 런타임 시 애플리케이션에서 참조합니다.

WSGI 서버 최적화

Python은 WSGI 표준 PEP-3333을 구현하여 애플리케이션이 웹 서버와 상호작용하는 방법을 표준화했습니다. 보다 일반적인 WSGI 서버 중 하나는 gunicorn이며 이는 많은 샘플 문서에서 사용됩니다.

gunicorn 최적화

gunicorn 호출을 최적화하려면 다음 CMDDockerfile에 추가합니다.

CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

이러한 설정을 변경하려는 경우 애플리케이션별로 작업자와 스레드의 수를 조정합니다. 예를 들어 작업자를 사용 가능한 코어 수와 같은 수로 사용하여 성능이 향상되었는지 확인한 후 스레드 수를 조정해 봅니다. 작업자 또는 스레드를 너무 많이 설정하면 콜드 스타트 지연 시간 증가, 메모리 소비 증가, 초당 요청 수 감소와 같은 부정적인 영향이 발생할 수 있습니다.

기본적으로 gunicorn은 애플리케이션 코드를 평가하기 전이더라도 시작 시 작업자를 생성하고 지정된 포트를 리슨합니다. 이 경우 Cloud Run 기본 시작 프로브가 $PORT에서 리슨하는 즉시 컨테이너 인스턴스를 정상으로 표시하므로 서비스에 커스텀 시작 프로브를 설정해야 합니다.

이 동작을 변경하려면 --preload 설정을 사용해 gunicorn을 호출하여 리슨 전에 애플리케이션 코드를 평가하면 됩니다. 이렇게 하면 다음과 같은 이점이 있습니다.

  • 배포 시 심각한 런타임 버그 식별
  • 메모리 리소스 저장

추가하기 전에 애플리케이션에서 미리 로드하는 항목을 고려해야 합니다.

기타 WSGI 서버

컨테이너에서 Python을 실행하는 데 gunicorn을 사용하도록 제한되지는 않습니다. 컨테이너 런타임 계약에 따라 컨테이너가 HTTP 포트 $PORT에서 리슨하는 한 모든 WSGI 또는 ASGI 웹 서버를 사용할 수 있습니다.

일반적인 대안으로는 uwsgi, uvicorn, waitress가 있습니다.

예를 들어 app 객체가 포함된 main.py라는 이름의 파일에서는 다음 호출로 WSGI 서버가 시작됩니다.

# uwsgi: pip install pyuwsgi
uwsgi --http :$PORT -s /tmp/app.sock --manage-script-name --mount /app=main:app

# uvicorn: pip install uvicorn
uvicorn --port $PORT --host 0.0.0.0 main:app

# waitress: pip install waitress
waitress-serve --port $PORT main:app

DockerfileCMD exec 줄로 추가하거나 Google Cloud 빌드팩을 사용할 때 Procfileweb: 항목으로 추가할 수 있습니다.

애플리케이션 최적화

Cloud Run 서비스 코드에서 시작 시간을 단축시키고 메모리 사용량을 향상시키도록 최적화할 수도 있습니다.

스레드 줄이기

차단하지 않는 반응형 전략을 사용하고 백그라운드 활동을 방지하여 스레드 수를 줄임으로써 메모리를 최적화할 수 있습니다. 또한 일반 팁 페이지에 설명되어 있듯이 파일 시스템에 기록하지 마세요.

Cloud Run 서비스에서 백그라운드 활동을 지원하려면 요청 외부에서 백그라운드 활동을 실행하고 CPU 액세스 권한을 유지할 수 있도록 Cloud Run 서비스 CPU를 항상 할당되도록 설정합니다.

시작 태스크 줄이기

Python 웹 기반 애플리케이션은 시작 시 여러 태스크(예: 데이터 사전 로드, 캐시 준비, 연결 풀 설정)을 완료해야 할 수 있습니다. 이러한 태스크를 순차적으로 실행하면 애플리케이션 속도가 느려질 수 있습니다. 반면, 이들 태스크를 동시에 실행하려면 CPU 코어 수를 늘려야 합니다.

Cloud Run은 현재 실제 사용자 요청을 보내서 콜드 스타트 인스턴스를 트리거합니다. 새로 시작된 인스턴스에 요청이 할당된 사용자는 긴 지연 시간을 경험할 수 있습니다. Cloud Run에는 현재 '준비' 확인 기능이 없으므로 준비되지 않은 애플리케이션에 요청을 보내지 않을 방법이 없습니다.

다음 단계

자세한 내용은 다음을 참조하세요.