なんかかきたい

プログラミングなどの個人的なメモやサークル「ゆきいろパラソル」の情報を載せてます

そこそこ使えるストレージサーバを作るときに考えていたこと(概念)

前置き

私がWebサービスの会社でサーバ構築・運用の仕事をやっていた頃の話です。

ストレージサーバはユーザさんのコンテンツを保存する重要なサーバでしたが、 コンテンツの増加に伴い、既存のサーバでは十分な性能を出せていなかったことがありました。

運用面でのコスト増加、スケールアウトを行う上での問題などいくつかの条件が重なった時期を見て、 コスト計算、技術的な問題点などを洗い出し、サーバを新規に作り直す提案をしたところ、会社として了承が得られ、 ちょっとしたリプレイスを行うことになりました。

その際、運用コストが増加している原因をあわせて取り除きつつ、安定するサーバ構成を取れればよいと思い、 ストレージサーバの性能向上と、データ冗長化を強化できる構成としました。

目標は、低コストで読み込み性能がそれなりにあり、メンテナンス性がよく耐障害性のあるサーバですが、 確保した予算を超えない範囲で性能、安定性の向上を果たす必要があります。

この件では考える機会が多く、日頃知っている知識を広く使った総合的なお仕事でしたので、 自分でも後から見直したときに便利そうということでまとめておきます。

(余談ですが職場にはこのページには書いていない具体的な戦略などをまとめた文書を置いてあり、 また特定の環境のことについては触れていないため、同僚の方は読まなくても差し支えないと思います。)

構築するサーバ

LinuxベースのWebサーバで、既存のサーバではnginxが動いていたため、ソフトウェアの構成はそのまま移行することとしました。

ソフトウェアの設定を変更せずとも今回の目標は達成できます。

ハードウェア

ストレージサーバ構築なのでストレージのことを考えがちですが、CPUやメモリも基本パーツとして重要となります。

サーバ筐体

この件でストレージの次に考える必要があったのは、サーバ筐体でした。当たり前ですがマザーボード込みで考える必要があります。

後述しますが、低コストで構築する必要があったため、3.5インチSATA HDDを多く搭載できるサーバを選んでいます。

1Uラックマウントサーバでは3.5インチベイを4つ持つサーバが多いのですが、サーバによっては1Uにもっと多くのディスクを搭載できることもあります。

1ノードあたりのSATAポート数には上限があるため、多くのSATAポートを持つ筐体はSASを変換したものがありますが、 SAS変換ボードが原因のトラブルにあった経験上あまりおススメできないです。

一般的な話として部品点数が多くなれば故障時に困りますので、必須でなければ部品点数は増やさないほうがよいでしょう。

コスト面で考えることとして、1Uあたりに搭載できるストレージ数が多いほど、ラックあたりのランニング費用は低くできると考えられます。

また、1ノードあたりに搭載できるディスクが多いほど、多くのデータを保持できるようになり運用面で楽になりますが、サーバ障害時に縮退するデータの量は多くなってしまいます。

もちろん、アクセス要求が集中しても十分に処理できるようでなければ1台に集中させることはできません。

ここは匙加減となりますので、データの縮退が許容できる構成をとりつつ多くのデータを1台にまとめることを目標にサーバを選びます。

  • コンシューマ向けSATAディスクを使う場合SASは避ける
  • 1Uあたりに多くのデータが搭載できるようにするとコストが下がる
  • 1Uあたりにデータ集中するとダウン時の影響は大きくなる
  • 電気使用量に気を付ける

CPU

近年CPU性能は大きく向上しており、ストレージサーバを構築するにあたってCPU処理性能が大きく影響するということはないと考えてよいと思います。

しかし、ストレージサーバではIO待ちが多く発生するため、同時に発生するIOの数に近いコアを持ったCPUを使用したほうがよいでしょう。

第5世代のXeonでは安価なXeon 3106で8コア8スレッド、Xeon 4108が8コア16スレッドですので、この辺りのプロセッサでも十分であると思われます。

メモリ

Linuxでストレージサーバを作るにあたり、メモリはページキャッシュの大きさを考えて搭載するサイズを決めるのがよいでしょう。

メモリキャッシュはディスクアクセスを減らすために有効で、Webでのコンテンツのようにデータの変更が少なく、読み込みが多いケースでは効果的です。

