Tenn25 Blog
「サーバ・インフラを支える技術」を読んだ
06.03.201918 Min Read — In others

サーバ/インフラを支える技術を読んだ

総括

  • 読んでよかった。主に負荷分散やフェールオーバーの仕組みについて体系立てて理解できた。
  • 10 年以上前の本なので、5 章以降のツールの話は読まなくていいと思う。

1 章. サーバ/インフラ構築入門

ハードウェアは壊れるので、冗長化が必要。
本章では、すごく原始的な冗長化から始まって、
順を追って冗長化の仕組みについて解説してくれている。

  • コールドスタンバイ

    • 更新が少ない、データを持たない、などのサーバは適してる
  • ホットスタンバイ

    • DB など常に同じ状態に保っておきたいならこちら
  • フェイルオーバー

    • ヘルスチェックをして、VIP を切り替えるスクリプトなどで運用するのが昔ながらの方法?
    • Web サーバなどはスタンバイ機のリソースが無駄になるので、できれば負荷分散をしたい。
  • 負荷分散

    • DNS ラウンドロビン
    • サーバの数だけグローバル IP が必要
    • 通信元の DNS キャッシュによって、均等に分散されない。
    • バックエンドが死んでても気づけない
    • ロードバランサー
    • 通信元から見えるのはロードバランサーだけ。こいつがリクエストを受ける。
    • そのためグローバル IP を割り当てるのも LB だけで良い。
    • LB がバックエンドの死活監視を行う。
    • 負荷分散アルゴリズムが複数あり柔軟に負荷分散できる。
  • Linux の負荷分散ソフトウェア

    • IPVS(IP Virtual Server)という。
    • ipvsadm, keepalived などがある。
  • L4 と L7 ロードバランサーの違い

    • L4 は TCP ヘッダなどを見て分散先を決める。
    • L7 はアプリケーション層の中身まで見て分散先を決める。
    • L4 では通信元とバックエンドサーバを直接結ぶ(分散先を紹介するだけのイメージ)
    • L7 では通信元 ⇄L7LB,L7LB⇄ バックエンド、の2つの TCP セッションが張られる(常に通信を仲介するイメージ)
    • パフォーマンスは L4 の方が良いが、L7 の方が柔軟な分散ができる
L7のロードバランサーを使うと、画像ファイルはWebサーバではなく、
画像ファイルサーバを直接見に行くなどの設定ができそう。(AWSの事例で見たことあるかも)

また、同じセッションIDの通信は同じサーバに振り分けるなどもできる。

2 章.ワンランク上のサーバ/インフラ構築

リバースプロキシ

L4 ロードバランサは分散先を決めるだけなので、
その後の通信はクライアントと Web サーバが直接行う。
ここで、間にリバースプロキシを挟むことでさらに柔軟な負荷分散ができる。
L4 ではできなかった、パスベースのルーティングを行うことができる!

