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

第23回LXCの構築・活用[9]─ lxc-start-ephemeralとフック(1)

前回までで、LXC 1.0でサポートされているストレージバックエンドの紹介が一通り終わりました。今回はこれまで紹介していなかったlxc-start-ephemeralコマンドを紹介しましょう。

lxc-start-ephemeralコマンドは、コンテナ起動前の準備や終了後の後始末で、LXCが持つフック機能を使っています。以前にLXCの設定について紹介した第11回第12回ではこの機能について紹介していませんでしたので、lxc-start-ephemeralコマンドを実例にしてあわせて簡単に紹介したいと思います。

今回の例はUbuntu 14.04.2 LTS上で、パッケージでインストールしたlxc 1.0.7を使って実行しています。

lxc-start-ephemeralコマンドの概要

lxc-start-ephemeralコマンドは、存在しているコンテナの一時的なコピーとなるコンテナを作成し、起動するコマンドです。

既存のコンテナをベースにいろいろな作業を行うには、これまでに紹介したlxc-cloneコマンドによるクローンが便利です。

一方、⁠ちょっと試したいことがある」という場合で、試した結果が保存されている必要がない場合にはlxc-start-ephemeralコマンドが便利な場面もあるでしょう。⁠一時的な(ephemeral)コピー」というくらいですので、デフォルトでは起動後に行った変更はすべて消えてなくなりますので注意してください。

また、lxc-start-ephemeralコマンドはoverlayfsもしくはaufsを使用しますので、このどちらかが使えない場合は動作しません。注意してください。

まずは使ってみましょう

まずは簡単にlxc-start-ephemeralコマンドの動きを見てみましょう。

lxc-start-ephemeralコマンドを一番シンプルに使用するには、一時的なコンテナのコピー元となるコンテナ名だけを指定します。

$ sudo lxc-ls
ct01  
$ sudo lxc-start-ephemeral -o ct01

以上のように実行するとフォアグラウンドでの起動になりますので、起動したコンソール上にログインプロンプトが表示されます。テンプレートによっては、コンテナを作成した直後はログインできるユーザが存在しない場合がありますので注意しましょう。

あらかじめ-oで指定したコピー元のコンテナにアクセスできるユーザが設定されていればそのまま使えますが、そうでなければ別の端末からlxc-stopで終了させるか、lxc-attachコマンドで必要な設定や操作を行いましょう。

なおlxc-startコマンドで起動したコンテナがデフォルトでバックグラウンド起動になるLXC 1.1をインストールした環境であっても、lxc-ephemeralコマンドは特に指定しなければフォアグラウンドでの起動になります。

lxc-startと同様に、lxc-start-ephemeralでも-dオプションを使ってバックグラウンドで起動できます。ここから後はバックグラウンドで起動していろいろな機能を紹介していきます。

それではバックグラウンドでコンテナを起動してみましょう。

$ sudo lxc-start-ephemeral -o ct01 -d
The ephemeral container is now started.

You can enter it from the command line with: lxc-console -n ct01-zvsn8pmn
The following IP addresses have be found in the container:
 - 10.0.3.130

バックグラウンドで起動すると、以上のようにコンテナ名とIPアドレスが表示されます。起動するコンテナ名はコピー元のコンテナ名をベースにランダムな文字列が付与された名前になります。

もし、コンテナへ直接アクセスしてコマンドを実行する必要があるのであれば、以下のような方法でコンテナにアクセスして必要な処理を行います。

  • lxc-consoleコマンドを使ってログインする
  • ssh経由でログインする
  • lxc-attachコマンドでコンテナ上でコマンドを実行する

一時的とは言え、通常のコンテナを起動している場合と何も変わりませんのでlxc-lsで確認できます。

$ sudo lxc-ls -f
NAME           STATE    IPV4        IPV6  AUTOSTART  
---------------------------------------------------
ct01           STOPPED  -           -     NO         
ct01-zvsn8pmn  RUNNING  10.0.3.130  -     NO         

起動を確認したあとコンテナを停止させてみます。

$ sudo lxc-stop -n ct01-zvsn8pmn
$ sudo lxc-ls 
ct01  

以上のように停止後はコンテナは消えています。

lxc-start-ephemeralコマンドのオプション

