テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

dm-thinの概要

わけあって, カーネルについて調査しなければいけないので, dm-thinについて調べることとした.

理由:

  • http://www.slideshare.net/enakai/docker-34668707, http://blog.etsukata.com/2014/05/docker-linux-kernel.html によると, 最近バカみたいに流行ってるDockerというコンテナ管理ソフトウェアがディスクイメージ管理方法としてdm-thinを使っているらしい.
    • もし, dm-thinにコードを入れることが出来たら, 流行りのDockerにも影響を与えることが出来て, 大きい. ライトブーストは性能オンリーだが, 機能面の拡充というDMのすべきことをきちんとしている感じでわりと好き.
  • ライトブーストの仕事を続けていくにあたって, 他のターゲットの設計/実装を理解していると有利だから.
  • 日立のストレージ部の新人研修でシンプロについて聞いた時, わりと面白いと思ったから.
    • 日立のブロックストレージはシンプロとか階層化管理とかかなりがんばってる.
    • DMにもまだ階層管理はない. btierというのがマージに挑戦して死んだ経緯があるが, これはDMではないだろう. http://www.spinics.net/lists/linux-kernel-janitors/msg15549.html 階層管理はDMでやるべきだ. ジョーと議論して, 必要ならばおれがやる.

ちなみに, http://d.hatena.ne.jp/defiant/20120201/1328097804によると, dm-thinは3.2から入ったらしい.

もともとジョーはdm-thinに相当な労力を注いでいた. その証拠に, 私が今コードを入れているdevice-mapper-test-suiteは, dm-thinのみのために作られたthinp-test-suiteというものをベースにしている. また, ライトブーストがマージされたthin-devというブランチはdm-thinのためのものだ. ここが彼のメインブランチなので, 彼の代表的な作品がdm-thinと言っても良い. dm-thinはジョーの子供なのだ.

コードはdm-thinだけで5000行以上あって, 結構きついので, まずはドキュメンテーションを読むことにした.


シンプロビジョニングとは

問題:

みなさんは3TBのHDDを買って, それを本当に使い切ったことが何度あるだろうか?ファイルシステムは初期化時にメタデータ領域を確保するが, データ領域については必要になるまでアクセスしない. つまり, ほとんどの領域は一度も触られていないことがほとんどだ.

これが企業レベルになると話がさらに大きくなる. これだけ容量がどれだけ必要になるか分からないからと言っていきなり1PBのストレージを導入してしまっては無駄となる可能性が高い. 入念に調査をしたところで, そもそもそんなことをしていたらビジネスチャンスはどこかに去ってしまうし, きっとその調査結果は間違うので, 将来的にどの程度ストレージ容量が必要になることを予測することは一般に不可能だ.

「今必要な容量から始めることが出来て, 将来的に容量が足りなくなれば拡充することも出来る」これは素人の直感的には簡単だろうが, 本当は全然簡単ではない.

解決:

ストレージを仮想化する. ここでいう仮想とは, メモリの仮想化でいう仮想と同じ意味である. 仮想ボリュームのあるブロックが物理的なブロックに束縛されるのはオンデマンドとなり遅延される. このマップはどこかに保存されて, アクセス時にはマップを参照する(並列プログラミングのギャザーのようなもの. 仮想データ[i] = 物理データ[[マップ[i] ]).

スナップショットとは

dm-thinはスナップショットもサポートする. スナップショットとは, ある仮想ボリュームAをもとにして, 別の仮想ボリュームBを作る. この時, 仮想ボリュームBの状態 = 仮想ボリュームAの状態 + 差分状態となる. この差分状態の管理はCoWによって行われる.


dm-thinの調査

(1) プール

dm-thinはデータデバイスとメタデータデバイスの2つによってプールを構成する.

    dmsetup create pool \
    --table "0 20971520 thin-pool $metadata_dev $data_dev \
       $data_block_size $low_water_mark"

low_water_markは, データボリュームについて「この閾値より残り物理容量が少なくなったらエラーを返すなりrequeueするなりと言った異常モードに入ってください」定義するものである. 同様の閾値メタデータにも定義されているが, これはカーネル内に埋め込まれているということである.

デバイスを分ける理由は, 1) metadata分だけ冗長化したい場合がある 2) metadataだけSSDにすることで性能を上げる. ちなみに, dm-cacheも同じ設計をとっている. ライトブーストは, すべてが同じデバイスである(そして, メタデータとデータをログ書きする).

Metadata is stored on a separate device from data, giving the
administrator some freedom, for example to:

- Improve metadata resilience by storing metadata on a mirrored volume
  but data on a non-mirrored one.

- Improve performance by storing the metadata on SSD.

なお, メタデータデバイスの容量が足りなくなると異常モードに入る. こうなると異常であるというフラグが立つ. 管理者がメタデータデバイスが不整合してないことを確かめてデータ容量を増やして, フラグを明示的に落とさなければいけない. メタデータデバイスが異常である間, データデバイスのリサイズも出来なくなる.

discardまわり

    thin-pool <metadata dev> <data dev> <data block size (sectors)> \
          <low water mark (blocks)> [<number of feature args> [<arg>]*]

    Optional feature arguments:

      skip_block_zeroing: Skip the zeroing of newly-provisioned blocks.

      ignore_discard: Disable discard support.

      no_discard_passdown: Don't pass discards down to the underlying
               data device, but just remove the mapping.

