【GCP】Cloud FunctionsのHTTPSエンドポイント試してみた。

こんにちは。SCSKの林です!

AWS LambdaでHTTPSエンドポイントが使えるようになったようですね。

Google CloudのCloud Functionsの方は以前からHTTPトリガー機能があるのは知っていましたが、今回改めて検証してみました!

Cloud Functionsって?

サーバーやコンテナによる管理なしで、クラウドでコードを実行します。Cloud Functions はスケーラブルなシンプルな従量課金制 Functions as a Service(FaaS)プロダクトで、シンプルな単一目的のコードを使用してイベント ドリブン サービスを構築し接続できます。

出典:Cloud Functions

Cloud Functions | Google Cloud
Scalable pay-as-you-go Function-as-a-Service (FaaS) to run your code with zero server management.

ふむふむ。Google Cloudのサーバーレスでコードを実行できるサービスですね。つまりはAWSでいうところのAWS Lambda。

ベータ版がリリースされたのは2017年3月でその当時からHTTPトリガー(HTTPエンドポイント)は利用できたようです。

イベントトリガー

トリガーはこれら⇩が設定できるそうです。以下二つのイベントトリガーを選択することができます。

イベントトリガー種類
説明
HTTP関数
  • POST、PUT、GET、DELETE、OPTIONSの各HTTPメソッドを使用しHTTPリクエストによって呼び出す
  • クライアントからのリクエストに対して同期的に実行される
  • 最大一回の呼び出しで、リトライ無
バックグラウンド関数
  • 自動リトライの設定によって、一回以上の実行が行われる
  • 以下のトリガーを利用してCloud Functionsを実行した場合、バックグラウンド関数として実行される
    • Pub/Subメッセージ
    • Cloud Storage変更通知
    • Cloud Firestore:create、update、delete、writeイベント
    • Firebase向けアナリティクス
    • Firebase Realtime Database
    • Firebase Authentication

関数作ってみた

特に難しいことはなく、トリガーでHTTPを選択することでエンドポイントが作成できます。

認証の要否は作成時に指定した設定を後から変えることはできませんでした。

※ドキュメントを確認したらデプロイ後に未認証の呼び出しを許可する方法もありました。
(関数の権限設定で「allUsers」に「Cloud Functions起動元ロール」を付与)

IAM でアクセスを承認する  |  Google Cloud Functions に関するドキュメント

Hello World!

関数作成時にランタイムを選択すると、選択したランタイムのサンプルが自動で入力されます。

今回はPython3.9で試しました。

関数作成のコンソール画面

def hello_world(request):
    """Responds to any HTTP request.
    Args:
        request (flask.Request): HTTP request object.
    Returns:
        The response text or any set of values that can be turned into a
        Response object using
        `make_response <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>`.
    """
    request_json = request.get_json()
    if request.args and 'message' in request.args:
        return request.args.get('message')
    elif request_json and 'message' in request_json:
        return request_json['message']
    else:
        return f'Hello World!'

サンプルのソースコードをそのままデプロイします。

HTTPSエンドポイントURLは関数の詳細画面のトリガータブから確認できます。

URLは「https://<リージョン名>-<プロジェクトID>.cloudfunctions.net/<関数名>」になるようです。

関数の詳細コンソール画面

試しに叩いてみました。

$ curl https://asia-northeast1-xxxxxxxxxx.cloudfunctions.net/testFunction
Hello World!
$

パラメーターをつけるとこんな感じです。

<GET>
$ curl https://asia-northeast1-xxxxxxxxxx.cloudfunctions.net/testFunction?message=sendparam
sendparam
$
<POST>
$ curl -X POST -H 'Content-Type: application/json' -d '{ "message": "sendparam" }' \
> https://asia-northeast1-xxxxxxxxxx.cloudfunctions.net/testFunction
post_param
$

レスポンスヘッダーを表示するとこんな感じです。

$ curl -i https://asia-northeast1-xxxxxxxxxx.cloudfunctions.net/testFunction
HTTP/2 201
access-control-allow-origin: *
content-type: text/html; charset=utf-8
function-execution-id: xxxxxxxxxx
x-cloud-trace-context: xxxxxxxxxxxxxxxxxxxx;o=1
date: Fri, 15 Apr 2022 09:34:48 GMT
server: Google Frontend
content-length: 13

Hello World!!
$

ブラウザだとこんな感じ。

ブラウザ画面

CORSを有効にしてみる

CORS設定前

Google Chromeの開発者ツールを使って、ブラウザ上から動的にCloud Functionsへアクセスしてみると、以下のようにCORSエラーが発生します。

Google Chromeの開発者ツール画面①

CORS対応の方法については公式ドキュメントにコードサンプルが載っています。

HTTP CORS  |  Google Cloud Functions に関するドキュメント
Cloud Functions を使用して CORS 対応のリクエストを行う方法について説明します。

LambdaのようにGUI上で設定することはできないようですね。

def hello_world(request):
    request_json = request.get_json()
    headers = {
        'Access-Control-Allow-Origin': '*'
    }
    if request.args and 'message' in request.args:
        return request.args.get('message')
    elif request_json and 'message' in request_json:
        return request_json['message']
    else:
        return ("Hello World!!", 200, headers)
CORS設定後

エラーが消えました。

Google Chromeの開発者ツール画面②

IAM認証を有効にしてみる

cloudfunctions.functions.invoke 権限を付与されているアカウントのみ実行を許可します。

認証なしでアクセスするとはじかれます。

$ curl https://asia-northeast1-xxxxxxxxxx.cloudfunctions.net/testFunction-IAM
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>403 Forbidden</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Forbidden</h1>
<h2>Your client does not have permission to get URL <code>/testFunction-IAM</code> from this server.</h2>
<h2></h2>
</body></html>
$

コマンドラインだとわかりにくいので、ブラウザでアクセスするとこんな感じ。

ブラウザエラー画面

認証ありでアクセス

HeaderにIDトークンを含めることで認証できます。

$ curl  -H "Authorization: bearer $(gcloud auth print-identity-token)" \
  https://asia-northeast1-xxxxxxxxxx.cloudfunctions.net/testFunction-IAM
Hello World!
$

プログラムから呼び出す場合はサンプルコードが公式ドキュメントに載っているのでそちらを参照ください。

参考:呼び出しの認証 – プログラムによるトークンの生成

呼び出しを認証する  |  Google Cloud Functions に関するドキュメント

その他

接続タブにこんな設定がありました。

VPC内部など、限られたトラフィックのみ許可する設定ですね。

接続タブコンソール画面

「内部トラフィックのみ許可」を設定して実行してみたら、ちゃんとはじかれました。

$ curl -i https://asia-northeast1-xxxxxxxxxx.cloudfunctions.net/testFunction
HTTP/2 403
x-appengine-city: ?
x-appengine-country: TW
x-appengine-citylatlong: 0.000000,0.000000
x-appengine-region: ?
content-length: 235
content-type: text/html; charset=UTF-8
date: Fri, 15 Apr 2022 09:38:21 GMT


<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>403 Forbidden</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Forbidden</h1>
<h2>Access is forbidden.</h2>
<h2></h2>
</body></html>
$

ブラウザエラー画面

最後に

Cloud FunctionsのHTTPSエンドポイント機能を紹介しました!

個人的な感想としては、LambdaのHTTPSエンドポイントとそんなに変わらないかなーと感じました。

Cloud Functionsは今日紹介した以外にもCloud Storage、Pub/Subなど様々なサービスと簡単に統合できるので、ぜひいろいろな機能を試してみてください!!

タイトルとURLをコピーしました