- 积分
- 16840
在线时间 小时
最后登录1970-1-1
|

楼主 |
发表于 2021-8-30 17:35:43
|
显示全部楼层
一. 当实例所在的节点发生故障不可用时,可执行evacuate操作,在另外一个新节点rebuild该实例,实现高可用。8 u( P6 ]( p6 {9 j
这可以是OpenStack计算节点HA的一种实现方案。
! |( p+ [* T+ M- q9 Q
# m* P8 R! d( h {二. API调用! I5 I9 b6 j0 [! k
nova.servers.evacuate(server=fm['id']), on_shared_storage=True
7 E2 d! j: ?4 u( ?( ^$ d1. on_shared_storage参数在2.14版本后废除,自动检查是否为共享存储。1 R% x8 @$ M- `5 r4 ?! D7 e7 n
共享存储能够保证实例在另外新节点重建后数据不丢失 d {4 S6 K% z8 Q
2. 可以设置目的主机host/ V: o. |6 }2 {' ?' J, ^- H R
如果不设置host,nova会通过scheduler选择一个新的主机(不会分到原主机,因为rebuild函数中过滤了原主机)( a' x! \9 `$ g, e& X
3. 这个调用只是发送了evacuate操作命令,具体是否真正疏散成功,无法知道- I' |1 |; v8 ^2 M9 J9 |
6 j' P: n( P% q6 s三. 源码分析$ J0 ^" N5 \( l- Y M, X
对应的是/nova/compute/api.py
- i6 r5 _& T, a7 c% V+ Z j2 d@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,/ c3 Y9 m4 p/ y5 s$ k* C7 [
vm_states.ERROR])
; ?0 m, {6 N8 y$ C( C9 D7 pdef evacuate(self, context, instance, host, on_shared_storage,( C- \- ]/ _8 X+ e4 L3 I- E
admin_password=None)$ T2 [: n; J$ P9 n5 ?
1. 函数上方有装饰符 @check_instance_state- ?: [% E" w2 o2 F% ~
表示在执行evacuate方法前先执行check_instance_state:检测传入的instance的vm_state是否为ACTIVE、STOPPED或ERROR。如果不是这三种状态,不能执行evacuate方法。
# c, m- |4 x) V$ x
8 Z( V5 t4 B3 S2 F) Z3 n2. 首先检测instance所在主机的状态是否为down,如果不是down(比如up),执行会出错。
- \% w' a% U. i. k3 S+ P; `LOG.debug('vm evacuation scheduled', instance=instance)
C, V3 ?1 q; e( M# 原实例所在主机$ X( ^1 ^; ~$ c5 V: H
inst_host = instance.host
; u2 n0 Q+ h: i5 }1 x2 Uservice = objects.Service.get_by_compute_host(context, inst_host)4 S8 v4 W+ L5 Z. i
# 首先确保compute主机的状态为down
' m) h: w$ W1 kif self.servicegroup_api.service_is_up(service):
7 F. h' G4 w- I; r4 j LOG.error(_LE('Instance compute service state on %s '. p- D g% m/ } t& l) u
'expected to be down, but it was up.'), inst_host)8 U" `% Q& h5 K% @
raise exception.ComputeServiceInUse(host=inst_host); ^$ J, o3 k/ J, p4 P. t
3 _! d0 b$ I `. J
3. 记录action执行操作
. s+ w) c& p- [1 A/ U# 实例的任务状态设置为REBUILDING% m0 C7 _0 n8 g3 l+ i# J
instance.task_state = task_states.REBUILDING% p) @$ b( Q3 g
instance.save(expected_task_state=[None]); `& S" L6 m& W7 {4 x, z
self._record_action_start(context, instance, instance_actions.EVACUATE)3 U+ E$ F: N( S( V6 a! {, w
# y4 [" b( w8 r% i/ g4 c
4. 初始化迁移类: o$ ?8 D3 |- f) s y! M# p0 ?
migration = objects.Migration(context,
+ L6 U4 |" [+ E5 v a source_compute=instance.host,
' w& u+ z8 {8 A2 {6 `/ s: d0 F( i source_node=instance.node,5 n; Y @2 _8 @! i7 a8 F
instance_uuid=instance_uuid,; c: Z4 T8 n; [: w: H
status='accepted',7 r/ Q( H. u) U$ d a
migration_type='evacuation') _7 @# T1 n5 G( n3 |5 p
5. 创建迁移(这里为什么要创建migration,并没有执行迁移)0 B# i7 m* J3 t% |$ d2 B. r& d
# 如果提供了目的主机! Z8 J: b( G' ?
if host:$ ~" A/ u7 _& Q2 u5 |8 @0 @
migration.dest_compute = host
7 x5 A+ f3 H) B- o g. Q4 I' S- u' Fmigration.create()6 [9 n5 j3 O& N6 y8 n
8 M( G" B+ A% q4 Q6. 发送消息通知实例的使用配额
# Q5 W" `8 |# ycompute_utils.notify_about_instance_usage(' l) j# D) I% a' n
self.notifier, context, instance, "evacuate")
$ A( t& \* G k* t* X6 Y: X, a& \! l& {* g7 ^
7. 最后执行task任务:rebuild_instance# \6 p( @" d7 E( o
所以evacuate的本质是在新节点上执行rebuild操作
6 `- B, Z+ @2 C' y( U' Yreturn self.compute_task_api.rebuild_instance(context,
8 h$ S; B9 S h4 [ instance=instance, ^6 \! A7 b' T1 k4 W2 W6 {' f
new_pass=admin_password,
" e$ O+ G) u0 f% O8 e7 p injected_files=None,
, B7 z) s8 k9 B! D7 Q2 w image_ref=None,7 w/ I: |3 i' ]5 m; \4 N7 r
orig_image_ref=None,# y1 }, o1 D7 @$ b* |8 ? X
orig_sys_metadata=None,+ R- t- B8 D0 B# z1 D) E. K
bdms=None,
) f8 N. r- H: T* T recreate=True,
, f# h# K- H% w8 d! }, i6 W on_shared_storage=on_shared_storage,
: V* n+ ]% w" W- p8 e: d host=host). C% t7 x2 p& Y
深入分析rebuild_instance方法,通过各种rpc调用,最终具体执行的是/nova/conductor/manager.py3 a' @- z* Q/ s% f) s7 c1 T
def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
- {- w" Z b: K injected_files, new_pass, orig_sys_metadata,
% _& U) X" v' i7 z' N& T; J9 C- A bdms, recreate, on_shared_storage,8 T% j5 j! c. m4 E
preserve_ephemeral=False, host=None):/ b6 t8 l2 j. m
(1)在选择新目的主机时先排除instance所在主机
' E% L2 f& b v$ F3 I这样能确保不会在原主机上执行rebuild操作
% h# j9 @# z# l; }6 y# 排除原实例所在的主机,即不能在同一个主机里进行rebuild' p) S7 [' O; R7 K* L7 v
filter_properties = {'ignore_hosts': [instance.host]}
) K- X( n" R t E+ dhosts = self.scheduler_client.select_destinations(context,
: N e, z( T% D) {; \ request_spec,
# B7 |3 T2 t$ H; P$ K filter_properties)5 T' L8 h) V$ c+ o h, C
(2)接下来会通过scheduler模块筛选出合适的新主机
# s9 \; y' T2 c3 j6 ^(3)如果没有选出足够的合适新主机,则抛出异常. T% L' u# e3 ?" h# \ B' C9 d
except exception.NoValidHost as ex:, U8 ~. p/ Q! D
with excutils.save_and_reraise_exception():
! ~+ r1 q7 e) R; A self._set_vm_state_and_notify(context, instance.uuid,
S! I; o9 |! `+ }( H7 N* e2 b3 U 'rebuild_server',! X. B% }& W+ g4 T; {
{'vm_state': instance.vm_state,
; P2 M' j, x0 Z2 l$ A 'task_state': None}, ex, request_spec)* @/ ?2 h, t7 G0 _
LOG.warning(_LW("No valid host found for rebuild"), `2 x6 x b' q; `
instance=instance)
! }1 \& b- l, a; \4 u不能选出合适的新主机,有可能是除了原节点外,其他节点都不可用(computer service status:disabled)或网络不通(computer service state:down),导致没有合适的新主机。. v" i. E, ]. G& c0 l/ D0 A9 H" ~
+ D6 F7 u v# n |
|