Flask 앱에서 App Engine 작업 대기열 (가져오기 작업)을 사용하는 방법 (모듈 18)

1. 개요

서버리스 마이그레이션 스테이션 Codelab 시리즈 (사용자 주도형 실습 튜토리얼) 및 관련 동영상Google Cloud 서버리스 개발자가 주로 기존 서비스에서 벗어나 하나 이상의 마이그레이션을 통해 애플리케이션을 현대화하도록 돕는 것을 목표로 합니다. 이렇게 하면 앱의 이식성이 향상되고 더 많은 옵션과 유연성이 제공되므로 다양한 Cloud 제품과 통합하고 액세스하고 최신 언어 버전으로 보다 쉽게 업그레이드할 수 있습니다. 이 시리즈는 처음에는 초기 Cloud 사용자, 특히 App Engine (표준 환경) 개발자에 중점을 두고 있지만 Cloud FunctionsCloud Run과 같은 다른 서버리스 플랫폼이나 해당하는 경우 다른 플랫폼을 포함할 만큼 광범위합니다.

이 Codelab에서는 모듈 1 Codelab에서 App Engine 작업 대기열 가져오기 작업샘플 앱에 포함하고 사용하는 방법을 설명합니다. 이 모듈 18 튜토리얼에서는 pull 작업의 사용을 추가한 다음 모듈 19에서 해당 사용을 Cloud Pub/Sub로 미리 마이그레이션합니다. push 태스크에 태스크 큐를 사용하는 사용자는 Cloud Tasks로 마이그레이션되며 모듈 7~9를 참조해야 합니다.

다음 실습에서는

  • App Engine Task Queue API/번들 서비스 사용
  • 기본 Python 2 Flask App Engine NBS 앱에 pull 큐 사용 추가

필요한 항목

설문조사

이 튜토리얼을 어떻게 사용하실 계획인가요?

읽기만 할 계획입니다 읽은 다음 연습 활동을 완료할 계획입니다

귀하의 Python 사용 경험이 어떤지 평가해 주세요.

초급 중급 고급

귀하의 Google Cloud 서비스 사용 경험을 평가해 주세요.

<ph type="x-smartling-placeholder"></ph> 초보자 중급 숙련도

2. 배경

App Engine 태스크 큐 가져오기 태스크에서 이전하려면 모듈 1 Codelab에서 얻은 기존 Flask 및 App Engine NBS 앱에 해당 사용량을 추가하세요. 샘플 앱은 최종 사용자의 가장 최근 방문을 표시합니다. 그래도 괜찮지만 방문자를 추적하여 누가 가장 많이 방문하는지 확인하는 것도 더욱 흥미롭습니다.

이러한 방문자 수에 푸시 작업을 사용할 수도 있지만, 방문을 등록하고 사용자에게 즉시 응답하는 작업을 담당하는 샘플 앱과 지정된 '작업자'가 책임을 분담하고자 합니다. 일반적인 요청-응답 워크플로우를 벗어나 방문자 수를 집계하는 일을 담당합니다.

이 설계를 구현하기 위해 pull 큐 사용을 기본 애플리케이션에 추가하고 작업자 기능을 지원합니다. 작업자는 별도의 프로세스 (예: 백엔드 인스턴스 또는 항상 작동 중인 VM에서 실행되는 코드), 크론 작업 또는 curlwget를 사용하는 기본 명령줄 HTTP 요청으로 실행할 수 있습니다. 통합 후 다음 Codelab (모듈 19) Codelab에서 앱을 Cloud Pub/Sub로 이전할 수 있습니다.

이 가이드에는 다음 단계가 포함됩니다.

  1. 설정/사전 작업
  2. 구성 업데이트
  3. 애플리케이션 코드 수정

3. 설정/사전 작업

이 섹션에서는 다음을 수행하는 방법을 설명합니다.

  1. Cloud 프로젝트 설정
  2. 기준 샘플 앱 가져오기
  3. 기준 앱 (재)배포 및 검증

다음 단계를 통해 실제로 작동하는 코드로 시작할 수 있습니다.

1. 프로젝트 설정

모듈 1 Codelab을 완료했다면 동일한 프로젝트와 코드를 재사용합니다. 또는 새 프로젝트를 만들거나 다른 기존 프로젝트를 재사용합니다. 프로젝트에 활성 결제 계정과 사용 설정된 App Engine 앱이 있는지 확인하세요. 이 Codelab에서 여러 번 필요하므로 프로젝트 ID를 찾아 PROJECT_ID 변수를 발견할 때마다 사용합니다.

2. 기준 샘플 앱 가져오기

