ぶていのログでぶログ

思い出したが吉日

OpenStack Octaviaを使うときはnova.enable_anti_affinity = trueを設定したほうがよさそう

結論から書くと、OpenStack OctaviaでACTIVE/STANDBYトポロジーを使う場合には、octavia.confで以下のような設定を入れたほうがよさそう。

[nova]
enable_anti_affinity = true

解説

OpenStack Octaviaについては以前記事を書いたのでそちらを参照のこと。

tech.buty4649.net

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ポリシーを適用することができる。

  1. Nova APIを実行してServer Groupを作成する
  2. Octaviaの作るServer Groupの名前は octavia-lb-<LoadBalancerのUUID>
  3. OctaviaのDBのload_balancer.server_group_id を書き換える
  4. e.g. UPDATE load_balancer SET server_group_id = "<ServerGroupのUUID>" WHERE id = "<LoadBalancerのUUDI>"
  5. 上記のスクリプトをNovaのコントロールノードで実行する

おわりに

こんな感じでLoadBalancerの冗長性を保つことができようになった。 nova.enable_anti_affinityは以前から*4使用できていたようなので、その時に有効化しておけばよかったと反省。。 まぁ、副産物としてOctaviaやNovaのコードを読んだりして理解が深まったのでよしとしよう。

OctaviaはOpenStack Trainリリースにあわせて5.0.0がリリースされて、便利な機能も追加されたので別の記事で紹介したい。

*1:OctaviaのNova driverを作り込めばコントロールすることはできるだろうが、標準ではできないという意味

*2:なお、Live MigrationはServer Groupを無視するっぽい…つらい

*3:レコードの中にJSONがあってry

*4:どのバージョンかは調べていない