LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術

第52回Linuxカーネルのコンテナ機能 ― cgroupを使ったI/O制限

第37回で説明した通り、cgroup v1には様々な問題点が指摘されており、その問題を解決すべくcgroup v2が実装されました。

cgroup v1では、各コントローラがバラバラに実装されており、コントローラ間の連携が取れませんでした。これが原因で、リソースを制限するにあたって一番表面化していた問題が、ディスクI/Oに対して制限をかける際の問題でした。cgroup v1ではblkioコントローラでI/Oに対する制限をかけられましたが、限定的な制限しかかけられませんでした。

LinuxでのI/O

コントローラ間で連携ができないため、blkioコントローラを使ったI/O制限が限定的になってしまう理由を説明するために、Linuxでディスクへファイル入出力する際の仕組みを簡単に説明しておきましょう。もう少し詳しい仕組みが知りたい方は[試して理解]Linuxのしくみなど、関連する書籍や文書をご覧ください。

ご存知の通り、CPUからメモリにアクセスする速度に比べて、ディスクへのアクセスは非常に遅いです。最近はストレージデバイスがSSDなどの高速なデバイスになりましたが、それでもメモリにアクセスする速度に比べると非常に遅いです。

このためLinuxでは、ページキャッシュという仕組みを使い、なるべく高速にファイルへアクセスできるようにしています。キャッシュというとおり、図1のように一度メモリ上にファイルのデータをコピーして、そこからファイルデータへアクセスします。

図1 ページキャッシュを使ったファイルの読み込み
ページキャッシュを使った読み込み

ページキャッシュにデータがある状態で、システム上のプロセスがこのデータにアクセスすると、図2のようにページキャッシュから直接データを読み込み、ディスクへのアクセスは行われません。

図2 ページキャッシュにデータがある状態でのファイルの読み込み
ページキャッシュにデータがある場合の読み込み

プロセスがファイルに書き込んだ場合は、図3のようにページキャッシュにのみデータが書き込まれます。このとき、ディスクとページキャッシュ上のデータが異なるという印がつけられます。

図3 ページキャッシュへの書き込み
ページキャッシュにデータがある場合の読み込み

ページキャッシュからディスクへのデータの書き込みは、書き込んだプロセスとは関係なく行われます。これはカーネルが行う仕事です。

図4 ページキャッシュからディスクへの書き込み
ページキャッシュからディスクへの書き込み

つまり、ファイルを読み込む際はメモリを介して読み込みますし、書き込む際はメモリへの書き込みしか行いません。このため、memoryコントローラーとioコントローラが連携していないときちんとI/O制限ができません。

そもそも、cgroup v1は複数の階層を持てますので、memoryコントローラとblkioコントローラが別々にマウントされ、階層内の構造が異なる可能性があります。あるプロセスが、memoryコントローラとblkioコントローラそれぞれの階層内で、異なる位置にあるcgroupに属している可能性があります。このような場合、技術的に連携ができたとしても、連携ができない状態でcgroupに属している可能性がありますので、連携させることができません。

通常のファイルI/Oは、ここまで説明したようにメモリを介して行います。しかし、プロセス自身でI/Oを制御したい場合があります。このような場合はダイレクトI/Oという仕組みがあり、ページキャッシュを使わずにI/Oが行えます。ダイレクトI/Oの場合は、メモリと関係なくI/Oが行われますので、memoryコントローラとの連携は関係ありません。つまり、blkioコントローラのみでI/Oが制限できます。

以上のように、cgroup v1ではダイレクトI/Oの場合のみI/Oが制限でき、通常のI/Oではきちんと制限できませんでした。

ライトバック処理

図4で説明した、ページキャッシュからディスクへの書き込みについても少し説明しておきます。このようなページキャッシュからディスクへの書き込みをライトバック処理といいます。

図3のように、ページキャッシュにデータが書かれると、ページキャッシュ上のデータとディスク上のデータ間に相違が生じます。つまりページキャッシュ上のデータはディスクに書き戻さなければいけないデータということになります。このような状態をダーティ(dirty)といいます。

