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

第10回LXCの基本操作 [3]

今回は前回紹介できなかったコマンドの紹介を続けます。その後、Ubuntu 14.04 LTSでcgroupを管理するために導入されたcgmanagerについて少し紹介します。

アプリケーションコンテナ

lxc-startを使ったアプリケーションコンテナの起動

第8回lxc-startコマンドの使い方を紹介しました。その際に紹介したのはシステムコンテナを起動する方法でした。lxc-startは特に指定しなければコンテナイメージ内の/sbin/initを実行します。

ここでコマンドを指定するとlxc-startはアプリケーションコンテナとして指定したコマンドを実行します

$ sudo lxc-start -n ct01 -- /bin/bash
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
root@ct01:/# hostname 
ct01

/bin/bashを指定すると警告は出るものの、コンテナct01として/bin/bashが実行されています。

lxc-startでアプリケーションコンテナを実行するときには注意が必要です。指定したコマンドがすぐに終了して制御が戻ってきてしまうような場合、lxc-startはコンテナのinitに相当するコンテナ内のPIDが1のプロセスがすぐに終了したとみなしてコンテナが起動しません。

以下の例を見てください。これはCentOSコンテナにApacheをインストールして、アプリケーションコンテナとして実行しようとしています。

# lxc-start -n apache01 -o log -l DEBUG -- /usr/sbin/httpd
# lxc-ls --fancy
NAME      STATE    IPV4  IPV6  AUTOSTART  
----------------------------------------
apache01  STOPPED  -     -     NO         

lxc-startの実行はエラー出力もなく成功しているようです。しかし、確認してみると停止しています。ログを見てみましょう。

# cat log
  : (略)
      lxc-start 1409655419.090 NOTICE   lxc_start - exec'ing '/usr/sbin/httpd'
      lxc-start 1409655419.091 NOTICE   lxc_start - '/usr/sbin/httpd' started with pid '1504'
      lxc-start 1409655419.157 DEBUG    lxc_start - container init process exited

指定した/usr/sbin/httpdの実行は行われ、PIDが1504として起動していますが、直後にexitしてしまっています。

このような場合は指定したコマンドが終了しなければ良いので、以下のように/sbin/sbin/httpdをフォアグラウンドで起動すれば良いのです。

# lxc-start -d -n apache01 -o log -l INFO -- /usr/sbin/httpd -D FOREGROUND
# lxc-ls --fancy
NAME      STATE    IPV4        IPV6  AUTOSTART  
----------------------------------------------
apache01  RUNNING  10.0.3.200  -     NO         

このようにきちんと実行されます。

lxc-executeによるアプリケーションコンテナの実行

以上のようにフォアグラウンドで起動させるためにちょっと工夫が必要なコマンドをコンテナとして実行する際に便利な場合があるコマンドがlxc-executeです。⁠場合がある」と微妙な表現になっている理由は後で説明します。

lxc-executelxc-startと同様にコンテナを起動するためのコマンドです。lxc-startとの違いは、lxc-executeはアプリケーションコンテナを起動させるためのコマンドであるという点です。

lxc-executeは内部的な処理はlxc-startと同じです。違いはコンテナのinitとしてLXCが提供する簡易的なinitプログラムであるlxc.initを起動し、その子プロセスとして指定したコマンドを実行するところです。

これを確認してみましょう。以下ではUbuntuコンテナにApacheをインストールして実行しています。

# lxc-execute -n apache01 -o log -l INFO -- /usr/sbin/apache2ctl start &
[1] 7612
# lxc-ls --fancy
NAME      STATE    IPV4        IPV6  AUTOSTART  
----------------------------------------------
apache01  RUNNING  10.0.3.200  -     NO       

ご覧のように通常通りApacheの起動を行ってもコンテナが終了することなく起動した状態になっています。

このlxc-executeコマンドをpstreeコマンドで見てみましょう。

# pstree -A  7612
lxc-execute---init.lxc---apache2---2*[apache2---26*[{apache2}]]

ご覧のようにinit.lxcというプログラムを挟んでapache2が実行されています。

このinit.lxclxc-executeで指定したコマンドの終了を監視し、コマンドが終了すると自身も終了し、コンテナを終了させます。また、lxc-stopから送られてくるシグナルを子プロセスに送る役割も果たします。

ここまでご覧になると、アプリケーションコンテナを起動するにはlxc-executeが便利なような気がします。しかし実はそうでもありません。LXC 1.0系列でlxc-executeを使うには準備が必要です。

