Python의 HTTP Cloud Functions

1. 소개

b158ce75c3cccd6d.png

Python은 데이터 과학자, 웹 애플리케이션 개발자, 시스템 관리자 등이 사용하는 인기 있는 오픈소스 프로그래밍 언어입니다.

Cloud Functions는 이벤트 기반 서버리스 컴퓨팅 플랫폼입니다. Cloud Functions를 사용하면 리소스를 프로비저닝하거나 변화하는 요구사항을 처리하기 위해 확장하는 것에 대해 걱정하지 않고 코드를 작성할 수 있습니다.

Cloud Functions에는 다음과 같은 두 가지 유형이 있습니다.

  • HTTP 함수는 HTTP 요청에 응답합니다. 이 Codelab에서는 몇 가지를 빌드합니다.
  • 백그라운드 함수는 Cloud Pub/Sub에 게시되는 메시지 또는 Cloud Storage에 업로드되는 파일과 같은 이벤트에 의해 트리거됩니다. 이 실습에서는 이 문제를 다루지 않지만 문서에서 자세히 알아볼 수 있습니다.

efb3268e3b74ed4f.png

이 Codelab에서는 Python으로 자체 Cloud 함수를 만드는 방법을 안내합니다.

빌드할 항목

이 Codelab에서는 HTTP를 통해 호출되면 'Python Powered' 로고를 표시하는 Cloud 함수를 게시합니다.

a7aaf656b78050fd.png

학습할 내용

  • HTTP Cloud 함수를 작성하는 방법
  • 인수를 사용하는 HTTP Cloud 함수를 작성하는 방법
  • HTTP Cloud 함수를 테스트하는 방법
  • 함수를 사용해 보기 위해 로컬 Python HTTP 서버를 실행하는 방법
  • 이미지를 반환하는 HTTP Cloud 함수를 작성하는 방법

2. 설정 및 요건

자습형 환경 설정

  1. Google Cloud Console에 로그인하여 새 프로젝트를 만들거나 기존 프로젝트를 재사용합니다. 아직 Gmail이나 Google Workspace 계정이 없는 경우 계정을 만들어야 합니다.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • 프로젝트 이름은 이 프로젝트 참가자의 표시 이름입니다. 이는 Google API에서 사용하지 않는 문자열이며 언제든지 업데이트할 수 있습니다.
  • 프로젝트 ID는 모든 Google Cloud 프로젝트에서 고유하며, 변경할 수 없습니다(설정된 후에는 변경할 수 없음). Cloud 콘솔은 고유한 문자열을 자동으로 생성합니다. 일반적으로는 신경 쓰지 않아도 됩니다. 대부분의 Codelab에서는 프로젝트 ID (일반적으로 PROJECT_ID로 식별됨)를 참조해야 합니다. 생성된 ID가 마음에 들지 않으면 다른 임의 ID를 생성할 수 있습니다. 또는 직접 시도해 보고 사용 가능한지 확인할 수도 있습니다. 이 단계 이후에는 변경할 수 없으며 프로젝트 기간 동안 유지됩니다.
  • 참고로 세 번째 값은 일부 API에서 사용하는 프로젝트 번호입니다. 이 세 가지 값에 대한 자세한 내용은 문서를 참고하세요.
  1. 다음으로 Cloud 리소스/API를 사용하려면 Cloud 콘솔에서 결제를 사용 설정해야 합니다. 이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 튜토리얼이 끝난 후에 요금이 청구되지 않도록 리소스를 종료하려면 만든 리소스 또는 프로젝트를 삭제하면 됩니다. Google Cloud 신규 사용자는 300달러(USD) 상당의 무료 체험판 프로그램에 참여할 수 있습니다.

Cloud Shell 시작

Google Cloud를 노트북에서 원격으로 실행할 수 있지만, 이 Codelab에서는 Cloud에서 실행되는 명령줄 환경인 Cloud Shell을 사용합니다.

Cloud Shell 활성화

  1. Cloud Console에서 Cloud Shell 활성화853e55310c205094.png를 클릭합니다.

3c1dabeca90e44e5.png

Cloud Shell을 처음 시작하는 경우 설명이 포함된 중간 화면이 제공됩니다. 중간 화면이 표시되면 계속을 클릭합니다.

9c92662c6a846a5c.png

Cloud Shell을 프로비저닝하고 연결하는 작업은 몇 분이면 끝납니다.

9f0e51b578fecce5.png

이 가상 머신에는 필요한 개발 도구가 모두 로드되어 있습니다. 영구적인 5GB 홈 디렉터리를 제공하고 Google Cloud에서 실행되므로 네트워크 성능과 인증이 크게 개선됩니다. 이 Codelab에서 대부분의 작업은 브라우저로 수행할 수 있습니다.

Cloud Shell에 연결되면 인증이 완료되었고 프로젝트가 해당 프로젝트 ID로 설정된 것을 확인할 수 있습니다.

  1. Cloud Shell에서 다음 명령어를 실행하여 인증되었는지 확인합니다.
gcloud auth list

