- 积分
- 16843
在线时间 小时
最后登录1970-1-1
|

楼主 |
发表于 2021-8-30 17:35:43
|
显示全部楼层
一. 当实例所在的节点发生故障不可用时,可执行evacuate操作,在另外一个新节点rebuild该实例,实现高可用。
. s* H2 p+ Q3 b: n- L) X' i这可以是OpenStack计算节点HA的一种实现方案。$ p' c0 C8 {+ o# Y
# c% k. P- D" ~% r) k二. API调用
. i/ Y$ J1 v. x+ g( _nova.servers.evacuate(server=fm['id']), on_shared_storage=True
) K& c8 g: V/ I' @9 u8 @* B# v1. on_shared_storage参数在2.14版本后废除,自动检查是否为共享存储。
& E# c- P9 @- }$ ^% w4 Z 共享存储能够保证实例在另外新节点重建后数据不丢失7 u4 B4 Q9 x q; s4 O6 H, w! M
2. 可以设置目的主机host
/ h3 Y8 j$ u) A, a5 ^& Z# t 如果不设置host,nova会通过scheduler选择一个新的主机(不会分到原主机,因为rebuild函数中过滤了原主机), w% I/ s) L' Y! F- n
3. 这个调用只是发送了evacuate操作命令,具体是否真正疏散成功,无法知道
( P0 {1 O: g: Y6 a. `
5 ~. c3 j3 z6 A. ]: @: N三. 源码分析
3 \8 Q2 X3 e/ v8 m2 A, j对应的是/nova/compute/api.py
- V0 {( P/ B& w6 x& A4 `@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED," q) E7 k ^$ N2 ?; w/ y$ q) ?
vm_states.ERROR])' p4 M* K5 P. [6 M
def evacuate(self, context, instance, host, on_shared_storage,
/ o# d) m7 _' y8 V. O admin_password=None)0 Q) t* t& } T) |
1. 函数上方有装饰符 @check_instance_state
3 N8 P, R a. M表示在执行evacuate方法前先执行check_instance_state:检测传入的instance的vm_state是否为ACTIVE、STOPPED或ERROR。如果不是这三种状态,不能执行evacuate方法。# b& p) P E J4 W. i9 @7 a/ |
+ {- W: d j) m; j7 b* o2. 首先检测instance所在主机的状态是否为down,如果不是down(比如up),执行会出错。
% s& Y1 A, m: n7 ELOG.debug('vm evacuation scheduled', instance=instance)
0 G; z0 f" j+ n M$ g5 D# 原实例所在主机
" b% V; f }/ h5 l$ l! K, ^inst_host = instance.host
* J6 d1 s) `9 K& E* i( K8 Wservice = objects.Service.get_by_compute_host(context, inst_host)
$ F* w9 }/ S( O6 p% r" \# 首先确保compute主机的状态为down: _. E x& m" D f2 U6 w Z
if self.servicegroup_api.service_is_up(service):
0 @9 @5 E4 V I; ]* Q- \ LOG.error(_LE('Instance compute service state on %s '/ F; _ `: C& b( h1 ]; a: s
'expected to be down, but it was up.'), inst_host)
( I' _. ^1 ]. _" D raise exception.ComputeServiceInUse(host=inst_host)
2 G) m7 B( S" f$ n' p& ?" F5 [/ u, y& g6 W) @/ p4 ?
3. 记录action执行操作
/ {" u& X* y! `+ V. y- y6 ~# 实例的任务状态设置为REBUILDING
8 x: x+ H- C1 o4 q# @* Kinstance.task_state = task_states.REBUILDING4 B9 m0 ~) q- B$ P$ p
instance.save(expected_task_state=[None])$ N% s2 p% `" x. }
self._record_action_start(context, instance, instance_actions.EVACUATE)
9 J, Y3 E3 _6 [& b: ~" [& w
$ N. s. j O5 H6 ^4. 初始化迁移类
9 L6 D( c4 y' D! r: B) T' f! x# nmigration = objects.Migration(context,$ e1 M* ^. \, A* H9 H( W, @: R
source_compute=instance.host,7 G* {. i+ B0 Q
source_node=instance.node,
% x# `: J" S8 H4 V' t& C. ~ instance_uuid=instance_uuid,
. A# A+ [, p% f status='accepted',
N/ P% G* x7 y% y migration_type='evacuation')7 U- T7 y4 ?+ e/ j- F
5. 创建迁移(这里为什么要创建migration,并没有执行迁移) B. d( k' E8 W& g# H5 |% H
# 如果提供了目的主机& }' m1 F9 d) I7 i$ a6 _* }
if host:# q0 w! A8 [ P7 C! \( Z0 x+ W
migration.dest_compute = host
! G" @& Q4 P3 f. J; E7 C& qmigration.create()* u. X8 h6 e. Y% {
/ j4 q" L7 D2 c3 u% S1 l4 O, g6. 发送消息通知实例的使用配额
/ q4 r$ E7 h3 O8 o' C8 V7 a8 Kcompute_utils.notify_about_instance_usage( u. u! P$ R3 G' }. W( f5 @
self.notifier, context, instance, "evacuate")
& t. H0 K" D, }) u% ?. c) R7 U* p* d H
7. 最后执行task任务:rebuild_instance2 e; T" C6 q; n* W& S
所以evacuate的本质是在新节点上执行rebuild操作
' Q: v. b7 V0 Z% yreturn self.compute_task_api.rebuild_instance(context,& m% n, i& ~; V0 P! l
instance=instance,' k! G6 L" r t; ^! D% Z
new_pass=admin_password,
/ B1 V6 h. v) B3 @2 U injected_files=None,
6 O. u# v/ J0 y- W image_ref=None,
8 ?! ] i* `% W5 n% W orig_image_ref=None,
4 V4 c# ?) ^$ r6 ]: v! z orig_sys_metadata=None," X. w$ C0 m5 e5 T6 M
bdms=None,
" H# k/ \8 S1 Y; l2 ~0 X8 l' {+ z recreate=True,
/ U& w1 N( x7 G( g4 u& L on_shared_storage=on_shared_storage,
8 [0 m$ s0 X8 k L! V5 | host=host)
+ q/ t! S" f4 g$ Y深入分析rebuild_instance方法,通过各种rpc调用,最终具体执行的是/nova/conductor/manager.py) {; B. ?4 Q# b0 s) H; f3 p1 ^
def rebuild_instance(self, context, instance, orig_image_ref, image_ref,# t* {. Y9 w6 f( |0 r5 h) ~( P
injected_files, new_pass, orig_sys_metadata," z6 B1 }' }. l: T3 e) P/ l
bdms, recreate, on_shared_storage,/ u5 c. E3 M; y! X
preserve_ephemeral=False, host=None):
4 H5 e) v4 F6 F3 m2 x o* ?(1)在选择新目的主机时先排除instance所在主机9 J+ I* p3 y/ r- P
这样能确保不会在原主机上执行rebuild操作% t9 G- _! |3 s* U
# 排除原实例所在的主机,即不能在同一个主机里进行rebuild
9 V( h; O3 K) v1 q8 u7 Jfilter_properties = {'ignore_hosts': [instance.host]}) n' ?8 L# y! C( P% E- T
hosts = self.scheduler_client.select_destinations(context,) n1 A& y! g2 l
request_spec,; o+ N4 @9 m; r+ o
filter_properties)7 z. S/ a7 V+ k# [$ q+ w- Y" t
(2)接下来会通过scheduler模块筛选出合适的新主机
# O$ U2 L, b7 m+ Y- e(3)如果没有选出足够的合适新主机,则抛出异常
8 L+ V( e( C2 c l% J" {except exception.NoValidHost as ex:7 { F4 T: e5 F- v# D
with excutils.save_and_reraise_exception():+ }* J0 T! m' U1 t7 R6 J
self._set_vm_state_and_notify(context, instance.uuid,
; |' ]+ E8 t+ d! t. P. x* y3 T 'rebuild_server',; @+ ?7 r" ?5 O/ R! C4 x( R4 b O
{'vm_state': instance.vm_state,, H. U2 |4 q/ ~; V/ K% t
'task_state': None}, ex, request_spec), |& Y B5 _0 V, u! E6 ?- X
LOG.warning(_LW("No valid host found for rebuild"),# C9 ^, u! h6 [- ] r0 a- s! s
instance=instance)9 w, ^& g: T! E8 g. c
不能选出合适的新主机,有可能是除了原节点外,其他节点都不可用(computer service status:disabled)或网络不通(computer service state:down),导致没有合适的新主机。
, R0 p/ n6 A4 W2 d" f
% `/ C! q3 k+ f; H& j# P- N |
|