前述の例はUbuntu 14.04 LTSのコンテナを使っています。このコンテナに"apache2"パッケージだけでなく、以下のように"lxc"パッケージもインストールしました。これはinit.lxcをインストールするためです。

$ sudo lxc-attach -n ct01 -- apt-get install lxc
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  apparmor bridge-utils busybox-static ca-certificates cgmanager
  cloud-image-utils debootstrap distro-info distro-info-data dnsmasq-base
  euca2ools genisoimage iptables libaio1 libapparmor-perl libapparmor1
    : (略)

コンテナ内で実行するコマンドはコンテナイメージ内に存在する必要がありますlxc-executeが内部的に実行するinit.lxcであってもこれは同じです。これを手っ取り早く準備するためにinit.lxcが含まれる"lxc"パッケージをインストールしたのです。

ホストOSにインストールした"lxc"パッケージにもinit.lxcコマンドが含まれますので、これをコンテナ内にコピーするかバインドマウントすれば良さそうに思えますが、そう簡単にはいきません。LXC 1.0系列のinit.lxcはダイナミックリンクされているため、依存するライブラリを全て調べてコンテナ内に準備する必要があります。

そこで"lxc"パッケージをインストールしたわけです。コンテナ内で"lxc"パッケージをインストールすれば依存関係で必要なパッケージがインストールされるので楽です。

しかし、この方法が使えたのは、ここで使った例ではコンテナの環境がUbuntu14.04 LTSだったからです。これが別のディストリビューションだとそもそもLXCのパッケージがすぐに利用可能かどうかわかりません。

環境が準備されてしまえばlxc-executeを使うのは簡単ですが、それまでの手間を考えると少し微妙な気がしますね。ここで紹介した例のように、init.lxcを使うためだけに、使わない"lxc"パッケージの他のコマンドや依存ライブラリを大量にインストールするのは無駄です。

なお、LXC 1.1からは、lxc-executeが使うinit.lxcはスタティックリンクされ、lxc-executeを実行した時点でホストOSのinit.lxcがコンテナ内に自動でバインドマウントされるようになる予定ですので、簡単に使えるようになりそうです。

LXCのシステム設定 ~ lxc-config

lxc-configはLXCのシステム設定を確認するためのコマンドです。⁠システム設定」とは第8回で紹介した「システム設定ファイル」で設定する項目です。コンテナのデフォルト設定ファイルのパスやコンテナの保存場所のパスなどの設定でしたね。

まずはlxc-configで確認できる値の一覧を表示してみましょう。

$ lxc-config -l
lxc.default_config
lxc.lxcpath
lxc.bdev.lvm.vg
lxc.bdev.lvm.thin_pool
lxc.bdev.zfs.root

コンテナのデフォルト設定ファイルとコンテナの保存場所を表示させてみましょう。一覧で出てきた設定を指定するだけです。

$ sudo lxc-config lxc.default_config
/etc/lxc/default.conf
$ sudo lxc-config lxc.lxcpath
/var/lib/lxc

コンテナの一時停止、再開 ~ lxc-freeze,lxc-unfreeze

起動中のコンテナを一時停止させる場合はlxc-freezeを使います。

$ sudo lxc-start -n ct01 -d
$ sudo lxc-freeze -n ct01
$ sudo lxc-ls --fancy ct01                      
NAME      STATE    IPV4        IPV6  AUTOSTART  
----------------------------------------------
ct01      FROZEN   10.0.3.156  -     NO         

一時停止中のコンテナを再開させる場合はlxc-unfreezeを使います。

$ sudo lxc-ls --fancy ct01
NAME  STATE   IPV4        IPV6  AUTOSTART  
-----------------------------------------
ct01  FROZEN  10.0.3.156  -     NO         
$ sudo lxc-unfreeze -n ct01
$ sudo lxc-ls --fancy ct01
NAME  STATE    IPV4        IPV6  AUTOSTART  
------------------------------------------
ct01  RUNNING  10.0.3.156  -     NO         

コンテナの状態のモニタリング ~ lxc-monitor

lxc-monitorはコンテナの状態をモニタリングします。実行すると、コンテナの状態が変わるたびに状態の表示が行います。

コンテナの状態はこれまでもlxc-lslxc-infoの出力として紹介しました。しかし、これまでどのような状態があるのかは説明していませんでしたので、ここで紹介しておきましょう。

