|
|
楼主 |
发表于 2021-8-30 17:35:43
|
显示全部楼层
一. 当实例所在的节点发生故障不可用时,可执行evacuate操作,在另外一个新节点rebuild该实例,实现高可用。4 ?% m3 |0 C- ]. X& Y3 k
这可以是OpenStack计算节点HA的一种实现方案。* i' W8 v( I2 q' B( r" ^
' I( `' ?: A3 a$ F% m二. API调用
- G" }0 F; Q! Vnova.servers.evacuate(server=fm['id']), on_shared_storage=True
g! a5 ~8 |/ K7 w/ U1. on_shared_storage参数在2.14版本后废除,自动检查是否为共享存储。
& b. X8 ]) [5 B 共享存储能够保证实例在另外新节点重建后数据不丢失# [- X, w# X+ H* ~
2. 可以设置目的主机host
4 c! \: \' V2 i; E$ L 如果不设置host,nova会通过scheduler选择一个新的主机(不会分到原主机,因为rebuild函数中过滤了原主机)
- ~' B- G+ e. y7 ?/ `3. 这个调用只是发送了evacuate操作命令,具体是否真正疏散成功,无法知道
: ?# Q( f& S% P. d" j) K9 f/ s% b: h6 B
三. 源码分析7 |/ B( k* [4 M- w% }& h) {
对应的是/nova/compute/api.py
1 S# ?+ w) E5 |% I# U% Q@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,) S# v7 M, g: t5 q# b* ~. g* {
vm_states.ERROR])
3 B" I" Z' w/ [def evacuate(self, context, instance, host, on_shared_storage,
/ i& n* h+ u* ~! ~7 j& H3 B admin_password=None)0 t+ ?& _& `5 z9 i1 I) q
1. 函数上方有装饰符 @check_instance_state
! k+ _# \' q: @# B! b表示在执行evacuate方法前先执行check_instance_state:检测传入的instance的vm_state是否为ACTIVE、STOPPED或ERROR。如果不是这三种状态,不能执行evacuate方法。5 `; {. E; F( y, T! _. Y
/ L Q8 g/ i0 m% R. x a2. 首先检测instance所在主机的状态是否为down,如果不是down(比如up),执行会出错。+ V# U& y P8 ~) i1 m, j; Z# ^) h/ Q
LOG.debug('vm evacuation scheduled', instance=instance)
/ F, Q# h7 \1 N9 Z7 y* _# 原实例所在主机
/ k- e9 S- V# J }! G, yinst_host = instance.host
" y( @* q8 K6 ~- kservice = objects.Service.get_by_compute_host(context, inst_host)1 o/ c9 ], W- T
# 首先确保compute主机的状态为down
0 s: J. L1 [. D* P4 vif self.servicegroup_api.service_is_up(service):
( i& t* ^5 e5 ? x# x' z% k LOG.error(_LE('Instance compute service state on %s '
4 Y' w6 K" f% \0 @- J. v- a/ s ^ 'expected to be down, but it was up.'), inst_host)9 |, A8 X4 L, ^/ @
raise exception.ComputeServiceInUse(host=inst_host). [' b# e$ p$ W; ~9 M# k
8 I! P2 p! G' D1 m( \$ T3. 记录action执行操作( k6 G0 Z" v% A/ a
# 实例的任务状态设置为REBUILDING7 A) P8 i# C( ~% }
instance.task_state = task_states.REBUILDING
0 e( d( ]0 i0 }5 G, finstance.save(expected_task_state=[None])
8 `) Y% ]0 W/ gself._record_action_start(context, instance, instance_actions.EVACUATE)4 }1 u0 K) N! k! g& j$ W
. M' I$ B! i- A4 ^
4. 初始化迁移类
6 b7 Y# S# \2 W3 R6 y& R; Imigration = objects.Migration(context,2 F7 t# C, U0 W+ d
source_compute=instance.host,6 r5 y* R# W1 [# w0 y3 T* ^
source_node=instance.node,
; A4 A4 S2 l6 Y* T7 W instance_uuid=instance_uuid,) L3 v" Y- c w- v0 j
status='accepted',( r& O& T E2 h) }
migration_type='evacuation')
: u4 _8 C1 z; `: O$ c+ k1 E& b5. 创建迁移(这里为什么要创建migration,并没有执行迁移)
% ` a" w2 W* }, d# 如果提供了目的主机/ P8 T+ Y. I5 o- i
if host:
; _6 }( A7 g& u- b- w N, O migration.dest_compute = host
" a" ?' m, P2 V) O! ^migration.create(), O1 L8 x5 L# J6 R9 J( n
) n( J; S c _1 G5 P h# R, t
6. 发送消息通知实例的使用配额6 F( w# w3 v( n4 F
compute_utils.notify_about_instance_usage(1 n2 v& X" @; E/ ~9 j
self.notifier, context, instance, "evacuate")* p2 F2 R. n1 A6 O& [' P6 {
2 n2 S# P4 w2 D% L' E7. 最后执行task任务:rebuild_instance* j: n$ R8 i7 X- Y, e" T
所以evacuate的本质是在新节点上执行rebuild操作
; q& H- D1 [# W5 {. e5 e6 G% n$ Q2 lreturn self.compute_task_api.rebuild_instance(context,' v- G( d# l( Y6 C8 p! @9 D9 V
instance=instance,) s- p" R2 p8 u; m- m: `, d" v
new_pass=admin_password,
2 {6 s& z4 Y7 m6 i7 m injected_files=None,& A% r. H6 R. ^9 M3 w1 C
image_ref=None,$ a9 _3 T. E' i* S* X& H
orig_image_ref=None,
- f) c: s0 _& g( u8 x: b0 R orig_sys_metadata=None,
$ A9 \" q M5 t8 z) V* H: k) B. V bdms=None,: I4 R( P" u8 P
recreate=True,) B2 H: s8 r3 j& l! y) @
on_shared_storage=on_shared_storage," ~. b, _' L. q% M2 o5 Y
host=host)( ~8 e, B* e7 [& @+ k9 Q8 t: e
深入分析rebuild_instance方法,通过各种rpc调用,最终具体执行的是/nova/conductor/manager.py$ E7 D, J1 T3 }& w9 ]5 p9 ]" _
def rebuild_instance(self, context, instance, orig_image_ref, image_ref, A& E6 k/ W7 e: Q3 C
injected_files, new_pass, orig_sys_metadata,$ S; l% G) Q- n+ r. V) S
bdms, recreate, on_shared_storage,3 x/ R8 N+ p3 g- G
preserve_ephemeral=False, host=None):
3 h. S: h6 Q( H5 D6 y) U(1)在选择新目的主机时先排除instance所在主机
% b! E! e) l9 N) p& \; g这样能确保不会在原主机上执行rebuild操作
' M; g7 W6 J7 X7 u q0 @, }6 ?# 排除原实例所在的主机,即不能在同一个主机里进行rebuild
, y* O. f9 ~, M. u; D* @; Nfilter_properties = {'ignore_hosts': [instance.host]} n& v' s J/ }" x; b
hosts = self.scheduler_client.select_destinations(context,
, v/ Y, m! u+ W4 d) K) u- X7 D request_spec,
2 t$ q" p9 J) U# q" |5 [) B filter_properties)7 g$ l: l/ }' n. z
(2)接下来会通过scheduler模块筛选出合适的新主机
2 O5 s6 I( L. w8 `. k! x; A& ?(3)如果没有选出足够的合适新主机,则抛出异常! ]0 ~/ ?% G! h, @
except exception.NoValidHost as ex:
+ r6 V {& _8 Y% P) t with excutils.save_and_reraise_exception():
; C* a* q! R8 \# m1 b2 K6 k self._set_vm_state_and_notify(context, instance.uuid,% l6 a/ [( T# M$ ~1 N
'rebuild_server',6 @. \ |8 P2 P
{'vm_state': instance.vm_state,
* }! F- M6 z: L" L0 l9 Z% `* K/ x 'task_state': None}, ex, request_spec), K1 Z$ X7 D& f5 T
LOG.warning(_LW("No valid host found for rebuild"),3 c Z: o2 r! ~* O# e5 A3 q& w# M
instance=instance)
) @" [" @; b0 I9 B/ N2 ~不能选出合适的新主机,有可能是除了原节点外,其他节点都不可用(computer service status:disabled)或网络不通(computer service state:down),导致没有合适的新主机。( ~6 q' J* j, V
0 S, j. {7 I1 C& q |
|