ダーティなデータは、図4で説明したようにファイルへの書き込みを行ったプロセスとは関係なく、カーネルが定期的にメモリ上に存在するファイルデータをディスクに書き込みます。

つまり、明示的にcgroupに属するように指定したプロセスとは異なるカーネルのタスクがディスクへ書き込みます。このため、memoryコントローラとioコントローラが連携する必要があるわけです。memoryコントローラ側で、ファイルのキャッシュとして存在するどこのメモリ領域がどのcgroupに属しているかを管理しておく必要があるためです。

このあたりの仕組みについては、カーネル付属のcgroup v2ドキュメント内の"Writeback"というセクションに書かれています。

ページキャッシュからディスクへの書き込みは、次のようなカーネルパラメータに設定されている値を使いながら行われます。設定や参照はsysctlコマンドで行えます。ここでは、今回の説明に関係するパラメータだけを紹介します。

表1 ライトバック処理に関係するカーネルパラメータ(一部)
パラメータ名 役割 単位
vm.dirty_background_ratio ダーティなデータの割合がこの値に達したときにバックグラウンドでディスクへ書き出される。使用可能なメモリに対する割合 %
vm.dirty_background_bytes ダーティなデータがこの値に達したときにバックグラウンドでディスクへ書きだされる バイト
vm.dirty_ratio ダーティなデータの割合がこの値に達したときにプロセス自身がディスクに書きだし始める %
vm.dirty_bytes ダーティなデータがこの値に達したときにプロセス自身がディスクに書きだし始める バイト

_ratioで終わるファイルと_bytesで終わるファイルはどちらか一方しか設定できません。どちらかに値が設定されると、対応するもう1つの設定は"0"に設定され、無効化されます。backgroundのほうは、設定値に達するとカーネルがバックグラウンドで書き込みを始めます。backgroundでないほうは、設定値に達すると書き込みを行っているプロセス自体が書き込みするようになります。

なぜ、このような説明をしたかというと、ディスクI/O処理を行ったプロセスと関係なくディスクへ書き込まれるため、実際に制限が効いていたり、効いていないところを例示することが非常に難しいからです。

このためあとで紹介する実行例では、実際に制限が効いていることがわかりやすいように、vm.dirty_background_ratioに設定する値を調整して実行します。

cgroup v1でblkioコントローラを使ったI/O制限

cgroup v1のblkioコントローラをこの連載で紹介したのは第5回でした。cgroup v2でのI/O制限の動きを見る前に、まずはv1での動きを復習しておきましょう。v1の処理はUbuntu 20.04環境で試しています。

cgroup v1のblkioコントローラ用ファイル

cgroup v1で制限値を設定する場合に使うファイルは、表2のファイルでした。

表2 cgroup v1のblkioコントローラで制限を行う場合に使うファイル
ファイル名 機能
blkio.throttle.read_bps_device デバイスからの読み込みの制限値の設定と確認(バイト/秒)
blkio.throttle.write_bps_device デバイスへの書き込みの制限値の設定と確認(バイト/秒)
blkio.throttle.read_iops_device デバイスからの読み込みの制限値の設定と確認(IOPS)
blkio.throttle.write_iops_device デバイスへの書き込みの制限値の設定と確認(IOPS)

これらのファイルにメジャー番号:マイナー番号 制限値のフォーマットで書き込みます。ファイルの設定値を確認する場合もこのフォーマットで表示されます。

cgroup v1のblkioコントローラを使ったダイレクトI/Oに対する制限

ここでは、test01 というcgroupを作成し、そこに/dev/vdbとして認識されているディスクに制限値を設定します。blkio.throttle.read_bps_deviceblkio.throttle.write_bps_deviceで制限値を設定します。

blkioコントローラに制限値を設定する場合は、デバイスのメジャー番号とマイナー番号が必要ですので、それも調べておきましょう。