表1 コンテナの状態一覧
状態説明
STOPPED 停止
STARTING 起動途中
RUNNING 実行中
STOPPING 停止途中
ABORTING コンテナの初期化に失敗
FREEZING 一時停止途中
FROZEN 一時停止
THAWED 再開
図1 状態遷移図
図1 状態遷移図

コンテナct01の状態をモニタリングしてみましょう。

$ sudo lxc-monitor -n ct01
'ct01' changed state to [STARTING]
'ct01' changed state to [RUNNING]
'ct01' changed state to [FREEZING]
'ct01' changed state to [FROZEN]
'ct01' changed state to [THAWED]
'ct01' changed state to [STOPPING]
'ct01' changed state to [STOPPED]

これはlxc-monitorを実行した後、別の端末で以下のようなコマンドを順に実行した時の出力です。

$ sudo lxc-start -n ct01 -d
$ sudo lxc-freeze -n ct01
$ sudo lxc-unfreeze -n ct01
$ sudo lxc-stop -n ct01

コンテナ名を指定しないで実行すると、コンテナの保存場所に存在する全てのコンテナをモニタリングします。

$ sudo lxc-monitor 
'ct01' changed state to [STARTING]
'ct01' changed state to [RUNNING]
'ct02' changed state to [STARTING]
'ct02' changed state to [RUNNING]
'ct02' changed state to [STOPPING]
'ct02' changed state to [STOPPED]
'ct01' changed state to [STOPPING]
'ct01' changed state to [STOPPED]

指定したコンテナの状態への待機 ~ lxc-wait

lxc-waitはコンテナが指定した状態になるのを待って終了します。コンテナの状態に応じて何かを行うようなスクリプト中で使えそうですね。

$ sudo lxc-wait -n ct01 -s STOPPED

と実行しておいて、別の端末で

$ sudo lxc-stop -n ct01

とコンテナを停止させるとlxc-waitの実行は終了します。

Ubuntu 14.04 LTSのcgroup管理

一通りコンテナの管理に必要なコマンドの紹介が済んだ所で、lxc-cgroupのところで触れたUbuntu 14.04 LTSでのcgroupの管理について簡単に紹介しておきます。

Ubuntu 12.04 LTSでは、"lxc"パッケージをインストールすると、依存関係で"cgroup-lite"というパッケージが同時にインストールされ、ホストOSの起動時にサブシステムごとのディレクトリを作成してマウントするシェルスクリプトが実行され、cgroupfsがマウントされていました。

$ grep cgroup /proc/mounts
cgroup /sys/fs/cgroup tmpfs rw,relatime,mode=755 0 0
cgroup /sys/fs/cgroup/cpuset cgroup rw,relatime,cpuset 0 0
cgroup /sys/fs/cgroup/cpu cgroup rw,relatime,cpu 0 0
cgroup /sys/fs/cgroup/cpuacct cgroup rw,relatime,cpuacct 0 0
cgroup /sys/fs/cgroup/memory cgroup rw,relatime,memory 0 0
cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0
cgroup /sys/fs/cgroup/freezer cgroup rw,relatime,freezer 0 0
cgroup /sys/fs/cgroup/blkio cgroup rw,relatime,blkio 0 0
cgroup /sys/fs/cgroup/perf_event cgroup rw,relatime,perf_event 0 0
cgroup /sys/fs/cgroup/hugetlb cgroup rw,relatime,hugetlb 0 0
$ ls /sys/fs/cgroup/
blkio  cpu  cpuacct  cpuset  devices  freezer  hugetlb  memory  perf_event

起動後、cgroupfsがマウントされている様子は以上のようになっていました。

ところがUbuntu 14.04 LTSでは"cgroup-lite"パッケージはインストールされません。

最近の議論では、cgroupは将来的にはcgroupfsを直接触って管理はせず、cgroupを管理するエージェントを介して管理するという流れになっています。このことは第5回「cgroupの今後」で少し紹介しました。

この流れに従い、Ubuntuでもcgmanagerというソフトウェアが開発されました。Ubuntu 14.04 LTSでは、cgroup-liteの代わりにこのcgmanagerがLXCと同時にインストールされます。

このためUbuntu 14.04 LTS環境では/sys/fs/cgroupディレクトリは以下のようにすっきりしています。