discardは, 「この領域は上位的には使ってない」ということをブロックレイヤに教えてあげることで, 最適化のヒントとするものである. シンプロといえば, discardだ. プールの作成時にはdiscardについて, さまざまなオプションが設定出来るようになっている. これらが動的に変更可能かどうかは分からないが, 直感的には, 出来てよいものと思う.

また, trimというメッセージもサポートしているようだ. reduce the sizeが何を表しているのか曖昧でよく分からない.

If you wish to reduce the size of your thin device and potentially
regain some space then send the 'trim' message to the pool.

(2) thinデバイス

thinボリュームはこのプールから切りだされる. あらゆるthinボリュームには24bitのIDがつけられる. 以下のコードは, poolにcreate_thinメッセージを送り, ID=0のthinボリュームを作る. この時点では, thinボリュームを登録しただけ(たぶんメタデータデバイスに登録をした)でありactivate(メモリ上に仮想デバイスを作る)されていない.

   dmsetup message /dev/mapper/pool 0 "create_thin 0"

activateするには以下のようにする. 美しいことに, メタデータデバイスは「ID=0のデバイスは2097152sector大であった」ことを記録しない. 次にactivateするときにこれより小さくしても大きくしても動作する. そこはユーザランドが管理しろという設計になっていて, 私は良いと思う. dm-thinには他にも, ユーザランドとの連携のために設計された箇所がある(transaction IDとか). これによってLVMで管理可能になったことでDockerなどでも応用が出来るようになったのだろう. ライトブーストもLVM対応する場合には何か連携するためのインターフェイスを定義する必要があるのだろうか. 私はないと思う.

   dmsetup create thin --table "0 2097152 thin /dev/mapper/pool 0"

(3) snapshot作成

dm-thinは多段的なsnapshotを許す.

Another significant feature is support for an arbitrary depth of
recursive snapshots (snapshots of snapshots of snapshots ...).  The
previous implementation of snapshots did this by chaining together
lookup tables, and so performance was O(depth). 

このため, 「すべてのデバイスはthinデバイス」であるということにしている. これはすなわち, あるthinデバイスからsnapshotを作ったとして, それもthinデバイスであるということだ. そして, その新しいthinデバイスを元(originという)にして新しいsnapshotを作ることも可能である.

    dmsetup suspend /dev/mapper/thin # ID=0のやつを止める. snapshotを作るためには止める静止させる必要がある. 
    dmsetup message /dev/mapper/pool 0 "create_snap 1 0" # ID=0をoriginとしてsnapshotによってID=1というthinデバイスを作る.
    dmsetup resume /dev/mapper/thin
    dmsetup create snap --table "0 2097152 thin /dev/mapper/pool 1" # 作ったsnapshotをactivateする.

この, 複数thinデバイスがあって, それぞれに親子関係あるということについて

  • あるthinデバイスがactivateであってもそうでなくても, それは他のthinデバイスに影響を与えない(以下記述X).
  • ループはたぶん許されていないが, 書いてない(チェックしてるのか分からない. やってみたい)
  • C -> B -> A (矢印は, originを表す)というスナップショット関係である時, Bをdelete(以下記述Y)するとどうなるのか. deleteはたぶん, metadataを消し去ってしまうということだと思う(不可逆と書いてある). たぶん危険なので許容されないが, どうなるかは書いてない.

記述X

  have only one of them active, and there's no ordering requirement on
  activating or removing them both.  (This differs from conventional
  device-mapper snapshots.)

記述Y

    delete <dev id>
    Deletes a thin device.  Irreversible.

internal と external

snapshotには, internal snapshotとexternal snapshotがある. internalは, プールとそれから切りだされるthinデバイスの系(ローカルな系とする)だけで語られるスナップショットのことである. externalは, 外部のリードオンリーなデバイスをoriginとしてスナップショットすることである. ライト差分はローカルな系に記録される. externalなものは, 一旦ローカルな系に書いてからそれをoriginにしてinternal snapshotをしても良いと思うが, それがだるいケースのためだろう. externalのユースケースとしては以下のように書いてある. VMのベースイメージを複数VM間で共有する場合など.

One use case for this is VM hosts that want to run guests on
thinly-provisioned volumes but have the base image on another device
(possibly shared between many VMs).

拡張案

階層化を実現する. 階層化とは, 仮想デバイスに割り当てるブロックを, プールの中のSSD, HDDなどからアクセス頻度によって割り当てる方式のことである.

階層化を実現する方法として, まず一つは, これをpureに分離すること. この意味は, poolのdataデバイスを階層化デバイスにしてしまうということ. もう一つは, dm-thinの中に階層化の実装を放り込むこと. シンプロも階層化も結局, 「仮想ブロックはどの物理ブロックに割当たっているか」などの処理が必要であるため, 融合させる相性は良い.

dm-thinはそれとしてpureに保つことに美しさはある. ただし, dm-thin自体はLVMのツールも実装されてしまってるしDockerにも使われてしまってるため, インターフェイスを変更することは難しい. その意味では, 分離したターゲットとして作るのが筋が良いだろう. しかし, 配置を変更している間のライトをどうするかなど, 密結合とした方が良い意味も多分にある. このあたりは熟考を要する. まずはpureに分離する実装から考えるのが良いような気がする.