$ sudo mkdir /sys/fs/cgroup/blkio/test01
(test01 cgroupの作成)
$ ls -l /dev/vdb
brw-rw---- 1 root disk 252, 16 Feb 23 16:20 /dev/vdb
(/dev/vdbのメジャー番号は252、マイナー番号は16)
$ sudo mount /dev/vdb /mnt
(/mntに/dev/vdbをマウント)

/dev/vdbに1Mバイト/秒の制限値を設定するには、252:16 1048576という文字列をファイルに書き込みます。

$ echo "252:16 1048576" | sudo tee /sys/fs/cgroup/blkio/test01/blkio.throttle.read_bps_device 
(読み込みの制限値の設定)
252:16 1048576
$ echo "252:16 1048576" | sudo tee /sys/fs/cgroup/blkio/test01/blkio.throttle.write_bps_device 
(書き込みの制限値の設定)
252:16 1048576
$ cat /sys/fs/cgroup/blkio/test01/blkio.throttle.{read,write}_bps_device
252:16 1048576
252:16 1048576
(設定できた)
$ echo $$ | sudo tee /sys/fs/cgroup/blkio/test01/cgroup.procs 
(シェルのPIDをtest01に登録)
1024

それでは/dev/vdbに対して書き込んでみましょう。I/Oのテストツールであるfioを使用し、-direct=1でダイレクトI/Oを指定して実行しています。

$ sudo fio -filename=/mnt/testfile -direct=1 -rw=randwrite -bs=1M -size=16M -name=testfile
testfile: (g=0): rw=randwrite, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1
fio-3.16
Starting 1 process  
Jobs: 1 (f=1): [w(1)][94.1%][w=1024KiB/s][w=1 IOPS][eta 00m:01s]
  :(略)
Run status group 0 (all jobs):
  WRITE: bw=1024KiB/s (1048kB/s), 1024KiB/s-1024KiB/s (1048kB/s-1048kB/s), io=16.0MiB (16.8MB), run=16008-16008msec
  :(略)
(設定通り1Mバイト/秒に制限されている)

続いて読み込みも行ってみます。

$ sudo fio -filename=/mnt/testfile -direct=1 -rw=randread -bs=1M -size=16M -name=testfile
testfile: (g=0): rw=randread, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1
fio-3.16
Starting 1 process  
Jobs: 1 (f=1): [r(1)][94.1%][r=1024KiB/s][r=1 IOPS][eta 00m:01s]
  :(略)
Run status group 0 (all jobs):
   READ: bw=1024KiB/s (1048kB/s), 1024KiB/s-1024KiB/s (1048kB/s-1048kB/s), io=16.0MiB (16.8MB), run=16005-16005msec
  :(略)
(設定通り1Mバイト/秒に制限されている)

いずれも1Mバイト/秒(1024KiB/s)での読み書きに制限されていることがわかります。期待通りに制限がかかっていることが確認できました。

cgroup v1のblkioを使った通常のI/Oに対する制限

それでは、cgroup v1のblkioコントローラで、ダイレクトI/Oではない通常のI/Oを行ったときはどうなるかを見てみましょう。fioでは-directオプションを与えなくてもデフォルトでダイレクトI/Oにはなりません。ここではわかりやすいように、明示的に-direct=0とオプション指定しています。

書き込みのとき

まずは書き込みです。

$ sudo fio -filename=/mnt/testfile -direct=0 -rw=write -bs=1M -size=128M -name=testfile
testfile: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1
fio-3.16
Starting 1 process
  :(略)
Run status group 0 (all jobs):
  WRITE: bw=1662MiB/s (1743MB/s), 1662MiB/s-1662MiB/s (1743MB/s-1743MB/s), io=128MiB (134MB), run=77-77msec
  :(略)

図3で説明したように、書き込みは一度メモリ上のページキャッシュに対して行われ、その後図4のように書き込んだプロセスとは別にディスクに書き込まれます。つまり、コマンドの結果は、速度は実際にはディスクに書き込んでいる速度ではありませんので、別に起動したシェルでiostatコマンドを実行し、制限が効いているか確認してみましょう。

