初心者がstreamlitを使って採点アプリを作ってみた#4

<!-- 目次部分(リンクになるところ) -->
# 目次
1. [はじめに](#anchor1)
1. [そもそもデプロイとはなんなのか](#anchor2)
1. [どのサービスでデプロイするのか](#anchor3)
1. [Google Cloud run](#anchor4)
1. [今後のアプリ開発体制を考える](#anchor5)
1. [CI/CDとは](#anchor6)
1. [Git Hub Actions](#anchor7)
1. [Google Cloudでデプロイするための事前準備](#anchor8)
1. [Git Hub Actionsで継続的デプロイを可能にする](#anchor9)
1. [終わりに](#anchor10)

<a id="anchor1"></a>
# はじめに
streamlitを使って、採点アプリを作ってみた記録の第３回目の投稿になります。
前回までの記事が気になった方は

第一回：<https://qiita.com/0dn09g3y726519/items/d78397d3c174f4388687>
第２回：<https://qiita.com/0dn09g3y726519/items/e95bae2595b951ddbecc>
第３回：<https://qiita.com/0dn09g3y726519/items/6fc96e928c4c910d3ed9>

こちらをご覧ください。
下図のような構成のアプリを作っています。

###
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821078/62a8f101-668e-21a8-4eb6-a02ebee17c33.png)


前回の記事で機能の紹介が終わりましたので、ここからは今回のアプリ制作活動の中で一番苦労したデプロイについてご紹介したいと思います。サーバーって何？ってレベルの初心者が、いろいろ調べながら頑張ってCI/CDができる環境を作って、アプリのデプロイまでたどり着きましたので、今後どなたかの参考になれば幸いです。
（初心者ゆえおそらく間違っているところも多々あると思いますので、間違いに気づかれた方はぜひコメントください。勉強させていただきたいと思います。）


<a id="anchor2"></a>
# そもそもデプロイとはなんなのか
私の認識では、デプロイとはアプリをサーバー上において、だれでもアクセスできる環境を作ることだと思っています。これをしないとstreamlitで作ったアプリはローカルでのみ使えるようにしかならないので、せっかく作った意味がなくなってしまいます。
デプロイするためにはサーバーが必要になります。私のパソコンでアプリをコンテナイメージに変換してどうにかウェブアプリとして発信することもおそらくできると思うのですが、おそらくすごく大変だと思います。そのため、世の中にはデプロイからサーバーのメンテナンスまでやってくれるサービスがたくさんあります。
（おそらくこれをPaaS？というのだと思っています。）

<a id="anchor3"></a>
# どのサービスでデプロイするのか
おそらくサーバーレス環境を構築できるサービスは多数あると思います。

・AWS
・Azure
・GoogleCloud
・NEC Cloud
・IBM Cloud などなど

様々なサービスがあって、どれも強み弱みがあると思うのですが、私はOCRをGoogleCloudで利用していたので、そこの認証が楽なのかなと思いGoogleCloudでデプロイからアプリとして配信することに決めました。
（AWSが一番記事やウェブページがある印象です。ただ専門用語も多く初心者が調べてわかるような記事は少なく感じました。この辺りのクラウドサービスに関しては専門用語とかわかってないときついですね。私はまだ全然わからない単語だらけです。）

<a id="anchor4"></a>
# Google Cloud run
Google Cloudの中にCloud runというサービスがあります。これは自分で作ったstreamlitコードをどうにかコンテナイメージにしたら、ビルドして全世界でアクセス可能なウェブアプリに変換してくれる便利なサービスです。これを使えば初心者でもウェブアプリを開発することができます！！
ただどうやってコードとか環境をコンテナイメージにするんだということですよね。dockerというコンテナをどうにかするアプリ？を聞いたことはあるのですが、またパソコンにdockerの環境を構築して、使い方を覚えてとか難しいしだるくてやる気になりませんでした。そこで調べてみるとGoogle CLoudの中にCoud Buildというサービスがありました！！！
cloud buildはdockerの代わりにdockerfileやコードやrequiremnets.txtを投げるだけで、コンテナイメージに変換して、cloud runに投げてデプロイしてくれるサービスみたいです（この認識ふわっとしていて間違っていたらすみません）

なのでイメージとしてはコード群 → cloud build → cloud run →　ウェブアプリ　という形でウェブアプリを開発していきます！！！

<a id="anchor5"></a>
# 今後のアプリ開発体制を考える
ここからデプロイをしていくのですが、ただデプロイをするだけだと、今後アプリコードに変更を加えたときに、毎回新しいファイルやコードを自分でまとめて、cloud buildに投げる処理を実行してデプロイをしてみたいな工程を踏むのが少し面倒に感じてしまいました。そこでCI/CDという考え方があることを知りました。

<a id="anchor6"></a>
# CI/CDとは
CI/CD (継続的インテグレーションおよび継続的デリバリー/デプロイメント)とは、要はアプリに変更を加えたときに、その変更を検知して自動でテストを実行して自動でアプリを再度デプロイしなおしてくれる手法のことです。
このサイクルができたら、もうあとはアプリのコードのことだけ集中しておけば、デプロイ周りのことは自動でやってもらえてテストでエラーが出たらそれを確認して直して再度デプロイみたいな試行錯誤ができるようになり、すごく便利で開発サイクルも早く回りそうです。
ただこの手法をどうやって実現するんだと思いました。

<a id="anchor7"></a>
# Git Hub Actions
そこでGitHubActionsを見つけました！GitHubは皆さんよく知っているリポジトリを作ってファイルを保存したり、ほかの人に共有したりできるサービスです。その中にGitHubActionsという項目があると思います。これはリポジトリ内のファイルに対して、なにかやってほしいこと（たとえば、プロジェクトのビルドやテストコードの実行など）をワークフローとして記述することで、リポジトリがcommitされたときに自動でそのワークフローを実行してくれるものになります。.github/workflows/main.ymlというようにリポジトリにディレクトリを作成して、そこのmain.yml内にやってほしいことを記述することで、自動で実行してくれます。
これを使うことでCI/CD環境を作ることができそうです。
main.ymlに中に
・cloud buildでのコンテナイメージの変換
・cloud runでのアプリのデプロイ
を記述しておけば、あとはgithubに変更したコードをpushするたびにそれらを実行してくれます。立派なCI/CD環境の完成です。
これでようやくデプロイの方針がたちました。
ここまでのデプロイのやり方を簡単にですが図にまとめました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821078/1c3caf2e-2bfb-0ee2-6294-8d2486605709.png)

このようなやり方でデプロイしていきます。

<a id="anchor8"></a>
# Google Cloud でデプロイするための事前準備

## Google Cloudにアカウント登録して、GoogleCloudを使用できるようにしましょう
まずはGoogle Cloudでアカウントを作成しましょう。
<https://support.google.com/accounts/answer/27441?hl=ja>
ここから飛んでアカウントを作成しましょう。

## Deploy用のProject、サービスアカウントを作成しましょう
アカウントを作成しましたら、ダッシュボード画面に行くとこのような画面になっていると思います。
![スクリーンショット 2024-07-09 211707.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821078/152776d8-ab76-7372-2415-38eaf699e020.png)
ここで左上のMyFirstProjectというところを押して、デプロイ用の新しいプロジェクトを作成しましょう。
作成できましたら、そのプロジェクト内で、左側のIAMと管理からサービスアカウントを選択します。
![スクリーンショット 2024-07-09 212855.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821078/73042ad1-1f2e-0975-1fb5-3f5b23a5a8f7.png)
ここでサービスアカウントを作成しましょう。
<https://cloud.google.com/iam/docs/service-accounts-create?hl=ja>
作り方の詳細はこのページを参考に見てください。

## APIを有効にする
Cloud Build, Cloud Run APIを有効にしましょう。左のナビゲーションメニューのAPIとサービスから、有効なAPIとサービスを選択して、上部のAPIとサービスを有効にするからこれらのAPIを有効にしましょう。

以上でCloud runを利用するためのgoogle cloudでの準備は終わりです。

<a id="anchor9"></a>
# Git Hub Actionsで継続的デプロイを可能にする

## GitHubリポジトリを作成
GItHubにリポジトリを作成して、現在のStreamlitコードと環境作成のためのrequirements.txtをpushしましょう。
pushが完了したら、pushのたびにGitHubActionsで実行してもらうアクションのフローを記述したmain.ymlを作成します。

```php:main.yml
name: Cloud Build

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - name: checkout
        uses: actions/checkout@v4

      - id: 'auth'
        name: 'Authenticate to Google Cloud'
        uses: 'google-github-actions/auth@v1'
        with:
          create_credentials_file: true
          workload_identity_provider: '${{secrets.WIF_PROVIDER}}'
          service_account: '${{secrets.WIF_SERVICE_ACCOUNT}}'


      - name: Set up environment variables
        run: |
          echo "CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE=${{ steps.auth.outputs.credentials_file_path }}" >> $GITHUB_ENV
          echo "GOOGLE_APPLICATION_CREDENTIALS=${{ steps.auth.outputs.credentials_file_path }}" >> $GITHUB_ENV
          echo "CLOUDSDK_CORE_PROJECT=${{ secrets.PROJECT_ID }}" >> $GITHUB_ENV

      - name: Configure gcloud CLI
        run: |
          gcloud config set project ${{ secrets.PROJECT_ID }}

      - name: Verify authentication and list services
        run: |
          gcloud auth list
          gcloud services list

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r src/requirements.txt
      
      - name: Set up environment variables
        run: |
          echo "export AUTH_USER='${{ secrets.AUTH_USER }}'" >> $GITHUB_ENV
          echo "export AUTH_PASSWORD='${{ secrets.AUTH_PASSWORD }}'" >> $GITHUB_ENV

      - name: Deploy to Cloud Run
        run: |
          gcloud builds submit --config=cloudbuild.yaml --substitutions=_AUTH_USER=${{ secrets.AUTH_USER}},_AUTH_PASSWORD=${{ secrets.AUTH_PASSWORD }}

```
このようなファイルを作成して、github上の.github\workflows\main.ymlに配置します。
ファイルの中身について解説していきます。

### 名前・トリガー
```php:main.yml
name: Cloud Build

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
```
このgithubactionsのワークフローの名前がCloud Buildとなっています。
on push,pull_requestと書くことで、mainブランチにpushまたはpull_requestが行われたとき、このワークフローを実行するという意味になります。

### ワークフローのジョブの定義
```php:main.yml
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - name: checkout
        uses: actions/checkout@v4
```
まず、実行するジョブの名前をdeployと名付けます。

runs-onがこのジョブを実行する環境です。ここでは最新のUbuntuバージョンの仮想マシンが使用するとしています。

permissionsがこのジョブに対して許可する動きを定義しています。ここではid-token:writeでIDトークンの書き込みを許可しています。このあとでgoogle cloudとgithubの認証をするのですが、その際に必要な許可になります。contents:readでリポジトリの内容を読み取ることを許可しています。

stepsではジョブが具体的に実行していく内容を記述する部分になります。まず初めにリポジトリ内のコードをジョブを実行する仮想マシンに移す必要があります。それを- name: checkout　uses: actions/checkout@v4で定義しています。checkoutとはファイルを仮想マシンに配置する意味であり、actions/checkout@v4を使用することで実行できます。これで仮想マシンを用いて自分のファイルに対してジョブを実行することができます。

### githubとgoogle cloudの認証
```php:main.yml
- id: 'auth'
        name: 'Authenticate to Google Cloud'
        uses: 'google-github-actions/auth@v1'
        with:
          create_credentials_file: true
          workload_identity_provider: '${{secrets.WIF_PROVIDER}}'
          service_account: '${{secrets.WIF_SERVICE_ACCOUNT}}'
```
今回ジョブにはgoogle cloud　runを使ってリポジトリに上がっているstreamlitアプリをデプロイしてもらうので、google cloudに対してgithubが認証を行わなければなりません。
ここでは認証を定義しています。uses: 'google-github-actions/auth@v1'これがgoogle 公式が出しているgithubactionsに対する認証のアクションになります。
with:
          create_credentials_file: true
これで認証情報ファイルを作成します。
workload_identity_provider: ' {{secrets.WIF_PROVIDER}}'
これで ワークロードアイデンティティプロバイダーを指定します。
service_account: '${{secrets.WIF_SERVICE_ACCOUNT}}'
これでサービスアカウントを指定します。

ここで2点説明できていないことがありました。
・githubactions secretsの使用
・githubからgoogle cloudへの認証方法

#### githubactions secretsの使用
![スクリーンショット 2024-07-18 164906.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821078/252c685f-6111-84ee-b104-98b7e706748d.png)
github のリポジトリからSettingsを開くと、左下にSecrets and variablesという項目があると思います。ここをクリックして"Actions"を選んでもらうと、githubactionsのsecretsを設定できます。これは要は秘密の環境変数みたいなものです。

"NewRepositoryscrets"を選択して、secretsの新規作成ができます。作成したsecretsはmain.ymlの中で'${{secrets.WIF_SERVICE_ACCOUNT}}'このようにして取得できます。

#### githubからgoogle cloudへの認証方法
github とgoogle cloudの認証方法は２通りあり、
- google cloudで出力されたjsonサービスアカウントキーを用いる方法
- google cloudのWorkload Identity連携用いてキーなしで認証する
上のほうが従来の方法で、下が2021年ころから出た新規の方法だと認識しています。今回は下のキーなしでの認証を行いました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821078/eaddf524-447a-c704-7150-da5739ee05bb.png)（引用元：https://cloud.google.com/blog/ja/products/identity-security/enabling-keyless-authentication-from-github-actions）

キーなしの認証のフローとしては画像のように、githubからgoogle cloud側に問い合わせると、workloadidentitypoolが認証されたgithubかどうかを判断してから、許可を出してくれてgithubからgoole cloudへのアクセスが許可されてgoolg cloudのサービスをgithubから動かすことが可能になります。
workloadidentityでの設定方法はのちのち説明しますので、ここではこういう認証方法があるんだなくらいの認識で大丈夫です。

main.ymlの説明に戻ります。
```php:main.yml
- id: 'auth'
        name: 'Authenticate to Google Cloud'
        uses: 'google-github-actions/auth@v1'
        with:
          create_credentials_file: true
          workload_identity_provider: '${{secrets.WIF_PROVIDER}}'
          service_account: '${{secrets.WIF_SERVICE_ACCOUNT}}'
```
workload_identity_provider: '{{secrets.WIF_PROVIDER}}'
service_account: '${{secrets.WIF_SERVICE_ACCOUNT}}'
ここではWorkloadIdentityで得られたWIF_PROVIDER, サービスアカウント名をgithubactionsのsecretsに設定し、それらの値を受け取ります。

### 環境変数の設定
```php:main.yml
- name: Set up environment variables
run: |
  echo "CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE=${{ steps.auth.outputs.credentials_file_path }}" >> $GITHUB_ENV
  echo "GOOGLE_APPLICATION_CREDENTIALS=${{ steps.auth.outputs.credentials_file_path }}" >> $GITHUB_ENV
  echo "CLOUDSDK_CORE_PROJECT=${{ secrets.PROJECT_ID }}" >> $GITHUB_ENV
```
先ほど得られた認証情報をもとに、仮想マシンの環境変数に必要な変数を定義していきます。

### プロジェクトの設定・認証の確認
```php:main.yml
- name: Configure gcloud CLI
run: |
  gcloud config set project ${{ secrets.PROJECT_ID }}
- name: Verify authentication and list services
run: |
  gcloud auth list
  gcloud services list
```
gcloud config set project ${{ secrets.PROJECT_ID }}　でgcloud CLIを利用してgoole cloudのプロジェクトを設定します。
gcloud auth listで認証されているアカウントの一覧を表示して、認証が正しくできているか確認をしています。ここで認証ができていないとのちのちエラーが出るようになっています。
gcloud services listで有効なサービスの一覧を表示しています。

### 環境構築・cloud runでのデプロイ
```php:main.yml
- name: Install dependencies
run: |
  python -m pip install --upgrade pip
  pip install -r src/requirements.txt

- name: Set up environment variables
run: |
  echo "export AUTH_USER='${{ secrets.AUTH_USER }}'" >> $GITHUB_ENV
  echo "export AUTH_PASSWORD='${{ secrets.AUTH_PASSWORD }}'" >> $GITHUB_ENV

- name: Deploy to Cloud Run
run: |
  gcloud builds submit --config=cloudbuild.yaml --substitutions=_AUTH_USER=${{ secrets.AUTH_USER}},_AUTH_PASSWORD=${{ secrets.AUTH_PASSWORD }}
```
ここではname: Install dependenciesで必要なPythonパッケージを仮想マシンにインストールし、- name: Set up environment variablesでアプリのログインに必要なID,Passwordを環境変数に組み込んでいます。この部分は通常、必要ないかもしれないです。
gcloud builds submit --config=cloudbuild.yamlでGoogle Cloud Buildにビルドを送信しています。config=cloudbuild.yamlを渡すことで、cloudbuild.yamlを使用してGoogle Cloud BUildがビルドしてくれます。cloudbuild.yamlにはビルドのステップや設定が記述されています。このファイルについては後々説明します。
--substitutions=_AUTH_USER=${{ secrets.AUTH_USER}},_AUTH_PASSWORD=${{ secrets.AUTH_PASSWORD }}この部分はCloud Buildに渡す変数の置換を指定しています。ここは私が設定したID,Passwordをgithubsecretsから取得して、渡しているだけですので、通常は必要のない部分になります。


main.ymlの中身の説明は以上になります。

<a id="anchor10"></a>
# 終わりに
今回はアプリをデプロイする手順の中で、githubactionsを使用してgoogle cloud buildを動かす手前まで説明していきました。
次回はGithubとGoogle cloudの認証に必要なWorkloadIdentifyの設定方法について説明します。
最後まで読んでいただき、ありがとうございました。