LXD 2.0: リソースの管理 [4/12]

## この文書について

この文書は、連載記事「[LXD 2.0: Blog post series](https://www.stgraber.org/2016/03/11/lxd-2-0-blog-post-series-012/)」（[日本語版目次](http://qiita.com/m-shibata/items/029b19d90af2a79116c2)）の一つである以下の記事を翻訳したものです。

* https://www.stgraber.org/2016/03/26/lxd-2-0-resource-control-412/

この文書のライセンスは原文と同じく、[Creative Commons BY-NC-SA 2.5](http://creativecommons.org/licenses/by-nc-sa/2.5/)のもとに提供されています。

![CC-BY-NC-SA 2.5](http://i.creativecommons.org/l/by-nc-sa/2.5/88x31.png)

---------


## リソース管理でできること

LXDにはいろいろなリソースを管理できます。いくつかはメモリクオータやCPUの制約、I/Oの優先度のようにコンテナ自身に結びついていますすし、いくつかはI/Oの帯域やディスクの使用量のように特定のデバイスに結びついています。

他のLXD設定と同様に、リソース管理はコンテナが動作中であっても動的に適用できます。たとえばメモリの制限を実際に使用中の量よりも小さくしようとした場合などは、とりあえず適用した上で、失敗したことをLXDが報告します。

すべての設定は、プロファイルを継承することで関連するすべてのコンテナに適用できます。つまり、既定のプロファイルに`limits.memory=256MB`と記述した場合、既定のプロファイルを使うコンテナ（大抵はあらゆるコンテナ）はすべてメモリの上限が256MBに設定されます。

コンテナグループで共用するような、リソース制限のプーリングはサポートしていません。単純に既存のカーネルAPIでうまく実装する方法がなかったからです。


### ディスク

ディスクはおそらくもっとも管理したいリソースでしょう。これは単純にコンテナのファイルシステムに容量の制限を設け、コンテナに対してその適用するだけです。

LXDはまさにこの作業を行ってくれます！ 残念ながら、実際にやることはこの説明ほど簡単ではありません。Linuxはパスベースのクオータを行わないため、ほとんどのファイルシステムは代わりにコンテナ上のユーザーやグループのクオータのみを使っています。

つまり、LXDはストレージバックエンドとしてZFSやbtrfsを使っている場合のみ、ディスクのリソース管理をサポートします。この機能自体はLVMにも実装可能だとは思われますが、実際はそこで使用しているファイルシステムに依存し、すべてのファイルシステムがオンラインのサイズ拡大に対応しているわけではなく、多くのファイルシステムはオンラインのサイズ縮小をサポートしていないため、ライブアップデートと組み合わせがトリッキーになります。


### CPU

CPUの制限については、以下の四つの方法をサポートしています：
When it comes to CPU limits, we support 4 different things:

<dl>
<dt>CPUの数を割り当てる</dt>
<dd>このモードの場合、LXDは指定した数のコアを取り出し、他のコンテナのためにロードバランスし、CPUをオンラインないしオフラインにします。そのコンテナは指定した数のCPUのみが見えるようになります。</dd>
<dt>特定のCPUの組み合わせ（1番目や3番目、5番目のコアなど）を割り当てる</dt>
<dd>最初の方法と同じですが、ロードバランスは発生しません。それがビジー状態かどうかに関わらずそのコアが割り当てられます。</dd>
<dt>CPUの作業量の20%を割り当てる</dt>
<dd>この場合、すべてのCPUが表示されるものの、スケジューラーは実行待ちのタスクに対して、CPU時間の割り当てを20%までに制限します。つまりシステムがビジーではなければ、コンテナは十分高速に動作するでしょう。コンテナが次にそのCPUを使うように起動したときに、この制限は適用されます。</dd>
<dt>200ミリ秒ごとに50ミリ秒だけ割り当てる（それ以上は割り当てない）</dt>
<dd>前のモードと似ており、すべてのCPUが表示される状態ではありますが、このモードはたとえシステムがアイドル状態になったとしても、設定した制限以上のCPU時間は使用しません。オーバーコミットのないシステムであれば、このコンテナは正確に一定したCPUのパフォーマンスを利用することを保証します。</dd>
</dl>

最初の2つのどちらかと、後者の2つのどちらかを組み合わせることも可能です。つまり、CPUの組み合わせを指定した上で、どれくらいのCPU時間を使用するかを設定できます。

それに加えて、二つのコンテナが同じリソースを奪い合ったときに、どちらが優先されるかスケジューラーに教えるための、一般的な設定方法も存在します。


### メモリ

メモリはとても簡単です。ただ、RAMのうち何MBを割り当てるか指定するだけです！

実際にやってることも単純です。またホストのメモリの10%のような、割合での指定も可能です。

さらにいくつかの機能もサポートしています。たとえば、コンテナ単位でスワップのオン・オフを選択できますし、オンの時はどのコンテナのメモリを最初にディスクにスワップするかの優先度を設定できるのです。

メモリの制限は最初は「`hard`」に設定されています。つまり、メモリ不足に陥ったら、カーネルのOOM Killerがいろいろとやりだすでしょう。

この設定ポリシーを「`soft`」に変更できます。この場合、もし他に使われていなければ、制限以上のメモリを利用できます。もし他の誰かがそのメモリを使うようになるとすぐに、そのコンテナはメモリの使用量が制限以下になるかホストのメモリが空くまでは、メモリの割り当てがまったくできない状態になるでしょう。


### ネットワークI/O

ネットワークI/Oはおそらくもっとも単純な制限機能ではありますが、その実装はまったく単純ではありません！

LXDでは二つの機能をサポートしています。一つはネットワークインターフェースに対するbit/sでの制限です。入力・出力のそれぞれに対して制限をかけたり、両方に対して最大値（`max`）として制限を設定できます。これは「`bridged`」タイプや「`p2p`」タイプのインターフェースのみサポートしています。

もう一つはネットワークI/Oのグローバルな優先度設定で、指定したインターフェースが集中的に利用されている場合にのみ適用されます。


### ブロックI/O

最後にもっともやっかいなものを。名前だけ見ると単純でユーザー向きの機能のように思えるかもしれませんが、大抵の場合は考えているような動作はしません。

これがサポートする機能は、ネットワークI/Oで説明したそれと基本的に同じです。

ディスクデバイスエントリに対して、直接IOpsやbyte/sの読み書きの制限値を設定したり、ブロックI/Oに対するグローバルな優先度をI/Oスケジューラーに通知できます。

問題はどのタイミングでどのようにこの制限を適用するかということです。残念ながら、内部機能を実装するために完全なブロックデバイスを使用しています。つまりパーティション単位のI/Oの制限はできず、パス単位の制限のみ可能ということになります。

また、RAIDを使っているかどうかに関係なく、一つのパスに複数のブロックデバイスを割り当てられるZFSやbtrfsを使用している時は、与えられたパスにどのブロックデバイスが割り当てられているか効率的に知る方法はありません。

実際、可能性という意味においては、コンテナは同じディスクに対して複数のディスクエントリ（バインドマウントや普通にマウントする形で）を持つ可能性があります。

ここでやっかいなことが起こります。この制限を動作させるために、LXDは与えられたパスに対して裏でどのブロックデバイスが動いているか推定する仕組みが存在します。これは、ZFSやbtrfsのツールに問い合わせを行い、ループバックマウントしているファイルを見つけたらその裏で動いているファイルシステムを再帰的に調べます。

この仕組みは完璧ではないため、通常は制限を適用すべきであろうブロックデバイスをリストアップします。その後、LXDはそれを記録した上で、次のパスに移ります。すべてのパスを見終わったら、ここで奇妙な動作になります。すべての影響するであろうブロックデバイスに対して、設定した制限の平均値が適用されるのです。

つまりコンテナの正しい速度は「平均値」になります。同じ物理ディスクに対して「`/fast/`」と「`/slow`」というディレクトリを設けてそれぞれ異なる速度制限をかけることはできません。そのような設定を行うと、結果的にLXDは二つの値の平均値を両方に適用することになります。


## どのように動作するのか？

これまで説明した制限の多くは、LinuxカーネルのCgroups API経由で適用されます。例外はネットワークの制限で、これは古き良き「`tc`」を使用します。

LXDは起動時に、カーネルのcgroups機能が有効になっているか確認し、カーネルがサポートしている時のみ制限を適用します。一部のcgroups機能が有効になっていない場合、デーモンが警告を表示し、おそらくinitシステムのログに保存されることになるでしょう。

Ubuntu 16.04では、標準ではスワップメモリの計量を除く、すべての機能が有効になっています。スワップメモリの計量機能を使用したい場合は、カーネルの起動オプションに「`swapaccount=1`」を設定してください。


## 制限を適用する

ここまで説明した制限はすべて、コンテナ単位で直接もしくはプロファイル経由で設定します。コンテナ単位で設定したい場合は次のように実行します：

```
lxc config set CONTAINER KEY VALUE
```

プロファイルに適用したい場合は、次のように実行します：

```
lxc profile set PROFILE KEY VALUE
```

デバイス固有の設定は、次のようになります：

```
lxc config device set CONTAINER DEVICE KEY VALUE
```

プロファイルの場合も同様です：

```
lxc profile device set PROFILE DEVICE KEY VALUE
```

利用可能な設定項目やデバイスタイプ、デバイスキーについては[こちら](https://github.com/lxc/lxd/blob/master/doc/configuration.md)を参照してください。


### CPU

コンテナが使用するCPUの数を二つに制限したい場合：

```
lxc config set my-container limits.cpu 2
```

2番目と4番目の特定のCPUに結びつけたい場合：

```
lxc config set my-container limits.cpu 1,3
```

より複雑な範囲設定も可能です：

```
lxc config set my-container limits.cpu 0-3,7-11
```

制限は動的に適用されます。例えば次のようになります：

```
stgraber@dakara:~$ lxc exec zerotier -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1
processor : 2
processor : 3
stgraber@dakara:~$ lxc config set zerotier limits.cpu 2
stgraber@dakara:~$ lxc exec zerotier -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1
```

ユーザー空間での混乱を避けるために、lxcfsが/proc/cpuinfoのエントリを差異がないように調整しています。

LXDの他の部分と同様に、これらの設定はプロファイル経由でも適用できます：

```
stgraber@dakara:~$ lxc exec snappy -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1
processor : 2
processor : 3
stgraber@dakara:~$ lxc profile set default limits.cpu 3
stgraber@dakara:~$ lxc exec snappy -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1
processor : 2
```

コンテナで使用するCPU時間を全体の10%に制限するには、CPUの`allowance`設定を使用します：
To limit the CPU time of a container to 10% of the total, set the CPU allowance:

```
lxc config set my-container limits.cpu.allowance 10%
```

固定的なCPU時間の割合も指定できます：

```
lxc config set my-container limits.cpu.allowance 25ms/200ms
```

最後に、コンテナの優先度をもっとも小さくしています：

```
lxc config set my-container limits.cpu.priority 0
```


### メモリ

メモリの制限は次のように設定できます：

```
lxc config set my-container limits.memory 256MB
```

（単位は`kB`や`MB`、`GB`、`TB`、`PB、`EB`を使用できます）

コンテナのスワップを無効化する場合（初期状態では有効化されています）：

```
lxc config set my-container limits.memory.swap false
```

そのコンテナのメモリをまず最初にスワップするようカーネルに通知する場合：

```
lxc config set my-container limits.memory.swap.priority 0
```

メモリ制限を`hard`から`soft`に変更したい場合は次のように設定してください：

```
lxc config set my-container limits.memory.enforce soft
```

### ディスクとブロックI/O

CPUやメモリと異なり、ディスクやI/Oの制限は特定のデバイスエントリに適用します。オリジナルのデバイスを編集するか、特定のデバイス以外をマスクする必要があります。

ディスクに制限をかける場合（btrfsかZFSが必要です）：

```
lxc config device set my-container root size 20GB
```

たとえば次のようになります：

```
stgraber@dakara:~$ lxc exec zerotier -- df -h /
Filesystem                        Size Used Avail Use% Mounted on
encrypted/lxd/containers/zerotier 179G 542M  178G   1% /
stgraber@dakara:~$ lxc config device set zerotier root size 20GB
stgraber@dakara:~$ lxc exec zerotier -- df -h /
Filesystem                       Size  Used Avail Use% Mounted on
encrypted/lxd/containers/zerotier 20G  542M   20G   3% /
```

速度を制限したい場合は次のように設定します：

```
lxc config device set my-container limits.read 30MB
lxc config device set my-container limits.write 10MB
```

IOps単位で設定する場合：

```
lxc config device set my-container limits.read 20Iops
lxc config device set my-container limits.write 10Iops
```

最後に、オーバーコミットが発生しているビジーなシステムの場合、次のように設定すると良いでしょう：

```
lxc config set my-container limits.disk.priority 10
```

コンテナのI/Oの優先度を最大にしています。


### ネットワークI/O

ネットワークI/Oは、使用できる設定項目は基本的にブロックI/Oと同じです。

たとえば：

```
stgraber@dakara:~$ lxc exec zerotier -- wget http://speedtest.newark.linode.com/100MB-newark.bin -O /dev/null
--2016-03-26 22:17:34-- http://speedtest.newark.linode.com/100MB-newark.bin
Resolving speedtest.newark.linode.com (speedtest.newark.linode.com)... 50.116.57.237, 2600:3c03::4b
Connecting to speedtest.newark.linode.com (speedtest.newark.linode.com)|50.116.57.237|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 104857600 (100M) [application/octet-stream]
Saving to: '/dev/null'

/dev/null 100%[===================>] 100.00M 58.7MB/s in 1.7s 

2016-03-26 22:17:36 (58.7 MB/s) - '/dev/null' saved [104857600/104857600]

stgraber@dakara:~$ lxc profile device set default eth0 limits.ingress 100Mbit
stgraber@dakara:~$ lxc profile device set default eth0 limits.egress 100Mbit
stgraber@dakara:~$ lxc exec zerotier -- wget http://speedtest.newark.linode.com/100MB-newark.bin -O /dev/null
--2016-03-26 22:17:47-- http://speedtest.newark.linode.com/100MB-newark.bin
Resolving speedtest.newark.linode.com (speedtest.newark.linode.com)... 50.116.57.237, 2600:3c03::4b
Connecting to speedtest.newark.linode.com (speedtest.newark.linode.com)|50.116.57.237|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 104857600 (100M) [application/octet-stream]
Saving to: '/dev/null'

/dev/null 100%[===================>] 100.00M 11.4MB/s in 8.8s 

2016-03-26 22:17:56 (11.4 MB/s) - '/dev/null' saved [104857600/104857600]
```

上記では、ギガビットイーサネットをたった100Mbit/sまで制限しています。

ブロックI/Oと同様に、ネットワークの優先度も設定できます：

```
lxc config set my-container limits.network.priority 5
```

## 現在のリソースの使用状況を取得する

[LXD API](https://github.com/lxc/lxd/blob/master/doc/rest-api.md)には、コンテナのリソースの使用状況を取得する方法が記載されています。次の情報を取得できます：

* *メモリ*：現在値、ピーク値、現在のスワップ、ピーク時のスワップ
* *ディスク*：現在のディスク使用量
* *ネットワーク*：すべてのインターフェースに対する受信・送信パケット数

ごく最近のLXD（執筆時点ではgitリポジトリのみに存在します）を使っているのであれば、「`lxc info`」でその情報をすべて取得できます：

```
stgraber@dakara:~$ lxc info zerotier
Name: zerotier
Architecture: x86_64
Created: 2016/02/20 20:01 UTC
Status: Running
Type: persistent
Profiles: default
Pid: 29258
Ips:
 eth0: inet 172.17.0.101
 eth0: inet6 2607:f2c0:f00f:2700:216:3eff:feec:65a8
 eth0: inet6 fe80::216:3eff:feec:65a8
 lo: inet 127.0.0.1
 lo: inet6 ::1
 lxcbr0: inet 10.0.3.1
 lxcbr0: inet6 fe80::f0bd:55ff:feee:97a2
 zt0: inet 29.17.181.59
 zt0: inet6 fd80:56c2:e21c:0:199:9379:e711:b3e1
 zt0: inet6 fe80::79:e7ff:fe0d:5123
Resources:
 Processes: 33
 Disk usage:
  root: 808.07MB
 Memory usage:
  Memory (current): 106.79MB
  Memory (peak): 195.51MB
  Swap (current): 124.00kB
  Swap (peak): 124.00kB
 Network usage:
  lxcbr0:
   Bytes received: 0 bytes
   Bytes sent: 570 bytes
   Packets received: 0
   Packets sent: 0
  zt0:
   Bytes received: 1.10MB
   Bytes sent: 806 bytes
   Packets received: 10957
   Packets sent: 10957
  eth0:
   Bytes received: 99.35MB
   Bytes sent: 5.88MB
   Packets received: 64481
   Packets sent: 64481
  lo:
   Bytes received: 9.57kB
   Bytes sent: 9.57kB
   Packets received: 81
   Packets sent: 81
Snapshots:
 zerotier/blah (taken at 2016/03/08 23:55 UTC) (stateless)
```

## まとめ

LXDチームは、これらのリソース制限のたために、使用する言語を越えて数ヶ月をかけてきました。つまりシンプルに保ちながらも、よりパワフルに、必要な項目を指定できるようになっています。

これらの制限の動的な適用や、プロファイルによる継承は、実行中のサービスに大きな影響を与えることなく、サーバーの動的な管理にとても便利なツールになることでしょう。


## その他の情報

* LXDのウェブサイト：[https://linuxcontainers.org/ja/lxd/](https://linuxcontainers.org/ja/lxd/)
* 開発はGitHub上で行なっています：[https://github.com/lxc/lxd](https://github.com/lxc/lxd)
* メーリングリスト：[https://lists.linuxcontainers.org](https://lists.linuxcontainers.org)
* IRCチャンネル：#lxcontainers on irc.freenode.net

LXDを試してみたいのであれば、[オンラインのデモページ](https://linuxcontainers.org/ja/lxd/try-it/)からLXDとそのガイドツアーを体験できます。