前回 を書いてからいつの間にか4ヵ月も経ってしまいました。この間にLXC を含むlinuxcontainers.org の各プロジェクトからは、いずれもバージョンを合わせた、新しいstableリリースとなる2.0が4月にリリースされています。
これまではLXCというソフトウェアだけが正式にリリースされていましたが、このタイミングでLXC 、LXD 、LXCFS というソフトウェアが同時にリリースされました。以前紹介したことがあるCGManager に関しては、新しいカーネルのリリースに含まれた機能とLXCFS のリリースにより不要となったので、今後はメンテナンスのみが行われます。
これらのstableリリースとなる2.0は、いずれもUbuntuの新しい長期サポート版である16.04に含まれています。
LXC 2.0や、その他のソフトウェアについては、今後機会があればこの連載でも取り上げたいと思います。
LXC 1.1で導入された大きな新機能は、コンテナ内で起動するinitとしてsystemdがサポートされたこと、CRIUを使ったチェックポイント・リストア機能がサポートされたことでした。
第31回 で紹介したのは設定項目に関する変更点でした。このうちのいくつかはsystemdをサポートするために内部の実装が変更されたことに伴う変更でした。
そして、まだ紹介していないLXC 1.1の新機能がCRIUを使ったチェックポイント・リストア機能です。今回はこの機能について紹介しましょう。
なお、今回は新たにリリースされたUbuntu 16.04 LTS上でLXC 2.0を使っていますが、LXC 1.1でも同じように実行できるはずです。
チェックポイント・リストアとCRIU
チェックポイント機能は、実行中のプロセスの状態をファイルに保存します。リストア機能は、そのファイルに保存された情報を使ってプロセスを再開させ、保存した時点の状態に戻します。
チェックポイント・リストア処理に必要な機能はLinuxカーネルに実装されています。そしてこの機能を実際に使ってチェックポイント・リストア処理を行うために、OpenVZの開発チームによって開発されているツールが、"Checkpoint/Restore In Userspace"の頭文字を取って命名されているCRIU というツールです。
チェックポイントの際、CRIUは主に/proc
以下からプロセスに関する情報を収集し、それをファイルに保存します。リストア処理の際は、ファイルから各種リソースの情報を取得してリストアし、プロセスを生成します。
CRIUはOpenVZ関連のプロジェクトですが、OpenVZに特化しているわけではありません。LXCやDockerというコンテナだけでなく、Linux上で動作しているプロセス一般に使えます。
CRIUが動作する環境
CRIUはカーネル3.11以上で動作します。
動作条件を満たすディストリビューションでは、パッケージが準備されているかもしれません。その場合はパッケージからインストールすれば簡単に使用する環境が整います。CRIUのページにパッケージ情報のページ がありますのでチェックしてみてください。
パッケージがなくても、比較的簡単にソースからビルドできます。こちらも公式ページに手順が説明されています 。この場合、CRIUのインストールが済んだあとに、カーネルがCRIUに必要な機能をサポートしているかをチェックできます。
$ sudo criu check
Looks good.
CRIUの用途
CRIUの用途として考えられることは、CRIUのページにも"Usage scenarios" として様々なシナリオが挙げられているようにいろいろあります。
ここに挙がっているシナリオのうち、LXCがチェックポイント・リストア処理をサポートする目的は主に、
ライブマイグレーション
起動が遅いサービスのスピードアップ
の2つでしょう。
ライブマイグレーションについては、大規模な仮想マシン環境ですでに使われている場合は多いでしょうから、みなさんもイメージしやすいと思います。あるコンテナホスト上で起動しているコンテナを、チェックポイント機能を使って実行イメージを保存し、保存したイメージを別ホストにコピーしてそのイメージから起動することにより、起動したままホスト間を移動させます。
後者の「起動が遅いサービスのスピードアップ」は、たとえばJavaサーブレットを使う環境などで、アプリの起動に非常に時間がかかるケースがあります。このような場合、起動をスピードアップするために、アプリが起動した状態でCRIUを使ってチェックポイント機能を実行し、実行イメージを保存しておき、起動時にそのイメージから起動できます。
CRIUコマンド
CRIUコマンドを試す前に、CRIUコマンドの実行方法を確認しておきましょう。
criuでは、criu
のあとに行いたい処理をコマンドとして指定し、そのあとにオプションを指定する形で実行します。チェックポイント処理の場合はcriu dump
、リストア処理の場合はcriu restore
となります。
criuコマンドはいろいろな機能を持っており、バージョンによっても機能が変わりますので、ここでは今回使用するコマンド、オプションのみを紹介します。
表1 criuのコマンド(主なもの)
コマンド
機能
dump
チェックポイント処理を実行
restore
リストア処理を実行
check
カーネルがCRIUに必要な機能をサポートするかどうかチェック
コマンドのあとに指定するオプションのうち、dump
とrestore
で共通に使うものは以下のようなオプションです。
表2 共通オプション(主なもの)
オプション
意味
-D / --images-dir
チェックポイントデータの保存ディレクトリ
-o / --log-file
ログファイル名
-v
ログレベル。vの数でレベルを指定でき、最大4まで
-j / --shell-job
シェルから実行したプロセスを対象とする場合に指定
dumpコマンドでは以下のようなオプションを使用します。チェックポイント対象のプロセスを指定する必要がありますので、-t
は必ず指定します。
表3 dumpコマンドで使う主なオプション
オプション
意味
-t / --tree
チェックポイント対象のプロセスの指定
restoreコマンドは、-d
オプションを指定せずにcriu restore
を実行すると、リストアしたプロセスはcriuコマンドの子プロセスになりますので、通常は-d
を指定すると良いでしょう。
表4 restoreコマンドで使う主なオプション
オプション
意味
-d / --restore-detached
リストア後プロセスをcriuから切り離す
CRIUを使ったプロセスのチェックポイントとリストア
それでは実際にCRIUを使っていきましょう。
今回の実行例はUbuntu 16.04 LTSを使用しています。CRIUはパッケージからインストールしており、バージョンは2.0です。
$ lsb_release -d
Description: Ubuntu 16.04 LTS
$ uname -r
4.4.0-21-generic
$ criu --version
Version: 2.0
criuはパッケージインストールしていますので、動作して当然なのですが、一応環境がCRIUをサポートしているかチェックしてみましょう。
$ sudo criu check
Warn (libnetlink.c:65): ERROR -2 reported by netlink
Warn (libnetlink.c:65): ERROR -2 reported by netlink
Warn (sockets.c:702): The current kernel doesn't support packet_diag
Warn (libnetlink.c:65): ERROR -2 reported by netlink
Warn (sockets.c:712): The current kernel doesn't support netlink_diag
Looks good.
警告は出るものの"Looks good"と出ているようにCRIUが動作する環境であることがわかります。
ではプロセスの状態をファイルに保存し、保存したファイルからリストアしてみましょう。チェックポイント・リストア処理の対象として、以下のようなシェルスクリプトを準備しました。1秒おきにファイルにインクリメントした数字を追加していくだけの簡単なスクリプトです。
#!/bin/sh
n = 0
while :
do
sleep 1
n = ` expr $n + 1 `
echo $n >> testfile
done
このスクリプトを実行し、ファイルへ数字が書き込まれていく様子を確認します。pstreeコマンドでプロセスの様子も確認しておきます。
$ sh test.sh &
[1] 16246
$ tail -f testfile
1
2
3
4
5
6
^C
$ pstree -p 15599
bash(15599)─┬─pstree(16265)
└─sh(16246)───sleep(16264)
さてそれではチェックポイント機能でプロセスの状態をファイルに保存します。ファイルを保存するディレクトリを作成したあと、criuコマンドでチェックポイント処理を実行します。
$ mkdir criu
$ sudo criu dump -t 16246 -vvvv -D criu/ -o dump.log -j
[1]+ Killed sh test.sh
$ pstree -p 15599
bash(15599)───pstree(16308)
$ sudo tail -n 1 criu/dump.log
(00.021306) Dumping finished successfully
以上のようにチェックポイント処理を実行すると、デフォルトではチェックポイント対象のプロセスは停止します。tail -f testfile
を実行すると、インクリメントされた数字の出力が止まっていることが確認できます。
ファイルが保存されているディレクトリを確認してみましょう。
$ ls -F criu/
core-16246.img ids-16246.img pages-1.img stats-dump
core-16297.img ids-16297.img pages-2.img stats-restore
dump.log inventory.img pstree.img tty.img
fdinfo-2.img mm-16246.img reg-files.img tty-info.img
fdinfo-3.img mm-16297.img restore.log
fs-16246.img pagemap-16246.img sigacts-16246.img
fs-16297.img pagemap-16297.img sigacts-16297.img
プロセスの状態を保存したファイルが確認できます。今回の例ではシンプルなシェルスクリプトの実行状態を保存しただけですので、ファイルの数は少ないですが、システムコンテナや子プロセスが多数存在するようなプロセスを保存した場合はもっと多数のファイルが保存されます。
では、チェックポイント処理は成功したようなので、この生成されたファイルからリストア処理を実行してみましょう。
$ sudo criu restore -vvvv -D criu/ -o restore.log -j -d
$ ps axjf
: (略)
1 16246 16313 15599 pts/0 20037 S 1001 0:00 sh test.sh
16246 20036 16313 15599 pts/0 20037 S 1001 0:00 \_ sleep 1
psコマンドを実行すると、dumpコマンド実行後存在しなかったプロセスが復活していることが確認できます。-d
でcriuコマンドから切り離したあと、criuコマンドは終了していますので、親プロセスのPIDは1となっています。
数字を出力していたファイルを確認すると、またインクリメントされた数字の出力が始まっていることが確認できました。
$ tail -f testfile
58
59
60
^C
異なるホスト間でのチェックポイント・リストア
前述の例はプロセスを実行したホスト上でチェックポイント処理を行い、同じホスト上でプロセスをリストアしました。
次にcriu dump
で取得したファイルを別のホストにコピーし、別のホスト上でプロセスをリストアしてみましょう。関連するファイルをコピーする以外は、実行する手順は同じです。リストア処理を実行する環境は、元と同じ環境にしておく必要がありますので、同じUbuntu 16.04 LTS環境を準備しました。
まずは先の例と同様にスクリプトを実行させて、チェックポイント処理を実行しましょう。
ubuntu@criu1:~$ sh test.sh &
[1] 1958
ubuntu@criu1:~$ sudo criu dump -t 1958 -vvvv -D criu/ -o dump.log -j
[1]+ Killed sh test.sh
ubuntu@criu1:~$ ls criu/
core-1958.img fs-1958.img mm-1958.img pages-2.img stats-dump
core-1996.img fs-1996.img mm-1996.img pstree.img tty.img
dump.log ids-1958.img pagemap-1958.img reg-files.img tty-info.img
fdinfo-2.img ids-1996.img pagemap-1996.img sigacts-1958.img
fdinfo-3.img inventory.img pages-1.img sigacts-1996.img
次に関連するファイルをrsyncコマンドを使ってリストア処理を実行するホスト上に転送しましょう。
ubuntu@criu1:~$ sudo rsync -a test.sh testfile criu ubuntu@criu2:/home/ubuntu/
転送先にファイルが転送されていることを確認します。
ubuntu@criu2:~$ ls -F
criu/ testfile test.sh
ubuntu@criu2:~$ ls criu
core-1958.img fs-1958.img mm-1958.img pages-2.img stats-dump
core-1996.img fs-1996.img mm-1996.img pstree.img tty.img
dump.log ids-1958.img pagemap-1958.img reg-files.img tty-info.img
fdinfo-2.img ids-1996.img pagemap-1996.img sigacts-1958.img
fdinfo-3.img inventory.img pages-1.img sigacts-1996.img
スクリプトの出力が記録されるファイルの最後の行を確認したあと、プロセスをリストアします。すると、きちんと停止した数字からインクリメントが始まっていることが確認できます。
ubuntu@criu2:~$ tail -n 1 testfile
17
ubuntu@criu2:~$ sudo criu restore -vvvv -D criu/ -o restore.log -j -d
ubuntu@criu2:~$ ps axjf | less
:
1 1958 1743 1511 pts/0 2092 S 1001 0:00 sh test.sh
1958 2091 1743 1511 pts/0 2092 S 1001 0:00 \_ sleep 1
ubuntu@criu2:~$ tail -f testfile
11
12
13
14
15
16
17
18
19
20
^C
異なるホスト間でもプロセスがリストアされることが確認できました。
lxc-checkpointコマンド
ここまでで紹介したCRIUを使った、コンテナのチェックポイント・リストア機能のサポートがLXC 1.1の大きな新機能のひとつでした。LXC 1.1で新たにチェックポイント・リストア処理を行うために追加されたコマンドがlxc-checkpointコマンドです。
LXCはチェックポイント・リストア処理を行う場合、内部ではcriu
コマンドに必要なオプションを指定して実行しています。
ちなみにlxcパッケージをインストールしても、依存関係でcriuパッケージはインストールされませんので、lxc-checkpointコマンドを使いたい場合はcriuパッケージをインストールしてください。
実際に実行例を見る前に、まずはlxc-checkpointコマンドのオプションを見ておきましょう。
表5 lxc-checkpointコマンドのオプション
オプション
意味
-r / --restore
リストア処理を実行
-D / --checkpoint-dir
チェックポイントデータの保存ディレクトリ
-s / --stop
チェックポイント後にコンテナを停止
-v / --verbose
CRIUのログ出力を冗長モードにする
-d / --daemon
リストア時、コンテナをバックグラウンド実行
-F / --foreground
リストア時、コンテナをフォアグラウンド実行
lxc-checkpointコマンドを使ったコンテナのチェックポイント・リストア
チェックポイント・リストア処理を行うには、コンテナに以下の設定が必要です。コンテナディレクトリ以下の設定ファイル(通常は/var/lib/lxc/[コンテナ名]/config
)に追加しましょう。
lxc.tty = 0
lxc.console = none
lxc.cgroup.devices.deny = c 5 :1 rwm
それでは、以上の設定を追加したコンテナを起動して、チェックポイント処理を実行してみましょう。チェックポイントのデータは/tmp/criu
に保存するとします。
root@criu01:~# mkdir /tmp/criu
root@criu01:~# lxc-start -n ct01
root@criu01:~# lxc-ls -f
NAME STATE AUTOSTART GROUPS IPV4 IPV6
ct01 RUNNING 0 - 10.0.3.196 -
root@criu01:~# lxc-checkpoint -n ct01 -D /tmp/criu -v -s -l debug -o log
root@criu01:~# lxc-ls -f
NAME STATE AUTOSTART GROUPS IPV4 IPV6
ct01 STOPPED 0 - - -
root@criu01:~# tail -n1 /tmp/criu/dump.log
(00.292169) Dumping finished successfully-
-s
オプションを付けて、チェックポイント後にコンテナは停止するように指定していますので、lxc-checkpointコマンドを実行した後はコンテナが停止しています。そして、/tmp/criu
以下に出力されているチェックポイントのログを見ると、チェックポイント処理が成功していることがわかります。
それでは、/tmp/criu
に保存したデータを使ってコンテナのリストア処理を実行してみましょう。ここではチェックポイント処理を行ったホストと別のホスト上にリストアしてみます。
異なるホスト間でチェックポイント・リストア処理を行うには、カーネル、CRIUコマンドなどは双方で同じ環境を準備しておく必要があるでしょう。ここでは別にもう一台のUbuntu 16.04 LTSホストを準備しました。
まずはリストア処理に必要なファイルをrsyncコマンドで転送しましょう。転送が必要なファイルは、チェックポイントで取得したダンプファイル群と、コンテナです。つまり/tmp/criu
と/var/lib/lxc/ct01
をrsyncで転送しましょう。
root@criu1:~# rsync -az /tmp/criu criu2:/tmp/
root@criu1:~# rsync -az /var/lib/lxc/ct01 criu2:/var/lib/lxc
実際にコンテナのマイグレーションを行う際は、事前にコンテナ自体は転送しておき、チェックポイント処理の後に再度変化したファイルだけを転送するというような処理を行って、ダウンタイムを短くすることになるでしょう。
それではリストア処理を実行してみましょう。
root@criu02:/tmp# lxc-checkpoint -n ct01 -r -D /tmp/criu -v -d -o log -l debug
root@criu02:/tmp# grep success /tmp/criu/restore.log
(00.259327) Restore finished successfully. Resuming tasks.
root@criu02:/tmp# lxc-ls -f
NAME STATE AUTOSTART GROUPS IPV4 IPV6
ct01 RUNNING 0 - 10.0.3.196 -
リストア処理のログと、lxc-lsコマンドでリストアが成功し、コンテナが起動していることが確認できました。
チェックポイント、リストア処理で実際にlxc-checkpointコマンドが内部的にどのようなcriuコマンドを実行しているかは、INFOレベルでログが出力されますので、興味がある方は確認してみてください。
非特権コンテナのチェックポイント・リストア
第16回 で紹介した、ユーザ名前空間を使った一般ユーザ権限で動く非特権コンテナについても、カーネルが4.4以上という新しいバージョンのカーネル上であれば、チェックポイント・リストア処理を行えます。
ただし、第17回 で紹介したような、一般ユーザ権限で起動したコンテナに関してはLXCではチェックポイント・リストア処理は行えません。これは、lxc-checkpointコマンドはroot権限が必要である一方で、LXCが提供するコンテナ操作コマンドは、コマンドを実行したユーザの権限で動いているコンテナの操作を行うように作られているためです。
では、どうやってLXCで非特権コンテナに対するチェックポイント・リストア処理を行うのかというと、rootで非特権コンテナを起動します。rootユーザがユーザ名前空間を作成し、コンテナ内のUID、GIDをホスト上の一般ユーザ権限のIDにマッピングすれば良いのです。これでrootがコンテナを起動しているにも関わらず、コンテナ自体は一般ユーザ権限で起動します。
このようなコンテナを作るために、特に特別な操作や設定が必要なわけではなく、第17回 で紹介したように非特権コンテナの設定と起動を行い、今回紹介したチェックポイント・リストア処理の操作を行うだけです。
まとめ
今回はCRIUとlxc-checkpointコマンドを簡単に紹介しました。
CRIUには今回紹介した他にも多数のオプションが存在し、いろいろなプロセスに対してチェックポイント・リストア処理ができるようになっていますので、興味がある方は公式ページ やオンラインマニュアル、ヘルプを参照してみてください。
lxc-checkpointコマンドは今回紹介した通り、LXCコンテナをcriuコマンドを使ってチェックポイント・リストア処理を実行するためのラッパー的なコマンドで、チェックポイント・リストア処理のみを行うシンプルなコマンドです。ライブマイグレーションなどの処理を行う際、チェックポイント・リストア処理以外にも必要な処理は、LXC付属のコマンドでは実装されていません。
このような目的にはLXCを使うのではなく、同じくlinuxcontainers.org 配下のプロジェクトであるLXD のような、実行したい処理を実装したソフトウェアを使うことになるでしょう。
LXDに関しては、いろいろなブログで触れられていたり、参考文献があったりしますが、機会があればこの連載でも紹介したいと思います。