結論から書くと、OpenStack OctaviaでACTIVE/STANDBYトポロジーを使う場合には、octavia.confで以下のような設定を入れたほうがよさそう。
[nova] enable_anti_affinity = true
解説
OpenStack Octaviaについては以前記事を書いたのでそちらを参照のこと。
OctaviaでACTIVE/STANDBYトポロジーを利用するように設定している場合に、LoadBalancerが作成されるとLBインスタンス(OctaviaではAmphoraと呼ばれる)が2つ作成されて、自動的に冗長構成を取ることができる。 インスタンス作成時のスケジューリングはNova任せでありOctaviaでコントロールすることはできない*1。 これで何が困るかと言うと、冗長構成を取っているはずのLBインスタンスが同じHypervisorに配置される可能性があるということだ。 そうなると、せっかくACTIVE/STANDBYで冗長を取っているのに、同じHypervisorになったことで母艦の障害で冗長性が失われる可能性が高いということだ。
この問題を解決するために、冒頭で書いた設定を入れると良い。
[nova] enable_anti_affinity = true
この設定を入れると、OctaviaがLBインスタンスを作成するときに、NovaのServer Groupを作成・設定する。 作成されるServer GroupはAntiAffinityポリシーが適用されているので、LBインスタンスが同じHypervisorにスケジューリングされることはなくなる*2。
既存のLoadBalancerにもAntiAffinityポリシーを設定したい
上記設定はすべてのLoadBalancerに適用されるわけではなく、設定を入れたあとに作られたLoadBalancerから設定される。 すでに、Octaviaを利用していてLoadBalancerが大量にあって、再作成が大変…なので、なんとかしたい。 Nova APIやOctavia APIでは変更できないのでDBを直接いじる必要がある。
Octaviaに関しては、load_balancerテーブルの中に server_group_id
があるので、対象のLoadBalancerのレコードの server_group_id
カラムに入力すればよい。
次に、Nova側なのだが、こちらは直接いじるにはとても複雑すぎた*3ので以下のようなPythonコードを作成した。
$ cat add_instance_to_servergroup.py #!/usr/bin/python from nova import config from nova import context from nova import objects from nova.objects import instance_group from nova.objects import request_spec from nova.objects import instance_pci_requests from nova.objects import image_meta from nova.objects import flavor instance_uuid = "XXXXXXXXXXXXX" servergroup_uuid = "YYYYYYYY" config.parse_args("") ctxt = context.get_admin_context() objects.InstanceGroup.add_members(ctxt, servergroup_uuid, [instance_uuid]) group = objects.InstanceGroup.get_by_uuid(ctxt, servergroup_uuid) rs = objects.RequestSpec.get_by_instance_uuid(ctxt, instance_uuid) rs.instance_group = group rs.scheduler_hints["group"] = [servergroup_uuid] rs.save()
これらを使って以下のような手順を実行すれば既存のLBインスタンスにもAntiAffinityポリシーを適用することができる。
- Nova APIを実行してServer Groupを作成する
- Octaviaの作るServer Groupの名前は
octavia-lb-<LoadBalancerのUUID>
- OctaviaのDBのload_balancer.server_group_id を書き換える
- e.g.
UPDATE load_balancer SET server_group_id = "<ServerGroupのUUID>" WHERE id = "<LoadBalancerのUUDI>"
- 上記のスクリプトをNovaのコントロールノードで実行する
おわりに
こんな感じでLoadBalancerの冗長性を保つことができようになった。 nova.enable_anti_affinityは以前から*4使用できていたようなので、その時に有効化しておけばよかったと反省。。 まぁ、副産物としてOctaviaやNovaのコードを読んだりして理解が深まったのでよしとしよう。
OctaviaはOpenStack Trainリリースにあわせて5.0.0がリリースされて、便利な機能も追加されたので別の記事で紹介したい。