lxc-start-ephemeralコマンドを詳しく見ていく前に、指定できるオプションを見ておきましょう。

オプション オプションの説明
-o,--orig コピー元のコンテナ名
-n,--name 一時的に起動するコンテナの名前
-d,--daemon コンテナをバックグラウンドで実行
-b,--bdir コンテナ内にバインドマウントするディレクトリ
-u,--user コンテナに接続するためのユーザ
-S,--key コンテナ内にコピーする SSH 公開鍵
-U,--union-type 使用する union filesystem
-k,--keep-data コマンド終了後にコンテナイメージを残す
-s,--storage-type 使用するストレージバックエンド

以上のオプションのうち、指定が必須のオプションは-oのみです。--storage-typeは現時点でマニュアルに記載がありません。筆者がパッチを送ってありますので、そのうちマニュアルにも記載されるはずです。

lxc-start-ephemeralコマンドの仕組みとフック

lxc-start-ephemeralコマンドがどのように一時的なコンテナを実現しているのかを少し詳しく見ていきましょう。あらためてバックグラウンドでコンテナを起動してみます。

$ sudo lxc-start-ephemeral -o ct01 -d
The ephemeral container is now started.

You can enter it from the command line with: lxc-console -n ct01-7iv1faqo
The following IP addresses have be found in the container:
 - 10.0.3.240

まずはコンテナ用のディレクトリを見てみましょう。

$ sudo ls -F /var/lib/lxc/ct01-7iv1faqo/
config  configured  delta0/  post-stop*  pre-mount*  rootfs/

見慣れたファイルの他にいくつか初めて見るファイルが存在しますね。

まずは設定ファイルであるconfigを見てみましょう。lxc-createコマンドを使ってコンテナを作成した時にも設定される項目は除いて、lxc-start-ephemeral特徴的な設定だけ挙げてみます。

$ sudo cat /var/lib/lxc/ct01-7iv1faqo/config
   :(略)
lxc.utsname = ct01-7iv1faqo
   :(略)
lxc.hook.pre-mount = /var/lib/lxc/ct01-7iv1faqo/pre-mount
lxc.hook.post-stop = /var/lib/lxc/ct01-7iv1faqo/post-stop
lxc.rootfs = /var/lib/lxc/ct01-7iv1faqo/rootfs

lxc.utsnamelxc.rootfslxc-start-ephemeralコマンドがランダムで付与したコンテナ名で設定されています。この2つの設定以外が初めて目にする設定ではないでしょうか。

LXCはコンテナを起動させようとした時からコンテナが停止するまでのいろいろな場面と、クローン時に必要な処理が行えるようにフックとしてプログラムやスクリプトを指定できるようになっています。lxc-start-ephemeralコマンドはこのフックを使って必要な処理を行っています。

第11回第12回でLXCの設定を紹介した時にはフックについては触れませんでした。

まずはこの2つのフックについて説明しましょう。

lxc-start-ephemeralコマンドのコンテナ起動前の処理

lxc-start-ephemeralコマンドは起動するコンテナのルートファイルシステムをunion filesystemを使って準備します。この準備をフックを使って行っています。

lxc.hook.pre-mountは、コンテナのルートファイルシステムが設定される前に実行されます。ルートファイルシステムが設定される前ですので、ルートファイルシステムに対する処理をここで行えます。この処理はコンテナのマウント名前空間で行われ、コンテナが停止する前にクリーンアップされますのでホストには影響を与えません。

lxc.hook.pre-mountで指定されているpre-mountスクリプトは、実行権限が付いたシェルスクリプトで以下のような内容です。

 1: #!/bin/sh
 2: LXC_DIR="/var/lib/lxc/ct01-7iv1faqo"
 3: LXC_BASE="ct01"
 4: LXC_NAME="ct01-7iv1faqo"
 5: mkdir -p /var/lib/lxc/ct01-7iv1faqo/delta0 /var/lib/lxc/ct01-7iv1faqo/rootfs/
 6: mount -n -t tmpfs -o mode=0755 none /var/lib/lxc/ct01-7iv1faqo/delta0
 7: mount -n -t overlayfs -oupperdir=/var/lib/lxc/ct01-7iv1faqo/delta0,lowerdir=/var/lib/lxc/ct01/rootfs none /var/lib/lxc/ct01-7iv1faqo/rootfs/
 8:
 9: [ -e $LXC_DIR/configured ] && exit 0
