😹

Google App Engineのスタンダード/フレキシブル環境を選ぶときのヒントと設定の注意点

2020/12/26に公開
1

TL;DR

  • GAEのスタンダード環境 vs フレキシブル環境
    • Zennでは最近スタンダードからフレキシブルへ移行した
    • スタンダードの方が安く済むとは限らない
    • フレキシブル環境の方がスケーリングを細かく制御できる
  • GAEのスケーリング設定(app.yaml)はちゃんとしておかないと大変なことになる

はじめに

Zennのバックエンドは Rails(APIモード) on Google App Engine(GAE) という構成で動いています。

https://zenn.dev/catnose99/articles/zenn-dev-stack

GAEの環境(スタンダード vs フレキシブル)

GAEにはスタンダード環境とフレキシブル環境があります。違いは公式ドキュメントのApp Engine 環境の選択を読むのが良いと思いますが、主要な点をかいつまむと以下のようになります。

スタンダード フレキシブル
ランタイム 使える言語・バージョンが決まっている Dockerコンテナを実行できる
最小インスタンス 0 1(常に1以上は起動する)
無料枠 あり なし
メモリ・CPU インスタンスクラスごとに決まっている 比較的自由に設定できる

イメージとしては

  • スタンダード環境の方が気楽にはじめられる
  • フレキシブル環境の方がより細かな設定ができる

という感じでしょうか。

「料金が安いのはスタンダード」とは限らない

ググって見つかる情報を読むと、多くの人は「スタンダード環境の方が安く済みそうだ」という印象を持つと思います。僕もそのような考えから、当然のようにスタンダード環境を選んでいました。しかし、結果として、Zennの場合にはフレキシブル環境の方が料金は大幅に安く済むことが分かりました。

Zennの場合

具体例があった方が読んでいて楽しいと思うので、恥を捨てて実際にかかっていたGAEの料金を載せてしまいます。ほれっ。

GAEの料金推移
※ 料金の推移は、サービスへのアクセス数とはほぼ相関していない

ピーク時には1万円/日近くいってしまっていますが、設定と環境を見直すと¥500/日くらいで済むようになりました。設定をミスらなければPS5を転売ヤーから買うことだってできたはずです。

何が起きていて、どう対策したのかを以下で説明していきます。

スタンダード環境を使うときは自動スケーリングの設定に気をつける

GAEでは、スケーリングの設定をapp.yamlに書きます。スタンダード環境の場合には、以下のように指定することになります。

app.yaml
# 自動スケーリング以外の項目は省略
automatic_scaling:
  target_cpu_utilization: 0.6 # インスタンスを起動するためのCPU使用率のしきい値
  min_instances: 2 # 最小インスタンス数
  max_instances: 20 # 最大インスタンス数
  min_idle_instances: 1 # アイドル状態インスタンスの最小数
  max_idle_instances: 20 # アイドル状態のインスタンスの最大数
  max_concurrent_requests: 80 # これ以上の同時リクエストで新しいインスタンスを立ち上げる(デフォルトは10)

Zennでは上記のような設定をしていたところ、なぜかトラフィックに大きな変化がないにも関わらず日に日に料金が高くなるという現象が生じてしました。¥1万/日近くまでいったのもこの設定のときです。

何かがおかしいと思いGCPのMetric Explorerで確認したところ、アクティブなインスタンスは2〜3にも関わらず、アイドル状態(待機状態)のインスタンスが10〜20個くらい立ち上がっていることが分かりました。

GAEではアイドル状態のインスタンスも課金対象ですので、10〜20個ぶんだけ課金されれば¥1万/日いってもおかしくありません。

対策: max_idle_instancesを厳し目に設定する

そこでmax_idle_instancesの値を20から2に変えたところ、アイドル状態のインスタンスがバカバカと立ち上がることがなくなり、GAEの料金が一気に下がりました。

トラフィックが大きく変わったわけでもないのに、日に日にアイドル状態のインスタンス数が増えていった原因はいまいち分かっていません。

まれに10秒以上のレイテンシが発生する問題

料金が異常に高かったことの他にもうひとつ問題がありました。それはアクセスが急増したタイミングで、まれにレスポンスに10秒以上かかる現象が発生していたことです。

ログを見てみると、その遅延はRailsの起動時間(rails serverにかかる時間)であることが分かりました。つまり、Railsが起動中のインスタンスへリクエストが流されてしまっているということです。

GAEのスタンダード環境ではウォームアップを簡単に設定できるためある程度の対策はできますが、アクセスが急増するとやはり問題が発生するようでした。

※ Gemを遅延読み込みするなどRailsの起動速度を上げる方向でも対策を行いましたが、目に見えた効果はありませんでした。

フレキシブル環境ならインスタンスの準備チェックができる

対策に困っていたところ、@en30さんにGAEのフレキシブル環境では準備チェック(readiness_check)を設定できることを教えてもらいました。

インスタンスが受信リクエストを処理できる状態かどうかを確認します。準備チェックに合格していないインスタンスは、使用可能なインスタンスのプールに追加されません。
app.yaml によるアプリの構成 - 準備チェック

これを使えばRailsが起動中のインスタンスへとリクエストが流れてしまうを防げそうです。

対策:スタンダードからフレキシブル環境へ移行

スタンダード環境では準備チェックに対応していないため、思い切ってフレキシブル環境へと移行することにしました。意外にも移行は簡単で、ほとんどapp.yamlを書き換えるだけで済みました。詳しくはスクラップに書いています。

https://zenn.dev/catnose99/scraps/1951c124257888

フレキシブル環境用のapp.yamlはとりあえず以下のようにしました。参考:フレキシブル環境のapp.yamlの書き方

app.yaml
# 一部を抜粋
env: flex # フレキシブル環境
resources:
  cpu: 2
  memory_gb: 4
  disk_size_gb: 10
automatic_scaling:
  min_num_instances: 1
  max_num_instances: 5
readiness_check:
  path: "/readiness_check" # このURLへアクセスされたときに200を返せば「準備完了」と判断してもらえる

移行後はときどき発生していたレイテンシはピタッとなくなりました。さらに料金も以下のように大幅に安くなりました。

唯一気になるのは、デプロイにかかる時間が5分ほど長くなった点ですね…。

結論

  • スタンダード環境の方が安いとは限らない
  • スタンダード環境を使うならmax_idle_instancesを設定しておいた方がいい
  • スタンダード→フレキシブルへの移行は楽なので、とりあえずスタンダード環境を使って様子見しても良いかも

Discussion

aliyomealiyome

めちゃくちゃ参考になりました。ありがとうございます…🙏