Ubuntu Weekly Recipe

第574回LXD 3.0のスナップショットとマイグレーション

第521回から紹介しているLXD 3.0の基本的な使い方シリーズ。今回はスナップショットとリストア、それにコンテナのマイグレーションについて紹介します。

LXDコンテナのバックアップとリストア

LXDはシステムコンテナです。つまりDockerコンテナのように不変(immutable)で使い捨て(disposable)可能な使い方ではなく、むしろvirt-manager/KVMやVMWareのように、システムを立ち上げたあとにログインして様々な変更を加えることで、最終的なイメージを構築する使い方を想定しています。

今そこにあるLXDコンテナが、もう一度まったく同じ構成で構築可能かどうかは運用次第です。実運用で使っているコンテナであれば、おそらくcloud-initやAnsibleで再構築可能でしょう。ポリシーによっては、ドキュメントでフォローしているかもしれません。

ただし実験目的でちょっと作ったコンテナにそこまで手間をかけていられないかもしれません。また「試しに何かする」際に「前の状態を残しておきたい」ケースもよくあります。

そこでLXDでは、virt-manager/KVMやVMWareのような仮想マシンシステムと同じように、次のようなコンテナイメージのバックアップ・リストア機能を備えています。

  • 停止中のコンテナを他のホストへ移動するマイグレーション機能
  • 停止中のコンテナを他のホストへ複製するコピー機能
  • 稼働中のコンテナをステートレスに保存するスナップショット機能
  • 稼働中のコンテナをステートフルに保存するスナップショット機能
  • スナップショットされたコンテナをリストアする機能
  • スナップショットをベースイメージとしてエクスポートする機能
  • ベースイメージをインポートする機能
  • クラスタとして構成されたLXDホスト間でイメージを同期する機能
  • 稼働中のコンテナを他のホストへ移動するライブマイグレーション機能(実験的な機能)

今回はこのスナップショットを利用したバックアップとリストア、さらには別ホストとのマイグレーションについても紹介しましょう。

今回もUbuntu 18.04 LTSとLXD 3.0.xの環境をベースに紹介します。debパッケージ版とsnap版でほぼ違いはありませんが、もし違いがある場合は都度説明します。

LXDのスナップショット機能

コンテナのスナップショットを取る方法は至極簡単です。まずは適当なコンテナを作っておきます。

$ lxc launch ubuntu:18.04 kasumi
Creating kasumi
Starting kasumi

$ lxc info kasumi
Name: kasumi
Remote: unix://
(中略)
Resources:
(中略)
    lo:
      Bytes received: 1.02kB
      Bytes sent: 1.02kB
      Packets received: 12
      Packets sent: 12

lxc infoはスナップショット作成後との比較のために表示しています。

さて、現在の状態のスナップショットを取ってみましょう。スナップショットはコンテナが起動状態でも取得できます。取得方法はlxc snapshot コンテナ名 スナップショット名です。スナップショット名を省略した場合は「snap0」から始まる連番の名前が自動的に付与されます。

$ lxc snapshot kasumi arisa
$ lxc info kasumi
(中略)
Snapshots:
  arisa (taken at 2019/06/16 08:24 UTC) (stateless)

コンテナの情報にスナップショットが追記されていることがわかりますね。

さっそく作ったスナップショットをリストアしてみましょう。リストアされたことがわかるよう、あらかじめコンテナに変更を加えておきます。

$ lxc exec kasumi touch /home/ubuntu/snap0
$ lxc exec kasumi ls /home/ubuntu/
snap0
$ lxc exec kasumi uptime
 09:02:37 up  2:49,  1 user,  load average: 0.07, 0.04, 0.00

最後のuptimeは、kasumiコンテナが起動していた時間を確認しています。

