ISUCON12 ノベルティ制作秘話

皆様こんにちは、カヤックのやましんです。

本選出場された皆様おめでとうございます!
なんと今回のISUCONはサイバーエージェント様とカヤックで出題しており、その関係で本選出場者に送られるノベルティをご用意させていただきました。
このブログではそれらにまつわる話をご紹介できればと思います。
なお、予選問題はカヤックの作問チームが担当していまして、本題に入る前にISUCON12予選について少し触れさせてください。

ISUCON12予選のおさらい

www.youtube.com

予選紹介動画、とてもかっこいいですね! お題がマルチテナントSaaSということですが、弊社の2つのサービス

から着想を得て作問されました。問題にどの要素が取り込まれているか探してみてくださいね。

ノベルティ制作ストーリー

ISUCON12では本選出場者にノベルティが送られます。
出題チームからもノベルティが出せるということなので、何がいいかなーとエンジニアが集結してアイディアを出し合いました。
実は弊社には古民家Make Room(通称: ラボ棟)と呼ばれるレーザーカッターを設置している施設があります。
ラボ棟のレーザーカッターを使えばアクリルを加工することができるので、アクリルを使った何かを作ろうとなりました。

とは言いつつも、アクリルをカットするだけのキーホルダーのようなものだとシンプルすぎるので

  • アクリルスタンド
  • 椅子のプラモデル
  • はめ込み式のサイコロ

などがアイディアとして出てきました。
椅子のプラモデルはエッジの効いたアイディアだったんですが、組み立てる技量が必要になったりモデルのデザインが大変そうという理由で採択には至りませんでした。
最終的にはISUCON12予選の作問をしたカヤックより予選突破にちなんだグッズを渡すのはどうだろうか、という出題者の一人でもある谷脇の発案でアクリルスタンドを作ろう!ということでスタートしました。

しかし、取り掛かったのは諸般の事情により発送リミットの10日前。
作るものとしてはISUPORTS入賞者に送られるトロフィーという設定まではすぐに決まったものの、とにかく時間がないのでデザインをどうしよう…となりました。
誰もイメージがなかったのでとりあえず5秒でモックアップを作るところから始めました。
ちなみに、今回使用しているツールはAffinity Designerというベクターツールです。

アクリルスタンドmock

モックデザインとはいえ流石に味気なさすぎるなと思ったのでちょっとだけ手を加えることにしました。

手を加えたもの

ISUCON12予選のテーマがISUPORTSというタイトルそのまま、e-sportsをイメージしていたというのもあったので

  • コンセプト: 近未来UI
  • e-sportsっぽさが出るようにメーター類を配置
  • 何かを書き込めるスペースを設ける

というのを考えつつ、デザインしました。 途中経過を関係者に共有しながら進めていましたが、特に反対意見もなかったので好きなようにやらせていただきました。
(ので、アクリルスタンドのデザインは筆者の趣味が出ています)

アクリルを発注したり梱包の検討を進めてなんとかなりそうだなと思っていたところ、「差し紙あったほうがよさそうですね〜」という話が出てきました。
アクリルのデザインで頭いっぱいだったので何も考えてなかったわ…となり、ノーアイディアandノータイムでかなり焦りました。
とりあえず小一時間でそれっぽいのを用意してみました。

取扱説明書mock

関係者の反応がよかったので勢いで残りを仕上げ、なんとか間に合ってよかったよかったとなりました。
ちなみに、制作風景はこんな感じです。

制作風景1
制作風景2
みんなで梱包

遊び方

今回用意したアクリルスタンドなんですが、組み立てて終わりではないんです。 ISUPORTSアクリルスタンドに2つの楽しみ方をご用意しました!

カスタマイズ

今回のアクリルスタンド内には2箇所ほど文字を書き込めるスペースを用意しています。 好きなものを書き込んでいただいてよいのですが、こういった遊び方があります。

遊び方4コマ

  1. アクリルをきれいに拭きます
  2. 書き込みスペースにシールを貼ります(直接書き込んでもOK)
  3. 予選競技中に載せ替えたミドルウェアor今後載せ替えてみたいミドルウェアを記入します
  4. 組み立てる!

