CentOS 7の仮想化、Docker、リソース管理(後編)

2015年1月30日(金)
古賀 政純

巷で話題のDockerとは?

最近、巷で注目を浴びているオープンソースの一つに「Docker」(ドッカー)があります。Dockerは、コンテナと呼ばれる隔離空間を管理するためのツールです。CentOS 7は、この「コンテナ」と呼ばれるアプリケーションとOSをパッケージ化した実行環境をサポートしています。一般的に、Linux OS上での隔離空間を「Linuxコンテナ」と呼びます。Linuxコンテナは、KVMやXenなどのハイパーバイザー型の仮想化技術に比べて、CPU、メモリ、ストレージ、ネットワーク等のハードウェア資源の消費やオーバーヘッドが小さいとう利点が挙げられます。Dockerは、Linuxコンテナの機能に加えて、API、イメージフォーマット、環境を配布する仕組みを持っています。しかし、このDockerが注目される理由は、ハードウェア資源の消費のオーバーヘッドが小さいだけではありません。Dockerが注目される背景には、アプリケーション開発者が最も嫌うであろう「ハードウェアの調達やメンテナンス」の隠蔽が挙げられます。ハードウェアに関する部分は、アプリケーション開発者にとってあまり重要ではありません。従来のKVM等による仮想化は、アプリケーション開発者にとってのメリットというよりもむしろ、システム管理者にとって大きなメリットがありました。一方、Dockerのような軽量なコンテナ技術の発達により、アプリケーション単位での隔離、開発環境の容易な作成と廃棄ができるようになり、アプリケーションのメンテナンスの簡素化がより一層進むことになります。これは、システム管理者よりもアプリケーション開発者にとってのメリットが非常に大きいことを意味します。また、Dockerの仕組みによって、アプリケーションの開発と実環境への素早い展開と運用の両立が見えてきました。これは、近年、開発者やIT部門の間で話題になっている「DevOps環境」の実現に他なりません。アプリケーション開発者やIT部門にとっては、アプリケーションの開発、運用、廃棄等を、ハードウェア資源を意識しない「雲」の上で迅速に行える環境がDockerを使って整備されつつあります。

物理基盤、仮想化基盤、PaaS基盤の比較。PaaS基盤では、DevOpsのようなアプリケーション中心の考えで隔離空間毎に異なるOSバージョンとサービスを提供できる

図6:物理基盤、仮想化基盤、PaaS基盤の比較。PaaS基盤では、DevOpsのようなアプリケーション中心の考えで隔離空間毎に異なるOSバージョンとサービスを提供できる

CentOS 7でdockerを使う

以下では、CentOS 7で採用されているdockerのインストールと基本的な利用方法について説明します。CentOS 7では、dockerパッケージが標準で含まれています。yumコマンドでインストールします。

# yum install -y docker
# rpm -qa |grep docker
docker-1.3.2-4.el7.centos.x86_64

docker pull centosでイメージをインターネット経由で入手しますが、もし企業内においてプロキシーサーバー経由でインターネットに接続する環境では、以下のように、/etc/sysconfig/dockerファイルにプロキシーサーバーを指定し、dockerサービスを再起動してください。

# vi /etc/sysconfig/docker
…
http_proxy=http://proxy.jpn.hp.com:8080
https_proxy=http://proxy.jpn.hp.com:8080
…

# systemctl restart docker
# docker pull centos

dockerサービスを起動します。

# systemctl start docker
# systemctl enable docker
ln -s '/usr/lib/systemd/system/docker.service' '/etc/systemd/system/multi-user.target.wants/docker.service'

これでDockerのインストールは完了です。Dockerでは、様々なOSとアプリケーションがパッケージ化された環境がDockerイメージとしてDockerリポジトリに用意されています。イメージファイルを一から作成することも可能ですが、すでにDockerリポジトリに用意されているイメージファイルを利用することができますので、コンテナへのOSやアプリケーションのインストール等の煩雑な作業をスキップすることができます。以下では、Dockerリポジトリに用意されているイメージファイルをDocker上で起動させてみます。
イメージファイルをDockerリポジトリから入手する前にSELinuxをdisableにしておきます。

# setenforce 0
# getenforce
Permissive

イメージファイルをインターネット経由でDockerリポジトリから入手します。

# docker pull centos:centos5
Pulling repository centos
bac0c97c3010: Download complete
511136ea3c5a: Download complete
5b12ef8fd570: Download complete
Status: Downloaded newer image for centos:centos5

# docker pull centos:centos6
Pulling repository centos
510cf09a7986: Download complete
511136ea3c5a: Download complete
5b12ef8fd570: Download complete
Status: Downloaded newer image for centos:centos6

# docker pull centos:centos7
Pulling repository centos
8efe422e6104: Download complete
511136ea3c5a: Download complete
5b12ef8fd570: Download complete
Status: Downloaded newer image for centos:centos7

