Gunosy Tech Blog

Gunosy Tech Blogは株式会社Gunosyのエンジニアが知見を共有する技術ブログです。

AWS Summit Tokyo 2023 に参加してきました

こんにちは、hyamamoto、TksYamaguchi、imamura、m-hamashita です。

今回の記事は、4/20, 4/21 の 2 日間に開催された AWS Summit Tokyo 2023 に参加してきたので、その参加レポートになります。

Gunosy によるコンテンツ

Gunosy 社では、AWS Summit Tokyo 2023 にて、DRE&MLOps と SRE チームがコンテンツを発表しました。 ここでは実際にその発表した内容やその時の様子をご紹介します。

DRE&MLOps:「Gunosy におけるデータの民主化を促進するデータ基盤」

紹介者: hyamamoto

DRE&MLOps チームでは「Gunosy におけるデータの民主化を促進するデータ基盤」というタイトルで事例展示とミニステージでの発表を行いました。 基本的な技術的な構成は過去のブログ*1*2で紹介したものの延長にはなりますが、今回はデータ基盤の民主化ということでもう少し踏み込んだ組織的な取り組みについてもご紹介できたのではないかと思います。

弊社におけるデータ基盤の民主化として、分析環境および開発環境のユーザー体験の向上を目指して取り組んでいます。 これは、データ基盤チームはデータ基盤アプリケーションを社内に提供するチームとして働くことで、組織のデータ活用を促進し意思決定を加速させることを目指しています。 このためにデータの整備や分析作業はそのドメインに詳しいチームが行える体制を整えることが重要になってきます。 下記は、データ基盤チームが取り組んでいる施策の一部です。

  • 分析環境の民主化によるデータ分析体験の向上
    • ユーザーのニーズに沿った複数の分析環境を提供
      • Amazon QuickSight, Redash, Google SpreadSheets など
    • SQL を使わずに品質の高いデータにアクセスできるように整備
      • Terraform で管理された Athena View を活用したデータマート
  • 開発環境の民主化によるプロダクトへのデータ活用体験の向上
    • データ基盤の開発を委譲できる体制の整備
      • 定型業務のコードを単純化
      • ペアプロ、ドキュメント整備といった施策

発表資料については下記で公開しているので良かったらご覧ください。

speakerdeck.com

ミニステージ発表の様子

事例展示ではインタラクティブに様々なお話をさせていただきました。 場所がセッション会場と近かったというのもあり、思っていたよりもたくさんの人に来ていただけたのはよかったです。 特に、自分たちの事例紹介をするだけでなく、これからデータ基盤を作っていきたい方やより具体的な課題感を持った方などと幅広い議論できたのは新鮮な体験でした。

事例展示の様子

SRE: 「Gunosy(グノシー)が選んだ開発者に負担をかけないスマートなセキュリティ対策」

紹介者: TksYamaguchi

SRE チームでは「Gunosy(グノシー)が選んだ開発者に負担をかけないスマートなセキュリティ対策」というタイトルでパートナーセッションで事例発表を行いました。 追加していただいた席も埋まり、立ち見の方も出るくらい大盛況でした。 またアンケート結果もおおむね好評で、弊社の開発体制、セキュリティへの取り組みの過程、Snyk の選定理由についてお伝えできた感触があります。

弊社におけるセキュリティへの取り組みの特徴は以下の2つの点です。

  1. セキュリティを専門に担当している部署・チームがなく、SRE チームと各開発チームが共同して対応している
  2. 本番環境への適用からの Shift-Left の流れ

1 点目の「セキュリティ専門に担当している部署・チームがなく SRE チームと各開発チームが共同して対応している」ですが、セッションの中で十分な発表ができていないところになりますので、この場で取り組みの内容をご紹介させていただきます。

セキュリティ対応フロー

スキャン → トリアージ → 対応というセキュリティ対応フローの中で、以下のように SRE チームと各開発チームで責任分担をしています。

アクション 責任・対応
スキャン システム化・自動化
トリアージ SRE チーム
対応 各開発チーム

SRE チームは「開発チームが 新たなプロダクト価値の創造 に集中できる状態を作る」をミッションとして活動を行っていますので、セキュリティ対応では開発チームに負担をかけずにセキュリティ対応を行うを基本方針とし、このような責任分担で行う計画を立てました。

この責任分担で実施した結果どうなったかというと。

