App Engine 태스크 큐 가져오기 태스크에서 Cloud Pub/Sub로 마이그레이션 (모듈 19)

1. 개요

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

이 Codelab의 목적은 Python 2 App Engine 개발자에게 App Engine 작업 대기열 가져오기 작업에서 Cloud Pub/Sub로 이전하는 방법을 보여주는 것입니다. 또한 Python 3로의 업그레이드뿐만 아니라 App Engine NBS에서 Datastore 액세스를 위해 암시적으로 이전하는 작업 (주로 모듈 2에서 다룸)을 위해 CloudNDB로의 마이그레이션도 있습니다.

모듈 18에서는 앱에 pull 작업 사용을 추가하는 방법을 알아봅니다. 이 모듈에서는 완료된 모듈 18 앱을 사용하여 해당 사용량을 Cloud Pub/Sub로 마이그레이션합니다. push 태스크에 태스크 큐를 사용하는 사용자는 Cloud Tasks로 마이그레이션되며 모듈 7~9를 참조해야 합니다.

다음 실습에서는

필요한 항목

설문조사

본 가이드를 어떻게 사용하실 계획인가요?

<ph type="x-smartling-placeholder"></ph> 읽어보기만 해도 됩니다. 읽고 연습 활동을 완료하세요

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

초급 중급 고급

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

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

2. 배경

App Engine 작업 대기열은 내보내기 및 가져오기 작업을 모두 지원합니다. 애플리케이션 이식성을 개선하기 위해 Google Cloud는 태스크 큐와 같은 기존 번들 서비스에서 다른 Cloud 독립형 또는 동등한 서드 파티 서비스로 마이그레이션할 것을 권장합니다.

마이그레이션 모듈 7~9는 푸시 작업 마이그레이션을 다루고 모듈 18~19는 가져오기 작업 마이그레이션에 중점을 둡니다. Cloud Tasks는 태스크 큐 푸시 태스크와 더 유사하지만 Pub/Sub는 태스크 큐 가져오기 태스크와 유사하지 않습니다.

Pub/Sub에는 태스크 큐에서 제공하는 가져오기 기능보다 많은 기능이 있습니다. 예를 들어 Pub/Sub에는 푸시 기능도 있지만 Cloud Tasks는 태스크 큐 푸시 태스크와 비슷하므로 Pub/Sub 푸시는 마이그레이션 모듈에 적용되지 않습니다. 이 모듈 19 Codelab에서는 큐 메커니즘을 태스크 큐 가져오기 대기열에서 Pub/Sub로 전환하는 것과 App Engine NBS에서 Datastore 액세스를 위해 Cloud NFS로 마이그레이션하는 방법을 모듈 2 마이그레이션을 반복하는 방법을 보여줍니다.

모듈 18 코드는 '광고'됨 Python 2 샘플 앱인 경우 소스 자체는 Python 2 및 3과 호환되며 모듈 19에서 Cloud Pub/Sub (및 Cloud NBS)로 마이그레이션한 후에도 그대로 유지됩니다.

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

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

3. 설정/사전 작업

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

  1. Cloud 프로젝트 설정
  2. 기준 샘플 앱 가져오기
  3. 기준 앱 (재)배포 및 검증
  4. 새로운 Google Cloud 서비스/API 사용 설정

이러한 단계를 통해 정상적으로 작동하는 코드로 시작하고 Cloud 서비스로 마이그레이션할 준비가 되었는지 확인할 수 있습니다.

1. 프로젝트 설정

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

2. 기준 샘플 앱 가져오기

기본 요건 중 하나는 작동하는 모듈 18 App Engine 앱이 있으므로 Codelab을 완료하거나 (위의 링크 권장됨) 저장소에서 모듈 18 코드를 복사하세요. 귀하의 계정을 사용하든 Google의 계정을 사용하든지 여기에서 시작('START')을 시작합니다. 이 Codelab에서는 이전을 안내하며 모듈 19 저장소 폴더('FINISH')에 있는 코드와 유사한 코드로 마무리합니다.

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

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