遊び方

自分がISUCONの競技中に載せ替えたいor載せ替えたミドルウェアを書くことでなんと、「サービスの基盤を載せ替える」と言う遊び方ができます!
(例で書いているkatsubushiはカヤックで使用しているOSSのID発番のミドルウェアです)

光らせる

透明のアクリルはLEDを当てることにより彫刻面が光ります。
100円ショップで売られているLEDスタンドなどを使っていただくことでアクリルスタンドが更にかっこよくなります。

Light up!

ご注意

お手元に届いたアクリルスタンドですが、いくつか注意点があります。

アクリルはアルコールに弱いので、ウェットティッシュを使用する場合はノンアルコールタイプをご使用ください

アルコールで拭いてしまうとアクリルが劣化して割れてしまうのでご注意ください。
もし、水分をつけてメンテナンスしたい場合は中性洗剤を少し混ぜた水で洗うと汚れが落ちやすいです。

アクリルは傷がつきやすいので、汚れを拭き取る場合は柔らかい布で軽く拭いてください

今回用意したアクリルは透明ということもあり、傷がつくと目立ってしまいます。
指紋などがついて汚れてしまった場合はメガネ拭きなどの柔らかい布でメンテナンスしてみてください。

アクリルの切断面(側面)は鋭くなっているので取り扱いにご注意ください

レーザーカッターで加工したアクリルの切断面は鋭利な状態になっています。
側面を持ったままスライドさせるとスパッと切れてしまうことがあるのでそっと持ち上げてください。

まとめ

いかがでしたでしょうか。
ノベルティを内製することも中々ないので、制作に関する記事が書けてよかったように思います。

トロフィーは以下のような形でお手元に届きます。

完成品

カヤックではレーザーカッターや3Dプリンタを使ったものづくりに興味のあるエンジニアを募集しています。

hubspot.kayac.com

SLI/SLO運用の実践 shimesabaによる指標モニタリング

カヤックSREの池田です。
先月は、カヤックのプロダクトの一つ『Tonamel』で導入したエラーバジェット算出ツール『shimesaba』の話をしました。

techblog.kayac.com github.com

今回は、実際にどのようにSLI/SLOを運用しているのか?という内容をshimesabaを使った設定例を交えつつ話します。 SLI/SLOの運用にお悩みの方の助けになれば幸いです。

最初のSLI/SLOはどう決定したのか?

SLI/SLOの運用を始めるにあたって、多くの人が悩むのは以下の2つだと思います。

  • 一体何をSLIとすれば良いのか?
  • 最初のSLOはどのくらいにしたら良いのか?

つまりは、最初の1歩をどうしたら良いか?と言う話ですが、こちらに関しては2つ参考になるものがあります。

  1. 『SLO決定のためのArt of SLO』 https://sre-next.dev/2022/schedule/#jp49 *1 を参考にする。
  2. サイトリライアビリティワークブックを参考にする。

先月の記事で紹介した、『shimesaba』を導入したプロダクト『Tonamel』では、2つ目のサイトリライアビリティワークブックを参考にしました。

より具体的には、こちらの書籍の2章全体、特に2.2.2と2.3.3を参考にしました。 最初のSLIについては、書籍には「プロダクトのメインになるアプリケーションのタイプによって異なる "一般的なもの" を採用するのが手堅い」とありました。以下はその例です。

  • リクエスト駆動: いわゆる普通のHTTPサービスなど
    • 可用性: レスポンスに成功したリクエストの比率
    • レイテンシー: 閾値よりも高速に処理されたリクエストの比率
    • 品質: レジリエンスやグレイスフルデグレーション等を備えてる場合、品質低下の起きてない正常なリクエストの比率
  • パイプライン: バッチプロセスや機械学習パイプライン等
    • 新鮮さ: 時間の閾値よりも最近に更新されたデータの比率
    • 正確性: 正しい出力につながった入力データの比率
    • カバレッジ: 正常に処理された有効なデータの割合
  • ストレージ: Amazon S3やGoogle Cloud Storageのようなサービスなど
    • 耐久性: 期待するデータのうちロストしてない比率