명령어 결과

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Cloud Shell에서 다음 명령어를 실행하여 gcloud 명령어가 프로젝트를 알고 있는지 확인합니다.
gcloud config list project

명령어 결과

[core]
project = <PROJECT_ID>

또는 다음 명령어로 설정할 수 있습니다.

gcloud config set project <PROJECT_ID>

명령어 결과

Updated property [core/project].

Cloud Functions 및 Cloud Build API가 사용 설정되어 있는지 확인

Cloud Shell에서 다음 명령어를 실행하여 Cloud Functions 및 Cloud Build API가 사용 설정되어 있는지 확인합니다.

gcloud services enable \
  cloudfunctions.googleapis.com \
  cloudbuild.googleapis.com

참고: Cloud Build는 gcloud functions deploy 명령어로 호출되며 코드를 컨테이너 이미지로 자동으로 빌드합니다.

소스 코드 다운로드하기

Cloud Shell 터미널에서 다음 명령어를 실행합니다.

REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="cloud-functions-python-http"

git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR

소스 디렉터리의 콘텐츠를 확인합니다.

ls

다음 파일이 있어야 합니다.

main.py  python-powered.png  test_main.py  web_app.py

3. HTTP Cloud Functions 소개

Python의 HTTP Cloud Functions는 일반 Python 함수로 작성됩니다. 함수는 일반적으로 request이라는 이름의 단일 flask.Request 인수를 허용해야 합니다.

main.py

import flask


