Dockerコンテナのdisk quota設定

# はじめに

[Docker](https://www.docker.com/)とはコンテナ型の仮想化ソフトウェアの一種で、プロセスの集合を「コンテナ」と呼ばれる環境に閉じ込めて実行します。Dockerは、ホストマシン上で動作する各コンテナが使用するハードウェアリソースの量を調整するために、Linuxの[cgroup](https://ja.wikipedia.org/wiki/Cgroups)と呼ばれる仕組みを利用しています。cgroupで調整できるリソースの一覧は[こちら](https://docs.docker.com/engine/reference/run/#runtime-constraints-on-resources)にあります。

リンク先の表からわかるように、cgroupを利用するとCPUやメモリの使用量、ディスクI/Oの速度(MB/s)を調整することができます。しかし、表を良く見ると「ディスクの使用量を制限する」というオプションは提供されていないようです。複数人でディスク領域を共有する基盤を運用する場合、各人が利用できる量を調整したい、という要望は自然です。実際に、Dockerに対して[そのような要望](https://github.com/docker/docker/issues/3804)が上がっていますが、現状はサポートされていないようです。

そこで、今回はLinuxのquotaと呼ばれる仕組みを用いて、各コンテナのストレージ使用量を調整できるか試してみました。方針は以下のとおりです。

1. ホストマシンでquotaを設定する。このとき、各コンテナ内に用意するユーザーID(UID)の範囲をあらかじめ決めておく。たとえば、コンテナAはUIDの範囲が1000-1999、コンテナBはUIDの範囲が2000-2999と決めて、quotaの設定を行う。
2. ホストマシンに用意したファイルシステムを共有するように、各コンテナを立ち上げる。
3. 各コンテナが利用できるストレージ領域が制限されたことを確認する。

それでは、順番にやってみましょう。

# ホストマシンでquotaを設定する。
今回の検証はホストマシンとしてUbuntu Server 14.04を用いました。Dockerのバージョンは1.11.1です。ホストマシンの"/mnt/disk"にマウントされたファイルシステムを各コンテナと共有する、と想定し、このファイルシステムに対してquotaによって使用量の制限をかけます。まずは/etc/fstabを編集し、制限をかけたいファイルシステムに対してusrquotaを追記します。

```shell-session
root@ubuntu:~# grep usrquota /etc/fstab
/root/disk.img /mnt/disk                    ext4    loop,usrquota     0       0
```

制限をかけたいファイルシステムをマウントしなおした後、`quotacheck /mnt/disk`を実行してquotaを有効にします。すると、aquota.userというファイルがマウントポイントに作成されます。

```shell-session
root@ubuntu:~# ls -l /mnt/disk
total 24
-rw------- 1 root root  6144 May 20 16:16 aquota.user
drwx------ 2 root root 16384 May 20 15:58 lost+found
```

コンテナAのUIDの範囲を1000-1999、コンテナBのUIDの範囲を2000-2999のように各コンテナが使うUIDの範囲を定義し、UID=1000とUID=2000のそれぞれのユーザーのquotaを設定します。`edquota 1000`のように、UIDを指定してedquotaコマンドを実行すると、エディタが開くので、それぞれのユーザーに以下のように制限を加えます。以下の例では、それぞれのユーザーが作成できるファイルの数に制限を10個と20個に制限しています。"soft"や"hard"の意味については、他の文献をあたってください。

```vim
Disk quotas for user 1000 (uid 1000):
  Filesystem                   blocks       soft       hard     inodes     soft     hard
  /dev/loop0                        0          0          0          0        5       10
```
```vim
Disk quotas for user 2000 (uid 2000):
  Filesystem                   blocks       soft       hard     inodes     soft     hard
  /dev/loop0                        0          0          0          0       10       20
```

最後に、quotaを有効にするために`quotaon`コマンドを実行します。

```shell-session
root@ubuntu:~# quotaon /mnt/disk
```


# 各コンテナを立ち上げる
次に、コンテナを立ち上げます。このときに、上で準備したファイルシステムを各コンテナが共有するようなオプションを指定します。シェルを二つ立ち上げて、コンテナAとコンテナBを起動し、それぞれUID=1000とUID=2000のユーザーを作成してからそのユーザに`su`します。

```shell-session
root@ubuntu:~# docker run -t -i -v /mnt/disk:/mnt/container --name containerA --rm ubuntu:14.04 /bin/bash
Unable to find image 'ubuntu:14.04' locally
14.04: Pulling from library/ubuntu
6599cadaf950: Pull complete
23eda618d451: Pull complete
f0be3084efe9: Pull complete
52de432f084b: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:15b79a6654811c8d992ebacdfbd5152fcf3d165e374e264076aa435214a947a3
Status: Downloaded newer image for ubuntu:14.04
root@a59d29c3c407:/# useradd -u 1000 testuser
root@a59d29c3c407:/# id testuser
uid=1000(testuser) gid=1000(testuser) groups=1000(testuser)
root@a59d29c3c407:/# su testuser
testuser@a59d29c3c407:/$
```

```shell-session
root@ubuntu:~# docker run -t -i -v /mnt/disk:/mnt/container --name containerB --rm ubuntu:14.04 /bin/bash
root@e627425b3fba:/# useradd -u 2000 testuser
root@e627425b3fba:/# id testuser
uid=2000(testuser) gid=2000(testuser) groups=2000(testuser)
root@e627425b3fba:/# su testuser
testuser@e627425b3fba:/$
```

# 各コンテナが利用できるストレージ領域が制限されたことを確認する
最後に、各コンテナでファイルを作成し、quotaの設定が有効になったことを確認します。

```shell-session
testuser@a59d29c3c407:/$ for i in $(seq 0 10); do
> touch /mnt/container/containerA_file$i
> done
touch: cannot touch '/mnt/container/containerA_file10': Disk quota exceeded
```

```shell-session
testuser@e627425b3fba:/$ for i in $(seq 0 20); do
> touch /mnt/container/containerB_file$i
> done
touch: cannot touch '/mnt/container/containerB_file20': Disk quota exceeded
```

コンテナA、コンテナBでそれぞれ11個目、21個目のファイルの作成に失敗したことが確認できました。

# 終わりに
Linuxのquotaを使って、Dockerコンテナのストレージ使用可能領域に制限を設けることができました。しかし、この方法はコンテナ上で作成するユーザーのUIDを明示的に指定する必要があり、いまいちです。上のリンクで紹介したように、Dockerコミュニティーに対してdisk quotaサポートの要望があがっていますので、もっと簡単な方法が近いうちに出来ることを期待したいですね。