Cloud SchedulerでCloud Functionsを起動する
Cloud SchedulerからCloud Functionsを起動しようとしたところ、認証部分でハマってしまったため、備忘を兼ねて設定手順を記録しました。
やったこと
Cloud Functions上にagraffe
を使ってAPIサーバを構築し、Cloud Schedulerで定期的に呼び出しを行うジョブを作成しました。Cloud Functions第二世代を用いています。
手順
以下の手順で構築します。
- Cloud Functionsへの関数のデプロイ
- Cloud Scheduler Jobの作成
- HTTPターゲットでの認証に用いるサービスアカウントを作成する
- サービスアカウントにCloud Functionsの呼び出しを許可する
- Scheduler Jobを作成する
1. Cloud Functionsへの関数のデプロイ
今回デプロイするコードは以下です。
agraffe
を用いてCloud Function上にAPIサーバを構築します。
agraffe==0.7.0
fastapi==0.108.0
from agraffe import Agraffe
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello/{hello_id}")
def get_hello_world(hello_id: str):
print("hello:", hello_id)
@app.post("/hell/{hell_id}")
def get_hell_world(hell_id: str):
print("hell:", hell_id)
entry_point = Agraffe.entry_point(app)
AuthenticationはRequire authentication
を選択し、関数の呼び出しは認証付きとします。
今回はagraffe
を用いているため、Entry pointはentry_point
を指定します(参考: agraffe)。
2. Cloud Scheduler Jobの作成
Cloud Functionsの関数を呼び出すCloud Scheduler Jobを作成します。
今回はHTTPターゲットを使用するので、認証で用いるサービスアカウントを作成します。
HTTPターゲットでの認証に用いるサービスアカウントを作成する
今回は invoke-gcf
というサービスアカウントを作成しました。ロールは空のまま作成しています。
サービスアカウントにCloud Functionsの呼び出しを許可する
関数の呼び出しを行うサービスアカウントにCloud Functionsの呼び出しを許可する必要があります。
Cloud FunctionsはCloud Runの上で動いています。権限の付与はCloud Runのコンソールから行います。先ほど作成したサービスアカウントにCloud Run Invokerを許可します。
手順は「HTTP ターゲットで認証を使用する」の「サービス アカウントを設定する」に記載があります。
Scheduler Jobを作成する
Cloud Functionsを起動するジョブを作成します。
Configure the executionを下記の通り設定します。
-
Target type:
HTTP
HTTPターゲットを使用するためHTTP
を指定します。 -
URL:
https://region-project-id.cloudfunctions.net/gcf_id/hello/id12345
Cloud Functionsを呼び出す際のURLを入力します。
今回は/hello/{hello_id}
を呼び出したいので上記のURLとしています。 -
HTTP method:
GET
-
HTTP headers
Auth headerのみ設定します。
他のヘッダは空のままでもUser-Agent: Google-Cloud-Scheduler
が自動で付与されます。
Auth headerの設定は下記の通りです。-
Auth header:
Add OIDC token
こちらを参考にOIDCを指定しています。 -
Service account:
invoke-gcf
先ほど作成したサービスアカウントを指定します。
HTTP呼び出しの際の認証に使用されます。 -
Audience:
https://region-project-id.cloudfunctions.net/gcf_id
こちらを参照しました。通常は、ジョブのターゲット URL(URL パラメータなし)です。
とのことなので、パスパラメータ
/hello/id12345
を除いています。
因みにCloud RunのURLhttps://gcf-id-xxxxxxxxxx-uc.a.run.app
でも動作します。
未入力の場合、デフォルトでリクエスト先のURLが入力されます。今回はパスパラメータ/hello/id12345
がついているため、これを除かないと下記の401
エラーが発生します(参考)。The request was not authorized to invoke this service. Read more at https://cloud.google.com/run/docs/securing/authenticating Additional troubleshooting documentation can be found at: https://cloud.google.com/run/docs/troubleshooting#401
-
ここまで完了すると、Cloud SchedulerのジョブからCloud Functionsを起動することができます。
手順は以上です。
Audienceについて
今回ハマったのがSchedulerのAudienceの設定間違いだったため、Audienceは何に使われるのか、少し調べてみました。サービス間認証のページの認証サンプルコードが参考になりました。
実際にサンプルコードを参考に作成したスクリプトが下記です。
import os
import requests
import google.auth.transport.requests
import google.oauth2.service_account
def make_authorized_get_request(endpoint: str, audience: str, sa_creds_path: str) -> requests.models.Response:
# ID Tokenの取得
## audienceとサービスアカウントのクレデンシャルを使ってIDトークンを取得している
creds = google.oauth2.service_account.IDTokenCredentials.from_service_account_file(
sa_creds_path,
target_audience=audience,
)
# HTTP GETリクエスト
## endpoint宛にHTTPリクエストを送信している
authed_session = google.auth.transport.requests.AuthorizedSession(creds)
response = authed_session.get(endpoint)
return response
if __name__ == "__main__":
endpoint = "https://region-project-id.cloudfunctions.net/gcf-id/hello/hello54321"
audience = "https://region-project-id.cloudfunctions.net/gcf-id"
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "./credential.json"
response = make_authorized_get_request(
endpoint, audience,
os.environ["GOOGLE_APPLICATION_CREDENTIALS"],
)
参照したサンプルコードではurllib
を使用していますが、上記ではAuthorizedSession
を用いています。AuthorizedSession
はrequests.Session
を継承しています。
また、サンプルコードではfetch_id_token
を用いているところを上記ではIDTokenCredentials.from_service_account_file
を用いています。fetch_id_token
はIDTokenCredentials.from_service_account_info
を使って実装されているため、上記コードもほぼ同じ意味となっています。
上記のコードからわかるように、Audience (audience
) はIDトークン取得のために使われ、Audienceを使って取得したIDトークンを用いてURL (endpoint
) に対してリクエストを送出しています。
一旦ここまでで自分としてはAudienceが何かについて納得することはできました。
この先はOpen ID Connectについて理解を深める必要がありそうなため、本記事はここまでとし詳細は別記事とします (作成するかどうかはわかりません)。
以下ページが参考になりそうです。
- Google Identity OAuth 2.0 の実装について
- Qiita: OpenID Connect 全フロー解説
- Qiita: 一番分かりやすい OpenID Connect の説明
- Qiita: IDトークンが分かれば OpenID Connect が分かる
終わりに
以上、Cloud Scheduerから認証付きのCloud Functionを呼び出す際の手順についての記録でした。
Discussion