|
|
当实例所在的节点发生故障不可用时,可执行evacuate操作,在另外一个新节点rebuild该实例,实现高可用。' T, |; x# Q0 `2 ~$ Q' I" @6 P
这可以是OpenStack计算节点HA的一种实现方案。
# D* H' s, R! R, G- [' q) s6 _5 N t$ Q* m+ f8 @( w
API调用# d$ k( H3 M! l' H: Z% M
nova.servers.evacuate(server=fm['id']), on_shared_storage=True
3 J# s. b/ t+ g$ c) \! R% y, Q1. on_shared_storage参数在2.14版本后废除,自动检查是否为共享存储。 p( G0 ]& H6 d/ D
共享存储能够保证实例在另外新节点重建后数据不丢失* k8 p4 \ }3 a+ e( y/ t7 {- q1 H
2. 可以设置目的主机host
2 H! ]: V4 D3 l 如果不设置host,nova会通过scheduler选择一个新的主机(不会分到原主机,因为rebuild函数中过滤了原主机)/ e) `& C6 J M& P
3. 这个调用只是发送了evacuate操作命令,具体是否真正疏散成功,无法知道# W9 E. Y z$ S/ I8 M0 e. f* u
9 U0 A5 H4 A( V- Q
源码分析
4 P7 G: H) Q& g: r$ }( m/ b1 U对应的是/nova/compute/api.py
: N7 k! x0 z0 R8 C1 {) o4 c4 A( e@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,3 k. s* W/ y F: V" A! C% ~
vm_states.ERROR])3 D S0 y2 w" E6 h9 t) U
def evacuate(self, context, instance, host, on_shared_storage,' z0 ` g5 m4 o) U0 _
admin_password=None)" }/ P# P6 C, x9 d
1. 函数上方有装饰符 @check_instance_state
* @2 t7 W2 F4 K+ ^6 Q表示在执行evacuate方法前先执行check_instance_state:检测传入的instance的vm_state是否为ACTIVE、STOPPED或ERROR。如果不是这三种状态,不能执行evacuate方法。1 W ]& }0 D+ b5 D- l
7 ^& Q+ ?+ u& Q" L2. 首先检测instance所在主机的状态是否为down,如果不是down(比如up),执行会出错。. c0 e" t0 X$ y: O
LOG.debug('vm evacuation scheduled', instance=instance)
( c* r( o* _* K9 T5 K( p/ V- J# 原实例所在主机, y+ K, W2 X. z8 B+ D
inst_host = instance.host
& n" l* G' [, o! bservice = objects.Service.get_by_compute_host(context, inst_host)
9 F: I5 e( n0 A. f9 }# Z+ x4 n# 首先确保compute主机的状态为down( z6 n, t' Y4 v! S9 |
if self.servicegroup_api.service_is_up(service):
, T+ ]2 @8 f9 B; ]% q# M LOG.error(_LE('Instance compute service state on %s '' R4 O- r: z. p( B1 L9 F: U
'expected to be down, but it was up.'), inst_host)
3 O+ B* K T' Z/ B% t raise exception.ComputeServiceInUse(host=inst_host)
! f9 }$ U$ K E+ k6 d. L9 V Y: \; G; w) a' G2 w+ D9 ^, w6 m: Q
3. 记录action执行操作
1 O( `3 x2 P1 |% E6 F. u2 ]# 实例的任务状态设置为REBUILDING
5 J/ G3 z( r' U# P$ u* X' Z. E/ ]instance.task_state = task_states.REBUILDING
# h, ?( K& S: i! c; u. Q, K# Linstance.save(expected_task_state=[None])
: Z* v. d: l i) t- _! bself._record_action_start(context, instance, instance_actions.EVACUATE)
) s- F% Y$ Z/ O7 p) G4 p, L/ s# D3 e+ v0 }
4. 初始化迁移类0 ^1 n4 n9 Y# T N$ F3 t3 P
migration = objects.Migration(context,9 Y O# w% M& b5 n' y0 u& m
source_compute=instance.host,
5 E# A- g7 [& o! H2 W4 u source_node=instance.node,2 x- f. u' [& E x; t3 E
instance_uuid=instance_uuid,
7 \8 w$ H6 r6 t) t& p; S' R status='accepted', C* J/ k+ {8 U( d( U2 z
migration_type='evacuation')6 ?- o3 K$ ?2 i7 a
5. 创建迁移(这里为什么要创建migration,并没有执行迁移)
o/ k9 {1 F* W6 L! s! k( k: a# 如果提供了目的主机1 H7 O a) V& C* A+ Y/ ^. e1 f. |
if host:
* b3 v6 }9 N+ G8 B* K migration.dest_compute = host1 M# r' b0 z9 I0 v2 S4 h
migration.create()3 B- G: C' v7 B
4 Y7 T/ R" n- L; x6. 发送消息通知实例的使用配额
' _# D# ^- T" [compute_utils.notify_about_instance_usage(
0 b6 i5 c4 ?' f, G self.notifier, context, instance, "evacuate")
6 y2 w+ L9 y& ]! k6 v1 V! C9 [& f: J* n9 M) d4 ^$ F7 a' R( `
7. 最后执行task任务:rebuild_instance3 r2 F' R) f- \2 N
所以evacuate的本质是在新节点上执行rebuild操作
# j) L! W X/ B1 [ jreturn self.compute_task_api.rebuild_instance(context,, U% v: G) D% @1 b6 c
instance=instance,
' P# f% O& S. `! A* I9 ~ new_pass=admin_password,5 L0 e0 W; z8 W% d8 u# [7 u' Z
injected_files=None,3 I9 z/ C) L; ?! A) z, B# D; ~
image_ref=None,* z: L* S7 c8 h% E5 s
orig_image_ref=None,
6 ?8 M2 v1 b. q( i- E8 Z orig_sys_metadata=None,/ d; Z8 U& w; U# W( v: A) T
bdms=None,
# Y+ i3 W* \% o9 u recreate=True,+ O6 J3 N+ D6 \8 _! x# T
on_shared_storage=on_shared_storage,
# h5 {( p; ^) M* U( r1 Q4 Y6 l% d, } host=host)% m) T6 I) ?/ \" z
深入分析rebuild_instance方法,通过各种rpc调用,最终具体执行的是/nova/conductor/manager.py
3 t& J2 ~" d- i7 X+ v( V6 @def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
0 ]' G$ ]. ]; ]$ ^. I5 I! t injected_files, new_pass, orig_sys_metadata,* T9 {. z3 r: Y
bdms, recreate, on_shared_storage,
, i @& W. ?: k4 t+ I, g# R preserve_ephemeral=False, host=None):. }& A1 u4 d3 Y& s8 f) a
(1)在选择新目的主机时先排除instance所在主机
# B) @( B/ w' m# N& d2 T3 k& N这样能确保不会在原主机上执行rebuild操作6 c8 [4 {" k9 r G4 N3 k! Z3 O
# 排除原实例所在的主机,即不能在同一个主机里进行rebuild
! j* z$ S. u' ffilter_properties = {'ignore_hosts': [instance.host]} Y( [$ C0 k! m6 g5 t8 T. m
hosts = self.scheduler_client.select_destinations(context,
) t8 r J" R: b; H1 [$ o5 h request_spec,/ F9 F5 {7 w7 e
filter_properties)
6 u* o8 v2 e' s: t" f* b; W8 }0 G(2)接下来会通过scheduler模块筛选出合适的新主机
! l$ N- ^ q% t4 o9 x+ P( g(3)如果没有选出足够的合适新主机,则抛出异常 ]) J0 G: W. y( }) r3 }! ^/ J
except exception.NoValidHost as ex:, [0 z' d8 U, b9 f, \% g# H
with excutils.save_and_reraise_exception():9 c1 [; v2 F( T2 i) E
self._set_vm_state_and_notify(context, instance.uuid,
' X* ]& Q* `" |* o1 `3 c 'rebuild_server',6 }) l4 D; }- C8 h; v. S4 K" o* s
{'vm_state': instance.vm_state,
7 s5 S$ T4 a0 Q- y 'task_state': None}, ex, request_spec)1 z# i0 f: O C) n# w9 M4 h
LOG.warning(_LW("No valid host found for rebuild"),6 N7 A8 x4 ~& M) e7 o& f$ ~
instance=instance)
F$ T+ M( _3 q |2 w不能选出合适的新主机,有可能是除了原节点外,其他节点都不可用(computer service status:disabled)或网络不通(computer service state:down),导致没有合适的新主机。" S( [" v2 ]4 v) A9 Q
% Z R) ]/ L# s0 Q8 P
|
|