実はLXDの普通のリストアは、対象となるコンテナを停止させる必要があります。もし起動中のコンテナに対してリストアを実行した場合、一度コンテナを停止し(シャットダウンし⁠⁠、リストアを実行した上で、自動的に再起動します。たとえばSSHでログインしているなら、リストアによって一度切断されます。

一時的であれコンテナが停止すると困る場合は、--statefulオプションを利用してステートフルなスナップショット・リストアを使いましょう[1]⁠。

リストアはlxc restore コンテナ名 スナップショット名で実施します。

$ lxc restore kasumi arisa
$ lxc exec kasumi ls /home/ubuntu/
$ lxc exec kasumi uptime
 09:11:56 up 0 min,  0 users,  load average: 1.19, 0.30, 0.09

先ほど作成したsnap0が、リストアによって削除されていることがわかります。さらにリストア対象のコンテナが再起動したために、uptimeが0分に戻っています。

LXDコンテナはシステムコンテナとは言え、カーネルの起動がスキップできるため、ベースコンテナの起動時間は数秒です。よって大抵のケースにおいては、再起動のダウンタイムはそこまで気にするほどの長さではないでしょう。もちろんcloud-initや、他のサービスによっては起動時間は長くなりますので、プロダクション用途の場合はなんらかの対策が必要になります。

スナップショットの削除はコンテナの削除と同様にlxc deleteコマンドを利用します。スナップショット単体で削除したい場合は、⁠コンテナ名/スナップショット名」と指定してください。

$ lxc delete kasumi/arisa

ちなみにLXD 3.8からはコンテナの自動スナップショット設定が追加されました。この設定を利用すると、特定のコンテナに対して定期的に任意の名前でスナップショットを作成できます。

コンテナのバックアップ

スナップショット機能はコンテナのバックアップにも利用できます。

具体的にはコンテナのルートファイルシステムをアーカイブしたい場合、コンテナ以外にもスナップショットを指定できるのです。つまり特定のスナップショットをリストアすることなく、他のLXDホストでそのスナップショットを復活する際にこの機能を活用できます。

まずは先ほどと同様にkasumiコンテナベースで、snap1ファイルを作成したrimiコンテナを作成しましょう。

$ lxc exec kasumi touch /home/ubuntu/snap1
$ lxc exec kasumi ls /home/ubuntu/
snap1
$ lxc snapshot kasumi rimi

次にスナップショットをコンテナイメージに変換します。コンテナイメージはインスタンスを作る際のベースイメージです。ベースイメージは別のコンテナインスタンスを作る際にも利用できます。たとえばLXDホストで初めてコンテナを作る際は、大抵の場合はUbuntuのベースイメージをインターネット経由でダウンロードしているはずです。

$ lxc publish kasumi/rimi --alias rimi
Container published with fingerprint: a859c8bb8d9e88f542dc1eb94757498745deffbaa0fdea8e2a0cbfa3cc1e0f89

コンテナイメージの作成にはlxc pulbish コンテナ名を使用します。コンテナ名の部分にはコンテナ名単体を指定すればそのコンテナを、スナップショット名も追加すればスナップショットをイメージに変換します。

また--aliasオプションで別名をつけることが可能です。別名をつけておくと、今後そのコンテナを指定する際に操作が楽になります。別名をつけなかった場合は、フィンガープリントでイメージを指定しなくてはなりません。

実際に作成されたイメージを確認しましょう。

$ lxc image list
+-------+--------------+--------+---------------------------------------------+--------+----------+-------------------------------+
| ALIAS | FINGERPRINT  | PUBLIC |                 DESCRIPTION                 |  ARCH  |   SIZE   |          UPLOAD DATE          |
+-------+--------------+--------+---------------------------------------------+--------+----------+-------------------------------+
| rimi  | a859c8bb8d9e | no     |                                             | x86_64 | 223.23MB | Jun 16, 2019 at 11:09am (UTC) |
+-------+--------------+--------+---------------------------------------------+--------+----------+-------------------------------+
|       | c234ecee3baa | no     | ubuntu 18.04 LTS amd64 (release) (20190604) | x86_64 | 177.97MB | Jun 15, 2019 at 12:06pm (UTC) |
+-------+--------------+--------+---------------------------------------------+--------+----------+-------------------------------+

「c234ecee3baa」はkasumiコンテナを作成したときにダウンロードしたUbuntu 18.04 LTSのベースイメージです。rimiスナップショットとサイズが異なるのは、おそらくインスタンス化の際に/var/logなどが生成されたからでしょう。

もちろん新しく作ったコンテナイメージをベースに新しいコンテナを作ることも可能です。

$ lxc launch rimi tae
Creating tae
Starting tae
$ lxc exec tae ls /home/ubuntu/
snap1

taeコンテナには最初からsnap1ファイルが作られていますね。このようにコンテナ・スナップショットのイメージ化もバックアップとして利用できます。

次は先ほど作ったコンテナをアーカイブにしてしまいましょう。

$ lxc image export rimi rimi_archive
Image exported successfully!

アーカイブ化のコマンドはlxc image export イメージ名 アーカイブ名です。イメージ名には先ほどつけた別名はもちろんのこと、別名がない場合はフィンガープリントを指定します。アーカイブ名は出力先のファイル名です。実際には「アーカイブ名.tar.gz」の名前がついたファイルが作られます。

$ ls -lh rimi_archive.tar.gz
-rw-rw-r-- 1 shibata shibata 224M  6月 16 20:28 rimi_archive.tar.gz

内容はルートファイルシステムをただtar.gzでアーカイブしたものです。またトップディレクトリにはmetadata.yamlやtemplatesというコンテナに関する情報が記録されたファイルやディレクトリも含まれています。いずれにせよルートファイルシステムであることには変わりないので、LXDに限らずいろいろな用途に使えます。

systemd-nspawnで使用する

たとえば第491回で紹介しているような、systemd-nspawnを使ってみましょう。

$ mkdir rimi
$ sudo tar xvf rimi_archive.tar.gz -C rimi/
$ ls rimi
metadata.yaml  rootfs  templates

$ sudo apt install systemd-container
$ sudo systemd-nspawn -b --network-bridge=lxdbr0 -D rimi/rootfs/
Spawning container rootfs on /home/shibata/rimi/rootfs.
Press ^] three times within 1s to kill container.
(中略)
Ubuntu 18.04.2 LTS kasumi console