キャッシュが有効なのは頻繁にアクセスされるデータですので、アクセスログから比較的アクセスの多いデータのサイズと数を事前に調べておけば 最適なメモリサイズを選択できます。

ストレージサーバではメモリに高速にアクセスできなくても、ストレージのIOに比べれば十分高速なため、マルチチャネル構成を取って最適化するほどではないでしょう。

DRAM価格が高値のため、現状メインメモリは高価なパーツとなりがちなため、必要なサイズに近いメモリを搭載することでコスト面で有利でしょう。

言わずもがなですが、ECCによるエラー訂正は必須です。ないと必ずデータが壊れますし、それなりの頻度でKernel Panicを起こします。

ストレージ

SSDはHDDと比較し、ランダムアクセス性能に優れ、読み書き速度も速い記憶装置ですが、2017年現在HDDと比較してまだまだ高価です。

IOPSの面ではSSDに軍配が上がりますが、シーケンシャルアクセス性能に限って言えば、RAIDを使ってある程度底上げできるため ブロックサイズに近い小さなファイルを大量に読み込む必要のある場合を除けばHDDで十分でしょう。

  • 4KB未満のファイルを大量に読み出す必要がある
    • HDDのブロックサイズ未満のファイルを読み出す場合、IOPSの上限がある
  • 512KB未満のファイルを大量に読み出す必要がある
    • RAIDのストライプによる効果が薄くなる
    • 512KB未満のファイルが多いことが分かっている場合は、チャンクサイズを小さくすればよい(例えば128KB。というかデフォルトが大きい気はする)

コンシューマ向けHDD

エンタープライズ品は保証期間が長かったり、サポートが充実していたり、検査項目が多いなどがメリットとなりますが、コンシューマ品と比較すると価格が高いです。

サポート代込みと考えれば意外と安いという見方もありますが、コンシューマ品でも故障時には交換対応してくれますし、 一部の故障しやすいブランドを除けばコストパフォーマンスから考えればよい選択肢ではあると思います。

コンシューマ向けHDDで有名どころは以下のメーカーでしょう。

  • Western Digital (WD)
    • 古くからあるHDDメーカー
    • 東芝のメモリ事業買収で有名
  • Seagate
    • 古くからあるHDDメーカー
    • 非常に安価なコンシューマ向けHDDを販売しており、小売店の特価でよく見かける
    • コンシューマ向けの品質が低いとの噂が絶えない
  • 東芝
    • コンシューマ向けHDDではあまりメジャーではない気はする
    • 安い割には意外と大丈夫な印象(個人の感想)
  • HGST

そのほか、OEM提供のHDDはありますが、中身は上のメーカーのものを使用していると思いますので、とりあえず上の4つだけ抑えておけばよいでしょう。

2017年現在、各メーカから販売されているHDDの特徴と個人の感想です。

容量あたりのコストパフォーマンスの良い8TBモデルのみまとめました。

メーカー 型番 備考
WesternDigital WD8002FRYZ WD Gold 高品質、高価
HGST 0S04012 コンシューマ向けだがエンタープライズの転用で高品質
東芝 MD05ACA800 コンシューマ向け、安価
WesternDigital WD8001FFWX WD Red Pro 高品質、高価
HGST HUH721008ALE600 エンタープライズ向け、高価

SeagateさんのBarraCudaはぶっちぎりで壊れてましたね。IronWolf Proの赤いものなら大丈夫な気もしてますが、検証までやっていないです。

ベンチマーク

読み書き性能のテスト

単にディスク読み書き性能を測定する場合、fioを使えば十分でしょう。Debianではaptでインストールできるので検証環境を用意するのも簡単です。

apt-get install fio

Windowsで有名な CrystalDiskMark っぽい表示をするための設定を公開している人もいるので、それに倣って設定を使えばネット上の性能と比較するときに便利です。

fioは並列読み書き性能を測定することができるので dd で読み書き性能を測定するよりは実際の使い方に近いテストができるでしょう。

読み込み速度 MB/s

HDDは高速化しており、シーケンシャルなアクセスが行える場合では 100MB/s を超える読み書きを行えるモデルがほとんどです。

配信サーバの場合、ファイルのサイズが数100kbを超えるファイルが多ければ、シーケンシャルリード性能に近い性能で読み込むことができます。

IO性能 IOPS

1秒間に処理できるIO数の指標です。読み込み速度の性能とは別に調べておきます。

細かな読み込みが多い場合に、読み込み性能の限界より先にIO性能の上限に達することがあります。

