DockerとLinux OSのリソース共有状況の調査

2015年6月9日(火)
森元 敏雄

ディスク容量の制限をテストしてみる

上限を超える大容量ファイルの作成

各ディストリビューションのDocker上にコンテナを起動し、そのコンテナ上で1つのファイルをエラーで停止するまで拡張する実験を行った。実験に使用したテスト用の簡易プログラム(disk_full.c)を、以下に示す。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LOOP_COUNT              10
#define WRITE_BUF_SIZE  1024
#define WRITE_COUNT             4
int main( int argc, char **argv )
{
        FILE    *fptr = NULL;
        int     ret = 0;
        int     cnt = 0;
        int     max_cnt = LOOP_COUNT;
        int     print_term = 1;
        char    write_buf[WRITE_BUF_SIZE];
        if( argc > 1 ){
                        max_cnt = atol( argv[1] );
        }
        if( max_cnt > 10 ){
                print_term = (int)( (double)max_cnt * 0.1 );
        }
        memset( write_buf, '@', WRITE_BUF_SIZE );
        fptr = fopen( "dummy.txt", "w+" );
        if( fptr == NULL ){
                        printf( "fopen error\n" );
                        return( EXIT_FAILURE );
        }
        for( cnt = 1; cnt <= max_cnt; cnt++ ){
                /* 4KBずつ書込み */
                ret = fwrite( write_buf, WRITE_BUF_SIZE, WRITE_COUNT, fptr );
                if( ret < WRITE_COUNT ){
                                printf( "fwrite [%d]回目でエラー rc = [%d]\n", cnt, ret );
                                return( EXIT_FAILURE );
                }
                if( ( cnt % print_term ) == 0 ){
                        printf( "fopen [%d]回目成功\n", cnt );
                }
        }
        fclose( fptr );
        return( EXIT_SUCCESS );
}

表4:コンテナ上で大容量のファイルを作成した結果

ディストリビューションストレージドライバ発生した事象
CentOS 6.6 x86_64
CentOS 7.0 x86_64
device-mapperコンテナに割り当てられた10GBを上限に、ファイルの生成が停止する。ベースOS側でもSparseファイルの実際の使用量が10GB増加する
Ubuntu Server 14.04 x86_64aufsベースOSの限界容量(空き容量+空きSWAPサイズ)までファイルが生成される。ベースOS側もDisk Fullとなり、ファイル書き込み不能となり全体障害状態となる

ストレージドライバとしてdevice-mapperを使用した場合、コンテナに割り当てられる仮想ディスクは1つのデバイスとして提供される。そのため、ディスク容量自体にも上限が存在する。コンテナ内で大容量ファイルを生成した場合でも、その限界を超えることはできない。

ところが、aufsを使用した場合は、ベースOSのファイルシステムのフォルダがそのまま提供されるので、コンテナごとの容量制限は行えない。そのため、ベースOSの空きディスク容量を完全に枯渇させるまでファイルサイズを拡大できてしまう。Disk Quotaでフォルダのサイズ制限を行うか、ストレージドライバをdevice-mapperに変更するといった対策が必要と考えられる。

ベースOSと共用のファイル

Dockerコンテナのファイルの大部分は、前述のコンテナ専用のディスク領域に格納されている。ただしコンテナ内の/etc以下に存在する「hosts」「hostname」「resolf.conf」の3つのファイルは、ベースOS上のファイルが1個ずつマウントされている。それらのファイルが置かれている場所を、表5に示す。

表5:ベースOSとの共用ファイルの格納場所

ディストリビューションストレージドライバ格納フォルダ
CentOS 6.6 x86_64
CentOS 7.0 x86_64
device-mapper/var/lib/docker/containers/[コンテナID]
Ubuntu Server 14.04 x86_64aufs/var/lib/docker/aufs/diff/[コンテナID]-init/etc

これらのファイル自体は単体でマウントされているため、削除もファイル名の変更も行えないが、上書きだけは行える。そのため、これらのファイルに大きなデータを追記すれば、ベースOSの領域を圧迫することも可能である。実際に/etc/hostsのファイルにデータを追記するテストを行った結果は以下となる。

(1)コンテナ内の/etc/hostsを1Gに拡大

# ls -lh /etc/hosts
-rw-r--r-- 1 root root 1.0G Mar 20 06:02 /etc/hosts

(2)ベースOSの"/"の空き容量が1G減少

# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/vg_kernelcentos6-lv_root
                       45G   12G   31G  27% /

