|
本文的环境均为:Openstack+Ceph 运行虚拟机的场景,即主要使用RBD,不包含RGW,MDS。虚机的系统盘(Nova),云硬盘(Cinder),镜像盘(Glance)的块均保存在共享存储Ceph中。 环境准备, N. O* H' S6 p5 e6 t0 h$ `
9 g& C6 [9 M1 }- Z
本文环境为 Openstack (Kilo) + Ceph(Jewel)
本文所用的环境包含一套完整的 Openstack 环境,一套 Ceph 环境,其中 Nova/Cinder/Glance 均已经对接到了 Ceph 集群上,具体节点配置如下: [td]| 主机名 | IP地址 | Openstack 组件 | Ceph 组件 | | con | 192.168.10.10 | nova,cinder,glance,neutron | mon,osd*1 | | com | 192.168.10.11 | nova,neutron | mon,osd*1 | | ceph | 192.168.10.12 | | mon,osd*1 |
1 i6 S7 @+ F; e# F
在集群整体迁移完后,各个组件分布如下,也就是说,将运行于 con,com,ceph三个节点的 Ceph 集群迁移到 new_mon_1,new_mon_2,new_mon_3 这三台新机器上。 [td]| 主机名 | IP地址 | Openstack 组件 | Ceph 组件 | | con | 192.168.10.10 | nova,cinder,glance,neutron | | | com | 192.168.10.11 | nova,neutron | | | ceph | 192.168.10.12 | | | | new_mon_1 | 192.168.10.13 | | mon,osd*1 | | new_mon_2 | 192.168.10.14 | | mon,osd*1 | | new_mon_3 | 192.168.10.15 | | mon,osd*1 |
( O2 G2 T0 p( b; X* U
在迁移之前,我们创建一个虚机,一个云盘,上传一个镜像,虚机此时正常运行,并将这这块云盘挂载到虚机上: [root@con ~(keystone_admin)]# nova list V1 U+ F0 E/ ?- C' N; {+ i! {
+--------------------------------------+---------+--------+------------+-------------+-------------------------+: B/ J# P/ y; x1 D9 u8 t
| ID | Name | Status | Task State | Power State | Networks |
$ ?' g) ?3 T( \% j% }+--------------------------------------+---------+--------+------------+-------------+-------------------------+
7 \7 I: o( r1 \ I6 D1 {. Y| f52191f-9645-448f-977b-80ca515387f7 | vm1-test | ACTIVE | - | Running | provider=192.168.8.111 |
) H( T1 ?; Q/ q+--------------------------------------+---------+--------+------------+-------------+-------------------------+
5 F& m: d5 b$ @- z[root@con ~(keystone_admin)]# cinder list
( d, {# i- |& E. ]+--------------------------------------+--------+--------------+------+-------------+----------+--------------------------------------+
0 I' ~5 r9 g }7 o, q; _| ID | Status | Display Name | Size | Volume Type | Bootable | Attached to |
0 v$ m: ?: h- d7 N9 h% S0 u+--------------------------------------+--------+--------------+------+-------------+----------+--------------------------------------+ h4 B: |: N# C
| 39c76d96-0f95-490c-b7db-b3da6d17331 | in-use | cinder-rbd | 1 | None | false | f52191f-9645-448f-977b-80ca515387f7 |% ]; Y: ~* q$ K
+--------------------------------------+--------+--------------+------+-------------+----------+--------------------------------------+6 D, S7 g3 g' g+ u1 ]3 D) B) ~- T
[root@con ~(keystone_admin)]# ip netns exec `ip netns` ssh cirros@192.168.8.111" d) A) w% u: N* Q" t
cirros@192.168.8.111's password:
, }3 ~" ]4 i( \4 P$ lsblk
" i# v; W3 ?8 r1 sNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
/ s4 R6 ~) K; G- T' Kvda 253:0 0 1G 0 disk
! s: Q- _4 z4 ~" S) C$ ~: y2 R1 D`-vda1 253:1 0 1011.9M 0 part /) Y8 G! A+ J' n
vdb 253:16 0 1G 0 disk
6 w& f# y- }' d: i; U$ Ceph 集群状态: [root@ceph cluster]# ceph -s1 ?! ]( i1 {+ p6 k+ m
cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d/ L+ Q& d+ A" Y! i, @% ], @
health HEALTH_OK
7 X8 Z/ t$ D/ T0 o8 w monmap e47: 3 mons at {ceph=192.168.10.12:6789/0,com=192.168.10.11:6789/0,con=192.168.10.10:6789/0}) C4 u/ g [0 s; N6 i/ {/ X2 q
election epoch 174, quorum 0,1,2 con,com,ceph
+ k, _+ f+ _3 A) I1 M osdmap e57: 3 osds: 3 up, 3 in
+ i8 h. G9 t3 ~: ~ ] flags sortbitwise,require_jewel_osds
6 N1 w& w6 k8 g7 z! T) V! M pgmap v6577: 768 pgs, 3 pools, 45659 kB data, 23 objects
: o4 s7 K" e9 C; l 178 MB used, 766 GB / 766 GB avail) R( t( ?9 q5 ?- M
768 active+clean7 t9 \2 p. |7 s. b/ W
" ^% S2 U% @! b+ y[root@ceph cluster]# ceph osd tree
! E0 x& o* H" n2 c4 zID WEIGHT TYPE NAME UP/DOWN REWEIGHT PRIMARY-AFFINITY , j; z: O0 `! s3 S2 s
-1 0.74876 root default 0 i$ A. K- ~4 f
-2 0.24959 host ceph 2 t, x7 X4 X4 W7 e( g$ U+ n
0 0.24959 osd.0 up 1.00000 1.00000 . [* x7 R, E( e; K4 Z! n
-3 0.24959 host con : T) B% v+ K; D
1 0.24959 osd.1 up 1.00000 1.00000 ' S/ @. N4 n) R0 P# j6 V- a
-4 0.24959 host com ' W4 r! _, H0 J6 [$ Y, z
2 0.24959 osd.2 up 1.00000 1.00000 ! u/ u) Z3 a& e, L% h
/ N# V% u/ x7 F9 i" K6 b[root@ceph cluster]# ceph osd pool ls detail 3 v3 k9 o- C! X3 f
pool 1 'volumes' replicated size 2 min_size 1 crush_ruleset 0 object_hash rjenkins pg_num 256 pgp_num 256 last_change 58 flags hashpspool stripe_width 05 L" r( ]" @ ?+ j9 l6 c4 N$ {! h. w
removed_snaps [1~3]' [' o* Z8 W, J3 d T* u
pool 2 'vms' replicated size 2 min_size 1 crush_ruleset 0 object_hash rjenkins pg_num 256 pgp_num 256 last_change 59 flags hashpspool stripe_width 0
) ]# C7 R: P8 e! M" Y removed_snaps [1~3]
' C3 _' Q- K/ h' o4 Q+ L& gpool 3 'images' replicated size 2 min_size 1 crush_ruleset 0 object_hash rjenkins pg_num 256 pgp_num 256 last_change 60 flags hashpspool stripe_width 0
. d* \( w0 V o0 Y6 z removed_snaps [1~9]
, S1 n& p' N2 K9 i: d0 i3 M% e: Z. Q& h( I( v% D) _/ m( d3 }' l+ D
OSD的数据迁移
2 h: [$ e5 q- {- x2 [% D8 X+ S( v9 F
: l# J6 _. T+ M3 B, ~
本次迁移主要分为两个组件的迁移,即 MON 和 OSD,这里我们先介绍 OSD 的数据迁移。相比迁移MON来说,OSD的数据迁移步骤更为单纯一些,因为所有操作均在 Ceph 侧执行,对 Openstack 来说是透明的。 原理简介由于CRUSH算法的伪随机性,对于一个PG来说,如果 OSD tree 结构不变的话,它所分布在的 OSD 集合总是固定的(同一棵tree下的OSD结构不变/不增减),即对于两副本来说: PG 1.0 => [osd.66, osd.33] 当副本数减少时,PG 1.0 => [osd.66] ,也就是说会删除在osd.33上的第二副本,而在 osd.66上的主副本是维持不变的,所以在降低副本数时,底层OSD实际上只删除了一份副本,而并没有发生数据的迁移。 当副本数增加时,PG 1.0 => [osd.66, osd.33, osd.188, osd.111],也就是说四副本的前两个副本依旧是之前的两个副本,而后面增加的两副本会从主副本osd.66将数据backfill到各自的OSD上。
: z. x+ e7 `: B 迁移思路
; F& _$ A* f) e9 N u2 p4 G+ A/ \' w& B! _: a
• crush rule 0 (原先默认生成的): 从 old_tree 下选出size副本(这里size=2)。 • crush rule 1 (新生成的第一条): 从 old_tree 下选出两副本。对于副本数为2的集群来说,crush_rule_0 和 crush_rule_1 选出的两副本是一样的。 • crush rule 2 (新生成的第二条): 从 old_tree 下选出两副本,再从 new_tree 下选出两副本。由**原理简介第二段**可知, 由于 old_tree 下面的 OSD结构不变也没有增加,所以 crush_rule_0 和 crush_rule_1 选出的前两副本是**一样的**。 • crush rule 3 (新生成的第三条): 从 new_tree 下选出两副本。 由于crush_rule_1 的第二次选择为选出 new_tree下的前两副本,这和 crush_rule_2 选出两副本(也是前两副本)其实是**一样的**。 注入新的CRUSHMAP后,我们做如下操作: • 将所有pool(当然建议一个pool一个pool来,后面类似) 的 CRUSH RULE 从 crush_rule_0 设置为 crush_rule_1 ,此时所有PG均保持active+clean,状态没有任何变化。 • 将所有pool的副本数设置为4, 由于此时各个 pool 的CRUSH RULE 均为 crush_rule_1 ,而这个 RULE 只能选出两副本,剩下两副本不会被选出,所以此时所有PG状态在重新 peer 之后,变为 active + undersized + degraded,由于不会生成新的三四副本,所以集群没有任何数据迁移(backfill) 动作,此步骤耗时短暂。 • 将所有pool的 CRUSH RULE 从 crush_rule_1 设置为 crush_rule_2,此时上一条动作中没有选出的三四副本会从 new_tree 下选出,并且原先的两副本不会发生任何迁移,整个过程宏观来看就是在 old_tree -> new_tree 单向数据复制克隆生成了新的两副本,而旧的两副本没有移动。此时生成新的两副本耗时较长,取决于磁盘性能带宽数据量等,可能需要几天到一周的时间,所有数据恢复完毕后,集群所有PG变为 active+clean 状态。 • 将所有pool的 CRUSH RULE 从 crush_rule_2 设置为 crush_rule_3,此时所有PG状态会变为 active+remapped,发生的另一个动作是,原先四副本的PG的主副本是在 old_tree 上的某一个OSD上的,现在这个PG的主副本变为 new_tree下的选出的第一个副本,也就是发生了主副本的切换。比如原先 PG 1.0 => [osd.a, osd.b, osd.c, osd.d] 在这步骤之后会变成 PG 1.0 => [osd.c, osd.d]。原先的第三副本也就是new_tree下的第一副本升级为主副本。 • 将所有pool的副本数设置为2,此时PG状态会很快变为 active+clean,然后在OSD层开始删除 old_tree 下的所有数据。此时数据已经全部迁移到新的OSD上。
$ U/ T! |' ~6 k j5 e 迁移指令1.初始化新的OSD一定要记住:在部署目录下的ceph.conf内添加 osd_crush_update_on_start =false,再开始部署新的OSD,并且一定要检查新节点配置,包括但不限于: yum 源,免秘钥配置,ceph的版本,主机名,防火墙,selinux,ntp,ntp,ntp,重要的时间对齐说三遍! 部署新的OSD: ### 前往部署目录
) C! ]! l, X; \: V* x: kcd /root/cluster
$ j1 J) |+ F) h' }echo "osd_crush_update_on_start = false " >> ceph.conf & F- V4 I- h5 j2 i5 p4 l. N2 E
ceph-deploy --overwrite-conf osd prepare new_mon_1:sdb new_mon_2:sdb new_mon_3:sdb --zap-disk1 k1 f; b3 H8 q. j t
ceph-deploy --overwrite-conf osd activate new_mon_1:sdb1 new_mon_2:sdb1 new_mon_3:sdb1添加完这三个OSD后,集群总共有六个OSD,此时不会有数据迁移。结构如下: . T6 A' K8 {8 U( h" \9 e
[root@ceph ~]# ceph osd tree9 W7 n7 ^# P$ f; w" n( v- X
ID WEIGHT TYPE NAME UP/DOWN REWEIGHT PRIMARY-AFFINITY 1 V1 Y- A! u. D: J p3 ^: ~
-1 0.75000 root default
) K* x! K$ \0 E, x9 O/ Y7 _-2 0.25000 host ceph 0 K; q9 w$ h- c+ `" n8 L0 r
0 0.25000 osd.0 up 1.00000 1.00000 - y4 u" ^ F' {
-3 0.25000 host con 0 R( ~( I4 C: L
1 0.25000 osd.1 up 1.00000 1.00000
+ ?: ~0 @& c1 q5 |1 v1 Y-4 0.25000 host com
( R- z6 q! ]; V% b 2 0.25000 osd.2 up 1.00000 1.00000 ( k, ~" m, N! d3 A9 M2 Z
3 0 osd.3 up 1.00000 1.00000
+ u4 Z9 }) j" B9 E1 o 4 0 osd.4 up 1.00000 1.00000 . z+ Y1 o: z5 j# M( R a
5 0 osd.5 up 1.00000 1.00000构建新的new_root根节点,并将这三个新的OSD加入到新的根节点下(注意OSD和主机的物理对应关系): $ v: G/ O5 n0 t" C& S
ceph osd crush add-bucket new_root root+ G7 f2 ^: Y& x4 x* q" H
ceph osd crush add-bucket new_mon_1 host: C5 B. ^3 \: I' k6 ]1 l h
ceph osd crush add-bucket new_mon_2 host
+ I* k& N9 I- Y% e6 @ M- zceph osd crush add-bucket new_mon_3 host
( B3 M: t& m9 v( T3 k6 yceph osd crush move new_mon_1 root=new_root5 b9 s7 C7 t+ R! `1 }; ^) D& @
ceph osd crush move new_mon_2 root=new_root
1 E/ m R+ ]2 g. y' N2 aceph osd crush move new_mon_3 root=new_root
@: t1 \" ^5 tceph osd crush add osd.3 0.25 host=new_mon_19 r' ~+ R7 F% @5 M7 N% C3 n
ceph osd crush add osd.4 0.25 host=new_mon_2, i, ^( M1 a. R/ f% e8 {( S
ceph osd crush add osd.5 0.25 host=new_mon_3此时,新的 TREE 结构如下: [root@ceph ~]# ceph osd tree9 Y' j* T7 p$ e8 r
ID WEIGHT TYPE NAME UP/DOWN REWEIGHT PRIMARY-AFFINITY % J$ Y5 r8 h! w+ ?2 B/ p- l
-5 0.75000 root new_root
, J. [" q3 q: Q3 @% N-6 0.25000 host new_mon_1 4 d, X) R2 U; L
3 0.25000 osd.3 up 1.00000 1.00000
- a4 P5 M: @/ A7 W/ W-7 0.25000 host new_mon_2 5 h% q, N8 ~, z4 z4 K8 h# a
4 0.25000 osd.4 up 1.00000 1.00000
8 `2 A4 t; C- s-8 0.25000 host new_mon_3
# t+ M) e* b4 r4 r w 5 0.25000 osd.5 up 1.00000 1.00000 & S4 C9 b, p* f* g1 Z
-1 0.75000 root default
& Q. a. h, j% Q Y; i) m+ }1 O-2 0.25000 host ceph 6 j. ~; Z S0 i4 h
0 0.25000 osd.0 up 1.00000 1.00000 . e r0 I& K% N) p
-3 0.25000 host con
2 Y ]& v, C6 |' A1 [8 p 1 0.25000 osd.1 up 1.00000 1.00000
7 W2 v! {; R' Q& V; v-4 0.25000 host com
0 M! ?0 A' k, P& Y! q6 Y 2 0.25000 osd.2 up 1.00000 1.000001 ^1 c" C; W9 N9 g `" L8 E$ R
2 r" U& O1 P& Y$ F
; C9 q# X& g2 I0 {) n# o* S) Q5 @( a 2.编辑 CRUSH MAP
# d2 s2 d D m! F- w l导出CRUSH MAP,并编辑添加三条新的 CRUSH RULE: ### 导出CRUSH MAP
- R2 N' W2 d( c1 H3 w& I8 {# hceph osd getcrushmap -o map
' |& P B6 T6 Ucrushtool -d map -o map.txt
+ q5 V0 j. @1 b% G6 P1 E! l### 在map.txt 最后添加以下内容
8 [' E: s {7 J! u1 Rvim map.txt
) B0 n/ n* B0 |! O% i$ U4 F
7 U7 V7 O2 }& n2 h) ]# rules9 ~* x' i8 O3 f% X
rule replicated_ruleset {
3 ?* N4 C; g" W4 w' q0 m! f ruleset 0
8 w4 ^+ ^7 t: C% D9 \% J; t+ Q type replicated" H* _. z+ L: A9 j8 v1 q# o' x
min_size 1# k8 [. |; `4 m
max_size 10
1 j( z7 r2 b4 i1 _. W, n% j step take default
; x# w3 G7 W! b' R8 o( W* Q step chooseleaf firstn 0 type host6 M3 j+ y- a$ C' E8 ^4 @
step emit
2 V+ d) f) m# D) `- ?}7 [0 h$ {; b9 }
>>>>>>>>>>>>>> 添加开始 >>>>>>>>>>
% D' b1 \# W0 y: ^4 c5 qrule replicated_ruleset1 {: J% v+ }- v, E5 V) h. m8 T9 p
ruleset 1- u) U9 a& M3 [$ _+ K. K% u
type replicated
* ^( P7 v# X/ L% G4 H1 r/ j- j min_size 1
- A! G% v' [$ u. _5 g2 z max_size 10
) u3 U6 l0 |* i1 t4 l7 o step take default2 L4 A' o* c- h% E* `: {
step chooseleaf firstn 2 type host
# Z# @4 q! }3 P8 ?7 [# M, [8 { step emit# q( M& T- P% h D$ i8 m z" O
}
& c L* E9 C J9 b2 Orule replicated_ruleset2 {6 e$ C/ f& v" g6 V5 U2 {9 i8 \
ruleset 21 f+ N9 Y4 {* N% X5 a2 _' t
type replicated5 c) t b2 j1 l1 ]* f( W& i T
min_size 1
) I! a/ j, g# E4 g max_size 10
! t! P* D( V9 e3 ?8 ? step take default
0 H* K1 |- V+ M step chooseleaf firstn 2 type host9 P! D; p2 @% f& s& n3 t6 M
step emit& k- x$ Y+ W; l/ k
step take new_root
& G( {9 I# B/ R, _4 V& T. I step chooseleaf firstn 2 type host" f5 p; X* d+ D3 Z% c; J' c8 m
step emit& C2 ], B5 ^9 k3 r, Z; q% l" l5 O) g/ D5 V
}9 V/ c3 k0 N1 o! u- C
rule replicated_ruleset3 {" ?, o* P/ Y+ O1 O7 M/ v
ruleset 3
! [9 Y) i# W: V$ q) M/ Y type replicated
6 A7 G) J: r5 W3 P min_size 1
& ]% @8 a5 a7 I" }& v max_size 107 V' `% u+ E Y7 g/ T! {
step take new_root1 v k7 s. J: J) C0 d
step chooseleaf firstn 2 type host
4 ]! n* E! S+ U step emit, D( m0 h" L! z- @' M( w* w! c
}
, [: b5 z$ E: v8 m( K$ @<<<<<<<<<<<< 添加结束 <<<<<<<<<<<<9 f! u% |9 V7 @7 M& ?3 |; C
# end crush map0 |4 |6 T" [8 q; S- H
0 X% m$ v) B, w6 M2 J( E1 Y8 Z
### 编译 CRUSH MAP,并注入到集群中. z* O2 Q" O, E1 ^
6 Z+ l/ c' @: Y9 c$ ]1 s0 `
crushtool -c map.txt -o map.bin
6 Q! Y, G+ _. nceph osd setcrushmap -i map.bin
* y, ]+ Q1 W q- ?/ h
% g2 h' @! }# T/ O# k7 R3. 开始迁移数据
, B, K; f4 a6 I1 ?9 u到目前为止的所有操作均不会发生数据迁移,对线上业务也就没有影响,下面我们要开始通过修改 POOL 的 CRUSH RULESET 和 副本数来实现数据的整体迁移(这里我们取 volumes 池来介绍): ###确认当前 volumes 池使用的是 crush_ruleset 08 s* S6 P( b+ O& D) f
[root@ceph cluster]# ceph osd pool get volumes crush_ruleset
2 b6 p1 P2 k3 X2 X4 i7 ~7 y( f* Tcrush_ruleset: 0: c+ o* T/ G9 G! g( T; p* p
) q! p5 `1 t$ A; I
### 将 volumes 池的 crush_ruleset 设置为 1,设置完后,集群一切正常,PG均为active+clean
& x h4 W1 m- L3 [/ @[root@ceph cluster]# ceph osd pool set volumes crush_ruleset 1
! N# N/ O8 d1 ?% `4 M5 vset pool 1 crush_ruleset to 1" k* c& p$ G, S& U6 u$ y) a
6 x2 R) t; L, d5 u
### 将 volumes 池的 副本数设置为4, 设置完后,PG经过短暂 Peer,变为 active+undersized+degraded( {# J# k& ]# l# I
[root@ceph cluster]# ceph osd pool set volumes size 4% J( r: |0 S" A o
set pool 1 size to 4
- s2 n8 _& m0 {9 H4 d1 F4 d7 p
' A/ R) y$ ?, S[root@ceph cluster]# ceph -s
" |7 V. ~4 V1 h0 X5 Q5 Z8 R cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d
& R2 x" u% A. `" O' I1 w! U7 k* t* y health HEALTH_WARN% ?3 S3 n" f+ d' T* u
256 pgs degraded. _: s" x6 w! J/ X* P+ K
256 pgs undersized. z% h* N1 d! e4 S
recovery 183984/368006 objects degraded (49.995%)
# g) ]3 C: z( B3 _; E monmap e47: 3 mons at {ceph=192.168.100.112:6789/0,com=192.168.100.111:6789/0,con=192.168.100.110:6789/0}5 k! }; M9 [ Q, |
election epoch 182, quorum 0,1,2 con,com,ceph2 M$ ], y/ j. E5 x- [1 v& I7 ~
osdmap e106: 6 osds: 6 up, 6 in
' ^ M; H6 D. C/ ]" g1 k- n flags sortbitwise,require_jewel_osds
, ?4 Q% s1 H- v. q, m pgmap v23391: 768 pgs, 3 pools, 403 MB data, 92011 objects' t9 N! b9 I2 `1 d5 O5 {! q
1523 MB used, 1532 GB / 1533 GB avail( B$ t8 t4 v; S: c+ r6 e
183984/368006 objects degraded (49.995%)* @3 S0 c6 v9 A$ m# N
512 active+clean$ K R1 m! @" @5 L
256 active+undersized+degraded# u, L* Q6 v! e/ |
1 Y: \4 J3 J d8 B
" U1 u+ O5 Y& ]2 s1 z
[root@ceph cluster]# ceph osd pool set volumes crush_ruleset 2
8 ~* p, d4 e' m6 Uset pool 1 crush_ruleset to 2
& j3 d& p6 l5 }: n' |8 L$ U5 w1 V: P! u0 j6 A* l; h9 ^$ _) a/ W/ f
### 等到 volumes 池的所有PG均变为 active+clean 后,将 volumes 池的 crush_ruleset 设置为3, 此步骤后,volumes 池的 PG状态变为 active+remapped。但是不影响集群IO。; B v- l* n8 Q; u
5 q4 T0 {, Z+ V, b* v
[root@ceph cluster]# ceph osd pool set volumes crush_ruleset 2" d: i* p2 C% W3 ^ f% t
set pool 1 crush_ruleset to 2( [2 G6 e, g8 N2 k, S \9 X% n4 g
2 B) o8 Q% P1 W: ?) J/ ~set pool 1 crush_ruleset to 3
6 m/ ?8 b, r& q* G L) g1 j' b0 Z" t6 y
### 此时,将 volumes 池的 副本数 设置为2 ,此时 PG 状态经过 peer 之后,很快变为 active+clean ,volumes 池的两副本均落在新的节点下,并且后台在自行删除之前旧节点上的数据。
# b7 \: v/ d6 o6 f" T9 g! C' Y) A& H( K4 w' ~7 P, C
[root@ceph cluster]# ceph osd pool set volumes size 2$ n8 F$ w# C0 ~% a) L
$ I9 y+ }1 u5 ]7 J注意事项
) S' X/ r+ o2 \' M# I$ V0 g( d0 G
6 W) j0 R+ s1 D, o$ D/ R8 F
由于本次数据迁移是在生产环境上执行的,所以没有直接执行将数据从旧节点直接 mv到新节点,而是选择了执行步骤较为复杂的上面的方案,先 scp 到新节点,再 rm掉旧节点的数据。并且每一个步骤都是可以快速回退到上一步的状态的,对于生产环境的操作,是比较友好的。在本次方案测试过程中,遇到了如下的一些问题,需要引起充分的注意: Ceph 版本不一致: 由于旧的节点的 Ceph 版本为 0.94.5 ,而新节点安装了较新版本的 10.2.7, 在副本 2=>4 的过程中Peer是正常的,而将池的crush_ruleset 设置为3 ,也就是将新节点的 PG 升级为主副本后,PG由新节点向旧节点发生Peer,此时会一直卡住,PG始终卡在了 remapped + peering,导致该 pool 无法IO。在将新旧节点 Ceph 版本一致后(旧节点升级,新节点降级),此现象得以消除。 为了确保此现象不会在实际操作中发生,应该在变更之前,新建一个测试pool,对其写入部分数据,再执行所有数据迁移指令,查看此过程是否顺畅,确认无误后,再对生产pool进行操作! 新节点 OSD 的重启问题: 如果没有添加了osd_crush_update_on_start 这个配置参数,那么当新节点的OSD重启后,会自动添加到默认的 root=default 下,然后立刻产生数据迁移,因此需要添加这个参数,保证OSD始终位于我们人为指定的节点下,并不受重启影响。这是个基本的Ceph运维常识,但是一旦遗忘了,可能造成较为严重的影响。更不用说防火墙时钟这些配置了。 集群性能降低:总体来说,在副本从2克隆为4这段时间(约2-3天,取决于集群数据量)内,集群的实际IO表现降低到变更前的 25%->80% 左右,时间越往后表现越接近变更前,这虽然不会导致客户端的IO阻塞,但从客户反馈来看,可以感知到较为明显的卡顿。因此克隆时间应该选择业务量较低的节假日等。 新节点IP不cluster_network范围内:这个比较好解决,只需要增大部署目录ceph.conf内的cluster_network 或者 public_network的掩码范围即可,不需要修改旧节点的,当然网络还是要通的。 变更的回退:对于生产环境来说,尽管执行步骤几乎是严谨不会出错的,但是难免会遇到意外情况,就比如上面的版本不一致导致的 peer 卡住现象。因此我们需要制定完善的回退步骤,在意外发生的时候,能够快速将环境回退到上一步集群正常的状况,而不是在意外发生时惊出一身冷汗,双手颤抖得敲指令。。。所以,下面的表格给出了这个变更操作的每一步的回退步骤:
; u7 G$ X# t ^ I% J
[td]| 变更步骤 | 实际影响 | 回退指令 | 回退影响 | | ceph osd pool set volumes crush_ruleset 1 | 无 | ceph osd pool set volumes crush_ruleset 0 | 无 | | ceph osd pool set volumes size 4 | PG由active+clean,变为 active+undersized+degraded | ceph osd pool set volumes size 2 | PG很快恢复active+clean | | ceph osd pool set volumes crush_ruleset 2 | PG 开始 backfill,需要较长时间 | ceph osd pool set volumes crush_ruleset 1 | PG很快恢复到active+undersized+degraded,并删除在新节点生成的数据。 | | ceph osd pool set volumes crush_ruleset 3 | PG 很快变为 active+remapped。 | ceph osd pool set volumes crush_ruleset2 | PG很快恢复到 active+clean | | ceph osd pool set volumes size 2 | PG 很快变为 active+clean,并且后台删除旧节点数据。 | ceph osd pool set volumes size 3 | PG 变为active+remapped。 |
0 O8 a! v/ I, I/ K1 a3 ~' w
为何不直接迁移数据到新节点?总体来看数据迁移过程,最耗时的地方是数据从两副本变为四副本的过程,其余过程是短暂且可以快速回退的,而如果直接将池的 crush_ruleset 设置为 3 ,数据开始从旧节点直接backfill到新节点上,其实这么做也是可以的,但是这里我们就会遇到一个问题,一旦数据复制了一天或者一段时间后,集群出现了问题,比如性能骤降,要求必须回退到操作之前的状态,此时已经有部分PG完成了迁移,也就是说旧节点上的两副本已经删除了,那么回退到上一步后,还会发生数据从新节点向旧节点的复制,那么之前复制了多久的数据,可能就需要多久来恢复旧节点上删除的数据,这很不友好。而用了我们的方法来复制数据的话,不会存在这个问题,因为这里的方法在最后一步将池的副本设置为2之前,旧节点上的数据始终都是在的,并且不会发生任何迁移,我们可以在任意意外情况下,通过几条指令将集群恢复到变更之前的状态。
, a. z# A: p2 ~6 |6 U* M MON的迁移原理介绍相比于 OSD 的数据迁移,MON 的迁移比较省时省力一些,步骤相对简单,但是里面涉及的原理比较复杂,操作也需要细心又细心。 首先,我们的环境为典型的 Openstack+Ceph的环境,其中 Openstack 的三个组件: Nova/Cinder/Glance 均已经对接到了Ceph集群中,也就是说虚机系统盘,云硬盘,镜像都保存在Ceph中。而这三个客户端调用Ceph的方式不太一样: 我们需要知道的是,当一个 Client需要连接 Ceph 集群时,它首先通过自己的用户名和秘钥(client.cinder/client.nova...) 来连接到 /etc/ceph/ceph.conf配置文件指定IP的MON,认证成功后,可以获取集群的很多MAP( monmap,osdmap,crushmap...),通过这些 MAP,即可向 Ceph 集群读取数据。 对于一个虚机进程(qemu-kvm)来说,虚机启动之初,它即获取到了集群的 monmap, 而当所连接 MON 的 IP 变化时,比如这个 MON 挂掉时,它便会尝试连接 monmap 里面的其他 IP 的 MON,如果每个MON都挂了,那么这个 Client 就不能连接上集群获取最新的 monmap,osdmap等。下面我们以一个 pid为3171的 qemu-kvm 进程来演示这一过程: [root@con ~(keystone_admin)]# ps -ef|grep kvm % H2 C7 ?' @# @+ {6 W# ~
qemu 3171 1 17 14:32 ? 00:31:08 /usr/libexec/qemu-kvm -name guest=instance-0000000b.........( U/ p9 S4 ~% ]& L: \( w4 k* x+ @
, ~9 b* g0 x$ [
[root@con ~(keystone_admin)]# netstat -tnp |grep 3171|grep 6789/ Q7 g0 A$ c) I. {
tcp 0 0 192.168.100.110:59926 192.168.10.12:6789 ESTABLISHED 3171/qemu-kvm 可以看到,这个进程连接着IP为 192.168.10.12 的 MON,而我们手动将这个IP的 MON 停掉,则会发现这个进程又连接到了剩余两个IP的MON之一上: [root@con ~(keystone_admin)]# ssh 192.168.10.12 systemctl stop ceph-mon.target" a' t8 ~ I' V$ L
$ S: J; R/ d6 ]) \[root@con ~(keystone_admin)]# netstat -tnp |grep 3171|grep 6789. V9 I6 _1 B: Z: |4 i4 |: [+ u! X
tcp 0 0 192.168.10.10:48792 192.168.10.11:6789 ESTABLISHED 3171/qemu-kvm 因此,如果我们每次都增加一个MON,再删除一个MON,那么在删除一个MON之后,之前连接到这个MON上的 Client 会自动连接到一个其他MON,并且再获取最新的monmap。那么我们增删过程就是: 原先有三个MON: con, com, ceph 增加 new_mon_1,变为四个: con, com, ceph, new_mon_1 删除con,变为三个:com, ceph, new_mon_1 增加 new_mon_2,变为四个: com, ceph, new_mon_1, new_mon_2 删除com,变为三个:ceph, new_mon_1, new_mon_2 增加 new_mon_3,变为四个: ceph, new_mon_1, new_mon_2, new_mon_3 删除con,变为三个:new_mon_1, new_mon_2, new_mon_3 ; E/ `# C$ a. s8 G% d/ R# E
Nova 侧的一个问题在实际操作中,发现了一个问题,会导致虚机无法重启等问题。 当虚机挂载一个云硬盘时,Nova 会将挂载这个云盘时所连接的MON IP 写入到数据库中,而在修改完MON的IP后,新的MON IP不会被更新到数据库中,而虚机启动时会加载 XML 文件,这个文件由数据库对应字段生成,由于没有更新 MON IP,所以 qemu-kvm 进程在启动时,会尝试向旧的MON IP发起连接请求,当然,旧MON已经删除,导致连接不上而卡住,最终致使虚机进程启动了,但是虚机状态始终不能更新为 RUNNING。 可以通过打开客户端的ceph.conf 内的 debug_rbd=20/20,查看qemu-kvm进程调用librbd时生成的log发现进程在启动时始终尝试连接旧的MON IP。 这里,我们只能手动修改数据库中记录的IP地址来确保虚机重启后能够连接上新的MON,需要注意的是,仅仅修改虚机XML文件是无法生效的,因为会被数据库内的字段覆盖而连上旧MON: ### 具体字段为:
2 X! O; x- l6 D: B, Lmysql =>
N5 d$ f' H6 c3 e( t. Pnova => block_device_mapping => connection_info8 ~* }; x+ {& k6 w/ p! D
7 z" D4 ~' |: n# E) h*************************** 23. row ***************************7 ~% L7 S8 Q8 @2 g; ^
created_at: 2018-03-19 08:50:59% ]9 a9 r2 i5 Z1 u, I+ ~
updated_at: 2018-03-26 06:32:06
7 P# K T0 T8 `4 ]* w deleted_at: 2018-03-26 09:20:02
8 a9 p% S9 l& ^/ h id: 29
* e" D7 i' Q' [% g& ` device_name: /dev/vdb
7 S" S2 [% c, \3 {' ~( X( t, ]/ b. }delete_on_termination: 0
! J8 m/ X" I+ ^) ?/ _4 P9 x snapshot_id: NULL
- A, G7 c8 A0 v3 E volume_id: 39c76d96-0f95-490c-b7db-b3da6d17331b. v# S1 J a1 q3 }
volume_size: NULL9 R' @; Z& U' E% H
no_device: NULL, R9 ^% H+ a* t' }- j/ p8 C
connection_info: {"driver_volume_type": "rbd", "serial": "39c76d96-0f95-490c-b7db-b3da6d17331b", "data": {"secret_type": "ceph", "name": "volumes/volume-39c76d96-0f95-490c-b7db-b3da6d17331b", "secret_uuid": "0668cc5e-7145-4b27-8c83-6c28e1353e83", "qos_specs": null, "hosts": ["192.168.10.10", "192.168.10.11", "192.168.10.12"], "auth_enabled": true, "access_mode": "rw", "auth_username": "cinder", "ports": ["6789", "6789", "6789"]}}* ^1 f; t" v5 l: \
instance_uuid: 4f52191f-9645-448f-977b-80ca515387f71 j7 h% i7 H, o! X6 T
deleted: 29
e2 Q( S1 \; ]5 Y+ A source_type: volume# k$ O: j9 e- y- |
destination_type: volume
' g' \, Y2 ^: _- f/ E guest_format: NULL
9 u( b$ |0 w3 d device_type: disk* j* G0 o8 \* @/ n4 }; c3 ^4 b% U
disk_bus: virtio
0 T' k1 O$ x( t boot_index: NULL
7 t. g' t# ^. @# d image_id: NULL迁移指令这里,我们使用 ceph-deploy 来增删 MON 节点,主要是为了操作的简洁和安全性着想。 首先,我们需要将新的三个MON的IP地址加入到所有节点的/etc/ceph/ceph.conf的mon_host 字段中。保证此时mon_host内是六个MON的IP地址。 ### 添加 new_mon_1 E% g+ c7 C$ A; B0 [
[root@ceph cluster]# ceph-deploy mon add new_mon_1. y6 l" g/ P" t3 }* ^5 ^
[root@ceph cluster]# ceph -s
) b+ r8 I& q1 U2 Q% f/ @8 l cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d
4 E7 P/ X6 v1 t6 U3 o/ P/ H health HEALTH_OK
- i; v3 r7 H8 h monmap e48: 4 mons at {ceph=192.168.10.12:6789/0,com=192.168.10.11:6789/0,con=192.168.10.10:6789/0,new_mon_1=192.168.10.13:6789/0}3 |5 @1 P& g- z" t$ o! [" ?: Z
; ^3 P# g2 b" s5 y( [### 删除 con 删除完后,建议等待1-3min再添加新的MON,使得qemu-kvm进程可以建立新的socket链接。
0 V; v& v3 U8 J1 Y3 b[root@ceph cluster]# ceph-deploy mon destroy con* ~& [6 K. a1 ]9 U
[root@ceph cluster]# ceph -s& F- n Y- Q- {% Y1 U4 \. A4 x W
cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d W2 L) g) K& g7 z% }: W9 s5 @
health HEALTH_OK
' j g; u1 W( F+ h+ r8 c4 X monmap e49: 3 mons at {ceph=192.168.10.12:6789/0,com=192.168.10.11:6789/0,new_mon_1=192.168.10.13:6789/0}
! R3 i, ?. r( N" J' y9 B& e . G" U" v$ i# H) x1 S
### 依次添加 new_mon_2 ,删除 com,添加 new_mon_3 , 删除 ceph:- h3 B: B# d7 ?" o: b1 o; S
[root@ceph cluster]# ceph-deploy mon add new_mon_2
& P& a4 ^/ k5 s4 A9 E/ Q[root@ceph cluster]# ceph-deploy mon destroy com o. p% o: ^3 c; c) c. |
### 等待1-3min,
1 O( b6 l; g0 j! l7 K[root@ceph cluster]# ceph-deploy mon add new_mon_36 J" Q" W- H. ]9 C7 E% D
[root@ceph cluster]# ceph-deploy mon destroy ceph7 Z$ C8 ?* O6 z6 r" f
[7 |8 L7 B, x, q; p. b
[root@con ~(keystone_admin)]# ceph -s" L3 s5 [/ _8 I. |$ p7 C
cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d
3 L; X' K# [( G( C9 ? health HEALTH_OK
3 J {2 J! a) k$ u3 D, ]6 H* S monmap e53: 3 mons at {new_mon_1=192.168.10.13:6789/0,new_mon_2=192.168.10.14:6789/0,new_mon_3=192.168.10.15:6789/0}此时,将所有节点/etc/ceph/ceph.conf内的mon_host字段中原先的MON删除,只保留三个新的MON IP地址。 ) `( g7 r' x+ V- ` S# l# s* S
Glance & Cinder & Nova 服务重启无需重启 Glance 服务。 需要重启 所有计算节点的 nova-compute 和 控制节点的 Cinder 服务,否则会导致虚机无法创建等问题。 ## 在计算节点0 z. i% C+ r& i- z/ S# ~$ p
openstack-service restart nova-compute" ~8 o6 d! V+ z7 n- W N" k
## 在控制节点/ n* g1 b' A) a: C, [
openstack-service restart cinderNova由于 nova 不会更新之前的已经挂载的磁盘所连接的MON IP 信息,这会导致虚机在重启等动作时,尝试连接到旧的已经被摧毁的MON的地址,导致动作卡住,因此这里要单独改一下数据库内的MON IP 信息: 这里我们将 192.168.10.10/11/12 改为 192.168.10.13/14/15 mysql >> ~$ N4 ]- m: F% a' I: U- j0 [4 o
0 h6 [' q3 j, Z/ i) s* u" duse nova;7 ]7 }' A( k1 U( _: A4 i0 p1 ~+ b
update block_device_mapping set connection_info=(replace(connection_info,'192.168.10.10','192.168.10.13'));
) ~& H) x7 c7 Y$ c$ aupdate block_device_mapping set connection_info=(replace(connection_info,'192.168.10.11','192.168.10.14'));$ s# X. u7 O& [
update block_device_mapping set connection_info=(replace(connection_info,'192.168.10.12','192.168.10.15'));7 F+ T1 s5 P' j5 r) I2 Z% c
9 j% q" r2 {: n
exit;更新完数据库后,这次数据迁移算是大功告成了。为何不需要重启虚机服务呢,这里再做一些简单的介绍: qemu-kvm 虚机进程是一个长连接的 Ceph Client,自虚机启动后,进程就和 Ceph 集群保持着连接,在我们更改 MON 后,这些 Client 会自动尝试重连 monmap 里面的其他MON,从而更新 monmap, 来获取最新的 MON 。修改 /etc/ceph/ceph.conf不会对虚机进程产生影响,除非虚机重启等,但是,虚机可以通过更新 monmap 的方式来感知集群MON的改变。 |