$ iostat -p /dev/vdb 1 | grep vdb
  :(略)
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb              48.00         0.00     53280.00         0.00          0      53280          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb              30.00         0.00     34056.00         0.00          0      34056          0
vdb              39.00         0.00     43780.00         0.00          0      43780          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               0.00         0.00         0.00         0.00          0          0          0
  :(略)

上のiostatの出力を見ると、その際の書き込みでは制限がかかっていません。常時ライトバック処理が行われているわけではない様子もうかがえます。

あとで行うcgroup v2での書き込み制限での例と比較できるように、なるべく書き込みが起こった直後にライトバック処理が行われるように設定をして試してみます。

ダーティなデータが使用できるメモリの1%に達したときに、カーネルがライトバック処理を行うように、vm.dirty_background_bytesを1に設定します。このように設定するとかなりダイレクトI/Oに近い状態になりますが、backgroundのほうのパラメータに設定しましたので、書き込みを行ったプロセスとは別にカーネルが書き込むはずです。

$ sudo sysctl -w vm.dirty_background_ratio=1
(vm.dirty_background_ratioを1に設定する)
$ sysctl vm.dirty_background_ratio
vm.dirty_background_ratio = 1
(設定されたことを確認)

別のシェルでiostatコマンドを実行した状態でさきほどと同じコマンドを実行してみましょう。

$ sudo fio -filename=/mnt/testfile -direct=0 -rw=write -bs=1M -size=16M -name=testfile
testfile: (g=0): rw=randwrite, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1
fio-3.16
Starting 1 process  
  :(略)
Run status group 0 (all jobs):
  WRITE: bw=615MiB/s (645MB/s), 615MiB/s-615MiB/s (645MB/s-645MB/s), io=16.0MiB (16.8MB), run=26-26msec
  :(略)

このとき、iostatの出力は次のようになりました。

$ iostat -p /dev/vdb 1 | grep vdb
  :(略)
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb              86.00         0.00    106496.00         0.00          0     106496          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb              21.00         0.00     24584.00         0.00          0      24584          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               1.00         0.00         4.00         0.00          0          4          0
  :(略)

さきほどの例と同じように制限は効いていません。

このように、cgroup v1ではダイレクトI/Oではない場合の書き込みでは制限値を設定しても制限がかかりません。

読み込みのとき

読み込みはどうでしょう? 図1のように、メモリ上にファイルのデータがない状態だと、ディスクから読み込む必要がありますので、読み込みでは制限が効きそうです。試してみましょう。

$ sudo fio -filename=/mnt/testfile -direct=0 -rw=read -bs=1M -size=128M -name=testfile
testfile: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1
fio-3.16
Starting 1 process
  :(略)
Run status group 0 (all jobs):
   READ: bw=1024KiB/s (1048kB/s), 1024KiB/s-1024KiB/s (1048kB/s-1048kB/s), io=128MiB (134MB), run=128022-128022msec
  :(略)

1Mバイト/秒(1024KiB/s)での読み込み制限が効いています。

さきほど行った書き込みのテストの直後に、このように読み込みのテストを行うと、ページキャッシュからデータが読まれて一瞬で終わるはずではないか?と思われた方もいらっしゃるかもしれません。fioのマニュアルによると、テスト実行前にキャッシュをクリアしてからテストを実行しますので、実際にディスクからのファイルが読み込まれます。上記のように、制限が効いていることが確認できました。

ここで、上の読み込みテストを行った直後に、再度fioでキャッシュをクリアしないように指定して同じ処理を実行すると、メモリからデータを読むので制限と関係なく処理が終わるはずです。キャッシュのクリアを無効化するには-invalidate=0を指定します。

$ sudo fio -filename=/mnt/testfile -direct=0 -rw=read -bs=1M -size=128M -name=testfile -invalidate=0
testfile: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1
fio-3.16
  :(略)
Run status group 0 (all jobs):
   READ: bw=7111MiB/s (7457MB/s), 7111MiB/s-7111MiB/s (7457MB/s-7457MB/s), io=128MiB (134MB), run=18-18msec
  :(略)