Tonamelの場合は、アプリケーションがHTTPサービスの形をとっているのでリクエスト駆動となります。
グレイスフルデグレーション等は特に行っていないので以下の2種類のSLIからはじめました。

  • 可用性: Application Load Balancer(ALB)のTarget 5xx の比率
  • レイテンシー: Application Load Balancer(ALB)のTarget Response time (p95,p90,平均)

SLOに関しては、直近のエラーバジェットを計算してみるのが手っ取り早かったです。 現在進行系でエラーバジェットが減り続けるようなSLOの場合は最初の一歩としては厳しすぎましすし、 逆に、エラーバジェットの残りが100%で張り付く場合は、最初の一歩としては緩すぎます。
現時点でのエラーバジェットの残りが80~95%くらいで落ち着くSLOを設定しました。

具体的には、図のように、エラーバジェットが減り続けるわけでもなく、かといって1%も減ってない状態ではないSLOを設定しました。 皆様も同様の調整をすれば、最初のSLI/SLOをそれほど悩まずに決定できると思います。

shimesabaを使う場合

shimesabaではMackerelで発報されたアラートを元にエラーバジェットを計算するので、Mackerel側に以下のようなアラートを設定します。

  • 可用性のSLOとして
    • 式監視: ALB Target 5xx / (ALB target 5xx + ALB target 2xx) > 0.1%
  • レイテンシーのSLOとして
    • ホストメトリック監視: ALB Target response time p90 > 300ms
    • ホストメトリック監視: ALB Target response time p99 > 500ms
    • ホストメトリック監視: ALB Target average response time > 200ms

この監視ルールをMackerelのCLIツールmkrで出力したものが以下となります。

$ mkr monitors | jq '.[] | select(.name | startswith("SLO"))'
{
  "id": "<monitor_id>",
  "name": "SLO availability request_error_rate > 0.1%",
  "type": "expression",
  "expression": "alias(scale(divide(\nhost(\n  <host_id>,\n  custom.alb.httpcode_count_per_group.<target_id>.target_5xx\n),\n sum(group(host(\n  <host_id>,\n  custom.alb.httpcode_count_per_group.<target_id>.target_2xx\n),host(\n  <host_id>,\n  custom.alb.httpcode_count_per_group.<target_id>.target_5xx\n))\n\n)\n),100), 'request_success_rate')",
  "operator": ">",
  "warning": 0.1,
  "critical": null
}
{
  "id": "<monitor_id>",
  "name": "SLO latency ALB target response time p90 > 300ms",
  "type": "host",
  "metric": "custom.alb.response_per_group.<target_id>.time_p90",
  "operator": ">",
  "warning": 0.3,
  "critical": null,
  "duration": 1,
  "maxCheckAttempts": 1,
  "scopes": [
    "prod"
  ]
}
{
  "id": "<monitor_id>",
  "name": "SLO latency ALB target response time p99 > 500ms",
  "type": "host",
  "metric": "custom.alb.response_per_group.<target_id>.time_p99",
  "operator": ">",
  "warning": 0.5,
  "critical": null,
  "duration": 1,
  "maxCheckAttempts": 1,
  "scopes": [
    "prod"
  ]
}
{
  "id": "<monitor_id>",
  "name": "SLO latency target response time average > 200ms",
  "type": "host",
  "metric": "custom.alb.response_per_group.<target_id>.time",
  "operator": ">",
  "warning": 0.2,
  "critical": null,
  "duration": 1,
  "maxCheckAttempts": 1,
  "scopes": [
    "prod"
  ]
}

監視設定の最初の閾値は、かなり野心的で厳しい基準にしておくところがポイントです。 上記の監視ルールを使った最初のshimesabaの設定としては以下のようになります。

required_version: ">=1.2.0"

destination:
  service_name:  'prod'
