lasciva blog

開発して得た知見やwebビジネスのストック

「OAuth徹底入門 セキュアな認可システムを適用するための原則と実践 」を読んだ

OAuth徹底入門 セキュアな認可システムを適用するための原則と実践

OAuth徹底入門 セキュアな認可システムを適用するための原則と実践

目的、モチベーション

  • OAuthの仕組みを雰囲気でしか理解していなかったので、理解を深めるため。
    • 普通の認可と違い、何故セキュアなのか。
  • アプリケーションにSNS認証を導入する際に、どのように導入すればセキュアなのかを理解するため。

全体の感想

読み進めていくうちに、OAuthは認証プロトコルではなく移譲プロトコルであるというのが特に衝撃的で、OAuthのことを全く理解していなかったんだなと思った。
実際にコードを書いて簡単だが一連のやり取りを把握できたのが良かった。
また、何故このパラメータがないと脆弱性につながるのかなども詳しく説明してあり、理解も深まりやすかった。
入門書レベルだとは思うがボリュームもあり、OAuthを雰囲気で使っている人にはオススメ。

目次

概要

github.com

英語版はこちら

OAuth 2 in Action

OAuth 2 in Action

  • 作者:Justin Richer,Antonio Sanso
  • 出版社/メーカー: Manning Publications
  • 発売日: 2017/03/18
  • メディア: ペーパーバック

第1章 OAuth 2.0とは何か?そして、なぜ気にかけるべきなのか?

OAuth2.0とは何か?

委譲プロトコルで、アプリケーション(twitterなど)のリソースの所有者に認可されるように許可を求めて、クライアント(twitterのデータを利用しているサービスなど)トークンによって所有者の代わりにアクセスできるようにする仕組み。
認可プロセスや暗号の方法などは定義しておらず、必ず認証がある訳ではなく、認証プロトコルでもない。

クレデンシャルの共有の問題点

  • クライアントが繰り返しパスワードを使うために、平文や可逆暗号でパスワードを保存してしまう
  • クライアントに与えるべきでない権限も付与してしまう

第2章 OAuthダンス - OAuthの構成要素間の相互作用

2.1 OAuth 2.0プロトコルの概要~トークンの取得と使用~

標準的な流れは以下の通り。

  1. リソース所有者はクライアントにリソース所有者の代わりとして振る舞ってほしいことを指示する
  2. クライアントは認可サーバにリクエストをし、そこでリソース所有者にクライアントを認可するかどうかの判断を行わせる
  3. こうすることで、クライアントはクレデンシャルを知らないで済む
  4. リソース所有者はクライアントを許可する
  5. クライアントは認可サーバからトークンを受け取る
  6. クライアントは保護対象リソースにトークンを提示する
2.2 OAuth 2.0における認可付与の詳細

認可コードによる付与(Authorization Code Grant)では、一時的なクレデンシャルである認可コードを使い、トークンを取得する。

  1. リソース所有者がクライアントを許可すると、認可コードとともにクライアントにリダイレクトされる
  2. クライアントは受け取った認可コードを、クライアント自身のクレデンシャルとともに認可サーバに送る
  3. 認可サーバは、認可コードのクライアントと、クレデンシャルのクライアントが一致した場合にトークンを返す(認可コードはrevokeされる)
2.4 トークン、スコープ、認可付与

アクセス・トーク
中身のないただの文字列で、クライアントは内容を知らずに扱うことができる。

スコープ
スペースで区切られた、保護対象リソースでの権限。

リフレッシュ・トーク
新しいアクセス・トークンを取得するのに必要なトークン。アクセス・トークンの期限が切れても、リソース所有者の認証を再度求めずに取得できる。

認可付与
OAuthのプロトコルを介して、アクセス権をクライアントが得る手段、即ちトークンを取得するフローのこと。

2.5 OAuthの構成要素間のやり取り

バック・チャネル・コミュニケーション
リソース所有者やブラウザ以外の、認可サーバやクライアント、保護対象リソースのHTTPのコミュニケーションのこと。

フロント・チャネル・コミュニケーション
サーバとの、ブラウザを介したコミュニケーションのこと。ブラウザを介すため、機密情報は渡らない方がベター。

第3章 シンプルなOAuthクライアントの構築

第4章 シンプルなOAuthの保護対象リソースの構築

第5章 シンプルなOAuthの認可サーバの構築

この3つの章は、サンプルコードを元に実際に簡易なサーバを実装していった。手を動かすとイメージがつかめて良かった。
気になったのは下記。

  • stateパラメータははcallbackを直接叩かれ乗っ取りをされかけたときの防止策
  • Bearerは規約的には大文字小文字は区別しない

第6章 実際の環境におけるOAuth 2.0