이 Codelab의 기본 요건 중 하나는 작동하는 모듈 1 App Engine 앱이 있어야 합니다. 모듈 1 Codelab (권장)을 완료하거나 저장소에서 모듈 1 앱을 복사합니다. 여러분의 것이든 우리의 것이든, 모듈 1 코드에서 '시작'할 것입니다. 이 Codelab에서는 각 단계를 살펴보고 모듈 18 저장소 폴더 'FINISH'에 있는 코드와 유사한 코드로 마무리합니다.

사용하는 모듈 1 앱과 관계없이 폴더는 아래 출력과 같이 표시되며 lib 폴더도 있을 수 있습니다.

$ ls
README.md               appengine_config.py     requirements.txt
app.yaml                main.py                 templates

3. 기준 앱 (재)배포

다음 단계를 실행하여 모듈 1 앱을 배포합니다.

  1. lib 폴더가 있으면 삭제하고 pip install -t lib -r requirements.txt를 실행하여 lib를 다시 채웁니다. Python 2와 3이 모두 설치된 경우 pip2 명령어를 대신 사용해야 할 수도 있습니다.
  2. gcloud 명령줄 도구를 설치초기화하고 사용법을 검토했는지 확인합니다.
  3. gcloud 명령어가 실행될 때마다 PROJECT_ID를 입력하지 않으려면 gcloud config set project PROJECT_ID로 Cloud 프로젝트를 설정합니다.
  4. gcloud app deploy를 사용하여 샘플 앱 배포
  5. 모듈 1 앱이 예상대로 실행되고 최신 방문 기록이 표시되는지 확인합니다 (아래 그림 참고).

a7a9d2b80d706a2b.png

4. 구성 업데이트

표준 App Engine 구성 파일 (app.yaml, requirements.txt, appengine_config.py)은 변경할 필요가 없습니다. 대신 다음 콘텐츠가 포함된 새 구성 파일 queue.yaml를 추가하여 동일한 최상위 디렉터리에 넣습니다.

queue:
- name: pullq
  mode: pull

queue.yaml 파일은 앱에 있는 모든 태스크 큐를 지정합니다 (App Engine에서 자동으로 생성되는 default [push] 큐 제외). 이 경우에는 pullq이라는 pull 큐만 있습니다. App Engine에서는 mode 지시문을 pull로 지정해야 하며, 그렇지 않으면 기본적으로 내보내기 큐가 생성됩니다. pull 큐 만들기에 대한 자세한 내용은 문서를 참조하세요. 또한 다른 옵션은 queue.yaml 참조 페이지를 확인하세요.

이 파일을 앱과 별도로 배포합니다. gcloud app deploy를 계속 사용하지만 명령줄에서 queue.yaml도 제공합니다.

$ gcloud app deploy queue.yaml
Configurations to update:

descriptor:      [/tmp/mod18-gaepull/queue.yaml]
type:            [task queues]
target project:  [my-project]

WARNING: Caution: You are updating queue configuration. This will override any changes performed using 'gcloud tasks'. More details at
https://cloud.google.com/tasks/docs/queue-yaml

Do you want to continue (Y/n)?

Updating config [queue]...⠹WARNING: We are using the App Engine app location (us-central1) as the default location. Please use the "--location" flag if you want to use a different location.
Updating config [queue]...done.

Task queues have been updated.

Visit the Cloud Platform Console Task Queues page to view your queues and cron jobs.
$

5. 애플리케이션 코드 수정

이 섹션에서는 다음 파일에 대한 업데이트를 다룹니다.

  • main.py — 기본 애플리케이션에 pull 큐 사용 추가
  • templates/index.html: 새 데이터를 표시하도록 웹 템플릿 업데이트

가져오기 및 상수

첫 번째 단계는 pull 큐를 지원하기 위해 하나의 새로운 가져오기와 여러 상수를 추가하는 것입니다.

  • 태스크 큐 라이브러리 google.appengine.api.taskqueue의 가져오기를 추가합니다.
  • 가져오기 대기열 (QUEUE)에서 1시간 (HOUR) 동안 최대 pull 작업 수 (TASKS)를 임대할 수 있도록 세 개의 상수를 추가합니다.
  • 가장 최근의 방문과 상위 방문자 (LIMIT)를 표시하는 상수를 추가합니다.

다음은 원본 코드와 업데이트 후의 모습입니다.

이전:

from flask import Flask, render_template, request
from google.appengine.ext import ndb

app = Flask(__name__)

변경 후:

from flask import Flask, render_template, request
from google.appengine.api import taskqueue
from google.appengine.ext import ndb

