StrapiをCloud Runで運用している話

こんにちは。株式会社dottの清水です。

今日は[Strapi Advent Calender](https://qiita.com/advent-calendar/2020/strapi)の3日目ということで、今dottでやっているCloud RunでのStrapiの運用の話をしたいと思います。

Strapiがなんだ、どうやって使うんだっていう話はカレンダーの前日までの記事で是非読んでみてください！
[HeadlessCMS Strapiの使いどころ](https://qiita.com/watalucky/items/13bba3ece7ac7605bc57
) / [Strapi Quick Start Guide 日本語訳](https://zenn.dev/tomigie/articles/e6c77540fb42f5)

# TL;DR
- Cloud RunとCloudSQLの組み合わせは、ローカル環境かってくらい速い。
- GAEはデフォルトでgzipに対応しているから、通信量は1/4くらいになってる。
- でもCloud Runが爆速。
- Cloud Runは、リクエスト時間の課金だからすごいやすい。

# Cloud SQLとの通信がめちゃめちゃ早い
Cloud RunにするまではGoogle App Engine(GAE)でStrapiを動かしていて、試しにCloud Runに同じものをデプロイしたときの話。管理画面を触って体感できるくらいに速くなってたので、簡単に記録しておいたときの記録です。もっとイベント単位でログをとって詳細に調査したかったんですが、それは将来またGDGのイベントなどで発表できればと思います。

### 実験方法
比較的複雑な集計処理をSQLでやって、ソートなどを色々回すAPIの実行スピードを計測をしてみました。もちろん両方のコードは全く同じものです。GCP上ではCloud SQLに接続する際に、[Cloud SQL Proxy](https://cloud.google.com/sql/docs/mysql/sql-proxy)（Public IPではないもの）という方法で接続をするんですが、どちらもCloud SQL Proxyでちゃんとアクセスできているかは、ログに出すようにして確認済み。あとは目チェックですごめんなさい。

#### Cloud Run
- vCPU: 1
- メモリ: 512MiB

#### GAE
- F2インスタンス
    - CPU: 1.2GHz
    - メモリ: 512MB
- StandartEnvironment

**GAE**
![a5aa7b85-1c80-24bc-60c0-dbbab07cac56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103745/d3b96219-7472-165a-b118-db74060b82c0.png)

**Cloud Run**
![5fc7bbf7-8627-3588-6d99-db8f41515763.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103745/5227fc17-0563-f4e3-747e-1441674f387d.png)

Cloud Runの方が230ms前後で安定しているのに対して、GAEは350msくらいで推移している感じでした。Cloud Runはまるでローカルの環境叩いているような印象です。

Strapi自体は管理画面を一つ触るだけでもDBに何回かアクセスしに行きますから、一回の通信で100msほど早いくらいでも、積み重なってとても体感速度が速くなったなという印象です。

ちなみになんでSizeが違うんだってヘッダー見たらGAEの方はgzip化されていて、なるほどまだまだGAEにはこういうアドバンテージもあるのね、と思いました。至れり尽くせりなGAEはやっぱり使い所としてはまだまだありそうです。

# Cloud Runへデプロイするまで
まずはデプロイする設定について。基本的に、GitHub Actionsを使ってデプロイは自動化している場合が多いのですが、その話はまた後日の記事で読んでもらえると嬉しいです！

## 1. Dockerfileを用意する
StrapiのDockerfileはいたってシンプルです。[こちらのページ](https://github.com/strapi/strapi-docker/blob/master/strapi/Dockerfile)にStrapiのオフィシャルで公開しているファイルがあるので、そういうものを参考にアレンジしてもいいかもしれません。

```Dockerfile:Dockerfile
FROM strapi/base

WORKDIR /my-path

COPY ./package.json ./
COPY ./yarn.lock ./

RUN yarn install

COPY . .

ENV NODE_ENV production

RUN yarn build

EXPOSE 1337

CMD ["yarn", "start"]
```

## 2. Cloud Buildでコンテナイメージを作りGCRに送信

```sh
gcloud builds submit --tag gcr.io/<GCPプロジェクトのID>/<任意のコンテナイメージのタグ>
```

これは用意したDockerfileで単純にビルドしてイメージをGCR(Google Container Registry)にプッシュしているだけですね。

## 3. Cloud Runにデプロイ
Strapiだと、`strapi/config/env/develop/database.js`のようなファイルに、環境変数を読み込んでDBにアクセス情報を記載するスタイルになっていることが多いかなと思います。

Cloud SQLに接続するときは、Cloud SQL Proxyでのアクセスになるのでこのような形の設定ファイルになっているんじゃないかなと思います。

```js:strapi/config/env/develop/database.js
module.exports = ({ env }) => ({
  defaultConnection: 'default',
  connections: {
    default: {
      connector: 'bookshelf',
      settings: {
        client: 'postgres',
        host: `/cloudsql/${env('INSTANCE_CONNECTION_NAME')}`,
        database: env('DATABASE_NAME'),
        username: env('DATABASE_USERNAME'),
        password: env('DATABASE_PASSWORD'),
      },
      options: {
        "pool": {
          "createTimeoutMillis": 3000,
          "acquireTimeoutMillis": 30000,
          "idleTimeoutMillis": 30000,
          "reapIntervalMillis": 1000,
          "createRetryIntervalMillis": 100,
          "propagateCreateError": false
        }
      }
    }
  }
});
```

Cloud Runへの環境変数の渡し方はコンソールの画面以外にもコマンドラインで渡すことができます。

```sh
gcloud run deploy --image gcr.io/<GCPプロジェクトのID>/<任意のコンテナイメージのタグ> <任意のCloud Runのサービス名> \
--region asia-northeast1 --platform managed --allow-unauthenticated \
--cpu 1 --memory 512Mi \
--set-env-vars "HOST=0.0.0.0" \
--set-env-vars "NODE_ENV=<developなどの環境名>" \
--set-env-vars "DATABASE_NAME=<DB名>" \
--set-env-vars "DATABASE_USERNAME=<DBユーザー名>" \
--set-env-vars "DATABASE_PASSWORD=<DBパスワード>" \
--set-env-vars "INSTANCE_CONNECTION_NAME=<ここはproject-id:asia-northeast1:db-nameみたいな物がCloud SQLのコンソール画面にあるのでそれを入れる>" \
--add-cloudsql-instances <ここはproject-id:asia-northeast1:db-nameみたいな物がCloud SQLのコンソール画面にあるのでそれを入れる>
```

最後の`--add-cloudsql-instances`で、Cloud Runのリビジョンに対してCloud SQL Proxyでそれぞれのインスタンスに接続できるようにする必要があります。
Cloud Runの話になっちゃいますが、この設定や環境変数などはサービスではなくてリビジョンによって異なる、というか変えることができるので、それでデプロイしたら設定が消えちゃっていた、なんてこともたまにありました。

しばらく待つと、Cloud Runのコンソールにサービスが作成されてURLも発行されているので、これでもうWEB上に公開されていると思います。

# 最後に

株式会社dottでは、GCPをメインに利用した開発を行っています。
今回のAdvent CalenderはStrapiをテーマにしていますが、GCP上で運用するときの話が他のdottのメンバーからも投稿される予定なので、ぜひお楽しみに！！