実行が一瞬で終わり、結果も制限値を大きく上回っています。キャッシュからデータが読み込まれたことがわかります。


以上のように、cgroup v1のblkioコントローラでは、次のような動きになることが確認できました。

  • ダイレクトI/Oに対しては設定どおりにI/O制限がかかる
  • ダイレクトI/Oではない場合、書き込みでは設定通りにI/O制限がかからない

cgroup v2でのioコントローラを使ったI/O制限

それでは、いよいよcgroup v2のioコントローラを紹介していきましょう。ここではUbuntu 22.04の環境で試しています。

cgroup v2のioコントローラ用ファイル

cgroup v2のioコントローラでI/O制限を行うには、表3のようにio.maxファイルを使います。カーネルの設定によっては、保証値を設定するためのio.lowというファイルが出現していることもあります。このファイルはカーネルの設定次第で出現します。Ubuntu 22.04環境ではio.lowファイルはありません。

表3 cgroup v2のioコントローラで制限を行う場合に使うファイル
ファイル名 機能
io.max デバイスごとのI/O制限値の設定と表示

v1では秒あたりのバイト数での制限、IOPSでの制限それぞれの読み書きすべてで別々にファイルが存在していましたが、v2では1つのファイルにすべてを設定します。

書式は、v1と同じくデバイスのメジャー番号とマイナー番号を使います。そして、デバイスを指定したあとに制限値を設定します。その際には表4のキーを使います。また、設定値を確認する際にも同じキーを使って設定値が表示されます。

表4 cgroup v2のioコントローラで制限値を設定する場合に使用するキー
キー名 設定内容
rbps 読み込みの制限値(バイト/秒)
wbps 書き込みの制限値(バイト/秒)
riops 読み込みの制限値(IOPS)
wiops 書き込みの制限値(IOPS)

設定はメジャー番号:マイナー番号 [設定値]...という形でio.maxに書き込みます。設定値は表4のキー名と制限値をイコール=でつなげます。設定値が複数ある場合はスペース区切りで複数指定します。

このように、io.maxは、第49回で紹介したインターフェースファイルのフォーマットのうち、⁠ネストしたキー」のフォーマットを使います。例えば、8:16のデバイスに対して読み込みの制限を2Mバイト/秒に、書き込みの制限を120IOPSに設定するには、次のように実行します。

$ echo "8:16 rbps=2097152 wiops=120" | sudo tee io.max

上記のように設定したあと、設定値を確認するためにファイルを読み取ると次のように表示されます。

8:16 rbps=2097152 wbps=max riops=max wiops=120

設定しなかった項目については、デフォルト値であるmaxが表示されています。デフォルト値については第49回をご覧ください。

cgroup v2のioコントローラを使ったダイレクトI/Oに対する制限

それでは、cgroup v2のioコントローラを使ってI/O制限を試していきましょう。まずは、v1でも制限が効いていたダイレクトI/Oを確認しておきましょう。

ここでもtest01 cgroupを作成します。そして、子cgroupでioコントローラが使えるようになっているかどうかを、root cgroup内に存在するcgroup.subtree_controlファイルで確認します。Ubuntu 22.04環境では、次のようにcgroup.subtee_controlファイルにioコントローラが登録されています。

cgroup.subtree_controlファイルなどの、cgroup v2を操作するファイルに関する詳細については第38回をご覧ください。

$ sudo mkdir /sys/fs/cgroup/test01
(test01 cgroupの作成)
$ cat /sys/fs/cgroup/cgroup.subtree_control 
(子cgroupでioコントローラが使えるようになっていることを確認)
cpuset cpu io memory pids
(ioが含まれているので使えるようになっている)

/dev/vdb/mntにマウントし、そこに対する制限値を1Mバイト/秒に設定します。

