Blog Entry  (Aug. 3, 2019, 12:11 p.m.)

Tilo Mitra's avatar

Google Cloud Functions (Python3) を使ってみた

Google Cloud Functions で Python3 が使えるということだったので使ってみました。実際のプロダクトで使うことも考えて、環境変数の扱い、ローカルでの開発についても説明しています。


なお、この記事はタスク管理にGitHubを使う一連の記事の中の一つです。

前提

以下のことが完了している前提で説明します。

クイックスタート

まずは簡単に関数を作ってみます。

関数の作成

https://console.cloud.google.com/functions

スクリーンショット 2019-07-31 21.31.23.png

設定

今回指定した設定は以下の表のとおりです。

項目
メモリ 256 MB
トリガー HTTP
ランタイム Python 3.7
ソースコード インライン エディタ
実行する関数 hello_world

スクリーンショット-2019-07-31-21.32.44.png

確認

Google Cloud Platform のコンソール画面からも確認できますが、 gcloud でログインしていればローカルからのコマンドでも確認できます。

$ gcloud functions list

結果

NAME        STATUS  TRIGGER       REGION
function-1  ACTIVE  HTTP Trigger  us-central1

実行

実行のためのトリガーは以下のように複数の選択肢があります。

  • HTTP
  • Cloud Storage
  • Cloud Pub/Sub
  • Firebase

今回はHTTPのトリガーを作成したので、ブラウザや curl コマンドで直接 GET リクエストを送って確認できます。URLはコンソール画面でに表示されています。

スクリーンショット-2019-07-31-21.41.52.png

スクリーンショット-2019-07-31-21.41.58.png

Python3 関連ファイル

Python3で Cloud Functions を作成すると、2つのファイルができています。

  • main.py
  • requirements.txt

実行したい関数は main.py の中に適当な名前で実装し、「実行する関数」でその名前を指定すると実行できます。サードパーティ製の必要なライブラリがあったら requirements.txt に書いておけばデプロイしたときに自動的にインストールしてくれます。また、Pythonファイルは一つだけではなくPythonパッケージを作成して main.py から import することもできます。

スクリーンショット-2019-08-02-21.32.24.png

ローカルからのデプロイ

できるならコードを毎回ブラウザ上のエディタではなくローカルで開発してGit管理するほうが望ましいと思われます。Cloud Source Repositories を使うと Google Cloud Platform 上での管理もできますが、ここではGitHubを想定します。

gcloudコマンドによるデプロイ

とりあえずコピーしてくるなり何なりして同様の環境をローカルに作り、ローカルからデプロイコマンドを実行すればデプロイできます。

$ gcloud functions deploy function-1 --trigger-http --runtime=python37

少し変更を加えて先程のエンドポイントにアクセスするとローカルからのデプロイができていることがわかると思います。

不要なファイルをアップロードしないようにする

ローカルに適当なファイルを置いておけばコマンド一つでデプロイしてくれるのですが、何もしなければGit管理用のファイルなど、関係ないファイルまでアップロードしてしまいます。

そこで .gcloudignore というファイルを作成しておきます。.gitignore のように、ここにアップロードしたくないファイルやディレクトリを記載しておけば、デプロイの際にアップロードされることがありません。

なお、 .gcloudignore から他のファイルを参照することもできるので、 .gitignore を参照すると良いかもしれません。

.gcloudignore
.gitignore
Makefile
README.md
requirements_local.txt
test/
venv/

#!include:.gitignore

ローカルでの開発

簡単にデプロイして確認ができる Cloud Functions ですが、毎回アップロードしなければ確認できないのは不便です。そのためにローカルでエミュレートする仕組みを作ります。

仮想環境の作成 (venv)

必須ではありませんが、必要に応じて仮想環境を用意しておくと便利です。

$ python3 venv venv
$ source venv/bin/activate

Flaskによるエミュレート

Google Cloud Function で始めに実行される関数は、Flaskの flask.Request オブジェクトを受け取ります。ローカルで開発する際にはlocalhostにFlaskの開発用サーバーが立ち上がるようにすることでエミュレートできます。

ライブラリのインストール

$ pip install flask

実装

main.py
from flask import Flask, request


def hello_world(request):
    # ...
    return 'HelloWorld!'


if __name__ == "__main__":
    app = Flask(__name__)

    @app.route('/')
    def index():
        return hello_world(request)

    app.run('127.0.0.1', 8000, debug=True)