kasumi login:

--network-bridge=lxdbr0オプションをつけることで、LXDが作成するブリッジインターフェースであるlxdbr0に接続したネットワークインターフェースを作成します。このオプションか他の方法でネットワーク接続しておかないと、DHCPでコンテナにアドレスが渡されないので注意してください。

さて、Ubuntuのコンテナイメージにはubuntuアカウントは作られているものの、パスワードは無効化されています。またSSHサーバーは動いているものの、公開鍵認証のみとなっています。つまりこのままだとログインできません。

方法はいくつかあるのですが、ここではssh-import-idコマンドを使って、対象となるルートファイルシステムのホームディレクトリにauthorized_keysを作ってしまいましょう。

$ ssh-import-id -o rimi/rootfs/home/ubuntu/.ssh/authorized_keys gh:m-shibata

「gh:m-shibata」を指定することで、GitHub上のm-shibataアカウントの公開鍵を取得し、-oで指定したファイルに保存してくれます。もちろんcatなどで直接authorized_keysを編集しても良いですし、/etc/passwdを編集するという方法もあります。

起動したコンテナのIPアドレスはmachinectlコマンドで確認できます。

$ sudo machinectl
MACHINE CLASS     SERVICE        OS     VERSION ADDRESSES
rootfs  container systemd-nspawn ubuntu 18.04   10.174.119.197…

1 machines listed.

あとはubuntu@10.164.119.197にログインするだけです。ログイン後は普通のコンテナとして操作できます。もし終了したいならコンテナの中から、shutdownコマンド等を実行してください。

もしログインしていない環境からシャットダウンしたいなら、上記と同じようにmachinectlコマンドを利用できます。

$ sudo machinectl poweroff rootfs

他のLXDインスタンスで利用する

アーカイブ化したコンテナイメージは、他のLXDインスタンスにもインポート可能です。

