GCP Cloud Functionsでシークレット付きのPythonスクリプトを定期実行する手順
この記事は、Zennの記事投稿キャンペーン(テーマ:Google Cloud)の応募記事です。以前に自作したPythonスクリプトをGCP上で定期実行する手順を紹介します。
はじめに
2023年の1月に、「降水確率に応じてSwitchBotのスマートライトの色を変える」というPythonスクリプトを自作し、大変多くの反響をいただきました。
このPythonスクリプトはGitHub Actionsを利用して定期実行しているのですが、リポジトリを60日以上メンテしていないと実行が停止されてしまうという問題がありました。
そこで実行環境の移行先を色々と検討していたのですが、今回のZennの記事投稿キャンペーンのテーマがGCPということなので、GCPを使って実装してみることにしました。
筆者は主にAWSを利用しており、GCPはほとんど利用経験がありません。そこで今回は学習記録も兼ねて、シークレット付きのPythonスクリプトをGCP Cloud Functionsで定期実行する手順を紹介したいと思います。
システム構成
今回はCloud Functionsを活用して自作のPythonスクリプトを定期実行することにしました。公式ドキュメント等を参考にしつつ、以下のような構成にしました。
Cloud Shchedulerが毎日6時にCloud Functionsを起動します。AWSだとEventBridgeとLambdaを用いた構成になるでしょうか。
システム構成図
GCP対応後のPythonスクリプトはこちらです。
なお、それほど頻繁にアップデートするスクリプトではないので、CI/CD環境は整えていません。すべてコンソール画面上で設定します。
実行環境を構築する
まずはGCP上で実行環境を整えて、サンプルのPythonスクリプトを定期的に実行できるようにします。
以下の公式ドキュメントを参考にしながら進めます。基本的な手順はドキュメントを参照すればわかるので、この記事ではポイントとなる点に絞って説明します。
Cloud FunctionsとPub/Subを設定する
まずはドキュメントに従い、Cloud Functionsのファンクションを新規作成します。トリガーには「Cloud Pub/Sub」を選択します。
「トピックを作成する」をクリックするとCloud Pub/Subの新規トピック作成画面が開くので、トピックIDを入力して「作成」をクリックします。
Pub/Subのトピックの作成
これでトピックの作成が完了しました。Cloud Functionsの画面から離れずに作成ができるのは便利ですね。
ファンクションの構成は以下のようになりました。
ファンクションの構成
コードの作成画面ではランタイムに「Python 3.12」を選択し、そのまま「デプロイ」をクリックします。
ファンクションのソースコード
1,2分程度でデプロイが完了します。これで、作成したトピックIDに対してメッセージを送るとファンクションが起動するようになりました。
Cloud Schedulerを設定する
Cloud Shchedulerのコンソール画面に移動して、新しいジョブを作成します。今回のPythonスクリプトは毎朝6時に起動したいため、実行頻度はcron形式で「0 6 * * *
」と入力しました。
スケジュールの設定
ターゲットタイプには「Pub/Sub」を選択し、先ほど作成したトピックIDを指定します。今回はメッセージの内容は使わないので、メッセージ本文には適当な文言を入れておきます。
スケジュールから呼び出すPub/Subの設定
これで、毎朝6時に先ほど作成したファンクションが実行されるようになりました。
ジョブを強制的に実行してみると、ファンクションが正しく実行されていることがわかります。
強制実行結果のログ
PythonスクリプトをCloud Functionsから実行可能にする
次に、Cloud Functionsから自作のPyhtonスクリプトを呼び出せるように改修します。
基本的なコーディング方法は以下のドキュメントが参考になります。
まず、プロジェクトのルートに「main.py
」を作成し、以下のようなハンドラーを書きます。必要なコーディングはここだけです。
import functions_framework
from rainylight.rainylight import main
# Triggered from a message on a Cloud Pub/Sub topic.
@functions_framework.cloud_event
def pubsub_hundler(cloud_event):
return main()
# ここはローカル動作確認用のコード
if __name__ == "__main__":
main()
次に、GCPにアップロードするzipファイルを作成します。ここではシェルスクリプトを作っておきました。これを実行してzipファイルを作成しておきます。
#!/bin/bash
# requirements.txtを出力する
pipenv requirements > requirements.txt
# GCPにアップロードするzipファイルを生成する
zip -r gcp_source.zip main.py requirements.txt rainylight
AWSのLambdaでは外部モジュールもzipファイルに含めておく必要がありますが、GCPではrequirements.txt
に書いておくだけで良いのでお手軽ですね。
シークレット情報を登録する
このPythonスクリプトではSwitchBotのAPIを利用するため、APIキーなどのシークレット情報が必要です。
環境変数に入れておくことを考えましたが、環境変数は「APIキーなどの保存には適さない」とドキュメントに記載されていました。ここではベストプラクティスにしたがって「Secret Manager」を活用することにしました。
Secret Managerのコンソール画面の指示にしたがって、シークレット情報を登録します。
今回はAPIのアクセスに必要なSWITCHBOT_ACCESS_TOKEN
と
SWITCHBOT_SECRET
の2つのシークレットを作成しました。
シークレットの作成
ここで設定したシークレットはデプロイの際に呼び出します。
Pythonスクリプトをデプロイする
これで必要な準備が整ったので、Pythonスクリプトをファンクションにデプロイします。
環境変数を登録する
今回のスクリプトでは「操作対象のデバイスのID」と、「天気予報取得用の地域コード」を環境変数として与えています。これらの環境変数は、Cloud Fuctions上ではランタイム環境変数として登録できます。
ファンクションのデプロイの際に、以下のようにランタイム環境変数を登録します。
ランタイム環境変数の設定
また、先ほど作成したシークレット情報も環境変数として登録します。
「セキュリティとイメージのリポジトリ」の項目を開き、シークレットへの参照を1つずつ設定します。
シークレットの設定
これでデプロイのための設定は完了です。
zipファイルをアップロードする
最後に、先ほど作成したzipファイルを選択してアップロードします。「エントリポイント」はpubsub_hundler
に変更しておきます。
zipファイルのアップロード
「デプロイ」をクリックすると、デプロイが完了することを確認します。
なお、環境変数が足りていないなどの実行上の問題が発生すると、デプロイエラーとなります。
実行結果を確認する
もういちどGoogle Schedulerのジョブを強制実行して、Pythonスクリプトが正しく実行されることを確認します。
実行結果のログ
期待通りの動作になりました!
まとめ
GCPを本格的に触るのはこれがはじめてでしたが、公式ドキュメントが非常に充実していたため、スムーズに実装することができました。また、GCPはGoogleアカウントがあれば始められるので、その点ではAWSよりもお手軽に感じました。
Pythonスクリプトをクラウド上で実行できるようになると、今回のようなスマート家電のユースケースで役に立ちます。皆さんもそのようなニーズがある時は、GCPでの実行も選択肢に入れてみてはいかがでしょうか。
参考文献
Discussion