rolling_period: 1d       # 運用を開始したら 28dくらいの長めのものに増やす
calculate_interval: 1m   
error_budget_size: 0.5% 

slo:
  - id: availability
    alert_based_sli:
      - monitor_name_prefix: "SLO availability"

  - id: latency
    alert_based_sli:
      - monitor_name_prefix: "SLO latency"

最初の設定では rollikng_period1d くらいの短めにしておきます。 最初はエラーバジェットがすぐに枯渇すると思います。
しかし、監視ルール側の閾値を徐々に緩めていけば、エラーバジェットが減り続けるわけでもなく、かといって1%も減ってない状態ではないSLOにたどり着くと思います。 そして、SLOが決まったら長めのrolling_periodをとって、最初の一歩を踏み出すと良いでしょう。

SLOの継続的な改善

最初のSLI/SLOを設定できたので、運用が始まりました。 Tonamelでは週次の定例があり、その定例のタイミングでSLOに関する見直しをすることがありました。 最初のうちは、ほぼ毎週見直しをしました。 SLOの見直しには、サイトの信頼性に関わる定性・定量とエラーバジェットの関係があるのかを考えました。

例えば、以下のようなものとエラーバジェットが関係しているのか?ということに注目しました。

  • お問い合わせ件数
  • 『つながらない』『見えれない』などの反応の多さ (可用性のエラーバジェット)
  • 『重い』『遅い』などの反応の多さ (レイテンシーのエラーバジェット)

プロダクトにとって『サイトの信頼性』に関係する現象というのは、様々でこれを見ておけばいいというものがあるとは思えません。
自分たちのプロダクトでは、何を重要視していてどういう事が起きたら、エラーバジェットが損失するというのが良いのか?ということを考えていく必要はあります。Tonamelの場合は、カスタマーサポートの担当者が週に1度、お問い合わせの件数をその内訳も含めて共有する機会があるので、エラーバジェットの増減とお問い合わせの件数についてを注視していました。
他にも開発チームのエンジニアに、なにか問題が起こっていないか?というヒアリングも行なっていました。

その経験を踏まえて、以下の表のようにして見直していくと、ある程度野心的なSLOに収束していくと思います。

エラーバジェット サイトの信頼性に対する評価 アクション
損失が20%以下で安定 特に問題はなさそう (そのまま様子見)
損失が20%以下で安定 なにか問題がありそう SLOを厳しくしてみる
減り続けてる 特に問題なさそう SLOを緩めてみる
減り続けてる なにか問題がありそう (早めにプロダクトを改善しよう!)
損失が20%より多くて安定 特に問題なさそう SLOを緩めてみても良いかもしれない
損失が20%より多くて安定 なにか問題がありそう (注意深く様子見)

特にSLI/SLOの運用の開始初期では、非エンジニアのチームメンバーやユーザーにサイトの信頼性に関する情報を収集して、積極的にSLOを厳しくしたり緩めたりするトライアンドエラーが大事だと感じました。
最初から完璧なSLI/SLOを求めるのではなく、徐々に良いものにしていくという運用をすることで、導入もしやすくなると思います。

shimesabaをお使いの場合は、この辺のSLOを緩めたり厳しくしたりすることは、監視ルールの閾値を変更するだけで実現できるので、柔軟に変動させることができると思います。

プロダクト特有のSLOの設定

最初の1歩では、一般的なSLI/SLOだけを設定して開始しました。 SLI/SLOの運用を続けていくと、場合によっては最初に設定したもの以外のプロダクト特有のSLI/SLOを設定する必要性も出てくる可能性はあります。
Tonamelの場合は、SLI/SLOの運用を開始したときは『可用性』と『レイテンシー』のエラーバジェットのみでしたが、途中で『品質』という名の新しいSLI/SLOを設定しました。
このSLI/SLOの設定の背景には、アプリケーションがGraphQLという技術を採用していることが関係しています。

GraphQLという技術ではエラーの取り扱い方にある一定の文化があり、『HTTPリクエストに対するレスポンスはHTTP 200 Okで正常ではあるが、レスポンスの内容としてはエラーである。』という状況があります 具体的には、HTTP 200 Okで以下のようなレスポンスボディを返す場合があるということです。

