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

楼主 |
发表于 2021-8-30 17:35:43
|
显示全部楼层
一. 当实例所在的节点发生故障不可用时,可执行evacuate操作,在另外一个新节点rebuild该实例,实现高可用。( r- b. h4 y8 w+ C% O( v
这可以是OpenStack计算节点HA的一种实现方案。; T3 P! f6 }$ w B2 K( a4 S
3 v$ E2 y1 e% k4 R, ^二. API调用
' x7 W2 _6 }; w6 t- p3 Tnova.servers.evacuate(server=fm['id']), on_shared_storage=True
" O& n x4 P( G) n9 Y% A1. on_shared_storage参数在2.14版本后废除,自动检查是否为共享存储。
5 n% M! P }8 |; B 共享存储能够保证实例在另外新节点重建后数据不丢失
7 _9 C% `$ B. w) w2. 可以设置目的主机host
* W( C( Q- g) A. u/ v; x# t6 Q 如果不设置host,nova会通过scheduler选择一个新的主机(不会分到原主机,因为rebuild函数中过滤了原主机)6 k1 n/ s9 U+ r
3. 这个调用只是发送了evacuate操作命令,具体是否真正疏散成功,无法知道
$ N8 C5 `9 q& d9 |* S$ S
1 O9 G1 Q3 i' F, l# Y+ R |* w" v9 g三. 源码分析, r: _: y/ I# G
对应的是/nova/compute/api.py
2 V* k) \3 j% _7 d6 h5 R9 w1 d@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,
, ^6 U9 j! G# E4 c) K% K" C vm_states.ERROR])
1 u: j# Q% v: y3 e3 Sdef evacuate(self, context, instance, host, on_shared_storage, \- K8 m) C% ]) m+ E+ }& a
admin_password=None)
( M, x5 T3 ^* M5 O$ G/ [1. 函数上方有装饰符 @check_instance_state, b+ g/ J! X2 j# P% B/ x
表示在执行evacuate方法前先执行check_instance_state:检测传入的instance的vm_state是否为ACTIVE、STOPPED或ERROR。如果不是这三种状态,不能执行evacuate方法。7 ^" J+ R: ]& }. |& f
. H; R$ Y# e/ U- ~
2. 首先检测instance所在主机的状态是否为down,如果不是down(比如up),执行会出错。
2 N: K/ C, N7 p7 U! w" \. Z$ {LOG.debug('vm evacuation scheduled', instance=instance)
6 s, h; F* ? w/ }9 b r( d+ X6 W/ }3 D# 原实例所在主机% Q; P: @% e; g$ b6 }
inst_host = instance.host
. L& V: V$ M5 ^( l6 w n" Lservice = objects.Service.get_by_compute_host(context, inst_host)
( P+ R+ ]6 A$ A/ T- n j& s# 首先确保compute主机的状态为down6 `5 v) r4 c/ p, I; ~# N! s/ Q" f
if self.servicegroup_api.service_is_up(service):$ E5 G F- Z$ T6 ^' O
LOG.error(_LE('Instance compute service state on %s '
N. U U& t# Y( n- \4 P 'expected to be down, but it was up.'), inst_host)
; `& h1 p! b: _# a raise exception.ComputeServiceInUse(host=inst_host)0 `4 W/ ]3 V. g5 w
4 q& i& p7 H4 z. G3. 记录action执行操作
9 B5 K6 t0 f' f# N. E1 C: Q8 v# 实例的任务状态设置为REBUILDING
1 Z5 t; n5 U6 }- Iinstance.task_state = task_states.REBUILDING: l1 o8 R/ x& B8 e
instance.save(expected_task_state=[None]): u: L R- M! y4 d" ^) X
self._record_action_start(context, instance, instance_actions.EVACUATE)$ X; h6 v8 h1 V7 o
3 F4 @- |# G s+ J" K; D3 W- ~1 k) l4. 初始化迁移类" B& B5 l3 k* A1 Q
migration = objects.Migration(context,3 r! U+ j8 a0 o
source_compute=instance.host,4 y& K8 z! q$ e" ], Y" m
source_node=instance.node,
: L6 ]' [# M' r; w- C instance_uuid=instance_uuid, v% X, |. I& Q* P
status='accepted',
* W `2 U S9 S migration_type='evacuation')
- X2 u- [8 n( l7 S$ M7 T# E5. 创建迁移(这里为什么要创建migration,并没有执行迁移)
4 a9 m4 o3 V6 K( T# 如果提供了目的主机' O0 |5 ?0 R+ w% }
if host:
) I; M5 J1 S. y migration.dest_compute = host
' [1 o' X& P. l+ X) Q* gmigration.create(): y0 g6 [4 Y+ ~ z$ J- c
' X- X! C8 F* j+ k6. 发送消息通知实例的使用配额
; q2 x: {% A; v( ? I" J( M$ kcompute_utils.notify_about_instance_usage($ l, r" l) @$ s
self.notifier, context, instance, "evacuate")
1 [4 U" o% A9 f2 B
0 g' z" o$ @* X, l7. 最后执行task任务:rebuild_instance( O5 @+ }% F0 ?) ?+ v+ ]! t
所以evacuate的本质是在新节点上执行rebuild操作
# \" n! b. w" l" X4 M0 v0 N' Zreturn self.compute_task_api.rebuild_instance(context,
* y8 p2 X" F1 t3 Q3 g instance=instance,
Q7 s6 f/ n; [6 e( ? D new_pass=admin_password,7 B* t+ Q6 ]! z1 A9 N
injected_files=None,
: G' Q) G5 f1 F. X" i2 z* m3 B+ A image_ref=None,
: y" f) L6 n k+ n orig_image_ref=None,
, h0 m3 P1 }# W2 b7 ]3 p# E orig_sys_metadata=None,7 T u2 p9 d/ C/ J. n0 Y5 ?
bdms=None,* W2 e* _0 D6 H2 N7 [- e
recreate=True,: K; B, P0 B7 M/ a
on_shared_storage=on_shared_storage,: Y* y1 o% p/ _ H
host=host)
5 m8 H: u+ X) Z& }/ } W2 A6 T深入分析rebuild_instance方法,通过各种rpc调用,最终具体执行的是/nova/conductor/manager.py8 z! p4 d1 y1 P" \4 E
def rebuild_instance(self, context, instance, orig_image_ref, image_ref,3 w' s. {* o2 u1 q% c# B; A) C
injected_files, new_pass, orig_sys_metadata,+ p! U3 C2 T: x$ Z% A! x% s& b
bdms, recreate, on_shared_storage,7 K6 B. k( r6 g. @* z8 _) k- l
preserve_ephemeral=False, host=None):) B0 v1 U: z7 d M. t. J
(1)在选择新目的主机时先排除instance所在主机
* S" }+ k1 O9 |% y7 H4 U" b4 s这样能确保不会在原主机上执行rebuild操作
! g/ Q Y& U7 j. i2 z2 _( S# 排除原实例所在的主机,即不能在同一个主机里进行rebuild3 u' A5 \4 B& }: U' H
filter_properties = {'ignore_hosts': [instance.host]}" }5 X5 g5 N; T }
hosts = self.scheduler_client.select_destinations(context,% ?. y2 l5 n2 v4 r/ h6 l
request_spec,
5 g8 O* R* _4 e! w8 l+ c8 `8 m filter_properties)( }1 w( Y3 U: C4 y% J; T
(2)接下来会通过scheduler模块筛选出合适的新主机& D. U# C9 {0 X3 r$ C: M0 x
(3)如果没有选出足够的合适新主机,则抛出异常. t" r" P* a' H' v% N
except exception.NoValidHost as ex:
$ X4 }/ z# N" P% v# c: b8 S with excutils.save_and_reraise_exception():
/ p( a% W0 o3 u. H0 Y# p+ [ self._set_vm_state_and_notify(context, instance.uuid,- v7 l( l& ]1 N. q
'rebuild_server',0 E6 ]$ I( C1 D; H3 t
{'vm_state': instance.vm_state,
" B- u5 g2 Y/ r, Z7 I5 P5 v2 L 'task_state': None}, ex, request_spec)2 f3 p6 K0 C3 x7 s7 s) a8 [# p3 h
LOG.warning(_LW("No valid host found for rebuild"),
# H1 e, w3 W1 R! `% C& Z5 j f instance=instance)
# W- c, d/ `1 ]4 p: i: ^6 L* [( \% I不能选出合适的新主机,有可能是除了原节点外,其他节点都不可用(computer service status:disabled)或网络不通(computer service state:down),导致没有合适的新主机。) O }1 u# p7 F# B2 z4 V
3 O) s+ Y1 f- b$ O |
|