|
|
楼主 |
发表于 2021-8-30 17:35:43
|
显示全部楼层
一. 当实例所在的节点发生故障不可用时,可执行evacuate操作,在另外一个新节点rebuild该实例,实现高可用。
8 l1 K* p8 W8 s* n' s- h2 a5 w& z这可以是OpenStack计算节点HA的一种实现方案。
2 z8 k: R: g' D; ]* b; [
5 @/ j- h) J4 V) J, J# u: t) @二. API调用 \. K1 ~2 ~/ ~7 j
nova.servers.evacuate(server=fm['id']), on_shared_storage=True* j: X: a( S9 I* l) U1 f
1. on_shared_storage参数在2.14版本后废除,自动检查是否为共享存储。1 R3 f, s2 W. A0 ]/ e- {- }- o
共享存储能够保证实例在另外新节点重建后数据不丢失
' {- @9 o9 l. R2. 可以设置目的主机host
; i/ W" a' m" r, q/ A/ n 如果不设置host,nova会通过scheduler选择一个新的主机(不会分到原主机,因为rebuild函数中过滤了原主机)
8 X; w$ E" h+ N$ _3. 这个调用只是发送了evacuate操作命令,具体是否真正疏散成功,无法知道
4 y8 r6 c8 A$ a4 S
4 a$ n) W! i" A a3 r3 c三. 源码分析
; [! ~' J. o1 Q3 |! O- h对应的是/nova/compute/api.py
! O2 @6 l% I3 n/ N9 e! U- S8 @@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,+ p' S) ~, b5 O, ~4 ~$ m: S- `
vm_states.ERROR])9 q0 m( q$ B, Y6 K$ p
def evacuate(self, context, instance, host, on_shared_storage, @8 C0 \% {! Y* M
admin_password=None)5 P8 }5 V: ^* k& o$ D! I: s
1. 函数上方有装饰符 @check_instance_state
* U ~1 A2 q0 z$ W* p* z- d& {# J( ?6 |表示在执行evacuate方法前先执行check_instance_state:检测传入的instance的vm_state是否为ACTIVE、STOPPED或ERROR。如果不是这三种状态,不能执行evacuate方法。
( _( i/ m8 v4 U u( R
r1 s4 W* k7 Y5 h; G* g2. 首先检测instance所在主机的状态是否为down,如果不是down(比如up),执行会出错。
, I4 N! V) {2 C; j7 qLOG.debug('vm evacuation scheduled', instance=instance)9 T3 k) z$ N% M2 V- A! w, T4 }
# 原实例所在主机
+ i7 n3 y. O, D& z* A3 Einst_host = instance.host& b1 u) j n5 d5 x
service = objects.Service.get_by_compute_host(context, inst_host)2 c' h) b# n7 `" e
# 首先确保compute主机的状态为down& h y' }1 h# h7 i2 g
if self.servicegroup_api.service_is_up(service):) Z" A8 n' k- z3 Q; N6 m4 i
LOG.error(_LE('Instance compute service state on %s '% U# A9 N; H% g/ v& f6 x
'expected to be down, but it was up.'), inst_host)
0 f& o) U2 U: [# {& z7 g4 H; _ raise exception.ComputeServiceInUse(host=inst_host)
, m3 l9 S! L7 I7 K6 e: ~
0 z- F! T/ o4 F' ^3. 记录action执行操作
! W( {$ [2 [$ o# 实例的任务状态设置为REBUILDING
. t5 S- q2 e8 B; Q- G' kinstance.task_state = task_states.REBUILDING- Y+ @1 C0 y' R) b, E$ Q9 K' t
instance.save(expected_task_state=[None])
4 W x; T$ Y0 x' w. j7 Y, Yself._record_action_start(context, instance, instance_actions.EVACUATE)
7 a% q. a; V+ t0 z+ u* R
- j0 V' k8 P. L4. 初始化迁移类
2 T/ E( |! F2 R: q- K, s; rmigration = objects.Migration(context,& s1 S, R+ V+ G, Z1 d- D
source_compute=instance.host,; e- K$ M& l. l7 j' F8 t
source_node=instance.node,1 t4 A( _6 P. p* ^
instance_uuid=instance_uuid,' d, A' m* b' M% Q
status='accepted',$ H$ K: r6 f: {) h: A
migration_type='evacuation')
" {1 R3 W" @6 U8 q5 J! A5 F5 \5. 创建迁移(这里为什么要创建migration,并没有执行迁移)
% o& H3 i6 b& v) q) m/ r4 Y( a# 如果提供了目的主机
; z4 s& [0 r6 h9 C# J7 U3 w6 lif host:; @* @( x. E/ \& s
migration.dest_compute = host' Q) p1 Z; {% W8 P- P2 f! ]
migration.create()
1 H3 v! m$ n1 m: @9 u# V# F8 A1 b1 Y6 u" c0 Q
6. 发送消息通知实例的使用配额
0 R! K5 o! f I6 Y O0 ]compute_utils.notify_about_instance_usage(
& H' V8 ?- B- {- [# r; d% _" X3 T self.notifier, context, instance, "evacuate")
$ \" B- H0 I8 v7 j
' `6 h, H6 }5 L7 L# J7. 最后执行task任务:rebuild_instance" _( `: S5 ^2 g
所以evacuate的本质是在新节点上执行rebuild操作
4 o- K8 D$ {: `& P9 [7 \return self.compute_task_api.rebuild_instance(context,5 P4 b! q J: [* h: R% ~3 T
instance=instance,+ Y& Z B1 X) G$ x. D# R
new_pass=admin_password,0 r# ^" Z h! Y4 i2 f0 t
injected_files=None,
, a3 j) o# i* A" U image_ref=None,
) E9 O2 N, W. `% S1 c- `+ _: g orig_image_ref=None,$ y3 F q% }8 b
orig_sys_metadata=None,
# k- c6 s7 k1 w bdms=None,
- N& i1 S% u0 B0 E8 b9 l recreate=True,+ ?% R' u5 g' H) V P
on_shared_storage=on_shared_storage,% H. z1 V& k+ g# _9 k, W! X
host=host)
X" d4 Z4 e4 m深入分析rebuild_instance方法,通过各种rpc调用,最终具体执行的是/nova/conductor/manager.py
" K8 L2 `3 B/ h! ?def rebuild_instance(self, context, instance, orig_image_ref, image_ref,& j* u0 z- w8 n, N! x
injected_files, new_pass, orig_sys_metadata,2 ?4 D$ r. C6 `
bdms, recreate, on_shared_storage,
7 G4 k, f/ f. I1 }4 ~ preserve_ephemeral=False, host=None):
5 s0 n3 K* j1 Y4 u; c(1)在选择新目的主机时先排除instance所在主机
% |$ Z `+ J5 P这样能确保不会在原主机上执行rebuild操作
: Z/ n' P6 H2 F7 W2 d! D1 n# 排除原实例所在的主机,即不能在同一个主机里进行rebuild3 D: t0 J+ n" l8 a, e# m
filter_properties = {'ignore_hosts': [instance.host]}' r( g8 v4 G+ S. x
hosts = self.scheduler_client.select_destinations(context,
: K |/ f' k9 }% |1 E request_spec,8 c" W7 C6 @3 s& B' e3 U
filter_properties)
9 P% y/ Y$ S3 L: u& F! @(2)接下来会通过scheduler模块筛选出合适的新主机
/ W7 L1 s3 S% e(3)如果没有选出足够的合适新主机,则抛出异常8 f. s! w7 ]& f+ c* V
except exception.NoValidHost as ex:, @) H7 U* l/ @# _' g9 P
with excutils.save_and_reraise_exception():
4 V# z" w7 C, g% q0 @# S self._set_vm_state_and_notify(context, instance.uuid,: F3 ~* x' g3 [$ M
'rebuild_server',
8 D9 ~& E( s3 i" d2 D {'vm_state': instance.vm_state,$ ?' u- l R, o8 a' F! M
'task_state': None}, ex, request_spec)! V6 O# F1 r4 P% _9 [+ f
LOG.warning(_LW("No valid host found for rebuild"),$ u. |( |# d$ {2 q# |& Q/ f
instance=instance)
+ V% Q8 t# h- ?1 i/ e/ y1 `不能选出合适的新主机,有可能是除了原节点外,其他节点都不可用(computer service status:disabled)或网络不通(computer service state:down),导致没有合适的新主机。
2 u& ]. A3 k) ^' I7 E# _% I6 Z+ a, O
# {8 F& y% y8 Z1 U- ?* U |
|