Dockerリポジトリから入手し、ローカルのCentOS 7サーバー上に保管されているイメージファイル一覧を確認します。

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              5                   bac0c97c3010        7 days ago          466.9 MB
centos              centos5             bac0c97c3010        7 days ago          466.9 MB
centos              6                   510cf09a7986        7 days ago          215.8 MB
centos              centos6             510cf09a7986        7 days ago          215.8 MB
centos              latest              8efe422e6104        7 days ago          224 MB
centos              7                   8efe422e6104        7 days ago          224 MB
centos              centos7             8efe422e6104        7 days ago          224 MB

上記のイメージファイル群のうち、centos6という名前の付いたタグの付いたイメージファイルから、コンテナを生成、起動し、そのコンテナ内で作業できるようにしてみましょう。Dockerのイメージファイルからコンテナを起動するには、docker runを実行します。--nameオプションに、コンテナの名前を指定します。今回は、test01という名前のコンテナを起動するように指定しました。-iオプションは、コンテナの標準入力を開いた状態にします。-tオプションを付与することにより、Dockerは、仮想端末を割り当てて、コンテナの標準入力にアタッチします。centos:centos6は、入手したイメージファイルのタグ付きのリポジトリ名です。最後に、/bin/bashを指定することで、コンテナ上のシェルを起動します。

# docker run --name test01 -i -t centos:centos6 /bin/bash
[root@d962dc3be270 /]#

コンテナにログインし、作業してみます。コンテナのOSのバージョンを確認します。

[root@d962dc3be270 /]# cat /etc/redhat-release
CentOS release 6.6 (Final)

すると、CentOS 6.6であることがわかります。ホスト名を確認してみます。

[root@d962dc3be270 /]# hostname
d962dc3be270

ホスト名は自動的に「d962dc3be270」という名前で割り振られていることがわかります。さらにIPアドレスを確認し、ホストOSや外部と通信できるかを確認します。dockerでは、標準で、インタフェースdocker0を割り当てます。

[root@d962dc3be270 /]# ifconfig eth0 |grep inet
          inet addr:172.17.0.5  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:5/64 Scope:Link
[root@d962dc3be270 /]# ping -c 3 172.16.1.1
PING 172.16.1.1 (172.16.1.1) 56(84) bytes of data.
64 bytes from 172.16.1.1: icmp_seq=1 ttl=63 time=0.690 ms
64 bytes from 172.16.1.1: icmp_seq=2 ttl=63 time=0.281 ms
64 bytes from 172.16.1.1: icmp_seq=3 ttl=63 time=0.239 ms

--- 172.16.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2373ms
rtt min/avg/max/mdev = 0.239/0.403/0.690/0.204 ms
[root@d962dc3be270 /]# nslookup www.hp.com<http://www.hp.com>

dockerのイメージファイルの仕組みを理解するため、まず、試しいdockerコンテナー上の/rootディレクトリにファイルtestfileを作成しておきます。

[root@d962dc3be270 /]# touch /root/testfile
[root@d962dc3be270 /]# ls /root/
testfile

dockerコンテナのOS環境から離脱します。

[root@d962dc3be270 /]# exit
exit
#

ホストOS上で、過去に起動したコンテナ一覧を確認します。

# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                          PORTS               NAMES
d962dc3be270        centos:6            "/bin/bash"         9 minutes ago       Exited (0) About a minute ago                       test01

先程作業したコンテナを再利用できるように、コンテナのイメージ化を行います。コンテナをイメージ化するには、コンテナをコミットします。先程作業したコンテナのコンテナIDとイメージを指定してコミットを行います。

# docker commit d962dc3be270 centos:centos6
5468f0e1072fbd5cdf0042efc76e3d0d8c778f235faf212bcbf9b2105ebdf30f

再び、現在のイメージファイルの一覧を確認します。

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              centos6             5468f0e1072f        24 seconds ago      215.8 MB
centos              centos5             bac0c97c3010        7 days ago          466.9 MB
centos              5                   bac0c97c3010        7 days ago          466.9 MB
centos              6                   510cf09a7986        7 days ago          215.8 MB
centos              centos7             8efe422e6104        7 days ago          224 MB
centos              latest              8efe422e6104        7 days ago          224 MB
centos              7                   8efe422e6104        7 days ago          224 MB

先程コミットしたイメージファイル「centos:centos6」を使って、別のコンテナtest02を生成してみます。

# docker run --name test02 -i -t centos:centos6 /bin/bash
[root@5b2ac9622d6f /]#

コンテナtest02上の/rootを確認してみます。

[root@5b2ac9622d6f /]# ls /root/
testfile
[root@5b2ac9622d6f /]#

コンテナtest02で、/root/testfile2を作成します。

[root@5b2ac9622d6f /]# touch /root/testfile2
[root@5b2ac9622d6f /]# exit
exit
# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                          PORTS               NAMES
5b2ac9622d6f        centos:centos6      "/bin/bash"         2 minutes ago       Exited (0) About a minute ago                       test02
d962dc3be270        centos:6            "/bin/bash"         20 minutes ago      Exited (0) 12 minutes ago                           test01

