|
|
当实例所在的节点发生故障不可用时,可执行evacuate操作,在另外一个新节点rebuild该实例,实现高可用。2 N: A0 g7 E' q
这可以是OpenStack计算节点HA的一种实现方案。
/ A8 b% l5 Q5 q+ D
% b( \( ~: D J! `: a0 ^: ]API调用& K* q; o( c) M0 H
nova.servers.evacuate(server=fm['id']), on_shared_storage=True
$ P* E8 s1 `( T* h2 }; L" B3 |1. on_shared_storage参数在2.14版本后废除,自动检查是否为共享存储。1 G1 Z6 z1 h* i; T- z" y" p" a
共享存储能够保证实例在另外新节点重建后数据不丢失! t2 }$ g4 g- a8 @, C, Y; ?
2. 可以设置目的主机host% Q/ \6 d: Y/ l# X3 K7 _; ^
如果不设置host,nova会通过scheduler选择一个新的主机(不会分到原主机,因为rebuild函数中过滤了原主机)
6 E# w1 A- Z0 U% z% L' ]5 j( w& ?& C3. 这个调用只是发送了evacuate操作命令,具体是否真正疏散成功,无法知道5 x$ G7 \2 O' m9 P, t
* |7 R3 d8 u% [( Q) L! l( c7 m
源码分析/ l5 C9 o) g$ B/ t( Q: a; M
对应的是/nova/compute/api.py
`+ @" s* {+ l: x2 k@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,! C8 C! }5 K+ @7 ?; P/ L, j
vm_states.ERROR])
/ \/ P, U/ f. m6 C& v4 mdef evacuate(self, context, instance, host, on_shared_storage,
, G# C5 C' c+ Q' u admin_password=None)0 j# H O% C: p) D4 L5 f* `" `
1. 函数上方有装饰符 @check_instance_state
* U/ \; f3 `: B3 o; U表示在执行evacuate方法前先执行check_instance_state:检测传入的instance的vm_state是否为ACTIVE、STOPPED或ERROR。如果不是这三种状态,不能执行evacuate方法。
) d7 v2 a( v/ i, }6 h0 _. r$ |9 r. W; i3 d8 w# {/ k( [ I7 l
2. 首先检测instance所在主机的状态是否为down,如果不是down(比如up),执行会出错。0 u3 }7 p3 e. i2 V0 I
LOG.debug('vm evacuation scheduled', instance=instance)3 L1 M# D. b5 o# y' T, h3 F
# 原实例所在主机
3 y" [5 V. o. U6 d/ C( K/ r2 kinst_host = instance.host; D$ l# y) e$ e
service = objects.Service.get_by_compute_host(context, inst_host)5 q3 w: d/ M" j
# 首先确保compute主机的状态为down
) P5 O0 @/ f4 a, P: G4 q: X' w' Bif self.servicegroup_api.service_is_up(service):
2 b# G6 `' x" d/ ^ LOG.error(_LE('Instance compute service state on %s '
; y! F* y! s. |* }- f, X 'expected to be down, but it was up.'), inst_host)# n$ Y/ o% D% y& B/ f/ b
raise exception.ComputeServiceInUse(host=inst_host)
& {1 S D; }, N; I' A [4 Q+ I5 s$ {. m. D
3. 记录action执行操作: D3 c2 B- Y3 m7 e
# 实例的任务状态设置为REBUILDING( M4 g) ~/ k' M+ U
instance.task_state = task_states.REBUILDING
- y( f5 c0 ^4 X/ p. P: F6 j5 uinstance.save(expected_task_state=[None])
q: \) u4 e) `# t7 ?# iself._record_action_start(context, instance, instance_actions.EVACUATE)
) l, O2 v4 I2 a
# E$ [8 V3 e5 V5 f8 `4. 初始化迁移类
3 l, ^. u; L2 }. k; v' z1 N5 kmigration = objects.Migration(context,
& w2 I4 \1 a N( A% A, M5 m5 P! \& h source_compute=instance.host,
* Q! D/ N1 F4 b7 U, k- U( C source_node=instance.node,& ^$ t) ~" O3 c$ {" ~
instance_uuid=instance_uuid,8 l+ u2 F) ^; u. m
status='accepted',# V/ b) H7 {7 Y# q7 ]
migration_type='evacuation')4 d. L6 w# m, L& ?9 \ R7 i
5. 创建迁移(这里为什么要创建migration,并没有执行迁移)
- e* E% E, @; J+ W- N* y/ t# 如果提供了目的主机
& J1 s+ p: L0 S$ Q3 Aif host:9 h' |1 E F% |% a9 d' d6 O
migration.dest_compute = host5 C ?* [* Q1 I* j
migration.create()
) u5 a) z* P+ ?( B0 J F4 c& d0 X, q- {4 C( R. b! }/ U9 I7 k
6. 发送消息通知实例的使用配额
D+ A1 n* \1 d Z$ fcompute_utils.notify_about_instance_usage(
. e# k5 T" C) Q7 g; | self.notifier, context, instance, "evacuate")) o/ Y$ M) S2 h8 m
3 b- b, i" \2 f7. 最后执行task任务:rebuild_instance( \+ p$ _3 K b; U) c& @" J$ d
所以evacuate的本质是在新节点上执行rebuild操作
5 c" S& w4 u7 c% f9 Y4 oreturn self.compute_task_api.rebuild_instance(context,1 [" E' Z* S7 a, v! I: d) e
instance=instance,; x# T% v0 Y& w+ e) p8 r
new_pass=admin_password,+ i9 Q" D# S: [8 `3 x' g% z+ A$ h
injected_files=None,
1 r! G: Q2 }2 k, V- s' h1 } image_ref=None,
" u& L5 l2 Z# g7 e( S orig_image_ref=None,, }6 e2 r) |2 [1 s, C" L
orig_sys_metadata=None,
. T% Z2 |, O( Z7 |3 y bdms=None,* b) R. f: Z; N, N0 G' `0 ~" d
recreate=True,
5 ?0 V, j. |- S* S& K on_shared_storage=on_shared_storage,. n9 B2 s0 Q, v3 N1 r- Z
host=host)+ p1 u& G" g: C0 |. W1 G3 o
深入分析rebuild_instance方法,通过各种rpc调用,最终具体执行的是/nova/conductor/manager.py
: T) K9 H6 s5 A+ q4 i* idef rebuild_instance(self, context, instance, orig_image_ref, image_ref,
/ d- F. O. s/ J$ m) L injected_files, new_pass, orig_sys_metadata,
# ~* U; G0 \! j bdms, recreate, on_shared_storage,
/ K2 u" M4 j5 d/ W) o0 X8 A2 N preserve_ephemeral=False, host=None):. {4 F& S8 f0 p+ J1 N
(1)在选择新目的主机时先排除instance所在主机9 P7 S' ]7 Q. A4 g" H9 [
这样能确保不会在原主机上执行rebuild操作
0 }0 @; ?* n5 P. ?5 c5 R' h- o6 t# 排除原实例所在的主机,即不能在同一个主机里进行rebuild
6 y; Q w! v/ @, v ]8 ]+ x% Q2 wfilter_properties = {'ignore_hosts': [instance.host]}2 G4 `9 v5 K1 I
hosts = self.scheduler_client.select_destinations(context,
0 T( f+ o6 r$ Z7 \ request_spec,
+ g7 I; t# n/ B9 H g7 W9 x filter_properties). ~# P7 B) j# h' g6 Y
(2)接下来会通过scheduler模块筛选出合适的新主机
3 X# \/ ]( J; G; E5 Z(3)如果没有选出足够的合适新主机,则抛出异常
) {7 V# x- @! f5 `' x F( Fexcept exception.NoValidHost as ex:5 f" s h- n, x, Y" W, P0 [% C
with excutils.save_and_reraise_exception():
* |8 }0 j9 t! ^ self._set_vm_state_and_notify(context, instance.uuid,
% |6 m* H7 `( e7 a8 \( R 'rebuild_server',' O' {$ K- B6 i% s. Z
{'vm_state': instance.vm_state,' @% W4 \ f% j) m
'task_state': None}, ex, request_spec)+ d7 E- w' ]8 a
LOG.warning(_LW("No valid host found for rebuild"),
. }) y* k3 t; f3 a6 f instance=instance)
( X/ e# L. I C不能选出合适的新主机,有可能是除了原节点外,其他节点都不可用(computer service status:disabled)或网络不通(computer service state:down),导致没有合适的新主机。2 B' J% Y7 f/ N8 n, Q
% m0 @* T9 q/ r; v4 Q0 p |
|