|
|
楼主 |
发表于 2021-8-30 17:35:43
|
显示全部楼层
一. 当实例所在的节点发生故障不可用时,可执行evacuate操作,在另外一个新节点rebuild该实例,实现高可用。
" Y8 O- v+ ?% `; Y. _这可以是OpenStack计算节点HA的一种实现方案。& [: z# `, I! ?' l) \
4 c. E" A( F9 {# l
二. API调用
$ e7 U1 j+ {" W* [7 y6 l( s7 wnova.servers.evacuate(server=fm['id']), on_shared_storage=True, F' z# ~0 x. H
1. on_shared_storage参数在2.14版本后废除,自动检查是否为共享存储。
r# y* ~: N9 N5 `; V& o- X 共享存储能够保证实例在另外新节点重建后数据不丢失
9 _( n/ ?3 u- k, e; |9 {; d2. 可以设置目的主机host. a2 }9 V' i/ P& I' B
如果不设置host,nova会通过scheduler选择一个新的主机(不会分到原主机,因为rebuild函数中过滤了原主机)8 D& O3 }7 G F/ Q. Q# b
3. 这个调用只是发送了evacuate操作命令,具体是否真正疏散成功,无法知道
+ T, q, E/ q' q# p% [9 C9 C3 s$ |3 I! i4 z: E* C
三. 源码分析
% o; t" W# k+ i4 |& F/ o对应的是/nova/compute/api.py
. D. ~' P4 _! ~$ y@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,
3 ]0 F. d6 z; `4 S/ M; w vm_states.ERROR])( F; ?7 F s% m; x: d$ a
def evacuate(self, context, instance, host, on_shared_storage,; m5 m4 p# O$ S1 \/ g5 D7 Y
admin_password=None)
6 c" E) h- W2 J1 a+ f: [1. 函数上方有装饰符 @check_instance_state: w, s3 u3 t: E$ q9 t( `
表示在执行evacuate方法前先执行check_instance_state:检测传入的instance的vm_state是否为ACTIVE、STOPPED或ERROR。如果不是这三种状态,不能执行evacuate方法。
. ^0 B# K1 E$ ~( G q2 j+ x
8 ^. {' S7 F1 }: } ?- ]2. 首先检测instance所在主机的状态是否为down,如果不是down(比如up),执行会出错。: y' a5 Q& ~2 s
LOG.debug('vm evacuation scheduled', instance=instance)
. X, }8 B8 S% s# w3 U6 G0 Y# 原实例所在主机8 A( `) J# i* ]( }, b
inst_host = instance.host
6 b, x% A" j; J+ I/ l1 b# ?2 x( lservice = objects.Service.get_by_compute_host(context, inst_host)
: C- e8 t7 A- c# 首先确保compute主机的状态为down2 e: ]: v$ \6 x) i: g" S
if self.servicegroup_api.service_is_up(service):
M, f! T: e. R2 r# { LOG.error(_LE('Instance compute service state on %s '2 f" G- ~2 u# {6 h! s. e
'expected to be down, but it was up.'), inst_host)
2 @: C+ P( m2 q4 O6 d# w raise exception.ComputeServiceInUse(host=inst_host)9 Q: |) X& e8 ~1 Y) V% S3 s
& e6 b9 }4 d1 f( L' I2 P5 X3. 记录action执行操作4 G. U) i% {) d4 Y
# 实例的任务状态设置为REBUILDING
1 |. c! \! G8 t% u$ |instance.task_state = task_states.REBUILDING* Z0 m6 R5 v9 b) A) ], v" a
instance.save(expected_task_state=[None])
* J/ \' P+ d8 h, R l. ?$ v Q- t* Hself._record_action_start(context, instance, instance_actions.EVACUATE)- T- W6 B7 e2 I/ U% _3 p
' @% ?$ K/ `! \% F4 @1 Y
4. 初始化迁移类0 ~2 t k V- }. y: p" T. e
migration = objects.Migration(context,4 A" M, N3 n+ f
source_compute=instance.host,4 t% o& g6 L% ]4 z9 F# J
source_node=instance.node,
4 U) Y3 m1 x. u instance_uuid=instance_uuid,( ~5 g/ x+ d* t. o
status='accepted',
- v D7 N1 Q0 `2 A3 O! t9 ? migration_type='evacuation')
6 r" U6 Q3 U$ R. m# J& z5. 创建迁移(这里为什么要创建migration,并没有执行迁移)
7 E6 o/ \' h4 ^/ Q& Y3 R6 B# 如果提供了目的主机
7 o; K4 p, C' X; w( a* nif host:
; x5 s, s$ ~" X3 Z' J migration.dest_compute = host
. T/ X& ]" |( i7 H+ }+ ], N) ~migration.create()
6 Q% b0 S6 f5 e1 C
" B1 [2 t# @9 d* q; t$ R6. 发送消息通知实例的使用配额/ D# h) K0 a. L2 I
compute_utils.notify_about_instance_usage(
$ A& R; n5 T6 F, v( a1 s$ ?; j self.notifier, context, instance, "evacuate")# x$ o* p0 V2 h
2 B* A8 c+ h) f( p% D. ~& D7. 最后执行task任务:rebuild_instance
, {4 z! ~; a2 c所以evacuate的本质是在新节点上执行rebuild操作" F: F* c4 i0 ?; G" L
return self.compute_task_api.rebuild_instance(context,! j. c) X1 \' e& a9 ]
instance=instance,8 v8 Q6 }% T6 F7 W' x
new_pass=admin_password,
: f/ n8 b' F6 k injected_files=None,/ S1 Y1 `6 s0 @" g, j. c+ `
image_ref=None,
5 b- _4 n! \1 e: F) [ orig_image_ref=None,2 h# F0 q' J1 A' w; |- J6 [- K
orig_sys_metadata=None,, R, u( d; {) `8 J
bdms=None,2 s6 d; n1 Y% @( G2 K
recreate=True,
6 \& ^0 j( u* Y- ` on_shared_storage=on_shared_storage,
" g) C7 U8 @ W- ? host=host)6 J: G5 S& [8 ]7 O# l% O' o! \
深入分析rebuild_instance方法,通过各种rpc调用,最终具体执行的是/nova/conductor/manager.py$ s9 t' y- H. \+ m
def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
) `6 D) T/ z8 K ? injected_files, new_pass, orig_sys_metadata,
0 `! v6 a. n: R bdms, recreate, on_shared_storage,
. n7 o. _1 }( U' ]8 l7 l$ N preserve_ephemeral=False, host=None):" Y2 V3 ^( P" l7 ?
(1)在选择新目的主机时先排除instance所在主机
# m# i: R( ]3 h- O$ ?4 J; v这样能确保不会在原主机上执行rebuild操作6 N7 o% m. T e2 B5 q
# 排除原实例所在的主机,即不能在同一个主机里进行rebuild3 @. z5 I- s: p0 [% j: g
filter_properties = {'ignore_hosts': [instance.host]}# d! L* s: e2 V* k
hosts = self.scheduler_client.select_destinations(context,
& _. s) @. Z4 h! e) [6 Y5 } request_spec,' w% e+ N1 d. v( N2 @7 X/ _& J( E
filter_properties)
4 l2 {4 ^6 k# J" {! |; [4 E0 u(2)接下来会通过scheduler模块筛选出合适的新主机
R0 T, A- d \" {2 E" ~+ ?(3)如果没有选出足够的合适新主机,则抛出异常7 q- S) V" }' T9 @
except exception.NoValidHost as ex:: Z6 A) q) b/ Y I
with excutils.save_and_reraise_exception(): ]+ T1 z6 _( N& W: y/ m
self._set_vm_state_and_notify(context, instance.uuid,/ \9 `$ ~) P5 }: w- W0 \
'rebuild_server',
! n6 }) h& x' F" K5 k8 W {'vm_state': instance.vm_state,
2 d& y3 L" }0 h! ?: d, A0 P8 I 'task_state': None}, ex, request_spec) `5 t# p: ]6 W+ @( W, u
LOG.warning(_LW("No valid host found for rebuild"),. w" [( n: }7 D/ u2 y( L
instance=instance)- k$ R- M3 P9 j6 M$ s
不能选出合适的新主机,有可能是除了原节点外,其他节点都不可用(computer service status:disabled)或网络不通(computer service state:down),导致没有合适的新主机。0 x4 p( Q V; c# `2 N, E' H7 Y
9 i) o' b2 i; q k" E
|
|