# docker commit 5b2ac9622d6f centos:testfile2
4eac0fb23ec1d4a212b4aa289637f43da3049dd975d48a91fd9890ca6a368cdd
# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              testfile2           4eac0fb23ec1        8 seconds ago       215.8 MB
centos              centos6             5468f0e1072f        7 minutes ago       215.8 MB
centos              5                   bac0c97c3010        7 days ago          466.9 MB
centos              centos5             bac0c97c3010        7 days ago          466.9 MB
centos              6                   510cf09a7986        7 days ago          215.8 MB
centos              centos7             8efe422e6104        7 days ago          224 MB
centos              latest              8efe422e6104        7 days ago          224 MB
centos              7                   8efe422e6104        7 days ago          224 MB

上記より、作業を行ったコンテナをコミットすることでその作業内容を反映したイメージファイルが生成でき、さらに作成したイメージファイルを再利用して、新たなコンテナを生成できていることがわかります。開発者は、アプリケーション毎に異なるコンテナを複数作成し、コミットしておけば、そのアプリケーションに特化したイメージファイルを持つことができ、すぐにアプリケーションが実行可能な環境をコンテナで利用することができます。

ホストマシンに保管されたDockerイメージからdocker runでコンテナを起動する。起動したコンテナで作業を施し、アプリケーション等の構築をしたものをdocker commitでDockerイメージとして登録する

図7:ホストマシンに保管されたDockerイメージからdocker runでコンテナを起動する。起動したコンテナで作業を施し、アプリケーション等の構築をしたものをdocker commitでDockerイメージとして登録する

コンテナに含まれるファイルの確認方法

現在起動しているDockerコンテナのファイルシステム上にあるファイル等をホストマシンから確認したい場合があります。起動中のコンテナのファイルシステムは、/var/lib/docker/devicemapper/metadataディレクトリ配下にあるコンテナID の名前が付いたメタデータの情報を元に、Device Mapperを使ってアクセスできます。以下は、CentOS 6.6のDockerイメージからコンテナを起動し、そのコンテナのファイルシステムに存在する/etc/redhat-releaseファイルに記載されたCentOSのバージョンをホストマシンから確認する手順です。まず、Dockerに現在登録されているイメージファイルを確認します。今回、CentOS 6.6のDockerイメージ「centos:centos6.6」は、事前にCentOS 6のDockerイメージを入手し、yum updateによりパッケージを最新に更新することにより作成しました。

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              centos6.6           6e80aa2dad3a        23 seconds ago      215.8 MB
centos              testfile2           4eac0fb23ec1        4 minutes ago       215.8 MB
centos              centos6             5468f0e1072f        12 minutes ago      215.8 MB
centos              5                   bac0c97c3010        7 days ago          466.9 MB
centos              centos5             bac0c97c3010        7 days ago          466.9 MB
centos              6                   510cf09a7986        7 days ago          215.8 MB
centos              centos7             8efe422e6104        7 days ago          224 MB
centos              7                   8efe422e6104        7 days ago          224 MB
centos              latest              8efe422e6104        7 days ago          224 MB

上記のDockerイメージ「centos:centos6.6」から、コンテナを起動します。コンテナ名はtest001としました。

# docker run --name test0001 -i -t centos:centos6.6 /bin/bash
[root@098686019f0d /]#

ホストマシンの別の端末で、起動中のコンテナを確認します。

# docker ps -a
CONTAINER ID    IMAGE           COMMAND         CREATED         STATUS  PORTS           NAMES
098686019f0d    centos:centos6.6        "/bin/bash"     12 minutes ago  Up      12 minutes      test001

以下は、起動中のコンテナ「098686019f0d」のファイルシステムをホストマシンから確認する手順です。まず、コンテナ「098686019f0d」のメタデータから、コンテナのデバイスIDとサイズを確認します。

# cd /var/lib/docker/devicemapper/metadata
# cat 098686019f0dec240ec5b0473046b78b5e56cb7dd0651e821d9e9216cfa9e37e |python -mjson.tool
{
    "device_id": 6,
    "initialized": false,
    "size": 10737418240,
    "transaction_id": 121
}

上記より、このコンテナのデバイスIDは6、サイズが、10737418240であることがわかります。次に、起動しているコンテナがマウントされているループバックデバイスを表示します。

# ls -l /dev/mapper/
合計 0
crw-------. 1 root root 10, 236 12月 17 17:46 control
lrwxrwxrwx. 1 root root       7  1月 13 11:23 docker-8:3-84171797-098686019f0dec240ec5b0473046b78b5e56cb7dd0651e821d9e9216cfa9e37e -> ../dm-3
lrwxrwxrwx. 1 root root       7  1月 13 08:33 docker-8:3-84171797-pool -> ../dm-1
lrwxrwxrwx. 1 root root       7  1月 13 05:28 encrypted001 -> ../dm-2
lrwxrwxrwx. 1 root root       7  1月 13 05:28 pool001-lvol001 -> ../dm-0

上記のdocker-8:3-84171797-poolと先述のデバイスID、サイズを使って、テスト用のボリュームを作成します。具体的には、コンテナのサイズ「10737418240」と、デバイスIDの「6」をdmsetupコマンドの--tableオプションで指定します。

