Ryu SDN Frameworkで、VRRP動作を試してみる
これまで、あまり知られておりませんでしたが、Ryu SDN Frameworkでは、VRRP機能を動作させることができます。
VRRP機能を自由に操れるようになれば、柔軟なネットワーク冗長構成も構築できそうです。
今回の元ネタは、こちらになります。
http://ryu.readthedocs.org/en/latest/test-vrrp.html
◆ まずは、VRRP動作環境の準備
Ryu VRRPを動作させるLinux環境としては、Ubuntu Server版を使用しました。
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=14.04 DISTRIB_CODENAME=trusty DISTRIB_DESCRIPTION="Ubuntu 14.04.3 LTS"
VRRP動作環境としては、2台のLinuxサーバ間でVRRPを動作させるイメージです。
ただし、今回のブログ記事では、あくまでもVRRPプロトコルによるコントロールプレーンの挙動に特化しております。
VRRPによるネットワーク冗長切り替えに関わるデータプレーンは、スコープ外になります。
-------------- -------------- | Ryu | eth1 eth1 | Ryu | | VRRP1 | ------------------------------------------| VRRP2 | | | 192.168.0.2 192.168.0.3 | | -------------- -------------- VIP: 192.168.0.1
VRRPを動作させるインタフェースは、"eth1"とします。
必要なアドレス設定を事前に行っておきます。
$ ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:0c:29:1c:78:07 brd ff:ff:ff:ff:ff:ff inet 192.168.195.153/24 brd 192.168.195.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::20c:29ff:fe1c:7807/64 scope link valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:0c:29:1c:78:11 brd ff:ff:ff:ff:ff:ff inet 192.168.0.3/24 brd 192.168.0.255 scope global eth1 valid_lft forever preferred_lft forever inet6 fe80::20c:29ff:fe1c:7811/64 scope link valid_lft forever preferred_ft forever
Ryu SDN Frameworkをインストールします。
$ sudo apt-get update $ sudo apt-get install python-dev $ sudo apt-get install python-pip $ sudo apt-get -y install libxml2-dev $ sudo apt-get -y install python-lxml $ sudo pip install --upgrade six $ sudo apt-get install git $ git clone https://github.com/osrg/ryu.git $ cd ryu/tools/ $ sudo pip install -r pip-requires $ cd .. $ sudo python ./setup.py install
正しく、Ryuがインストールされたことを確認しておきます。
$ ryu-manager --version rya-manager 3.26
◆ Ryu VRRPサンプルアプリ
Linuxサーバに、おのおのRyu SDN Frameworkをインストールしたら、次のVRRPサンプルアプリを保存します。
(1) RyuVRRP1側のサンプルアプリ
import logging import datetime import time import netaddr from ryu.base import app_manager from ryu.lib import hub from ryu.controller import handler from ryu.services.protocols.vrrp import api as vrrp_api from ryu.services.protocols.vrrp import event as vrrp_event _VRRP_VERSION_V3 = 3 _VRID = 1 _VIRTUAL_IP_ADDRESS = '192.168.0.1' _PRIMARY_IP_ADDRESS = '192.168.0.2' _VIRTUAL_MAC_ADDRESS = '00:00:5e:00:01:01' _PRIORITY = 250 _IFNAME = 'eth1' _PREEMPT_DELAY = 10 LOG = logging.getLogger('SampleVrrp') LOG.setLevel(logging.DEBUG) logging.basicConfig() class SampleVrrp(app_manager.RyuApp): def __init__(self, *args, **kwargs): super(SampleVrrp, self).__init__(*args, **kwargs) hub.spawn(self._test_senario) def _test_senario(self): LOG.info("") LOG.info("////// 1. Create Vrrp Router //////") vrrp_mgr = self._configure_vrrp_router(_VRRP_VERSION_V3, _PRIORITY, _PRIMARY_IP_ADDRESS, _VIRTUAL_IP_ADDRESS, _IFNAME, _VRID, _PREEMPT_DELAY) time.sleep(30) LOG.info("") LOG.info("////// 2. Change Priority [250] -> [50] //////") self._configure_vrrp_change(_VRID, 50) time.sleep(30) LOG.info("") LOG.info("////// 3. Change Priority [50] -> [250] //////") self._configure_vrrp_change(_VRID, _PRIORITY) time.sleep(30) LOG.info("") LOG.info("////// 4. Shutdown Vrrp Router //////") self._shutdown_vrrp_router(_VRID) def _configure_vrrp_change(self, vrid, priority): instance_name = self._lookup_instance(vrid) if not instance_name: raise RPCError('vrid %d is not found' % (vrid)) vrrp_api.vrrp_config_change(self, instance_name, priority=priority) def _shutdown_vrrp_router(self, vrid): instance_name = self._lookup_instance(vrid) if not instance_name: raise RPCError('vrid %d is not found' % (vrid)) vrrp_api.vrrp_shutdown(self, instance_name) def _lookup_instance(self, vrid): for instance in vrrp_api.vrrp_list(self).instance_list: if vrid == instance.config.vrid: return instance.instance_name return None def _configure_vrrp_router(self, vrrp_version, vrrp_priority, primary_ip_address, virtual_ip_address, ifname, vrid, preempt_delay): interface = vrrp_event.VRRPInterfaceNetworkDevice( _VIRTUAL_MAC_ADDRESS, primary_ip_address, None, ifname) ip_addresses = [virtual_ip_address] config = vrrp_event.VRRPConfig( version=vrrp_version, vrid=vrid, priority=vrrp_priority, ip_addresses=ip_addresses, preempt_delay=preempt_delay) config_result = vrrp_api.vrrp_config(self, interface, config) return config_result @handler.set_ev_cls(vrrp_event.EventVRRPStateChanged) def vrrp_state_changed_handler(self, ev): old_state = ev.old_state new_state = ev.new_state now = datetime.datetime.now() now_time = now.strftime("%H:%M:%S") micro_time = "%03d" % (now.microsecond // 1000) LOG.debug("%s.%s: State Changed [%s] -> [%s]" % (now_time, micro_time, old_state, new_state))
(2) RyuVRRP2側のサンプルアプリ
import logging import datetime from ryu.base import app_manager from ryu.lib import hub from ryu.controller import handler from ryu.services.protocols.vrrp import api as vrrp_api from ryu.services.protocols.vrrp import event as vrrp_event _VRRP_VERSION_V3 = 3 _VRID = 1 _VIRTUAL_IP_ADDRESS = '192.168.0.1' _PRIMARY_IP_ADDRESS = '192.168.0.3' _VIRTUAL_MAC_ADDRESS = '00:00:5e:00:01:01' _PRIORITY = 100 _IFNAME = 'eth1' _PREEMPT_DELAY = 10 LOG = logging.getLogger('SampleVrrp') LOG.setLevel(logging.DEBUG) logging.basicConfig() class SampleVrrp(app_manager.RyuApp): def __init__(self, *args, **kwargs): super(SampleVrrp, self).__init__(*args, **kwargs) hub.spawn(self._test_senario) def _test_senario(self): LOG.info("") LOG.info("////// 1. Create Vrrp Router //////") vrrp_mgr = self._configure_vrrp_router(_VRRP_VERSION_V3, _PRIORITY, _PRIMARY_IP_ADDRESS, _VIRTUAL_IP_ADDRESS, _IFNAME, _VRID, _PREEMPT_DELAY) def _configure_vrrp_router(self, vrrp_version, vrrp_priority, primary_ip_address, virtual_ip_address, ifname, vrid, preempt_delay): interface = vrrp_event.VRRPInterfaceNetworkDevice( _VIRTUAL_MAC_ADDRESS, primary_ip_address, None, ifname) ip_addresses = [virtual_ip_address] config = vrrp_event.VRRPConfig( version=vrrp_version, vrid=vrid, priority=vrrp_priority, ip_addresses=ip_addresses, preempt_delay=preempt_delay) rep = vrrp_api.vrrp_config(self, interface, config) return rep @handler.set_ev_cls(vrrp_event.EventVRRPStateChanged) def vrrp_state_changed_handler(self, ev): old_state = ev.old_state new_state = ev.new_state now = datetime.datetime.now() now_time = now.strftime("%H:%M:%S") micro_time = "%03d" % (now.microsecond // 1000) LOG.debug("%s.%s: State Changed [%s] -> [%s]" % (now_time, micro_time, old_state, new_state))
◆ 早速、VRRPサンプルアプリを動かしてみる
まずは、Ryu VRRP2側サンプルアプリを起動したのちに、Ryu VRRP1側サンプルアプリを起動します。
すると、Ryu VRRPサンプルアプリ動作として、以下のテストシナリオが自動で開始されます。
(1) Ryu VRRP1サンプルアプリ動作テストシナリオ
1. VRRP Router起動
2. Priority値を"250"から"50"に変更
3. Priority値を"50"から"250"に変更
4. VRRP Router停止
(2) Ryu VRRP2サンプルアプリ動作テストシナリオ
1. VRRP Router起動
ちなみに、Backup -> Masterへの状態遷移に関わるPreempt modeは有効になっております。[preempt delay: 10秒]
Ryu VRRPサンプルアプリを動作させると、コマンド結果として、いろいろとデバック情報が表示されます。
Ryu VRRP1起動結果
$ sudo ryu-manager sample_vrrp1.py loading app sample_vrrp1.py loading app ryu.services.protocols.vrrp.manager instantiating app sample_vrrp.py of SampleVrrp instantiating app ryu.services.protocols.vrrp.manager of VRRPManager ////// 1. Create Vrrp Router ////// instantiating app None of VRRPInterfaceMonitorNetworkDevice instantiating app None of VRRPRouterV3 14:12:05.593: State Changed [None] -> [Initialize] 14:12:05.594: State Changed [Initialize] -> [Backup] VRRPV3StateBackup preempt_delay 14:12:15.989: State Changed [Backup] -> [Master] ////// 2. Change Priority [250] -> [50] ////// VRRPV3StateMaster vrrp_config_change_request 14:12:45.603: State Changed [Master] -> [Backup] ////// 3. Change Priority [50] -> [250] ////// VRRPV3StateBackup vrrp_config_change_request VRRPV3StateBackup preempt_delay 14:13:15.645: State Changed [Backup] -> [Master] ////// 4. Shutdown Vrrp Router ////// 14:13:35.600: State Changed [Master] -> [Initialize]
Ryu VRRP2起動結果
$ sudo ryu-manager sample_vrrp2.py loading app sample_vrrp2.py loading app ryu.services.protocols.vrrp.manager instantiating app sample_vrrp.py of SampleVrrp instantiating app ryu.services.protocols.vrrp.manager of VRRPManager ////// 1. Create Vrrp Router ////// instantiating app None of VRRPInterfaceMonitorNetworkDevice instantiating app None of VRRPRouterV3 14:11:58.294: State Changed [None] -> [Initialize] 14:11:58.295: State Changed [Initialize] -> [Backup] 14:12:01.908: State Changed [Backup] -> [Master] 14:12:15.922: State Changed [Master] -> [Backup] VRRPV3StateBackup preempt_delay 14:12:45.533: State Changed [Backup] -> [Master] 14:13:15.578: State Changed [Master] -> [Backup] 14:13:36.144: State Changed [Backup] -> [Master]
Ryu VRRPサンプルアプリのデバック情報のみだと、本当に、VRRPが正しく動作しているのかを確認するのも、困難だと思います。
そこで、今回の動作テスト時に、Linuxサーバ間でPacketキャプチャしておいて、デバック情報とPacketキャプチャ結果を目視で紐付けてみました。
おかげで、Backup -> Masterへの状態遷移に関わるPreempt delayの挙動と、実際のVRRP Advertisementパケットの送出タイミングの因果関係を理解できるようになりました。以下に、VRRP状態遷移の様子になります。
実際、VRRPパケットダンプも貼っておきます。
今回、はじめて気がついたのですが、VRRP Advertisementパケットには、仮想MACアドレス情報は設定されないんですよね。
Ryu VRRPモジュールでの仮想MACアドレスの扱いについて、もう少し、リバースエンジニアリングが必要かもしれません。