1. 개요
서버리스 마이그레이션 스테이션 Codelab 시리즈 (사용자 주도형, 실무 가이드) 및 관련 동영상은 Google Cloud 서버리스 개발자가 주로 기존 서비스에서 벗어나는 하나 이상의 마이그레이션을 통해 애플리케이션을 현대화할 수 있도록 돕기 위한 것입니다. 이렇게 하면 앱의 이식성이 높아지고 더 많은 옵션과 유연성을 제공하여 다양한 Cloud 제품과 통합하고 액세스할 수 있으며 최신 언어 출시로 더 쉽게 업그레이드할 수 있습니다. 이 시리즈는 처음에는 주로 App Engine (표준 환경) 개발자와 같은 초기 Cloud 사용자를 대상으로 하지만, Cloud Functions, Cloud Run과 같은 다른 서버리스 플랫폼이나 해당하는 경우 다른 플랫폼도 포함할 수 있을 만큼 광범위합니다.
이 Codelab의 목적은 Python 2 App Engine 개발자에게 App Engine 태스크 큐 풀 태스크에서 Cloud Pub/Sub로 마이그레이션하는 방법을 보여주는 것입니다. Datastore 액세스를 위한 App Engine NDB에서 Cloud NDB로의 암시적 마이그레이션 (주로 모듈 2에서 다룸)과 Python 3로의 업그레이드도 있습니다.
모듈 18에서는 앱에 pull 태스크 사용을 추가하는 방법을 알아봅니다. 이 모듈에서는 완료된 모듈 18 앱을 가져와 Cloud Pub/Sub로 사용을 이전합니다. 푸시 작업에 태스크 큐를 사용하는 사용자는 대신 Cloud Tasks로 마이그레이션하며 모듈 7~9를 참고해야 합니다.
다음 실습에서는
- App Engine 태스크 큐 (풀 태스크) 사용을 Cloud Pub/Sub로 대체
- App Engine NDB 사용을 Cloud NDB로 대체 (모듈 2 참고)
- 앱을 Python 3로 포팅
필요한 항목
- 활성 GCP 결제 계정이 있는 Google Cloud Platform 프로젝트
- 기본 Python 기술
- 일반적인 Linux 명령어에 대한 실무 지식
- App Engine 앱 개발 및 배포에 대한 기본 지식
- 작동되는 모듈 18 App Engine 샘플 앱
설문조사
본 가이드를 어떻게 사용하실 계획인가요?
귀하의 Python 사용 경험이 어떤지 평가해 주세요.
귀하의 Google Cloud 서비스 사용 경험을 평가해 주세요.
2. 배경
App Engine 태스크 큐는 푸시 및 풀 태스크를 모두 지원합니다. 애플리케이션 이식성을 개선하기 위해 Google Cloud에서는 태스크 큐와 같은 기존 번들 서비스에서 다른 Cloud 독립형 또는 서드 파티 동등 서비스로 마이그레이션하는 것이 좋습니다.
- 태스크 큐 푸시 태스크 사용자는 Cloud Tasks로 마이그레이션해야 합니다.
- 태스크 큐 pull 태스크 사용자는 Cloud Pub/Sub로 마이그레이션해야 합니다.
마이그레이션 모듈 7~9에서는 푸시 작업 마이그레이션을 다루고 모듈 18~19에서는 풀 작업 마이그레이션에 중점을 둡니다. Cloud Tasks는 태스크 큐 푸시 태스크와 더 유사하지만 Pub/Sub는 태스크 큐 풀 태스크와 유사하지 않습니다.
Pub/Sub에는 태스크 큐에서 제공하는 풀 기능보다 더 많은 기능이 있습니다. 예를 들어 Pub/Sub에도 푸시 기능이 있지만 Cloud Tasks는 태스크 큐 푸시 태스크와 더 유사하므로 Pub/Sub 푸시는 마이그레이션 모듈에서 다루지 않습니다. 이 모듈 19 Codelab에서는 태스크 큐 풀 큐에서 Pub/Sub로 대기열 메커니즘을 전환하고 App Engine NDB에서 Datastore 액세스를 위한 Cloud NDB로 마이그레이션하여 모듈 2 마이그레이션을 반복하는 방법을 보여줍니다.
모듈 18 코드는 Python 2 샘플 앱으로 '광고'되지만 소스 자체는 Python 2 및 3과 호환되며 모듈 19에서 Cloud Pub/Sub (및 Cloud NDB)로 마이그레이션한 후에도 마찬가지입니다.
이 튜토리얼에서는 다음 단계를 다룹니다.
- 설정/사전 작업
- 구성 업데이트
- 애플리케이션 코드 수정
3. 설정/사전 작업
이 섹션에서는 다음을 수행하는 방법을 설명합니다.
- Cloud 프로젝트 설정
- 기준 샘플 앱 가져오기
- 기준 앱 (재)배포 및 검증
- 새 Google Cloud 서비스/API 사용 설정
이 단계를 통해 작동하는 코드로 시작하고 Cloud 서비스로 마이그레이션할 준비가 되었는지 확인할 수 있습니다.
1. 프로젝트 설정
모듈 18 Codelab을 완료했으면 동일 프로젝트 (및 코드)를 다시 사용합니다. 또는 완전히 새로운 프로젝트를 만들거나 다른 기존 프로젝트를 다시 사용할 수도 있습니다. 프로젝트에 활성 결제 계정이 있고 App Engine 앱이 사용 설정되어 있는지 확인합니다. 이 Codelab에서 PROJECT_ID 변수가 표시될 때마다 사용해야 하므로 프로젝트 ID를 찾아둡니다.
2. 기준 샘플 앱 가져오기
기본 요건 중 하나는 작동하는 모듈 18 App Engine 앱이므로 Codelab을 완료하거나 (권장; 위 링크) 저장소에서 모듈 18 코드를 복사하세요. 무엇을 사용하든 여기서 시작합니다 ('START'). 이 Codelab에서는 이전 과정을 안내하며, 모듈 19 저장소 폴더 ('완료')에 있는 것과 비슷한 코드로 마무리합니다.
Module 18 앱을 무엇을 사용하든 폴더는 아래와 같이 표시되며 lib 폴더도 있을 수 있습니다.
$ ls README.md appengine_config.py queue.yaml templates app.yaml main.py requirements.txt
3. 기준 앱 (재)배포 및 검증
다음 단계를 실행하여 모듈 18 앱을 배포합니다.
lib폴더가 있는 경우 삭제하고pip install -t lib -r requirements.txt를 실행하여lib를 다시 채웁니다. 개발 머신에 Python 2와 3이 모두 설치되어 있는 경우pip2를 대신 사용해야 할 수도 있습니다.gcloud명령줄 도구를 설치하고 초기화했으며 사용법을 검토했는지 확인합니다.- (선택사항) 실행하는 각
gcloud명령어에PROJECT_ID를 입력하지 않으려면gcloud config set projectPROJECT_ID로 클라우드 프로젝트를 설정합니다. gcloud app deploy로 샘플 앱 배포- 앱이 문제없이 예상대로 실행되는지 확인합니다. 모듈 18 Codelab을 완료한 경우 앱에 가장 최근 방문과 함께 상위 방문자가 표시됩니다 (아래 그림 참고). 그렇지 않으면 표시할 방문자 수가 없을 수 있습니다.

