|
|
楼主 |
发表于 2021-8-30 17:35:43
|
显示全部楼层
一. 当实例所在的节点发生故障不可用时,可执行evacuate操作,在另外一个新节点rebuild该实例,实现高可用。
$ K7 M4 X. D2 {5 k/ h, a这可以是OpenStack计算节点HA的一种实现方案。
+ A& V L: C! D- V) g1 |2 v
2 i, J" {, @$ G, K6 j2 ?+ Y二. API调用3 N3 {) P; p* Q8 ]3 C j
nova.servers.evacuate(server=fm['id']), on_shared_storage=True: A1 _5 a# q3 h# R O
1. on_shared_storage参数在2.14版本后废除,自动检查是否为共享存储。
9 c5 {" B6 Q( T% R; n1 Z 共享存储能够保证实例在另外新节点重建后数据不丢失3 O% p% K {/ Y( {: @ ?" L/ Q
2. 可以设置目的主机host
. j C! R1 A+ X2 S( B6 Q 如果不设置host,nova会通过scheduler选择一个新的主机(不会分到原主机,因为rebuild函数中过滤了原主机)5 u T7 C' g$ D; |' d
3. 这个调用只是发送了evacuate操作命令,具体是否真正疏散成功,无法知道
% C$ m# g9 K. h/ T5 q- m) f
" B- F2 c7 J, i三. 源码分析
! ?! d+ Z" v6 r7 C5 f1 N& ~对应的是/nova/compute/api.py% v) O" r% s/ E8 E
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,. x6 ]* R( K9 R, u# u8 q* v, w' x! k
vm_states.ERROR])
; a. W$ g+ a' K/ ~0 M) z& U. f* B- rdef evacuate(self, context, instance, host, on_shared_storage,; L) x& ~. J0 d+ o3 W8 H9 j
admin_password=None)
8 J. y& W; M) a% W }0 B+ ^1. 函数上方有装饰符 @check_instance_state
3 l+ I" H; s: G! Q: v+ n表示在执行evacuate方法前先执行check_instance_state:检测传入的instance的vm_state是否为ACTIVE、STOPPED或ERROR。如果不是这三种状态,不能执行evacuate方法。
" q! C, S/ K5 Y6 v0 K& x! C' Q( w* Q2 n% l4 X* J" H5 d/ u
2. 首先检测instance所在主机的状态是否为down,如果不是down(比如up),执行会出错。: L1 n# R( @0 T- L
LOG.debug('vm evacuation scheduled', instance=instance)
- u# ~5 m+ Z- n# 原实例所在主机, V( c0 x2 X8 ~/ s) Y, T
inst_host = instance.host, J9 j' M+ S( }4 b; _2 @' _! { J4 N: Y4 k
service = objects.Service.get_by_compute_host(context, inst_host)+ v2 c! ~2 C: z2 N! ]5 W: |+ L
# 首先确保compute主机的状态为down
) b0 v% p4 P9 nif self.servicegroup_api.service_is_up(service):2 W' p8 K) w i5 z) H
LOG.error(_LE('Instance compute service state on %s ' ^# S% n, H& S" `1 p) u3 p1 f& h
'expected to be down, but it was up.'), inst_host)
0 J% q# g- H& }5 E. l( w) G$ j raise exception.ComputeServiceInUse(host=inst_host), F& f0 d6 j, r- n9 W
; ?6 R$ o }; _; |' q
3. 记录action执行操作3 C4 l( t. }( s" h# V7 J$ M1 q
# 实例的任务状态设置为REBUILDING7 M1 j+ m! S6 ?
instance.task_state = task_states.REBUILDING
( n+ |" P: U9 F/ ^2 jinstance.save(expected_task_state=[None])& x& @: ~2 q, S- u9 @
self._record_action_start(context, instance, instance_actions.EVACUATE)* l( F5 s4 I" |8 h
; _1 X6 L! o+ b/ O) S3 w+ U0 e4. 初始化迁移类
% F. c( L' Z3 O+ Fmigration = objects.Migration(context,) c: j* _( G7 m9 {/ ^! ~5 r. d8 j
source_compute=instance.host,& @% X' H( b3 D' X4 w, E
source_node=instance.node,' o+ y$ `3 i( z i3 b" N
instance_uuid=instance_uuid,
- ?5 g1 H# ?7 w3 k2 H status='accepted',
0 V- X1 u U& k6 C9 G" x migration_type='evacuation')% h R6 I- m, l% o
5. 创建迁移(这里为什么要创建migration,并没有执行迁移)
0 i6 |% `0 u5 U# 如果提供了目的主机
. q- T; V( `1 T. V" {2 eif host:
9 j, a/ s) k/ J* x' i* \. H migration.dest_compute = host* C- B3 d6 B, [; F% T
migration.create()
% F7 b" x1 ~. ^7 J' f8 V% m( a; C; V5 W- E9 \! }8 N
6. 发送消息通知实例的使用配额
2 |; G7 {$ e/ j; ^* L. dcompute_utils.notify_about_instance_usage(
; h; y. u# i) s& s self.notifier, context, instance, "evacuate")
: j- z5 k- {' j8 m- g+ P
" d$ M; P R# h. ]' E7. 最后执行task任务:rebuild_instance
( J$ J/ K, t" f* [. s所以evacuate的本质是在新节点上执行rebuild操作
/ B, m( g) M( Zreturn self.compute_task_api.rebuild_instance(context,
0 i3 @/ g/ S3 Z/ k5 m instance=instance,
* U* B8 @+ U/ e" Q3 a& m, n new_pass=admin_password,
& Y' g* I$ H. k8 Q: m injected_files=None,
5 ~3 e: p+ D V# u image_ref=None,( [' J4 k3 z0 H0 v4 x1 D- `
orig_image_ref=None,
- J8 Y/ z1 q. x. c' e; J orig_sys_metadata=None,
& Q# F- X$ Q( E4 q B% G bdms=None,- T$ `3 N3 p* E. s$ R' l1 S$ X- M
recreate=True,' b* \$ d. v$ q4 d4 A$ ~
on_shared_storage=on_shared_storage,
, R8 `5 N. A! I! _0 W P' T5 P/ k: P/ o! c host=host) H5 o% `( l$ w$ U/ H
深入分析rebuild_instance方法,通过各种rpc调用,最终具体执行的是/nova/conductor/manager.py
! m; ]" r; `8 O z( J6 Zdef rebuild_instance(self, context, instance, orig_image_ref, image_ref,8 l0 A T( e! @
injected_files, new_pass, orig_sys_metadata,$ _% W; o* m9 O
bdms, recreate, on_shared_storage,
9 K5 _8 f/ n% c+ V& } preserve_ephemeral=False, host=None):$ Y5 T( [( a* s% A( q1 F! j
(1)在选择新目的主机时先排除instance所在主机 j6 f3 I }. x; F9 U
这样能确保不会在原主机上执行rebuild操作
6 ]/ D8 J7 i6 U; v# 排除原实例所在的主机,即不能在同一个主机里进行rebuild
, {& ~5 N' ]! y, M/ R, ^filter_properties = {'ignore_hosts': [instance.host]}4 j# D7 a2 i8 c) @# b: R& Q
hosts = self.scheduler_client.select_destinations(context,
. E: p* W4 _0 R3 _ request_spec,
& c% i0 y( M, L& s filter_properties)
2 [/ h! i) a3 r1 M) A8 [- J! `(2)接下来会通过scheduler模块筛选出合适的新主机" s. ~9 ?7 F1 B3 D- m2 n$ I
(3)如果没有选出足够的合适新主机,则抛出异常& N6 S, _, _2 i5 z
except exception.NoValidHost as ex:2 B. t, \9 k) h: c2 ^; {; Z- Q
with excutils.save_and_reraise_exception():
' u7 ^0 t' Z) N$ G' ~' j# o, f self._set_vm_state_and_notify(context, instance.uuid,
, _. b/ B( E6 A/ B" Q 'rebuild_server',
) R' d; B! o. u {'vm_state': instance.vm_state,3 Y& _! K9 b! o9 M+ P4 U+ |
'task_state': None}, ex, request_spec)
/ ~& p$ h) j5 e* o7 @1 p2 W6 B, v/ Q LOG.warning(_LW("No valid host found for rebuild"),
) I* D( n! ~0 m instance=instance)
6 O4 q# F& _6 k. u: U8 P s) l不能选出合适的新主机,有可能是除了原节点外,其他节点都不可用(computer service status:disabled)或网络不通(computer service state:down),导致没有合适的新主机。
y6 L/ q- A, t1 p1 Y4 R, A' i7 k5 a& N& i; O
|
|