# dmsetup create testvol01 --table "0 $((10737418240 / 512)) thin /dev/mapper/docker-8:3-84171797-pool 6"

テスト用のボリュームtestvol01は/dev/mapperディレクトリ以下に作成されていますので、これをマウントします。

# mount /dev/mapper/testvol01 /mnt

/mntにマウントしたコンテナのディレクトリツリーと/etc/redhat-releaseファイルを確認します。

# ls -la /mnt/rootfs/
合計 176
drwxr-xr-x. 21 root root  4096  1月 13 08:07 .
drwxr-xr-x.  4 root root  4096  9月  9 06:30 ..
dr-xr-xr-x.  2 root root  4096  1月  2 23:23 bin
drwxr-xr-x.  4 root root 90112  1月  2 23:23 dev
drwxr-xr-x. 45 root root  4096  1月  2 23:23 etc
drwxr-xr-x.  2 root root  4096  9月 23  2011 home
dr-xr-xr-x.  7 root root  4096  1月  2 23:23 lib
dr-xr-xr-x.  6 root root 12288  1月  2 23:23 lib64
drwx------.  2 root root  4096  1月  2 23:21 lost+found
drwxr-xr-x.  2 root root  4096  9月 23  2011 media
drwxr-xr-x.  2 root root  4096  9月 23  2011 mnt
drwxr-xr-x.  2 root root  4096  9月 23  2011 opt
drwxr-xr-x.  2 root root  4096  1月  2 23:21 proc
dr-xr-x---.  2 root root  4096  1月  2 23:23 root
dr-xr-xr-x.  2 root root  4096  1月  2 23:23 sbin
drwxr-xr-x.  3 root root  4096  1月  2 23:23 selinux
drwxr-xr-x.  2 root root  4096  9月 23  2011 srv
drwxr-xr-x.  2 root root  4096  1月  2 23:21 sys
drwxrwxrwt.  2 root root  4096  1月  2 23:23 tmp
drwxr-xr-x. 13 root root  4096  1月  2 23:21 usr
drwxr-xr-x. 17 root root  4096  1月  2 23:21 var

# cat /mnt/rootfs/etc/redhat-release
CentOS release 6.6 (Final)

コンテナが持つファイルシステムを確認し終えたら、マウントを解除し、テスト用のボリュームは削除しておきます。

# cd
# umount /mnt
# dmsetup remove /dev/mapper/testvol01

【注意】

dmsetupコマンドにより、コンテナのファイルシステムを確認するためのテスト用のボリュームを作成する際に、デバイスIDを間違えると、別のコンテナのファイルシステムの中身を見ることになりますので十分注意して下さい。

Dockerfileを使ったイメージファイルの構築

Dockerイメージの作成や、イメージファイルからコンテナを起動する一連の手順は、先述のdockerコマンドでコマンドラインから入力することで可能ですが、イメージ作成作業、アプリケーションのインストール等の複数の作業をまとめて行いたい場合があります。そのような場合は、Dockerfileを使うと便利です。Dockerファイルは、開発環境におけるMakefileのように、一連の作業をDockerで定義された書式に従って事前に記述し、それに基づいてイメージの作成を行います。以下では、CentOS 7で稼働するApache Webサーバーが起動するコンテナのイメージファイルの作成、コンテナの起動、コンテナへのアクセス方法を述べます。まず、Apacheが稼働するコンテナ用のDockerfileを用意します。今回は、/root/apacheディレクトリを作成し、その下にDockerfileを作成します。

# mkdir /root/apache
# cd /root/apache
# vi Dockerfile
FROM            centos:centos7  ←使用するDockerイメージ名を指定
MAINTAINER      Masazumi Koga
ENV             container       docker
RUN             yum swap -y     fakesystemd systemd ←systemdをDockerイメージにインストール
RUN             yum install -y  initscripts
RUN             yum install -y  httpd   ←Apache WebサーバーのhttpdパッケージをDockerイメージにインストール
RUN             echo "Hello Apache." > /var/www/html/index.html ←テスト用のHTMLファイルをDockerイメージに配置
RUN             systemctl enable httpd ←コンテナ起動時にhttpdサービスが起動するように設定
EXPOSE          80 ←Dockerコンテナが外部に開放するポート番号を指定

Dockerfile内では、FROM行に利用するイメージの種類を記述します。イメージ名はコマンドラインから「docker images」で確認可能ですので、一覧に表示されているイメージ名を記述します。ここではCentOS 7のイメージ「ceentos:centos7」を指定しています。RUN行では、Dockerのイメージを作成する際に実行したいコマンドを記述します。今回、RUN行に、「yum swap -y fakesystemd systemd」が指定されています。Dockerリポジトリに標準で用意されているCentOS 7のDockerイメージは、デフォルトでsystemdを利用しないものが用意されています。しかし、多くのCentOS 7向けのアプリケーションやサービスがsystemdを使用するため、Dockerイメージにsystemdをインストールしたい場合があります。今回は、yumコマンドで、fakesystemdを削除し、systemdをインストールしています。さらに、RUN行の「yum install -y httpd」は、Apache Webサーバーのhttpdパッケージをインストールする記述です。今回使用するDockerイメージは、CentOS 7のsystemdを有効にしますので、RUN行で「systemctl enable httpd」を記述し、コンテナが起動した際に、自動的にApache Webサーバーが起動するようにします。Apache Webサーバーのコンテナが外部に開放するポート番号をEXPOSE行で指定します。Dockerfileを記述したら、Dockerイメージを生成します。