$ lxc image import rimi_archive.tar.gz
Image imported with fingerprint: a859c8bb8d9e88f542dc1eb94757498745deffbaa0fdea8e2a0cbfa3cc1e0f89
$ lxc image list
+-------+--------------+--------+------------------------------------+--------+----------+------------------------------+
| ALIAS | FINGERPRINT  | PUBLIC |            DESCRIPTION             |  ARCH  |   SIZE   |         UPLOAD DATE          |
+-------+--------------+--------+------------------------------------+--------+----------+------------------------------+
|       | a859c8bb8d9e | no     | Ubuntu 18.04 LTS server (20190604) | x86_64 | 223.23MB | Jun 16, 2019 at 2:00pm (UTC) |
+-------+--------------+--------+------------------------------------+--------+----------+------------------------------+

これだけですね。もちろんpublishのときと同じように--aliasオプションで別名をつけられます。

ちなみにイメージをアーカイブしたLXDインスタンスでは、フィンガープリントが衝突してしまうため、そのアーカイブをインポートできません。言い方を変えると、一度インスタンスから衝突するイメージを削除してしまえば、アーカイブからのインポートが可能になります。

LXDコンテナのマイグレーション

単純にあるLXDホストで動いているコンテナを、別のLXDコンテナで動かしたいだけなら、LXD API over HTTPSを利用したマイグレーションという方法があります。ただし2台以上のホストの間でマイグレーションを行うには事前設定が必要です。

まず対象のホストのうちいずれか1台のLXDコンテナのリモートアクセスを有効化します。今回のようなマイグレーションの場合は、マイグレーション元・先どちらのリモートアクセスを有効化してもかまいません。本記事ではkasumiコンテナが存在していたほうのホストをリモート側、リモート側にアクセスするホストをクライアント側とし、リモート側のリモートアクセスを有効化することにしましょう。

remote$ lxc config set core.https_address "[::]"
remote$ lxc config set core.trust_password NANIKA_PASSWORD
remote$ lxc info | grep certificate_fingerprint
  certificate_fingerprint: 35dd575685cb4cd6474527e3b3b146edb36ba58c15552498cf99acb992b1934e

core.https_addressにはリモートホストでbindするIPアドレスを設定します。上記のように[::]と設定しておけば、リモートホスト上のすべてのアドレスにbindします。また末尾に:443と加えることで特定のポートを指定可能です。未指定の場合は8443番ポートを利用します。

core.trust_passwdにはクライアント側からこのリモートLXDホストにアクセスする際のパスワードを指定します。

最後に表示しているのはリモートホストの証明書のフィンガープリントです。クライアント側から接続する際に正しいリモートに接続しようとしているかを確認する際に使用します。

クライアント側は、lxc remote addコマンドでリモート側のアドレスを指定します。ここではホスト名を指定していますが、IPアドレスでもかまいません。

client$ lxc remote add saya saya.example.com
Certificate fingerprint: 35dd575685cb4cd6474527e3b3b146edb36ba58c15552498cf99acb992b1934e
ok (y/n)? y
Admin password for saya:
Client certificate stored at server:  saya

「Certificate fingerprint」にリモート側のフィンガープリントが表示されているので、先ほどリモート側で表示したそれと一致しているか確認してください。問題なければリモート側で設定したパスワードを入力し、登録完了です。

リモートLXDは「リモート名:」で指定します。最後のコロンが重要です。コマンドによっては最後のコロンがないとコンテナ名として認識してしまうので注意してください。

client$ lxc info saya:
config:
  core.https_address: '[::]'
  core.trust_password: true
(中略)
  storage_version: "4.4"
  server_clustered: false
  server_name: ubuntu-nuc
  project: ""

さっそくリモートのコンテナをコピーしてみましょう。今回はコールドマイグレーションなので、リモート側のkasumiコンテナは停止しておいてください。ちなみにリモート側のLXDサービスにアクセス可能なので、クライアント側からもリモートのkasumiコンテナを操作できます。