HDDは細かなIOを処理するのがやや苦手なため、IO数が多くなる小さなファイルを大量に配置する構成はオススメできません。

具体例としては、サムネイルのような小さな画像を保存する場合で、HDDに保存する戦略は性能を最大まで発揮できないことが多いです。

細かなIOにはメモリやSSDなどより適したハードウェアを使うほうが有利なため、用途に合わせて使いわけましょう。

テスト環境を用意したベンチマーク

fioを用いた性能測定では、実際のアクセスにより近いディスクアクセスを再現するのは限界があるため、大まかな性能測定としては有用ですが、 本番データをコピーした環境を用意できる場合は、より本番に近い環境でテストする方がよいでしょう。

今回は配信サーバの構築ですので、書き込み数に対して読み込みリクエストが非常に多いため、読み込みテストのみでも本番に近いといえ、テスト環境は構築しやすい部類でした。

配信はHTTPで行う想定でしたので、並列でHTTPリクエストを投げるベンチマークスクリプトを作成し、 秒間リクエスト数を変化させながらサーバにかかる負荷や、アクセスログからレスポンスタイムを集計し、 遅いリクエストがどの程度の遅さなのか、リクエスト数が増えた場合に最大どの程度のリクエストに耐えられるのかなどを測定しておきました。

例えば、nginxの例では $request_time をログに書き出せば、容易に測定ができます。

注意点としては、Linuxにはぺージキャッシュがあるため、同じファイルや少ないファイル数、 少ない時間での測定を行うと本運用時のようにディスクからの読み込みが行われず性能測定を誤ることがある点です。

多くの場合、メインメモリのサイズよりストレージのサイズは非常に大きいので、スラブキャッシュはさておきページキャッシュはヒット率が低い場合がほとんどです。

測定する際には、キャッシュアウトする程度の量のデータにランダムである程度の時間アクセスするようにし、キャッシュにヒットしなかった場合での性能を測定するようにしましょう。

冗長化

RAID

RAIDにはハードウェアRAIDソフトウェアRAIDやRAID0などの種類ががあります。 目標とする要件に合わせて選べばよいのですが、ソフトウェアRAIDを使いました。

  • LinuxではRAIDコントローラーを使わなくてもmdadmのインストールや設定が容易
  • ストレージサーバにおいてCPUリソースは余りがちである
  • 専用のハードウェアを用意することによる費用と故障率の増加、故障時のメンテナンス性

RAIDレベル

  • RAID 0
    • ストライピングを行う
    • 複数のディスクに分けて書き込み、読み込みを行うことで読み書き速度の向上を狙える。
    • 冗長化とは関係がない。
    • ディスクサイズは複数ディスクを合計したサイズになる
    • 必要ディスクは2台以上
  • RAID 1
    • ミラーリングを行う
    • 2つのディスクに同じ内容を書き込み一方のディスクが故障したときに備えられる。
    • コピーを書き込むため、ディスクサイズは変わらない。
    • 必要ディスクは2台以上
  • RAID 10
    • RAID1と0の組み合わせ。ストライピングとミラーリングを同時に行う。
    • 必要ディスクは4台以上
  • RAID 5
    • 分散パリティ
    • RAID0のように複数のストレージに分散して書き込む
    • 誤り訂正情報を書き込んでおくことで1台のディスク故障時にパリティから復旧できる。
    • 故障率がRAID10と比較して高い
    • 必要ディスクは3台以上
  • RAID 6
    • RAID5のように分散パリティの書き込みを行うがパリティを2箇所に書き込む
    • 2台のディスク故障まで耐えられる
    • 故障率はRAID10と比較して低い
    • 必要ディスクは4台以上

細かいことは tagomoris さんの記事にまとまっているので以下の記事をどうぞ。

個人で作るNASのような構成はさておき、ストレージサーバではディスク4台以上を搭載したマシンを用意することが多いと思うので、通常RAID6を選択しておけばよいでしょう。 ただし、2台分をパリティに使うため利用可能な容量は2台分減ることとなります。

ハードウェアコストの面からみると、サーバ1台あたりに搭載できるディスク数が多いほど、パリティのための割合は小さくなるためコスト面では有利になります。 ただし、CPUコア数が少ないとIO待ちに取られますし、サーバ1台へのディスク搭載数が増えると保存容量も大きくなるため、ダウン時に失うデータ量も多くなります。

  • 近年では実コア数の多い、CPUが比較的安い値段で提供されているため、コストは下がっています。
  • 保存容量の多さについてはバックアップマシンを用意することでダウン時に備えておくべきでしょう。