# pwd
/root/apache

# ls -l
合計 4
-rw-r--r--. 1 root root 263 11月  9 17:09 Dockerfile


# docker build -t centos7_apache .

Apache Webサーバー入りのDockerイメージ「centos7_apache」が作成されているかを確認します。

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
centos7_apache      latest              ac8e9d882d7e        About a minute ago   381.9 MB
...
...

作成したDockerイメージ「centos7_apache」を使ってDockerコンテナを起動します。今回起動するコンテナ名は、「apache001」としました。

# docker run --name apache001 --privileged -i -t -d -p 80:80 centos7_apache /sbin/init
3db4623293db8c6a2e516ac788bb10fa32a73175c46d08c19a6b441d148b6838

DockerイメージがCentOS 7ベースで、コンテナ内でsystemdを使用する場合は、コンテナの起動オプションに、「--privileged」オプションが必要になります。またコンテナの起動に指定するコマンドは、「/sbin/init」を指定します。コンテナが起動したかを確認します。

# docker ps -a
CONTAINER ID        IMAGE                   COMMAND             CREATED             STATUS              PORTS                NAMES
3db4623293db        centos7_apache:latest   "/sbin/init"        15 seconds ago      Up 15 seconds       0.0.0.0:80->80/tcp   apache001

コンテナ「apache001」が起動していることがわかります。ホストマシンからコンテナ「apache001」にログインします。

# nsenter -t $(docker inspect --format '{{.State.Pid}}' apache001) -i -m -n -p -u /bin/bash
[root@3db4623293db /]#

上記のようにログインプロンプトが「[root@3db4623293db /]」になったことで、コンテナにログインできていることがわかります。コンテナにログインできていますので、コンテナ上でApache Webサービスが起動しているかを確認します。

[root@3db4623293db /]# systemctl status httpd

コンテナがhttpdサービスを正常に起動できていることを確認できたら、コンテナに割り振られたIPアドレスをコンテナ上で確認します。

[root@3db4623293db /]# ip a |grep inet
    ...
    inet 172.17.0.26/16 scope global eth0
    ...

コンテナが外部にWebサービスを提供できているかをホストマシンや他のクライアントマシンから確認します。コマンドラインから確認するには、curlコマンドが有用です。

# curl 172.17.0.26/index.html
Hello Apache.

【注意】

本連載では、CentOS 7のDockerイメージにsystemdをインストールする方法を紹介しましたが、Dockerコンテナ上でのsystemdの利用については、全てのアプリケーションやデーモンについて、十分なテストが行われているわけではありません。したがって、利用者の自己責任の元で十分な動作確認を行って上記手順を利用するように下さい。

cgroupによるハードウェア資源管理

一つのホスト上で複数の隔離空間として稼働するDockerコンテナやKVMの仮想マシンが稼働する環境において、限られたハードウェア資源の利用制限は非常に重要です。特定のユーザーが使用するコンテナがホストマシンのハードウェア資源を食いつぶすようなことがあると、他のユーザーの利用に支障をきたします。そこで、CentOS 7では、cgroupとよばれる資源管理の仕組みが備わっています。以下では、cgroupを使ったネットワーク帯域制御とディスクI/O制御の手法を紹介します。

cgroupとは

「cgroupは、Linuxのカーネルに実装されているリソース制御の仕組みです。CPU、メモリ、ネットワーク通信の帯域幅などのコンピュータ資源を組み合わせ、ユーザーが定義したタスクのグループに割り当て、このグループに対してリソースの利用の制限や開放を設定することが可能となります。この設定は、システムが稼働中に行うことができ、OSの再起動を行うことなく資源の割り当てを動的に行う事が可能となります。特に、マルチコアのシステムにおいて、CPUを効率よく利用するために商用UNIXでも利用されている技術です。マルチコアシステムで稼働させるマルチスレッドのアプリケーションの性能劣化をできるだけ発生させないようにするために、cgroupによってコンピューター資源を割り当てます。

cgroupの初期設定

CentOS 7におけるcgroupを利用するには、cgconfigサービスを起動します。

# systemctl enable cgconfig
ln -s '/usr/lib/systemd/system/cgconfig.service' '/etc/systemd/system/sysinit.target.wants/cgconfig.service'
 
# systemctl start cgconfig

cgroupによる資源管理は、/sys/fs/cgroup配下の各種ディレクトリ配下に、ハードウェア資源に対応したサブシステムが存在します。