よく言うプロキシサーバはLAN→WANへ出るときに間を仲介して代理で通信してくれるサーバのこと。
逆に、WAN→LANヘリクエストが来るときに間を仲介してくれるので、「リバースプロキシ」と呼ぶ。
  • リバースプロキシでできることの例

    • IP アドレスによる制御。特定の IP からしかリクエストを受付けない等。
    • パスによる制御。画像ファイルなど静的ファイルは S3、動的ファイルは AP(アプリケーション)サーバへ。
    • URL の書き換え。リクエスト元に見える見た目上のパスと、サーバ側のパスを変えたい場合。
    • UserAgent による制御。
  • AP サーバの動き

    • 基本的に 1 ユーザ(1 リクエスト)を、1 プロセス(1 スレッド)で処理する。
    • これによって、リソースの競合を防ぐことができるので、アプリケーション実装者が楽になる。
    • modperl,modphp,tomcat,gunicorn などがこれにあたる認識・・・。
  • 動的リクエストの動き

    • まず、動的コンテンツへのリクエストによって HTML が生成され返される。
    • その後、HTML に記載された画像ファイル、JavaScript、css などのリクエストが送られる。
    • 1 動的リクエスト+30 静的リクエスト のようになる。
  • よくある構成として

    • 上でも書いているように、リクエストのパスによって、静的なファイルは Web サーバ、動的ファイルは AP サーバが返すようにする。
    • リバースプロキシ自身も Web サーバなので、自分自身が返すことが多い。(nginx とか使ってるとよくわかる)
  • HTTP の Keepalive

    • ユーザが Web ページを表示するのに 1 動的リクエスト+30 静的リクエストかかるとする。
    • 31 回コネクションを張ることはせず、Web サーバ側の keepalive の設定時間はコネクションを再利用する。
    • Keepalive のおかげでコネクションのオーバーヘッドが減る。
    • keepaliveTimeout=15 なら 15 秒で切れる。一般的に 1 リクエスト 15 秒もかからないのでもっと下げても良い。
  • リバースプロキシを使う性能面のメリット

    • リバースプロキシが無いと AP サーバが直接セッションを維持することになるのでリソースを多く食う
    • リバースプロキシ(Web サーバ)は AP サーバに比べてリソース使用が小さいので数百〜数千プロセス保持されても大丈夫。
    • Web サーバ ⇄AP サーバ間は Keepalive をオフしておけば良い。
  • リバースプロキシを使う機能面のメリット

    • HTTP リクエストの前処理/後処理ができる
    • mod_deflate...レスポンスを gzip 圧縮して通信量を下げる(gzip はブラウザ側で解凍可能)
    • mod_dosdetector...DDoS 対策のモジュール(急激なアクセス負荷から守る)
  • リバースプロキシについてその他ノウハウ

    • プロセスが大量に増えるので worker モデルが適している。
    • prefork は 1 クライアント 1 プロセス、worker は 1 クライアント 1 スレッド。
    • httpd.conf の設定で最大プロセス数(MaxCLients)と、プロセスあたりの最大スレッド数()ThreadsPerChild を設定。
    • ServerLimit,ThredLimit も注意。これらのデフォルト値を超えそうな場合は調整しよう。
    • ただしメモリがスワップしない程度を上限としてチューニングすること。

キャッシュサーバ

  • ブラウザ-サーバ間のキャッシュの働き

    1. すでにブラウザ側で画像のキャッシュを持っている
    2. 新たにリクエストを投げる時は If-Modified-Since という日付がリクエストに含められる
    3. サーバ側はその日付以降にファイルが更新されていなければ、304(Not Modified)を返す
    4. ブラウザは自身の持っているキャッシュを表示する。
  • squid をプロキシサーバとして用いてキャッシュを利用する例

    1. 学内 LAN から WAN に出る時はキャッシュサーバである squid を仲介する。
    2. 他の PC から同じリクエストがあった場合、キャッシュが残っていればそれを返す。
    3. これによって、限られたネットワーク帯域節約できる。
  • squid をリバースプロキシとして用いる例

    • 仕組みは上記と同様
    • 別の squid とキャッシュを共有することもできるので、冗長化も可能
    • 動的コンテンツを 30 分だけキャッシュするなども可能(※ただしログインが絡む箇所は注意が必要)
  • memchached の利点

    • 上の注記で書いたように squid は、cookie によるセッション管理をする場合など、ステートレスでない場合は対応しきれない。
    • squid はあくまでページごとのキャッシュであるのに対し、memcached はアプリケーション内で使うデータをキャッシュする。