セキュリティ対応の対象を拡大(Shift-Left、アカウント・システム)していくに従い、SRE チームで実施しているトリアージがボトルネックになり始めました。 これは、SRE チームのマンパワーが限られる中で、全社横断的にシステムを見ていることによる過負荷が原因でした。

そこで、SRE チームが行うトリアージを定常業務に組み込める程度の負荷に抑えられるか?という観点をプラスして再度技術選定を行い、結果として Snyk を採用しています。

AWS Security Hub を使用した AWS 環境のミスコンフィグレーションの追跡とトリアージも Amazon Security Lake へ変更しましたが、この辺の詳細は Security-JAWS、JAWS-UG で発表していますので、よろしければご覧ください。

speakerdeck.com speakerdeck.com

2 点目の「本番環境への適用からの Shift-Left の流れ」ですが、これはセッションで発表した通り、以下の流れで導入を進めています。

  1. 本番で動作しているインフラ環境をスキャンして重大なセキュリティリスク(大きな穴)がない状態にする
  2. インフラ環境に大きな穴を再度発生させないように IaC コードをスキャンして重大な脆弱性がある場合は CICD で止める
  3. アプリケーションのコンテナ Image の OSS ライブラリをスキャンして重大な脆弱性あるライブラリを使用していない状態にする
  4. アプリケーションコードをローカルでのスキャンに加えて、PR でもスキャンを実施し重大な脆弱性がある場合は CICD で止める

現在の状況は、4 の段階でパイロットプロジェクトへの導入を進めている段階となります。

パートナーセッションは録画が公開されないようなので、簡単に Snyk の選定理由をご紹介します。

Snyk の各製品の選定理由

Snyk Container

Amazon Inspector では Tag でスキャン対象の絞り込みができず、最新 Image では対応済みの脆弱性も含めて検知結果として出力されるため、トリアージの負荷が高すぎて定常的な対応ができませんでした。

Snyk Container では Tag での絞り込みと継続的なスキャンができるため、例えば本番環境で動作する image に常に latest Tag をつけるような運用を行えば、Snyk Container で継続的なスキャンができることになります。本番環境で動作している image のスキャン結果だけを Web 画面で確認できるためトリアージの負荷を最小限に抑えることができます。

Base Image や OSS ライブラリの脆弱性の場合、メジャーバージョンをあげるとどれくらい脆弱性が減るのか、マイナーバージョンだったらどれくらい脆弱性が減るのか、をあらかじめ Web 画面上でシミュレートしてくれるので、脆弱性が 0 件になるから今回はメジャーバージョンを上げようとか、Critical がなくなるからマイナーバージョンだけ上げようとか、緊急度と余力により開発者側で対応の選択が取りやすいこと、あらかじめどれくらい脆弱性が無くなるのかがわかるので、開発者のセキュリティ対応に対するモチベーションを維持できることが期待できます。

Snyk IaC

全社一括で無視して良いルール、無視してほしくないルールを、Web 画面の Organization 機能を使用することで簡単に設定できるので、初期設定のコスト削減と、ノイズが減ることで SRE が行うトリアージの負荷軽減が期待できます。

スキャンして検知されたミスコンフィグレーションに対して、どう修正したら良いかのわかりやすいドキュメントがあることで実際に対応する開発者の負担軽減が期待できます。

Snyk OSS

GitHub リポジトリを取り込むことで自動で PR を作ってくれることで、開発者の負担軽減、SRE が行うトリアージおよび開発者の対応の負担軽減が期待できます。

Fix がない脆弱性について fail させないオプションがあり、これを活用することでノイズが減り、SRE が行うトリアージおよび開発者の負担感の軽減が期待できます。

Snyk Code

VS Code 上の拡張機能で動作可能で、開発者の作業との親和性が高く、オンボーディングの負担感の軽減が期待できます。

スキャン結果に対してどう修正すれば良いかの提案をしてくれるので、開発者が対応しやすい。提案自体はコピーペーストで動作するコードではないのですが、いわゆるベストプラクティスに沿ったコードを提案してくれるので、開発者の対応もしやすいですし開発者自身のレベルアップも期待できます。

Snyk Web 画面

セキュリティリスク管理という側面において、Web 画面が見やすくて使いやすいです。

具体的には、Critical、High の指摘がどこにどれくらい存在しているか?ということが一覧で見えること。攻撃コードが存在している脆弱性や、セキュリティリスクの高い脆弱性が存在しているリポジトリ、Image が何か?が追跡しやすいことを意味しています。

