Merge "Devref to explain nova-neutron interactions during live-migration"

This commit is contained in:
Jenkins 2017-04-20 18:49:27 +00:00 committed by Gerrit Code Review
commit 39c05533a3
6 changed files with 490 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -0,0 +1,144 @@
..
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Generated using plantuml.
@startuml
title live-migration with ovs-hybrid plug : nova<->neutron interactions
participant nova_conductor
participant nova_compute1
participant nova_compute2
participant neutron_server
participant neutron_l2_agent2
participant neutron_l2_agent1
participant neutron_l3_agent2
participant neutron_l3_agent1
nova_conductor -> nova_compute1 : live_migrate
activate nova_compute1
nova_compute1 -> nova_compute2 : RPC.call : pre_live_migrate()
activate nova_compute2
nova_compute2 -> neutron_server : REST : list_port()
activate neutron_server
neutron_server -> nova_compute2
deactivate neutron_server
group port plugged on host2
nova_compute2 -> nova_compute2 : plug_vifs()
activate neutron_l2_agent2
neutron_l2_agent2 -> neutron_server : RPC.call : get_devices_details_list_and_failed_devices(devices : [port])
activate neutron_server
neutron_server -> neutron_l2_agent2
deactivate neutron_server
neutron_l2_agent2 -> neutron_server : RPC.call : update_device_list(devices_up : [port])
activate neutron_server
neutron_server -> neutron_l2_agent2
deactivate neutron_server
note over neutron_server
port status is never
changed since port
is not bound to host2
end note
deactivate neutron_l2_agent2
end
nova_compute2 -> nova_compute1
deactivate nova_compute2
group proactive dvr router creation
nova_compute1 -> neutron_server : REST : update_port('binding:profile'={'migrating_to':'host2'})
activate neutron_server
neutron_server -> neutron_l3_agent1: RPC.cast(fanout) : port_update(port)
activate neutron_l3_agent1
destroy neutron_l3_agent1
note over neutron_l3_agent1
"migrating_to" does
not match host
end note
neutron_server -> neutron_l3_agent2: RPC.cast(fanout) : port_update(port)
activate neutron_l3_agent2
note over neutron_l3_agent2
proactively create DVR router
end note
deactivate neutron_l3_agent2
neutron_server -> nova_compute1
deactivate neutron_server
end
note over nova_compute1, nova_compute2
libvirt handles the live-migration
end note
note left of nova_compute1
live migration
succeded
end note
nova_compute1 -> nova_compute1 : post_live_migration
group port unplugged on host1
nova_compute1 -> nova_compute1 : unplug_vifs()
activate neutron_l2_agent1
neutron_l2_agent1 -> neutron_server : RPC.call : update_device_list(devices_down : [port])
activate neutron_server
neutron_server -> neutron_server : update_port_status(DOWN)
neutron_server -> neutron_l2_agent1
deactivate neutron_l2_agent1
note over neutron_server
port status changed to
DOWN since port is bound
to host1
end note
deactivate neutron_server
end
nova_compute1 -> nova_compute2 : RPC.cast : post_live_migration_at_destination()
deactivate nova_compute1
activate nova_compute2
nova_compute2 -> neutron_server : REST : update_port({'binding:host_id':'host2', 'binding:profile':{}})
activate neutron_server
neutron_server -> neutron_server : update_port_status(DOWN)
neutron_server -> neutron_l2_agent1 : RPC.cast(fanout) : port_update(port)
activate neutron_l2_agent1
destroy neutron_l2_agent1
note over neutron_l2_agent1
port not hosted
on host1
end note
neutron_server -> neutron_l2_agent2 : RPC.cast(fanout) : port_update(port)
activate neutron_l2_agent2
neutron_server -> nova_compute2
deactivate nova_compute2
deactivate neutron_server
group port_update processed by agent that really hosts the port
neutron_l2_agent2 -> neutron_server : RPC.call : get_devices_details_list_and_failed_devices(devices : [port])
activate neutron_server
neutron_server -> neutron_server : update_port_status(BUILD)
neutron_server -> neutron_l2_agent2
deactivate neutron_server
neutron_l2_agent2 -> neutron_server : RPC.call : update_device_list(devices_up : [port])
activate neutron_server
neutron_server -> neutron_server : update_port_status(ACTIVE)
neutron_server -> neutron_l2_agent2
deactivate neutron_server
note over neutron_server
port status changed to
ACTIVE since port
is now bound to host2
end note
deactivate neutron_l2_agent2
end group
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@ -0,0 +1,155 @@
..
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Generated using plantuml.
@startuml
title live-migration with ovs-normal plug : nova<->neutron interactions
participant nova_conductor
participant nova_compute1
participant nova_compute2
participant neutron_server
participant neutron_l2_agent2
participant neutron_l2_agent1
participant neutron_l3_agent2
nova_conductor -> nova_compute1 : live_migrate
activate nova_compute1
nova_compute1 -> nova_compute2 : RPC.call : pre_live_migrate()
activate nova_compute2
nova_compute2 -> neutron_server : REST : list_port()
activate neutron_server
neutron_server -> nova_compute2
deactivate neutron_server
nova_compute2 -> nova_compute2 : plug_vifs()
nova_compute2 -> nova_compute1
deactivate nova_compute2
group proactive dvr router creation
nova_compute1 -> neutron_server : REST : update_port('binding:profile'={'migrating_to':'host2'})
activate neutron_server
neutron_server -> neutron_l3_agent1: RPC.cast(fanout) : port_update(port)
activate neutron_l3_agent1
destroy neutron_l3_agent1
note over neutron_l3_agent1
"migrating_to" does
not match host
end note
neutron_server -> neutron_l3_agent2: RPC.cast(fanout) : port_update(port)
activate neutron_l3_agent2
note over neutron_l3_agent2
proactively create DVR router
end note
deactivate neutron_l3_agent2
neutron_server -> nova_compute1
deactivate neutron_server
end
note over nova_compute1, nova_compute2
libvirt handles the live-migration
end note
group port plugged on host2
note over nova_compute2
libvirt creates tap device
end note
neutron_l2_agent2 -> neutron_server : RPC.call : get_devices_details_list_and_failed_devices(devices : [port])
activate neutron_l2_agent2
activate neutron_server
neutron_server -> neutron_l2_agent2
deactivate neutron_server
neutron_l2_agent2 -> neutron_server : RPC.call : update_device_list(devices_up : [port])
activate neutron_server
neutron_server -> neutron_l2_agent2
deactivate neutron_server
note over neutron_server
port status is never
changed since port
is not bound to host2
end note
deactivate neutron_l2_agent2
end
group port unplugged on host1
note over nova_compute1
libvirt destroys the VM
and corresponding tap device
end note
neutron_l2_agent1 -> neutron_server : RPC.call : update_device_list(devices_down : [port])
activate neutron_l2_agent1
activate neutron_server
neutron_server -> neutron_server : update_port_status(DOWN)
neutron_server -> neutron_l2_agent1
deactivate neutron_l2_agent1
note over neutron_server
port status changed to
DOWN since port is bound
to host1
end note
deactivate neutron_server
end
note left of nova_compute1
live migration
succeded
end note
nova_compute1 -> nova_compute1 : post_live_migration
nova_compute1 -> nova_compute1 : unplug_vifs()
nova_compute1 -> nova_compute2 : RPC.cast : post_live_migration_at_destination()
deactivate nova_compute1
activate nova_compute2
nova_compute2 -> neutron_server : REST : update_port({'binding:host_id':'host2', 'binding:profile':{}})
activate neutron_server
neutron_server -> neutron_server : update_port_status(DOWN)
neutron_server -> neutron_l2_agent1 : RPC.cast(fanout) : port_update(port)
activate neutron_l2_agent1
destroy neutron_l2_agent1
note over neutron_l2_agent1
port not hosted
on host1
end note
neutron_server -> neutron_l2_agent2 : RPC.cast(fanout) : port_update(port)
activate neutron_l2_agent2
neutron_server -> nova_compute2
deactivate nova_compute2
deactivate neutron_server
group port_update processed by agent that really hosts the port
neutron_l2_agent2 -> neutron_server : RPC.call : get_devices_details_list_and_failed_devices(devices : [port])
activate neutron_server
neutron_server -> neutron_server : update_port_status(BUILD)
neutron_server -> neutron_l2_agent2
deactivate neutron_server
neutron_l2_agent2 -> neutron_server : RPC.call : update_device_list(devices_up : [port])
activate neutron_server
neutron_server -> neutron_server : update_port_status(ACTIVE)
neutron_server -> neutron_l2_agent2
deactivate neutron_server
note over neutron_server
port status changed to
ACTIVE since port
is now bound to host2
end note
deactivate neutron_l2_agent2
end group
@enduml