このように、 main.py が直接呼ばれた場合は開発用サーバーが立ち上がり、 localhost:8000 にアクセスすると hello_world が実行されます。

環境変数

作成するアプリケーションによっては、他のサービスのAPIキーなど、ソースコードに書きたくない情報を含んでいる場合があります。そういった場合には環境変数を設定することができます。

設定方法

コマンドの引数を使う方法

一つの方法は、デプロイする際にコマンドの引数で指定する方法です。

$ gcloud functions deploy function-1 --trigger-http --runtime=python37 --set-env-vars FOO=bar,BAZ=boo

スクリーンショット-2019-08-03-13.31.36.png

Yamlを使う方法

env.yaml
FOO: bar
BAZ: boo
$ gcloud functions deploy function-1 --trigger-http --runtime=python37 --env-vars-file=./env.yaml

読み込み

Google Cloud Function に設定した環境変数を使う方法は自分でサーバーを立ち上げたときなど、他の環境と同じです。

import os


FOO = os.getenv('FOO')  # 'bar'
BAZ = os.getenv('BAZ')  # 'boo'

Yamlを使うときの運用方法

コマンドの引数を使う方法とYamlを使う方法がありますが、どちらかといえばファイルで管理できるYamlを使うほうが好みです。しかし、Git管理はしたくないので、そのために少し工夫を加えます。

ローカルでの確認

Yamlで環境変数を設定する場合は、ローカル環境でもそのYamlを読み込むようにすると便利です。そこで以下のようにYamlから環境変数を設定するように手を加えます。

ライブラリのインストール
$ pip install PyYAML

※ ImportError にならないようにするために requirements.txt にもPyYAMLを書き加えておきます。

実装
import os
import yaml


try:
    # local
    with open('./env.yaml') as f:
        os.environ.update(yaml.load(f))
except FileNotFoundError as e:
    # Google Cloud Functions
    pass


FOO = os.getenv('FOO')  # 'bar'
BAZ = os.getenv('BAZ')  # 'boo'

Google Cloud Storage を使う

Git管理をしたくないので、適当な Google Cloud Storage のバケットを作って、そこに env.yaml を保存しておき、デプロイする際にコピーしてくるようにします。 .gitignore に env.yaml を書き加えるのも忘れずに。

アップロード
$ gsutil cp ./env.yaml gs://[bucket_name]/env.yaml
ダウンロード
$ gsutil cp gs://[bucket_name]/env.yaml ./env.yaml

ちなみに gsutil コマンドは Google Cloud SDK をインストールしたときにインストールされているかと思います。

環境変数を使わない運用

公開したくない情報をGit管理しないために環境変数を使う方法もありますが、 Google Cloud Storage を直接 Google Cloud Function から読み込んで環境変数を使わない方法もあります。

IAMの設定

Google Cloud Functions から Cloud Storage の情報を取得するために、必要に応じてサービスアカウントに対して権限を付与します。ちなみにサービスアカウントとは Google Cloud Platform の各サービスが、疑似ユーザーのように振る舞う仕組みです。

Google Cloud Functions のサービスアカウント

サービスアカウントはコンソール画面から確認できます。

スクリーンショット-2019-08-03-14.58.32.png

サービスアカウントに権限を付与

おそらくデフォルトのままでも大丈夫ですが、権限が足りない場合は「 IAM と管理 」から対象のサービスアカウントを編集して↓の権限を付与します。

ストレージ→ストレージ オブジェクトの閲覧者

※ なお、これは「バケットレベルで権限を一様に設定(バケットポリシーのみ)」のアクセス制御モデルで作成したバケットを使用しています。

ライブラリのインストール

ここでは google-cloud-storage を利用してみます。

$ pip install google-cloud-storage

実装

google-cloud-storage を利用したときの実装例です。

import os
import yaml

from google.cloud import storage


def get_data():
    client = storage.Client()
    bucket = client.get_bucket('[bucket_name]')
    blob = storage.Blob('env.yaml', bucket)
    return yaml.load(blob.download_as_string())


os.environ.update(get_data())

FOO = os.getenv('FOO')  # 'bar'
BAZ = os.getenv('BAZ')  # 'boo'

ローカルでの開発

なお、ローカルで開発する場合は↓のようにログインする必要があります。

$ gcloud auth application-default login

元の記事へ