Web 画面での絞り込みも高機能でありつつ操作が簡単で、Web 画面上で重要度別等の Tag を付けておくと、開発チーム別(Organization 機能であらかじめ分離しておく必要があります)に 重要な Image やコードに絞り込んで脆弱性の有無を確認できるので、緊急に対応する必要がある脆弱性をピンポイントで確認できるため、最小の負担で最大限にセキュリティリスクを低減する対応が期待できます。

発表資料について

発表資料については AWS から後日公開されますので、公開されましたらご確認ください。

発表の様子

セッションの紹介

Everything fails, all the time : 分散システムにおける耐障害性のある設計について (AWS-27)

紹介者: imamura

分散システムの問題点や、それに対する解決策を紹介するという内容のセッションでした。

例えば旅行予約アプリの場合、交通手段の予約、決済など、内部にさまざまなコンポーネントを持っています。 モノリスから始まったアプリケーションであっても、成長するにつれて

  • 一部のコンポーネントだけをスケールアウトさせたい
  • 他のコンポーネントに影響を与えずに機能追加したい
  • 開発者の増員に合わせてコンポーネント単位で分担したい

といった要望が出てきます。 これらは、各コンポーネントをマイクロサービスとして切り出す (分散システム化する) ことで実現できます。 しかし、分散システムにはネットワークを介することによる障害点の増加というデメリットもあるため、耐障害性を担保するための設計が必要になります。

1 つ目のトピックは、ネットワークの瞬断などの短期的な障害への耐性です。 これはサービスの呼び出しに自動リトライを入れることで対応できます。 リトライ間隔の調整にエクスポネンシャルバックオフやジッターといった手法を使います。 普段 AWS SDK を使っている人にとっては馴染みのある話だと思います。 また、自分で実装する方法として、StepFunctions のリトライ機能と Lambda を使う例が紹介されていました。

2 つ目のトピックは一部サービスの長期的な障害への耐性です。 サービス自体の復旧はケースバイケースですが、分散システムの観点では、サーキットブレーカーを導入して異常なサービスの呼び出しを遮るという対策が可能です。 逆に、全てのサービスが同期呼び出しで直接繋がっていると、一部サービスの障害がシステム全体に伝播してしまいます。 導入方法としては、ライブラリを組み込む (例 : Hystrix) 、サービスメッシュを使う (例 : Envoy + App Mesh) などがあります。 加えて、StepFunctions を使って自前で実装することもできるようです。 紹介されていた方法を以下で説明します。

  • サービス間通信に StepFunctions を挟み、正常系では単に StepFunctions 内の Lambda が通信を仲介する
  • 呼び出し先サービスが異常な場合、Lambda がサービス名を DynamoDB に保存する
  • DynamoDB にサービス名が登録済みの場合、以降の呼び出しでは StepFunctions が (実際にサービスを呼び出すことなく) 失敗ステータスを返す
  • DynamoDB のアイテムに TTL を設定しておき, 失効後の初回リクエスト時にヘルスチェックが行われる

3 つ目のトピックはデータの一貫性です。 マイクロサービスでは、サービスごとに用途に適した異なるデータベースを使う (ポリグロット・パーシステンス) ことができますが、データベースが分かれていることによってデータの一貫性が損なわれる可能性があります。 これを防ぐには、複数のサービスを跨ぐ処理が途中で失敗したときに、それまでに済んでいる操作をロールバックする補償トランザクションが必要です。 実装例として、トランザクションや失敗時のロールバックを StepFunctions のワークフローとして記述する方法が紹介されていました。

4 つ目のトピックはカオスエンジニアリングです。 サービスごとの状態や呼び出しパターンが無数にある分散システムでは、システム全体の障害パターンを全て事前に予測するのは困難です。 しかし、システムの一部で意図的に障害を起こす実験を行うことで、問題点や足りていないログなどを見つけ、耐障害性を向上させることができます。 AWS では Fault Injection Simulator というサービスで実現できるようです。

以上がセッションの内容です。 StepFunctions の応用例が豊富に紹介されていたのが印象的でした。 これらの実装方法を実際に業務で使うかは分かりませんが、StepFunctions による簡単な実装を見ることで、補償トランザクション、サーキットブレーカーといった概念への理解が深まった気がします。

アーキテクチャ道場 2023 ! (AWS-28)

紹介者: m-hamashita

まずお題が与えられて、それをお題の問題を解決する設計を説明するという内容のセッションでした。 今回は 1 つ目のお題について紹介します。