3. 기준 앱 (재)배포 및 검증

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

  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. 앱이 문제 없이 예상대로 실행되는지 확인합니다. 모듈 18 Codelab을 완료했다면 앱에 최근 방문 기록과 함께 상위 방문자가 표시됩니다 (아래 참고). 그렇지 않으면 표시할 방문자 수가 없을 수 있습니다.

b667551dcbab1a09.png

모듈 18 샘플 앱을 마이그레이션하기 전에 먼저 수정된 앱에서 사용할 Cloud 서비스를 사용 설정해야 합니다.

4. 새로운 Google Cloud 서비스/API 사용 설정

이전 앱은 추가 설정이 필요하지 않은 App Engine 번들 서비스를 사용했지만 독립형 Cloud 서비스는 이를 사용했으며 업데이트된 앱은 Cloud Pub/Sub 및 Cloud Datastore를 모두 사용합니다 (CloudNDB 클라이언트 라이브러리를 통해). App Engine과 두 Cloud API 모두 '항상 무료' Tier 할당량을 사용하며, 이 한도를 초과하지 않는 한 이 튜토리얼을 완료하면 요금이 발생하지 않습니다. Cloud API는 사용자의 환경설정에 따라 Cloud 콘솔 또는 명령줄에서 사용 설정할 수 있습니다.

Cloud Console 사용

Cloud 콘솔에서 API 관리자의 라이브러리 페이지 (올바른 프로젝트)로 이동한 후 페이지 가운데의 검색창을 사용하여 Cloud Datastore 및 Cloud Pub/Sub API를 검색합니다.

c7a740304e9d35b.png

각 API에 대해 개별적으로 사용 버튼을 클릭하면 결제 정보를 입력하라는 메시지가 표시될 수 있습니다. 예를 들어 다음은 Cloud Pub/Sub API 라이브러리 페이지입니다.

1b6c0a2a73124f6b.jpeg

명령줄에서

콘솔에서 API를 사용 설정하는 것은 시각적으로 유익하지만 일부 개발자는 명령줄을 선호합니다. gcloud services enable pubsub.googleapis.com datastore.googleapis.com 명령어를 실행하여 두 API를 동시에 사용 설정합니다.

$ gcloud services enable pubsub.googleapis.com datastore.googleapis.com
Operation "operations/acat.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.

결제 정보를 입력하라는 메시지가 표시될 수 있습니다. 다른 Cloud API를 사용 설정하고 URI가 궁금하다면 각 API의 라이브러리 페이지 하단에서 확인할 수 있습니다. 예를 들어 pubsub.googleapis.com를 '서비스 이름'으로 관찰합니다. Cloud Pub/Sub 페이지 하단에 클릭합니다

단계가 완료되면 프로젝트에서 API에 액세스할 수 있습니다. 이제 이러한 API를 사용하도록 애플리케이션을 업데이트할 차례입니다.

4. Pub/Sub 리소스 만들기

모듈 18의 태스크 큐 워크플로 순서 요약:

  1. 모듈 18은 queue.yaml 파일을 사용하여 pullq이라는 pull 큐를 만들었습니다.
  2. 앱이 방문자를 추적하는 작업을 가져오기 대기열에 추가합니다.
  3. 태스크는 결국 작업자가 처리하며, 한정된 시간 (1시간) 동안 임대됩니다.
  4. 작업이 실행되어 최근 방문자 수를 집계합니다.
  5. 태스크는 완료 시 큐에서 삭제됩니다.

Pub/Sub를 사용하여 유사한 워크플로를 복제하겠습니다. 다음 섹션에서는 필요한 Pub/Sub 리소스를 만드는 세 가지 방법으로 기본적인 Pub/Sub 용어를 소개합니다.

App Engine 태스크 큐 (가져오기)와 Cloud Pub/Sub 용어 비교