10: for file in $LXC_DIR/rootfs/etc/hostname \
11:             $LXC_DIR/rootfs/etc/hosts \
12:             $LXC_DIR/rootfs/etc/sysconfig/network \
13:             $LXC_DIR/rootfs/etc/sysconfig/network-scripts/ifcfg-eth0; do
14:         [ -f "$file" ] && sed -i -e "s/$LXC_BASE/$LXC_NAME/" $file
15: done
16: touch $LXC_DIR/configured

順に見ていきましょう。

  • 2~4行目:変数の定義です。
  • 5行目:delta0ディレクトリとrootfsディレクトリを作成します。
  • 6行目:rootfsをtmpfsとしてマウントします。
  • 7行目:上層側ディレクトリとしてdelta0を、下層側ディレクトリとしてコピー元であるコンテナのイメージを指定してrootfsをoverlayfsとしてマウントします。
  • 9行目:既にconfiguredファイルが存在する場合は10行目以下の処理を行いません。
  • 10行目~15行目:コンテナのルートファイルシステム以下に存在しているファイルに記載されているホスト名を、これから起動するコンテナ名で書き換えます。
  • 16行目:configuredファイルを作成する。

という処理を行っています。

つまりoverlayfsが変化を記録するために使う上層側ディレクトリとしてtmpfsを使い、これから起動するコンテナのルートファイルシステムをoverlayfsで準備します。上層側ディレクトリがtmpfsですから、コンテナの名前空間が消滅するコンテナ終了時に中身は消えてしまいます。そのため、起動後に行ったコンテナイメージに対する変更が消えてしまうわけです。

overlayfsを使っているので、元となるコンテナイメージを全てコピーする必要はありません。このようにして素早くコンテナのイメージを準備しています。

lxc-start-ephemeralコマンドのコンテナ終了後の処理

lxc-start-ephmeralコマンドを使って作成されたコンテナでは、もう一つフックが使われていましたのでそちらも見ておきましょう。

コンテナ終了後に後始末をするため、lxc.hook.post-stopという設定がされていました。これはコンテナがシャットダウンされた後にホストの名前空間上で実行されるフックです。

lxc.hook.post-stop = /var/lib/lxc/ct01-7iv1faqo/post-stop

ここで指定されているファイルの中を見てみましょう。

#!/bin/sh
[ -d "/var/lib/lxc/ct01-7iv1faqo" ] && rm -Rf "/var/lib/lxc/ct01-7iv1faqo"

一時的なコンテナ用に作成されたディレクトリを消去しています。このようにlxc.hook.post-stopを使って、コンテナ終了後の後処理が行えますね。

lxc-start-ephemeralのその他の使い方

コンテナ名の指定

デフォルトではlxc-start-ephemeralコマンドは、起動する一時的なコンテナの名前を、元のコンテナの名前の後に"-"(ハイフン)とランダムな文字列を続けた形で自動的に付与します。自動的に名前を生成させる代わりに-nオプションで名前を付けられます。

コマンドの実行

lxc-start-ephemeralコマンドはシステムコンテナだけでなく、アプリケーションコンテナも実行できます。アプリケーションコンテナを実行する場合は、実行したいコマンドを指定します。

$ sudo lxc-start-ephemeral -o ct01 -- /bin/hostname
ct01-d6cz_82i
$ sudo lxc-ls
ct01

以上の例は/bin/hostnameコマンドを実行しました。hostnameコマンドが実行されるとコンテナは終了しますので、lxc-lsコマンドで確認してもコンテナは存在しません。

$ sudo lxc-start-ephemeral -o ct01 -- /bin/bash
root@ct01-lk_clba7:~# (必要な処理を行う)
root@ct01-lk_clba7:~# exit
exit
$ sudo lxc-ls
ct01

以上の例はbashを実行しています。lxc-startで作成したコンテナの内部を、作成したコンテナを起動する前に確認する必要がある場合に便利かも知れませんね。こちらも必要な処理が終わりexitするとコンテナは消去されています。

コマンドを指定した場合、ホストOSのカーネルがlxc-attachコマンドが実行できる環境である場合はlxc-attachで実行した場合と同等の処理でコマンドを実行します。