{
  "errors":[
    {
      "message": "error message.",
      "extensions":{
        "code":"INTERNAL_SERVER_ERROR"
      }
    }
  ]
}

このようなレスポンスを通称GraphQL Errorと呼んでいますが、このGraphQL Errorが発生すると、フロントエンド側でエラーハンドリングをすることになります。 エラーハンドリングした結果、対応できない場合はユーザーに対してエラーメッセージを表示することになります。 ユーザーに見える形でのエラーメッセージが頻出すると、それはサイトの信頼性低下に繋がってしまいます。 ですので、このGraphQL Errorの発生率をSLI/SLOとして追跡することが重要だということがわかりました。 しかしながら、GraphQL ErrorはHTTPリクエストへのレスポンスとしては正常に返せているので、最初に設定した『可用性』というALBのTarget 5xx の比率をSLIとして用いたSLOには反映されません。 そこで別途、Kinesis Data Streamで収集しているアクセスログからGraphQL Errorの発生件数とGraphQLリクエスト件数を集計し、サービスメトリックとしてを投稿しました。 そして、GraphQL Errorの発生件数/GraphQLリクエスト件数 = GraphQL Error rateをSLIとした『品質』と言う名前のSLOを設定しました。

より具体的には、以下のような式監視を追加し、shimesabaの設定を変更しました。

監視ルール

$ mkr monitors | jq '.[] | select(.name | startswith("SLO quality"))'
{
  "id": "<monitor_id>",
  "name": "SLO quality graphql_error_rate > 0.5",
  "type": "expression",
  "expression": "alias(\nscale(divide(\n  service(\n prod,\n \"graphql.error.request_count\"\n)\n,service(\n prod,\n \"graphql.requests.request_count\"\n)),100),'graphql_error_rate')",
  "operator": ">",
  "warning": 0.5,
  "critical": null
}

shimesabaの設定

required_version: ">=1.2.0"

destination:
  service_name:  'prod'
rolling_period: 28d      
calculate_interval: 30m   
error_budget_size: 200m   

slo:
  - id: availability
    alert_based_sli:
      - monitor_name_prefix: "SLO availability"

  - id: latency
    alert_based_sli:
      - monitor_name_prefix: "SLO latency"

  - id: quality
    alert_based_sli:
      - monitor_name_prefix: "SLO quality"

以上の設定で、1分間のGraphQL Errorの比率が0.5%を超えた場合にエラーバジェットが削れるSLOが設定できました。

このように、アプリケーションが採用している技術やプロダクトの性質由来で、追加のSLI/SLOを設定することや最初のSLI/SLOの定義を変えることはありえます。

まとめ

今回は、SLI/SLOを具体的にどう設定するのか、shimesabaを用いながらどう運用していくのかの話をしました。 これからSLI/SLOの運用をこれから開始する場合は、以下の点を踏まえておくと良いでしょう。

  • 何をSLIとするか?: 最初は、一般的なもので良い(リクエスト駆動のアプリケーションの場合は『可用性』と『レイテンシー』)
  • 最初のSLOはどのくらいにするか?: 試しにエラーバジェットを計算して80%~95%になるくらい。
  • 定期的に見直しをして、積極的にSLOを厳しくしたり緩めたりするのは大事。
  • 場合によっては、プロダクト特有の新たなSLI/SLOを設定することもある。

実際にSLI/SLOの運用をしてみたところ、個人的にはエラーバジェットを常に20%くらい使った状態で安定し続けるくらいのSLOが良いと思っています。 エラーバジェットは、文字どおりエラーに関する予算ですので、使いすぎても、使わなさすぎてもあまり良くないと感じています。 以上、皆様のSLI/SLOの運用にお役に立てば幸いです。

カヤックでは、SLO運用に興味があるエンジニアも募集しています

hubspot.kayac.com

*1:2022/5/15に開催されてた『SRE Next 2022』でグーグル合同会社による素晴らしい発表