Pub/Sub로 전환하려면 어휘를 약간 조정해야 합니다. 다음은 두 제품의 관련 용어와 기본 카테고리 목록입니다. 유사한 비교를 제공하는 이전 가이드도 검토하세요.

  • 데이터 구조 큐에 추가: 태스크 큐를 사용하면 데이터가 가져오기 큐로 이동합니다. Pub/Sub를 사용하면 데이터가 주제로 이동합니다
  • 큐에 추가된 데이터의 단위: 태스크 큐가 있는 가져오기 작업을 Pub/Sub에서는 메시지라고 합니다.
  • 데이터 프로세서: 작업 대기열을 통해 작업자가 pull 작업에 액세스합니다. Pub/Sub를 사용하는 경우 메시지를 수신하려면 구독/구독자가 필요합니다.
  • 데이터 추출: 가져오기 작업을 할당하는 것은 구독을 통해 주제에서 메시지를 가져오는 것과 같습니다.
  • 정리/완료: 완료되면 pull 큐에서 태스크 큐 태스크를 삭제하는 것은 Pub/Sub 메시지를 확인하는 것과 유사합니다.

큐에 추가하는 제품은 달라지지만 워크플로는 비교적 유사하게 유지됩니다.

  1. 앱은 pull 큐 대신 pullq이라는 주제를 사용합니다.
  2. pull 큐에 작업을 추가하는 대신 앱이 메시지를 주제 (pullq)에 전송합니다.
  3. 작업자가 가져오기 대기열에서 작업을 임대하는 대신 worker이라는 구독자pullq 주제에서 메시지를 가져옵니다.
  4. 앱이 메시지 페이로드를 처리하여 Datastore의 방문자 수를 늘립니다.
  5. 가져오기 대기열에서 작업을 삭제하는 대신 앱이 처리된 메시지를 확인합니다.

작업 대기열을 사용할 경우 가져오기 대기열을 만들어야 합니다. Pub/Sub를 사용하여 설정하려면 주제와 구독을 모두 만들어야 합니다. 모듈 18에서는 앱 실행 외부에서 queue.yaml를 처리했습니다. 이제 Pub/Sub에서도 동일한 작업을 수행합니다.

주제와 구독 항목을 만드는 방법에는 세 가지가 있습니다.

  1. Cloud 콘솔에서
  2. 명령줄에서
  3. 코드에서 (짧은 Python 스크립트)

아래 옵션 중 하나를 선택하고 해당 안내에 따라 Pub/Sub 리소스를 만듭니다.

Cloud 콘솔에서

Cloud 콘솔에서 주제를 만들려면 다음 단계를 따르세요.

  1. Cloud 콘솔 Pub/Sub 주제 페이지로 이동합니다.
  2. 상단의 주제 만들기를 클릭합니다. 새 대화상자 창이 열립니다 (아래 이미지 참고).
  3. 주제 ID 입력란에 pullq을 입력합니다.
  4. 선택한 옵션을 모두 선택 해제하고 Google 관리 암호화 키를 선택합니다.
  5. 주제 만들기 버튼을 클릭합니다.

주제 만들기 대화상자는 다음과 같습니다.

a05cfdbf64571ceb.png

이제 주제가 있으므로 해당 주제에 대한 구독을 만들어야 합니다.

  1. Cloud 콘솔 Pub/Sub 구독 페이지로 이동합니다.
  2. 상단에서 구독 만들기를 클릭합니다 (아래 이미지 참고).
  3. Subscription ID(구독 ID) 필드에 worker를 입력합니다.
  4. Cloud Pub/Sub 주제 선택 풀다운에서 '정규화된 경로 이름'을 나타내는 pullq을 선택합니다. 예: projects/PROJECT_ID/topics/pullq
  5. 전송 유형에서 가져오기를 선택합니다.
  6. 다른 옵션은 모두 그대로 두고 만들기 버튼을 클릭합니다.

정기 결제 만들기 화면은 다음과 같습니다.

c5444375c20b0618.jpeg

또한 주제 페이지에서 구독을 만들 수 있습니다. 이 '바로가기' 주제와 구독을 연결하는 데 유용할 수 있습니다. 구독 생성에 관한 자세한 내용은 문서를 참고하세요.

명령줄에서