(3)コンテナ内の/etc/hostsを元に戻す

(4)ベースOSの"/"の空き容量が元に戻る(1G増える)

# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/vg_kernelcentos6-lv_root
                       45G   11G   32G  25% /

実際にこのような操作がされることはないと考えられるが、マルチテナントでDocker環境を提供するような場合は、これらの共用ファイルは読み取り専用で提供し、管理者が変更を行うなどの運用が必要になると考えられる。

追加検証:device-mapperの仮想ディスクイメージ用のSparseファイルの上限

ストレージドライバにdevice-mapperを選択した場合、標準インストールでは仮想ディスク領域として100GBのSparseファイルが作成される。Sparse(疎)ファイルであるため、lsコマンドで確認するとファイルサイズは100GBと表示されるが、実際に使用されている領域はもっと少ない。下記の例を見ると、ファイルサイズ(data)は100GBと表示されているが、実際のディスク使用量の表示であるtotalには7.6Gと表示されている。

# cd /var/lib/docker/devicemapper/devicemapper/
# ls -lh
total 7.6G
-rw------- 1 root root 100G Apr 15 18:36 data
-rw------- 1 root root 2.0G Apr  9 16:16 metadata

Dockerコンテナは1個あたり10GBの上限が存在するが、コンテナを複数個起動し、合計サイズが100GBを上回った場合の挙動を検証してみた。結果としては、「コンテナ上の領域は空いているように見える(dfコマンドでディスク使用率が100%となっていない)が、書き込みエラーが発生する」状態となった。

Sparseファイル自体は、その割り当てサイズの空き容量が存在しなくても作成できてしまう。今回検証に使用した環境は、KVM上で50GB程度のハードディスク容量を割り当ててベースOSを起動している。その状態でも、100GBの容量のSparseファイルを作成可能である。そして、その環境で検証を行った場合もベースOSのディスク使用率が100%になった時点で、まったく同じ状況が発生した。

追加検証:Sparseファイルの拡張

ベースOSのディスクに空き容量が存在する状態で、Spaceファイルの上限に到達してしまった場合、より大きなサイズのSparseファイルを再作成する必要が発生する。ただしSparseファイルを再作成する際に、中のコンテナの情報を消失してしまう可能性があるため、全コンテナをexportするなどの作業が非常に煩雑である。コンテナの情報を失うことなく拡張するためには、以下の手順で操作すればいい。

(1)dockerのサービスを停止

# /bin/systemctl stop docker.service

(2)念のため、Sparseファイルをバックアップしておく(推奨)

# cp –p /var/lib/docker/devicemapper/devicemapper/data /tmp/data.backup

(3)ddコマンドでSparseファイルのサイズを変更する

# dd if=/dev/zero of=data bs=1G count=0 seek=200

※例では200GBに拡張している。count=0以外だと実際に0x00が書き込まれるので注意が必要。

(4)dockerのサービスを起動

# /bin/systemctl start docker.service

追加検証:検証中のDisk I/O負荷

大容量ファイルを作成する際、ディスクに対してかなりの負荷をかけることとなった。その負荷状況を参考情報として取得した。ディスクI/O速度については、双方の検証環境や検証実行時のコンディションが同一ではないため、この数字での評価は行わない。

(1)CentOS 7.0(device-mapper)

ディスクI/O負荷が急上昇し、ベースOS側も%utilが高まり、スローダウンが発生していると推察される。

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           1.34    0.00   56.86   41.81    0.00    0.00

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
vda               0.00     2.34    1.00  168.23    20.07 79743.14   942.66    74.15  407.64   72.33  409.64   4.81  81.44
dm-0              0.00     0.00    1.34 1626.42     5.35 103824.75   127.57    57.00   35.02  233.00   34.85   0.43  70.74
dm-1              0.00     0.00    1.00 1897.32     4.01 121107.69   127.60  1388.45  776.05  855.33  776.01   0.53 100.03
dm-2              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00

(2)Ubuntu 14.04(aufs)

こちらも同様である。

vg-cpu:  %user   %nice %system %iowait  %steal   %idle
           0.00    0.00    1.57   79.50    0.00   18.93
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
fd0               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
sda               0.00     1.40    0.60   95.00     2.40 47310.40   989.81   129.39 1345.36  134.67 1353.01  10.46 100.00

