先日の記事 「Cloud SchedulerでGoのCloud Functionを定期的に実行する」で書いたCloud Functionsは、これまでgcloud
コマンドを人間系で実行してデプロイしていたのですが、流石に面倒くさくなりました...
ので、Github Actionsを使って、自動でデプロイしようと試みたところ、なんとかうまくいったので纏めておきます。
今回試したのは、「master
ブランチにコミットがプッシュされたら、Github Actionsを使って、Cloud FunctionsをGCPにデプロイする」です。
Github Actionsを使ってデプロイしようとしたのは、Go言語で書かれたGCF(Google Cloud Functions)です。このコードは、Github上でPrivateなリポジトリで管理しています。 Github Actions導入前のリポジトリの中身は、↓のような感じでした。
some-functions
├── .git
├── .go-version
├── README.md
└── function-a
├── function-a.go
├── go.mod
└── go.sum
デプロイしたいのは、上記でいうところのfunction-a.go
になります。
Github Actionsを使うのは今回が初めてでした。 公式ドキュメントを読みながら、導入していきました。
最初にやったのは、ワークフローを定義するファイルをリポジトリに配置することでした。
リポジトリ直下に、.github/workflows
というディレクトリを作成し、その中に.yml
ファイルを作ります。今回は、上記のfunction-a
のデプロイを定義するということで、deploy-function-a.yml
という名前にしました。リポジトリ内のディレクトリ構成は、↓のようになりました。
some-functions
├── .git
├── .github
│ └── workflows
│ └── deploy-function-a.yml
├── .go-version
├── README.md
└── function-a
├── function-a.go
├── go.mod
└── go.sum
次にワークフローの中身を.yml
に書いていきました。
結論を書くと↓のような内容になりました。
name: Deploy function-a
on:
push:
branches:
- master
paths:
- 'function-a/**'
jobs:
deploy:
name: Deploy Functions
runs-on: ubuntu-latest
env:
REGION: デプロイするGCPのリージョン
ENTRY_POINT: エントリーポイントとなる関数名
TOPIC: トリガーとなるPubSubのトピックID
steps:
- uses: actions/checkout@v2
# 2021-04-22
# 元々、GoogleCloudPlatform/github-actions/setup-gcloud@master
# を使う記述をしていましたが、実際に実行すると、
# google-github-actions/setup-gcloud を使ってくれ、と警告されるようになりました。
# - uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
- uses: google-github-actions/setup-gcloud@master
with:
project_id: GCPのプロジェクトID
service_account_email: GCPのサービスアカウントのメールアドレス
service_account_key: ${{ secrets.Secretの名前 }}
- name: Deploy Functions
run: |
gcloud functions deploy Function名 --source ./function-a --region=${REGION} --entry-point ${ENTRY_POINT} --runtime go113 --trigger-topic=${TOPIC}
今回は、master
ブランチにfunction-a
ディレクトリ配下のファイルのコミットがプッシュされたらこのワークフローを起動させたいので、
on:
push:
branches:
- master
paths:
- 'function-a/**'
としました。
次に、実際の関数のデプロイですが、簡単に言うと
という流れになるようです。 個人的にポイントとなる点と思ったのは、
でしょうか...
それぞれの詳細を書いておきます。
Github Actions上でgcloud
コマンドを実行する際に、GCPのサービスアカウントが必要になります。これは、GCPの管理画面の「IAMと管理」メニューの「サービスアカウント」で作成できます。
作成したサービスアカウントを使ってGCFのデプロイを実行したいので、「Cloud Functions開発者」のロールを割り当てました。
また、作成したサービスアカウントを認証するためのキーを作成し、JSONファイルとしてダウンロードしておきます。(このファイルが漏洩するとまずいので、厳重に管理する必要があります。)
次に、サービスアカウントの認証用のキーを、Githubにシークレットとして登録します。これは、Githubのリポジトリの「Settings」タブで「Secrets」メニューを選択して進ます。
「New Secret」ボタンをクリックすると、新しいSecretを登録できるので、適切な名前をつけて、サービスアカウントの認証用のキーを値として登録します。これがワークフロー定義の中で${{ secrets.Secretの名前 }}
とすることで使用できます。
ワークフローの中身については、おおよそこんな感じです。
実際に期待通りに動作させるまでに、いくつかハマったので、その点を纏めておきます。
.github/workflow
ディレクトリとワークフロー定義ファイルを作ってコミットし、いざプッシュしようとしたら、次のエラーが発生し、プッシュできませんでした。(ちなみに、これはターミナルでgit
コマンドを実行した際に発生しました。)
! [remote rejected] master -> master (refusing to allow an OAuth App to create or update workflow `.github/workflows/deploy-function-a.yml` without `workflow` scope)
error: failed to push some refs to 'https://github.com/shimar/some-functions'
調べてみると、VSCodeやGithub Desktopでの話題がヒットしたものの、ターミナルで僕と同じ現象になった例は見つけられませんでした。仕方ないので、Macの「キーチェーンアクセス」アプリで、Githubへの認証情報を削除し、ターミナルでgit
コマンドを実行し、Githubの認証を再実行したところ、無事にプッシュすることができました。
GCPのサービスアカウントのキーは、JSONで発行しました。これをGithubのSecretとして登録する場合、base64化する必要がありました。最初、JSONファイルの中身をそのまま登録していたのですが、ワークフロー実行時にエラーになりました。
JSONファイルの中身をbase64化するには、Macの場合はbase64
コマンドでできます。
cat key-of-service-account.json | base64
gcloud
コマンドでGCFのでプロイする際のオプションに--source
があります。最初、これをデプロイしたいファイルのパス(つまり./function-a/function-a.go
)としていたのですが、ここに指定するのはディレクトリパスが正しいようです。つまり、./function-a
を指定します。
今回「Cloud Functions開発者」のロールを割り当てたGCPのサービスアカウントを使ってワークフローの中でGCFのデプロイを実施しようとしましたが、次のようなエラーが発生しました。
ERROR: (gcloud.functions.deploy) ResponseError: status=[403], code=[Forbidden], message=[Missing necessary permission iam.serviceAccounts.actAs for $MEMBER on the service account プロジェジェクトID@appspot.gserviceaccount.com.
Ensure that service account プロジェクトID@appspot.gserviceaccount.com is a member of the project プロジェクトID, and then grant $MEMBER the role 'roles/iam.serviceAccountUser'.
You can do that by running 'gcloud iam service-accounts add-iam-policy-binding プロジェクトID@appspot.gserviceaccount.com --member=$MEMBER --role=roles/iam.serviceAccountUser'
In case the member is a service account please use the prefix 'serviceAccount:' instead of 'user:'. Please visit https://cloud.google.com/functions/docs/troubleshooting for in-depth troubleshooting documentation.]
どうやら、今回作成したサービスアカウトにiam.serviceAccounts.actAs
なる権限が足りていない、ということのようです。(正直、どういうことなのかわかっていません...)
ただ、エラーメッセージに対応方法が書かれていたので、素直に実行してみたところ、無事にデプロイができるようになりました。
Github Actionsを使って、GCFをデプロイする方法と、ハマったポイントについて纏めてみました。Github Actionsそのものよりも、GCPのサービスアカウント周りの方が難しいです... 今回、初めてGithub Actionsを使ってみましたが、Github上のリポジトリとは相性が良さそうなので、今後積極的に使ってみようと思います。