362 Views
November 01, 20
スライド概要
2020-11-01
セキュリティ・キャンプ全国大会2020 オンライン
プロダクトセキュリティトラック
クラウド時代における分散Webシステムの構成とスケーリング
事前課題: https://gist.github.com/nekoruri/6b638b4ca551552480b9d3de3bb61151
秋葉原生まれ大手町育ちの歌って踊れる江戸っ子インフラエンジニア。 0と1が紡ぐ「ゆるやかなつながり」に魅せられ早20年、 SNSとCGMの力で世界を幸福にするのがライフワーク。 市民、幸福は義務です。 あなたは幸福ですか?
クラウド時代における 分散Webシステムの構成とスケーリング セキュリティ・キャンプ プロダクトセキュリティトラック 仲山昌宏 (@nekoruri)
仲山 昌宏 / @nekoruri • 株式会社WHERE IoT基盤センター サービスプロデューサー(2016-) • セキュリティ・キャンプ 講師(2015-) • SecHack365 トレーナー(2017-) • 技術系同人誌サークル 「めもおきば」「ssmjp同人部」 • #ssmjp / #qpstudy / #serverlesstokyo • ProjectDIVA Arcade LV.631 2
経歴 • • • • • • • • 2003-09~2005-03 大学院ネットワーク管理(慶應SFC/徳田村井研) SNS 2004-03~2010-06 WIDE Project irc.fujisawa.wide.ad.jp運用 2004-09〜2005-10 国際大学GLOCOM (研究アシスタント) 2005-08〜2008-09 AIP CGM 2008-10〜2009-12 KBB, I&D ソーシャルゲーム ・クラウド 2010-01〜2013-06 Xtone 2013-07〜2016-01 CyberAgent (パシャオク⇒Ameba⇒DC運用) 2016-02〜現職 WHERE (IoTスタートアップ) ⬅今ここ サーバーレスIoT
主なスキルセット • システム設計 • クラウドとIoTデバイス組み合わせ 良い感じのシステム/ソリューション • ウェブアプリの内部アーキテクチャ • アプリケーション開発 • サーバーレス構成なサーバサイド • 普通のサーバサイド(+フロント) Perl、Ruby、Python、JS、PHP • 過去にはWindowsアプリとかも • 情報システム • 社内ITシステムの設計、運用 • 情報セキュリティマネジメント • サーバ/ネットワーク運用 • サーバHW(特にストレージ)周り • IPネットワーク • 「必要があればなんでもやる」 • 「やりたい事を実現する」 4
すすめかた • サンプルアプリをベースに、高信頼なシステムを設計する • 最初にサンプルアプリの解説をします • 5チームに分かれてハッカソン • モブプログラミング(後述) • ハッカソンの流れ • サンプルアプリのユースケースをより具体的に想定し、 スケールさせるうえでの課題と、それを解決する設計を考える • 時間内にできる限り実装して、その設計を検証する • どんな設計をしたかを発表し、それに対して議論する
スケジュール • 前説 • サンプルアプリの解説 • ミニハッカソン 設計テーマ決め • ミニハッカソン 実装 • 各チーム発表 • 講評+まとめ 08:30~08:40 08:40~09:00 09:00~09:30 09:30~11:30 11:30~12:00 12:00~12:30
サンプルアプリ • タイムライン型アプリケーションの基本的な構造 • ニュースサイトの新着ニュース通知や フォロー関係のあるタイムラインサービス(SNS) • 今は「自分の投稿」をただ保存し取得する部分だけを実装 • サーバーレスアーキテクチャを採用
サーバーレス • 狭義には、「サーバー」を事前に確保することなく、実際に消費した リソース分だけ利用できるような実行モデル • 広義には、それを活用したベストプラクティス群を含む 非同期、疎結合、ステートレス、など • FaaS • 自分の書いたプログラムを動かせる • Functional SaaS(BaaS) • 具体的なサービスをAPIとして提供する
FaaS (Function as a Service) • 「Function(関数)」と呼ばれる小さなプログラムの実行環境 • • • • 起動プロセス数などリソース量をクラウド側が動的に管理 消費したCPU時間やメモリに対して課金 AWS Lambda、Azure Functions、Google Cloud Functions 今回はAzure Functionsを利用します • 参考:サーバーレスコンテナ実行環境 • 関数の代わりにDockerコンテナを動かす基盤 • Google Cloud Run • AWS Fargate、Azure Container Instances
Functional SaaS(Software as a Service) • 具体的なサービスをAPIとして提供 • • • • 既存のアプリケーションにおける「ライブラリ」「ミドルウェア」に相当 データベース、メール送受信、メッセージキュー、全文検索、etc BaaS(Backend as a Service)とも呼ばれる 今回はAzure Cosmos DBを利用
全体像 HTML JS CSS Static Web Apps (Netlify) 投稿API タイムラインAPI クライアント アプリ その他API ブラウザ 認証 Azure Functions
Static Web Apps • 静的コンテンツを提供 • 機能としては3つ • ログイン・ログアウト用URLへのリンク • 投稿 • タイムラインの表示
Azure Functions • サーバ側のAPIを提供 • 「Azure App Service」という基盤の上で 「Azure Functions」というFaaSが動いている • ドキュメント • Azure Functions のドキュメント • App Service のドキュメント
Azure Functions (認証) • App Service組み込みの認証連携機能を利用 • Azure App Service および Azure Functions での認証と承認 • Azure AD ログインを使用するように App Service または Azure Functions アプリを構成する • しくみ • • • • 特定のURL(/.auth/login/aad)を開くと認証される 認証されるとCookieに保存 API呼び出し時にCookieを送ることで認証状態を送信 関数実行前に検証されてリクエストヘッダに追加される
Azure Functions (投稿API) • 投稿処理 • POST /api/post • 実装: api/post/index.js • 投稿されたデータを、Cosmos DBに保存する • アウトプットバインディングという仕組みを利用 • functions.jsonで出力先DBを設定 • context.bindings.outputDocumentに保存内容を代入 • 関数の実行後にDBに保存される
Azure Functions (投稿API) 検証されたログインユーザ名 保存したいドキュメントを アウトプットバインディングに送る
Azure Functions (タイムラインAPI) • 特定のユーザのタイムラインを表示 • POST /api/timeline • 実装: api/timeline/index.js • 指定されたユーザか自分の投稿をCosmos DBから取得し返す • Cosmos DB(旧名:DocumentDB)へのクエリを実行 • Azure Cosmos DB のドキュメント • Azure Cosmos DB でのパーティション分割と水平スケーリング • Azure Cosmos DB のインデックス作成 - 概要
Azure Functions (タイムラインAPI) Cosmos DBへのクエリとパラメータ クエリの実行と配列化 動作確認用ログ出力 必要な項目のみ出力
新しい関数を増やす
Cosmos DB • 分散データベース • うまくつかえば地球規模のスケーラビリティ • 様々なデータモデル(今回は単純なドキュメントDBを利用) • RU(要求ユニット)に対して課金される • IDとパーティションキーを指定した1KBの読み取りが1RU • Azure Cosmos DB の要求ユニット • パーティションキーをどう選ぶかが重要 • Azure Cosmos DB Performance Tips • 消費したRUを確認する • timelineアプリには確認用にCosmos DBからの戻り値をログ出力 しているので、x-ms-request-charge として確認可能 • Azure Portal上でクエリを実行してみて確認
消費RUの確認 (1)
消費RUの確認 (2)
Next step • 「フォロー関係」を保存し、フォロー先の投稿が見られるようにする • ここからきちんと設計が必要! • ユースケースをきちんと考える • 「フォローした相手の投稿を一覧で見られる」 というのは突き詰めると何をやっているのか? 何が必要なのか? • 他に必要なユースケースはなんだろうか?
すすめかた • サンプルアプリをベースに、高信頼なシステムを設計する • 最初にサンプルアプリの解説をします • 5チームに分かれてハッカソン • モブプログラミング(後述) • ハッカソンの流れ • サンプルアプリのユースケースをより具体的に想定し、 スケールさせるうえでの課題と、それを解決する設計を考える • 時間内にできる限り実装して、その設計を検証する • どんな設計をしたかを発表し、それに対して議論する
設計テーマの共有 • 9時30分になったら、設計テーマをスライドにまとめてください。 • どんなユースケースと課題を想定し、 どんな設計でそれを解決・改善しようとしているのか • Google Slideで2枚以内 • まとめたらSlackに投稿 • 良いテーマがあったら話してもらう、かも?
モブプログラミング • チーム「全員」で「同じ作業」に向かう • 分担では無く共同作業 出典:https://speakerdeck.com/takaking22/mob-programming-startup-manual-number-mobprogramming-number-mobupuro
モブプログラミング • 15~20分くらいでドライバー(操作担当)を交代 ⇒いつでも作業を共有できる状態を保つことが重要 画面共有やVSCode Live Shareなどを上手く使ってください。 • 作業内容はどんどんGitHubに上げていく • 議論や結論もその場でドキュメントに書いて共有 • Google DocumentやGitHub Wiki等を活用 • 苦手な作業があればチーム内で手助けしあう(暗黙知の共有)
チーム発表 • 11時30分から各チーム持ち時間5分で発表 • 発表しているチーム以外の人は、 シートを用意するのでコメントを書いてください • 必ず入れて欲しい内容 • 想定した利用ケースとスケール上の課題 • スケールさせるために考えた設計 • どこまで実装できたか、検証できたか
チーム発表 • ヒント • • • • 設計を数値による検証で裏付ける コストとユースケースのバランスを取る 実装してわかった課題に立ち向かう 運用時の視点を持つ
設計例
ユースケースと課題 • Twitterを想定 • ツイートできる • ツイートはどんどん増えていく • 利用者もどんどん増えていく • フォローできる • ほとんどの人は、フォロー人数はそこまで多くない • ツイートを削除できる
基本設計 • 以下のテーブルを持つ • (A)ユーザ自身のツイートのテーブル(ツイート) • (B)ユーザをフォローしている相手を格納するテーブル(フォロイー) • (C)ユーザのフォロー先のツイートのテーブル(タイムライン)
テーブル構造 • (A)ユーザ自身のツイートのテーブル(ツイート) • パーティションキー:ユーザID • インデックス:タイムスタンプ • データ:ツイート内容、削除フラグ • (B)ユーザをフォローしている相手を格納するテーブル(フォロイー) • パーティションキー:ユーザID • データ:「ユーザID」をフォローしている人のユーザIDの配列 • (C)ユーザのフォロー先のツイートのテーブル(タイムライン) • パーティションキー:ユーザID • インデックス:タイムスタンプ • TTL:1ヶ月
投稿時 • 投稿時にA(ツイート)に保存すると同時に、以下の処理を非同期 でおこなう • B(フォロイー)からフォローしている相手の一覧を取得 • 相手のC(タイムライン)に追加していく • A(ツイート)への追記は1回 • フォロワー数に応じて回数が増えるので、特別にフォロワー数が 多い人の処理は遅れるが、ほとんどの人は非同期でも少ない時間 で書き込みされる ⇒品質の制御(何かを切り捨て、守りたい品質を守る)
タイムライン表示 • C(タイムライン)から最新のタイムラインを取得する • タイムスタンプでソートすることで最新N件 • 一ヶ月以上古いツイートはタイムライン経由では諦める • A(ツイート)からツイート本文を取得する • インデックス完全指定なのでこの処理は早い • ツイート数が極めて多い人は、ノード間でデータが偏り、 処理コストが増大する可能性がある ⇒API利用制限などで対応 • 取得できる件数に制限を設けることで、 Aへのリクエスト回数の最大を制御できる
フォロー時 • フォロー相手のB(フォロイー)に自分を追加する • フォロー相手のA(ツイート)から新しい順に取得し、 C(タイムライン)に追加していく • タイムラインを取得できる件数や期間に制限があれば、 期間分だけAから取得すれば良い⇒処理量の最大を制限できる
フォロー解除時 • フォロー解除相手のB(フォロイー)から自分を削除する • C(タイムライン)から解除相手のツイートを削除していく • タイムラインを取得できる件数や期間に制限があれば、 期間分だけAから取得すれば良い⇒処理量の最大を制限できる
ツイート削除時 • ツイートのユーザIDとタイムスタンプを指定して削除 • A(ツイート)の削除フラグを立てる
キャッシュ戦略 • Aへのリクエストがまずボトルネックになるのでキャッシュを併用 • ツイート本文は書き換わらない • 削除フラグに関しては、「消える」までの遅延を許容する必要がある
ふりかえり • 今日やったことを「次」につなげる • 今日やったこと • わかったこと、印象に残ったこと • 次にやってみたいと思ったこと • スプレッドシートに書いていってください。 • 書くことで自分へのおさらい