ZFS ストレージを使う¶
ZFS は次世代のファイルシステムです。ボリューム管理、スナップショット、チェックサム処理、圧縮、重複除外(deduplication)、レプリケーションなどの高度なストレージ技術をサポートしています。
ZFS はサン・マイクロシステムズ(現オラクル・コーポレーション)によって開発され、CDDL ライセンスの下でオープンソース化されています。CDDL と GPL 間でライセンスの互換性がないため、ZFS は Linux カーネル・モジュールのメインラインに取り込まれません。しかし、ZFS On Linux (ZoL)プロジェクトにより、外部のカーネル・モジュールとユーザ用のツールを別々にインストールできるように提供されています。
ZFS on Linux (ZoL) への移植は正常かつ成熟しています。しかし ZFS on Linux に対する十分な経験がないのであれば、現時点では zfs
Docker ストレージ・ドライバをプロダクションで使うことを推奨しません。
注釈
Linux プラットフォーム上では、FUSE で ZFS を実装する方法もあります。Docker とも動作するでしょうが、推奨されません。ネイティブな ZFS ドライバ(ZoL)のほうがよりテストされ、性能も良く、より幅広く使われています。このドキュメントでは、ネイティブな ZoL の移植版について言及することにご注意ください。
ZFS でイメージのレイヤ化と共有¶
Docker zfs
ストレージ・ドライバは、3種類の豊富な ZFS データセットを使えます。
- ファイルシステム(filesystems)
- スナップショット(snapshots)
- クローン(clones)
ZFS ファイルシステムはシン・プロビジョニング(訳者注:データ書き込みの領域が、初期環境では薄く構築される)であり、ZFS プール(zpool)から要求処理があるごとに、領域を割り当てます。スナップショットとクローンは、その時々で ZFS ファイルシステムのコピーをするため、領域を効率的に使います。スナップショットは読み込み専用です。クローンは読み書きできます。クローンはスナップショットからのみ作成可能です。以下の図は、これらの関係性を簡単にしたものです。
図の実線はクローン作成手順の流れです。手順1はファイルシステムのスナップショットを作成します。手順2はスナップショットからクローンを作成します。図の点線はクローンとスナップショットの関係であり、スナップショットを経由しています。
Docker ホストで zfs
ストレージ・ドライバを使うと、イメージのベース・レイヤは ZFS ファイルシステムになります。それぞれの子レイヤとは、下にあるレイヤの ZFS スナップショットをベースとした ZFS クローンです。コンテナとは、作成するにあたってイメージの最上位レイヤの ZFS スナップショットをベースとした ZFS クローンです。全ての ZFS データセットは、共通の zpool から領域を取り込みます。以下の図は2つのレイヤ・イメージをベースにまとめたもので、コンテナを実行しています。
以下で説明する手順は、どのようにイメージをレイヤ化し、コンテナを作成するかです。手順は先ほどの図を元にしています。
- イメージのベース・レイヤは Docker ホスト上に ZFS ファイルシステムとして存在しています。
ファイルシステムが要領を使うのは、Docker ホスト上のローカル・ストレージ領域 /var/lib/docker
に zpool を作成するときです。
- 追加のイメージ・レイヤは、直下にあるイメージ・レイヤが保存されているデータセットのクローンにあたります。
図において、「レイヤ1」にはベース・レイヤの ZFS スナップショットによって追加されたものです。そして、スナップショットからクローンを作成します。クローンは書き込み可能であり、必要があれば zpool の容量を使います。スナップショットは読み込み専用であり、ベース・レイヤが変更できない(イミュータブルな)ものとして管理されます。
- コンテナが起動すると、そのイメージ上に読み書き可能なレイヤが追加されます。
先ほどの図では、コンテナの読み書きレイヤは、イメージ(レイヤ1)の最上位レイヤのスナップショットの上に作成されます。そして、スナップショットからクローンが作成されます。
コンテナに対して変更が加えられると、オンデマンドの割り当て(allocate-on-demand)処理によって zpool から領域が割り当てられます。デフォルトでは、ZFS が割り当てる領域は 128KB のブロックです。
子レイヤの作成と、読み込み専用のスナップショットからコンテナを作成する手順において、イメージを不変な(イミュータブルな)オブジェクトとして扱えます。
コンテナを ZFS で読み書き¶
コンテナが zfs
ストレージ・ドライバから読み込むのは、非常にシンプルです。直近で起動したコンテナは、ZFS クローンを元にしています。このクローンは作成時、まず全てのデータセットを共有します。つまり、 zfs
ストレージ・ドライバの読み込み処理が高速なことを意味します。これは、読み込み対象のデータがコンテナ内にコピーされていなくてもです。データブロックの共有は、次のような図になります。
コンテナに対する新しいデータの書き込みは、オンデマンドの割り当て処理によって完了します。コンテナが書き込みのために新しい領域が必要であれば、その都度、新しいブロックが zpool から割り当てられます。つまりコンテナに対する書き込みによって、新しいデータ用の領域が追加される時のみ、容量を消費します。新しい領域は、根底にある zpool からコンテナ(ZFS クローン)に対して割り当てられるものです。
コンテナ内に 存在するデータ に対する更新は、コンテナのクローンに新しいブロックを割り当て、変更したデータを新しいブロックに保管したら、処理が完了となります。オリジナルのデータに対する変更は行われません。元になったイメージのデータセットは、変わらない(イミュータブルな)ままです。つまり、通常の ZFS ファイルシステムに対する書き込みと同じであり、そこにコピー・オンライトの仕組みが実装されています。
Docker で ZFS ストレージ・ドライバを使う設定¶
zfs
ストレージ・ドライバがサポートされるのは、Docker ホスト上で /var/lib/docker
が ZFS ファイルシステムでマウントされている場合のみです。このセクションでは、Ubuntu 14.04 システム上に、ネイティブな ZFS on Linux (ZoL) のインストールと設定方法を紹介します。
動作条件¶
既に Docker ホスト上で Docker デーモンを使っている場合は、イメージを維持する必要がありますので、処理を進める前に、それらのイメージを Docker Hub やプライベート Docker Trusted Registry に push
しておきます。
Docker デーモンを停止します。それから別のブロックデバイス /dev/xvdb
があることを確認します。このデバイス識別子は皆さんの環境によって異なるかもしれません。そのような場合は、以降の処理で適切なものに置き換えてください。
Ubuntu 14.04 LTS に Zfs をインストール¶
- Docker
daemon
を実行中であれば、停止します。
software-properties-common
パッケージをインストールします。
この時 apt-get-repository
コマンドが必要です。
$ sudo apt-get install software-properties-common
Reading package lists... Done
Building dependency tree
<出力を省略>
zfs-native
パッケージ・アーカイブを追加します。
$ sudo add-apt-repository ppa:zfs-native/stable
The native ZFS filesystem for Linux. Install the ubuntu-zfs package.
<出力を省略>
gpg: key F6B0FC61: public key "Launchpad PPA for Native ZFS for Linux" imported
gpg: Total number processed: 1
gpg: imported: 1 (RSA: 1)
OK
- 全ての登録リポジトリとパッケージ・アーカイブから、最新のパッケージ一覧を取得します。
$ sudo apt-get update
Ign http://us-west-2.ec2.archive.ubuntu.com trusty InRelease
Get:1 http://us-west-2.ec2.archive.ubuntu.com trusty-updates InRelease [64.4 kB]
<output truncated>
Fetched 10.3 MB in 4s (2,370 kB/s)
Reading package lists... Done
ubuntu-zfs
パッケージをインストールします。
$ sudo apt-get install -y ubuntu-zfs
Reading package lists... Done
Building dependency tree
<出力を省略>
zfs
モジュールを読み込みます。
$ sudo modprobe zfs
- 正常に読み込まれていることを確認します。
$ lsmod | grep zfs
zfs 2768247 0
zunicode 331170 1 zfs
zcommon 55411 1 zfs
znvpair 89086 2 zfs,zcommon
spl 96378 3 zfs,zcommon,znvpair
zavl 15236 1 zfs
ZFS を Docker に設定¶
ZFS をインストールして読み込むと、Docker で ZFS 設定をする準備が整いました。
- 新しい
zpool
を作成します。
$ sudo zpool create -f zpool-docker /dev/xvdb
このコマンドは zpool
を作成し、そこに「zpool-docker」という名前を割り当てています。この名前は任意です。
zpool
が存在しているかどうか確認します。
$ sudo zfs list
NAME USED AVAIL REFER MOUNTPOINT
zpool-docker 55K 3.84G 19K /zpool-docker
/var/lib/docker
に新しい ZFS ファイルシステムを作成・マウントします。
$ sudo zfs create -o mountpoint=/var/lib/docker zpool-docker/docker
- 直前の手順が正常に行われたか確認します。
$ sudo zfs list -t all
NAME USED AVAIL REFER MOUNTPOINT
zpool-docker 93.5K 3.84G 19K /zpool-docker
zpool-docker/docker 19K 3.84G 19K /var/lib/docker
これで ZFS ファイルシステムが /var/lib/docker
にマウントされました。デーモンは自動的に zfs
ストレージを読み込むでしょう。
- Docker デーモンを起動します。
$ sudo service docker start
docker start/running, process 2315
使用している Linux ディストリビューションによっては、この Docker デーモンの開始手順は少し異なる場合があります。Docker デーモンに対して zfs
ストレージ・ドライバの利用を明示する場合は、 docker daemon
コマンドで --storage-driver=zfs
フラグを使うか、Docker 設定ファイル中の DOCKER_OPTS
行を編集します。
- デーモンが
zfs
ストレージ・ドライバを使っているのを確認します。
$ sudo docker info
Containers: 0
Images: 0
Storage Driver: zfs
Zpool: zpool-docker
Zpool Health: ONLINE
Parent Dataset: zpool-docker/docker
Space Used By Parent: 27648
Space Available: 4128139776
Parent Quota: no
Compression: off
Execution Driver: native-0.2
[...]
先ほどの出力は、Docker デーモンが zfs
ストレージ・ドライバを使っており、親データセットは先ほど作成した zpool-docker/docker
ファイルシステムだと分かります。
これで Docker ホストは、イメージとコンテナの管理・保管に ZFS を使います。
ZFS と Docker 性能¶
Docker で zfs
ストレージ・ドライバを使うにあたり、パフォーマンスに影響を与えるいくつかの要素があります。
- メモリ 。ZFS の性能に、メモリはとても大きな影響があります。そもそもの事実として、本来の ZFS は、大きな Sun Solaris サーバ上で大容量のメモリを使うよう設計されていました。Docker ホストのサイジング時には、この点を忘れないでください。
- ZFS の機能 。ZFS の機能、たとえば重複削除(deduplication)ではメモリで ZFS が使う容量が明らかに増加します。メモリの消費と性能面から、ZFS 重複削除の機能を無効にすることを推奨します。しかし、別のスタック上(SAN や NAS アレイ)のレイヤに対する重複削除は、ZFS のメモリ使用や性能に関する影響が無いので、利用できるでしょう。もし SAN、NAS、その他のハードウェア RAID 技術を使うのであれば、ZFS の利用にあたり、以下にある既知のベストプラクティスをご利用ください。
- ZFS キャッシュ 。ZFS はディスク・ブロックを、アダプティブ・リプレースメント・キャッシュ(ARC; adaptive replacement cache)と呼ばれるメモリ上の構造にキャッシュします。ZFS の Single Copy ARC 機能により、ブロックをコピーした単一キャッシュが、ファイルシステムの複数のクローンから共有されます。つまり、複数の実行チュのコンテナは、キャッシュされたブロックのコピーを共有できるのです。これが意味するのは、ZFS は PaaS や他の高密度の利用例にとっては良い選択肢となるでしょう。
- 断片化 。断片化は ZFS のようなコピー・オン・ライトなファイルシステムにおける、自然な副産物です。ZFS は 128KB のブロックに書き込みますが、slabs(複数の 128KB ブロック)をコピー・オン・ライト処理に割り当てるので、断片化を減らそうとしています。また、 ZFS intent log (ZIL) と書き込みの一体化も断片化を減らすものです。
- ネイティブな Linux 用 ZFS ドライバの使用 。Docker
zfs
ストレージ・ドライバは ZFS FUSE 実装をサポートしているとはいえ、高い性能が必要な場合は推奨されません。ネイティブな Linux 用 ZFS ドライバは、FUSE 実装よりも良い性能でしょう。
以下の一般的な性能に関するベストプラクティスは、ZFS でも適用できます。
- SSD 。ベストな性能のために、SSD(ソリッド・ステート・デバイス)のような高速なストレージ・メディアを使うのは常に良い考えです。十分に利用可能な SSD ストレージ容量があるのなら、ZIL を SSD 上に置くことを推奨します。
- データ・ボリュームの使用 。データ・ボリュームは最上かつ最も予測可能な性能を提供します。これは、ストレージ・ドライバを迂回し、シン・プロビジョニングやコピー・オン・ライト処理を行わないためです。そのため、データ・ボリューム上で重たい書き込みを場合に使うべきでしょう。
参考
- Docker and ZFS in practice
- https://docs.docker.com/engine/userguide/storagedriver/zfs-driver/