Pub/Sub 사용자는 각각 gcloud pubsub topics create TOPIC_IDgcloud pubsub subscriptions create SUBSCRIPTION_ID --topic=TOPIC_ID 명령어를 사용하여 주제와 구독을 만들 수 있습니다. TOPIC_IDpullq로, SUBSCRIPTION_IDworker로 실행하면 프로젝트 PROJECT_ID에 다음과 같은 출력이 표시됩니다.

$ gcloud pubsub topics create pullq
Created topic [projects/PROJECT_ID/topics/pullq].

$ gcloud pubsub subscriptions create worker --topic=pullq
Created subscription [projects/PROJECT_ID/subscriptions/worker].

빠른 시작 문서의 이 페이지도 참고하세요. 명령줄을 사용하면 주제 및 구독이 정기적으로 생성되는 워크플로를 간소화할 수 있으며, 이러한 명령어를 셸 스크립트에서 이러한 용도로 사용할 수 있습니다.

코드에서 (짧은 Python 스크립트)

주제 및 구독 생성을 자동화하는 또 다른 방법은 소스 코드에서 Pub/Sub API를 사용하는 것입니다. 다음은 모듈 19 저장소 폴더에 있는 maker.py 스크립트의 코드입니다.

from __future__ import print_function
import google.auth
from google.api_core import exceptions
from google.cloud import pubsub

_, PROJECT_ID = google.auth.default()
TOPIC = 'pullq'
SBSCR = 'worker'
ppc_client = pubsub.PublisherClient()
psc_client = pubsub.SubscriberClient()
TOP_PATH = ppc_client.topic_path(PROJECT_ID, TOPIC)
SUB_PATH = psc_client.subscription_path(PROJECT_ID, SBSCR)

def make_top():
    try:
        top = ppc_client.create_topic(name=TOP_PATH)
        print('Created topic %r (%s)' % (TOPIC, top.name))
    except exceptions.AlreadyExists:
        print('Topic %r already exists at %r' % (TOPIC, TOP_PATH))

def make_sub():
    try:
        sub = psc_client.create_subscription(name=SUB_PATH, topic=TOP_PATH)
        print('Subscription created %r (%s)' % (SBSCR, sub.name))
    except exceptions.AlreadyExists:
        print('Subscription %r already exists at %r' % (SBSCR, SUB_PATH))
    try:
        psc_client.close()
    except AttributeError:  # special Py2 handler for grpcio<1.12.0
        pass

make_top()
make_sub()

이 스크립트를 실행하면 다음과 같이 예상 출력이 표시됩니다 (오류가 없는 경우).

$ python3 maker.py
Created topic 'pullq' (projects/PROJECT_ID/topics/pullq)
Subscription created 'worker' (projects/PROJECT_ID/subscriptions/worker)

API를 호출하여 기존 리소스를 만들면 클라이언트 라이브러리에서 google.api_core.exceptions.AlreadyExists 예외가 발생하고 스크립트에서 적절하게 처리됩니다.

$ python3 maker.py
Topic 'pullq' already exists at 'projects/PROJECT_ID/topics/pullq'
Subscription 'worker' already exists at 'projects/PROJECT_ID/subscriptions/worker'

Pub/Sub를 처음 사용하는 경우 Pub/Sub 아키텍처 백서에서 자세한 정보를 확인하세요.

5. 구성 업데이트

구성 업데이트에는 다양한 구성 파일을 변경하는 것뿐만 아니라 App Engine pull 큐와 동일한 기능을 만드는 것이 포함됩니다. 하지만 Cloud Pub/Sub 생태계 내에서도 마찬가지입니다.

queue.yaml 삭제

태스크 큐에서 완전히 벗어나므로 Pub/Sub가 이 파일을 사용하지 않으므로 queue.yaml를 삭제합니다. 가져오기 큐를 만드는 대신 Pub/Sub 주제 (및 구독)를 만듭니다.

requirements.txt

모듈 18의 flask를 조인하도록 google-cloud-ndbgoogle-cloud-pubsub를 모두 requirements.txt에 추가합니다. 업데이트된 모듈 19 requirements.txt는 다음과 같이 표시됩니다.