lxc-attachコマンドが動かなような古いカーネルを使っているような場合は、ssh経由でコマンドが実行されます。この場合、後で説明するユーザやssh公開鍵をオプションで設定できます。

現時点でのマニュアルでは、コマンド実行はattachを使わずにssh経由で行われるという記載があります。しかしコードを確認してみると使用の可否を判断してどのようにコマンドを実行するかを決定しています。※1

ユーザの指定

lxc-start-ephemeralでコマンドを実行する際に、そのコマンドを実行するユーザを指定できます。

もちろん、起動してくるコンテナでそのユーザが存在しており、そのユーザに対する必要な設定が行われている必要があります。

LXCのubuntuテンプレートやdownloadテンプレートでubuntuコンテナを作成した場合は、ubuntuユーザが作成されていますので、ubuntuユーザを指定してコマンドを実行してみましょう。ユーザを指定する場合は--userオプションを使います。

$ sudo lxc-start-ephemeral -o ct01 --user=ubuntu  -- /bin/bash
ubuntu@ct01-0d5di0mf:~$ id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),27(sudo)
ubuntu@ct01-0d5di0mf:~$ exit
exit
$ 

以上のように指定したubuntuユーザでbashが実行されていますね。存在しないユーザで実行すると、コマンドを実行してもエラーになりますからすぐにコンテナが終了します。

sshでアクセスする時は、--keyオプションを使ってユーザの公開鍵をコンテナ内にコピーできます。

ストレージタイプの指定

lxc-start-ephemeralコマンドは、先に説明したようにoverlayfsもしくはaufsを使って一時的なコンテナを作成します。先に紹介したpre-mountスクリプトでは、一時的なコンテナ用のデータはoverlayfsの上層側ディレクトリに保存されるように設定されていました。

この上層側ディレクトリをtmpfsで作成する代わりに、--storage-typeオプションでdirを指定して普通のディレクトリにできます。

もちろんコンテナの実行が終了した時点でコンテナ自体が消去される場合には、ストレージタイプでdirを指定してもtmpfsを指定してもあまり変わりはないように思えます。dirが選択肢にある理由は、後で説明する--keep-dataオプションで使うためです。

使用するUnion filesystemのタイプの指定

一時的なコンテナを作成する際に使用するUnion filesystemのタイプを--union-typeで指定できます。overlayfsaufsを指定できます。もちろん、指定したファイルシステムが使える環境である必要があります。

永続的なコンテナにする

--keep-dataオプションを付けてlxc-start-ephemeralコマンドを実行すると、コンテナ終了後にコンテナイメージを残せます。この場合、tmpfsを使うわけにはいきませんので普通にディレクトリを使います。

--keep-dataを使うと、lxc-cloneコマンドでoverlayfsもしくはaufsを使ったスナップショットクローンを指定した場合と同じような状態になりますね。

コンテナ内にホストのディレクトリをマウントする

ホスト上のディレクトリをコンテナ内にバインドマウントできます。これは--bdirオプションを使います。指定したパスと同じコンテナ内のパスにバインドマウントされます。

$ touch on_host
$ sudo lxc-start-ephemeral -o ct01 --user=ubuntu --bdir=/home/ubuntu -- /bin/bash
ubuntu@ct01-212j96rq:~$ pwd
/home/ubuntu
ubuntu@ct01-212j96rq:~$ ls
on_host

上記の例では、まず/home/ubuntu/on_hostというファイルを作成して、/home/ubuntuをバインドマウントする指定でコンテナを起動しています。コンテナ内の/home/ubuntuには確かにon_hostファイルがあります。

これは先に説明したlxc.hook.pre-mountを使ってマウントしています。

$ sudo cat /var/lib/lxc/ct01-212j96rq/pre-mount | grep bind
mount -n --bind /home/ubuntu /var/lib/lxc/ct01-212j96rq/rootfs//home/ubuntu

pre-mountスクリプトを確認してみると、以上のようにバインドマウントする行が存在します。

まとめ

今回は既に存在するコンテナの一時的なコピーを作成し、起動するlxc-start-ephemeralコマンドを紹介しました。また、lxc-start-ephemeralコマンドが処理中に使用しているLXCのフック機能を簡単に紹介しました。

次回は今回説明していないフック機能を紹介する予定です。

おすすめ記事

記事・ニュース一覧