RAIDアレイのベリファイ

DebianなどのLinuxディストリビューションでは、定期的なRAIDアレイのベリファイを行うためのタスクが設定されています。

/etc/cron/mdadm などには月に1回チェックをするよう記述されています。

HDDの故障は実際に不良セクタに当たって、読み込みエラーとなるまでわからないため、故障を確認する最も確実な方法はディスクの全データの読み込みチェックです。

S.M.A.R.T. はエラーの記録をしているにすぎないため、素早く故障に気づく方法としては十分ではありません。

一ヶ月に一度の全チェックで、不良セクタの検出ができるため、監視ソフトなどでこのチェック結果を確認することで故障に気づけるようになります。

当然ではありますが、故障が見つかった場合、速やかにディスクを交換すべきです。

予備のディスクは常に用意しておき、すぐに取り付けできるようにしておくか、コールドスタンバイとして未使用のまま取り付けをしておけばよいでしょう。

後述するRAID再構築時の性能低下にも注意し、本運用中のサーバから切り離した上で、ディスク交換、RAIDの再構築を行います。

ディスク故障時のRAID再構築

RAIDアレイから故障したディスクを取り外し、新たにディスクを追加すると、 mdadmRAIDの再構築を始めますが、 データの再構築中、多くの読み書きが発生するため、ユーザリクエストを処理させるサーバとしては適切ではないことがあります。 そのため、1台は本運用中のサーバから切り離しても、十分処理できるようサーバの性能には余裕を持たせるようにしておきます。

台数が多いほど1台ダウン時の影響は少なくなりますが、 例えば3台で配信している場合、1台ダウン時に全体の33%が他2台に移り、リクエストはそれぞれ1.5倍となります。 1台切り離すことを想定するならば、1.5倍のリクエストでも処理できるかどうか事前に調べておく必要があります。

構築にかかる時間についてはディスクのサイズによりますが、1週間以上かかることもあるため、こちらも事前に調べておくのがよいでしょう。

デフォルトでは新規にRAIDを構築したあとでチェックを行うため、 cat /proc/mdstat を確認することでどの程度時間がかかるのか予想することができます。

まとめ

今回はストレージサーバを作る例で紹介をしましたが、 必要となるサーバの構築手順の大枠は以下の通りです。

  • 要件の洗い出し (目標とするリクエスト数の設定)
  • 費用の見積もり (ハードウェア選び)
  • 検証環境の構築
  • 本運用環境の構築
  • 既存のデータの移行

基本的な手順に従って見積もり、構築をしていくだけですが、 目標とする要件の洗い出しや費用の見積もりをうまくやっていくのがポイントかなと思います。

実際のところ私がこの件で狙っていたのは、以下のような運用上のコストで、まずはストレージサーバの運用コストを下げたいという点で考えていました。

  • 運用時に必要となる多くの確認項目を自動化する
  • データ冗長を強化することでデータの損失を起こりづらくする
  • IO性能に余裕あるサーバ構成とし障害発生時の影響を小さくする
  • 運用を簡単にすることでミスが起こりづらくなる

ただ、これらの要件を満たすためには、どうしても費用が増えてしまうため、 なるべく低コストで済ませるために中長期運用に伴いコスト削減ができる点を強調した提案をしました。

この辺りはビジネス上必要なことで、ある費用の場合にどのような構成を取れるのか、プランをいくつか持って置き、 決定権のあるオーナーへの説明が十分にできるよう考えおく必要があります。

技術者としてあまり必要ではないかもしれませんが、インフラエンジニアには必要になってくるスキルだと思います。

ところで、ここまで長々と書いてきましたが、 通常このようなサーバの構築と運用は費用対効果で考えると 今一つなことも多く、予想される配信量が極めて少ないことが予想できたり、 ストレージに保管するファイルを自社のサーバ以外においてもよいなどの 緩い要件であれば、AmazonS3など外部のストレージサービスを利用したほうが、 結果的にコストが安くなることもあります。

費用見積もりの時点でストレージサービスと比較してコスト面で有利かどうか考えるのもよいでしょう。

そういえばファイルシステムについては書かなかったのですが、xfs などを使えばいいかと思います。 あんまり困ることもないでしょうし。