모듈 18 샘플 앱을 이전하기 전에 수정된 앱에서 사용할 Cloud 서비스를 먼저 사용 설정해야 합니다.
4. 새 Google Cloud 서비스/API 사용 설정
이전 앱은 추가 설정이 필요하지 않은 App Engine 번들 서비스를 사용했지만 독립형 Cloud 서비스는 추가 설정이 필요하며 업데이트된 앱은 Cloud Pub/Sub와 Cloud Datastore (Cloud NDB 클라이언트 라이브러리를 통해)를 모두 사용합니다. App Engine과 두 Cloud API 모두 '상시 무료' 등급 할당량이 있으므로 이러한 한도를 초과하지 않는 한 이 튜토리얼을 완료하는 데 비용이 발생하지 않습니다. Cloud API는 원하는 대로 Cloud 콘솔 또는 명령줄에서 사용 설정할 수 있습니다.
Cloud Console 사용
Cloud 콘솔에서 API 관리자의 라이브러리 페이지 (올바른 프로젝트용)로 이동하고 페이지 중앙의 검색창을 사용하여 Cloud Datastore 및 Cloud Pub/Sub API를 검색합니다.

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

명령줄에서
콘솔에서 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의 라이브러리 페이지 하단에서 확인할 수 있습니다. 예를 들어 바로 위의 Pub/Sub 페이지 하단에 '서비스 이름'으로 pubsub.googleapis.com가 표시됩니다.
단계를 완료하면 프로젝트에서 API에 액세스할 수 있습니다. 이제 이러한 API를 사용하도록 애플리케이션을 업데이트할 차례입니다.
4. Pub/Sub 리소스 만들기
모듈 18의 태스크 큐 워크플로의 시퀀스 순서를 요약하면 다음과 같습니다.
- 모듈 18에서는
queue.yaml파일을 사용하여pullq이라는 풀 대기열을 만들었습니다. - 앱은 방문자를 추적하기 위해 pull 큐에 작업을 추가합니다.
- 태스크는 결국 제한된 시간 (1시간) 동안 임대된 작업자에 의해 처리됩니다.
- 최근 방문자 수를 집계하기 위해 태스크가 실행됩니다.
- 작업이 완료되면 대기열에서 삭제됩니다.
Pub/Sub를 사용하여 유사한 워크플로를 복제합니다. 다음 섹션에서는 필요한 Pub/Sub 리소스를 만드는 세 가지 방법을 통해 기본적인 Pub/Sub 용어를 소개합니다.
App Engine 태스크 큐 (풀)와 Cloud Pub/Sub 용어 비교
Pub/Sub로 전환하려면 어휘를 약간 조정해야 합니다. 아래에는 두 제품의 관련 용어와 함께 기본 카테고리가 나열되어 있습니다. 유사한 비교가 포함된 이전 가이드도 검토하세요.
- 데이터 구조 대기열: 태스크 큐를 사용하면 데이터가 풀 큐로 이동하고 Pub/Sub를 사용하면 데이터가 주제로 이동합니다.
- 대기열에 추가된 데이터 단위: 태스크 큐의 풀 태스크는 Pub/Sub의 메시지라고 합니다.
- 데이터 처리자: 태스크 큐를 사용하면 작업자가 풀 태스크에 액세스합니다. Pub/Sub를 사용하면 메시지를 수신하기 위해 구독/구독자가 필요합니다.
- 데이터 추출: 풀 작업을 임대하는 것은 구독을 통해 주제에서 메시지를 풀링하는 것과 같습니다.
- 정리/완료: 작업이 완료되면 pull 큐에서 태스크 큐 작업을 삭제하는 것은 Pub/Sub 메시지를 승인하는 것과 유사합니다.
대기열 제품은 변경되지만 워크플로는 비교적 유사하게 유지됩니다.
- 풀 대기열 대신 앱은
pullq라는 주제를 사용합니다. - 앱은 풀 큐에 태스크를 추가하는 대신 주제 (
pullq)에 메시지를 전송합니다. - 작업자가 가져오기 대기열에서 태스크를 임대하는 대신
worker라는 구독자가pullq주제에서 메시지를 가져옵니다. - 앱이 메시지 페이로드를 처리하여 Datastore의 방문자 수를 늘립니다.
- 가져오기 큐에서 작업을 삭제하는 대신 앱은 처리된 메시지를 확인합니다.
태스크 큐를 사용하면 pull 큐를 만드는 작업이 설정에 포함됩니다. Pub/Sub를 사용하려면 주제와 구독을 모두 만들어야 합니다. 모듈 18에서는 앱 실행 외부에서 queue.yaml를 처리했습니다. 이제 Pub/Sub에서도 동일한 작업을 해야 합니다.
주제와 구독을 만드는 방법에는 다음 세 가지 옵션이 있습니다.
- Cloud 콘솔에서
- 명령줄에서
- 코드 (짧은 Python 스크립트)
아래 옵션 중 하나를 선택하고 해당 안내에 따라 Pub/Sub 리소스를 만듭니다.
Cloud 콘솔에서
Cloud Console에서 주제를 만들려면 다음 단계를 따르세요.
- Cloud 콘솔의 Pub/Sub 주제 페이지로 이동합니다.
- 상단의 주제 만들기를 클릭합니다. 새 대화상자가 열립니다 (아래 이미지 참고).
- 주제 ID 입력란에
pullq을 입력합니다. - 선택된 옵션을 모두 선택 해제하고 Google 관리 암호화 키를 선택합니다.
- 만들기 주제 버튼을 클릭합니다.
주제 만들기 대화상자는 다음과 같습니다.