MySQL のレプリケーション

  • MySQL の非同期レプリケーションの概要

    • シングルマスタ、マルチスレーブの構成が主
    • レプリケーションの遅延を減らすため、スレーブには、IO スレッド、SQL スレッドに別れている。
    • IO スレッド...マスタ側が出力した更新情報(バイナリログ)を読み取り、リレーログに書き込む。
    • SQL スレッド...リレーログを読み取りスレーブに SQL を発行する。
    • スレーブは「どこまで同期したか」を示す「ポジション情報」を持っている。
  • スレーブを読み取り専用として活用する

    • リアルタイムバックアップとしてだけでなく、負荷分散のために使うことを考える。
    • 更新処理はマスターに、参照はスレーブに送ることで負荷分散させる方法が一般的。
    • さらに、スレーブをスケールアウトすることで、読み取りの負荷分散が可能。
    • 一般的に RDBMS は SELECT 操作が大部分を閉めるのでこの方法は理にかなっている。
    • 更新 or 参照の割り振りは AP サーバ側で行う。
    • スレーブの負荷分散には内部ロードバランサーを使う。

高速で軽量なストレージサーバの選択

今の現場だと、オンプレ環境でも Amazon S3 を s3fs でマウントするとかできるので、
よほどキツイ要件じゃなければ勉強する必要性が低そうなので飛ばす。


3 章.止まらないインフラを目指す

DNS サーバの冗長化

  • resolv.conf に複数 DNS サーバを記載しておくことはできるが、1 台目のタイムアウト待ち時間は深刻な問題になることがある。
  • DNS サーバ自体の冗長化 VRRP(keepAlive など)を使った冗長化が可能(VIP を付け替えることによる Active/Backup 構成)
  • IPVS と KeepAlive などでロードバランサーによる冗長化が可能(Active/Active 構成)

ストレージサーバの冗長化

割愛

ネットワークの冗長化

L1/l2 の冗長化

  • LAN ケーブルや NIC などの故障(リンク故障)
    → サーバとスイッチ間を冗長化する。ボンディングによって複数の物理 NIC を1つの論理 NIC として扱う
  • ネットワークスイッチなどスイッチ間接続の故障
    →NIC を冗長化しても接続先のスイッチが単一だったら意味がない。
さらなる冗長化について書かれているが、自分の業務とは遠いので割愛・・
このへんは基礎用語が分かってればいいかな・・・

VLAN の冗長化

VLAN のメリット

  • 構成変更の柔軟性が上がる
  • スイッチの有効活用ができる...上位ルーター、下位ルーターの別セグメントをまとめて管理できる
  • サーバの追加や置き換え、故障の復旧を物理的なつなぎ直しなしで行える。
Q. もしVLANを使わず同じスイッチで別セグメントで利用すると?
A. マルチキャスト/ブロードキャストフレームや、
まだスイッチが学習できていない宛先不明なユニキャストフレームなどは、
関係のないセグメントへも転送されてしまう。
無駄な帯域を消費するうえ、セキュリティ的にもよくない!

VLAN の種類

  • スタティック VLAN...ポート単位にグループわけをする。変更も手動。
  • ダイナミック VLAN...ルールに基づいてグループわけをする。

ポート VLAN

  • ポートごとに VLANID を割り当て、グループごとに同じ ID を割り当てる。
  • 設定はシンプルだが、複数スイッチ間でグルーピングできない。

タグ VLAN

  • 端末やスイッチから Ethernet フレームが送られる時に VLAN ID を挿入する。
  • 複数スイッチ OK だが、フレームが流れる機器が全て VLAN タグに対応してないといけない。

4 章.性能チューニング

「負荷を知るということは、OSを知るということ」
「推測するな。計測せよ。」
Webアプリケーションの負荷分散は、多くの場合ディスクIOの分散と軽減。
OSはIOを軽減するために、キャッシュの仕組みを持つ。
キャッシュが最も効くようにすることがIO分散のコツ。