$ grep cgroup /proc/mounts 
none /sys/fs/cgroup tmpfs rw,relatime,size=4k,mode=755 0 0
systemd /sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,release_agent=/run/cgmanager/agents/cgm-release-agent.systemd,name=systemd 0 0
$ ls /sys/fs/cgroup
cgmanager  systemd
$ ls -l /sys/fs/cgroup/cgmanager/
total 0
srwxrwxrwx 1 root root 0 Aug 12 18:40 sock

cgmanagerというディレクトリが存在し、その下にはソケットが存在するだけです。

この状態でもLXCを使ってコンテナは起動し、cgroupを使ったリソースが制限できます。cgroupはcgroupfsをマウントしなければ利用できません。どこで管理されているのでしょう?

実はcgmanagerは起動時に新たなマウント名前空間を作成し、その名前空間内でcgroupfsをマウントします。このため、ホストOSが属する名前空間からはcgmanagerがマウントしたcgroupfsが見えないのです。

util-linux 2.23以降に含まれるnsenterというコマンドを使って確認してみましょう。nsenterは任意の名前空間上でコマンドが実行できるコマンドです。実行中のcgmanagerデーモンが属するマウント名前空間を指定してコマンドを実行してみます[1]⁠。

$ sudo ./nsenter --target `pgrep cgmanager` --mount -- grep cgroup /proc/mounts 
none /sys/fs/cgroup tmpfs rw,relatime,size=4k,mode=755 0 0
none,name=systemd /run/cgmanager/fs/none,name=systemd cgroup rw,relatime,release_agent=/run/cgmanager/agents/cgm-release-agent.systemd,name=systemd 0 0
cpuset /run/cgmanager/fs/cpuset cgroup rw,relatime,cpuset,release_agent=/run/cgmanager/agents/cgm-release-agent.cpuset,clone_children 0 0
cpu /run/cgmanager/fs/cpu cgroup rw,relatime,cpu,release_agent=/run/cgmanager/agents/cgm-release-agent.cpu 0 0
cpuacct /run/cgmanager/fs/cpuacct cgroup rw,relatime,cpuacct,release_agent=/run/cgmanager/agents/cgm-release-agent.cpuacct 0 0
memory /run/cgmanager/fs/memory cgroup rw,relatime,memory,release_agent=/run/cgmanager/agents/cgm-release-agent.memory 0 0
devices /run/cgmanager/fs/devices cgroup rw,relatime,devices,release_agent=/run/cgmanager/agents/cgm-release-agent.devices 0 0
freezer /run/cgmanager/fs/freezer cgroup rw,relatime,freezer,release_agent=/run/cgmanager/agents/cgm-release-agent.freezer 0 0
blkio /run/cgmanager/fs/blkio cgroup rw,relatime,blkio,release_agent=/run/cgmanager/agents/cgm-release-agent.blkio 0 0
perf_event /run/cgmanager/fs/perf_event cgroup rw,relatime,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event 0 0
hugetlb /run/cgmanager/fs/hugetlb cgroup rw,relatime,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb 0 0
$ sudo ./nsenter --target `pgrep cgmanager` --mount -- ls /run/cgmanager/fs
blkio  cpu  cpuacct  cpuset  devices  freezer  hugetlb  memory  none,name=systemd  perf_event

ご覧のように実行中のcgmanagerが属するマウント名前空間では/run/cgmanager/fs以下にcgroupfsがマウントされているのがわかります。

cgmanagerはホストOSの属する名前空間にあった/sys/fs/cgroup/cgmanager/sock経由でDBusのメッセージを受け付けて別の名前空間でマウントされたcgroupfsの操作を行います。

なお、LXC自体はcgmanager経由でも、従来通りのcgroupfsがホストOSの名前空間でマウントされていて直接cgroupfsを触る環境でもcgroupの管理が可能なように作られています。

まとめ

今回はLXCに付属するコマンドのうち、これまで紹介していなかったものを紹介し、その後でUbuntu 14.04 LTSでLXCを使う際にcgroupを管理する役割を果たすcgmanagerを紹介しました。

次回からはLXCの設定について解説していく予定です。

第4回 コンテナ型仮想化の情報交換会@東京

筆者が主催している「コンテナ型仮想化の情報交換会」の第4回目を、9月6日(土)にニフティ株式会社に会場をお借りして開催してきました。

この勉強会の資料や動画を以下にまとめました。LinuxやLXCに限らないいろいろなコンテナ関連の技術のお話や活用事例のお話が聞けて非常に勉強になりました。興味のある方はぜひご覧ください。

第4回 コンテナ型仮想化の情報交換会@東京

おすすめ記事

記事・ニュース一覧