flask
google-cloud-ndb
google-cloud-pubsub

requirements.txt 파일에는 버전 번호가 없습니다. 즉, 최신 버전이 선택됩니다. 비호환성이 발생할 경우 버전 번호를 사용하여 작동하는 앱 버전을 잠그는 표준 관행을 따릅니다.

app.yaml

app.yaml의 변경사항은 Python 2를 사용하는지 아니면 Python 3으로 업그레이드하는지에 따라 다릅니다.

Python 2

위의 requirements.txt 업데이트로 Google Cloud 클라이언트 라이브러리 사용이 추가되었습니다. 이를 위해서는 App Engine의 추가 지원, 즉 몇 개의 기본 제공 라이브러리, setuptools, grpcio가 필요합니다. 내장 라이브러리를 사용하려면 app.yamllibraries 섹션과 라이브러리 버전 번호 또는 'latest'가 필요합니다. 를 참조하세요. 모듈 18 app.yaml에는 아직 이러한 섹션 중 하나가 없습니다.

이전:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

setuptoolsgrpcio 항목과 함께 app.yamllibraries 섹션을 추가하고 최신 버전을 선택합니다. 또한 이 문서 작성 시점을 기준으로 현재 3.x 출시 버전(예: 3.10)과 함께 주석 처리된 Python 3용 자리표시자 runtime 항목을 추가합니다. 이 변경사항에 따라 이제 app.yaml가 다음과 같이 표시됩니다.

변경 후:

#runtime: python310
runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: setuptools
  version: latest
- name: grpcio
  version: latest

Python 3

Python 3 사용자 및 app.yaml의 경우 가장 중요한 것은 항목을 삭제하는 것입니다. 이 섹션에서는 handlers 섹션, threadsafeapi_version 지시어를 삭제하고 libraries 섹션을 만들지 않습니다.

2세대 런타임은 내장된 서드 파티 라이브러리를 제공하지 않으므로 app.yaml에는 libraries 섹션이 필요하지 않습니다. 또한 기본 제공 서드 파티 패키지가 아닌 복사 (벤더링 또는 자체 번들링이라고도 함)가 더 이상 필요하지 않습니다. 앱에서 사용하는 서드 파티 라이브러리만 requirements.txt에 나열하면 됩니다.

app.yamlhandlers 섹션은 애플리케이션 (스크립트)과 정적 파일 핸들러를 지정하는 데 사용됩니다. Python 3 런타임에서는 웹 프레임워크가 자체 라우팅을 실행해야 하므로 모든 스크립트 핸들러를 auto로 변경해야 합니다. 모듈 18과 같은 앱이 정적 파일을 제공하지 않으면 모든 경로가 auto이 되어 관련이 없습니다. 따라서 handlers 섹션도 필요하지 않으므로 삭제합니다.

마지막으로, threadsafe 또는 api_version 지시문은 Python 3에서 사용되지 않으므로 모두 삭제합니다. 핵심은 runtime 지시어만 유지되도록 app.yaml의 모든 섹션을 삭제하여 최신 버전의 Python 3(예: 3.10)를 지정해야 한다는 것입니다. 이 업데이트 전과 후의 app.yaml 모습은 다음과 같습니다.

이전:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

변경 후:

runtime: python310

Python 3용 app.yaml에서 모든 항목을 삭제할 준비가 되지 않은 사용자를 위해 모듈 19 저장소 폴더에 app3.yaml 대체 파일이 제공되어 있습니다. 배포에 대신 사용하려면 gcloud app deploy app3.yaml 명령어 끝에 이 파일 이름을 추가해야 합니다. 그러지 않으면 기본적으로 변경되지 않은 Python 2 app.yaml 파일과 함께 앱이 배포됩니다.

appengine_config.py

Python 3로 업그레이드하는 경우 appengine_config.py가 필요하지 않으므로 삭제합니다. 필요하지 않은 이유는 서드 파티 라이브러리 지원을 위해 requirements.txt에 이를 지정하기만 하면 되기 때문입니다. Python 2 사용자에 대한 자세한 내용을 읽어 보세요.