リモート側のkasumiコンテナの停止
client$ lxc stop saya:kasumi
client$ lxc list saya:
+--------+---------+-----------------------+----------------------------------------------+------------+-----------+
|  NAME  |  STATE  |         IPV4          |                     IPV6                     |    TYPE    | SNAPSHOTS |
+--------+---------+-----------------------+----------------------------------------------+------------+-----------+
| kasumi | STOPPED |                       |                                              | PERSISTENT | 1         |
+--------+---------+-----------------------+----------------------------------------------+------------+-----------+
| tae    | RUNNING | 10.174.119.235 (eth0) | fd42:d66a:76d:c9a8:216:3eff:fe49:3b67 (eth0) | PERSISTENT | 0         |
+--------+---------+-----------------------+----------------------------------------------+------------+-----------+

リモートからローカルへkasumiコンテナをコピー
client$ lxc copy saya:kasumi kasumi
client$ lxc info kasumi
(中略)
Snapshots:
  rimi (taken at 2019/06/16 11:07 UTC) (stateless)

スナップショットごと移動されていることがわかりますね。copyコマンドはさまざまなオプションを渡すことで、コピー先の状態をコントロールできます。詳しいことはlxc copy -hを確認してください。

copyコマンドはコピー元にコンテナを残します。もしコピー元からコンテナを残したくない場合は、copyの代わりにmoveコマンドを利用します。ちなみにローカルのコンテナ名やスナップショット名を変更したい場合にもmoveコマンドを利用できます。

LXDのライブマイグレーション

第459回の最後でも紹介したように、LXDはコンテナのライブマイグレーションにも実験的に対応しています[2]⁠。これはコンテナをステートフルな状態で保存し、そのままホスト間を移動して、移動先で再稼働させるモードです。また、同様にコンテナのステートフルなスナップショット・リストアにも対応しています。

ライブマイグレーション機能を有効化するには、CRIUパッケージが必要です。Ubuntu 18.04 LTSのサーバー版のように、DebianパッケージとしてLXDをインストールしている場合は、次のようにインストールしてください。

$ sudo apt install criu

snapパッケージ版のLXDを利用している場合は、CRIUはインストール済みなので、snapコマンドから機能を有効化します。また、有効化後にLXDサービスを再起動する必要があります。

$ sudo snap set lxd criu.enable=true
$ sudo snap restart lxd

上記はローカルとリモート両方で設定しておいてください。

また、ローカルとリモートの両方でネットワークの設定をあわせておく必要もあります。リモート側でlxc network show lxdbr0を実行した結果を記録しておき、ローカル側でlxc network edit lxdbr0を実行して、ipv4.addressとipv6.addressを揃えておけば良いでしょう。

実際にsayaで動いているtaeコンテナをローカルにマイグレーションしてみましょう。

client$ lxc list saya:tae
+------+---------+-----------------------+----------------------------------------------+------------+-----------+
| NAME |  STATE  |         IPV4          |                     IPV6                     |    TYPE    | SNAPSHOTS |
+------+---------+-----------------------+----------------------------------------------+------------+-----------+
| tae  | RUNNING | 10.174.119.235 (eth0) | fd42:d66a:76d:c9a8:216:3eff:fe49:3b67 (eth0) | PERSISTENT | 0         |
+------+---------+-----------------------+----------------------------------------------+------------+-----------+

client$ lxc move saya:tae tae

これで本来はマイグレーション完了なはずです。はずではあるのですが、環境によって成功したり失敗しました。

ライブマイグレーションを実施するには、リモートとホストでLXDの設定を合わせておく必要があるなど、まだまだ事前準備が必要な状況です。あくまで実験的な機能と考え、普段はコールドなマイグレーションを活用してください。

なお、LXD 3.0からはクラスタリング機能が追加されました。今回は特定のホストのLXDサービスだけ公開し、それ以外のホストからアクセスする形態をとりましたが、クラスタリング機能を使うと複数のホストをひとつのLXDサービスのように見せることが可能になります。クラスタリング機能を使うなら、ホスト間のストレージやネットワーク設定も自動的に同期を取ることになるので、コンテナの移動がより便利になることでしょう。

おすすめ記事

記事・ニュース一覧