LoginSignup
0
0

More than 3 years have passed since last update.

GCP - Slackボットのパフォーマンスについて(コールドスタート対応)

Posted at

先日のに引き続き、Slackの3秒の壁を越えるための施策について、考えてみたいと思います。

今回は、FaaSの特性であるコールドスタートについて、どうにかならないか考えてみます。

概要

Slack Event APIのエラーハンドリング

SlackのEvent APIは、HTTP200を受け取れなかった際にエラーと判定し、都合3回リトライします。
受け付けるこちら側としては、極力エラーを発生させないようにしつつ、リトライが発生して処理が複数
回実行された場合においても、処理結果が問題とならないように、作り込んだ方が良さそうです。

Event APIのエラー判定

Slackのドキュメント

原文 内容
we are unable to negotiate or validate your server's SSL certificate SSLネゴシエートに失敗など
we wait longer than 3 seconds to receive a valid response from your server 3秒以上待たされたとき
we encounter more than 2 HTTP redirects to follow フォローしなくちゃいけないリダイレクトが2つ以上
we receive any other response than a HTTP 200-series response (besides allowed redirects mentioned above) HTTP2xx 以外のレスポンス

エラー判定時のリトライ

Slackのドキュメント

原文 内容
1. the first retry will be sent nearly immediately 即時
2. the second retry will be attempted after 1 minute 1分後
3. the third and final retry will be sent after 5 minutes 5分後

対策案の検討

インスタンスをアイドル状態にしておけないか

まずは最小起動数などで、インスタンスをアイドル状態にしておけないか調べてみました。
結論的には、出来なさそうです。

  1. Cloud Functions
    Cloud Functionsは、最大インスタンス数(--max-instances)はありますが、
    最小インスタンス数に該当しそうなパラメタはなさそうですね。

    また、AWS LamdaのProvisioned Concurrencyのような機能も現時点では無いですね。

  2. Cloud Run
    Cloud Runの方は、最小インスタンス数を指定する --min-instancesというパラメタがありますが、
    フルマネージドでは使うことが出来ないようです。

    以下はgcloud run deploy の結果。

    ERROR: (gcloud.run.deploy) The --min-instances flag is not supported on the fully managed version of Cloud Run. Specify --platform kubernetes or run gcloud config set run/platform kubernetes to work with Cloud Run for Anthos deployed on VMware.

    ドキュメントの以下の部分がそれを表しているのでしょうか。わからんわ。

    ただし、Cloud Run(フルマネージド)の場合、CPU は使用できません。

定期的に起こしてあげる。

