以下は、Linux カーネル文書、Documentation/device-mapper/thin-provisioning.txt の kanda.motohiro@gmail.com による訳です。原文と同じ、GPL v2 で公開します。日本語で書かれた、dm-thin の実装についての説明は、http://www.slideshare.net/akirahayakawa716/dmthin20140528 が助かります。
はじめに
========
この文書は、シンプロビジョニングとスナップショットを実装するデバイスマッパーターゲットの集合について述べます。
以前のスナップショットの実装と比べた時のこの実装の主なハイライトは、多くの仮想ボリュームが、1つの同じデータボリュームに格納できることです。これは管理を簡単にし、ボリューム間のデータの共用を可能とします。このためディスク使用量が減ります。
もう一つの重要な機能は、任意の深さの再帰的スナップショットがサポートされることです。(スナップショットのスナップショットのスナップショット。。。)以前のスナップショットの実装は、これを、検索テーブルを順にチェーンすることで行いました。このため、性能は、O(深さ)でした。新しい実装は、単一のデータ構造を使い、この深さによる性能低下を避けます。しかし、あるシナリオの元で、断片化は依然として問題となることがあります。
メタデータは、データとは別のデバイスに格納されます。これにより管理者は、例えば、以下のような自由を持ちます。
- メタデータをミラーボリュームに、データをミラーされてないボリュームに格納することで、メタデータの堅牢性を高めることができます。
- メタデータを SSD に格納することで、性能を高められます。
ステータス
========
これらのターゲットは、いまだ、とても実験的な状態です。どうか、本番環境でこれに頼るのは、まだやめてください。その代わり、実験をして、私達にフィードバックを下さい。異なるユースケースは、例えば、データボリュームの断片化のために、異なる性能特性を示すでしょう。
もしこのソフトウェアが期待通りに動かないことに気がつかれたら dm-devel@redhat.com に、詳細を添えてメール下さい。私達はあなたのためにそれを改善するよう、ベストを尽くします。
メタデータをチェックしたり、修復するためのユーザ空間のツールは開発中です。
訳注。https://github.com/jthornber/thin-provisioning-tools
クックブック
===========
この節は、シンプロビジョニングを使うための素早いレシピをいくつか述べます。デバイスマッパーのドライバを直接制御するために、 dmsetup プログラムを使います。エンドユーザは、サポートが追加された後に、LVM2 のような高レベルのボリュームマネージャを使うことをお勧めします。
訳注。man lvmthin が動くなら、サポートされています。
プールデバイス
---------------------
プールデバイスは、メタデータボリュームとデータボリュームを一緒にまとめます。それは、I/O をデータボリュームにリニアにマップし、メタデータを2つの機構を使って更新します。
- シンターゲットからの関数呼び出し
- ユーザ空間からのデバイスマッパー「メッセージ」。これには、新しい仮想デバイスの作成を制御することも含まれます。
新鮮なプールデバイスを設定する
----------------------------------------------
プールデバイスの設定には、有効なメタデータデバイスとデータデバイスが必要です。既存のメタデータデバイスが無い時は、最初の 4K をゼロにすれば、空のメタデータデバイスができます。
dd if=/dev/zero of=$metadata_dev bs=4096 count=1
必要なメタデータの量は、シンデバイス間でどれだけのブロックが共有されるかによって変わります(つまり、スナップショットによって)。もしあなたが、平均以下の共有をするなら、平均以上のメタデータデバイスが必要です。
指針として、メタデータデバイスで使うバイト数を、48 * $data_dev_size / $data_block_size で計算するのをお勧めします。なお、結果が 2MB より小さい時は、切り上げて下さい。もしあなたが、大量の変更を記録する多数のスナップショットを作っているなら、これを大きくする必要があるでしょう。
サポートされる最大長は、16GB です。デバイスがこれより大きい時は、警告がされ、余分の領域は使われません。
プールテーブルを再ロードする
--------------------------------------------
訳注。テーブルとは、デバイスマッパーターゲットの領域定義で、特別な用語です。
プールのテーブルを再ロードできます。実際、プールの領域がなくなった時に、リサイズする方法がこれです。(注意。再ロードの時に、異なるメタデータデバイスを指定するのは、今のところ禁止されていません。しかしそれによって、以前と少しでも異なるディスク上の位置に I/O がルーティングされると、えらいことになります。
既存のプールデバイスを使う
--------------------------------------------
dmsetup create pool \
--table "0 20971520 thin-pool $metadata_dev $data_dev \
$data_block_size $low_water_mark"
$data_block_size は、一度に確保することのできる最小のディスク領域の単位を、512バイトのセクタを単位として指定します。$data_block_size は、 128 (64KB) から 2097152 (1GB) の間で、128 (64KB) の倍数でなくてはいけません。シンプールを作った後で、$data_block_size を変えることはできません。主にシンプロビジョニングに興味がある人は、1024 (512KB) のような値を使うのがよいでしょう。多くのスナップショットをしたい人は、より小さな値、例えば、128 (64KB) を使うのがよいでしょう。もしあなたが、新しく確保されたデータをゼロにしないならば、より大きな、256000 (128MB) くらいの領域にある $data_block_size をお勧めします。
$low_water_mark は、$data_block_size 長のブロック数で表します。もし、データデバイスの空き領域が、これのレベルよりも小さくなると、dm イベントがトリガーされます。ユーザ空間のデーモンがこれを捉えて、プールデバイスを拡張することができるようにするためです。そのようなイベントは、一度しか送られません。デバイスを、新しいデーブルで再開すると、それもまたイベントをトリガーします。これにより、ユーザ空間のデーモンはこれを使って、新しいテーブルが最初からしきい値を超えている場合を検出できます。
メタデータデバイスのローウォーターマークは、カーネルで維持され、メタデータデバイスの空き領域がそれより小さくなると dm イベントがトリガーされます。
ディスク上メタデータを更新する
-----------------------------------------------
ディスク上メタデータは、FLUSH あるいは FUA bio が書かれた時に毎回、コミットされます。それらの要求がないときは、毎秒コミットされます。これは、シンプロビジョニングターゲットは、揮発性ライトキャッシュを持つ物理ディスクのように振る舞うことを意味します。電源が失われたら、最近のライトのいくつかは失われることがあります。どのような障害があっても、メタデータは常に矛盾が無いはずです。
データ領域がなくなったら、プールは、I/O を、エラーあるいはキューします。これは設定に従います。
メタデータ領域がなくなった場合、あるいはメタデータ操作が失敗したら、プールは、I/O をエラーにします。これは、プールをオフラインにして、修復を行い、1) 潜在的な矛盾を修正し、2) 修復が必要というフラグをクリアする、までなおりません。
プールのメタデータデバイスが修復されたら、リサイズできます。そして、プールは通常の動作に戻れます。プールが修復が必要とフラグが立ったら、プールのデータもメタデータも、修復がされるまでリサイズできないことに注意下さい。また、プールのメタデータ領域がなくなったら、現在のメタデータトランザクションはアボートされることにも注意下さい。プールは、上の IO 層(例えば、ファイルシステム)に、既に完了を通知した後の IO をキャッシュしていることがあるため、プールの修復が必要となった時は、これらの層において一貫性のチェック(例えば fsck) を行うのを強くお勧めします。
シンプロビジョニング
--------------------------------
i) 新しいシンプロビジョニングボリュームを作る
新しいシンプロビジョニングボリュームを作るには、アクティブなプールデバイス、この例では /dev/mapper/pool に、メッセージを送ります。
dmsetup message /dev/mapper/pool 0 "create_thin 0"
ここで、'0' は、ボリュームの識別子で、24ビットの数字です。これらの識別子を確保、維持するのはコール元の責任です。識別子が既に使われていたら、メッセージは、-EEXIST で失敗します。
ii) シンプロビジョニングボリュームを使う
シンプロビジョニングボリュームは、'thin' ターゲットで活性化します。
dmsetup create thin --table "0 2097152 thin /dev/mapper/pool 0"
最後のパラメタは、デバイスの識別子です。
内部のスナップショット
---------------------------------
i) 内部のスナップショットを作る
スナップショットを作るには、別のメッセージをプールに送ります。
注意。もし、スナップショットを取りたいオリジン(訳注。元の)デバイスがアクティブなら、スナップショットを作る前に、破壊を防ぐためにそれをサスペンドしなくてはいけません。これは今のところ、強制されません。なので、どうぞ注意下さい。
dmsetup suspend /dev/mapper/thin
dmsetup message /dev/mapper/pool 0 "create_snap 1 0"
dmsetup resume /dev/mapper/thin
ここで、'1' は、ボリュームの識別子で、24ビットの数字です。'0' は、オリジンデバイスの識別子です。
ii) 内部のスナップショットを使う
一度作成したら、ユーザはオリジンとスナップショットの関係を何も考える必要はありません。実際、スナップショットは他のシンプロビジョニングデバイスと違うところはなく、それ自身、同じ方法でスナップショットできます。スナップショットの内、一つだけがアクティブなのは完璧に合法ですし、それらを活性化したり削除したりする順序にも制限はありません。(これは以前のデバイスマッパースナップショットと違います。)
他のシンプロビジョニングボリュームと全く同じように、それを活性化して下さい。
dmsetup create snap --table "0 2097152 thin /dev/mapper/pool 1"
外部のスナップショット
---------------------------------
シンプロビジョニングボリュームのオリジンとして、外部のリードオンリーのデバイスを使うこともできます。シンデバイスの、プロビジョンされていない領域へのリードは、オリジンにパススルーされます。ライトは、通常通り、新しいブロックの確保をトリガーします。
この一つのユースケースは、VM ホストです。ゲストをシンプロビジョニングボリュームの上で走らせ、一方でベースイメージを他のデバイスに持ちます。(多分、多くのVM の間で共有します。)
この技術を使う時には、オリジンデバイスに書いてはいけません!もちろん、シンデバイスには書けますし、そのシンボリュームの内部のスナップショットを取ることができます。
i) 外部デバイスのスナップショットを作る
これは、シンデバイスを作るのと同じです。この段階では、オリジンを指定しません。
dmsetup message /dev/mapper/pool 0 "create_thin 0"
ii) 外部デバイスのスナップショットを使う
シンターゲットに、オリジンを指定する追加のパラメタを加えます。
dmsetup create snap --table "0 2097152 thin /dev/mapper/pool 0 /dev/image"
注意。このスナップショットの全ての子孫(内部のスナップショット)は、同じ、追加のオリジンパラメタが必要です。
非活性化
--------------
プールを非活性化する前に、プールを使う全てのデバイスは、非活性化しなくてはいけません。
dmsetup remove thin
dmsetup remove snap
dmsetup remove pool
レファレンス
============
'thin-pool' ターゲット
------------------
i) コンストラクタ
thin-pool <metadata dev> <data dev> <data block size (sectors)> \
<low water mark (blocks)> [<number of feature args> [<arg>]*]
オプショナルの機能の引数:
skip_block_zeroing: 新しくプロビジョンしたブロックをゼロクリアしない。
ignore_discard: discard サポートを無効にする。
no_discard_passdown: discard を、下のデータデバイスに渡さないで、マッピングだけを削除する。
read_only: プールメタデータに、いかなる変更も許さない。
error_if_no_space: 空き領域が無くなった時に、IO をキューしないで、エラーにする。
データブロック長は、64KB (128 sectors) 以上、 1GB (2097152 sectors) 以下でなくてはいけません。
ii) ステータス
<transaction id> <used metadata blocks>/<total metadata blocks>
<used data blocks>/<total data blocks> <held metadata root>
[no_]discard_passdown ro|rw
transaction id:
ユーザ空間が、ボリュームマネージャのメタデータと同期するために使う64ビットの数。
used data blocks / total data blocks
空きブロック数が、プールのローウォーターマーク以下になると、ユーザ空間に dm イベントが送られます。このイベントはエッジトリガーで、それぞれのリジュームの後、一度だけ起きます。このため、ボリュームマネージャを書く人は、そのイベントに登録して、ターゲットの状態を調べるべきです。
held metadata root:
ユーザ空間からリードアクセスをするために、「凍結」されたメタデータルートの位置をブロックで示したもの。'-' は、凍結されたルートが無いことを示す。
discard_passdown|no_discard_passdown
discard が、下のデバイスに実際に渡されるかを示します。テーブルをロードする時にこれが有効であっても、下のデバイスがそれをサポートしない時には無効になることがあります。
ro|rw
プールがある種のデバイス障害になった時、リードオンリーのメタデータモードに落ちます。そこでは、プールメタデータへの変更(新しいブロックを確保するなど)は許されません。
リードオンリーモードでさえも安全でないとみなされる深刻な場合は、I/O は以降、一切許可されず、ステータスは、単に 'Fail' という文字列を含みます。この場合、ユーザ空間の回復ツールを使って下さい。
error_if_no_space|queue_if_no_space
プールの、データあるいはメタデータ領域が無くなったら、プールはデータデバイスへの I/O をキューあるいは、エラーとします。デフォルトは、余分の領域が追加されるかあるいは、'no_space_timeout' が満了するまで I/O をキューします。dm-thin-pool の 'no_space_timeout' モジュールパラメタで、このタイムアウト期間を変えられます。デフォルトは、60秒で、0を指定すると無効にできます。
iii) メッセージ
create_thin <dev id>
新しいシンプロビジョニングデバイスを作る。
<dev id> は、任意のユニークな24ビット識別子で、コール元が選択する。
create_snap <dev id> <origin id>
シンプロビジョニングデバイスの新しいスナップショットを作る。
<dev id> は、任意のユニークな24ビット識別子で、コール元が選択する。
<origin id> は、新しいデバイスがそのスナップショットとなる、シンプロビジョニングデバイスの識別子。
delete <dev id>
シンデバイスを削除する。取り消せません。
set_transaction_id <current id> <new id>
LVM などのユーザ空間のボリュームマネージャは、自分の外部メタデータを、プールターゲットの内部メタデータと同期するための方法が必要です。シンプールターゲットは、任意の64ビットのトランザクション id を格納し、それをターゲットのステータス行に表示する機能を提供します。競合を避けるために、あなたは、自分が現在のトランザクション id であると信じるものを、このコンペアアンドスワップメッセージに与えて変更しなければいけません。
reserve_metadata_snap
ユーザ空間が使うための、データマッピングの btree のコピーを用意します。これによって、ユーザ空間は、このメッセージが実行された時のマッピングを調査できます。メタデータスナップショットに関連するルートブロックを得るには、プールのステータスコマンドを使って下さい。
release_metadata_snap
以前に用意された、データマッピングの btree のコピーを解放します。
'thin' ターゲット
-------------
i) コンストラクタ
thin <pool dev> <dev id> [<external origin dev>]
pool dev:
シンプールデバイス。例えば、/dev/mapper/my_pool あるいは 253:0
dev id:
活性化されるデバイスの内部的なデバイス識別子。
external origin dev:
プールの外にあるオプショナルなブロックデバイスで、リードオンリーのスナップショットオリジンとして使われるもの。シンターゲットで、プロビジョンされていない領域を読むと、このデバイスにマップされます。
プールは、シンデバイスに対して、どのような大きさも格納していません。あなたがシンターゲットをロードする時に、それが以前に使っていたものより小さい場合、終端の先にマップされるブロックにはアクセスできません。あなたが、以前よりも大きいターゲットをロードすると、余分のブロックは、必要に応じてプロビジョンされます。
ii) ステータス
<nr mapped sectors> <highest mapped sector>
プールがデバイス障害にあって失敗した場合、ステータスは、単に 'Fail' という文字列を含みます。この場合、ユーザ空間の回復ツールを使って下さい。
以上