이제 주제가 있으므로 해당 주제에 대한 구독을 만들어야 합니다.
- Cloud 콘솔의 Pub/Sub 구독 페이지로 이동합니다.
- 상단에서 구독 만들기를 클릭합니다 (아래 이미지 참고).
- 구독 ID 필드에
worker를 입력합니다. - Cloud Pub/Sub 주제 선택 풀다운에서
pullq를 선택하고 '정규화된 경로 이름'을 기록해 둡니다(예:projects/PROJECT_ID/topics/pullq). - 전송 유형에서 가져오기를 선택합니다.
- 다른 옵션은 모두 그대로 두고 만들기 버튼을 클릭합니다.
정기 결제 생성 화면은 다음과 같습니다.

주제 페이지에서 구독을 만들 수도 있습니다. 이 '단축키'는 주제를 구독과 연결하는 데 유용할 수 있습니다. 구독 만들기에 대해 자세히 알아보려면 문서를 참고하세요.
명령줄에서
Pub/Sub 사용자는 각각 gcloud pubsub topics create TOPIC_ID 및 gcloud pubsub subscriptions create SUBSCRIPTION_ID --topic=TOPIC_ID 명령어를 사용하여 주제와 구독을 만들 수 있습니다. pullq의 TOPIC_ID 및 worker의 SUBSCRIPTION_ID로 이를 실행하면 프로젝트 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. 구성 업데이트
구성 업데이트에는 다양한 구성 파일을 변경하는 것과 Cloud Pub/Sub 생태계 내에서 App Engine 풀 큐와 동일한 항목을 만드는 것이 모두 포함됩니다.
queue.yaml 삭제
태스크 큐를 완전히 사용하지 않으므로 Pub/Sub에서는 이 파일을 사용하지 않기 때문에 queue.yaml를 삭제합니다. 풀 대기열을 만드는 대신 Pub/Sub 주제 (및 구독)을 만듭니다.
requirements.txt
모듈 18에서 flask에 참여할 수 있도록 google-cloud-ndb와 google-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.yaml에 libraries 섹션과 라이브러리 버전 번호 또는 App Engine 서버에서 사용할 수 있는 최신 버전의 경우 'latest'가 필요합니다. 모듈 18 app.yaml에는 아직 이러한 섹션이 없습니다.
이전:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
setuptools 및 grpcio 항목과 함께 libraries 섹션을 app.yaml에 추가하고 최신 버전을 선택합니다. 또한 이 글을 작성하는 시점에 현재 3.x 버전(예: 3.10)과 함께 주석 처리된 Python 3용 자리표시자 runtime 항목을 추가합니다. 이러한 변경사항에 따라 app.yaml는 이제 다음과 같이 표시됩니다.
AFTER:
#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 섹션과 threadsafe 및 api_version 지시어를 삭제하고 libraries 섹션을 만들지 않습니다.
2세대 런타임은 기본 제공 서드 파티 라이브러리를 제공하지 않으므로 app.yaml에 libraries 섹션이 필요하지 않습니다. 또한 기본 제공이 아닌 서드 파티 패키지를 복사 (벤더링 또는 자체 번들링이라고도 함)할 필요가 없습니다. 앱에서 사용하는 서드 파티 라이브러리만 requirements.txt에 나열하면 됩니다.
app.yaml의 handlers 섹션은 애플리케이션 (스크립트) 및 정적 파일 핸들러를 지정하는 데 사용됩니다. Python 3 런타임에서는 웹 프레임워크가 자체 라우팅을 실행해야 하므로 모든 스크립트 핸들러를 auto로 변경해야 합니다. 앱 (예: 모듈 18)이 정적 파일을 제공하지 않으면 모든 경로가 auto이므로 관련성이 없습니다. 따라서 handlers 섹션도 필요하지 않으므로 삭제합니다.
마지막으로 Python 3에서는 threadsafe 및 api_version 지시어가 사용되지 않으므로 이 지시어도 삭제합니다. 결론적으로 app.yaml의 모든 섹션을 삭제하여 최신 버전의 Python 3(예: 3.10)을 지정하는 runtime 지시문만 남겨야 합니다. 업데이트 전후의 app.yaml는 다음과 같습니다.
이전:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
AFTER:
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를 다음과 같이 업데이트하세요.
AFTER:
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 태스크 큐 가져오기 큐를 Cloud Pub/Sub로 대체하는 기본 애플리케이션 파일 main.py의 업데이트를 다룹니다. 웹 템플릿 templates/index.html은 변경되지 않습니다. 두 앱은 동일하게 작동하여 동일한 데이터를 표시해야 합니다.
가져오기 및 초기화 업데이트
가져오기 및 초기화에는 다음과 같은 여러 업데이트가 있습니다.
- 가져오기의 경우 App Engine NDB 및 태스크 큐를 Cloud NDB 및 Pub/Sub로 바꿉니다.
QUEUE이름에서TOPIC이름으로pullq이름을 바꿉니다.- 풀 작업에서는 작업자가 1시간 동안 작업을 임대했지만 Pub/Sub에서는 제한 시간이 메시지별로 측정되므로
HOUR상수를 삭제합니다. - Cloud API를 사용하려면 API 클라이언트를 사용해야 하므로 Cloud NDB 및 Cloud Pub/Sub용 클라이언트를 시작합니다. 후자는 주제와 구독 모두에 클라이언트를 제공합니다.
- Pub/Sub에는 Cloud 프로젝트 ID가 필요하므로
google.auth.default()에서 가져와야 합니다. - Pub/Sub에는 주제와 구독에 '정규화된 경로 이름'이 필요하므로
*_path()편의 함수를 사용하여 이를 만듭니다.
아래는 섹션이 위의 변경사항을 구현한 후 어떻게 표시되는지 보여주는 Module 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__)
AFTER:
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 NDB API 클라이언트 컨텍스트 관리자 ds_client.context()를 명시적으로 사용해야 합니다. 코드에서 이는 Python with 블록 내에서 store_visit() 및 fetch_visits() 모두에 Datastore 호출을 래핑한다는 의미입니다. 이 업데이트는 모듈 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)
AFTER:
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)
AFTER:
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)
작업자 코드 업데이트
작업자 코드는 NDB를 Cloud NDB로, 태스크 큐를 Pub/Sub로 대체하는 정도로 업데이트되지만 워크플로는 동일하게 유지됩니다.
- Cloud NDB 컨텍스트 관리자
with블록에서 Datastore 호출을 래핑합니다. - 태스크 큐 정리에는 풀 큐에서 모든 태스크를 삭제하는 작업이 포함됩니다. Pub/Sub에서는 '확인 ID'가
acks에 수집된 후 마지막에 삭제/확인됩니다. - 태스크 큐 풀 태스크는 Pub/Sub 메시지가 풀되는 방식과 유사하게 임대됩니다. 풀 작업은 작업 객체 자체로 삭제되지만 Pub/Sub 메시지는 확인 ID를 통해 삭제됩니다.
- 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))
AFTER:
@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 앱과 동일해야 합니다.