# lssubsys -am
cpuset /sys/fs/cgroup/cpuset    ←CPUリソースに関する各種パラメータを格納しているディレクトリ
cpu,cpuacct /sys/fs/cgroup/cpu,cpuacct ←CPUリソースに関する自動レポートを生成
memory /sys/fs/cgroup/memory ←メモリーリソースの自動レポートを生成、タスクが使用するメモリの上限の設定
devices /sys/fs/cgroup/devices ←ブロックデバイスや文字デバイスへのアクセス可否を設定
freezer /sys/fs/cgroup/freezer ←タスクの一時停止や再開
net_cls /sys/fs/cgroup/net_cls ←ネットワークパケットのタグ付け
blkio /sys/fs/cgroup/blkio ←ブロックデバイスへのI/Oを制御、監視
perf_event /sys/fs/cgroup/perf_event ←プロセスやスレッドをperfツールで監視可能にする
hugetlb /sys/fs/cgroup/hugetlb ←大きいサイズの仮想メモリページを利用可能にする

今回は、ネットワーク通信の帯域制御を行うため、net_clsサブシステムを使って、test01という名前のcgroupを作成します。

# cgcreate -t koga:koga -g net_cls:/test01

# ls -lF /sys/fs/cgroup/net_cls/test01/
total 0
-rw-rw-r--. 1 root root 0 Sep  9 02:06 cgroup.clone_children
--w--w----. 1 root root 0 Sep  9 02:06 cgroup.event_control
-rw-rw-r--. 1 root root 0 Sep  9 02:06 cgroup.procs
-rw-rw-r--. 1 root root 0 Sep  9 02:06 net_cls.classid
-rw-rw-r--. 1 root root 0 Sep  9 02:06 notify_on_release
-rw-rw-r--. 1 koga koga 0 Sep  9 02:06 tasks

作成したtest01というcgroupに対してパラメータを設定します。

# cgset -r net_cls.classid=0x00010002 /test01
# cat /sys/fs/cgroup/net_cls/test01/net_cls.classid
65538

通信の帯域制御を行うためのネットワークインタフェースを確認します。

# ip a
...
2: enp0s25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 70:5a:b6:ab:5e:da brd ff:ff:ff:ff:ff:ff
    inet 172.16.25.30/16 brd 172.16.255.255 scope global enp0s25
       valid_lft forever preferred_lft forever
...

tcコマンドにより、トラフィック量を調整します。

# tc qdisc add dev enp0s25 root handle 1: htb

tcコマンドはトラフィックを制御するコマンドです。qdisc(queueing discipline)は、キューイングに関する規則を表します。OSがデータをどのように送信するかは、どのようなキューイングを使うかに依存します。仮に、Linuxマシンが1GbEのネットワークカードを持っているにも関わらず、接続先のスイッチが100Mb/sにしか対応していない場合、Linuxマシン側で、送信するデータの量を調整する必要があります。この調整を行うためには、データの送信の仕方を工夫する必要があり、その送信の仕方には、キューイングなどが使われています。カーネルは、パケットをインタフェースに送信する時は、インタフェースに対して設定したqdiscにキューイングされます。一般的なqdiscとしては、FIFO型のキューイングがあります。「qdisc add」により、qdiscを追加することを意味します。devには、ネットワークインタフェース名を指定します。今回指定しているネットワークインタフェース名は、enp0s25です。インタフェース名はipコマンドで確認できます。さらに、指定したインタフェースに出入り口を作る必要があります。この出入り口は、root qdiscと呼ばれます。qdiscには、ハンドル(handle)を割り当てます。このハンドルを使ってqdiscを参照することができます。ハンドルは、「メジャー番号:マイナー番号」の書式をとります。ただし、ルートqdiscについては、上記のように、マイナー番号を省略し、「1:」と記述するのが一般的です。これは「1:0」と同じ意味になります。

# tc class add dev enp0s25 parent 1: classid 1:2 htb rate 256kbps

先程作成したルートqdiscの「1:」に繋がるクラス「1:2」を作成します。このクラスを流れるパケットの帯域幅をrateで指定します。htb(hierarchical token bucket)は、階層型トークンバケットと呼ばれ、キューイングの規則にとってかわる高速化の一手法です。

# tc filter add dev enp0s25 parent 1: protocol ip prio 10 handle 1: cgroup

上記は、ネットワークインタフェースenp0s25に対して、フィルタを作成します。「protocol ip」でIPプロトコルを指定しています。キューイングにおける複数のクラスに対して、優先度を設定することができます。

ネットワークインタフェースenp0s25に対する通信性能を検証します。今回は、転送速度の性能検証に用いるファイルをddコマンドで用意します。性能検証用のファイル「testfile」のサイズは30MBとしました。

# cd
# dd if=/dev/zero of=/root/testfile bs=1024k count=30
# ls -lh testfile
-rw-r--r--. 1 root root 30M Sep  9 02:28 testfile

転送前にtestfileのチェックサムを確認しておきます。遠隔にあるサーバーに転送されたファイルとチェックサムが一致しているかを確認するためです。

# md5sum ./testfile
281ed1d5ae50e8419f9b978aab16de83  ./testfile