ボトルネック見極めの流れ

  • まずロードアベレージを見る

    • 高い?→CPU がボトルネック
    • top,sar...カーネルかユーザーか、コア毎に見る
    • ps...プロセスの特定
    • strace,oprofile...ボトルネックの特定

    逆にいうと他がボトルネックになっていない理想的な状況
    または、プログラムのバグやリクエスト過多によるもの

    • 高くない → メモリ、IO
    • ps でプロセスを特定

    単純に入出力が多いか、スワップしてページアクセスが発生している

  • そもそも負荷とは

    • 「複数タスクによるサーバリソースの奪い合いによって生じる待ち時間」
  • プロセスとは

    • 「プログラムの命令と、実行時に必要な情報がひとまとめになったオブジェクト」
  • プロセスディスクリプタ

    • プロセスごとに作られる管理用のテーブル。プロセスの状態を持つ。
    • TASK_RUNNNING...実行可能な待ち状態
    • TASK_INTERRUPTABLE...割り込み可能な待ち状態。スリープやユーザからの入力待ちなど。復帰可能時間が不明
    • TASK_UNINETERRUPTABLE...割り込み不可能な待ち状態。ディスクの入出力待ち。短時間で復帰可能。
    • TASK_STOPPED...サスペンドのシグナルを送られてリジュームされるまでスケジューリングされない
    • TASK_ZOMBIE...ゾンビプロセス。

プロセスとスレッド

  • スレッドの方が、メモリ空間を共有する分リソースを食わない
  • スレッドの方が、コンテキストスイッチが発生しない分コストが低い
  • ただし、カーネルから見ればスレッドも 1 つずつのプロセスのように見えている(LWP...Light Wight Process)
  • ps -L でマルチスレッドのスレッドも一覧で表示される。
sarコマンドのオプション
sar -u...CPU(cpU)
sar -r...メモリ(memoRy)
sar -q...LoadAverage(キュー)

各項目の意味
usr...ユーザモードでCPUが消費された割合
nice...niceで優先度を変更していたプロセスがユーザモードでCPUを消費した時間の割合
system...システムモードでCPUが消費された時間の割合
iowait...CPUがディスクのIO待ちのためにアイドル状態で消費した時間の割合
steal...OS仮装化の場合に、他の仮装CPUの計算で待たされた時間
idle...待たされることなくアイドル状態で消費した時間の割合

チューニング

チューニングは、そのハードウェア、ミドルウェアが本来持つ性能を十分発揮できるようにすること。
限界以上の性能が出るわけではない。

遅くなった原因が明確になれば、対応方法を実施するだけ。
ボトルネックが発生したときに原因を特定するための知識が必要

チューニングのアプローチ

  • 頭が痛いから頭痛薬を飲もう!みたいな対処療法はやめよう
  • 根本原因を突き止めて根本解決を。(肩凝りからの頭痛かもしれない)
  • システムチューニングの場合は、推測ではなく計測をすること!!!
  • システムの性能限界以上の性能は出ない。ボトルネックが無いならサーバのスペックを上げるしかない
  • 計測するためのツールの使い方と、OS やミドルウェアの仕組み・内部構造を理解しておくこと

Web サーバ(Apache)

並行処理の実装モデル

  • マルチプロセス
  • マルチスレッド
  • イベントドリブン

MPM(multi processing module)

  • prefork...あらかじめ複数プロセスを生成する
  • worker...マルチプロセスとマルチスレッドの複合技。大規模やスケーラビリティを求めるとこちら

プログラミングの観点から見た違い

  • マルチプロセスの方が、メモリ空間が分割されているので安全
  • マルチスレッドの方がリソースの競合を考慮しないといけないので実装が難しい。
結局、1クライアントあたりのレイテンシに違いはない。
大量のコンテキストスイッチがある場合をのぞいて大きな性能差は無い。

DB サーバ(MySQL)

チューニングの観点としては「いかに高速にデータの入出力を行うか」
  • サーバサイド

    • ディスク IO のカーネルパラメータの設定
    • ファイルシステムの選択とマウントオプション
    • パーティショニング
  • サーバサイド以外

    • テーブルの設計
    • SQL の最適化