모듈 18 appengine_config.py에는 requirements.txt에 방금 추가된 Flask 및 Cloud 클라이언트 라이브러리와 같은 서드 파티 라이브러리를 지원하는 적절한 코드가 있습니다.

이전:

from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)

그러나 이 코드만으로는 방금 추가된 내장 라이브러리 (setuptools, grpcio)를 지원하기에 충분하지 않습니다. 몇 줄이 더 필요하므로 appengine_config.py를 다음과 같이 업데이트합니다.

변경 후:

import pkg_resources
from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
# Add libraries to pkg_resources working set to find the distribution.
pkg_resources.working_set.add_entry(PATH)

Cloud 클라이언트 라이브러리를 지원하는 데 필요한 변경사항에 관한 자세한 내용은 번들 서비스 마이그레이션 문서를 참고하세요.

기타 구성 업데이트

lib 폴더가 있으면 삭제합니다. Python 2 사용자인 경우 다음 명령어를 실행하여 lib 폴더를 보충합니다.

pip install -t lib -r requirements.txt  # or pip2

개발 시스템에 Python 2와 3이 모두 설치되어 있는 경우 pip 대신 pip2를 사용해야 할 수 있습니다.

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

이 섹션에서는 App Engine 태스크 큐 pull 큐 사용을 Cloud Pub/Sub로 대체하여 기본 애플리케이션 파일 main.py를 업데이트하는 방법을 설명합니다. 웹 템플릿(templates/index.html)에 변경사항이 없습니다. 두 앱은 동일하게 작동하면서 동일한 데이터를 표시해야 합니다.

가져오기 및 초기화 업데이트

가져오기와 초기화에는 몇 가지 업데이트가 있습니다.

  1. 가져오기의 경우 App Engine Dataplex 및 태스크 큐를 Cloud NBS 및 Pub/Sub로 바꿉니다.
  2. pullq의 이름을 QUEUE 이름에서 TOPIC 이름으로 바꿉니다.
  3. 가져오기 작업의 경우 작업자가 1시간 동안 임대했지만, Pub/Sub에서는 시간 제한이 메시지 단위로 측정되므로 HOUR 상수를 삭제하세요.
  4. Cloud API를 사용하려면 API 클라이언트를 사용해야 하므로 Cloud Dataplex 및 Cloud Pub/Sub에서 시작하세요. 후자는 주제와 구독 모두에 대해 클라이언트를 제공합니다.
  5. Pub/Sub에는 Cloud 프로젝트 ID가 필요하므로 google.auth.default()에서 가져와서 가져옵니다.
  6. Pub/Sub에는 '정규화된 경로 이름'이 필요합니다. *_path() 편의 함수를 사용하여 주제를 만들어야 합니다.

다음은 모듈 18에서 가져온 가져오기 및 초기화이며, 이어서 위 변경사항을 구현한 후 섹션을 어떻게 살펴볼까요? 새 코드는 대부분 다양한 Pub/Sub 리소스입니다.

이전:

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__)

변경 후:

from flask import Flask, render_template, request
import google.auth
from google.cloud import ndb, pubsub

LIMIT = 10
TASKS = 1000
TOPIC = 'pullq'
SBSCR = 'worker'

app = Flask(__name__)
ds_client  = ndb.Client()
ppc_client = pubsub.PublisherClient()
psc_client = pubsub.SubscriberClient()
_, PROJECT_ID = google.auth.default()
TOP_PATH = ppc_client.topic_path(PROJECT_ID, TOPIC)
SUB_PATH = psc_client.subscription_path(PROJECT_ID, SBSCR)

데이터 모델 업데이트로 이동

Visit 데이터 모델은 변경되지 않습니다. Datastore에 액세스하려면 Cloud Datalab API 클라이언트 컨텍스트 관리자(ds_client.context())를 명시적으로 사용해야 합니다. 즉, 코드에서 store_visit()fetch_visits() 모두 Datastore 호출을 Python with 블록 내에 래핑합니다. 이 업데이트는 모듈 2에서 다룬 내용과 동일합니다.

