neutron + DVR + dragent で OpenStack のスケーラビリティを高める方法について検証してみました

#はじめに
OpenStack を使うと、compute-node 50台程度の小〜中規模クラスタであればネットワークのボトルネックを考慮しなくてもそれなりに動作する環境が構築できます。
もう少し大規模なクラスタを構築したい場合やトラフィックが大量に発生するインスタンスが多い場合は、DVR (Distributed Virtual Router) を使用することである程度の性能向上が期待できます。
しかしながら、DVR を使うことで、次のような制約が発生するため、例えば 100 compute-node を超えるような構成に DVR を適用することは現実的には難しいという問題がありました。

DVR には、次のような課題があります。

- DVR の Local Router に 別途 IPv4 Address を割り当てる必要があるため、compute-node 数に比例して Public IPv4 アドレスを消費してしまう
  - subnet-type を設定することで消費量を抑えることはできますが、Provider Network (Router) 側に Secondary IP Address を付与する必要があります
- 全ての compute-node を同一の L2 Network に所属させる必要があるため、CLOS 構成(IP Fabric) を用いたネットワークが導入できない
  - North-South トラフィックについて、IP Fabric のような、全リンクを active 構成にした広帯域構成を容易に実現する方法が無い
  - 全ての Top of Rack Switch を収容できる Aggregation Swtich を導入する必要もあるためコストが大きい
  - Aggregation Switch で発生した問題が全てのノードに波及する可能性があるため、堅牢性の観点では大規模化が難しい

DVR を使わずにシンプルかつ IP Fabric にも対応できる環境を構築したい場合は Routed Provider Network を使う方法があります。ただし、次のような欠点があります。

- 仕様による制約として、Floating-IP が使えない
- metadata service を使用する場合は、instance 上で Host Route を設定する必要がある。DHCP server の option 情報として当該経路を配布することになるため、instance で稼働している dhcp client によっては期待通りに動作しない可能性がある
- Provider Network に Public IPv4 Address を割り当てる場合だと、Rack 毎に Subnet Block を割り当てることになるため、ある程度まとまった数の IPv4 アドレスが必要になる
- Rack 毎に IPv4 subnet を用意して Default Route や DHCP-server を割り当てることになるため、(貴重な) Public IPv4 アドレスブロックをエンドユーザのために全て使い切れず、勿体無い
- IPアドレスの消費やCPU/メモリリソースの消費に偏りが出た際に、インフラ側での対策が困難となる(Rackを跨いだ migration が出来ないため)

実は、DVR は Routed Provider Network と 後述する BGP Dynamic Routing Agent (dragent) を組み合わせることで、上記の課題を解決できてしまいます。
従来は、OpenStack で一定規模以上のクラスタを構築する場合は別途 SDN 製品を導入するか(機能の制約を受け入れた上で) Provider Network を直接 instance に割り当てるしか方策がなかったのですが、
SDN製品が提供している特別な機能(見える化やSFC等)が不要な場合は、リファレンス実装の neutron だけでも十分に大規模な構成を実現できそうです。

関連する Blueprint(RFE) は、2017年に提出されております。ただし、2019年12月現在の進捗は In-Progress となっており、一部 neutron のコード修正が必要となります。