testfileのファイル転送は、scpを使います。上記testfileをscpでコピーするスクリプトを作成します。転送先のマシンは、遠隔にある別のLinuxサーバーで構いません。

# vi /root/scp.sh
scp /root/testfile 172.16.1.5:/root/

# chmod +x /root/scp.sh

帯域を制限できるかどうかをテストします。ネットワークインタフェースenp0s25のパケット送受信の帯域幅を100キロビット/秒に設定します。

# tc class change dev enp0s25 parent 1: classid 1:2 htb rate 100kbps
# cgexec --sticky -g net_cls:test01 ./scp.sh
root@172.16.1.5's password:
testfile                                                   25% 7952KB  99.0KB/s   03:49 ETA

次に、1メガビット/秒に帯域を制限できるかどうかをテストします。

# tc class change dev enp0s25 parent 1: classid 1:2 htb rate 1mbps
# time cgexec --sticky -g net_cls:test01 ./scp.sh
root@172.16.1.5's password:
testfile                                                   97%   29MB 991.0KB/s   00:00 ETA

最後に、10メガビット/秒に帯域を制限できるかどうかをテストします。

# tc class change dev enp0s25 parent 1: classid 1:2 htb rate 10mbps
[root@c70n2530 ~]# time cgexec --sticky -g net_cls:test01 ./scp.sh
root@172.16.1.5's password:
testfile                                                  100%   30MB  10.0MB/s   00:03

各帯域制限の検証において、ファイル転送完了後は、転送先に保管されたtestfileのMD5チェックサムも確認して下さい。今回は、100kb/s、1Mb/s、10Mb/sの3種類の帯域制限で検証を行いましたが、この値以外の転送速度を指定することも可能です。

ディスクI/Oの帯域制御

以下では、cgroupを使ったディスクI/Oの帯域制御の手順を述べます。まず、test01というcgroupを作成します。/sys/fs/cgroup/blkio/test01ディレクトリ配下に、様々なファイルが用意されます。

# cgcreate -t koga:koga -g blkio:/test01

# ls -l /sys/fs/cgroup/blkio/test01/
total 0
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_merged
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_merged_recursive
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_queued
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_queued_recursive
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_service_bytes
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_service_bytes_recursive
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_serviced
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_serviced_recursive
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_service_time
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_service_time_recursive
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_wait_time
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.io_wait_time_recursive
-rw-rw-r--. 1 root root 0 Sep  9 05:31 blkio.leaf_weight
-rw-rw-r--. 1 root root 0 Sep  9 05:31 blkio.leaf_weight_device
--w--w----. 1 root root 0 Sep  9 05:31 blkio.reset_stats
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.sectors
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.sectors_recursive
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.throttle.io_service_bytes
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.throttle.io_serviced
-rw-rw-r--. 1 root root 0 Sep  9 05:31 blkio.throttle.read_bps_device
-rw-rw-r--. 1 root root 0 Sep  9 05:31 blkio.throttle.read_iops_device
-rw-rw-r--. 1 root root 0 Sep  9 05:31 blkio.throttle.write_bps_device
-rw-rw-r--. 1 root root 0 Sep  9 05:31 blkio.throttle.write_iops_device
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.time
-r--r--r--. 1 root root 0 Sep  9 05:31 blkio.time_recursive
-rw-rw-r--. 1 root root 0 Sep  9 05:31 blkio.weight
-rw-rw-r--. 1 root root 0 Sep  9 05:31 blkio.weight_device
-rw-rw-r--. 1 root root 0 Sep  9 05:31 cgroup.clone_children
--w--w----. 1 root root 0 Sep  9 05:31 cgroup.event_control
-rw-rw-r--. 1 root root 0 Sep  9 05:31 cgroup.procs
-rw-rw-r--. 1 root root 0 Sep  9 05:31 notify_on_release
-rw-rw-r--. 1 koga koga 0 Sep  9 05:31 tasks

次に、I/O帯域制御を行いたいストレージデバイスのメジャー番号とマイナー番号をlsコマンドで確認します。今回の場合は、メジャー番号が8、マイナー番号が0です。

# ls -l /dev/sda
brw-rw----. 1 root disk 8, 0 Sep  7 12:29 /dev/sda

ディスクI/Oを発生させるスクリプトio.shを作成します。io.shスクリプトは、/usr/share/doc以下の全てのファイルを/dev/nullにアーカイブするスクリプトです。さらに、tarコマンドに--totalsオプションを付与することで、書き出したバイト数を出力することができます。

# vi /root/io.sh
echo 3 > /proc/sys/vm/drop_caches
time tar cvf /dev/null /usr/share/doc --totals

# chmod +x /root/io.sh

ディスクI/Oを10IOPS以下に制限できるかを検証します。I/O帯域制御を行うストレージデバイスのメジャー番号とマイナー番号を「blkio.throttle.read_iops_device」に指定します。

