GAE/Goが好きなのでだいたいGAEでやっちゃおうとするんですけど、せっかくなのでCloud Functions使ってみたいじゃないですか。
- Cloud Functions - Event-driven Serverless Computing | Google Cloud
- When to choose App Engine over Cloud Functions? - Stack Overflow
- What's the difference between Cloud Functions and App Engine in Google Cloud? - Quora
すげー雑に、かんたんなことはGCFでやりましょう、となる。知らんけど。ある1つのトリガーに対して連鎖的に外部APIをトリガーしたいとき、みたいなシンプルで軽い用途に向いている、的な雰囲気だと思う。
% mkdir -p ~/tmp/gcftest % cd ~/tmp/gcftest % gcloud components update % gcloud projects create otiai10-gcftest
# ファイルつくります % vi hello.js % cat hello.js exports.hello = (req, res) => { res.send(`Hello World!`); } # デプロイします % gcloud functions deploy hello --trigger-http --project otiai10-gcftest API [cloudfunctions.googleapis.com] not enabled on project [otiai10-gcftest]. Would you like to enable and retry? (y/N)? y # 中略 Deploying function (may take a while - up to 2 minutes)...failed. ERROR: (gcloud.functions.deploy) OperationError: code=3, message=Function load error: File index.js or function.js that is expected to define function does not exist in the root directory. # は? # function.js にする % ls -la % mv hello.js function.js # 再度デプロイを試みる % gcloud functions deploy hello --trigger-http --project otiai10-gcftest Deploying function (may take a while - up to 2 minutes)...done. availableMemoryMb: 256 entryPoint: hello httpsTrigger: url: https://us-central1-otiai10-gcftest.cloudfunctions.net/hello # 中略 status: ACTIVE timeout: 60s updateTime: '2018-04-27T06:15:24Z' versionId: '2
regionがus-central1になってるのがアレですが、
% curl https://us-central1-otiai10-gcftest.cloudfunctions.net/hello Hello World!% %
やるじゃん... 2分でできた。
以下、いろいろためす
とりあえずprojectの指定めんどいので
% gcloud config set project otiai10-gcftest
% gcloud functions --help % gcloud functions call --help # callでcurl相当のことができる % gcloud functions call hello executionId: dnaermtmefak result: Hello World! % % gcloud functions list NAME STATUS TRIGGER REGION hello ACTIVE HTTP Trigger us-central1 % gcloud functions describe hello # 削除 % gcloud functions delete hello Resource [projects/otiai10-gcftest/locations/us-central1/functions/hello] will be deleted. Do you want to continue (Y/n)? y Waiting for operation to finish...done. Deleted [projects/otiai10-gcftest/locations/us-central1/functions/hello]. % gcloud functions call hello ERROR: (gcloud.functions.call) ResponseError: status=[404], code=[Not Found], message=[Function hello in region us-central1 in project otiai10-gcftest does not exist] % # deployのオプションいろいろある % gcloud functions deploy --help # これとか --entry-point=ENTRY_POINT By default when a Google Cloud Function is triggered, it executes a JavaScript function with the same name. Or, if it cannot find a function with the same name, it executes a function named function. You can use this flag to override the default behavior, by specifying the name of a JavaScript function that will be executed when the Google Cloud Function is triggered. # deployのargに渡すNAMEが、HTTPのendpointになって、 # デフォルトではその名前が、exportされてる関数名と紐づくっぽい。 # --entry-pointでは、エントリポイントとなる関数名を指定できるようだ。 % gcloud functions deploy testtest \ --region ap-northeast1 \ --entry-point hello \ --trigger-http ERROR: (gcloud.functions.deploy) ResponseError: status=[400], code=[Bad Request], message=[The request has errors Problems: region asia-northeast1 is not supported. # マジか
あ、Tokyoにまだ来てないのか...
% gcloud functions deploy testtest \ --entry-point hello \ --trigger-http % curl https://us-central1-otiai10-gcftest.cloudfunctions.net/testtest Hello World!
やるじゃん...!
シークレットの受け渡しどうする
外部APIを叩かせるにはそのAPIシークレットをもたせる必要があるよなー、と思い。環境変数かな?と思ったけどそうではないのかな。
- Cloud Functionsで環境変数を設定する方法 - Qiita
- Google Cloud Functions Environment Variables - Stack Overflow
- Runtime Configurator Fundamentals | Cloud Deployment Manager Documentation | Google Cloud
めんどくさそう。jsonでいいや。
const secrets = require("./secrets.json"); exports.leton = (req, res) => { res.send(secrets); }
% gcloud functions deploy leton --trigger-http % curl https://us-central1-otiai10-gcftest.cloudfunctions.net/leton {"name":"Hiromu OCHIAI","age":17} %
秘密が漏れてしまった。ソースコード管理・共有時はsecrets.json
をignoreするなりすればよいのだろうけど、アーキテクチャ的にプロジェクトのrootディレクトリをGCSに上げてるっぽいし、なんかちょっと気になるなと思った。ので質問してみた。
既出だったらすみません。GCFに、なんらかのシークレットを渡したい(だいたいの場合たぶん外部APIのトークンとか)場合のベストプラクティスって紹介されてますか? 環境変数かなと思ってググったらRuntime-Config とかいうやつにぶちあたったんですが、betaっぽいし、今の所普通にデプロイ対象ディレクトリ配下に
secrets.json
みたいなの作ってdeployしてます。
Functionかー。多少悩ましいねー 最近、適当にSecret ManagerのDemoアプリみたいなのを作ってみたりしていた https://github.com/sinmetal/gcpsm IAP, App Engine, Cloud KMS, Cloud Datastoreを使っている App Engineの時は、インスタンス起動時に取りに行って、メモリに持っておけばいいやーと思っていたけど、Functionですごい細かく起動する場合にこういうことするとLatencyとコストが多少気になるかも?
意外と無い。
意外とふわふわしてるんですね。KMSとかoverkillなので当面
secrets.json
でいいや、という気持ちにおちつきました :wink: ありがとうございます :bow:
認証などどうする
世界中にオープンでいくらでもリクエストできるのはおかしい。限られた者からの呼び出しだけ許すにはどうすればよいか。
「CloudStorageへの権限をチェックする」という機構を使って、CloudFunctionsにおける認証機構を実現している。だるい。 これもsecrets.json
的な感じでまずは実装してしまおうと思う。
雑感
- 慣れたらサクッと作れてよさそう
- とりあえず東京来てくれたのむ
DRYな備忘録として