6.1 認可における付与方式

6.1.1 インプリシット付与方式

この付与形式では、認可エンドポイントから直接トークンを得る。

クライアントがブラウザ内部に埋め込まれたJavaScriptアプリケーションのような場合、認可コードによる付与を行う際に、認可コードをクライアントに返すとブラウザに認可コードが渡ってしまいクライアント以外も知ってしまうため、メリットがない。
ブラウザアプリケーションの場合、ユーザがいるためトークンが無効になっても再度認証を求められるため、TOFUも維持できる。

6.1.2 クライアント・クレデンシャルによる付与方式

バックエンドの認証などで、リソース所有者が明確に存在しなかったり、クライアント自体がリソース所有者の場合、この付与方式を用いる。
scopeなどを指定して、認可エンドポイントから直接トークンを得る。

6.1.3 リソース所有者のクレデンシャルによる付与方式

リソース所有者のクレデンシャルをクライアントに送り、それを使って認可サーバからアクセストークンを得る方法。
クライアントにクレデンシャルが渡り、アンチパターンなのでできれば避けるべき。

第7章 よく狙われるクライアントの脆弱性

7.2 クライアントに対するCSRF攻撃

仕様ではstateパラメータは必須でないが、CSRF攻撃を防ぐために検証した方が良い。

7.3 クライアント・クレデンシャルの不当な取得

ネイティブ・アプリケーションなどは、デコンパイルされる可能性が0ではないため、クレデンシャルは動的に取得するようにして、各デバイス毎にクライアントIDを管理した方が安全。

7.4 リダイレクトURIの登録

リダイレクトURIは攻撃されやすく、バグも発生しやすいので、完全一致で検証するのがベスト。
サブディレクトリを検証しない場合、CGM型のサイトなどでユーザがページを作成できるときなどに、そのURLを指定されると漏洩の元になる。

  • HTTPリファラー: URLのパラメータにcodeが付与された状態で、攻撃者のページにリダイレクトした場合、imgやscriptを攻撃者のサイトにリクエストすることで、リファラー中のURLから盗まれる。
  • オープンリダイレクト: パラメータの値のURLを検証せずに、そのURLにリダイレクトしてしまうこと。URIフラグメントにトークン情報を含めた状態でリダイレクトすると盗まれる。
7.6 トークンの不正な取得

Authorizationヘッダを利用できず、URLのパラメータに含める場合は攻撃されやすくなるので、注意が必要。

  • access.logなどにパラメータとして書き出される
  • オンライン掲示板などに、ユーザが意図せず貼ってしまう
  • リファラーヘッダにトークンが含まれる

第8章 よく狙われる保護対象リソースの脆弱性

8.2 保護対象リソースのエンドポイントの設計

HTTPヘッダを適切に設定すれば、XSS対策をより安全に行える。

ヘッダ 説明
Content-Type jsonを指定することで、ブラウザがXSSから保護する
X-Content-Type-Options MIMEスニッフィングを防ぐ。
X-XSS-Protection 自動的にXSS攻撃を除外
8.3 トークンのリプレイ攻撃

OAuth2.0ではTLSが前提となっているが、HSTS(HTTP Strict Transport Security)を使えば、サーバ側で安全なHTTPSのみを使うように宣言できる。
使い方は、レスポンスのヘッダに Strict-Transport-Securityを設定する。

第9章 よく狙われる認可サーバの脆弱性

  • 認可コード
    • 一度使われたら、破棄する(URLに含まれるため、ブラウザの履歴などに残ってしまう可能性がある)
    • クライアントが一致するか確認する
  • リダイレクト
    • 絶対に、何の検証もなしにリダイレクトしないこと
    • バグの元なので、redirect_uriは極力完全一致するか検証する
    • エラーを返す際にも、Refererヘッダなどを介して情報漏洩しないように注意する

第10章 よく狙われるOAuthトークンの脆弱性

トークンはシンプルである一方で、盗まれた際には何でもできてしまうため、工夫が必要。

  • 盗まれないように保護
  • 盗まれてしまった際の被害を小さくする
    • クライアント: 必要最低限のscopeのトークンを要求する
    • 認可サーバ: 有効期限を短くする
  • サーバを攻撃されてしまった際の被害を小さくする
    • 認可サーバ: トークンをハッシュ値で保存する
    • リソースサーバ: キャッシュする場合は、一時的なメモリを使用する

第11章 OAuthトーク

11.1 OAuthにおけるトークンとは何か?

OAuthでは、トークンの形式は定められていない。

よく用いられる乱数のトークンは、サイズを小さく保ちつつ、文字列を更に乱雑にすることでセキュアにできる。その一方で、認可サーバと保護対象リソースとの間でデータソースを共有できない場合は、通信処理がボトルネックになったりするなどのデメリットもある。

11.2 JWT(JSON Web Token)

トークンそのものに、必要な情報を詰め込んだものの例として、JWTが紹介されていた。
内部に情報を持つことで、認可サーバを介さずにリソースサーバがトークンの有効性やスコープを確認することができる。

11.2.1 JWTの構造
.で区切られた3つのパート(header, payload, verify signature)の文字列から成る。文字列はJSONオブジェクトをBase64エンコードされている。
トークンが盗まれた場合、デコードされるとpayloadの情報が読み取れてしまうので、HTTPSを使いかつ機密情報はトークンに含めないこと。
Base64エンコードされているのは、トークンを受け渡してる間にエンコードされてしまったり(URLのパーセントエンコーディングなど)して、処理を煩雑にしないための工夫である。

11.3 JOSE(JSON Object Signing and Encryption)

JWTでは、署名のアルゴリズムを指定することができる。
例として、RS256を使うと秘密鍵を認証サーバで管理して、公開鍵をリソースサーバが受け取り検証することでセキュアに検証できる。

トークン内の情報を暗号化するためにJWE(JSON Web Encryption)という仕組みもある。

11.4 トークン・イントロスペクション(Token Introspection)

トークン自身に情報を持たせると、scopeを最新の状態に更新できないなどのデメリットもある。 それを解決する一つが、Token Introspection。 認可サーバに /introspectのようなエンドポイントを用意し、tokenの詳細の情報を取得できるようにして解決するプロトコルである。
ただし、リクエスト数が増えて認可サーバに負荷がかかるので、リソースサーバ側でキャッシュするなどの工夫が必要。

11.5 トークン取り消し(Token Revocation)

ユーザがログアウトしたなど、クライアントが能動的にトークンを無効にしたい場合には、Token Revocationを利用する。
単にアクセストークンとリフレッシュトークンを無効にするだけだが、攻撃されないように、トークンのクライアントとリクエストしてきたクライアントが一致するかの検証を行い、無効であったとしても201を返すこと。401を返すとDoS攻撃が行われ、他のクライアントでトークンが有効であることがわかってしまう。

第12章 動的クライアント登録(Dynamic Client Registration)

スケーラビリティを確保し、クライアントとの信頼関係を築くために動的にクライアントの管理を行いましょうという話だった。

第13章 OAuth2.0を使ったユーザ認証

OAuth 2.0自身は認証プロトコルでないが、認証プロトコルを構築するために用いられることもある。
その例として、OpenID Connectが紹介されていた。

第14章 OAuth2.0を使うプロトコルとプロファイル

OAuth2.0を拡張したプロトコルやプロファイルが紹介されていた。

14.1 UMA(User Managed Access

三者に自分のリソースの利用をクライアントで利用するのを許可する(ex. Aliceが自身のリソースの利用を、Bobに利用許可するとき)を安全に管理するために、UMA(User Managed Access)が利用できる。
UMAでは、自分のリソースポリシーを設定し、Aliceを介さずにトークンを取得できる。また、トークンも第三者である利用者毎に発行できるため、revokeなどの処理も行える。

処理の流れとしては、通常のOAuthに加えて、リソース所有者がポリシーを先に設定し、リソースサーバはトークンが有効な権限を持つか検証しないといけない。

14.2 HEART(HEAlth Relationship Trust)

OAuthは柔軟な反面、相互運用性や互換性の確保が難しい。医療分野などの単一分野を取り扱っている場合、共通のAPIを利用することがよくあり、サーバ同士が連携しやすいように、ガイドラインが利便性が高まる。
その一例として、HEART(HEAlth Relationship Trust)が紹介されていた。

14.3 iGov(international Government assurance)

HEARTと同じように、行政機関で使われることを目指すiGov(international Government assurance)が紹介されていた。
行政機関のシステムは、長期間使用される一方で、システムのアップデートが遅くなったりする傾向もあるため、互換性が担保できるように慎重に策定する必要がある。

第15章 Bearerトークンの次のもの

Bearerトークンはシンプルなため、トークンを奪われると再利用されてしまう。
よりセキュアな所有証明(Proof of Possession:PoP)トークンと、TLSトークン・バインディングが紹介されていた。

OAuth徹底入門 セキュアな認可システムを適用するための原則と実践

OAuth徹底入門 セキュアな認可システムを適用するための原則と実践

「ベゾス・レター:アマゾンに学ぶ14ヵ条の成長原則」を読んだ

目的、モチベーション

twitterで知った。ベゾスが発してる情報源である株主への手紙を読み解いて、成長原則を学ぶというコンセプトが面白そうだったので、読んだ。

全体の感想

Amazon系のビジネス書は、「ワンクリック ジェフ・ベゾス率いるAMAZONの隆盛」や「ジェフ・ベゾス 果てなき野望」など何冊か読んだことがあったり、ネットの記事を読んだりしてたので、めちゃくちゃ目新しいものがあったかというと微妙。頭の中は整理できたり、復習にはなったりした。

ややこしいが、本書の14ヵ条はAmazonが掲げる「Our Leadership Principles」という14項目からなる信条とは別物で、著者が約20年分の株主へのレターからベゾスやAmazonが重視している原則を見出したものなので注意。

メモ

前段として、Amazonにおけるリスクの話があった。
リスクは一般的には避けられるものだが、長期的に正しく進むためや、大きな革新やリターンを得るためには大胆に迅速に進める必要がある。
そのため、ある程度は失敗する前提で学びを得られれば良しとしてリスクを犯すべき。

14ヵ条は以下の通りで、企業文化として有名なものが多かった。

  • 成長サイクル:実験
    • 第1条 「いい失敗」を促す
    • 第2条 大きなアイデアに賭ける
    • 第3条 ダイナミックな発明や革新を実践する
  • 成長サイクル:構築
    • 第4条 顧客にこだわる
    • 第5条 長期的な考え方を採用する
    • 第6条 自分の「弾み車」 を理解する
  • 成長サイクル:加速
    • 第7条 決定は迅速に行う
    • 第8条 複雑なことは単純化する
    • 第9条 テクノロジーで時間を短縮する
    • 第10条 所有者意識を持たせる
  • 成長サイクル:規模の拡大
    • 第11条 企業文化を守る
    • 第12条 高水準を重視する
    • 第13条 重要な項目を計測し、計測項目を疑い、自分の直感を信じる
    • 第14条 常に1日目だと信じる

特に印象に残ったのは、以下。

第4条 顧客にこだわる
ユーザ本位というのは、どのような会社でも同じようなことを掲げるが、Amazonでは顧客に「すごい」と言われるような極端なレベルまでこだわるというのを徹底している。
顧客の問題を解決するのではなく、問題を発生させずに完全に潰すのが仕事。
徹底している一例として挙げられて面白かったのは、オンデマンドの映画を購入した顧客が見た時間帯に、顧客が気づかないレベルでネットワークの状態が少しだけ悪かったことをシステムが検知し、Amazon側から自ら顧客に謝罪と返金の連絡を行った。

第7条 決定は迅速に行う

大きい組織でも迅速に意思決定を行うために、「後戻りできない」ものと「失敗しても辞めたり取り返しのつく」ものの2種類に分類している。
遅くなって手遅れになるよりも、情報不足で間違うことの方を良しとする。

第13条 重要な項目を計測し、計測項目を疑い、自分の直感を信じる
意思決定の際には数字をみて良し悪しの判断を勿論行っているが、スムーズにできる一方で、革新が起こりにくくもなるので数字は疑いもする。

「絵で見てわかるシステムパフォーマンスの仕組み」を読んだ

絵で見てわかるシステムパフォーマンスの仕組み

絵で見てわかるシステムパフォーマンスの仕組み

目的、モチベーション

バックエンドのパフォーマンス改善において、指標となるCPUやメモリ等の基礎の理解を深め、実際の調査方法などが知りたかった。

全体の感想

表紙とタイトルから、図録みたいなものなのかと勝手に連想していたが、いい意味で裏切られた。普通にテキストによる説明がメインで、他の技術書よりもイメージをつかみやすいように絵がたくさんあったという印象。
アルゴリズムやデータ構造、アプリケーション、OS、インフラ、プロジェクトの進め方など、カバー範囲がとにかく広かった。
パフォーマンスのボトルネックの調査方法を知りたいのが目的の一つだったので、OSのコマンドの使い分けなどは特に良かった。
またパフォーマンステストの進め方は目的ではなかったが、私の経験で思ってるよりも時間がかかったり、再現が難しかったり今まであまりうまくいかなかったことが言語化されていて、整理もできて良かった。

少し個人的に残念なのは、2014年に出版された本のためDockerやコンテナ周りの話は触れられてなかった。

目次

概要

【第1章】パフォーマンスの基礎的な考え方

計算量やアルゴリズムの説明だった。知ってる内容だったので割愛。

【第2章】パフォーマンス分析の基本

2.2 必要なパフォーマンス情報とは

2.2.1 「はさみうち」の原則
前後の計測も行わないと、ただのスパイクなのか、何が原因なのかを確認できないので、必ず行うこと。

2.2.2 パフォーマンス情報の3種類

名前 特徴
サマリ形式 一定期間の合計や平均の情報。概況を抑えるのには向いているが、変動を捉えるのには不向き。
イベント記録形式 個々のイベントを逐次記録された情報。詳細な調査には向くが、データ量や負荷が大きくなるので、本番で常に実行するには不向き。
スナップショット形式 ある時点での状況の記録情報。定期的に取得することで、原因調査などが行える。
2.4 OSのコマンド
コマンド 形式 計測点 補足
sar サマリ形式 OSのカーネルからのOS情報 CPU、I/O、メモリなどの概況がわかる。
vmstat サマリ形式 OSのカーネル情報からのOS情報 実行待ちの平均プロセス数、ブロックされているプロセス数などがわかる。
ps スナップショット形式 OSのカーネルで各プロセスの情報 プロセスの状態、CPU時間などがわかる。負荷が高いので高頻度で取得するには向いてない。
netstat サマリ形式/スナップショット形式 ドライバレベル ソケット、ルーティング、インターフェースごとの統計がわかる。
iostat サマリ形式 OSカーネル内部のブロックデバイスレベル
top スナップショット形式 OSレベル OS全体の概況把握に最適。少々負荷が高い。
パケットダンプ(wiresharktcpdumpなど) イベント記録形式 ドライバレベル 負荷が大きく、パフォーマンスに影響が出る。
pstack スナップショット形式 OSから見たコールスタックの情報 何度か取得し、詰まってる処理を特定して、アプリケーションなどの問題を特定する。負荷は低い。
システムコール(straceなど) イベント記録形式 OSから見たプロセスのシステムコール情報 負荷が高い。
プロファイラ サマリ形式 OSから見たプロセスの各関数の処理時間

【第3章】実システムのパフォーマンス分析

用語の説明が多かったので、割愛。

【第4章】パフォーマンスチューニング

4.3 現場で用いられるテクニック

パフォーマンス改善と一口に言っても様々な方法があるが、一覧化されると整理できて良かった。

  • ループの省略、キャッチボールの削減
    • 局所最適に陥られないこと。例えば、DBで何度もINDEXを使った参照をするよりかは、1度のフルスキャンでデータを取得する。
  • 参照頻度の高いデータはキーバリューストア化かハッシュ化する
    • ハッシュのアクセスはO(1)
  • 参照頻度の高いデータは使う場所の近くに置く
    • CPUの内部レベルでも、CDNなどでも。
  • 同期を非同期に変える
  • 帯域制限
  • LRU(Least Recent Used)方式
  • 処理の分割またはロックの粒度の詳細化
  • 不揮発なライトバックのキャッシュの採用
  • マルチレイヤのキャッシュの採用
  • ジャンボフレームと高速ネットワークの採用
    • パケットあたりのデータ量を増やすことでパケット数を減らし、CPU使用量を減らす
  • 負荷分散、ラウンドロビン
  • アフィニティ、バインド、Stickyセッション
    • キャッシュの特性を活かすために局所化する
  • copy on write(COW)
  • ジャーナル、ログ
    • DBへの書き込みなどを一括で行い、パフォーマンスを改善する
  • 圧縮
  • 楽観的ロック
  • カラムナデータベース(Columnar Database)
  • サーバのパフォーマンス設定は初期値=最大値?

【第5章】パフォーマンステスト

この章では、パフォーマンステストの進め方が紹介されていた。
企画段階から、リリースしてから運用するまで、関係者とどのように進めるのかなど。
ややSIerっぽい内容ではあるが、環境構築や実際のテスト時の注意点は大変参考になった。

5.2 よくある失敗:9つのアンチパターン

5.2.1 期間内に終わらない!
本番環境でないと再現しない、再現するための環境構築に時間がかかる、特定の操作で問題発生などで、スケジュール通りに進まない。

5.2.2 パフォーマンスが出ない! パフォーマンス問題が解決できない!
原因究明には、あらゆるレイヤーの知識が求められるため、時間がかかる。

5.2.3 環境差異を考慮しないために問題が発生
インフラやサーバの設定が異なるなど。

5.2.4 負荷シナリオ設計に不備があるために問題が発生

  • 一部の画面操作しか考慮していなかった
  • 長時間滞在して多くの画面を遷移し、セッションに蓄積されて多くのメモリが使用された

5.2.5 バッファ/キャッシュの利用を考慮しないために問題が発生
特定のデータやパターンを集中的にテストしたために、キャッシュが乗った状態で十分に検証できてなかった。

5.2.6 シンクタイムを考慮しないために問題が発生
本番環境でユーザが複数の操作する際にテスト時よりも時間がかかり、HTTPのコネクション数やセッションの維持数に影響が出てしまう。

5.2.9 テストに時間がかかる
インフラの環境構築、計測や監視の設定、負荷生成シナリオスクリプト、本番同等のデータ作成、調査の時間など。

5.3 パフォーマンステストの種類

開発段階に応じて、できる種類のテストが異なる(インフラ、アプリケーションなどの単体しか行えないなど)。
また、知りたいパフォーマンスの種類によっても分類できる。

  • 狭義のパフォーマンステスト: 要件が満たせるか
  • 限界パフォーマンス: 上限の調査
  • 縮退パフォーマンス: 一部のノードが落ちてしまったときのテスト
  • 障害テスト: 障害が起こってしまった際のエラーの確認や対応の確認など
5.4 プロジェクト工程で考えるパフォーマンステスト

5.4.1 要件定義
スループット」、「レスポンスタイム」、「ユーザ多重度」の3つは必ず設定すること。これらは互いに依存し合うため、一つ改善しても他が悪化する可能性があるため。

【第6章】仮想化環境におけるパフォーマンス

仮想化をすることによって、CPUの命令の変換やメモリのマッピングなどの処理が入るため、遅くなる。
また、元のOSのCPU数より多くのCPUを使用するとオーバーヘッドが生じるので、チューニングが必要になる。

【第7章】クラウド環境におけるパフォーマンス

ざっと読んだが、今回の目的とは関係ないので割愛。

次のアクション

詳解 システム・パフォーマンスを読む。

絵で見てわかるシステムパフォーマンスの仕組み

絵で見てわかるシステムパフォーマンスの仕組み

「[試して理解]Linuxのしくみ」を読んだ

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

目的、モチベーション

バックエンドのパフォーマンス改善において、指標となるCPUやメモリ等の基礎の復習をしたかった。

全体の感想

サブタイトルにある通り、実験と図解を重視されており、理解しやすかった。
OS周りはとっつきにくく、今まで教科書的な本を読んだことがあったが、頭には入りにくかったりイメージしにくかったが、こちらの本は図がとにかく多いのが良かった。
あやふやだった点を整理することもでき、理解しやすく良書だったが、今回求めてたレベル感ではなかった。1,2年目のときに読みたかった。
OS周りの1冊目の入門書としてや、理解を整理したいときにオススメです。

気になったところ

第2章 ユーザモードで実現する機能

コマンド

コマンド 説明
strace システムコールの呼び出しの詳細が見れる
sar CPU時間の割合の詳細が見れる

第5章 メモリ管理

メモリに関する統計情報

統計情報の取得には、freeコマンドを用いる。

フェールド名 説明
total 全メモリの量
free 見かけ上の空きメモリ(詳しくはavailable)
buff/cache バッファキャッシュ、及びページキャッシュが利用するメモリ。システムの空きメモリが減少してきたら、カーネルによって解放される。
available 実質的な空きメモリ。freeフィールドの値に、空きメモリがたりなくなってきたら、解放できるカーネル内メモリ領域のサイズを足したもの。

仮想記憶
プロセスが物理アドレスを直接扱うと、プロセス間で干渉したり、連続したアドレスが取得できない場合に処理が煩雑になってしまう。
この問題に解決するために、仮想記憶を介して、仮想アドレスを扱う。仮想記憶はページテーブルと呼ばれる、仮想アドレスと物理アドレスのマップングした対応情報をカーネルが使うメモリ内保存される。

デマンドページング
メモリはプロセスが生成された際に割り当てられる。これだとメモリが無駄に割り当てられる可能性がある。
デマンドページングを用いれば、仮想アドレスは最初に割り当てて、物理メモリは実際に使用される際に割り当てることで、改善できる。

第7章 ファイルシステム

ファイルシステムの不整合
システムの電源が処理途中に落ちてしまったりして、ファイルシステムのデータに不整合が生じる可能性がある。
不整合を防ぐ代表的なものは、「ジャーナリング」と「コピーオンライト」の2つの方式がある。

ジャーナリング
この手法では、下記のように処理内容を別のメタデータで保存することで防ぐ。

  1. 処理に必要なアトミック処理の一覧を、いったんジャーナル領域に書き出す(この一覧をジャーナルログと呼ぶ)。
  2. ジャーナル領域の内容に基づいて、実際にファイルシステムの内容を更新する。

1つ目の処理の途中で終わった場合はメタデータを破棄し、2つ目の処理の途中で終わった場合は再実行することで、不整合を解消できる。

コピーオンライト
この手法では、更新されるデータを別の場所にすべて書き込んでからリンクを張り替えることで防ぐ。
処理の途中で終わった場合は、再起動時にデータを削除することで、不整合を解消できる。

第8章 ストレージデバイス

HDDの性質上、ハードウェアがレイテンシーボトルネックになる。
そのため、シーケンシャルにデータを配置し、できるだけ多くのデータを一度に取得することで、パフォーマンスの向上が期待できる。

I/Oスケジューラ

ブロックデバイスへのアクセス要求を一定期間溜めて、下記のような加工を行ってから、デバイスドライバにI/O要求をすることで、I/O性能の向上を目指す。

  • マージ: 複数の連続するセクタへのI/O要求を1つにまとめる。
  • ソート: 複数の不連続なセクタへのI/O要求をセクタ番号順に並び替える。

次のアクション

低レイヤーの理解が欠かせないなと思ったので、そちらの本とかを調査しつつ、もう1冊入門レベルの本(絵で見てわかるシステムパフォーマンスの仕組み)を買ったので読む。 あとは、積読中の詳解 システム・パフォーマンスを読む。

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

「Apache Kafka 分散メッセージングシステムの構築と活用」を読んだ

Apache Kafka 分散メッセージングシステムの構築と活用 (NEXT ONE)

Apache Kafka 分散メッセージングシステムの構築と活用 (NEXT ONE)

目的、モチベーション

本業のプロダクトでKafkaを使っていて、理解を深めたいと思ったので読んだ。
読む前の状態としては、Kafkaは触れてるが大規模のデータを捌けるPub/Subメッセージを扱うログ型DBみたいなものぐらいのイメージだった。

全体の感想

生まれた歴史から、どのような仕組みで何を解決するものなのか、実際の例まで一通り網羅されていて、入門書として良かった。
実用例のパターンも何種類も紹介されていて、本番環境レベルではないがイメージを掴むには良かった。
インストール方法やコード例(Javaがほとんど)もあって、手も動かせる。
コードで動かさなかったが、半日ぐらいで読めた。

Kafkaは便利だと思っていたが、完全にメッセージを厳密に一度だけ処理するには、アプリケーション側でも冪等性を保つなどの工夫が必要で単純なものではないんだなと思った。

目次

概要

印象に残ったところを記載してます。少し公式ドキュメントを読んで補足している箇所もあります。

第1部 導入Apache Kafka

1 Apache Kafkaの概要

1.3 Kafka誕生の背景

元々は、LinkedInの下記のような技術的課題からオープンソースとして生まれた。

  1. スループットでリアルタイムに処理したい
  2. 任意のタイミングでデータを読み出したい
  3. 各種プロダクトやシステムとの接続を用意にしたい
  4. メッセージをロストしたくない
1.4 Kafkaによる要求仕様の実現

下記の3要素からなる。

  • Producer: メッセージの送信元
  • Broker: メッセージの収集/配信役
    • 挟むことで、ProducerとConsumerの接続先を一つにでき、増減などの構成変更に影響されないようにできる
  • Consumer: メッセージの配信先

https://kafka.apache.org/images/producer_consumer.png

https://kafka.apache.org/images/producer_consumer.png

送達保証
Exactly Onceレベル(Consumerが処理を実行するのを一回にする)の送達保証をするために、トランザクションの概念を導入。

ProducerとBroker間では、BrokerがProducerにAckの返送に失敗した場合、ProducerがRetryしてBrokerがメッセージを受け取り、2度目以降は記録せずに排除することで実現する。
ConsumerとBroker間では、どこまでメッセージを受け取ったかのオフセットを管理されており、トランザクションでオフセットのコミットを行うことで実現する。オフセットコミットに失敗した場合、もう一度メッセージが処理されるため、Consumer側でも冪等性が必要。

2 Kafkaの基本

2.3 システム構成

Brokerは耐久性向上のために複数台で構成されており、ZooKeeperで分散処理の管理が行われている。

2.4 分散メッセージングのための仕組み

f:id:hacking15dog:20191014221434p:plain

Partition: Topicに対する負荷を分散させるために、Partition単位で分割されている。負荷分散のために、Brokerクラスタ中に分散に配置される。

Messageの送受信
ProducerのMessage送信: メッセージがあるデータ量まで蓄積されたか、指定した時間毎に送ることでパフォーマンス向上できる。
ConsumerのMessage取得: 同様にまとめて送信することができる。

スループットの向上が期待できる一方で、レイテンシーが発生するのでトレードオフ

Consumerのロールバック
ロールバックによって、メッセージのAt Least Once(最低でも一回は受信される)ことは保証できるが、Exactly Onceはアプリケーション側の対応が必要。

2.5 データの堅牢性を高めるレプリケーションの仕組み

ISR: In-Sync Replica, 最新の状態を保っているReplicaのこと。
最小のISR数を min.insync.replicaで指定できる。

High Watermark
レプリケーションが完了しているOffsetのことを指す。これより新しいOffsetのものはConsumerが取得できない。

ProducerのMessage到達保証レベルの調整
Messageが送信されたことを示すAckを、BrokerがProducerへ返す設定によって、性能と耐障害性に大きく影響する。

説明
0 BrokerのAckを待たない。Brokerに保存されたことは一切保証されず、リトライも行われない。
1 Leaderのレプリカに書き込まれたらAckを返す。Followerのレプリカに書き込まれる前に、Leaderが落ちるとデータロストが発生する。
all すべてのISRの数までレプリケーションされたらAckを返す。但し、万一レプリケーションされてないノードのみ生き残った場合、データロストが発生する。

上記の「書き込まれた」というのはディスクに書き込まれたタイミングではなく、あくまでもメモリに書き込まれたタイミング。
ディスクへの書き込みは、 log.flush.interval.msを指定する。

3 Kafkaのインストール

インストール方法が紹介されてました。

4 KafkaのJava APIを用いたアプリケーションの作成

Javaの標準ライブラリでの実装例が紹介されてました。

4.4 作成したProducerアプリケーションのポイント

Producerの送信処理は非同期で行われるため、アプリケーション側で sendメソッドでメッセージをする際にはキューに入れられただけで実際の送信は別で行われる。

第2部 実践Apache Kafka

5 Kafkaのユースケース

下記のような実例の概要が紹介されてました。

  • データハブ
  • ログ収集
  • Webアクティビティ分析
  • IoT
  • イベントソーシング
5.3 データハブ

システムが成長していくと、複数のDBやログデータを収集して分析用のDBなどに加工して送りたくなるケースが出てくる。
それぞれを直接つないでしまうと、影響範囲が広くなる上に、必要となるフォーマットが異なる度に実装が必要となってしまう。
このサイロ化の解決手段の一つが、データハブアーキテクチャ。上流のシステムの接続をKafkaなどのハブを介することで、疎結合にすることができる。

6 Kafkaを用いたデータパイプライン構築時の前提知識

6.3 データパイプラインで扱うデータ

ストリーミング処理では、複数のアプリケーションが常時起動している状態で、改修などで発生するデータの変更等を行わなければならない。

Messageのデータ型は、異なる言語でも使えるフォーマット(JSON, Apache Avroなど)を用いた上で、互換性を保つべき。
しかし、どうしても互換性を担保できないケースも出てくるが、Schema Registryを用いれば解決できるケースもある。
Schema Registryでは、Apache Avro前提で、Producerの送信時にスキーマ情報を保存してConsumerがそのスキーマ情報をもとにデシリアライズする。

7 KafkaとKafka Connectによるデータハブ

8 ストリーム処理の基本

9 Structured Streamingによるストリーム処理

5章で紹介されていたものの、実装例を中心に紹介されてました。

10 Kafkaで構成するIoTデータハブ

10.2 IoTに求められるシステム特性とKafka

IoTでは、下記のような特徴からKafkaと相性が良い。

  • センサーデバイスはマシンパワーやストレージがない
  • 大量のデバイスが接続されているため、データ量が多い
  • レイテンシーが求められるケースが多い(すぐに反応しないデバイスは使いにくい)
10.3 センサーデータ向けデータハブの設計

センサー側でデータを加工するのは難しいため、サーバ側で加工することになる。
ブローカーに保存する前に、加工すればデータ量を削ることができるが、生データの情報が失われ、保存後に加工するとその逆が発生するので、トレードオフ

軽量なMQTTプロトコルを用いれば、メッセージングモデルによる非同期処理が効率的に行える。

11 さらにKafkaを使いこなすために

11.2 Consumer Group

Consumer Groupというグループを各Consumerに設定することで、複数のConsumerで分散処理を行える。

各Partitionにつき必ず1つのConsumerが対応付けられるように割り当てられる。
そのため、Consumerの数が多いと割り当てられないConsumerが発生する。逆にPartition数の方が多いと、複数のPartitionが割り当てられるConsumerが発生する。
割り当てのロジックは partition.assignment.strategyで指定できる。

11.3 Offset Commit

enable.auto.commitでOffset Commitの自動化を切り替えられる。
現時点(2019/10/14)の最新のバージョンでは、基本的にはtrueがデフォルトとなっている。
自動化するとアプリケーション側の制御が不要になる一方で、障害のタイミング次第で複数回処理されたり、一度も処理されないケースが発生するので要注意。

11.5 Partition数の考慮

Brokerの数に対して、Partition数が少ないと送受信の処理の重いLeaderがあるBrokerに負荷が集中してしまうため、要注意。

Apache Kafka 分散メッセージングシステムの構築と活用 (NEXT ONE)

Apache Kafka 分散メッセージングシステムの構築と活用 (NEXT ONE)