HOUR = 3600
LIMIT = 10
TASKS = 1000
QNAME = 'pullq'
QUEUE = taskqueue.Queue(QNAME)
app = Flask(__name__)

가져오기 작업 추가 (작업 관련 데이터 수집 및 가져오기 대기열에서 작업 만들기)

데이터 모델 Visitfetch_visits()에 표시할 방문수 쿼리와 동일하게 유지됩니다. 코드의 이 부분에서 필요한 유일한 변경사항은 store_visit()입니다. 방문을 등록하는 것 외에도 작업자가 방문자 수 카운터를 늘릴 수 있도록 방문자의 IP 주소를 사용하여 가져오기 대기열에 작업을 추가합니다.

이전:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

변경 후:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit in Datastore and queue request to bump visitor count'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
    QUEUE.add(taskqueue.Task(payload=remote_addr, method='PULL'))

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

방문자 추적을 위한 데이터 모델 및 쿼리 함수 만들기

방문자 추적용 VisitorCount 데이터 모델 추가 visitor 자체 필드와 방문수를 추적하기 위한 정수 counter가 있어야 합니다. 그런 다음 fetch_counts()라는 새 함수 (Python classmethod)를 추가하여 상위 방문자를 쿼리하고 가장 낮은 순서의 순서로 반환합니다. fetch_visits()의 본문 바로 아래에 클래스와 함수를 추가합니다.

class VisitorCount(ndb.Model):
    visitor = ndb.StringProperty(repeated=False, required=True)
    counter = ndb.IntegerProperty()

def fetch_counts(limit):
    'get top visitors'
    return VisitCount.query().order(-VisitCount.counter).fetch(limit)

작업자 코드 추가

새 함수 log_visitors()를 추가하여 /log에 대한 GET 요청을 통해 방문자를 기록합니다. 사전/해시를 사용하여 최근 방문자 수를 추적하고 한 시간 동안 최대한 많은 작업을 임대합니다. 각 태스크에 대해 동일한 방문자의 모든 방문을 집계합니다. 집계를 통해 앱은 이미 Datastore에 있는 모든 해당 VisitorCount 항목을 업데이트하거나 필요한 경우 새 항목을 만듭니다. 마지막 단계에서는 처리된 작업 수에서 등록된 방문자 수를 나타내는 일반 텍스트 메시지를 반환합니다. 이 함수를 fetch_counts() 바로 아래의 main.py에 추가합니다.

@app.route('/log')
def log_visitors():
    'worker processes recent visitor counts and updates them in Datastore'
    # tally recent visitor counts from queue then delete those tasks
    tallies = {}
    tasks = QUEUE.lease_tasks(HOUR, TASKS)
    for task in tasks:
        visitor = task.payload
        tallies[visitor] = tallies.get(visitor, 0) + 1
    if tasks:
        QUEUE.delete_tasks(tasks)

    # increment those counts in Datastore and return
    for visitor in tallies:
        counter = VisitorCount.query(VisitorCount.visitor == visitor).get()
        if not counter:
            counter = VisitorCount(visitor=visitor, counter=0)
            counter.put()
        counter.counter += tallies[visitor]
        counter.put()
    return 'DONE (with %d task[s] logging %d visitor[s])\r\n' % (
            len(tasks), len(tallies))

새 디스플레이 데이터로 기본 핸들러 업데이트

상위 방문자를 표시하려면 기본 핸들러 root()를 업데이트하여 fetch_counts()를 호출합니다. 또한 템플릿이 업데이트되어 상위 방문자 수와 최근 방문 수가 표시됩니다. 방문자 수를 fetch_visits() 호출의 최근 방문수와 함께 패키징하고 이를 단일 context에 드롭하여 웹 템플릿에 전달합니다. 다음은 이 변경 전과 변경 후의 코드입니다.

이전:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)
    return render_template('index.html', visits=visits)

변경 후:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    context = {
        'limit':  LIMIT,
        'visits': fetch_visits(LIMIT),
        'counts': fetch_counts(LIMIT),
    }
    return render_template('index.html', **context)

다음은 main.py에 필요한 모든 변경사항입니다. 다음은 main.py의 변경사항에 관한 폭넓은 아이디어를 제공하기 위한 설명 목적으로 이러한 업데이트를 그림으로 표현한 것입니다.

ad5fd3345efc13d0.png

새 디스플레이 데이터로 웹 템플릿 업데이트

웹 템플릿(templates/index.html)에서 최근 방문자의 일반 페이로드 외에도 상위 방문자를 표시하려면 업데이트가 필요합니다. 상위 방문자와 그 수를 페이지 상단의 표에 드롭하고 최근 방문 데이터를 이전과 동일하게 계속 렌더링합니다. 유일한 다른 변경사항은 숫자를 하드코딩하는 대신 limit 변수를 통해 표시되는 숫자를 지정하는 것입니다. 웹 템플릿을 업데이트해야 하는 사항은 다음과 같습니다.