1 つ目のお題は以下のようなものでした。

  • オンラインボードゲームのプラットフォームのバックエンドを刷新したい
    • 現状はオンプレミスで稼働しているため、ゲームセッションをホストするサーバーフリートの伸縮性が低い
  • AWS を利用してプレイヤーの増減に応じてサーバーフリートのリソースをスケールしたい
  • セッション割り当てや、オートスケーリングをおこなうコントロールプレーンを設計する

このお題に対して、以下のようなテーブルを用意していました。

  • インスタンステーブル
    • インスタンス情報とインスタンスが使用しているゲームセッションのキャパシティを管理
  • セッションテーブル
    • セッションとセッションが割り当てられたインスタンスとのマッピングを管理

基本的な流れとしては次のようになります。 ゲームセッションが開始されると、インスタンステーブルに対して割り当て先のインスタンスの保持セッション数をインクリメントし、セッションテーブルにレコードを追加します。 その後 IP アドレスとセッション ID を返すことで、ゲームセッションを開始することができます。 ゲームセッションが終了すると、セッションテーブルからレコードを削除し、インスタンステーブルの保持セッション数をデクリメントします。

次に以下のような問題分割をおこなっていました。

  • サーバーとセッションのマッピング
    • セッションの割り当て
    • セッションの割り当て解除
  • オートスケーリング
    • スケールアウト
    • スケールイン

まずサーバーとセッションのマッピングについてです。 セッションの割り当てと割り当て解除の流れは先述の通りです。 一方で、セッション数が増えるにつれ、DB への書き込み負荷が増加しボトルネックとなることが想定されます。 ナイーブな新規セッション割り当て方法と問題点は以下の通りです。

  • セッション数が少ないインスタンスを選択
    • セッション数が少ないインスタンスのレコードに対するデータ更新が集中する
  • ランダムにインスタンスを選択
    • インスタンスリストの選択にテーブルをスキャンする必要がある

この問題を解決するために、インスタンスをツリー構造で管理されていると考え、インスタンステーブルに親インスタンス ID と子インスタンス ID のリストを保持することで実現していました。 そうすることで、テーブル全体をスキャンせずに効率よくインスタンスを選択することができます。 具体的な選択方法としては、以下のようになります*3

  • 根からスタートし、子インスタンスをランダムに探索
  • インスタンスを割り当てることができるか確認し、できない場合はさらに子インスタンスをランダムに探索を繰り返す
    • 割り当てることができれば、そのインスタンスを返す
  • 子インスタンスが存在しない場合は、親インスタンスに戻って未探索の子インスタンスを何回か探索、そこでも割り当てできなかった場合はさらに親インスタンスに戻って探索を繰り返す
  • 親インスタンスが存在しない、または既定の探索回数を超えた場合はエラーを返す

次にオートスケーリングについてです。 オートスケールをおこなうために全体の保持セッションの割合を取得しようとすると次のような問題があります。

  • インスタンステーブルをポーリングする場合
    • テーブルをスキャンする必要があり、インスタンス数が増えるにつれてコストが増加する
  • 別テーブルで管理する
    • セッション割り当て、割り当て解除が増えるとデータ更新が集中する

この問題を解決するために全体の保持セッションの割合を監視するのではなく、インスタンス単位で保持セッションの割合を監視してスケールするようにしていました。 つまり、あるインスタンスの保持セッションの割合が閾値を超えた場合にスケールアウトするようにしていました。 これにより、テーブルをスキャンする必要がなく、またデータ更新が集中することもなくなります。

そうすることで、オンラインボードゲームのサーバーをスケールすることができます。 それぞれの構成要素に対する AWS サービスは次のようになります。

  • セッション割り当て/割り当て解除 API: Amazon API Gateway + AWS Lambda
  • DB: Amazon DynamoDB
  • オートスケーリング: DynamoDB Stream + AWS Lambda

以上がセッションの紹介でした。 ツリー構造でインスタンスを管理することで、書き込み負荷を軽減するところがとても面白かったです。 2 つ目のお題の設計方法にも共通していたこととして、お題に対する設計を考える際に、まずはシンプルな設計を考え、その後に問題点を洗い出して解決するという方法を取っていて、とても良いなと思いました。

まとめ

大きく盛り上がったオフラインイベントでお祭りみたいで非常に楽しかったです! またこのようなイベントに引き続き参加していきたいと思います。