def hello_world(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello World! 👋"
    """
    response = "Hello World! 👋"

    return flask.Response(response, mimetype="text/plain")

# ...

선호하는 명령줄 편집기 (nano, vim, emacs)로 파일을 열 수 있습니다. 소스 디렉터리를 작업공간으로 설정한 후 Cloud Shell 편집기에서 열 수도 있습니다.

cloudshell open-workspace .

gcloud functions deploy 명령어를 사용하여 이 함수를 HTTP Cloud 함수로 배포해 보겠습니다.

FUNCTION_NAME="hello_world"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

명령어 결과 출력:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

gcloud functions deploy 옵션에 대한 참고사항:

  • --runtime: 언어 런타임을 지정합니다. Python의 경우 현재 python37, python38, python39, python310 또는 python312일 수 있습니다. 런타임을 참고하세요.
  • --trigger-http: 함수에 엔드포인트가 할당됩니다. 엔드포인트에 대한 HTTP 요청 (POST, PUT, GET, DELETE, OPTIONS)은 함수 실행을 트리거합니다.
  • --allow-unauthenticated: 함수가 공개되어 인증 확인 없이 모든 호출자가 허용됩니다.
  • 자세한 내용은 gcloud functions deploy를 참고하세요.

함수를 테스트하려면 위의 명령어 출력에 표시된 httpsTrigger.url URL을 클릭하면 됩니다. 다음 명령어를 사용하여 URL을 프로그래매틱 방식으로 검색하고 함수를 호출할 수도 있습니다.

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

다음과 같은 결과가 표시됩니다.

Hello World! 👋

4. 인수를 사용하는 HTTP Cloud 함수 작성

함수는 인수를 허용할 때 더 다재다능해집니다. name 매개변수를 지원하는 새 함수 hello_name를 정의해 보겠습니다.

main.py

# ...

def hello_name(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello {NAME}! 🚀" if "name=NAME" is defined in the GET request
    - "Hello World! 🚀" otherwise
    """
    name = request.args.get("name", "World")
    response = f"Hello {name}! 🚀"

    return flask.Response(response, mimetype="text/plain")

# ...

이 새로운 함수를 배포해 보겠습니다.

FUNCTION_NAME="hello_name"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

명령어 결과 출력:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

함수를 테스트하려면 위의 명령어 출력에 표시된 httpsTrigger.url URL을 클릭하면 됩니다. 다음 명령어를 사용하여 URL을 프로그래매틱 방식으로 검색하고 함수를 호출할 수도 있습니다.

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

다음과 같은 기본 결과가 표시됩니다.

Hello World! 🚀

name 인수가 설정되지 않았기 때문에 기본 결과가 표시됩니다. URL에 매개변수를 추가합니다.

curl -w "\n" $URL?name=YOUR%20NAME

이번에는 맞춤 응답이 표시됩니다.

Hello YOUR NAME! 🚀

다음 단계는 소스 코드가 업데이트될 때 함수가 의도한 대로 계속 작동하도록 단위 테스트를 추가하는 것입니다.

5. 테스트 작성

Python의 HTTP Cloud Functions는 표준 라이브러리의 unittest 모듈을 사용하여 테스트됩니다. 함수를 테스트하기 위해 에뮬레이터나 다른 시뮬레이션을 실행할 필요가 없으며 일반적인 Python 코드만 있으면 됩니다.

다음은 hello_worldhello_name 함수의 테스트가 어떻게 표시되는지 보여줍니다.

test_main.py

import unittest
import unittest.mock

import main


class TestHello(unittest.TestCase):
    def test_hello_world(self):
        request = unittest.mock.Mock()

        response = main.hello_world(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 👋"

    def test_hello_name_no_name(self):
        request = unittest.mock.Mock(args={})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 🚀"

    def test_hello_name_with_name(self):
        name = "FirstName LastName"
        request = unittest.mock.Mock(args={"name": name})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == f"Hello {name}! 🚀"
  1. Python 테스트는 다른 Python 파일과 동일한 방식으로 작성됩니다. 가져오기 집합으로 시작한 다음 클래스와 함수를 정의합니다.
  2. 테스트 선언은 class TestHello(TestCase) 형식입니다. unittest.TestCase에서 상속받는 클래스여야 합니다.
  3. 테스트 클래스에는 각 메서드가 test_로 시작해야 하는 메서드가 있으며, 이는 개별 테스트 사례를 나타냅니다.
  4. 각 테스트 사례는 request 매개변수를 모의하여 (즉, 테스트에 필요한 특정 데이터가 있는 가짜 객체로 대체) 함수 중 하나를 테스트합니다.
  5. 각 함수를 호출한 후 테스트는 HTTP 응답을 확인하여 예상한 결과가 맞는지 확인합니다.

main.pyflask에 종속되므로 테스트 환경에 Flask 프레임워크가 설치되어 있는지 확인하세요.

pip install flask

Flask를 설치하면 다음과 비슷한 결과가 출력됩니다.

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

다음 테스트를 로컬에서 실행합니다.

python -m unittest

세 가지 단위 테스트를 통과해야 합니다.

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

다음으로 'Python Powered' 로고를 반환하는 새 함수를 만듭니다.

6. 'Python Powered' HTTP Cloud 함수 작성

모든 요청에 'Python Powered' 이미지를 반환하여 새 함수를 좀 더 재미있게 만들어 보겠습니다.

a7aaf656b78050fd.png

다음 목록은 이를 구현하는 코드를 보여줍니다.

main.py

# ...

def python_powered(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - The official "Python Powered" logo
    """
    return flask.send_file("python-powered.png")

python_powered 함수를 배포합니다.

FUNCTION_NAME="python_powered"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

명령어 결과 출력:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

함수를 테스트하려면 위의 명령어 출력에 표시된 httpsTrigger.url URL을 클릭합니다. 모든 것이 올바르게 작동하면 새 브라우저 탭에 'Python Powered' 로고가 표시됩니다.

다음으로 배포 전에 함수를 로컬에서 실행하고 사용해 볼 수 있도록 앱을 만듭니다.

7. 로컬에서 함수 실행

웹 앱을 만들고 경로에서 함수를 호출하여 HTTP 함수를 로컬로 실행할 수 있습니다. 함수와 동일한 디렉터리에 추가할 수 있습니다. web_app.py 파일에는 다음 콘텐츠가 포함됩니다.

web_app.py

import flask

import main

app = flask.Flask(__name__)


@app.get("/")
def index():
    return main.python_powered(flask.request)


if __name__ == "__main__":
    # Local development only
    # Run "python web_app.py" and open http://localhost:8080
    app.run(host="localhost", port=8080, debug=True)
  1. 이 파일은 Flask 애플리케이션을 만듭니다.
  2. index()라는 함수로 처리되는 기본 URL에 경로를 등록합니다.
  3. 그런 다음 index() 함수는 현재 요청을 전달하여 python_powered 함수를 호출합니다.

개발 환경에 Flask 프레임워크가 설치되어 있는지 확인합니다.

pip install flask

Flask를 설치하면 다음과 비슷한 결과가 출력됩니다.

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

이 애플리케이션을 로컬에서 실행하려면 다음 명령어를 실행합니다.

python web_app.py

이제 Cloud Shell 웹 미리보기를 사용하여 브라우저에서 웹 앱을 테스트합니다. Cloud Shell에서 '웹 미리보기' 버튼을 클릭하고 '포트 8080에서 미리보기'를 선택합니다.

6c9ff9e5c692c58e.gif

Cloud Shell에서 새로운 브라우저 창이 열리면서 프록시 서비스의 미리보기 URL이 표시됩니다. 웹 미리보기는 HTTPS를 통한 액세스를 사용자 계정으로만 제한합니다. 모든 것이 제대로 작동하면 'Python Powered' 로고가 표시됩니다.

8e5c3ead11cfd103.png

8. 축하합니다.

b158ce75c3cccd6d.png

Flask 프레임워크로 웹 요청을 처리하는 관용적 함수를 사용하여 HTTP Cloud Functions를 배포했습니다.

Cloud Functions 가격 책정은 함수의 호출 빈도에 따라 결정되며, 자주 실행되지 않는 함수에는 무료 등급이 적용됩니다. Cloud Functions 테스트를 완료한 후 gcloud를 사용하여 삭제할 수 있습니다.

gcloud functions delete hello_world --quiet
gcloud functions delete hello_name --quiet
gcloud functions delete python_powered --quiet

Google Cloud 콘솔에서 함수를 삭제할 수도 있습니다.

Python에서 Cloud Functions를 유용하게 사용하시기 바랍니다.