1. 개요
서버리스 마이그레이션 스테이션 Codelab 시리즈 (사용자 주도형, 실무 가이드) 및 관련 동영상은 Google Cloud 서버리스 개발자가 주로 기존 서비스에서 벗어나는 하나 이상의 마이그레이션을 통해 애플리케이션을 현대화할 수 있도록 돕기 위한 것입니다. 이렇게 하면 앱의 이식성이 높아지고 더 많은 옵션과 유연성을 제공하여 다양한 Cloud 제품과 통합하고 액세스할 수 있으며 최신 언어 출시로 더 쉽게 업그레이드할 수 있습니다. 이 시리즈는 처음에는 주로 App Engine (표준 환경) 개발자와 같은 초기 Cloud 사용자를 대상으로 하지만, Cloud Functions, Cloud Run과 같은 다른 서버리스 플랫폼이나 해당하는 경우 다른 플랫폼도 포함할 수 있을 만큼 광범위합니다.
이 Codelab에서는 App Engine Blobstore에서 Cloud Storage로 마이그레이션하는 방법을 알아봅니다. 다음으로부터의 암시적 이전도 있습니다.
webapp2웹 프레임워크에서 Flask로 (모듈 1에서 다룸)- Datastore 액세스를 위한 App Engine NDB에서 Cloud NDB로 (모듈 2에서 다룸)
- Python 2~3 (마이그레이션된 앱이 Python 2 및 3과 모두 호환됨)
단계별 정보는 관련 마이그레이션 모듈을 참고하세요.
다음 실습에서는
- App Engine Blobstore API/라이브러리 사용 추가
- Blobstore 서비스에 사용자 업로드 저장
- Cloud Storage로 마이그레이션하기 위한 다음 단계 준비
필요한 항목
- 활성 GCP 결제 계정이 있는 Google Cloud Platform 프로젝트
- 기본 Python 기술
- 일반적인 Linux 명령어에 대한 실무 지식
- App Engine 앱 개발 및 배포에 대한 기본 지식
- 작동하는 모듈 15 App Engine 앱: 모듈 15 Codelab을 완료하거나 (권장) 저장소에서 모듈 15 앱을 복사합니다.
설문조사
이 튜토리얼을 어떻게 사용하실 계획인가요?
귀하의 Python 사용 경험이 어떤지 평가해 주세요.
귀하의 Google Cloud 서비스 사용 경험을 평가해 주세요.
2. 배경
이 Codelab은 모듈 15의 샘플 앱으로 시작하여 Blobstore (및 NDB)에서 Cloud Storage (및 Cloud NDB)로 마이그레이션하는 방법을 보여줍니다. 마이그레이션 프로세스에는 App Engine의 기존 번들 서비스에 대한 종속 항목을 대체하는 작업이 포함되며, 이를 통해 원하는 경우 앱을 다른 Cloud 서버리스 플랫폼이나 다른 호스팅 플랫폼으로 이동할 수 있습니다.
이 마이그레이션은 이 시리즈의 다른 마이그레이션에 비해 약간 더 많은 노력이 필요합니다. Blobstore는 원래 webapp 프레임워크에 종속되므로 샘플 앱은 Flask 대신 webapp2 프레임워크를 사용합니다. 이 튜토리얼에서는 Cloud Storage, Cloud NDB, Flask, Python 3로의 마이그레이션을 다룹니다.
앱은 여전히 최종 사용자 '방문'을 등록하고 가장 최근의 10개를 표시하지만 이전 (모듈 15) Codelab에서는 Blobstore 사용을 수용하기 위해 새로운 기능을 추가했습니다. 앱은 최종 사용자에게 '방문'에 해당하는 아티팩트 (파일)를 업로드하라는 메시지를 표시합니다. 사용자는 이 작업을 수행하거나 '건너뛰기'를 선택하여 선택 해제할 수 있습니다. 사용자의 결정과 관계없이 다음 페이지는 이 앱의 이전 버전과 동일한 출력을 렌더링하여 가장 최근 방문을 표시합니다. 한 가지 추가적인 사항은 해당 아티팩트가 있는 방문에는 방문의 아티팩트를 표시하는 '보기' 링크가 있다는 것입니다. 이 Codelab에서는 설명된 기능을 유지하면서 앞에서 언급한 이전 작업을 구현합니다.
3. 설정/사전 작업
이 튜토리얼의 주요 부분을 진행하기 전에 프로젝트를 설정하고, 코드를 가져온 다음, 기본 앱을 배포하여 작동하는 코드로 시작할 수 있도록 준비합니다.
1. 프로젝트 설정
모듈 15 앱을 이미 배포한 경우 동일 프로젝트 (및 코드)를 다시 사용하는 것이 좋습니다. 또는 완전히 새로운 프로젝트를 만들거나 다른 기존 프로젝트를 다시 사용할 수도 있습니다. 프로젝트에 활성 결제 계정이 있고 App Engine이 사용 설정되어 있는지 확인합니다.
2. 기준 샘플 앱 가져오기
이 Codelab의 기본 요건 중 하나는 작동하는 모듈 15 샘플 앱을 준비하는 것입니다. 샘플 앱이 없는 경우 모듈 15 'START' 폴더 (아래 링크)에서 다운로드할 수 있습니다. 이 Codelab은 각 단계를 안내하며, 모듈 16 '완료' 폴더에 있는 것과 비슷한 코드로 마무리됩니다.
모듈 15 시작 파일의 디렉터리는 다음과 같습니다.
$ ls README.md app.yaml main-gcs.py main.py templates
main-gcs.py 파일은 모듈 15의 main.py의 대체 버전으로, 프로젝트 ID(PROJECT_ID.appspot.com)에 따라 앱에 할당된 URL의 기본값과 다른 Cloud Storage 버킷을 선택할 수 있습니다. 이 파일은 원하는 경우 유사한 이전 기법을 적용할 수 있다는 점을 제외하고 이 Codelab (모듈 16)에서 아무런 역할을 하지 않습니다.
3. 기준 앱 (재)배포
이제 남은 사전 작업 실행 단계는 다음과 같습니다.
gcloud명령줄 도구 다시 숙지gcloud app deploy를 사용하여 샘플 앱을 다시 배포합니다.- 앱이 문제 없이 App Engine에서 실행되는지 확인합니다.
이 단계를 성공적으로 실행하고 모듈 15 앱이 작동하는지 확인합니다. 초기 페이지에는 업로드할 방문 아티팩트 파일을 묻는 양식과 함께 선택 해제할 수 있는 '건너뛰기' 버튼이 표시됩니다.

사용자가 파일을 업로드하거나 건너뛰면 앱에서 익숙한 '최근 방문' 페이지가 렌더링됩니다.

아티팩트가 포함된 방문에는 방문 타임스탬프 오른쪽에 아티팩트를 표시 (또는 다운로드)하는 '보기' 링크가 표시됩니다. 앱의 기능을 확인하면 App Engine 기존 서비스 (webapp2, NDB, Blobstore)에서 최신 대안 (Flask, Cloud NDB, Cloud Storage)으로 마이그레이션할 수 있습니다.
4. 구성 파일 업데이트
업데이트된 앱 버전에는 세 개의 구성 파일이 사용됩니다. 필요한 작업은 다음과 같습니다.
app.yaml에서 필수 기본 제공 서드 파티 라이브러리를 업데이트하고 Python 3 이전의 문을 열어 둡니다.- 기본 제공되지 않는 모든 필수 라이브러리를 지정하는
requirements.txt추가 - 앱이 내장 및 비내장 서드 파티 라이브러리를 모두 지원하도록
appengine_config.py추가
app.yaml
libraries 섹션을 업데이트하여 app.yaml 파일을 수정합니다. jinja2를 삭제하고 grpcio, setuptools, ssl를 추가합니다. 세 라이브러리 모두에 사용 가능한 최신 버전을 선택합니다. Python 3 runtime 지시문도 추가하되 주석 처리합니다. 완료되면 다음과 같이 표시됩니다 (Python 3.9를 선택한 경우).
이전:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
libraries:
- name: jinja2
version: latest
AFTER:
#runtime: python39
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
libraries:
- name: grpcio
version: latest
- name: setuptools
version: latest
- name: ssl
version: latest
이 변경사항은 주로 App Engine 서버에서 사용할 수 있는 Python 2 내장 라이브러리와 관련이 있으므로 직접 번들로 묶지 않아도 됩니다. reqs.txt에 추가할 Flask에 Jinja2가 포함되어 있으므로 Jinja2를 삭제했습니다. Cloud NDB 및 Cloud Storage용과 같은 Google Cloud 클라이언트 라이브러리를 사용하는 경우 grpcio 및 setuptools가 필요합니다. 마지막으로 Cloud Storage 자체에 ssl 라이브러리가 필요합니다. 상단의 주석 처리된 런타임 지시어는 이 앱을 Python 3로 포팅할 준비가 되었을 때 사용합니다. 이 주제는 이 튜토리얼의 끝부분에서 다루겠습니다.
requirements.txt
Flask 프레임워크와 Cloud NDB 및 Cloud Storage 클라이언트 라이브러리를 요구하는 requirements.txt 파일을 추가합니다. 이러한 항목은 모두 기본적으로 제공되지 않습니다. 다음 콘텐츠로 파일을 만듭니다.
flask
google-cloud-ndb
google-cloud-storage
Python 2 App Engine 런타임에는 내장되지 않은 서드 파티 라이브러리의 자체 번들링이 필요하므로 다음 명령어를 실행하여 이러한 라이브러리를 lib 폴더에 설치합니다.
pip install -t lib -r requirements.txt
개발 머신에 Python 2와 3이 모두 있는 경우 pip2 명령어를 사용하여 이러한 라이브러리의 Python 2 버전을 가져와야 할 수 있습니다. Python 3으로 업그레이드하면 더 이상 자체 번들링할 필요가 없습니다.
appengine_config.py
내장 및 비내장 서드 파티 라이브러리를 지원하는 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)
방금 완료한 단계는 App Engine 문서의 Python 2 앱용 라이브러리 설치 섹션에 나열된 단계와 유사하거나 동일해야 하며, 특히 appengine_config.py의 콘텐츠는 5단계의 콘텐츠와 일치해야 합니다.
구성 파일 작업이 완료되었으므로 애플리케이션으로 이동하겠습니다.
5. 애플리케이션 파일 수정
가져오기
main.py의 첫 번째 변경사항 세트에는 대체되는 모든 항목을 바꾸는 작업이 포함됩니다. 변경되는 사항은 다음과 같습니다.
webapp2가 Flask로 대체됨webapp2_extras에서 Jinja2를 사용하는 대신 Flask와 함께 제공되는 Jinja2를 사용하세요.- App Engine Blobstore 및 NDB가 Cloud NDB 및 Cloud Storage로 대체됨
webapp의 Blobstore 핸들러는io표준 라이브러리 모듈, Flask,werkzeug유틸리티의 조합으로 대체됩니다.- 기본적으로 Blobstore는 앱의 URL (
PROJECT_ID.appspot.com)에 따라 이름이 지정된 Cloud Storage 버킷에 씁니다. Cloud Storage 클라이언트 라이브러리로 포팅하므로google.auth는 정확히 동일한 버킷 이름을 지정하는 데 필요한 프로젝트 ID를 가져오는 데 사용됩니다. (더 이상 하드코딩되지 않으므로 버킷 이름을 변경할 수 있습니다.)
이전:
import webapp2
from webapp2_extras import jinja2
from google.appengine.ext import blobstore, ndb
from google.appengine.ext.webapp import blobstore_handlers
main.py의 현재 가져오기 섹션을 아래 코드 스니펫으로 바꿔 위의 목록에 나온 변경사항을 구현합니다.
AFTER:
import io
from flask import (Flask, abort, redirect, render_template,
request, send_file, url_for)
from werkzeug.utils import secure_filename
import google.auth
from google.cloud import exceptions, ndb, storage
초기화 및 불필요한 Jinja2 지원
바꿀 다음 코드 블록은 webapp2_extras에서 Jinja2 사용을 지정하는 BaseHandler입니다. Jinja2는 Flask와 함께 제공되며 기본 템플릿 엔진이므로 불필요합니다. 삭제하세요.
모듈 16에서는 이전 앱에 없던 객체를 인스턴스화합니다. 여기에는 Flask 앱 초기화와 Cloud NDB 및 Cloud Storage용 API 클라이언트 생성이 포함됩니다. 마지막으로 가져오기 섹션에서 설명한 대로 Cloud Storage 버킷 이름을 조합합니다. 업데이트를 구현하기 전후는 다음과 같습니다.
이전:
class BaseHandler(webapp2.RequestHandler):
'Derived request handler mixing-in Jinja2 support'
@webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(app=self.app)
def render_response(self, _template, **context):
self.response.write(self.jinja2.render_template(_template, **context))
AFTER:
app = Flask(__name__)
ds_client = ndb.Client()
gcs_client = storage.Client()
_, PROJECT_ID = google.auth.default()
BUCKET = '%s.appspot.com' % PROJECT_ID
Datastore 액세스 업데이트
Cloud NDB는 App Engine NDB와 대부분 호환됩니다. 이미 설명한 한 가지 차이점은 API 클라이언트가 필요하다는 것입니다. 후자의 경우 API 클라이언트의 Python 컨텍스트 관리자가 Datastore 액세스를 제어해야 합니다. 즉, Cloud NDB 클라이언트 라이브러리를 사용하는 모든 Datastore 액세스 호출은 Python with 블록 내에서만 발생할 수 있습니다.
한 가지 변경사항은 Blobstore와 그 객체(예: BlobKey)가 Cloud Storage에서 지원되지 않으므로 file_blob을 ndb.StringProperty로 변경해야 한다는 것입니다. 아래는 데이터 모델 클래스와 이러한 변경사항을 반영하여 업데이트된 store_visit() 및 fetch_visits() 함수입니다.
이전:
class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
file_blob = ndb.BlobKeyProperty()
def store_visit(remote_addr, user_agent, upload_key):
'create new Visit entity in Datastore'
Visit(visitor='{}: {}'.format(remote_addr, user_agent),
file_blob=upload_key).put()
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)
file_blob = ndb.StringProperty()
def store_visit(remote_addr, user_agent, upload_key):
'create new Visit entity in Datastore'
with ds_client.context():
Visit(visitor='{}: {}'.format(remote_addr, user_agent),
file_blob=upload_key).put()
def fetch_visits(limit):
'get most recent visits'
with ds_client.context():
return Visit.query().order(-Visit.timestamp).fetch(limit)
지금까지 적용된 변경사항을 그림으로 나타내면 다음과 같습니다.

핸들러 업데이트
업로드 핸들러
webapp2의 핸들러는 클래스인 반면 Flask에서는 함수입니다. HTTP 동사 메서드 대신 Flask는 동사를 사용하여 함수를 데코레이션합니다. Blobstore와 webapp 핸들러는 Cloud Storage와 Flask 및 해당 유틸리티의 기능으로 대체됩니다.
이전:
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
'Upload blob (POST) handler'
def post(self):
uploads = self.get_uploads()
blob_id = uploads[0].key() if uploads else None
store_visit(self.request.remote_addr, self.request.user_agent, blob_id)
self.redirect('/', code=307)
AFTER:
@app.route('/upload', methods=['POST'])
def upload():
'Upload blob (POST) handler'
fname = None
upload = request.files.get('file', None)
if upload:
fname = secure_filename(upload.filename)
blob = gcs_client.bucket(BUCKET).blob(fname)
blob.upload_from_file(upload, content_type=upload.content_type)
store_visit(request.remote_addr, request.user_agent, fname)
return redirect(url_for('root'), code=307)
이 업데이트와 관련된 참고사항은 다음과 같습니다.
- 이제 파일 아티팩트는
blob_id대신 파일 이름 (fname)이 있는 경우 파일 이름으로, 그렇지 않은 경우None로 식별됩니다 (사용자가 파일 업로드를 선택 해제함). - Blobstore 핸들러는 사용자로부터 업로드 프로세스를 추상화했지만 Cloud Storage는 그렇지 않으므로 파일의 blob 객체와 위치 (버킷)를 설정하는 새로 추가된 코드와 실제 업로드를 실행하는 호출을 확인할 수 있습니다. (
upload_from_file()). webapp2는 애플리케이션 파일 하단의 라우팅 테이블을 사용하는 반면 Flask 경로는 장식된 각 핸들러에 있습니다.- 두 핸들러 모두 HTTP 307 반환 코드로
POST요청을 유지하면서 홈 (/)으로 리디렉션하여 기능을 래핑합니다.
다운로드 핸들러
다운로드 핸들러 업데이트는 업로드 핸들러와 유사한 패턴을 따르지만 살펴볼 코드가 훨씬 적습니다. Blobstore 및 webapp 기능을 Cloud Storage 및 Flask에 상응하는 기능으로 바꿉니다.
이전:
class ViewBlobHandler(blobstore_handlers.BlobstoreDownloadHandler):
'view uploaded blob (GET) handler'
def get(self, blob_key):
self.send_blob(blob_key) if blobstore.get(blob_key) else self.error(404)
AFTER:
@app.route('/view/<path:fname>')
def view(fname):
'view uploaded blob (GET) handler'
blob = gcs_client.bucket(BUCKET).blob(fname)
try:
media = blob.download_as_bytes()
except exceptions.NotFound:
abort(404)
return send_file(io.BytesIO(media), mimetype=blob.content_type)
이 업데이트에 관한 참고사항:
- 다시 말하지만 Flask는 핸들러 함수를 경로로 장식하는 반면
webapp는 하단의 라우팅 테이블에서 이를 수행하므로 후자의 패턴 일치 구문(('/view/([^/]+)?')과 Flask의 구문('/view/<path:fname>')을 구분해야 합니다. - 업로드 핸들러와 마찬가지로 Blobstore 핸들러에서 추상화한 기능, 즉 문제의 파일 (블롭)을 식별하고 Blobstore 핸들러의 단일
send_blob()메서드 호출과 달리 바이너리를 명시적으로 다운로드하려면 Cloud Storage 측에서 약간 더 많은 작업이 필요합니다. - 두 경우 모두 아티팩트를 찾을 수 없으면 HTTP 404 오류가 사용자에게 반환됩니다.
기본 핸들러
기본 애플리케이션의 최종 변경사항은 기본 핸들러에서 발생합니다. webapp2 HTTP 동사 메서드는 기능을 결합하는 단일 함수로 대체됩니다. MainHandler 클래스를 root() 함수로 바꾸고 아래와 같이 webapp2 라우팅 테이블을 삭제합니다.
이전:
class MainHandler(BaseHandler):
'main application (GET/POST) handler'
def get(self):
self.render_response('index.html',
upload_url=blobstore.create_upload_url('/upload'))
def post(self):
visits = fetch_visits(10)
self.render_response('index.html', visits=visits)
app = webapp2.WSGIApplication([
('/', MainHandler),
('/upload', UploadHandler),
('/view/([^/]+)?', ViewBlobHandler),
], debug=True)
AFTER:
@app.route('/', methods=['GET', 'POST'])
def root():
'main application (GET/POST) handler'
context = {}
if request.method == 'GET':
context['upload_url'] = url_for('upload')
else:
context['visits'] = fetch_visits(10)
return render_template('index.html', **context)
별도의 get() 및 post() 메서드 대신 root()의 if-else 문입니다. 또한 root()는 단일 함수이므로 GET와 POST 모두에 템플릿을 렌더링하는 호출이 하나만 있는 반면 webapp2에서는 불가능합니다.
다음은 main.py의 두 번째이자 마지막 변경사항을 그림으로 나타낸 것입니다.

(선택사항) 하위 호환성 '향상'
따라서 위에서 만든 솔루션은 처음부터 시작하고 Blobstore에서 만든 파일이 없는 경우에만 완벽하게 작동합니다. BlobKey 대신 파일 이름으로 파일을 식별하도록 앱을 업데이트했으므로 완료된 모듈 16 앱은 Blobstore 파일을 볼 수 없습니다. 즉, 이 이전을 수행하면서 하위 호환되지 않는 변경사항이 적용되었습니다. 이제 이 격차를 해소하기 위해 main-migrate.py이라는 main.py의 대체 버전이 제공됩니다 (저장소에 있음).
Blobstore 생성 파일을 지원하는 첫 번째 '확장 프로그램'은 Cloud Storage 생성 파일의 StringProperty 외에 BlobKeyProperty가 있는 데이터 모델입니다.
class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
file_blob = ndb.BlobKeyProperty() # backwards-compatibility
file_gcs = ndb.StringProperty()
file_blob 속성은 Blobstore에서 만든 파일을 식별하는 데 사용되고 file_gcs은 Cloud Storage 파일에 사용됩니다. 이제 새 방문을 만들 때 file_blob 대신 file_gcs에 값을 명시적으로 저장하므로 store_visit가 약간 다르게 표시됩니다.
이전:
def store_visit(remote_addr, user_agent, upload_key):
'create new Visit entity in Datastore'
with ds_client.context():
Visit(visitor='{}: {}'.format(remote_addr, user_agent),
file_blob=upload_key).put()
AFTER:
def store_visit(remote_addr, user_agent, upload_key):
'create new Visit entity in Datastore'
with ds_client.context():
Visit(visitor='{}: {}'.format(remote_addr, user_agent),
file_gcs=upload_key).put()
최근 방문을 가져올 때는 템플릿에 전송하기 전에 데이터를 '정규화'합니다.
이전:
@app.route('/', methods=['GET', 'POST'])
def root():
'main application (GET/POST) handler'
context = {}
if request.method == 'GET':
context['upload_url'] = url_for('upload')
else:
context['visits'] = fetch_visits(10)
return render_template('index.html', **context)
AFTER:
@app.route('/', methods=['GET', 'POST'])
def root():
'main application (GET/POST) handler'
context = {}
if request.method == 'GET':
context['upload_url'] = url_for('upload')
else:
context['visits'] = etl_visits(fetch_visits(10))
return render_template('index.html', **context)
다음으로 file_blob 또는 file_gcs (또는 둘 다 아님)의 존재를 확인합니다. 사용 가능한 파일이 있으면 기존 파일을 선택하고 해당 식별자 (Blobstore에서 생성된 파일의 경우 BlobKey, Cloud Storage에서 생성된 파일의 경우 파일 이름)를 사용합니다. 'Cloud Storage 생성 파일'은 Cloud Storage 클라이언트 라이브러리를 사용하여 생성된 파일을 의미합니다. Blobstore는 Cloud Storage에도 쓰지만 이 경우 Blobstore에서 생성된 파일이 됩니다.
이제 더 중요한 것은 최종 사용자를 위해 데이터를 정규화하거나 ETL (추출, 변환, 로드)하는 데 사용되는 etl_visits() 함수가 무엇인가입니다. 상태 메시지가 표시됩니다.
def etl_visits(visits):
return [{
'visitor': v.visitor,
'timestamp': v.timestamp,
'file_blob': v.file_gcs if hasattr(v, 'file_gcs') \
and v.file_gcs else v.file_blob
} for v in visits]
예상한 대로 코드에서 모든 방문을 반복하고 각 방문에 대해 방문자 및 타임스탬프 데이터를 그대로 가져온 다음 file_gcs 또는 file_blob가 있는지 확인하고 있는 경우 둘 중 하나 (또는 둘 다 없는 경우 None)를 선택합니다.
다음은 main.py와 main-migrate.py의 차이점을 보여주는 그림입니다.

Blobstore에서 만든 파일 없이 처음부터 시작하는 경우 main.py를 사용하고, 전환 중이며 Blobstore와 Cloud Storage 모두에서 만든 파일을 지원하려는 경우 main-migrate.py를 참고하여 자체 앱의 이전 계획을 세우세요. 복잡한 이전의 경우 특수한 사례가 발생할 수 있으므로 이 예는 실제 데이터로 실제 앱을 현대화하는 데 더 적합합니다.
6. 요약/삭제
이 섹션에서는 앱을 배포하고 의도한 대로 작동하는지, 반영된 출력이 있는지 확인하여 이 Codelab을 마무리합니다. 앱 검증 후 정리 단계를 실행하고 다음 단계를 고려합니다.
애플리케이션 배포 및 확인
앱을 다시 배포하기 전에 pip install -t lib -r requirements.txt를 실행하여 자체 번들 서드 파티 라이브러리를 lib 폴더에 가져와야 합니다. 이전 버전과 호환되는 솔루션을 실행하려면 먼저 main-migrate.py을 main.py로 이름을 바꿉니다. 이제 gcloud app deploy를 실행하고 앱이 모듈 15 앱과 동일하게 작동하는지 확인합니다. 양식 화면은 다음과 같습니다.

최근 방문 페이지는 다음과 같이 표시됩니다.

App Engine Blobstore를 Cloud Storage로, App Engine NDB를 Cloud NDB로, webapp2를 Flask로 대체하는 Codelab을 완료하신 것을 축하드립니다. 이제 코드는 FINISH (모듈 16) 폴더에 있는 것과 일치해야 합니다. 대체 main-migrate.py도 해당 폴더에 있습니다.
Python 3 '마이그레이션'
app.yaml 상단의 주석 처리된 Python 3 runtime 지시문은 이 앱을 Python 3로 포팅하는 데 필요한 전부입니다. 소스 코드 자체가 이미 Python 3와 호환되므로 변경할 필요가 없습니다. Python 3 앱으로 배포하려면 다음 단계를 실행하세요.
app.yaml상단에서 Python 3runtime지시문을 주석 해제합니다.app.yaml에서 다른 모든 줄을 삭제합니다.appengine_config.py파일을 삭제합니다. (Python 3 런타임에서 사용되지 않음)lib폴더가 있으면 삭제합니다. (Python 3 런타임에서는 불필요)
삭제
일반
지금까지 완료한 경우 청구가 발생하지 않도록 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에만 해당합니다. 자세한 내용은 각 제품의 문서를 참고하세요.
- App Engine Blobstore 서비스는 저장된 데이터 할당량 및 한도에 해당하므로 해당 페이지와 기존 번들 서비스 가격 책정 페이지를 검토하세요.
- Cloud Storage는 특정 지역에 무료 등급을 제공합니다. 자세한 내용은 일반 가격 책정 페이지를 참고하세요.
- App Engine Datastore 서비스는 무료 등급도 있는 Cloud Datastore (Datastore 모드의 Cloud Firestore)에서 제공합니다. 자세한 내용은 가격 책정 페이지를 참고하세요."
모듈 15에서 16으로 이전한 경우 Blobstore에 데이터가 남아 있으므로 위의 가격 정보에 포함됩니다.
다음 단계
이 튜토리얼 외에도 기존 번들 서비스에서 벗어나는 데 중점을 둔 다른 이전 모듈은 다음과 같습니다.
- 모듈 2: App Engine
ndb에서 Cloud NDB로 마이그레이션 - 모듈 7~9: App Engine 태스크 큐 푸시 작업에서 Cloud Tasks로 마이그레이션
- 모듈 12~13: App Engine Memcache에서 Cloud Memorystore로 이전
- 모듈 18~19: App Engine 태스크 큐 (풀 태스크)에서 Cloud Pub/Sub로 이전
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에서는 고려해야 할 이전과 관련 '순서'의 이전 모듈에 관한 안내도 제공합니다.
7. 추가 리소스
Codelab 문제/의견
이 Codelab에 문제가 발견된 경우 문제를 기록하기 전에 먼저 비슷한 기록이 있는지 검색해보세요. 검색 및 새 문제 만들기 링크:
마이그레이션 리소스
모듈 15 (시작) 및 모듈 16 (완료)의 저장소 폴더 링크는 아래 표에서 찾을 수 있습니다. 또한 클론 또는 ZIP 파일로 다운로드할 수 있는 모든 App Engine Codelab 마이그레이션 저장소에서 액세스할 수도 있습니다.
Codelab | Python 2 | Python 3 |
모듈 15 | 해당 사항 없음 | |
모듈 16 (이 Codelab) | (Python 2와 동일) |
온라인 리소스
다음은 이 튜토리얼과 관련이 있을 수 있는 온라인 리소스입니다.
App Engine Blobstore 및 Cloud Storage
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 문서
Python
- Django 및 Jinja2 템플릿 시스템
webapp2웹 프레임워크webapp2문서webapp2_extras링크webapp2_extrasJinja2 문서- Flask 웹 프레임워크
동영상
라이선스
이 작업물은 Creative Commons Attribution 2.0 일반 라이선스에 따라 사용이 허가되었습니다.