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

楼主 |
发表于 2021-8-30 17:35:43
|
显示全部楼层
一. 当实例所在的节点发生故障不可用时,可执行evacuate操作,在另外一个新节点rebuild该实例,实现高可用。
1 Z* P. q6 ] v$ h+ f. }这可以是OpenStack计算节点HA的一种实现方案。
( W' {; w7 G8 B
* Q! m) }' B& L: Y; R- e二. API调用) }+ m/ `: K4 t+ s& l% ?
nova.servers.evacuate(server=fm['id']), on_shared_storage=True
9 B+ c( t0 {9 I1. on_shared_storage参数在2.14版本后废除,自动检查是否为共享存储。3 ?$ k+ N" o* J. ]3 u1 t
共享存储能够保证实例在另外新节点重建后数据不丢失( d: N- C) m. B' K1 }2 ?
2. 可以设置目的主机host3 Y" W% X/ X! ]
如果不设置host,nova会通过scheduler选择一个新的主机(不会分到原主机,因为rebuild函数中过滤了原主机)
: i' B" n* o: S5 @5 I+ s, y L( g3. 这个调用只是发送了evacuate操作命令,具体是否真正疏散成功,无法知道. B8 ]. e4 k7 |- ^: F
7 a5 Z/ l5 ^9 W1 Q( V3 P: r
三. 源码分析( ^* B1 `/ I6 H
对应的是/nova/compute/api.py2 K5 Q0 Q, |: Z. E+ [/ s6 f) P
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,$ X' B1 _' P4 D5 A: w3 L# A) M- v
vm_states.ERROR])2 X$ g5 C/ n+ [' c4 r7 ]$ y6 k
def evacuate(self, context, instance, host, on_shared_storage,8 F" I% j) E7 |8 E& x5 N$ V7 W
admin_password=None)
" D; V- ^- s) A1. 函数上方有装饰符 @check_instance_state
9 {$ T( v3 N' {: Q/ B表示在执行evacuate方法前先执行check_instance_state:检测传入的instance的vm_state是否为ACTIVE、STOPPED或ERROR。如果不是这三种状态,不能执行evacuate方法。
$ ~- V9 H2 w3 _$ J
3 I7 F7 `- T+ {( W- t7 D. t m* ^2. 首先检测instance所在主机的状态是否为down,如果不是down(比如up),执行会出错。6 o* n- y: N& _8 f* O
LOG.debug('vm evacuation scheduled', instance=instance)$ i+ U- x7 ^( o$ r5 s( E
# 原实例所在主机) Q" N% L! r% d* a4 W. |
inst_host = instance.host
! c4 J! Y) c- U, Q! K' g1 tservice = objects.Service.get_by_compute_host(context, inst_host)9 [9 p% z- @0 q$ w" w
# 首先确保compute主机的状态为down3 M7 n: p8 F' y- H; X+ q1 ]
if self.servicegroup_api.service_is_up(service):
6 J+ m4 I. L$ R; N$ U$ f LOG.error(_LE('Instance compute service state on %s '
; F- j- d1 u5 G0 W0 C( `6 y% h& i 'expected to be down, but it was up.'), inst_host)
0 t) T# J# t$ H, s% C7 {5 j/ C raise exception.ComputeServiceInUse(host=inst_host) X' g1 o+ b# r4 U$ v( ?2 i( ~( [
- V& ]" t! q1 a" v; {' Y% I3. 记录action执行操作2 w: E9 ^+ n- J% x
# 实例的任务状态设置为REBUILDING" H6 H9 Y3 o+ W7 E! \8 O- [
instance.task_state = task_states.REBUILDING* n6 M. ^9 d) S$ V
instance.save(expected_task_state=[None])3 i5 ]: ~, w# N# s6 w* n
self._record_action_start(context, instance, instance_actions.EVACUATE)
6 Q4 X* I k- {! q5 _4 n1 d$ L2 J% H% o" F, m/ E: N1 b
4. 初始化迁移类1 X. [6 V7 Y# L' t
migration = objects.Migration(context,
! `2 \5 k+ K7 [5 [6 o% V4 V1 C source_compute=instance.host,- w) \; q7 x& [8 [ L$ F. B
source_node=instance.node,3 L1 N8 j) w4 z/ F- O
instance_uuid=instance_uuid,
: i; P+ H k T3 s. _$ f* i) x/ \ status='accepted',
. k {8 E' x/ L. b; `1 R9 G% y& \ migration_type='evacuation')- J$ D, Y7 E1 y% R0 S3 u0 z; A: R* S
5. 创建迁移(这里为什么要创建migration,并没有执行迁移)
. W* ?1 _% {- o; q2 J: ]# 如果提供了目的主机, ^' g/ E, j" j: S5 K2 H
if host:
8 d5 [6 @6 m3 O migration.dest_compute = host% X5 C: A0 E" A3 T* }
migration.create()
. A; i! U5 g5 @# b
6 ~/ o+ F7 E$ N7 j! T6. 发送消息通知实例的使用配额
$ s1 t' a, ]! G& T |$ q6 Wcompute_utils.notify_about_instance_usage(
! ]. E0 U4 Q' ^! ]0 A3 f( W self.notifier, context, instance, "evacuate")& o3 v5 b5 ~8 X( }8 c
$ |! I' F4 G+ V3 j# E n
7. 最后执行task任务:rebuild_instance
# @' @/ ~- B/ x6 [0 F' h所以evacuate的本质是在新节点上执行rebuild操作
; k; O4 x, H6 r4 }- K5 b; b+ wreturn self.compute_task_api.rebuild_instance(context,
6 |% g; T. \1 M1 Z instance=instance,
+ z- V) z: j4 T: R/ d new_pass=admin_password,
) _0 ^# l4 v9 L# y injected_files=None,
( d2 S: ~! m4 N6 O$ @/ A6 G image_ref=None,3 G% x. ~9 ^; o
orig_image_ref=None,
1 H4 s& \4 S1 b, n/ X9 ^& ^. G orig_sys_metadata=None,
2 w: Y# }4 T* t" p* s/ N7 D$ A" g bdms=None,
: [% g- E9 n& [2 m recreate=True,
9 _, U$ o6 \) t& M1 V4 ~% x0 N on_shared_storage=on_shared_storage,0 ] n: D' }2 E6 F4 F K8 |
host=host)' ^7 [% P# ~, q1 Y. X( v) B
深入分析rebuild_instance方法,通过各种rpc调用,最终具体执行的是/nova/conductor/manager.py
% P# |5 U: ^* S) R( [/ L7 Q! \def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
, _+ o/ A% R8 H4 Q1 J injected_files, new_pass, orig_sys_metadata,
$ v: Z/ E. z( G) W bdms, recreate, on_shared_storage,8 [7 q& m$ G0 H' ? _ `
preserve_ephemeral=False, host=None):; B/ \3 j4 r& L. q8 ?
(1)在选择新目的主机时先排除instance所在主机
) C* B) Y, r6 e8 z这样能确保不会在原主机上执行rebuild操作
7 a) X# W |. k% h$ ~: s( y, U$ z1 u1 Y# 排除原实例所在的主机,即不能在同一个主机里进行rebuild
1 q5 _) {# Y7 Zfilter_properties = {'ignore_hosts': [instance.host]}1 D Z0 \9 f% t
hosts = self.scheduler_client.select_destinations(context,1 a) h3 A1 Z) ~) a) t
request_spec,
4 R: Q# O2 c, P# T( j( a filter_properties). G( q# \5 }- e* `1 {8 o1 A' C
(2)接下来会通过scheduler模块筛选出合适的新主机 B/ E% f0 t- L( b: f) ^- U x
(3)如果没有选出足够的合适新主机,则抛出异常5 q$ }* J8 [( G7 p# S) ]4 \
except exception.NoValidHost as ex:
$ A8 u# g# \" C7 E! ?) T( I! l" B with excutils.save_and_reraise_exception():- T) u3 L* r% B, j
self._set_vm_state_and_notify(context, instance.uuid,3 O+ ~, F+ s1 S) _) U8 h1 f9 `8 d
'rebuild_server',
( ^- e# ]8 _3 {: u {'vm_state': instance.vm_state,2 j! \; d( a! j" ? n$ D1 U$ Q
'task_state': None}, ex, request_spec)
" Q9 n3 O4 { v4 v LOG.warning(_LW("No valid host found for rebuild"),7 c! r: A. v" t2 M
instance=instance)7 U% u+ N9 u3 E6 H: B7 p
不能选出合适的新主机,有可能是除了原节点外,其他节点都不可用(computer service status:disabled)或网络不通(computer service state:down),导致没有合适的新主机。& Q+ _0 D* f0 z( p/ K9 S' x
; x$ X- v3 J6 h$ Q
|
|