LXD 2.0: リモートホストとコンテナマイグレーション [6/12]

## この文書について

この文書は、連載記事「[LXD 2.0: Blog post series](https://www.stgraber.org/2016/03/11/lxd-2-0-blog-post-series-012/)」（[日本語版目次](http://qiita.com/m-shibata/items/029b19d90af2a79116c2)）の一つである以下の記事を翻訳したものです。

* https://www.stgraber.org/2016/04/12/lxd-2-0-remote-hosts-and-container-migration-612/

この文書のライセンスは原文と同じく、[Creative Commons BY-NC-SA 2.5](http://creativecommons.org/licenses/by-nc-sa/2.5/)のもとに提供されています。

![CC-BY-NC-SA 2.5](http://i.creativecommons.org/l/by-nc-sa/2.5/88x31.png)

---------

## リモートプロトコル

LXD 2.0は次のふたつのプロトコルをサポートしています：

* **LXD 1.0 API**: クライアントとLXDデーモン間で使われるREST APIです。同様にLXDデーモン同士でもイメージやコンテナのコピーや移動にも使います。
* **Simplestreams**: Simplestreamsプロトコルは読み込み専用、イメージ専用のプロトコルで、LXDクライアントとデーモンの双方が（Ubuntuイメージのような）パブリックなイメージサーバーからイメージ情報の取得やイメージのインポートを行う際に使用します。

以下の説明はすべて、これらのうち前者に関連します。

## セキュリティ

LXD APIによる認証は、最新の暗号化技術を用いたTLS 1.2越しのクライアント証明書の認証によって行われます。二つのLXDデーモンが直接情報をやりとりする際は、最初にソースとなるデーモンが一時的なトークンを生成し、クライアント越しにターゲットのデーモンへ送られます。そのトークンはその場のストリームでのみ使われ、即座に破棄し、再利用はできません。

中間者攻撃を回避するために、クライアントツールはソースサーバーの証明書をターゲットにも送ります。つまり、特定のダウンロード操作においては、ターゲットサーバーに送信側サーバーのURL、そのリソースに必要なワンタイムのアクセストークン、そのサーバーが使うであろう証明書が提供されます。この方法は中間者攻撃を防ぎ、送信者のオブジェクトの一時的なアクセスのみを与えます。

## 必要なネットワーク

LXD 2.0は操作のターゲット側（データの受信側）が直に送信側に接続し、データを取得するモデルを採用しています。

これはターゲットサーバーが、ファイヤーウォールなどを越えて、直に送信サーバーに接続できなくてはならないことを意味します。

厳しいファイヤウォールによってホスト間の通信ができない時のために、逆方向の接続やプロキシ越しの接続のサポートも[計画しています](https://github.com/lxc/lxd/issues/553)。


## リモートホストへの接続

リモートホストを使いたいユーザーが常にホストネームやIPアドレスを把握し、正しいリモートホストに接続しようとしているか検証する手間を省くために、LXDは「リモート」という概念を導入してます。

初期状態ではLXDは「`local:`」リモートのみが設定されています。これは常に規定の（名前を省略した時の）リモートとして使われます。このローカルリモートは、LXD REST APIを使ってunixソケット越しにローカルのデーモンと通信します。

### リモートの追加

LXDがインストールされた二つのマシンがあるとします。一つはローカルのマシンで、リモートホストの方は「`foo`」と呼ぶことにしましょう。

最初に、「`foo`」をリッスン状態にし、パスワードを設定する必要があります。リモートのシェルで次のコマンドを実行してください：

```
lxc config set core.https_address [::]:8443
lxc config set core.trust_password 何かパスワード
```

次にローカルのLXDでも、そこからコンテナやイメージを送信できるように、ネットワークに対して見えるように設定する必要があります：

```
lxc config set core.https_address [::]:8443
```

両方のデーモンの設定が完了しました。これで「`foo`」をローカルのクライアントに追加できます：

```
lxc remote add foo 1.2.3.4
```

（ここで1.2.3.4は、IPアドレスかFQDNに変更してください）

次のような感じで表示されることでしょう：

```
stgraber@dakara:~$ lxc remote add foo 2607:f2c0:f00f:2770:216:3eff:fee1:bd67
Certificate fingerprint: fdb06d909b77a5311d7437cabb6c203374462b907f3923cefc91dd5fce8d7b60
ok (y/n)? y
Admin password for foo:
Client certificate stored at server: foo
```

リモートのリストを表示したら、「`foo`」が追加されていることでしょう：

```
stgraber@dakara:~$ lxc remote list
+-----------------+-------------------------------------------------------+---------------+--------+--------+
|      NAME       |                         URL                           |   PROTOCOL    | PUBLIC | STATIC |
+-----------------+-------------------------------------------------------+---------------+--------+--------+
| foo             | https://[2607:f2c0:f00f:2770:216:3eff:fee1:bd67]:8443 | lxd           | NO     | NO     |
+-----------------+-------------------------------------------------------+---------------+--------+--------+
| images          | https://images.linuxcontainers.org:8443               | lxd           | YES    | NO     |
+-----------------+-------------------------------------------------------+---------------+--------+--------+
| local (default) | unix://                                               | lxd           | NO     | YES    |
+-----------------+-------------------------------------------------------+---------------+--------+--------+
| ubuntu          | https://cloud-images.ubuntu.com/releases              | simplestreams | YES    | YES    |
+-----------------+-------------------------------------------------------+---------------+--------+--------+
| ubuntu-daily    | https://cloud-images.ubuntu.com/daily                 | simplestreams | YES    | YES    |
+-----------------+-------------------------------------------------------+---------------+--------+--------+
```

### リモートとの通信

さて、リモートサーバーを設定しました。これで何ができるようになるのでしょうか？

これまでの投稿で説明したことはすべてできます。唯一異なるのは、LXDに対してどのホストで実行するかを指定することです。

たとえば次のようなコマンドがあるとします：

```
lxc launch ubuntu:14.04 c1
```

上記は規定のリモート（`lxc remote get-default`）で実行されるので、ローカルのホストに対して適用されることでしょう。

```
lxc launch ubuntu:14.04 foo:c1
```

上記は、その代わり`foo`上でコマンドを実行します。

リモートホスト上の実行中のコンテナを確認したい場合は、次のようになります：

```
stgraber@dakara:~$ lxc list foo:
+------+---------+---------------------+-----------------------------------------------+------------+-----------+
| NAME |  STATE  |         IPV4        |                     IPV6                      |    TYPE    | SNAPSHOTS |
+------+---------+---------------------+-----------------------------------------------+------------+-----------+
| c1   | RUNNING | 10.245.81.95 (eth0) | 2607:f2c0:f00f:2770:216:3eff:fe43:7994 (eth0) | PERSISTENT | 0         |
+------+---------+---------------------+-----------------------------------------------+------------+-----------+
```

一つ注意しなくてはならないのは、イメージとコンテナの双方にリモートを指定しなくてはならないということです。「`foo`」上に「`my-image`」という名前のローカルイメージが存在し、そのイメージから「`c2`」という名前のコンテナを作りたいときは、次のように指定しなくてはなりません：

```
lxc launch foo:my-image foo:c2
```

最後に、リモートコンテナへのシェルの実行も、期待通り動作します：

```
lxc exec foo:c1 bash
```

### コンテナのコピー

ホスト間のコンテナのコピーはとても簡単です：

```
lxc copy foo:c1 c2
```

これによりリモートの「`c1`」コンテナのコピーがローカルの「`c2`」コンテナとして作成されます。「`c1`」コンテナはまず停止している必要があります。ただしスナップショットをコピーすることで、コピー元のコンテナを停止せず実行中のままコピーできます：

```
lxc snapshot foo:c1 current
lxc copy foo:c1/current c3
```

### コンテナの移動

ライブマイグレーション（別の投稿で説明する予定です）をするのでなければ、コンテナを移動する前にそれを停止している必要があります。それ以外は、予想通りの動作になるはずです：

```
lxc stop foo:c1
lxc move foo:c1 local:
```

上記の例は以下と同じ動作です：

```
lxc stop foo:c1
lxc move foo:c1 c1
```

## どのように操作しているのか

リモートコンテナの操作は、想像通りの動きをすることでしょう。LXDは、ローカルのunixソケットに対するREST APIを使うのではなく、同じAPIをHTTPS経由のリモートに対して呼び出しています。

コピーや移動など、二つのデーモン間でやり取りを行う場合、もう少し複雑な処理を行っています。

上記の場合は、次のような処理を行います：

1. 利用者は「`lxc move foo:c1 c1`」を実行します。
2. クライアントは`local:`リモートに「`c1`」コンテナが存在するかどうか確認します。
3. クライアントは「`foo`」の情報を取得します。
4. クライアントは移動元となる「`foo`」デーモンに対してマイグレーショントークンを要求します。
5. クライアントは移動元のURLと「`foo`」の証明書と同じくマイグレーショントークン、コンテナとデバイスの設定一緒にローカルのLXDデーモンに送ります。
6. ローカルのLXDデーモンは与えられたトークンを用いて直接「`foo`」に接続します。

  1. 最初にコントロールWebSocketに接続します。
  2. ファイルシステムの転送プロトコル（zfsのsend/receiveやbtrfsのsend/receive、もしくは単純なrsync）とネゴシエーションします。
  3. もしローカルに存在すれば、移動元のコンテナを作るために使われたイメージを展開します。これにより不要なデータ転送を抑制します。
  4. コンテナと、その差分のスナップショットなどを転送します。

7. 成功したら、クライアントは移動元のコンテナの削除を「`foo`」に伝えます。


## オンラインで試してみる

リモートの操作やコンテナの移動・コピーのための複数のマシンを持っていますでしょうか？

持っていないのなら、オンラインの[デモサービス](https://linuxcontainers.org/lxd/try-it/)で、この機能を試すことができます。ここで説明している内容を手順に沿って説明しています！


## その他の情報

* LXDのウェブサイト：[https://linuxcontainers.org/ja/lxd/](https://linuxcontainers.org/ja/lxd/)
* 開発はGitHub上で行なっています：[https://github.com/lxc/lxd](https://github.com/lxc/lxd)
* メーリングリスト：[https://lists.linuxcontainers.org](https://lists.linuxcontainers.org)
* IRCチャンネル：#lxcontainers on irc.freenode.net