이전:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

변경 후:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>

<h1>VisitMe example</h1>

<h3>Top {{ limit }} visitors</h3>
<table border=1 cellspacing=0 cellpadding=2>
    <tr><th>Visitor</th><th>Visits</th></tr>
{% for count in counts %}
    <tr><td>{{ count.visitor|e }}</td><td align="center">{{ count.counter }}</td></tr>
{% endfor %}
</table>

<h3>Last {{ limit }} visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

이로써 모듈 1 샘플 앱에 App Engine 태스크 큐 가져오기 태스크 사용을 추가하는 데 필요한 변경사항을 마칩니다. 이제 디렉터리는 모듈 18 샘플 앱을 나타내며 다음 파일을 포함해야 합니다.

$ ls
README.md               appengine_config.py     queue.yaml              templates
app.yaml                main.py                 requirements.txt

6. 요약/삭제

이 섹션에서는 앱을 배포하고 의도한 대로 반영된 출력에서 작동하는지 확인하여 이 Codelab을 마무리합니다. 방문자 수를 처리하려면 작업자를 별도로 실행합니다. 앱 유효성 검사 후 정리 단계를 실행하고 다음 단계를 고려합니다.

애플리케이션 배포 및 확인

이 Codelab의 상단 부분에서 gcloud app deploy queue.yaml를 사용하여 했던 것처럼 pull 큐를 이미 설정했는지 확인합니다. 이 단계를 완료하고 샘플 앱을 게시할 준비가 되었다면 gcloud app deploy를 사용하여 앱을 배포합니다. 이제 '상위 방문자'가 포함된다는 점을 제외하고 출력이 모듈 1 앱과 동일합니다. 다음 표와 같이 입력합니다.

b667551dcbab1a09.png

업데이트된 웹 프런트엔드에는 상위 방문자와 최근 방문이 표시되지만, 방문자 수에는 방문이 포함되지 않습니다. 앱이 처리 대기 중인 작업인 pull 큐에서 방문자 수를 늘리는 새 작업을 삭제하는 동시에 이전 방문자 수를 표시합니다.

다음과 같은 다양한 방법으로 /log를 호출하여 작업을 실행할 수 있습니다.

예를 들어 curl를 사용하여 /log에 GET 요청을 보내면 PROJECT_ID를 제공한 경우 출력은 다음과 같습니다.

$ curl https://PROJECT_ID.appspot.com/log
DONE (with 1 task[s] logging 1 visitor[s])

그러면 업데이트된 수가 다음 웹사이트 방문 시 반영됩니다. 작업이 끝났습니다.

축하합니다. App Engine 작업 대기열 가져오기 대기열 서비스 사용을 샘플 앱에 추가하여 이 Codelab을 완료했습니다. 이제 모듈 19에서 Cloud Pub/Sub, Cloud NBS, Python 3으로 마이그레이션할 준비가 되었습니다.

삭제

일반

이 작업이 완료되면 요금이 청구되지 않도록 App Engine 앱을 사용 중지하는 것이 좋습니다. 하지만 추가 테스트 또는 실험을 원하는 경우 App Engine 플랫폼에서 무료 할당량을 사용할 수 있으므로 이 사용 등급을 초과하지 않는 한 요금이 청구되지 않습니다. 이는 컴퓨팅을 위한 것이며, 관련 App Engine 서비스에 대한 요금이 부과될 수 있으므로 자세한 내용은 가격 책정 페이지를 확인하세요. 이 마이그레이션에 다른 Cloud 서비스가 포함된 경우 별도로 요금이 청구됩니다. 두 경우 모두 해당하는 경우 '이 Codelab과 관련된 내용'을 참고하세요. 섹션을 참조하세요.

자세히 알려드리자면 App Engine과 같은 Google Cloud 서버리스 컴퓨팅 플랫폼에 배포할 경우 약간의 빌드 및 스토리지 비용이 발생합니다. Cloud Build에는 Cloud Storage와 마찬가지로 자체 무료 할당량이 있습니다. 해당 이미지의 스토리지가 할당량 중 일부를 사용합니다. 하지만 이러한 무료 등급이 없는 지역에 거주할 수도 있으므로 잠재적인 비용을 최소화하려면 저장용량 사용량에 유의해야 합니다. 특정 Cloud Storage '폴더' 다음을 포함해야 합니다.

  • console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • 위의 스토리지 링크는 PROJECT_ID 및 *LOC*기호에 따라 다릅니다(예: 'us'). (앱이 미국에서 호스팅되는 경우)

