|
|
当实例所在的节点发生故障不可用时,可执行evacuate操作,在另外一个新节点rebuild该实例,实现高可用。
% a3 m6 w0 ?% O! I- i这可以是OpenStack计算节点HA的一种实现方案。
" K9 d7 I+ S! b+ P% f% i1 C; i$ x0 }# U* |9 G, ~
API调用
/ S6 p& R- B3 ~3 S$ d( d; Lnova.servers.evacuate(server=fm['id']), on_shared_storage=True
1 C4 @7 Z( ~8 V! a8 B4 E* M! ]1. on_shared_storage参数在2.14版本后废除,自动检查是否为共享存储。% C' G: a) a3 N. O0 \
共享存储能够保证实例在另外新节点重建后数据不丢失, A2 y5 }% {3 }$ C+ f( n* t+ O
2. 可以设置目的主机host/ P! T/ P7 f6 N& g
如果不设置host,nova会通过scheduler选择一个新的主机(不会分到原主机,因为rebuild函数中过滤了原主机)7 k; P) q& y+ p g& v! |
3. 这个调用只是发送了evacuate操作命令,具体是否真正疏散成功,无法知道% V" d# f9 E! m9 m5 ^. x$ F
% E$ n- N, t0 D9 b/ C 源码分析5 e: l; }0 N: g$ [* {) b D
对应的是/nova/compute/api.py# o2 [2 V8 I1 v j( z% V" Y
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,: ]' _1 Z# [6 m' d- N
vm_states.ERROR])
/ b5 ^- F; V0 C- ^, w, _# ldef evacuate(self, context, instance, host, on_shared_storage,! {( P) [+ u5 S t; ?
admin_password=None)
, F5 R8 [: w* {7 i. p6 q1. 函数上方有装饰符 @check_instance_state
s/ B- b6 b0 Y- k- b4 k表示在执行evacuate方法前先执行check_instance_state:检测传入的instance的vm_state是否为ACTIVE、STOPPED或ERROR。如果不是这三种状态,不能执行evacuate方法。
' k) i$ o- S7 S
# C+ o+ g, ^. y Q B2. 首先检测instance所在主机的状态是否为down,如果不是down(比如up),执行会出错。
; e* E: o7 h) o* qLOG.debug('vm evacuation scheduled', instance=instance)
5 O/ I3 _' f+ m4 W# 原实例所在主机4 o9 D$ M) ?- {' Q" j1 J
inst_host = instance.host
( r& g3 d4 Z, g- Y1 u8 l/ Yservice = objects.Service.get_by_compute_host(context, inst_host)/ [8 H* g$ G2 F% H# H/ n
# 首先确保compute主机的状态为down' }$ d% @ s5 n2 `4 E0 r
if self.servicegroup_api.service_is_up(service):
$ L0 O' @6 x4 T" p LOG.error(_LE('Instance compute service state on %s '
2 ~" e8 Q- X: W. P( N) b 'expected to be down, but it was up.'), inst_host)
, n5 ~5 V9 p0 {; w+ O# Y. w raise exception.ComputeServiceInUse(host=inst_host)
) U+ X6 K1 e$ ~- R h( @
% H: H: M; d+ L9 b/ ^3. 记录action执行操作1 ~7 I, i+ @, O$ D6 k1 Y& o
# 实例的任务状态设置为REBUILDING- l4 |1 K6 x3 Y0 I' b6 @
instance.task_state = task_states.REBUILDING
* F# y/ A \- a3 Yinstance.save(expected_task_state=[None])# h3 R7 Y+ y% j+ V" b
self._record_action_start(context, instance, instance_actions.EVACUATE)
* J: ^1 `+ P' |! i- o5 i- l. v7 [$ N# \. j: Z7 w
4. 初始化迁移类& B* t- C+ j9 H# ~1 I8 R
migration = objects.Migration(context,$ ?# u$ u, `7 l B( J5 D- u7 l6 e
source_compute=instance.host,
4 A M. K+ l7 \8 f source_node=instance.node,7 ~3 W+ z# y# g
instance_uuid=instance_uuid,
+ Z9 Z. r' b' G8 V& N" x status='accepted',7 T3 g+ t! w- J
migration_type='evacuation')" [$ y* J7 g- _. L! j) L8 M
5. 创建迁移(这里为什么要创建migration,并没有执行迁移)
: p& G4 b h2 P8 G# m# 如果提供了目的主机
; L/ h* z3 d6 f, p8 I. ~" M: J; ~if host:3 W7 g- U7 E" [9 R* t
migration.dest_compute = host
& z% N: t# W k- r# x9 fmigration.create()( j( u% `: |$ m/ T
2 ?$ j" A3 [* B6. 发送消息通知实例的使用配额) g2 u1 @* v9 A c& Q) d
compute_utils.notify_about_instance_usage(: W. l- A0 y2 }0 i
self.notifier, context, instance, "evacuate")
/ j% d! \8 e$ G W: q! \' p6 o: }& m9 N& ^5 q9 b
7. 最后执行task任务:rebuild_instance0 e3 Q! M! ^1 J! }. X$ g
所以evacuate的本质是在新节点上执行rebuild操作( P* H: `. W+ P1 K" d# M6 z
return self.compute_task_api.rebuild_instance(context,
& B! T1 v1 s7 f2 J instance=instance,& l# o, N+ d$ d8 j' b- d& s# t0 E
new_pass=admin_password,+ S2 F. k' z6 Z j" S% u
injected_files=None,
# T+ e* h# O' e0 [3 X. ^! _1 J# t6 o" r image_ref=None,5 f D& j/ W) y* A4 }7 {# c1 `/ d0 x
orig_image_ref=None,
% i% L, H \1 b5 Q orig_sys_metadata=None,# l r; v$ k2 b
bdms=None,7 | `& o* B4 k) k, s3 z
recreate=True,' f/ ]4 y6 a/ A8 G
on_shared_storage=on_shared_storage,
- d% B. k+ s: ?2 l- m: Q host=host)
' G# t( l) S" h深入分析rebuild_instance方法,通过各种rpc调用,最终具体执行的是/nova/conductor/manager.py6 d/ v: ]3 N: s% n9 z
def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
4 I7 k6 B$ O. o9 s7 z( z3 m# L" G injected_files, new_pass, orig_sys_metadata,& \& t& \# C0 }: @, c% h% b
bdms, recreate, on_shared_storage,
3 @$ L) q5 ]1 a8 p8 v2 j9 V preserve_ephemeral=False, host=None):
! M/ {" }/ T+ {& F. t! J(1)在选择新目的主机时先排除instance所在主机6 A" U4 P- k0 I: I7 @" v
这样能确保不会在原主机上执行rebuild操作2 Q) m/ j( f8 u( w* e
# 排除原实例所在的主机,即不能在同一个主机里进行rebuild; e9 f" r' |9 I- q( |. f+ z3 @
filter_properties = {'ignore_hosts': [instance.host]}/ s" I2 r" x* w! n' A
hosts = self.scheduler_client.select_destinations(context,
- p/ I, k- j! T6 g3 g request_spec,$ |. b& U; y+ ]- C" w6 K9 \6 q
filter_properties)4 M9 r; R* G5 ]* B1 B# o4 z+ S
(2)接下来会通过scheduler模块筛选出合适的新主机
2 R/ b9 l) ^2 I(3)如果没有选出足够的合适新主机,则抛出异常7 i1 g/ N. I2 l: u
except exception.NoValidHost as ex:' o8 P3 ~& Y9 b
with excutils.save_and_reraise_exception():
' g: r+ p4 N0 k8 g self._set_vm_state_and_notify(context, instance.uuid,9 A/ C! K" E7 \4 s9 J
'rebuild_server',. M N h" _, h. i1 z! i7 d p
{'vm_state': instance.vm_state,
# w. l& y l7 ?; R7 S ?' Q; } 'task_state': None}, ex, request_spec)2 u7 m0 }) @+ C( L! ?; F9 q
LOG.warning(_LW("No valid host found for rebuild"),: D5 [9 v- Z/ I/ S# O
instance=instance)7 P0 m9 b, l! q/ e0 d: K
不能选出合适的新主机,有可能是除了原节点外,其他节点都不可用(computer service status:disabled)或网络不通(computer service state:down),导致没有合适的新主机。
( H+ O! @) n9 @+ I; ^
$ z" a+ I% z" R% W9 V4 ^6 O |
|