Pub/Sub와 관련된 가장 중요한 변경사항은 작업 대기열 가져오기 작업을 대기열에 추가하는 것을 pullq 주제에 대한 Pub/Sub 메시지 게시로 대체하는 것입니다. 다음은 업데이트 전과 후의 코드입니다.

이전:

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)

변경 후:

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'
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
    ppc_client.publish(TOP_PATH, remote_addr.encode('utf-8'))

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

VisitorCount 데이터 모델 업데이트

VisitorCount 데이터 모델은 변경되지 않으며 아래와 같이 Datastore 쿼리를 with 블록 내에 래핑하는 경우를 제외하고 fetch_counts()를 실행합니다.

이전:

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

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

변경 후:

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

def fetch_counts(limit):
    'get top visitors'
    with ds_client.context():
        return VisitorCount.query().order(-VisitorCount.counter).fetch(limit)

작업자 코드 업데이트

작업자 코드는 NBS를 Cloud NBS로, 태스크 큐를 Pub/Sub로 대체하는 한 업데이트되지만 워크플로는 동일하게 유지됩니다.

  1. Cloud Dataplex 컨텍스트 관리자 with 블록에서 Datastore 호출을 래핑합니다.
  2. 태스크 큐 정리에는 pull 큐에서 모든 태스크를 삭제하는 작업이 포함됩니다. Pub/Sub 사용 시 '확인 ID' acks에서 수집된 후 마지막에 삭제/확인됩니다.
  3. 태스크 큐 가져오기 태스크는 Pub/Sub 메시지를 가져오는 것과 유사한 방식으로 임대됩니다. 가져오기 작업의 삭제는 작업 객체 자체에서 실행되지만 Pub/Sub 메시지는 확인 ID를 통해 삭제됩니다.
  4. Pub/Sub 메시지 페이로드에는 Python 문자열이 아닌 바이트가 필요하므로 주제에 메시지를 게시하고 주제에서 메시지를 가져올 때 각각 UTF-8 인코딩과 디코딩이 사용됩니다.

log_visitors()를 방금 설명한 변경사항을 구현하는 아래의 업데이트된 코드로 바꿉니다.

이전:

@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))

변경 후:

@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 = {}
    acks = set()
    rsp = psc_client.pull(subscription=SUB_PATH, max_messages=TASKS)
    msgs = rsp.received_messages
    for rcvd_msg in msgs:
        acks.add(rcvd_msg.ack_id)
        visitor = rcvd_msg.message.data.decode('utf-8')
        tallies[visitor] = tallies.get(visitor, 0) + 1
    if acks:
        psc_client.acknowledge(subscription=SUB_PATH, ack_ids=acks)
    try:
        psc_client.close()
    except AttributeError:  # special handler for grpcio<1.12.0
        pass

    # increment those counts in Datastore and return
    if tallies:
        with ds_client.context():
            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(msgs), len(tallies))

기본 애플리케이션 핸들러 root()에는 변경사항이 없습니다. HTML 템플릿 파일 templates/index.html도 변경할 필요가 없으므로 필요한 모든 업데이트가 래핑됩니다. 축하합니다. Cloud Pub/Sub를 사용하는 새로운 모듈 19 애플리케이션에 오신 것을 환영합니다.

7. 요약/삭제

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

애플리케이션 배포 및 확인

pullq 주제 및 worker 구독을 이미 만들었는지 확인합니다. 이 단계를 완료하고 샘플 앱을 게시할 준비가 되었다면 gcloud app deploy를 사용하여 앱을 배포합니다. 전체 기본 큐 메커니즘이 성공적으로 대체되었다는 점을 제외하고 출력은 모듈 18 앱과 동일해야 합니다.

b667551dcbab1a09.png

이제 앱의 웹 프런트엔드가 애플리케이션의 이 부분이 작동하는지 확인합니다. 앱의 이 부분은 상위 방문자 및 최근 방문 수를 쿼리하고 표시하는 데 성공하지만, 앱은 이 방문자를 전체 수에 추가하는 가져오기 작업과 함께 이 방문을 등록합니다. 해당 작업은 현재 처리 대기 중인 대기열에 있습니다.