# cgset -r blkio.throttle.read_iops_device="8:0 10" /test01
# cgexec --sticky -g blkio:test01 ./io.sh
tar: Removing leading `/' from member names
Total bytes written: 95610880 (92MiB, 971KiB/s)

real    1m38.296s
user    0m0.029s
sys     0m0.112s

同様に、ディスクI/Oを50IOPS以下に制限できるかを検証します。

# cgset -r blkio.throttle.read_iops_device="8:0 50" /test01
# cgexec --sticky -g blkio:test01 ./io.sh
tar: Removing leading `/' from member names
Total bytes written: 95610880 (92MiB, 4.5MiB/s)

real    0m21.327s
user    0m0.043s
sys     0m0.099s

ディスクI/Oを500IOPS以下に制限できるかも検証します。

# cgset -r blkio.throttle.read_iops_device="8:0 500" /test01
# cgexec --sticky -g blkio:test01 ./io.sh
tar: Removing leading `/' from member names
Total bytes written: 95610880 (92MiB, 8.3MiB/s)

real    0m11.549s
user    0m0.034s
sys     0m0.108s

最後に、cgroupsを適用しない場合のディスクI/O性能を計測しておきます。

# ./io.sh
tar: Removing leading `/' from member names
Total bytes written: 95610880 (92MiB, 8.4MiB/s)

real    0m11.504s
user    0m0.029s
sys     0m0.113s

ディスクI/Oを10IOPSに設定した場合、io.shスクリプトの実行に、約1分38秒掛っているのに対し、ディスクI/Oを50IOPSに設定した場合は、約21秒に短縮されていることがわかります。このことから、cgroupにより、限られたディスクの性能をユーザーのアプリケーション毎に制限することで、コンピューターシステム全体をより多くのユーザーやアプリケーションで効率的に利用できることがわかります。

systemdを使ったリソース制限

CentOS 7では、systemctlコマンドを使って、httpdサービスのメモリ制限値を設定することができます。以下は、httpdサービスのメモリ使用量の制限値を1GBにする例です。

# systemctl set-property httpd.service MemoryLimit=1G
# systemctl daemon-reload ; systemctl restart httpd.service
# cat /etc/systemd/system/httpd.service.d/90-MemoryLimit.conf
[Service]
MemoryLimit=1073741824

実際のクラウドサービス等では、サービスメニューに応じて様々な帯域制限が設けられているのが一般的です。Linuxのcgroupを使えば、クラウド基盤において、特定のユーザーがネットワーク帯域を使い切らないように帯域制限をかけることができます。利用できるネットワーク帯域幅による重量課金制のクラウド基盤システムを構築する場合に有用です。

今回は、仮想化、コンテナ、そして、それらの環境で必要とされるcgroupによるリソース管理の基礎をご紹介しました。仮想化やコンテナは、単なるサーバー集約だけでなく、クラウド基盤でのサービス提供やDevOps環境で必要となる非常に重要な基礎技術です。CentOS 7には、クラウドコンピューティングに必要なこれらの機能が多く搭載されています。全てを紹介することはできませんが、少なくとも、本連載の手順を一通り試し、仮想化やクラウド基盤、開発環境のありかたを再考してみて下さい。

この連載が書籍になりました!
CentOS 7実践ガイド

古賀 政純 著
価格:3,000円+税
発売日:2015年2月25日発売
ISBN:978-4-8443-3753-9
発行:インプレスジャパン

CentOS 7実践ガイド

本書は、CentOS 7を取り巻く市場動向、CentOS 7が利用されるサーバーシステムの選定、CentOS 7の基礎、システム設計、OS管理やCentOS 7に対応したアプリケーションサーバーの構築手順などの勘所をご紹介します。連載では書ききれなかった本の内容、見どころが満載!

  • セキュリティ管理
  • チューニング
  • 自動インストール
  • Hadoop構築
  • GlusterFS
  • Ceph

Amazon詳細ページへImpress詳細ページへ

日本ヒューレット・パッカード株式会社 プリセールス統括本部 ソリューションセンター OSS・Linux担当 シニアITスペシャリスト

兵庫県伊丹市出身。1996年頃からオープンソースに携わる。2000年よりUNIXサーバーのSE及びスーパーコンピューターの並列計算プログラミング講師を担当。科学技術計算サーバーのSI経験も持つ。2005年、大手製造業向けLinuxサーバー提案で日本HP社長賞受賞。2006年、米国HPからLinux技術の伝道師に与えられる「OpenSource and Linux Ambassador Hall of Fame」を2年連続受賞。日本HPプリセールスMVPを4度受賞。現在は、Linux、FreeBSD、Hadoop等のOSSを駆使したスケールアウト型サーバー基盤のプリセールスSE、技術検証、技術文書執筆を担当。日本HPのオープンソース・Linuxテクノロジーエバンジェリストとして講演活動も行っている。Red Hat Certified Engineer、Red Hat Certified Virtualization Administrator、Novell Certified Linux Professional、EXIN Cloud Computing Foundation Certificate、HP Accredited Systems Engineer Cloud Architect、Red Hat Certified System Administrator in Red Hat OpenStack、Cloudera Certified Administrator for Apache Hadoop認定技術者。HP公式ブログ執筆者。趣味はレーシングカートとビリヤード

連載バックナンバー

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

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

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

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