이제 앱의 웹 프런트엔드에서 애플리케이션의 이 부분이 작동하는지 확인합니다. 앱의 이 부분은 인기 방문자와 가장 최근 방문을 성공적으로 쿼리하고 표시하지만, 앱은 이 방문을 등록하는 동시에 이 방문자를 전체 수에 추가하는 풀 작업을 생성합니다. 이제 해당 작업이 처리 대기열에 있습니다.
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 서비스에 대한 요금도 부과될 수 있으므로 자세한 내용은 가격 책정 페이지를 확인하세요. 이 이전과 관련된 다른 클라우드 서비스는 별도로 청구됩니다. 두 경우 모두 해당하는 경우 아래의 '이 Codelab에만 해당' 섹션을 참고하세요.
완전한 공개를 위해 말씀드리면 App Engine과 같은 Google Cloud 서버리스 컴퓨팅 플랫폼에 배포하면 약간의 빌드 및 스토리지 비용이 발생합니다. Cloud Build에는 Cloud Storage와 마찬가지로 자체 무료 할당량이 있습니다. 해당 이미지를 저장하면 할당량의 일부가 사용됩니다. 하지만 무료 등급이 없는 지역에 거주할 수도 있으므로 스토리지 사용량을 파악하여 잠재적인 비용을 최소화하세요. 검토해야 하는 특정 Cloud Storage '폴더'는 다음과 같습니다.
console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/imagesconsole.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 NDB로 마이그레이션 - 모듈 7~9: App Engine 태스크 큐 (푸시 태스크)에서 Cloud Tasks로 마이그레이션
- 모듈 12~13: App Engine Memcache에서 Cloud Memorystore로 이전
- 모듈 15~16: App Engine Blobstore에서 Cloud Storage로 이전
App Engine은 더 이상 Google Cloud의 유일한 서버리스 플랫폼이 아닙니다. 소규모 App Engine 앱이 있거나 기능이 제한된 앱이 있는데 이를 독립형 마이크로서비스로 전환하려는 경우 또는 모놀리식 앱을 재사용 가능한 여러 구성요소로 분할하려는 경우 Cloud Functions로 이전하는 것이 좋습니다. 컨테이너화가 애플리케이션 개발 워크플로의 일부가 된 경우, 특히 CI/CD (지속적 통합/지속적 배포) 파이프라인으로 구성된 경우 Cloud Run으로 이전하는 것이 좋습니다. 이러한 시나리오는 다음 모듈에서 다룹니다.
- App Engine에서 Cloud Functions로 이전: 모듈 11 참고
- App Engine에서 Cloud Run으로 마이그레이션: 모듈 4에서 Docker를 사용하여 앱을 컨테이너화하거나 모듈 5에서 컨테이너, Docker 지식 또는
Dockerfile없이 컨테이너화하는 방법을 알아보세요.
다른 서버리스 플랫폼으로 전환하는 것은 선택사항이며, 변경하기 전에 앱과 사용 사례에 가장 적합한 옵션을 고려하는 것이 좋습니다.
다음에 고려할 마이그레이션 모듈과 관계없이 모든 서버리스 마이그레이션 스테이션 콘텐츠 (Codelabs, 동영상, 소스 코드[사용 가능한 경우])는 오픈소스 저장소에서 액세스할 수 있습니다. 저장소의 README에서는 고려해야 할 이전과 관련 '순서'의 이전 모듈에 관한 안내도 제공합니다.
8. 추가 리소스
아래에는 이 마이그레이션 모듈 또는 관련 마이그레이션 모듈과 관련 제품을 자세히 살펴보는 개발자를 위한 추가 리소스가 나와 있습니다. 여기에는 이 콘텐츠에 대한 의견을 제공할 수 있는 위치, 코드 링크, 유용할 수 있는 다양한 문서가 포함됩니다.
Codelabs 문제/의견
이 Codelab에 문제가 발견된 경우 문제를 기록하기 전에 먼저 비슷한 기록이 있는지 검색해보세요. 검색 및 새 문제 만들기 링크:
마이그레이션 리소스
모듈 18 (시작) 및 모듈 19 (완료)의 저장소 폴더 링크는 아래 표에서 찾을 수 있습니다.
Codelab | Python 2 | Python 3 |
(해당 없음) | ||
모듈 19 (이 Codelab) | (위에서 설명한 대로 app.yaml을 업데이트한 경우를 제외하고 Python 2와 동일하며 app3.yaml 사용) |
온라인 참조
다음은 이 튜토리얼과 관련된 리소스입니다.
App Engine 작업 대기열
- App Engine 작업 대기열 개요
- App Engine 작업 대기열 가져오기 대기열 개요
- App Engine 작업 대기열 가져오기 대기열 전체 샘플 앱
- Task Queue 풀 큐 만들기
- Google I/O 2011 풀 대기열 출시 동영상 ( Votelator 샘플 앱)
queue.yaml참조queue.yaml와 Cloud Tasks 비교- Pub/Sub로 pull 큐 마이그레이션 가이드
Cloud Pub/Sub
- Cloud Pub/Sub 제품 페이지
- Pub/Sub 클라이언트 라이브러리 사용
- Pub/Sub Python 클라이언트 라이브러리 샘플
- Pub/Sub Python 클라이언트 라이브러리 문서
- Pub/Sub 주제 만들기 및 관리
- Pub/Sub 주제 이름 지정 가이드라인
- Pub/Sub 구독 만들기 및 관리
- App Engine (가변형) 샘플 앱 (표준에도 배포 가능, Python 3)
- 위의 샘플 앱 저장소
- Pub/Sub 풀 구독
- Pub/Sub 푸시 구독
- App Engine Pub/Sub 푸시 샘플 앱 (Python 3)
- App Engine Pub/Sub 푸시 샘플 앱 저장소
- Pub/Sub 가격 정보
- Cloud Tasks 또는 Cloud Pub/Sub? (푸시 vs 풀)
App Engine NDB 및 Cloud NDB (Datastore)
App Engine 플랫폼
- App Engine 문서
- Python 2 App Engine (표준 환경) 런타임
- Python 2 App Engine에서 App Engine 내장 라이브러리 사용
- Python 3 App Engine (표준 환경) 런타임
- Python 2 및 3 App Engine (표준 환경) 런타임 간의 차이점
- Python 2~3 App Engine (표준 환경) 마이그레이션 가이드
- App Engine 가격 및 할당량 정보
- 2세대 App Engine 플랫폼 출시 (2018)
- 1세대 플랫폼과 2세대 플랫폼 비교
- 기존 런타임 장기 지원
- 문서 이전 샘플
- 커뮤니티에서 제공한 마이그레이션 샘플
기타 클라우드 정보
- Google Cloud Platform에서 Python 사용
- Google Cloud Python 클라이언트 라이브러리
- Google Cloud '상시 무료' 등급
- Google Cloud SDK (
gcloud명령줄 도구) - 모든 Google Cloud 문서
동영상
라이선스
이 작업물은 Creative Commons Attribution 2.0 일반 라이선스에 따라 사용이 허가되었습니다.