クラスタリング
LXD はクラスタリングモードで実行できます。クラスタリングモードでは複数台の LXD サーバが同じ分散データベースを共有し、REST API や lxc クライアントで統合管理できます。
この機能は API 拡張の "clustering" の一部として導入しました。
クラスターの形成
まず、ブートストラップノードを選択する必要があります。既存の LXD サーバでも新しいインスタンスでもブートストラップノードになれます。ブートストラップノードとなるサーバを決めた後は、ブートストラップノードを初期化し、それからクラスターへ追加ノードを参加させます。この処理はインタラクティブに行えますし、前もって定義ファイルを作成しても行えます。
クラスターに追加するノードはすべて、ストーレージプールとネットワークについて、ブートストラップノードと同じ構成を持たなければなりません。ノード特有の設定として持てる唯一の設定は、ストレージプールに対する source
と size
、ネットワークに対する bridge.external_interface
です。
クラスター内のノード数としては 3 以上を強く推奨します。これは少なくとも 1 ノードが落ちても分散状態のクオラムを確立できるからです(分散状態は Raft アルゴリズムを使ってレプリケーションされている SQLite データベースに保管されています)。ノード数が 3 より小さくなるとクラスター内のただ 1 つのノードだけが SQLite データベースを保管します。第 3 のノードがクラスターに参加したときに、第 2 と第 3 のノードがデータベースの複製を受け取ります。
インタラクティブに行う方法
lxd init
を実行し、最初の質問("Would you like to use LXD clustering?")に yes
と答えます。そして、そのノードを特定する名前、他のノードが接続するための IP もしくは DNS アドレスを選択します。そして、既存のクラスターに加わるかどうかの質問には no
と答えます。最後に、オプショナルでストレージプールとネットワークブリッジを作成できます。これで、最初のクラスターノードが起動し、ネットワークが利用できるようになります。
更に追加のノードをクラスターに追加できます。しかし、追加ノードの既存データはすべて失われるため、追加のノードは完全に新しい LXD サーバであるか、追加前にすべての情報をクリアしたノードである必要があります。
ノードを追加するために、lxd init
を実行し、クラスタリングを使うかどうかの質問に yes
と答えます。ブートストラップノード、それまでに参加したノードとは異なる名前を指定します。IP もしくは DNS アドレスを指定し、既存のクラスターに加わるかどうかの質問には yes
と答えます。クラスター内の既存のノードのアドレスを指定し、表示されたフィンガープリントを確認します。
事前に定義して行う方法
事前にブートストラップノードの設定内容を書いた定義ファイルを作成できます。例えば:
config:
core.trust_password: sekret
core.https_address: 10.55.60.171:8443
images.auto_update_interval: 15
storage_pools:
- name: default
driver: dir
networks:
- name: lxdbr0
type: bridge
config:
ipv4.address: 192.168.100.14/24
ipv6.address: none
profiles:
- name: default
devices:
root:
path: /
pool: default
type: disk
eth0:
name: eth0
nictype: bridged
parent: lxdbr0
type: nic
cluster:
server_name: node1
enabled: true
定義ファイルを作成したあと、cat <preseed-file> | lxd init --preseed
を実行し、最初のノードを作成します。
次に、他のノードのブートストラップファイルを作成します。cluster
セクションに、追加するノード固有のデータと設定値を指定するだけです。
ターゲットとなるブートストラップノードのアドレスと証明書を必ず含めてください。cluster_certificate
に対する YAML 互換のエントリーを作成するには、sed ':a;N;$!ba;s/\n/\n\n/g' /var/lib/lxd/server.crt
のようにコマンドを実行します。このコマンドはブートストラップノードで実行する必要があります。
例えば:
cluster:
enabled: true
server_name: node2
server_address: 10.55.60.155:8443
cluster_address: 10.55.60.171:8443
cluster_certificate: "-----BEGIN CERTIFICATE-----
opyQ1VRpAg2sV2C4W8irbNqeUsTeZZxhLqp4vNOXXBBrSqUCdPu1JXADV0kavg1l
2sXYoMobyV3K+RaJgsr1OiHjacGiGCQT3YyNGGY/n5zgT/8xI0Dquvja0bNkaf6f
...
-----END CERTIFICATE-----
"
cluster_password: sekret
member_config:
- entity: storage-pool
name: default
key: source
value: ""
クラスターの管理
クラスターが形成されると、lxc cluster list
を実行して、ノードのリストと状態を見ることができます。ノードそれぞれのもっと詳細な情報は lxc cluster show <node name>
を実行して取得できます。
投票 (voting) メンバーとスタンバイメンバー
クラスターは状態を保管するために分散 データベース を使用します。 クラスター内の全てのノードはユーザーのリクエストに応えるためにそのような分散データベースにアクセスする必要があります。
クラスター内に多くのノードがある場合、それらのうちいくつかだけがデータベースのデータを複製するために選ばれます。 選ばれた各オンードは投票者 (voter) としてあるいはスタンバイとしてデータを複製できます。 データベース(とそれに由来するクラスター)は投票者の過半数がオンラインである限り利用可能です。 別の投票者が正常にシャットダウンした時やオフラインであると検出された時はスタンバイノードが自動的に投票者に昇格されます。
投票ノードのデフォルト数は 3 で、スタンバイノードのデフォルト数は 2 です。 これは 1 度に最大で 1 つの投票ノードの電源を切る限りあなたのクラスターは稼働し続けることを意味します。
投票ノードとスタンバイノードの望ましい数は以下のように変更できます。
lxc config set cluster.max_voters <n>
そして
lxc config set cluster.max_standby <n>
投票者の最大数は奇数で最低でも 3 であるという制約があります。 一方、スタンバイノードは 0 から 5 の間でなければなりません。
ノードの削除
クラスターからノードをクリーンに削除するには、lxc cluster remove <node name>
を使います。
オフラインノードとフォールトトレランス
都度、選出されたクラスターリーダーが存在し、そのリーダーが他のノードの健全性をモニタリングします。20 秒以上ノードがダウンした場合は、ステータスは OFFLINE とマークされ、そのノード上での操作はできなくなります。また、すべてのノードで状態の変更が必要な操作が可能です。
リーダーがオフラインに移行した場合、他のノードが新しいリーダーに選出されます。
オフラインノードがオンラインに戻るとすぐに、ふたたび操作できるようになります。
ノードをオンラインに戻せないとき、ノードをオンラインに戻したくないときは、lxc cluster remove --force <node name>
を使ってクラスターからノードを削除できます。
反応しないノードがオフラインと認識されるまでの秒数は以下のようにして変更できます。
lxc config set cluster.offline_threshold <n seconds>
最小値は 10 秒です。
ノードのアップグレード
クラスターをアップグレードするためには、すべてのノードをアップグレードし、すべてが確実に同じバージョンの LXD にする必要があります。
単一のノードをアップグレードするには、単にホスト上で(snap や他のパッケージ管理システムを使って) lxd/lxc バイナリをアップグレードし、lxd デーモンを再起動します。
デーモンの新バージョンでデータベーススキーマや API が変更になった場合は、再起動したノードは Blocked 状態に移行する可能性があります。これは、クラスター内にまだアップグレードされていないノードが存在し、その上で古いバージョンが動作している場合に起こります。ノードが Blocked 状態にあるとき、このノードは LXD API リクエストを一切受け付けません(詳しく言うと、実行中のインスタンスは動き続けますが、ノード上の lxc コマンドは動きません)。
ブロックされていないノード上で lxc cluster list
を実行すると、ノードがブロックされているかどうかを確認できます。
残りのノードのアップグレードを進めると、最後のノードをアップグレードするまでは、ノードはすべて Blocked 状態に移行します。その時点で、Blocked ノードは古いノードが残っていないかを確認し、再度操作できるようになります。
クォーラム消失からの復旧
各 LXD クラスターはデータベースノードとして機能するメンバーを最大 3 つまで持つことができます。 恒久的にデータベースノードとして機能するクラスターメンバーの過半数を失った場合 (例えば 3 メンバーのクラスターで 2 メンバーを失った場合)、 クラスターは利用不可能になります。しかし、 1 つでもデータベースノードが生き残っていれば、クラスターをリカバーすることができます。
クラスターメンバーがデータベースノードとして設定されているかどうかをチェックするには、クラスターのいずれかの生き残っているメンバーにログオンして以下のコマンドを実行します。
lxd cluster list-database
これは LXD デーモンが実行中でなくても実行できます。
一覧表示されたメンバーの中で、生き残っていてログインしたものを選びます (コマンドを実行したメンバーと異なる場合)。
LXD デーモンが実行していないことを確認したうえで次のコマンドを実行します。
lxd cluster recover-from-quorum-loss
この時点で LXD デーモンを再起動できるようになり、データベースはオンラインに復帰するはずです。
データベースからは何の情報も削除されていないことに注意してください。特に失われたクラスターメンバーに関する情報は、それらのインスタンスについてのメタデータも含めて、まだそこに残っています。 この情報は失われたインスタンスを再度作成する必要がある場合に、さらなるリカバーのステップで利用することができます。
失われたクラスターメンバーを恒久的に削除するためには、次のコマンドが利用できます。
lxc cluster remove <name> --force
ここでは ``lxdではなく通常の
lxcコマンドを使う必要があることに注意してください。
<!--
Note that this time you have to use the regular
lxccommand line tool, not
lxd```.
-->
インスタンス
クラスター上の任意のノード上でインスタンスを起動できます。例えば、node1 から:
lxc launch --target node2 ubuntu:18.04 bionic
のように実行すれば、node2 上で Ubuntu 18.04 コンテナーが起動します。
ターゲットを指定せずにインスタンスを起動したときは、インスタンスの数が一番少ないサーバ上でインスタンスが起動されます。全てのサーバが同じ数のインスタンスを持っている場合はランダムに選ばれます。
以下のように実行すると、インスタンス上のすべてのコンテナーをリストできます:
lxc list
NODE 列がコンテナーが実行中のノードを示します。
インスタンスが起動後、任意のノードからそのコンテナーを操作できます。例えば、node1 から:
lxc exec bionic ls /
lxc stop bionic
lxc delete bionic
lxc pull file bionic/etc/hosts .
のように操作できます。
Raft メンバーシップの手動での変更
何か予期せぬ出来事が起こった場合など、クラスターの Raft メンバーシップの設定を手動で変更する必要がある状況があるかもしれません。
例えばクリーンに削除できなかったクラスターメンバーがある場合、 lxc cluster list
に表示されないですが、引き続き Raft 設定の一部になってしまう場合があるかもしれません
(この状況は lxd sql local "SELECT * FROM raft_nodes"
で確認できます)。
この場合は以下のように実行すると
lxd cluster remove-raft-node <address>
残ってしまったノードを削除できます。
イメージ
デフォルトではデータベースメンバを持っているのと同じ数のクラスターに LXD はイメージを複製します。これは通常はクラスター内で最大3つのコピーを 持つことを意味します。
耐障害性とイメージがローカルにある可能性を上げるためにこの数を増やす ことができます。
特別な値である "-1" は全てのノードにイメージをコピーするために使用できます。
この数を 1 に設定することでイメージの複製を無効にできます。
lxc config set cluster.images_minimal_replica 1
ストレージプール
先に述べたように、すべてのノードは同一のストレージプールを持たなければなりません。異なるノード上のプール間の違いは、設定項目、source
、size
、zfs.pool\_name
のみです。
新たにストレージプールを作成するためには、すべてのノードでストレージプールをを定義する必要があります。例えば:
lxc storage create --target node1 data zfs source=/dev/vdb1
lxc storage create --target node2 data zfs source=/dev/vdc1
のようにします。
新しいストレージプールをノード上に定義する際、ノード固有で与えることのできる設定項目は上記設定のみです。
この時点ではプールはまだ実際には作られていませんが、定義はされています(lxc storage list
を実行すると、状態が Pending とマークされています)。
次のように実行しましょう:
lxc storage create data zfs
するとストレージがすべてのノードでインスタンス化されます。特定のノードで定義を行っていない場合、もしくはノードがダウンしている場合は、エラーが返ります。
この最後の storage create
コマンドには、ノード固有ではない(上記参照)任意の設定項目を与えることができます。
ストレージボリューム
各ボリュームは特定のノード上に存在しています。lxc storage volume list
は、特定のボリュームがどのノードにあるかを示す NODE
列を表示します。
異なるボリュームは、異なるノード(例えば image volumes)上に存在する限りは同じ名前を持てます。複数のノードが与えた名前のボリュームを持つ場合には、ボリュームコマンドに --target <node name>
を与える必要がある点を除いて、ストレージボリュームはクラスター化されていない場合と同じ方法で管理できます。
例えば:
# Create a volume on the node this client is pointing at
lxc storage volume create default web
# Create a volume with the same node on another node
lxc storage volume create default web --target node2
# Show the two volumes defined
lxc storage volume show default web --target node1
lxc storage volume show default web --target node2
ネットワーク
先に述べたように、すべてのノードは同じネットワークを定義しなければなりません。異なるノード間のネットワークで異なっても良い設定は、bridge.external_interfaces
というオプショナルの設定項目です(ネットワーク設定の文書を参照してください)
新しいネットワークを作成するには、最初にすべてのノードで以下のように定義を行う必要があります:
lxc network create --target node1 my-network
lxc network create --target node2 my-network
ノード上に新しいネットワークを定義する場合、先に述べたように bridge.external_interfaces
のみ有効な設定として与えることができます。
この時点では、ネットワークはまだ実際には作成されていません。しかし定義はされています(lxc network list
を実行すると、状態が Pending とマークされています)。
次のように実行しましょう:
lxc network create my-network
するとネットワークがすべてのノード上でインスタンス化されます。特定のノードで定義していない場合、もしくはノードがダウンしている場合は、エラーが返ります。
この最後の network create
コマンドには、ノード固有ではない(上記参照)任意の設定項目を与えることができます。
分離した REST API とクラスターネットワーク
クライアントの REST API エンドポイントとクラスター内のノード間の内部的なトラフィック (例えば REST API に DNS ラウンドロビンとともに仮想 IP アドレスを使うために) で別のネットワークを設定できます。
このためには、クラスターの最初のノードを cluster.https_address
設定キーを
使ってブートストラップする必要があります。例えば以下の定義ファイルを使うと
config:
core.trust_password: sekret
core.https_address: my.lxd.cluster:8443
cluster.https_address: 10.55.60.171:8443
...
(YAML 定義ファイルの残りは上記と同じ)。
新しいノードを参加させるには、まず REST API のアドレスを設定します。
例えば lxc
クライアントを使って以下のように実行し
lxc config set core.https_address my.lxd.cluster:8443
そして通常通り PUT /1.0/cluster
API エンドポイントを使って、
server_address
フィールドで参加するノードのアドレスを設定します。
定義ファイルを使うなら YAML のペイロードは完全に上記のものと同じに
なるでしょう。