View File

@ -83,6 +83,7 @@ Neutron Internals
provisioning_blocks
retries
l3_agent_extensions
live_migration
Testing
-------

View File

@ -0,0 +1,190 @@
..
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Convention for heading levels in Neutron devref:
======= Heading 0 (reserved for the title in a document)
------- Heading 1
~~~~~~~ Heading 2
+++++++ Heading 3
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
Live-migration
==============
Let's consider a VM with one port migrating from host1 with nova-compute1,
neutron-l2-agent1 and neutron-l3-agent1 to host2 with nova-compute2 and
neutron-l2-agent2 and neutron-l3agent2.
Since the VM that is about to migrate is hosted by nova-compute1, nova sends
the live-migration order to nova-compute1 through RPC.
Nova Live Migration consists of the following stages:
* Pre-live-migration
* Live-migration-operation
* Post-live-migration
Pre-live-migration actions
--------------------------
Nova-compute1 will first ask nova-compute2 to perform pre-live-migration
actions with a synchronous RPC call.
Nova-compute2 will use neutron REST API to retrieve the list of VM's ports.
Then, it calls its vif driver to create the VM's port (VIF) using plug_vifs().
In the case Open vSwitch Hybrid plug is used, Neutron-l2-agent2 will detect
this new VIF, request the device details from the neutron server and configure
it accordingly. However, port's status won't change, since this port is
not bound to nova-compute2.
Nova-compute1 calls setup_networks_on_hosts. This updates the Neutron ports
binding:profile with the information of the target host. The port update RPC
message sent out by Neutron server will be received by neutron-l3-agent2,
which proactively sets up the DVR router.
If pre-live-migration fails, nova rollbacks and port is removed from host2.
If pre-live-migration succeeds, nova proceeds with live-migration-operation.
Potential error cases related to networking
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Plugging the VIFs on host2 fails
As Live migration operation was not yet started, the instance resides
active on host1.
.. _live_mig_operation:
Live-migration-operation
------------------------
Once nova-compute2 has performed pre-live-migration actions, nova-compute1 can
start the live-migration. This results in the creation of the VM and its
corresponding tap interface on node 2.
In the case Open vSwitch normal plug, linux bridge or MacVTap is being used,
Neutron-l2-agent2 will detect this new tap device and configure it accordingly.
However, port's status won't change, since this port is not bound to
nova-compute2.
As soon as the instance is active on host2, the original instance on host1
gets removed and with it the corresponding tap device. Assuming OVS-hybrid plug
is NOT used, Neutron-l2-agent1 detects the removal and tells the neutron
server to set the port's status to DOWN state with RPC messages.
There is no rollback if failure happens in live-migration-operation stage.
TBD: Error are handled by the post-live-migration stage.
Potential error cases related to networking
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Some host devices that are specified in the instance definition are not
present on the target host. Migration fails before it really started.
This can happen with MacVTap agent. See bug
https://bugs.launchpad.net/bugs/1550400
Post-live-migration actions
---------------------------
Once live-migration succeeded, both nova-compute1 and nova-compute2
perform post-live-migration actions. Nova-compute1 which is aware of
the success will send a RPC cast to nova-compute2 to tell it to perform
post-live-migration actions.
On host2, nova-compute2 sends a REST call
"update_port(binding=host2, profile={})" to the neutron server to tell it to
update the port's binding. This will clear the port binding information and
move the port's status to DOWN. The ML2 plugin will then try to rebind the
port according to its new host. This update_port REST call always triggers a
port-update RPC fanout message to every neutron-l2-agent. Since
neutron-l2-agent2 is now hosting the port, it will take this message into
account and re-synchronize the port by asking the neutron server details about
it through RPC messages. This will move the port from DOWN status to BUILD,
and then back to ACTIVE.
This update also removes the 'migrating_to' value from the portbindng
dictionary. It's not clearing it totally, like indicated by {}, but just
removing the 'migrating_to' key and value.
On host1, nova-compute1 calls its vif driver to unplug the VM's port.
Assuming, Open vSwitch Hybrid plug is used, Neutron-l2-agent1 detects the
removal and tells the neutron server to set the port's status to DOWN state
with RPC messages. For all other cases this happens as soon as the instance
and its tap device got destroyed on host1, like described in
:ref:`live_mig_operation`.
If neutron didn't already processed the REST call "update_port(binding=host2)",
the port status will effectively move to BUILD and then to DOWN. Otherwise,
the port is bound to host2, and neutron won't change the port status since
it's not bound the host that is sending RPC messages.
There is no rollback if failure happens in post-live-migration stage. In the
case of an error, the instance is set into "ERROR" state.
Potential error cases related to networking
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Portbinding for host2 fails
If this happens, the vif_type of the port is set to 'binding_failed'.
When Nova tries to recreated the domain.xml on the migration target
it will stumble over this invalid vif_type and fail. The instance is put
into "ERROR" state.
Post-Copy Migration
-------------------
Usually, Live Migration is executed as pre-copy migration. The instance is
active on host1 until nearly all memory has been copied to host2. If a
certain threshold of copied memory is met, the instance on the source get's
paused, the rest of the memory copied over and the instance started on
the target. The challenge with this approach is, that migration might take
a infinite amount of time, when the instance is heavily writing to memory.
This issue gets solved with post-copy migration. At some point in time,
the instance on host2 will be set to active, although still
a huge amount of memory pages reside only on host1. The phase that starts
now is called the post_copy phase. If the instance tries to access a memory
page that has not yet been transferred, libvirt/qemu takes care of moving this
page to the target immediately. New pages will only be written to the source.
With this approach the migration operation takes a finite amount of time.
Today, the rebinding of the port from host1 to host2 happens
in the post_live_migration phase, after migration finished. This is fine for
the pre-copy case, as the time windows between the activation of the instance
on the target and the binding of the port to the target is pretty small. This
becomes more problematic for the post-copy migration case. The instance
becomes active on the target pretty early but the portbinding still happens
after migration finished. During this time window, the instance might not be
reachable via the network. This should be solved with bug
https://bugs.launchpad.net/nova/+bug/1605016
Flow Diagram
------------
OVS Normal plug, Linux bridge, MacVTap, SR-IOV
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. image:: images/live-mig.png
OVS-Hybrid plug
~~~~~~~~~~~~~~~
The sequence with RPC messages from neutron-l2-agent processed first is
described in the following UML sequence diagram
.. image:: images/live-mig-ovs-hybrid.png