$ sudo mount /dev/vdb /mnt
(/dev/vdbを/mntにマウント)
$ ls -l /dev/vdb
brw-rw---- 1 root disk 252, 16 Mar  7 13:33 /dev/vdb
(/dev/vdbのメジャー番号、マイナー番号を確認)
$ echo "252:16 rbps=1048576 wbps=1048576" | sudo tee /sys/fs/cgroup/test01/io.max
(/dev/vdbに対してI/Oの制限値を読み書きともに1MB/secに設定)
252:16 rbps=1048576 wbps=1048576
$ cat /sys/fs/cgroup/test01/io.max 
252:16 rbps=1048576 wbps=1048576 riops=max wiops=max
(読み書きともに1MB/secに設定されている)
$ echo $$ | sudo tee /sys/fs/cgroup/test01/cgroup.procs
(シェルのPIDをcgroupに登録)
1108

それではfioコマンドでダイレクトI/Oを指定して書き込んでみましょう。

$ sudo fio -filename=/mnt/testfile -direct=1 -rw=write -bs=1M -size=128M -name=testfile
(ダイレクトI/Oで書き込み)
testfile: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1
fio-3.28
Starting 1 process
Jobs: 1 (f=1): [W(1)][99.2%][w=1024KiB/s][w=1 IOPS][eta 00m:01s]
  :(略)
Run status group 0 (all jobs):
  WRITE: bw=1024KiB/s (1048kB/s), 1024KiB/s-1024KiB/s (1048kB/s-1048kB/s), io=128MiB (134MB), run=128015-128015msec
  :(略)
(設定通り1Mバイト/秒に制限されている)

次に読み込みです。

$ sudo fio -filename=/mnt/testfile -direct=1 -rw=read -bs=1M -size=128M -name=testfile
(ダイレクトI/Oで読み込み)
testfile: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1
fio-3.28
Starting 1 process
Jobs: 1 (f=1): [R(1)][99.2%][r=1024KiB/s][r=1 IOPS][eta 00m:01s]
  :(略)
Run status group 0 (all jobs):
   READ: bw=1024KiB/s (1049kB/s), 1024KiB/s-1024KiB/s (1049kB/s-1049kB/s), io=128MiB (134MB), run=128004-128004msec
  :(略)
(設定通り1Mバイト/秒に制限されている)

設定通り1Mバイト/秒での読み書きに制限されています。ダイレクトI/Oについては、v1でも問題なく制限がかかっていましたので、v2でも変わりなく制限がかかることが確認できました。

cgroup v2のioコントローラを使った通常のI/Oに対する制限

それでは、いよいよcgroup v2のioコントローラを使った、通常のI/Oに対する制限についてみていきましょう。

先に紹介した、cgroup v2のダイレクトI/Oに対する実行例と同様にcgroup、マウント、制限値を設定します。

$ sudo mkdir /sys/fs/cgroup/test01
(test01 cgroupの作成)
$ cat /sys/fs/cgroup/cgroup.subtree_control 
(子cgroupでioコントローラが使えるようになっていることを確認)
cpuset cpu io memory pids
$ mount /dev/vdb /mnt
(/dev/vdbを/mntにマウント)
$ echo "252:16 rbps=1048576 wbps=1048576" | sudo tee /sys/fs/cgroup/test01/io.max
(/dev/vdbに対してI/Oの制限値を読み書きともに1MB/secに設定)
252:16 rbps=1048576 wbps=1048576
$ cat /sys/fs/cgroup/test01/io.max
252:16 rbps=1048576 wbps=1048576 riops=max wiops=max
(読み書きともに1MB/secに設定されている)

次に、⁠cgroup v1のblkioを使った通常のI/Oに対する制限」で試したように、ダーティなデータが使用できるメモリの1%に達したときに、カーネルがライトバック処理を行うように設定します。

$ sudo sysctl -w vm.dirty_background_ratio=1
(vm.dirty_background_ratioを1に設定する)
$ sysctl vm.dirty_background_ratio
vm.dirty_background_ratio = 1
(設定されたことを確認)

それでは実際にディスクへの書き込みを行ってみます。cgroup v1のところで試した際と同じようにfioコマンドを使用しています。

