CloudRunにStrapiをデプロイして運用できるようにしたい

# **はじめに**
こんにちは、Intimate Merger(インティメート・マージャー) の@eknisです。
今回はGoogle CloudのCloudRunを使用してStrapiをデプロイし、運用を想定した構築手順を紹介します。
公式ドキュメントが充実しているので基本的なところは割愛しつつ、
GCPの複数のサービスを組み合わせてStrapiをより堅牢かつ拡張性を持った環境で動かす方法に焦点を当てます。

Intimate Mergerでは社内ツールが多く運用されており、少人数でも継続的に改善できるよう、監視、自動化を徹底しています。
事業をより拡大していく中でなにか利用できるものがないかを考え、ヘッドレスCMSを使ってみようと思い調べていく中で目に止まったのがStrapiでした。実際に業務の中で使うかはこれから次第ですがなかなかちょうどよいCMSな気がしています。

https://strapi.io/

# **構成**
まず利用するプロダクトなどを軽く紹介します。
| 名称 | 説明 |
|:-----------|:------------|
| CloudRun  | サーバーレスな環境で、アプリケーションをコンテナとしてデプロイするためのプラットフォーム |
| CloudSQL  | 管理されたリレーショナルデータベースサービス      |
| CloudStorage  | オブジェクトストレージで、大容量のデータを保存し、取得するためのスケーラブルなストレージ|
| SecretManager  | APIキー、パスワード、APIトークンなどの機密データを安全に管理するためのサービス |
| IAP (Identity-Aware Proxy)  | アプリケーションへのアクセスを認証し、認可するためのサービス   |