- [RFE: Floating IP Subnets on Routed Provider Networks](https://bugs.launchpad.net/neutron/+bug/1667329)

本ドキュメントでは、devstack を用いて実際に RFE の構成が実現できることを検証した結果をまとめさせていただきます。

#BGP Dynamic Routing Agent の役割
そもそも、BGP Dynamic Routing Agent (dragent) を使うと何が嬉しいのかについて解説いたします。

dragent は、instance から見た際の Inbound 通信について、経路情報をインフラ側のネットワーク機器に自動的に設定する役割を果たしています。
Outbound 通信については default route を辿るだけで destination に到達できるので特別な対応は必要ありませんが、Inbound については fixed-ip / floating-ip への転送先としてどの Virtual Router が next-hop になるかを Top Of Rack Switch等のインフラ側ルータが知っている必要があります。
next-hop の情報は neutron のデータベースを見れば分かるので、極端な話では手動で static route を追加することでも目的は達成できます。
ただし、手動やバッチ処理等ではあまりにも非効率なので、ネットワーク機器の得意分野である Dynamic Routing を利用する形で、neutron が持っている経路情報を動的に学習させます。

BGP(Border Gateway Protocol) 自体はインターネットの世界では中継区間のバックボーン等で広く使われている技術であり、スケール性能についても十分に高いため、IP Fabric 等でも良く使用されております。
dragent の役割としては、自分自身は routing に直接関わらずに、Route Reflector の形でインフラ側の Router に経路を広報します。

なお、dragent は現時点では os-ken と呼ばれる ryu の fork version を用いて BGP を実装しております。
内部的には os-ken のレイヤは Pluggable になっているので、(実装さえすれば) bird や quagga 等、他の実装に差し替えることも可能です。

dragent を使うことで、具体的には tenant network (fixed-ip) の経路が次のような形で設定されるようになります。[(OpenStack 公式ドキュメントより抜粋)](https://docs.openstack.org/mitaka/networking-guide/config-bgp-dynamic-routing.html)

<img width="634" alt="bgp-dynamic-routing-fixed_ip-example.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96389/4529376f-8cbd-dd61-8c00-97e7ca1231a8.png">

点線で囲まれたネットワーク(10.0.1.0/24, 10.0.2.0/24) が同一の Address Scope となっており、10.0.1.0/24 については next-hop = 203.0.113.1 として BGP Agent が Provider Router に経路を広報していることが分かります。

floating-ip については、次のように fixed-ip とは別に host route が設定されることが分かります。

<img width="742" alt="bgp-dynamic-routing-fip-example.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96389/ff52b1f9-a182-4770-a99f-d3d57be9fbd8.png">

#検証構成
devstack を用いた検証環境として、次のような環境を構築しました。

<img width="747" alt="dvr-dragent-poc-overview.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96389/8fe6b51e-3b62-45aa-b7e9-12a679fa91a3.png">

#検証結果
検証結果としては、特に問題なく Floating-IP アドレスが外部の広報でき、通信についても支障はありませんでした。
大規模なクラスタを構築しても問題なく稼働することについては別途検証する必要がありますが、アーキテクチャ上は(適切な aggregation をしている限りは)無尽蔵に compute-node を増やすことができそうです。

#参考情報
- [Routed provider networks](https://docs.openstack.org/neutron/train/admin/config-routed-networks.html)
  - Routed Provider Network についての解説があります
- [Welcome to neutron-dynamic-routing’s documentation!](https://docs.openstack.org/neutron-dynamic-routing/latest/)
  - BGP Dynamic Routing のアーキテクチャ解説、設定方法、API 及び CLI の解説等があります
- [Floating IPs for Routed Networks](https://review.opendev.org/#/c/486450/16/specs/train/routed-networks-floating-ips.rst)
  - Routed Provider Network を Floating IP に対応させる使い方、及び考慮事項について解説されています
- [WIP POC: Floating IP's for routed networks](https://review.opendev.org/#/c/669395/)
  - Routed Provider Network を BGP Dynamic Routing に対応させるために必要な修正について、PoC コードが提案されています

#インストール手順
まずはじめに、vagrant を用いて all-in-one node + compute-node + dragent 3台構成のクラスタを構築します。

##devstack インストール手順
次の手順を参考にして、Vagrant を用いて 3ノードの openstack クラスタ を作成します。

- https://qiita.com/rev4t/items/43fa6d1200d8af5e285d


##node1 (all-in-one node) のデプロイ

```bash
vagrant ssh node1
sudo apt update
sudo apt upgrade

git clone https://github.com/openstack-dev/devstack
cd devstack

# devstack 設定ファイルを編集します
cp samples/local.conf ./
vi local.conf

# OpenStack を構築します
./stack.sh
```

```bash:local.conf(node1)
...
[[local|localrc]]
enable_plugin neutron-dynamic-routing http://git.openstack.org/openstack/neutron-dynamic-routing stable/train

GIT_BASE=https://git.openstack.org
HORIZON_BRANCH=stable/train
KEYSTONE_BRANCH=stable/train
KEYSTONECLIENT_BRANCH=stable/train
NOVA_BRANCH=stable/train
NOVACLIENT_BRANCH=stable/train
NEUTRON_BRANCH=stable/train
GLANCE_BRANCH=stable/train
CINDER_BRANCH=stable/train
REQUIREMENTS_BRANCH=stable/train

Q_PLUGIN=ml2
Q_ML2_TENANT_NETWORK_TYPE=vxlan
Q_DVR_MODE=dvr

# Replace HOST_IP with IP of eth0
HOST_IP=192.168.1.11
PUBLIC_INTERFACE=enp0s9
Q_PLUGIN=ml2
Q_ML2_TENANT_NETWORK_TYPE=vxlan
Q_DVR_MODE=dvr_snat

[[post-config|/$Q_PLUGIN_CONF_FILE]]
[agent]
arp_responder=True
```

##node2, node3 (compute node) のデプロイ
node2, node3 については nova-compute 関連のコンポーネントのみのデプロイになるため、devstack の設定ファイルを少し修正する必要がありますが、大まかな手順については node1 と同様になります。

```bash
...
[[local|localrc]]
enable_plugin neutron-dynamic-routing http://git.openstack.org/openstack/neutron-dynamic-routing stable/train

GIT_BASE=https://git.openstack.org
HORIZON_BRANCH=stable/train
KEYSTONE_BRANCH=stable/train
KEYSTONECLIENT_BRANCH=stable/train
NOVA_BRANCH=stable/train
NOVACLIENT_BRANCH=stable/train
NEUTRON_BRANCH=stable/train
GLANCE_BRANCH=stable/train
CINDER_BRANCH=stable/train
REQUIREMENTS_BRANCH=stable/train

Q_PLUGIN=ml2
Q_ML2_TENANT_NETWORK_TYPE=vxlan
Q_DVR_MODE=dvr

# Replace HOST_IP with IP of eth0
#for node3
#HOST_IP=192.168.1.13
#for node2
HOST_IP=192.168.1.12
PUBLIC_INTERFACE=enp0s9
# Replace SERVICE_HOST with IP of eth0 on node1
SERVICE_HOST=192.168.1.11
MYSQL_HOST=$SERVICE_HOST
RABBIT_HOST=$SERVICE_HOST
ENABLED_SERVICES=n-cpu,neutron,n-novnc,q-agt,q-l3,q-meta,q-dhcp,placement-client

[[post-config|/$Q_PLUGIN_CONF_FILE]]
[agent]
arp_responder=True
```

##Routed Provider Networkの設定
devstack のデフォルト設定では Routed Provider Network に対応していないため、手動で neutron の設定を修正します。
はじめに、/etc/neutron/neutron.conf の 'service_plugins' に 'segments' を追加します。

```/etc/neutron/neutron.conf
...
service_plugins = router,neutron_dynamic_routing.services.bgp.bgp_plugin.BgpPlugin,segments
...
[placement]
region_name = RegionOne
project_domain_name = Default
project_name = service
user_domain_name = Default
password = nomoresecret
username = placement
auth_url = http://192.168.1.11/identity
auth_type = password
```

設定が完了したら、neutron server サービスを再起動します。

```
sudo systemctl restart devstack@q-svc
```

次に、openvswitch-agent の設定を変更し、multi provider network に対応させます。
なお、'flat_networks' の設定については、ワイルドカードの '*' を指定することもできます。

```ini:/etc/neutron/plugins/ml2/ml2_conf.ini(node2)
...
[ml2_type_flat]
flat_networks = public,public2
...

[ovs]
bridge_mappings = public:br-ex
```

```ini:/etc/neutron/plugins/ml2/ml2_conf.ini(node2)
...
[ml2_type_flat]
flat_networks = public,public2
...

[ovs]
bridge_mappings = public2:br-ex
```

設定変更後、openvswitch-agent 及び openvswitch-server サービスを再起動します。

```bash
# at node1
sudo systemctl restart devstack@q-svc
sudo systemctl restart devstack@q-agt

# at node2
sudo systemctl restart devstack@q-agt
```

##Patch の適用
2019年12月現在、PoC 構成を設定するために必要な validation のコードが upstream 版には merge されていません。
次の手順で、必要な修正を neutron-server に適用します。

```bash
wget https://review.opendev.org/changes/669395/revisions/d069054b32adbf0f20da602ae5678e41448dec26/patch?zip
mv patch\?zip 00patch.zip
unzip 00patch.zip

cd /opt/stack/neutron
patch -p1 < ~/devstack/d069054.diff

sudo systemctl restart devstack@q-svc
```

##quagga のインストール

```bash
sudo apt -y install quagga quagga-doc

sudo touch /etc/quagga/zebra.conf
sudo chown quagga.quagga /etc/quagga/zebra.conf
sudo chmod 640 /etc/quagga/zebra.conf

sudo vi /etc/quagga/daemons
sudo vi /etc/quagga/zebra.conf
sudo vi /etc/quagga/bgpd.conf
```

```ini:/etc/quagga/daemons
zebra=yes
bgpd=yes
ospfd=no
ospf6d=no
ripd=no
ripngd=no
isisd=no
```

```/etc/quagga/zebra.conf
# Zebra configuration
# name of the router
hostname quagga_1
password zebra

# log
log file /var/log/quagga/zebra.log
```

```/etc/quagga/bgpd.conf
hostname bgp_1
password bgpd

# declare a router with local-as 65500
router bgp 65500

# set router-id to the network address we announce
bgp router-id 192.168.1.14

# expose neighbor network which dynamic routing agent is using
neighbor 192.168.1.11 remote-as 65501

# treat neutron dynamic routing agent as a passive peer in case
# quagga keeps making futile connection attempts
neighbor 192.168.1.11 passive

# log
log file /var/log/quagga/bgpd.log

debug bgp events
debug bgp filters
debug bgp fsm
debug bgp keepalives
debug bgp updates
```

```bash
sudo systemctl start zebra
sudo systemctl start bgpd
```

#各ノードの Network 初期設定

```bash
# at node1
# all-in-one 環境用に設定している default router の ip address を削除します
sudo ip addr delete 172.24.4.1/24 dev br-ex
# neutron-gateway (DVR SNAT用 router) 向けの ip-masquerade を設定します
sudo ip addr add 172.24.6.1/24 dev br-ex
sudo iptables -t nat -A POSTROUTING -o enp0s3 -s 172.24.6.0/24 -j MASQUERADE

# at node3
# provider network 用の default router を設定します
sudo ip addr add 172.24.4.1/24 dev enp0s9
sudo ip addr add 172.24.5.1/24 dev enp0s10
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A POSTROUTING -o enp0s3 -s 172.24.6.0/24 -j MASQUERADE
```

#ネットワーク設定手順
検証のためのネットワークを構築します。はじめに、devstack が自動的に作成したネットワーク関連のリソースを削除します。

##devstack 自動作成ネットワークの削除

```bash
neutron net-list
+--------------------------------------+---------+----------------------------------+----------------------------------------------------------+
| id                                   | name    | tenant_id                        | subnets                                                  |
+--------------------------------------+---------+----------------------------------+----------------------------------------------------------+
| 2106d95b-6fdc-4f6c-a6ca-b0ea38da9114 | private | 2df8ff541f374fafa44a303223da5895 | 94c896a4-a179-4da2-9e29-95e81923a2d6 fd21:f2e6:739b::/64 |
|                                      |         |                                  | c9a9a38d-83ec-47b1-8519-2bd570901ace 10.0.0.0/26         |
| 8e75954e-a21d-4484-94be-a3b5857e875b | shared  | 8760a0e5977b49f7bae9fb0c1c92ada8 | 59a4418d-e0dd-42ef-940c-6cb11e92848a 192.168.233.0/24    |
| cf363333-a89d-4fff-927e-77e3891b7577 | public  | 8760a0e5977b49f7bae9fb0c1c92ada8 | 247457f6-dbb6-4b28-8002-7d380cfca0ec 172.24.4.0/24       |
|                                      |         |                                  | 4ba00287-58d7-4d79-b5d4-dfc10b52a5a3 2001:db8::/64       |
+--------------------------------------+---------+----------------------------------+----------------------------------------------------------+```

```bash
neutron subnetpool-list
+--------------------------------------+------------------------------+----------------------------------+---------------------+-------------------+------------------+------------+
| id                                   | name                         | tenant_id                        | prefixes            | default_prefixlen | address_scope_id | is_default |
+--------------------------------------+------------------------------+----------------------------------+---------------------+-------------------+------------------+------------+
| 2bc06767-77c8-496b-bf7f-ac03b170018e | shared-default-subnetpool-v6 | 8760a0e5977b49f7bae9fb0c1c92ada8 | fd21:f2e6:739b::/56 | 64                |                  | True       |
| 52b84811-c891-4358-85a5-dc54d63750d3 | shared-default-subnetpool-v4 | 8760a0e5977b49f7bae9fb0c1c92ada8 | 10.0.0.0/22         | 26                |                  | True       |
+--------------------------------------+------------------------------+----------------------------------+---------------------+-------------------+------------------+------------+
```bash
neutron router-gateway-clear router1
```

```bash
neutron router-port-list router1
+--------------------------------------+------+----------------------------------+-------------------+------------------------------------------------------------------------------------------+
| id                                   | name | tenant_id                        | mac_address       | fixed_ips                                                                                |
+--------------------------------------+------+----------------------------------+-------------------+------------------------------------------------------------------------------------------+
| a68a2a35-d54a-436b-86ca-16e6171711d1 |      | 2df8ff541f374fafa44a303223da5895 | fa:16:3e:50:73:53 | {"subnet_id": "94c896a4-a179-4da2-9e29-95e81923a2d6", "ip_address": "fd21:f2e6:739b::1"} |
| f58aa95b-5da6-485f-b17d-07a7e7876d80 |      | 2df8ff541f374fafa44a303223da5895 | fa:16:3e:21:d0:74 | {"subnet_id": "c9a9a38d-83ec-47b1-8519-2bd570901ace", "ip_address": "10.0.0.1"}          |
+--------------------------------------+------+----------------------------------+-------------------+------------------------------------------------------------------------------------------+```

```bash
for i in $(neutron router-port-list router1 | grep 'subnet_id' | awk '{print $2}'); do neutron router-interface-delete router1 port=$i; done
neutron router-delete router1
```

```bash
neutron subnet-list
+--------------------------------------+---------------------+----------------------------------+---------------------+-------------------------------------------------------------------------------+
| id                                   | name                | tenant_id                        | cidr                | allocation_pools                                                              |
+--------------------------------------+---------------------+----------------------------------+---------------------+-------------------------------------------------------------------------------+
| 247457f6-dbb6-4b28-8002-7d380cfca0ec | public-subnet       | 8760a0e5977b49f7bae9fb0c1c92ada8 | 172.24.4.0/24       | {"start": "172.24.4.2", "end": "172.24.4.254"}                                |
| 4ba00287-58d7-4d79-b5d4-dfc10b52a5a3 | ipv6-public-subnet  | 8760a0e5977b49f7bae9fb0c1c92ada8 | 2001:db8::/64       | {"start": "2001:db8::3", "end": "2001:db8::ffff:ffff:ffff:ffff"}              |
|                                      |                     |                                  |                     | {"start": "2001:db8::1", "end": "2001:db8::1"}                                |
| 59a4418d-e0dd-42ef-940c-6cb11e92848a | shared-subnet       | 8760a0e5977b49f7bae9fb0c1c92ada8 | 192.168.233.0/24    | {"start": "192.168.233.2", "end": "192.168.233.254"}                          |
| 94c896a4-a179-4da2-9e29-95e81923a2d6 | ipv6-private-subnet | 2df8ff541f374fafa44a303223da5895 | fd21:f2e6:739b::/64 | {"start": "fd21:f2e6:739b::2", "end": "fd21:f2e6:739b:0:ffff:ffff:ffff:ffff"} |
| c9a9a38d-83ec-47b1-8519-2bd570901ace | private-subnet      | 2df8ff541f374fafa44a303223da5895 | 10.0.0.0/26         | {"start": "10.0.0.2", "end": "10.0.0.62"}                                     |
+--------------------------------------+---------------------+----------------------------------+---------------------+-------------------------------------------------------------------------------+```

```bash
# subnet の削除
neutron subnet-list | grep '\-subnet' | awk '{print $2}' | xargs neutron subnet-delete

# subnetpool の削除
neutron subnetpool-delete shared-default-subnetpool-v4
neutron subnetpool-delete shared-default-subnetpool-v6

# network の削除
neutron net-delete shared
neutron net-delete private
neutron net-delete public
```

##PoC ネットワークの構築
次に、検証用ネットワークを構築します。

```bash
openstack address scope create --share --ip-version 4 public
openstack network create --share --provider-physical-network public --provider-network-type flat provider1
openstack network set --external provider1
openstack subnet pool create --pool-prefix 172.24.6.0/24 --address-scope public provider_ext_1
openstack subnet pool create --pool-prefix 172.24.4.0/24 --address-scope public provider_gw1
openstack subnet pool create --pool-prefix 172.24.5.0/24 --address-scope public provider_gw2
openstack network create private
openstack subnet pool create --pool-prefix 10.0.0.0/16 --share selfservice1
openstack subnet create selfservice1 --subnet-pool selfservice1 --prefix-length 24 --network private

provider1_id=$(openstack network segment list | grep flat | awk '{print $2}')
openstack network segment set --name segment1 $provider1_id
openstack network segment create --physical-network public2 --network-type flat --network provider1 segment2
openstack subnet create provider_1 --subnet-pool provider_ext_1 --prefix-length 24 --network provider1 --service-type 'network:routed' --no-dhcp
openstack subnet set provider_1 --service-type 'network:router_gateway'
provider_1_subnet_id=$(openstack subnet list | grep provider_1 | awk '{print $2}')
openstack subnet set --service-type 'network:floatingip' $provider_1_subnet_id

openstack subnet create provider_gw1 --subnet-pool provider_gw1 --prefix-length 24 --network provider1 --service-type 'network:floatingip_agent_gateway' --no-dhcp --network-segment segment1
openstack subnet create provider_gw2 --subnet-pool provider_gw2 --prefix-length 24 --network provider1 --service-type 'network:floatingip_agent_gateway' --no-dhcp --network-segment segment2

openstack router create router1

neutron router-gateway-set router1 provider1

openstack router add subnet router1 selfservice1

private_id=`openstack network list | grep private\ | awk '{print $2}'`

# node2 の nova cell 情報を更新します
nova-manage cell_v2 discover_hosts

# security-group に ssh, icmp を許可する設定を追加します
sudo apt-get install -y jq
openstack security group list -f json | jq -r '.[].ID' | xargs -I {} openstack security group rule create {} --protocol tcp --ingress --dst-port 22
openstack security group list -f json | jq -r '.[].ID' | xargs -I {} openstack security group rule create {} --protocol icmp

openstack server create --flavor m1.tiny --image cirros-0.4.0-x86_64-disk --availability-zone nova:node1:node1 --nic net-id=$private_id test01
openstack server create --flavor m1.tiny --image cirros-0.4.0-x86_64-disk --availability-zone nova:node2:node2 --nic net-id=$private_id test02

openstack floating ip create provider1
openstack server add floating ip test01 172.24.6.84

openstack server list
+--------------------------------------+--------+--------+---------------------------------+--------------------------+---------+
| ID                                   | Name   | Status | Networks                        | Image                    | Flavor  |
+--------------------------------------+--------+--------+---------------------------------+--------------------------+---------+
| f51d4147-12b9-4ccf-8fe5-3fe2c642b028 | test02 | ACTIVE | private=10.0.0.135              | cirros-0.4.0-x86_64-disk | m1.tiny |
| 09f3e913-1809-4b07-9cd0-efe07c6591a7 | test01 | ACTIVE | private=10.0.0.185, 172.24.6.84 | cirros-0.4.0-x86_64-disk | m1.tiny |
+--------------------------------------+--------+--------+---------------------------------+--------------------------+---------+
```

##Dynamic Routing Agent の設定
dragent に対して、必要な設定を実施します。

はじめに、BGP Speaker を作成します。

```bash
neutron bgp-speaker-create --ip-version 4 --local-as 65501 bgp-speaker
+-----------------------------------+--------------------------------------+
| Field                             | Value                                |
+-----------------------------------+--------------------------------------+
| advertise_floating_ip_host_routes | True                                 |
| advertise_tenant_networks         | True                                 |
| id                                | 858872f7-4b8b-4f2e-93b9-7f4d9a068916 |
| ip_version                        | 4                                    |
| local_as                          | 65501                                |
| name                              | bgp-speaker                          |
| networks                          |                                      |
| peers                             |                                      |
| project_id                        | 8760a0e5977b49f7bae9fb0c1c92ada8     |
| tenant_id                         | 8760a0e5977b49f7bae9fb0c1c92ada8     |
+-----------------------------------+--------------------------------------+
```

次に、bgp-speaker に network を追加します。

```bash
neutron bgp-speaker-network-add bgp-speaker provider1
#出力
Added network provider1 to BGP speaker bgp-speaker.
```

bgp-speaker の設定は、次のようになります。

```bash
neutron bgp-speaker-show bgp-speaker
+-----------------------------------+--------------------------------------+
| Field                             | Value                                |
+-----------------------------------+--------------------------------------+
| advertise_floating_ip_host_routes | True                                 |
| advertise_tenant_networks         | True                                 |
| id                                | 858872f7-4b8b-4f2e-93b9-7f4d9a068916 |
| ip_version                        | 4                                    |
| local_as                          | 65501                                |
| name                              | bgp-speaker                          |
| networks                          | c4d4d43e-115b-4997-ba62-f246bc8f9bbd |
| peers                             |                                      |
| project_id                        | 8760a0e5977b49f7bae9fb0c1c92ada8     |
| tenant_id                         | 8760a0e5977b49f7bae9fb0c1c92ada8     |
+-----------------------------------+--------------------------------------+
```



```bash
neutron bgp-speaker-advertiseroute-list bgp-speaker
+----------------+--------------+
| destination    | next_hop     |
+----------------+--------------+
| 172.24.6.84/32 | 172.24.4.247 |
+----------------+--------------+
```

bgp-peer 'bgp-peer' を作成します。

```bash
neutron bgp-peer-create --peer-ip 192.168.1.13 --remote-as 65500 bgp-peer
Created a new bgp_peer:
+------------+--------------------------------------+
| Field      | Value                                |
+------------+--------------------------------------+
| auth_type  | none                                 |
| id         | d2c1642a-9adc-4059-99ae-9b76d1c07e3f |
| name       | bgp-peer                             |
| peer_ip    | 192.168.1.13                         |
| project_id | 8760a0e5977b49f7bae9fb0c1c92ada8     |
| remote_as  | 65500                                |
| tenant_id  | 8760a0e5977b49f7bae9fb0c1c92ada8     |
+------------+--------------------------------------+
```

BGP speaker 'bgp-speaker' に peer 'bgp-peer' を追加します。

```bash
neutron bgp-speaker-peer-add bgp-speaker bgp-peer
#出力
Added BGP peer bgp-peer to BGP speaker bgp-speaker.
```

```bash
dragent_id=$(neutron agent-list | grep dragent | grep node1 | awk '{print $2}')
neutron bgp-dragent-speaker-add ${dragent_id} bgp-speaker
#出力
Associated BGP speaker bgp-speaker to the Dynamic Routing agent.
```

次に、bgp-speaker 'bgp-speaker' を node1 上で稼働している dragent に追加します。
コマンドの実行結果は 'is already associated to a BGP speaker' となったので、明示的に設定する必要は無さそうでした。

```bash
neutron bgp-dragent-speaker-add ${dragent_id} bgp-speaker
#出力
BgpDrAgent 7ee16f55-9754-4a49-9377-cc91660050ff is already associated to a BGP speaker.
Neutron server returns request_ids: ['req-7563454b-d1d0-4a5e-a1bc-63fd380fb0f4']
```

最後に、bgp-speaker 'bgp-speaker' の稼働状況を確認します。次の出力では node1, node2 の2ノードで bgp-speaker が稼働していることが分かります。

```bash
neutron bgp-dragent-list-hosting-speaker bgp-speaker
+--------------------------------------+-------+----------------+-------+
| id                                   | host  | admin_state_up | alive |
+--------------------------------------+-------+----------------+-------+
| 7ee16f55-9754-4a49-9377-cc91660050ff | node1 | True           | :-)   |
| a7b181c0-7a2f-4d8c-985a-f26af1c209b6 | node2 | True           | :-)   |
+--------------------------------------+-------+----------------+-------+
```

##Routing　設定状況の確認
dragent の対向側(quagga) から見た、経路の設定状況を確認します。
quagga 上で BGP 経路情報を確認すると、instance の Floating-IP アドレスが Host Route として広報されていることが分かります。

なお、fixed-ip の subnet pool に対して address-scope を設定することで、fixed-ip についても BGP で経路を広報することが出来ます。
(Tenant Network <-> Bare Metal サーバ間で直接通信するようなユースケースで使用出来ます)

```bash
# node3
telnet localhost bgpd

# show ip bgp summary コマンドを実行すると、dragent との BGP 接続が確立していることが分かります
bgp_1> show ip bgp summary
BGP router identifier 192.168.1.14, local AS number 65500
RIB entries 1, using 112 bytes of memory
Peers 1, using 9088 bytes of memory

Neighbor        V         AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
192.168.1.11    4 65501      58      60        0    0    0 00:12:28        1

Total number of neighbors 1

Total num. Established sessions 1
Total num. of routes received     1

# show ip bgp コマンドを実行すると、dragent から受信した経路の情報を確認できます
# 下記では、172.24.6.84/32 のホスト経路が、next-hop: 172.24.4.247 に設定されていることが分かります
bgp_1> show ip bgp
BGP table version is 0, local router ID is 192.168.1.14
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
              i internal, r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*> 172.24.6.84/32   172.24.4.247                           0 65501 i

Displayed  1 out of 1 total prefixes
```

quagga の稼働しているホストから Floating-IP アドレスに対して ping を打つと、次のような結果となります。

```bash
vagrant@node3:~$ ping -nR -c 1 172.24.6.84
PING 172.24.6.84 (172.24.6.84) 56(124) bytes of data.
64 bytes from 172.24.6.84: icmp_seq=1 ttl=62 time=6.14 ms
RR:     172.24.4.253
        169.254.110.47
        10.0.0.1
        10.0.0.185
        10.0.0.185
        169.254.110.46
        172.24.4.247
        172.24.4.253


--- 172.24.6.84 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.145/6.145/6.145/0.000 ms
```

#検証結果
今回の検証結果としては、全く何の問題もなく Routed Provider Network 上で Floating-IP を使用できるようになりました。
また、貴重な Public IPv4 Address をインフラ側に割り当てる必要がなく、アドレスブロックの全領域をどの Rack からでも Floating-IP としてユーザに割り当てることができました。

最近の Top of Rack Switch であれば 1〜3万経路程度を余裕に捌けるはずなので、例えば 10 Rack 程度を1単位にて Route Aggregation をすることで、事実上無制限に compute-node を拡張し続けることができます。
(ただし、rabbitmq 等、コントロールプレーン側については本構成でも引き続きボトルネックになり得るので注意が必要です)

この他に検証の際に気づいたことをメモとして次にまとめておきます。

- proxy-arp を default router(今回の場合は node3) 側で設定することで、ToR を跨いだ instance(fip) <-> instance(fip) 間の通信ができるようになります
  - source ip が NAT にて floating-ip に書き換えられてしまい、destination と同一 network(172.24.6.0/24) となるため、proxy-arp をしていない環境だと node3 が ARP に答えず、default route として packet を受信出来ないため
- provider_gw1, provider_gw2 の ip address 空間を private ip にすることで public ip address の消費を抑えられるようになります
  - SNAT (fixed-ip only) instance を外部に接続する必要がない場合は、SNAT router に Private IP アドレスを割り当てることもできます
      - network node の external network に所属する subnet(provider_gw1) に対して service-type ('network:router_gateway') を設定します
    - 'Fast exit' を用いることで、outbound SNATの Outbound 通信を最適化することもできます
      - Tenant subnet の address scope を  External subnet と 同一にすることで、compute-node の fip namespace から直接 outbound 通信ができるようになります
      - https://bugs.launchpad.net/neutron/+bug/1577488