やり方は色々あると思いますが、今回はCloud Monitoringの稼働時間チェックでやってみました。
完全にコールドスタートを封じ込めることは難しいですが、緩和という意味ではかなり効果があると思います。

  1. Cloud Functions
    実際に運用しているチーム向けの勤怠管理ボット(Cloud Functions)で試してみました。

    概要

    Monitoringの稼働時間チェックを組み込み、定期的に関数を叩いてもらって、
    なるべくインスタンスがお寝んねしないようにします。

    稼働時間チェックの設定

    Monitoring - 稼働時間チェック - 稼働時間チェックの作成 より、作成します。

    区分 項目 内容 デフォルト値 設定値 備考
    タイトル タイトル 稼働時間チェックの名前 なし 任意
    ターゲット Protocol ターゲットのプロトコルを指定 HTTP HTTPS
    ターゲット リソースの種類 URL
    ターゲット Hostname チェック対象のURLのベース部分(https://bba.com/articleであれば、bba.com) なし Cloud FunctionsのURL
    ターゲット Path チェック対象のURLのパス部分(https://bba.com/articleであれば、/article) なし
    ターゲット Check Frequency チェック間隔 1m 15m 一旦15分間隔で、運用しながらチューニング
    ターゲット Regions リクエストの送信元 グローバル 米国 Slack APIしか相手にしないので、米国のみでOK
    レスポンスの検証 Response Timeout リクエストが完了するまでの待機時間 10s 3s Slack APIのタイムアウト値に合わせる
    アラートと通知 アラートを作成する この稼働時間チェックが失敗したときに通知するアラートを作成するか On Off

    稼働時間チェックの効果

    稼働時間チェックを適用したボットは、勤怠管理ツールということもあり、
    朝と夕方に処理が集中し、それぞれの間の数時間は全く使われないという
    特性があります。

    適用前は朝or夕の最初の人がほぼ毎回タイムアウトを食らう状況でしたが、
    適用後はかなり「緩和」することができました。

    適用前 適用後
    タイムアウト発生頻度 1日に2回くらい 10日に1回くらい

    それでもタイムアウトする要因

    パターン1 インスタンスが落ちている

    稼働時間チェックで定期的に、関数を叩いてもらっても、インスタンスの居眠りは完全には防げませんでした。
    インスタンスがお寝んねする条件については、ドキュメントからは明確な数値を見つけられませんでしたが、
    1分間隔にしても寝るときは寝てしまっていたので、諦めるしかないですね。

    Active instances for slack-bot-function.png

    パターン2 新しくインスタンスが生成されている

    ユーザのリクエスト同士や、ユーザのリクエストと稼働時間チェックのリクエストがかち合ったりなどして、
    新しいインスタンスが生成されるケースも、1インスタンスあたりの多重度が1なので致し方ないですね。
    また、最小インスタンス数のようなパラメタも無さそうなので、ある程度許容する他ないですね。

    Active instances for slack-bot-function (1).png

  2. Cloud Run
    Cloud Runもリクエストがないと勝手にインスタンスがお寝んねしてしまいます。
    ただこちらは、少なくとも今回確認した限りにおいては、リクエストが来なくなって15分きっかりにインスタンスの停止処理が始まることがログ上確認できました。
    ドキュメント上、明記されている訳ではないので、何とも言えませんが、ひとつの目安とすることはできそうです。

    Cloud Functionsと異なり、インスタンスごとに多重度の設定が出来ますので、
    上記パターン2のような状況にはなりませんので、稼働時間チェックの間隔をなるべく
    短くしてあげることで、ほぼほぼコールドスタートを封じ込めることが出来ると思われます。

測定

では実際に、Apache BenchでCloud FunctionsとCloud Runのパフォーマンスを測ってみます。

構成概要

ボットへのメンションを契機にEvent APIを発行し、Web APIでユーザが投稿したチャンネルへ返す感じにのシンプルな構成で、Cloud FunctionsおよびCloud Runをデプロイしてみました。
しかしながら、Apache Benchでの測定では、Event APIへのレスポンスを返すところまでとしました。

無題のプレゼンテーション.png

Slack Appsの設定(参考)

上記の構成で必要な権限は以下のとおりです。

区分 項目 設定値 備考
OAuth & Permissions Bot Token Scopes app_mentions:read ボットへのメンションを読み取り可能とする
OAuth & Permissions Bot Token Scopes chat:write Web API経由でのチャットへの書き込みを許可
Event Subscriptions Subscribe to bot events app_mention ボットへのメンションを契機にEvent API発砲

ランタイムやフレームワークなど

Functionsは素で入っているFlaskとし、Runの方はFlaskと、興味本位でFastAPIで比べてみることとします。

項目 Cloud Functions Cloud Run
(Flask)
Cloud Run
(FastAPI)
ベースイメージ Ubuntu 18.04 python:3.8-slim-buster
ランタイム Python3.8
Webフレームワーク Flask 1.0.2 Flask 1.1.2
(gunicorn20.0.4)
FastAPI 0.61.1
(uvicorn0.11.8)
サードパーティライブラリ Requests 2.21.0 Requests 2.24.0

拙いですが、検証用コードです。
一応、ちゃんと動作します。

チューニングパラメタ

Functionsはインスタンス数を自動 or 1、Runの方はインスタンスは1で固定とし、インスタンスあたり20多重で比べてみることとします。

項目 Cloud Functions Cloud Functions Cloud Run
(Flask)
Cloud Run
(FastAPI)
リージョン us-east4
CPU(インスタンスあたり) 500ms 1
メモリ(インスタンスあたり) 128MiB 256MiB
最小起動インスタンス数 - -
最大起動インスタンス数 auto 1 1
多重度(インスタンスあたり) 1 20

負荷がけ条件

以下のような組み合わせで、Apache Benchから負荷がけしてみます。

同時実行数 リクエスト数
1 100
2 200
3 300
5 500
10 1,000

結果

負荷がけにあたっては、以下のようにコールドスタートから始まるようにしています。

  • Cloud Functions : 毎回デプロイしなおしてから
  • Cloud Run : 毎回15分以上待って、インスタンスが停止したことを確認してから
  1. スループット(秒あたりレスポンス)
    大体想定とおりの結果となりました。
    Cloud Functionsは、同時実行数が増えるに従い、新たなインスタンスを起動してスケーリングしますので、
    同時実行数が増加に伴い、劣化していきます。
    FlaskとFastAPIでは、ややFastAPIの方が良さそうです。

    無題のプレゼンテーション (1).png

  2. 平均レスポンス(ミリ秒)
    スループットと同様です。

    無題のプレゼンテーション (2).png

  3. 99パーセンタイル(ミリ秒)
    あくまで今回の環境設定においてですが、インスタンス数を増加して同時実行数を増やすしかない
    Cloud Functionsと比較して、Cloud Runでは最初のコールドスタートさえなんとかすれば、
    以下のようにかなりの低レイテンシーを実現することができそうです。

    無題のプレゼンテーション (3).png

  4. インスタンス数
    Functionsのインスタンス数は、同時実行数10のときには、15までいってました。

    無題のプレゼンテーション (4).png

  5. インスタンスの起動にかかる時間
    ログで確認ができるCloud Runのみグラフ化してみました。
    こちらは、Flaskの方が良さそうです。

    とりあえず、さくっと作った環境なので、色々と削ることは出来ると思います(ドキュメント)。

    無題のプレゼンテーション (5).png

結論

Slackボットのように、そこそこ厳しいレイテンシーが要求され、かつ、それなりの同時実行が想定される場合においては、
ほぼほぼコールドスタートを封じ込むことが出来そうな、Cloud Runで実装するほうが良さそうです。

ただし、あくまで私のように、インスタンスの自動スケールが不要な場合に限られますね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0