# **作業準備**
## **GCPアカウントの作成とプロジェクトの設定**
Google Cloudの[公式](https://console.cloud.google.com/)にアクセスして、Googleアカウントでログインします。
ダッシュボードから「プロジェクトを選択」をクリックし、プロジェクト名を適切に設定します。
必要に応じて新しいプロジェクトを作成してください。

## **Google Cloud SDK（gcloudコマンド）のインストール**
GCPのリソースを管理するためには、Google Cloud SDK（gcloudコマンド）をインストールする必要があります。
詳細な手順は[公式ドキュメント](https://cloud.google.com/sdk/docs/install)を参照してください。

## **Strapiのプロジェクトの作成**
以前までは[公式イメージ](https://github.com/strapi/strapi-docker)があったので利用できたのですが `Strapiv3`まででメンテナンスは終了しており、`Strapiv4`には対応していません。
代替手段としてcommunityでメンテナンスされている[`strapi-tool-dockerize`](https://github.com/strapi-community/strapi-tool-dockerize)を利用することを検討してみても良いかもしれません。
今回は公式の[クイックスタート](https://docs.strapi.io/dev-docs/quick-start)を利用します。

:::note
例として`npm` をパッケージマネージャーとして利用します。
:::
以下のコマンドを実行して、StrapiのCLIをグローバルにインストールします。
```bash
  npm install -g create-strapi-app
```
        
新しいStrapiプロジェクトを作成します。        
```bash
  npx create-strapi-app my-strapi-project
```
プロジェクトの作成中に、データベースの設定や管理者の作成など、必要な情報を入力します。
プロジェクトが作成されたら、プロジェクトディレクトリに移動してください。        
```bash
  cd my-strapi-project
```
        

これで、作業準備が整いました。GCPプロジェクトが作成され、Cloud SDKがインストールされ、Strapiプロジェクトが作成された状態で次の手順に進むことができます。

# **作業手順**

## **StrapiのDockerイメージ作成**
[公式ドキュメント](https://docs.strapi.io/dev-docs/installation/docker)に記載があるので、参考に準備して下さい。

## **DockerイメージをGCPのContainer Registryにプッシュ**

```bash
# プロジェクトIDを設定
PROJECT_ID=your-project-id

# Dockerイメージをビルド
docker build -t gcr.io/$PROJECT_ID/strapi-app .

# Container Registryにプッシュ
docker push gcr.io/$PROJECT_ID/strapi-app
```

## CloudRunで新しいサービスを作成
```bash
gcloud run deploy strapi-service \
  --image gcr.io/$PROJECT_ID/strapi-app \
  --platform managed \
  --region $REGION \
  # --allow-unauthenticated
```
:::note warn
`--allow-unauthenticated` は手順で書かれていることが多いですが一般公開アクセスを許可するオプションなので、公開しないのであれば削除しておいてください。
:::
ここまでで画面確認できる状態です。ですが実際の運用を考えると
CloudSQL、CloudStorageを利用したいなあ
secret管理できると安心だなあ
となるのでぐっと堪えて引き続き準備を進めます。

## **CloudSQLの利用**
:::note
パラメータに関しては[公式ドキュメント](https://cloud.google.com/sql/docs/postgres/create-instance?hl=ja)を参考に設定して下さい。
:::
まず、CloudSQLインスタンスの作成します。
```bash
gcloud sql instances create strapi-db --region=asia-northeast1 --database-version=POSTGRES_15 --cpu=2 --memory=4G --root-password=your-password
```

次にデータベースとユーザーの作成していきます。
```bash
gcloud sql databases create strapi --instance=strapi-db
gcloud sql users create strapi --instance=strapi-db --password=your-password
```

## **CloudStorageの利用** 

### **バケットの作成**

```bash
gsutil mb -p $PROJECT_ID -c STANDARD -l asia-northeast1 gs://your-bucket-name/
```

### **Strapiの設定変更**

Strapiの設定ファイル（**`config/server.js`**）を変更して、CloudStorageを使うように設定します。
Strapiはデフォルトでローカルのストレージに画像を保存します。
そのためuploadの設定を変更する必要がありますが
communityがproviderを用意してくれているので利用します。

https://github.com/strapi-community/strapi-provider-upload-google-cloud-storage

:::note
全てではないですがplugin, providerは[market](https://market.strapi.io/)で探すことができます
:::
```jsx
module.exports = ({ env }) => {
  if (env('NODE_ENV') === 'production') {
    return {
      upload: {
        config: {
          provider: '@strapi-community/strapi-provider-upload-google-cloud-storage',
          providerOptions: {
            bucketName: env('BUCKET_NAME'),
            publicFiles: true,
            uniform: false,
            basePath: '',
          },
        },
      }
    }
  } else {
    return {
    }
  }
  //...
```

## **SecretManagerの利用**

### **SecretManagerにシークレットの作成**
利用したいケースは多くあるはず。
[公式ドキュメント](https://cloud.google.com/run/docs/configuring/secrets?hl=ja)を見て設定して下さい。

```bash
gcloud secrets create strapi-secrets --replication-policy=automatic
gcloud secrets versions add strapi-secrets --data-file=secrets.json
```

## **CloudRunで新しいサービスを作成**

```bash
gcloud run deploy strapi-service \
  --image gcr.io/$PROJECT_ID/strapi-app \
  --platform managed \
  --region $REGION \
  --set-secrets=strapi-secrets:latest \
  --add-cloudsql-instances=your-project-id:us-central1:strapi-db \
  --set-cloudsql-instances=your-project-id:us-central1:strapi-db \
  --set-env-vars=GOOGLE_CLOUD_STORAGE_BUCKET_NAME=your-bucket-name \
  # --allow-unauthenticated
```

事前にdeployしている場合は
`--set-env-vars`を`--update-env-vars`
`--set-secrets`を`--update-secrets`
としたりします。

:::note warn
環境変数をシークレットの保存と利用に使用しないでください。
環境変数は、プロジェクト閲覧者以上の権限を持つユーザーに表示されます。
:::


一連の手順を踏むことで、StrapiアプリケーションがCloudRun上で動作し、
CloudSQLとCloudStorageと連携して運用できるようになります。

## **IAPの利用**

### **IAPの有効化**
適切に閲覧権限を付与したくなりそうです。
GCPコンソールからIAPを有効にし、設定を行います。

### **CloudRunへのアクセス制御**

IAPの設定でCloudRunサービスにIAPを適用し、認証・認可を行います。
ここも[公式ドキュメント](https://cloud.google.com/iap/docs/enabling-cloud-run?hl=ja)が充実しているので簡単ですね。
APIの設定などで一山ありますがここでは説明しません。

# **まとめ**
CloudRunにStrapiをデプロイし、GCPの多くのサービスと組み合わせることで、拡張性のあるCMSを構築することができました。
今回紹介できませんでしたが運用フェーズでは、適切な権限設定、十分なモニタリングと継続的な最適化が重要です。
また、Strapiの実装としてproductionモードではコンテントタイプの変更ができないので、どのように運用設計していくかが大切です。
新しい機能の追加やトラフィックの変動にも対応できるよう、慎重に設計しましょう。

# **あとがき**
次回担当分は2024年に大きく変わりそうな最近のブラウザ事情についてまとめられたらと思っています。
若手も面白い記事を書いているので来週以降の記事も読んでいただけたらと幸いです！

https://qiita.com/advent-calendar/2023/intimatemerger