App Engine 백엔드 서비스 또는 cron 작업을 사용하거나, /log로 이동하거나, 명령줄 HTTP 요청을 실행하여 이 작업을 실행할 수 있습니다. 다음은 curl로 작업자 코드를 호출하는 실행 샘플입니다 (PROJECT_ID 대체).

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

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

삭제

일반

이 작업이 완료되면 요금이 청구되지 않도록 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의 고유한 서비스입니다. 자세한 내용은 각 제품의 문서를 참조하세요.

  • Cloud Pub/Sub의 다양한 구성요소에는 무료 등급이 있습니다. 전체 사용량을 확인하여 비용에 미치는 영향에 대해 자세히 알아보고 가격 책정 페이지에서 자세한 내용을 확인하세요.
  • App Engine Datastore 서비스는 무료 등급인 Cloud Datastore (Datastore 모드의 Cloud Firestore)에서 제공합니다. 자세한 내용은 가격 책정 페이지를 참고하세요.

다음 단계

이 튜토리얼 외에도 기존 번들 서비스에서 벗어나는 데 중점을 둔 다른 마이그레이션 모듈에는 다음이 포함됩니다.

  • 모듈 2: App Engine ndb에서 Cloud Dataplex로 마이그레이션
  • 모듈 7~9: App Engine 태스크 큐 (푸시 태스크)에서 Cloud Tasks로 이전
  • 모듈 12~13: App Engine Memcache에서 Cloud Memorystore로 마이그레이션
  • 모듈 15~16: App Engine blobstore에서 Cloud Storage로 이전

Google Cloud의 서버리스 플랫폼은 더 이상 App Engine이 아닙니다. App Engine 앱이 작거나 기능이 제한적이며 독립형 마이크로서비스로 전환하려는 경우 또는 모놀리식 앱을 재사용 가능한 여러 구성요소로 분할하려는 경우 Cloud Functions로 이전하는 것이 좋습니다. 특히 CI/CD (지속적 통합/지속적 배포 또는 배포) 파이프라인으로 구성된 컨테이너화가 애플리케이션 개발 워크플로의 일부가 되었다면 Cloud Run으로 마이그레이션하는 것이 좋습니다. 이러한 시나리오는 다음 모듈에서 다룹니다.

  • App Engine에서 Cloud Functions로 마이그레이션: 모듈 11 참조
  • App Engine에서 Cloud Run으로 마이그레이션: Docker로 앱을 컨테이너화하려면 모듈 4를 참조하고, 컨테이너, Docker 지식 또는 Dockerfile 없이 마이그레이션하려면 모듈 5를 참조하세요.

다른 서버리스 플랫폼으로 전환하는 것은 선택사항이며 변경하기 전에 앱 및 사용 사례에 가장 적합한 옵션을 고려하는 것이 좋습니다.

다음으로 고려할 마이그레이션 모듈과 관계없이 모든 서버리스 마이그레이션 스테이션 콘텐츠 (Codelab, 동영상, 소스 코드[사용 가능한 경우])는 오픈소스 저장소에서 액세스할 수 있습니다. 저장소의 README는 고려할 이전 및 관련 '순서'에 관한 안내도 제공합니다. 오신 것을 환영합니다

8. 추가 리소스

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

Codelabs 문제/의견

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

마이그레이션 리소스

모듈 18 (START)과 모듈 19 (FINISH)의 저장소 폴더 링크는 아래 표에서 찾을 수 있습니다.

Codelab

Python 2

Python 3

모듈 18

코드

(해당 없음)

모듈 19 (본 Codelab)

코드

(위에서 설명한 대로 app.yaml을 업데이트하지 않는 한 app3.yaml 사용을 제외하면 Python 2와 동일함)

온라인 참조

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

App Engine 작업 대기열

Cloud Pub/Sub

App Engine NDB 및 CloudNDB (Datastore)

App Engine 플랫폼

기타 클라우드 정보

동영상

라이선스

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