実際に行ったのは、1つのファイルに連続してデータを書き込むだけの処理である。現状のDockerの機能では、コンテナごとのディスクI/Oの速度の制限は行えない。そのため、1つのコンテナが連続で大量のディスクI/Oを行うと、物理サーバ全体がスローダウンする事態が発生する。

ディスクI/Oについて制限は、CPUやメモリの使用量制限にも利用されているcgroupで設定が可能だ。

cgroupの設定ファイルとパラメータ

ディスクI/Oの制限を行う設定ファイルは、CentOS7の場合、/sys/fs/cgroup/blkio/system.slice/ [デバイス名].scope以下に存在する。デバイス名はブロックデバイス情報を参照するlsblkコマンドで確認できる。

# lsblk -o NAME
loop0
└─docker-253:1-134489996-pool
  ├─docker-253:1-134489996-a29fe7f14c40f295db44e78cf57806a5148971823ebc381dfb1bc0e2076cc
  └─a29fe7f14c40_vol

ディスク書き込み速度の制限を行うblkio.throttle.write_bps_deviceのパラメータで制限を実施してみた。

※その他のパラメータについては下記の表6を参照してほしい。

10MB/secに制限するため、ファイルに "253:1 10485760"を書き込む必要がある。

blkio.throttle.write_bps_deviceデバイスが実行できる書き込み操作数の上限をデータ量で指定する(単位はバイト/秒)

実際に行ったところ、コンテナを1個だけを動作させている状態では制限がかかっており、書き込み速度を10MB/secに制限できることがわかった。(特にマルチテナント環境での)コンテナを複数実行する環境では、コンテナごとのディスクI/O速度の制限が必要になると考えられる。各パラメータの設定や複数コンテナ実行時の挙動などは、今後検証してみたい。 

表6:【参考】cgroupのblkioの設定ファイル名と機能一覧

ファイル名機能
1blkio.io_mergedcgroupにより、I/O操作要求にマージされた、BIOS要求数をレポート
2blkio.io_queuedgrouptにより、I/O操作のキューに入れられた要求の数をレポート
3blkio.io_service_bytesCFQスケジューラーに認識されるように、cgroupにより特定のデバイスとの間で転送されたバイト数をレポート
4blkio.io_service_timeCFQスケジューラーに認識されるように、cgroupにより特定のデバイス上で行われるI/O操作の要求がディスパッチされてから完了するまでの合計時間をレポート
5blkio.io_servicedCFSスケジューラーに認識されるように、cgroupにより特定のデバイス上で実行されたI/O操作の回数をレポート
6blkio.io_wait_timeスケジューラーキュー内のサービスを待つのに費した、cgroupによる 特定のデバイス上のI/O操作の合計時間をレポート
7blkio.reset_statsその他の疑似ファイルに記録されている統計情報をリセットする
8blkio.sectorscgroupにより、特定のデバイスとの間で転送されたセクターの数をレポート
9blkio.throttle.io_service_bytesスロットリングのポリシーに認識されるように、特定のデバイス上でcgroupにより実行されたI/O操作の回数をレポート
10blkio.throttle.io_servicedcgroupにより、特定のデバイスとの間で転送されたバイト数をレポート
11blkio.throttle.read_bps_deviceデバイスが実行できる読み取り操作の上限をデータ量で指定する(単位はバイト/秒)
12blkio.throttle.read_iops_deviceデバイスが実行できる読み取り操作数の上限を毎秒の操作数で指定する
13blkio.throttle.write_bps_deviceデバイスが実行できる書き込み操作の上限をデータ量で指定する(単位はバイト/秒)
14blkio.throttle.write_iops_deviceデバイスが実行できる書き込み操作数の上限を毎秒の操作数で指定する
15blkio.timecgroupが特定のデバイスにI/Oアクセスを行った時間をレポート
16blkio.weightデフォルトでcgroupに提供されるブロックI/Oアクセスの相対的比率(ウェイト)を100から1000の範囲内で指定する
17blkio.weight_devicecgroupに提供される特定のデバイス上のI/Oアクセスの相対的比率(ウェイト)を100から1000の範囲内で指定する

各ファイルの詳細はレッドハットの以下のサイトで公開されている。

第3章 サブシステムと調整可能なパラメーター

https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/6/html/Resource_Management_Guide/ch-Subsystems_and_Tunable_Parameters.html

TIS株式会社

R&D部門である戦略技術センター所属。
金融系の大規模システム開発やプライベートクラウド開発環境の構築・運用の経験を生かし、OSS製品を中心としたの技術調査・検証を担当。
> TIS株式会社

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています