Cloud Functions + Flaskで動的にHTMLを作成する

プログラミング

「Webページを公開したいが、ほとんど使わないものにはあまり費用をかけたくない。」
そんなときにはGCP「Cloud Functions」やAWS「Lambda」などのサーバレス技術を活用すれば、基本的に固定費はかからず、アクセス数に応じた課金だけでWebページを配信することができます。

サーバレスで固定のHTMLを配信する方法はよく見かけますが、動的にWebページを作成する方法はあまり見かけなかったので、この記事では Cloud Functions + Flask を使って、アクセス時のパラメータでWebページの内容を動的に変更する方法を紹介します。

※ここではCloud Functionsをデプロイするまでの「手順」の説明となります。GCP、Python、Flaskに関する技術的な内容はご自身で学習してください。

対象読者

  • 動的なWebコンテンツ(Webアプリ)を、費用を抑えながら公開したい
  • Python、Flaskに関して多少の知識があるのが望ましい

 GCPプロジェクトを作成する

GCP コンソール(https://console.cloud.google.com/)にアクセスし、Webコンテンツを公開するためのプロジェクトを作成する。

①プロジェクト名の横の三角マークをクリックし、プロジェクトの選択を表示する。

GCPのメニューからプロジェクト名の横の三角をクリック
プロジェクト名の横の三角をクリック

②「プロジェクトの選択」で「新しいプロジェクト」を選択する。

「プロジェクトの選択」で「新しいプロジェクト」をクリック
新しいプロジェクトをクリック

③「新しいプロジェクト」でプロジェクト名や組織を設定して作成する。

プロジェクト名、組織を設定し、プロジェクトを作成する
任意のプロジェクトを作成する

以降は、ここで作成したプロジェクトに設定を行っていく。

プロジェクトに請求アカウントを設定する

Cloud Functionsでも料金は発生する可能性があるため、プロジェクトに請求アカウントを設定しておく必要がある。

①「プロジェクトの選択」で作成したプロジェクトを開く。後で必要になるので、このときプロジェクトIDの値を確認しておく。

プロジェクトを選択する。プロジェクトIDも確認しておく。
プロジェクトを開く。プロジェクトIDも確認しておく。

②GCPの検索ボックスに「請求」と入力し「請求アカウント」を開く。

③この時点では「このプロジェクトには請求先アカウントがありません」と表示されるので、「請求先アカウントをリンク」をクリックする。

「請求先アカウントをリンク」をクリックする。
「請求先アカウントをリンク」をクリックする。

④任意の請求先アカウントを設定する。

請求先アカウントを設定する
請求先アカウントを設定する

コンテンツ用ファイルを作成する

WebコンテンツはGCP上のFlaskで生成して表示させる。
今回のサンプルでは、以下のファイル構成でGCP上にFlask用のファイルを作成する。

  • ~/func1/ … ホームディレクトリにWebアプリ用のディレクトリを作成しておく
    • main.py … Webアプリケーションのプログラム
    • templates … Flaskのテンプレートファイル用のディレクトリ
      • template1.html … テンプレートファイル その1
      • template2.html … テンプレートファイル その2

①ファイルの作成はCloud Shellで行います。GCP コンソールのメニューからCloud Shellを起動する。

GCPのメニューからCloud Shellをクリック
GCPのメニューからCloud Shellをクリック

②Cloud Shellを起動すると、シェルの「$」の前のカッコ内はさきほど作成したプロジェクトのID(上記で確認した値)になっているはず。
もしもプロジェクトIDになっていない場合は、Cloud Shellで下記のコマンドを実行する。
このときに承認要求が出てきたら「承認」を選ぶ。

$gcloud config set project プロジェクトID(上記で確認した値)
承認の要求が出てきたら「承認」を選択する
承認の要求が出てきたら「承認」を選択する

main.pyを作成する

③ホームディレクトリにfunc1ディレクトリを作成するため、Cloud Shellで以下のコマンドを実行する。

$mkdir ~/func1

④func1ディレクトリ内にmain.pyを作成するため、Cloud Shellで以下のコマンドを実行してテキストエディタを開く。

$nano ~/func1/main.py

⑤テキストエディタに、下記「main.pyの内容」の内容をコピペする。

main.pyの内容

from flask import render_template, Flask

app = Flask(__name__)

@app.route("/", methods=["GET"])
def webapp(request):
	# URLのパラメータ「tno」でテンプレートファイルを切り替える
	template_no = request.args.get('tno')

	if template_no == "2":
		# URLのパラメータ「id」をテンプレートファイルに埋め込む
		return render_template('template2.html', id=request.args.get('id'))
	else:
		# URLのパラメータ「id」をテンプレートファイルに埋め込む
		return render_template('template1.html', id=request.args.get('id'))

if __name__ == "__main__":
    app.run(debug=False, host='0.0.0.0', port=80)

⑥貼り付けたら、キーボードで Ctrl + x → y → Enter の順に操作し、テキストエディタを終了させる。

以上でmain.pyが作成される。

template1.htmlを作成する

⑦~/func1ディレクトリにtemplatesディレクトリを作成するため、Cloud Shellで以下のコマンドを実行する。

$mkdir ~/func1/templates

⑧templatesディレクトリにtemplate1.htmlを作成するため、Cloud Shellで以下のコマンドを実行してテキストエディタを開く。

$nano ~/func1/templates/template1.html

⑨テキストエディタに、下記「template1.htmlの内容」をコピペする。

template1.htmlの内容

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>template1</title>
</head>
<body>
  <h1>テンプレートファイル1</h1>
  受け取ったidは {{id}} です。
</body>
</html>

⑩貼り付けたら、キーボードで Ctrl + x → y → Enter の順に操作し、テキストエディタを終了させる。

以上でtemplate1.htmlが作成される。

template2.htmlを作成する

⑪templatesディレクトリにtemplate2.htmlを作成するため、Cloud Shellで以下のコマンドを実行してテキストエディタを開く。

$nano ~/func1/templates/template2.html

⑫テキストエディタに、下記「template2.htmlの内容」をコピペする。

template2.htmlの内容

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>template2</title>
</head>
<body>
  <h1>テンプレートファイル2</h1>
  受け取ったidは {{id}} です。
</body>
</html>

⑬貼り付けたら、キーボードで Ctrl + x → y → Enter の順に操作し、テキストエディタを終了させる。

以上でtemplate2.htmlが作成される。

デプロイする

①Cloud Shellで以下のコマンドを実行して、Cloud Functionsをデプロイする。

$cd ~/func1
$gcloud beta functions deploy webapp --runtime python37 --trigger-http

②以下のような確認メッセージ(2種類)が表示されたら、どちらもyを入力する。

API [cloudfunctions.googleapis.com] not enabled on project [xxxxxxxxxxx]. Would you like to enable
and retry (this will take a few minutes)? (y/N)? 
Allow unauthenticated invocations of new function [webapp]? (y/N)?

③以下のようなエラーメッセージが表示されたら、メッセージ内のURLをクリックする。Cloud Build APIのページが表示されるので「有効にする」をクリックすると1分程度で有効になり、ページが切り替わる。APIが有効になったら、再度デプロイのコマンドを実行する。
有効化した直後にコマンドを再実行すると同じエラーメッセージが出ることがある。その場合は数分待ってからコマンドを実行する。

ERROR: (gcloud.beta.functions.deploy) OperationError: code=7, message=Build failed: Cloud Build API has not been used in project xxxxxxxxxxxx before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudbuild.googleapis.com/overview?project=xxxxxxxxxxxx then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
Cloud Build APIを有効にする
Cloud Build APIを有効にする

デプロイに成功すると以下のようなメッセージが表示される。
この「url」欄がデプロイしたCloud FunctionsのURLになる。

availableMemoryMb: 256
buildId: xxxxx
buildName: projects/xxxxxxxxxxx/locations/us-central1/builds/xxxxx
dockerRegistry: CONTAINER_REGISTRY
entryPoint: webapp
httpsTrigger:
  securityLevel: SECURE_OPTIONAL
  url: https://us-central1-samplefunction-xxxxxx.cloudfunctions.net/webapp
ingressSettings: ALLOW_ALL
labels:
  deployment-tool: cli-gcloud
name: projects/samplefunction-xxxxxx/locations/us-central1/functions/webapp
runtime: python37
serviceAccountEmail: samplefunction-xxxxxx@appspot.gserviceaccount.com
sourceUploadUrl: xxxxxx
status: ACTIVE
timeout: 60s

Cloud Functionsにアクセスする

上記で発行されたURLにアクセスすれば、デプロイしたCloud Functionsを利用できる。
今回のサンプルでは、URLに「tno」、「id」のパラメータを指定することで表示するWebページの内容が以下のように動的に変わる。

  • tno
    • パラメータに「2」を指定すると、テンプレートファイルとして「template2.html」を使用する。パラメータがそれ以外の値なら「template1.html」を使用する。
  • id
    • テンプレートファイル内の{{id}}の部分を、このパラメータに指定した値で置き換える。

例えば、「template2.html」を使用して、テンプレートファイル内の{{id}}の部分を「123」に置き換える場合には、URLを以下のように指定する。

https://us-central1-samplefunction-xxxxxx.cloudfunctions.net/webapp?tno=2&id=123

パラメータによって動的にWebページの内容が変わる
パラメータによって動的にWebページの内容が変わる

さいごに

以上の様に、Cloud Functionsを使えば動的なWebコンテンツをサーバレスで配信できるので、Webサーバを稼働し続けるまでもないようなWebアプリには便利ですね。

今回紹介したソースファイルが、ご自身のコンテンツ配信アプリの参考になれば幸いです。

お疲れ様でした。

プログラミングGCP

Posted by moto2g