Streamlit with Google Cloud: GitHub、GitLab、Cloud Build での CI/CD
今回は Streamlit アプリケーションを CI/CD パイプラインからデプロイする方法をみていきます。
この連載では、Google Cloud 上で Streamlit を上手に動かす方法をご紹介しています。
- Cloud Run での Hello, world!
- Firebase 認証との連携
- BigQuery へのクエリ
- GitHub、GitLab、Cloud Build での CI/CD (本記事)
Cloud Build での CI/CD
Cloud Build にはビルドトリガーという機能があり、これを設定すると Google Cloud の Cloud Source Repositories (CSR) のみならず、GitHub や GitLab、それらの Enterprise 版に対してのイベントを契機に CI/CD パイプラインが起動できます。
1. Cloud Build に権限を付与
まずは Cloud Build が内部的に利用するサービス アカウントに権限を付与します。
export project_id=$( gcloud config get-value project )
export project_number=$(gcloud projects describe ${project_id} \
--format="value(projectNumber)")
gcloud projects add-iam-policy-binding "${project_id}" \
--member "serviceAccount:${project_number}@cloudbuild.gserviceaccount.com" \
--role "roles/run.developer"
gcloud iam service-accounts add-iam-policy-binding \
sa-app@${project_id}.iam.gserviceaccount.com \
--member "serviceAccount:${project_number}@cloudbuild.gserviceaccount.com" \
--role "roles/iam.serviceAccountUser"
2. ビルド構成ファイルの作成
どんなパイプラインとするかを YAML で定義できます。docker build
+ Artifact Registry に docker push
した後に Cloud Run へデプロイ、並行して静的解析もしておく例は以下です。cloudbuild.yaml
として保存してみましょう。
steps:
- id: 'test'
name: python:3.11
entrypoint: bash
args: ["-c", "python -m pip install poetry && poetry install --no-interaction --no-ansi --no-root && ./test.sh"]
- id: 'push'
name: 'gcr.io/kaniko-project/executor:latest'
args: ['--destination=asia-northeast1-docker.pkg.dev/$PROJECT_ID/my-apps/streamlit:$SHORT_SHA',
'--cache=true', '--cache-ttl=6h']
waitFor: ['-']
- id: 'deploy'
name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args: ['run', 'deploy', 'my-app', '--region=asia-northeast1',
'--image=asia-northeast1-docker.pkg.dev/$PROJECT_ID/my-apps/streamlit:$SHORT_SHA',
'--service-account=sa-app@$PROJECT_ID.iam.gserviceaccount.com']
3. お試しビルド
正しく動作するか、まずは手元からパイプラインを起動してみます。実際のパイプラインでは $SHORT_SHA
として補完されるはずの git hash ですが、ローカルから実行する際はコマンドライン引数として何かを渡してあげる必要があります。
gcloud builds submit . --config cloudbuild.yaml --substitutions SHORT_SHA=test
テストフェーズでの依存解決がキャッシュされない影響で、パイプラインの完了まで 7 分強かかります。高速化を図るためには、Poetry を事前にビルドしたイメージを使ってもよさそうです。
4. トリガーの設定
GitHub を例にすると
- ソース リポジトリに接続し
- トリガーを作成すれば OK です
- リポジトリが非公開なら SSH 認証鍵を使ったアクセスもあります
トリガーの ビルド構成ファイル
には 2. で作った cloudbuild.yaml
を設定しましょう。
GitHub Actions または GitLab CI/CD での CI/CD
いずれのケースにも共通した準備ステップがあります。
1. サービス アカウントの作成
sa-cicd
という名前で、CI と連携するサービス アカウントを作ります。
gcloud iam service-accounts create sa-cicd \
--display-name "SA for CI/CD"
gcloud projects add-iam-policy-binding "${project_id}" \
--member "serviceAccount:sa-cicd@${project_id}.iam.gserviceaccount.com" \
--role "roles/viewer"
gcloud projects add-iam-policy-binding "${project_id}" \
--member "serviceAccount:sa-cicd@${project_id}.iam.gserviceaccount.com" \
--role "roles/run.admin"
gcloud projects add-iam-policy-binding "${project_id}" \
--member "serviceAccount:sa-cicd@${project_id}.iam.gserviceaccount.com" \
--role "roles/storage.admin"
gcloud projects add-iam-policy-binding "${project_id}" \
--member "serviceAccount:sa-cicd@${project_id}.iam.gserviceaccount.com" \
--role "roles/artifactregistry.writer"
gcloud projects add-iam-policy-binding "${project_id}" \
--member "serviceAccount:sa-cicd@${project_id}.iam.gserviceaccount.com" \
--role "roles/cloudbuild.builds.editor"
gcloud iam service-accounts add-iam-policy-binding \
sa-app@${project_id}.iam.gserviceaccount.com \
--member "serviceAccount:sa-cicd@${project_id}.iam.gserviceaccount.com" \
--role "roles/iam.serviceAccountUser"
2. Workload Identity の準備
一時的な認証トークンの発行を可能にする IAM Service Account Credentials API を有効化します。
gcloud services enable iamcredentials.googleapis.com
ファイル形式でのクレデンシャルなしに CI を実行できるよう、Workload Identity に ID プールを作ります。
gcloud iam workload-identity-pools create "idpool-cicd" --location "global" \
--display-name "Identity pool for CI/CD services"
idp_id=$( gcloud iam workload-identity-pools describe "idpool-cicd" \
--location "global" --format "value(name)" )
3. GitHub Actions の場合
Workload Identity 連携を通してサービス アカウントの利用を許可する単位は設定でよしなに変えられるのですが、ここでは 特定の GitHub プロジェクトにのみ許可する 設定例を取り上げます。
repo=<org-id>/<repo-id>
gcloud iam workload-identity-pools providers create-oidc "idp-github" \
--workload-identity-pool "idpool-cicd" --location "global" \
--issuer-uri "https://token.actions.githubusercontent.com" \
--attribute-mapping "google.subject=assertion.sub,attribute.repository=assertion.repository" \
--display-name "Workload IdP for GitHub"
gcloud iam service-accounts add-iam-policy-binding \
sa-cicd@${project_id}.iam.gserviceaccount.com \
--member "principalSet://iam.googleapis.com/${idp_id}/attribute.repository/${repo}" \
--role "roles/iam.workloadIdentityUser"
gcloud iam workload-identity-pools providers describe "idp-github" \
--workload-identity-pool "idpool-cicd" --location "global" \
--format "value(name)"
GitHub プロジェクトの Actions secrets and variables
に以下の値を設定します。
- GOOGLE_CLOUD_PROJECT: プロジェクト ID
- GOOGLE_CLOUD_WORKLOAD_IDP: Workload Identity の IdP ID
.github/workflows/release.yaml
として以下の内容を保存し、git push
をしてみましょう。
name: Release to Cloud Run
on:
push:
branches:
- main
env:
GOOGLE_CLOUD_REGION: "asia-northeast1"
jobs:
release:
name: Release
runs-on: ubuntu-latest
permissions:
contents: 'read'
id-token: 'write'
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.11'
- name: Test
run: |
pip install poetry
poetry install --no-interaction --no-ansi --no-root
./test.sh
- name: Auth
uses: google-github-actions/auth@v1
with:
workload_identity_provider: ${{ secrets.GOOGLE_CLOUD_WORKLOAD_IDP }}
service_account: "sa-cicd@${{ secrets.GOOGLE_CLOUD_PROJECT }}.iam.gserviceaccount.com"
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v1
- name: Configure docker
run: gcloud auth configure-docker ${{ env.GOOGLE_CLOUD_REGION }}-docker.pkg.dev --quiet
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build the image
uses: docker/build-push-action@v3
with:
tags: ${{ env.GOOGLE_CLOUD_REGION }}-docker.pkg.dev/${{ secrets.GOOGLE_CLOUD_PROJECT }}/my-apps/streamlit:${{ github.sha }}
push: true
- name: Release
uses: google-github-actions/deploy-cloudrun@v1
with:
service: my-app
region: ${{ env.GOOGLE_CLOUD_REGION }}
image: ${{ env.GOOGLE_CLOUD_REGION }}-docker.pkg.dev/${{ secrets.GOOGLE_CLOUD_PROJECT }}/my-apps/streamlit:${{ github.sha }}
flags: --service-account=sa-app@${{ secrets.GOOGLE_CLOUD_PROJECT }}.iam.gserviceaccount.com
4. GitLab CI/CD の場合
Workload Identity 連携を通してサービス アカウントの利用を許可する単位は設定でよしなに変えられるのですが、ここでは 特定の GitLab プロジェクトにのみ許可する 設定例を取り上げます。
repo=<org-id>/<repo-id>
gcloud iam workload-identity-pools providers create-oidc "idp-gitlab" \
--workload-identity-pool "idpool-cicd" --location "global" \
--issuer-uri "https://gitlab.com/" \
--allowed-audiences "https://gitlab.com" \
--attribute-mapping "google.subject=assertion.sub,attribute.project_path=assertion.project_path" \
--display-name "Workload IdP for GitLab"
gcloud iam service-accounts add-iam-policy-binding \
sa-cicd@${project_id}.iam.gserviceaccount.com \
--member "principalSet://iam.googleapis.com/${idp_id}/attribute.project_path/${repo}" \
--role "roles/iam.workloadIdentityUser"
gcloud iam workload-identity-pools providers describe "idp-gitlab" \
--workload-identity-pool "idpool-cicd" --location "global" \
--format "value(name)"
GitLab プロジェクトの CI/CD > Variables
に以下の値を設定します。
- GOOGLE_CLOUD_PROJECT: プロジェクト ID
- GOOGLE_CLOUD_WORKLOAD_IDP: Workload Identity の IdP ID
.gitlab-ci.yml
を以下の通り用意したら git push
を試しましょう!
image:
name: google/cloud-sdk:417.0.0-alpine
variables:
GOOGLE_CLOUD_REGION: asia-northeast1
stages:
- test
- deploy
test:
stage: test
image: python:3.11
script:
- pip install poetry
- poetry install --no-interaction --no-ansi --no-root
- ./test.sh
except:
- tags
.gcloud_auth: &gcloud_auth
before_script:
- echo ${CI_JOB_JWT_V2} > .ci_jwt
- gcloud iam workload-identity-pools create-cred-config ${GOOGLE_CLOUD_WORKLOAD_IDP} --service-account "sa-cicd@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" --credential-source-file .ci_jwt --output-file .cred.json
- gcloud auth login --cred-file "$(pwd)/.cred.json"
- gcloud config set project ${GOOGLE_CLOUD_PROJECT}
deploy-to-dev:
stage: deploy
<<: *gcloud_auth
script:
- gcloud builds submit --tag ${GOOGLE_CLOUD_REGION}-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/my-apps/streamlit:${CI_COMMIT_SHORT_SHA} .
- gcloud run deploy my-app --image ${GOOGLE_CLOUD_REGION}-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/my-apps/streamlit:${CI_COMMIT_SHORT_SHA} --region "${GOOGLE_CLOUD_REGION}" --service-account "sa-app@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com"
only:
- main
さいごに
最終的なファイル群はこちらにおいてあります。
Streamlit アプリケーションを Google Cloud 上で動かすイメージは湧きましたでしょうか?今回は触れていませんが、いずれ他のデータソースを扱ったり、より高度なアクセス制御を設定したりしてみたいと思います。長文にお付き合いいただき感謝です。みなさんに、少しでも参考にしていただけるトピックがあったことを祈りつつ。ではまた〜
Discussion
大変参考になりましたmm
そう言っていただけるとうれしいです!コメントありがとうございます