반면 이 애플리케이션 또는 다른 관련 이전 Codelab을 계속 진행하지 않고 모든 항목을 완전히 삭제하려면 프로젝트를 종료합니다.

이 Codelab에만 해당

아래에 나열된 서비스는 이 Codelab의 고유한 서비스입니다. 자세한 내용은 각 제품의 문서를 참조하세요.

다음 단계

이 '이전'에서는 방문자 추적 지원을 추가하고 모듈 18 샘플 앱을 구현하여 모듈 1 샘플 앱에 태스크 큐 푸시 큐 사용을 추가했습니다. 다음 마이그레이션에서는 App Engine 가져오기 작업을 Cloud Pub/Sub로 업그레이드합니다. 2021년 말부터 사용자는 Python 3로 업그레이드할 때 Cloud Pub/Sub로 마이그레이션할 필요가 없습니다. 다음 섹션에서 자세히 알아보세요.

Cloud Pub/Sub로 마이그레이션하려면 모듈 19 Codelab을 참고하세요. 이 외에도 Cloud Datastore, Cloud Memorystore, Cloud Storage, Cloud Tasks (push 큐)와 같은 마이그레이션도 고려해야 합니다. 또한 Cloud Run 및 Cloud Functions로의 제품 간 마이그레이션도 있습니다. 모든 서버리스 마이그레이션 스테이션 콘텐츠 (Codelab, 동영상, 소스 코드[사용 가능한 경우])는 오픈소스 저장소에서 액세스할 수 있습니다.

7. Python 3로 마이그레이션

2021년 가을, App Engine팀은 많은 번들 서비스에 대한 지원을 2세대 런타임 (1세대 런타임 포함)으로 확장했습니다. 따라서 앱을 Python 3로 포팅할 때 더 이상 App Engine 태스크 큐와 같은 번들 서비스에서 독립형 Cloud 또는 Cloud Pub/Sub와 같은 서드 파티 서비스로 마이그레이션할 필요가 없습니다. 즉, 코드를 재구성하여 차세대 런타임에서 번들 서비스에 액세스하는 한 Python 3 App Engine 앱에서 작업 대기열을 계속 사용할 수 있습니다.

모듈 17 Codelab 및 관련 동영상에서 번들 서비스 사용량을 Python 3로 이전하는 방법을 자세히 알아볼 수 있습니다. 이 주제는 모듈 18의 범위에 속하지 않지만, 아래 링크는 Python 3으로 포팅되고 여전히 App Engine NBS를 사용하는 모듈 1 앱의 Python 3 버전입니다. (언젠가는 Module 18 앱의 Python 3 버전도 사용할 수 있습니다.)

8. 추가 리소스

아래에는 이 모듈이나 관련 이전 모듈 및 관련 제품을 자세히 살펴보는 개발자를 위한 추가 리소스가 나열되어 있습니다. 여기에는 이 콘텐츠에 대한 의견을 제공할 수 있는 곳, 코드 링크 및 도움이 될 만한 다양한 문서가 포함됩니다.

Codelab 문제/의견

이 Codelab에 문제가 발견된 경우 문제를 기록하기 전에 먼저 비슷한 기록이 있는지 검색해보세요. 검색 및 새 문제 만들기 링크:

마이그레이션 리소스

모듈 1 (START)과 모듈 18 (FINISH)의 저장소 폴더 링크는 아래 표에서 찾을 수 있습니다. 모든 App Engine Codelab 이전을 위한 저장소에서도 액세스할 수 있습니다. 클론하거나 ZIP 파일을 다운로드합니다.

Codelab

Python 2

Python 3

모듈 1

코드

code (이 가이드에 포함되지 않음)

모듈 18 (이 Codelab)

코드

해당 사항 없음

온라인 참조

다음은 이 튜토리얼과 관련된 리소스입니다.

App Engine 작업 대기열

App Engine 플랫폼

App Engine 문서

Python 2 App Engine (표준 환경) 런타임

Python 3 App Engine (표준 환경) 런타임

Python 2와 Python 2의 차이점 3 App Engine (표준 환경) 런타임

Python 2에서 3으로 App Engine (표준 환경) 마이그레이션 가이드

App Engine 가격 책정할당량 정보

2세대 App Engine 플랫폼 출시 (2018)

기존 런타임 장기 지원

문서 마이그레이션 샘플

기타 클라우드 정보

동영상

라이선스

이 작업물은 Creative Commons Attribution 2.0 일반 라이선스에 따라 사용이 허가되었습니다.