$ sudo fio -filename=/mnt/testfile -direct=0 -rw=randwrite -bs=1M -size=256M -name=testfile
testfile: (g=0): rw=randwrite, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1
fio-3.28
Starting 1 process  
Jobs: 1 (f=1): [w(1)][100.0%][w=1025KiB/s][w=1 IOPS][eta 00m:00s]
  :(略)
Run status group 0 (all jobs):
  WRITE: bw=3413MiB/s (3579MB/s), 3413MiB/s-3413MiB/s (3579MB/s-3579MB/s), io=256MiB (268MB), run=75-75msec

Disk stats (read/write):
  vdb: ios=0/0, merge=0/0, ticks=0/0, in_queue=0, util=0.00%

-direct=0でダイレクトI/Oを無効にしていますので、fioコマンドはすぐに実行が終了します。

ここであらかじめ別に起動したシェルでiostatコマンドを実行しておくと、次のようにバックグラウンドでゆっくり書き込まれていることがわかります。

$ iostat -p /dev/vdb 1 | grep 'vdb '
  :(略)
vdb               1.00         0.00      1024.00         0.00          0       1024          0
vdb               1.00         0.00      1024.00         0.00          0       1024          0
vdb               1.00         0.00      1024.00         0.00          0       1024          0
vdb               1.00         0.00        40.00         0.00          0         40          0
vdb               1.00         0.00         4.00         0.00          0          4          0
vdb               3.00         0.00      3072.00         0.00          0       3072          0
vdb               1.00         0.00      1024.00         0.00          0       1024          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               2.00         0.00      2048.00         0.00          0       2048          0
vdb               5.00         0.00      1052.00         0.00          0       1052          0
vdb               3.00         0.00      1040.00         0.00          0       1040          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               0.00         0.00         0.00         0.00          0          0          0
vdb               3.00         0.00      3072.00         0.00          0       3072          0
vdb               1.00         0.00      1024.00         0.00          0       1024          0
vdb               1.00         0.00      1024.00         0.00          0       1024          0
vdb               1.00         0.00      1024.00         0.00          0       1024          0
  :(略)

ところどころ制限値を超えたりはしていますが、1024.00という値が表示されており、設定通りに制限が効いている様子が観察できました。

読み込み時も確認しておきましょう。

$ sudo fio -filename=/mnt/testfile -direct=0 -rw=read -bs=1M -size=16M -name=testfile
testfile: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1   
fio-3.28
Starting 1 process  
Jobs: 1 (f=1): [R(1)][94.1%][r=1025KiB/s][r=1 IOPS][eta 00m:01s]
  :(略)
Run status group 0 (all jobs):
   READ: bw=1023KiB/s (1048kB/s), 1023KiB/s-1023KiB/s (1048kB/s-1048kB/s), io=16.0MiB (16.8MB), run=16010-16010msec

Disk stats (read/write):
  vdb: ios=39/0, merge=0/0, ticks=31/0, in_queue=31, util=0.95%
(設定通りおよそ1Mバイト/秒に制限されている)

読み込みについては、cgroup v1のところで試した際と同様で、実際にディスクからデータが読み込まれますので、設定通りに制限が効いています。


以上のように、cgroup v2では、ダイレクトI/O、通常のI/Oともに、設定通りに制限がかかることが確認できました。

まとめ

今回は、実行例を紹介している部分が長く、機能の説明については少しだけになってしまいましたが、cgroup v2一番の目玉となる改良点を紹介しました。

最初に説明したとおり、LinuxではメモリとI/Oは密接に関係しており、メモリとストレージとのやりとりになるため、コントローラが連携して動けないcgroup v1では制限がうまくかからないケースがありました。

今回紹介したとおり、cgroup v2では、memoryコントローラとioコントローラが連携して、I/Oに対する制限がかかるようになりました。これは、cgroup v2では階層が1つになり、プロセスはmemoryコントローラから見ても、ioコントローラから見ても同じcgroupに属しているためです。このため、ライトバック処理に関わるmemoryコントローラとioコントローラの連携機能が実装できるようになりました。

参考文献

おすすめ記事

記事・ニュース一覧