Using HA VRRP L3 Routers in OpenStack Neutron

OpenStack Juno introduced HA L3 Routers in Neutron, and I've spent a bit of time learning about how to configure this feature in Neutron as well as some of the caveats with its current implementation.

First off, lets go over the caveats.

  • Make sure you install keepalived
  • Each tenant with a HA router consumes 1 network, (VLAN, VXLAN, GRE)
    • The HA networks are not deleted after removal of the last HA router in a tenant Neutron Bug# 1367157
    • Not really an issue with VXLAN, but could be a problem with VLANs
  • Policy only allows admins to create a router as HA by default.
    • Enabling by default l3_ha = True breaks user creation unless they specify --ha false
    • Fixed Neutron Bug# 1388716
  • The router can not be Distributed and HA
  • The server enforces is_ha as a read only attribute

And now lets go over the config options

# By Default l3_ha defaults to 'False'
# This options defines if routers are set to be created as ha by default.
l3_ha = False

# Redundancy level 3 by default, as it should
# By default we want to allocated keepalived across three systems.
max_l3_agents_per_router = 3

# This is the absolute minimum routers we create
# If we can not schedule to at least 2 systems then fail creation, because we are not HA in such a case.
min_l3_agents_per_router = 2

# This is the network address range neutron will allocate to the dedicated HA network.
# This is a good setting, and will not have any effects on tenancy networking
l3_ha_net_cidr = 169.254.192.0/18

Let's elaborate on the 'l3_ha' setting.

class L3HARouterAgentPortBinding(model_base.BASEV2):
    @classmethod
    def _is_ha(cls, router):
        ha = router.get('ha')
        if not attributes.is_attr_set(ha):
            ha = cfg.CONF.l3_ha
        return ha

    def create_router(self, context, router):
        is_ha = self._is_ha(router['router'])

        router['router']['ha'] = is_ha
        router_dict = super(L3_HA_NAT_db_mixin,
                            self).create_router(context, router)

        if is_ha:
            ...
        return router_dict

We also have agent configuration

# Where do we put the config files
ha_confs_path = '$state_path/ha_confs'

# Default: "PASS"
# Other Options: "AH" (Authentication Header)
ha_vrrp_auth_type = PASS

# Default is unset
# If set, Keepalived communication will have password auth set in config
ha_vrrp_auth_password = 

# Advertisement Interval for keepalived
# This means somewhere between 3-4 seconds to failover
ha_vrrp_advert_int = 2

And that pretty much wraps up L3 HA from a user perspective.