|
本文的环境均为:Openstack+Ceph 运行虚拟机的场景,即主要使用RBD,不包含RGW,MDS。虚机的系统盘(Nova),云硬盘(Cinder),镜像盘(Glance)的块均保存在共享存储Ceph中。 环境准备. G, y$ u- N. r7 F! z9 N( P
# S! h+ b& O. l# f$ d, c/ Y% U1 v
本文环境为 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 |
7 Q2 W1 Q) G& o8 S# B
在集群整体迁移完后,各个组件分布如下,也就是说,将运行于 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 |
; U7 @0 Q* w' ^" V在迁移之前,我们创建一个虚机,一个云盘,上传一个镜像,虚机此时正常运行,并将这这块云盘挂载到虚机上: [root@con ~(keystone_admin)]# nova list & W5 x5 X) u: A6 ]' d
+--------------------------------------+---------+--------+------------+-------------+-------------------------+
/ l b, l, G8 G/ x- a) m0 c3 n| ID | Name | Status | Task State | Power State | Networks |" r4 Z7 k3 D" q& B7 \7 U3 m5 x
+--------------------------------------+---------+--------+------------+-------------+-------------------------+4 n$ C! {% t& L
| f52191f-9645-448f-977b-80ca515387f7 | vm1-test | ACTIVE | - | Running | provider=192.168.8.111 |
" e H- G0 u# L4 M: w+--------------------------------------+---------+--------+------------+-------------+-------------------------+
6 k+ O4 w8 \6 Z' B f6 A: Q[root@con ~(keystone_admin)]# cinder list
; Q" r1 N$ g2 @. l5 N9 U/ N( _+--------------------------------------+--------+--------------+------+-------------+----------+--------------------------------------+! e8 {! n+ Z1 [6 M, D! M k$ l
| ID | Status | Display Name | Size | Volume Type | Bootable | Attached to |
1 K! r3 o2 Y! O" K% P+--------------------------------------+--------+--------------+------+-------------+----------+--------------------------------------+3 W# U/ d: b7 {* i
| 39c76d96-0f95-490c-b7db-b3da6d17331 | in-use | cinder-rbd | 1 | None | false | f52191f-9645-448f-977b-80ca515387f7 |" b3 S$ ^! R, M, R( g' ^, {
+--------------------------------------+--------+--------------+------+-------------+----------+--------------------------------------+, O: ?; k7 V0 F% g7 F9 U9 @6 x- H
[root@con ~(keystone_admin)]# ip netns exec `ip netns` ssh cirros@192.168.8.1115 B. F! u3 O. X2 w' R4 A
cirros@192.168.8.111's password:
! D8 @. G" L# v$ lsblk$ y `9 t7 u9 q; O2 I# J% c
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT& |* i( N6 k0 l+ f1 E0 C- u
vda 253:0 0 1G 0 disk , x6 T# G3 Z& c* ]' C9 L+ @. V# K/ V: e
`-vda1 253:1 0 1011.9M 0 part /: \ N' u6 K$ }3 [
vdb 253:16 0 1G 0 disk
& L$ A: Q! I% a& y& B$ Ceph 集群状态: [root@ceph cluster]# ceph -s
3 U) G: {. r& Q# R" z cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d
; \. u& v1 x G6 M4 y8 U health HEALTH_OK
. f ~) _4 X1 X$ J 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}
F) t: z+ \; T8 }9 H election epoch 174, quorum 0,1,2 con,com,ceph$ }0 S1 k$ H s7 _
osdmap e57: 3 osds: 3 up, 3 in& X9 r: D( ~! `; X
flags sortbitwise,require_jewel_osds7 H5 e$ N V, a: l5 V1 r3 ~6 Y
pgmap v6577: 768 pgs, 3 pools, 45659 kB data, 23 objects: a! }6 @/ Z" @' F/ x S% N
178 MB used, 766 GB / 766 GB avail
3 Z7 n8 M- g( T0 B# J, _ 768 active+clean
1 c& ]6 U* e5 r$ V- c% ~/ ~2 t! w
8 C2 n7 _- H$ g! L% Q9 [[root@ceph cluster]# ceph osd tree# e! r; `5 G2 X" g6 K! {
ID WEIGHT TYPE NAME UP/DOWN REWEIGHT PRIMARY-AFFINITY
8 O/ v' L( ~$ Q: g, _9 X3 x" w-1 0.74876 root default + i& l; x1 O; h5 L) k
-2 0.24959 host ceph 0 E U6 V: u! v/ ?
0 0.24959 osd.0 up 1.00000 1.00000 5 T& K) D$ N1 u5 a7 P7 t, V' I
-3 0.24959 host con
( n2 F9 |; m9 u. J' r1 e 1 0.24959 osd.1 up 1.00000 1.00000 / a0 t" o5 u6 w
-4 0.24959 host com / T. l. ?8 V' `, {8 ?( |
2 0.24959 osd.2 up 1.00000 1.00000
9 h8 B) N& h4 V+ o% S s0 L& t; [# k; e& v) I! T
[root@ceph cluster]# ceph osd pool ls detail W% c; b4 `: G& `: _9 F; a6 \
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 0$ h4 t' W; Q4 D) q
removed_snaps [1~3]
1 `4 g9 Q. N6 [4 d( `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% R; ? g; \/ ]7 k& m, ]
removed_snaps [1~3]0 R; e$ h# r) w( _, t6 v
pool 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 08 S5 j- _# a+ D& j3 p8 K/ S# c
removed_snaps [1~9]6 L: S: Q% h! C o9 L9 ^" p
! B% K; D* K9 lOSD的数据迁移7 _. \' q2 j3 S- R3 d3 _
8 x6 Z5 g7 Q$ D3 D4 V y) ~
% } h9 F: X( a, E, J本次迁移主要分为两个组件的迁移,即 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上。 5 l/ V- e$ W. H% f: t
迁移思路 ^# [1 v0 v3 S. Q5 g5 R* f
( g- V- C7 S @3 _; m7 V$ R
• 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上。
) x; n8 x- g6 k8 Q 迁移指令1.初始化新的OSD一定要记住:在部署目录下的ceph.conf内添加 osd_crush_update_on_start =false,再开始部署新的OSD,并且一定要检查新节点配置,包括但不限于: yum 源,免秘钥配置,ceph的版本,主机名,防火墙,selinux,ntp,ntp,ntp,重要的时间对齐说三遍! 部署新的OSD: ### 前往部署目录
- \5 {# E5 h% r) F* bcd /root/cluster
! y* S4 \3 e4 hecho "osd_crush_update_on_start = false " >> ceph.conf 1 `# n% {1 A2 | w5 z: m3 W5 o
ceph-deploy --overwrite-conf osd prepare new_mon_1:sdb new_mon_2:sdb new_mon_3:sdb --zap-disk
- L F6 W- E* N# N) g! i' |ceph-deploy --overwrite-conf osd activate new_mon_1:sdb1 new_mon_2:sdb1 new_mon_3:sdb1添加完这三个OSD后,集群总共有六个OSD,此时不会有数据迁移。结构如下: - ~' m8 n8 a& y
[root@ceph ~]# ceph osd tree( [/ g9 _( _! Q$ s$ p8 c( ^
ID WEIGHT TYPE NAME UP/DOWN REWEIGHT PRIMARY-AFFINITY , s& k& [( E; }# A
-1 0.75000 root default
( `* X9 g: `7 v- b: G; g) n* h) ~7 n-2 0.25000 host ceph 9 h4 t1 q% A4 ]6 X+ z
0 0.25000 osd.0 up 1.00000 1.00000 1 K# v0 x' o/ u# y3 K _- |
-3 0.25000 host con - Y' e( U* G; W4 J4 x9 Y
1 0.25000 osd.1 up 1.00000 1.00000 ; I. ~, ]8 M- Y8 A# O1 } N
-4 0.25000 host com
0 a3 ]8 G3 S; z 2 0.25000 osd.2 up 1.00000 1.00000
4 w) T( y7 D1 z7 H* c 3 0 osd.3 up 1.00000 1.00000 7 ~6 \0 S8 G9 r" D. \& ]: J3 j5 G
4 0 osd.4 up 1.00000 1.00000
X# d4 I1 j( _! k# K& x8 U! U 5 0 osd.5 up 1.00000 1.00000构建新的new_root根节点,并将这三个新的OSD加入到新的根节点下(注意OSD和主机的物理对应关系):
* S0 j8 V- I# F3 R6 ^* V; ]2 F ceph osd crush add-bucket new_root root5 J. Y, I- k/ `" e
ceph osd crush add-bucket new_mon_1 host
. D$ ?% A0 U. e: Bceph osd crush add-bucket new_mon_2 host
: m4 \2 Y4 q: H; v$ l7 a" |$ k: i1 Gceph osd crush add-bucket new_mon_3 host
9 ^* q$ d( K8 U. `/ }# q v0 k& xceph osd crush move new_mon_1 root=new_root. k+ v$ y+ ]. a0 K. o
ceph osd crush move new_mon_2 root=new_root3 x0 i1 b0 f8 ^; Q' H" A' E
ceph osd crush move new_mon_3 root=new_root
- c- x' \- C C: J9 fceph osd crush add osd.3 0.25 host=new_mon_1
+ Z! J3 a2 G& n, }5 \$ Hceph osd crush add osd.4 0.25 host=new_mon_2: f0 L* C. O) z( s. s
ceph osd crush add osd.5 0.25 host=new_mon_3此时,新的 TREE 结构如下: [root@ceph ~]# ceph osd tree B9 H0 ^1 w, b& j
ID WEIGHT TYPE NAME UP/DOWN REWEIGHT PRIMARY-AFFINITY
8 v+ ]* }. P( B" p( \: c-5 0.75000 root new_root
4 y% l! F3 w1 b-6 0.25000 host new_mon_1 $ j' h1 [4 p* u
3 0.25000 osd.3 up 1.00000 1.00000 ! O, S7 z' d! ]4 k
-7 0.25000 host new_mon_2
! r- [6 [! Z+ N ]7 i 4 0.25000 osd.4 up 1.00000 1.00000 2 b5 a+ N) U; `- h1 D; a
-8 0.25000 host new_mon_3 2 A( W& B2 B% K" L1 M" ~
5 0.25000 osd.5 up 1.00000 1.00000 * L6 |& _. T) e* V! `) j9 D$ @$ h
-1 0.75000 root default
% m" ?% J2 u6 p7 p" U$ b0 ^-2 0.25000 host ceph
' D& b5 L z+ D5 r( a 0 0.25000 osd.0 up 1.00000 1.00000
. k' |1 Z C4 p9 K0 f-3 0.25000 host con
+ i U5 v7 V* x# g( g 1 0.25000 osd.1 up 1.00000 1.00000
9 ^9 T, Q B$ F- ^# \. u) h7 b4 A-4 0.25000 host com / X$ N- Y l/ ^2 G% m" y6 v7 L+ ~
2 0.25000 osd.2 up 1.00000 1.00000% ~) _5 M2 Y( e
4 ^* }7 e0 [) E
; b0 \2 W- Y( M/ `0 T( R5 } 2.编辑 CRUSH MAP: S* z& A+ E8 r3 W4 r1 e& f
导出CRUSH MAP,并编辑添加三条新的 CRUSH RULE: ### 导出CRUSH MAP/ ^1 F5 A: h4 ^5 h
ceph osd getcrushmap -o map 2 S7 @' A& ~9 H% s) ] n, m! K# u
crushtool -d map -o map.txt
- G( g& ?. m. h% m### 在map.txt 最后添加以下内容
6 N) D& _# ?5 f# w. _, @9 M9 [! ^/ ]% `vim map.txt ) }* a+ C7 @5 b- U2 }+ Y
7 D1 f0 ?; R5 n# ?% D3 S! E# rules
4 b$ \. K5 T5 I+ ]6 p8 Krule replicated_ruleset {1 ]( M& K1 Y6 a6 o8 a& U' w
ruleset 0
r0 x+ L1 ]8 ~& j7 K( O type replicated
0 V% c1 o# j/ ?5 Q, u min_size 1
2 U" B' E- D1 g max_size 10
) d* B0 o) ]$ z, Q step take default
* c9 Z- U$ X& y* V: x4 i step chooseleaf firstn 0 type host3 P' E t) Y) ^4 a, t
step emit
9 c5 {+ f. ^! R) h}/ q k1 E2 _# A7 ]" F5 R
>>>>>>>>>>>>>> 添加开始 >>>>>>>>>>: M% v8 i4 v4 V: u/ {" J! R! a. r
rule replicated_ruleset1 {4 g, p3 p; o1 D; C& v$ T& F) H# C
ruleset 1
% G1 I; j, u5 A: F; l/ \ type replicated2 V' v9 ~! A; t$ j' P5 M4 }
min_size 1- m1 s/ a1 P1 ~0 P
max_size 10
" S4 c8 f# ^0 q3 S step take default5 y) G0 l' v2 R: Q- r
step chooseleaf firstn 2 type host4 j7 t& g4 r! a& l$ B7 |2 {
step emit5 z+ n. E! S! c9 }' s3 Z
}
* \% [* N, ?8 }0 L' k/ Krule replicated_ruleset2 {
1 C; a3 W1 w5 h ?- [, g ruleset 2
' l( {) q: j$ Y type replicated
0 @' k- w+ n5 x( n) R1 E min_size 1$ E3 @; A' H7 |6 _
max_size 10
" Y8 u- }8 R) G# t$ E step take default. v$ L( V5 O( V% d
step chooseleaf firstn 2 type host0 a1 R, u; d& S. \% a {
step emit
+ l- H4 ^# k" j3 m step take new_root% o$ V4 P9 }$ \1 H% C
step chooseleaf firstn 2 type host
' b2 D( y5 y7 R+ T7 n$ l& V3 ? step emit& O) _3 M7 r+ z: q# N3 w9 D& g
}! z, j9 ^4 u% s/ F6 U* B! A; T
rule replicated_ruleset3 {
. `5 _6 p' \( Z1 V6 F# n% \& L7 T0 N ruleset 3
/ p$ ^0 a! n# V type replicated
/ ]' m& A/ B5 [0 |* D" x, _$ S min_size 17 X5 `- J5 w( ]3 Y8 E0 \
max_size 10
" [3 k5 } U5 b1 d step take new_root
6 q$ M R2 h$ K e. Q step chooseleaf firstn 2 type host
7 P7 ~+ O5 F4 c step emit5 E1 F3 O0 K \. Z
}
1 o- S" S7 n: s: Q! X/ J<<<<<<<<<<<< 添加结束 <<<<<<<<<<<<( l: c5 {& c, W: l
# end crush map* o$ S8 {/ X N6 z" U! @& s7 I
( S5 b/ ]% j b### 编译 CRUSH MAP,并注入到集群中$ T' \9 ?& i' Y: N( O
! ?2 [# `& H( i7 s9 Q) \9 _
crushtool -c map.txt -o map.bin6 a- e* G8 c0 `1 m* ~
ceph osd setcrushmap -i map.bin [2 Q0 m% H7 a3 i/ d3 H1 M
* {# ]2 k, t0 T
3. 开始迁移数据
& ~+ u# f' y* f1 {5 ^% L- G到目前为止的所有操作均不会发生数据迁移,对线上业务也就没有影响,下面我们要开始通过修改 POOL 的 CRUSH RULESET 和 副本数来实现数据的整体迁移(这里我们取 volumes 池来介绍): ###确认当前 volumes 池使用的是 crush_ruleset 0' c& I' s2 U' u' a% p9 _
[root@ceph cluster]# ceph osd pool get volumes crush_ruleset 0 u1 K8 E# h9 D- G
crush_ruleset: 0
0 F2 w* p0 l! j# l. i. a
5 s4 C4 r T8 ]### 将 volumes 池的 crush_ruleset 设置为 1,设置完后,集群一切正常,PG均为active+clean
6 m* ]* C" B( A; `; }# H[root@ceph cluster]# ceph osd pool set volumes crush_ruleset 12 } t% r5 ^' z) R+ C1 X% `+ J
set pool 1 crush_ruleset to 13 v8 X) s+ u& u" p+ [9 ~8 x
/ w6 O9 i) H: o### 将 volumes 池的 副本数设置为4, 设置完后,PG经过短暂 Peer,变为 active+undersized+degraded
7 H1 t5 j" L" ]* f% K[root@ceph cluster]# ceph osd pool set volumes size 4
& Y( i$ p: r; j! w- P V9 Bset pool 1 size to 4
9 M, E1 j/ H$ r$ O7 A, M/ Z4 _# {2 ~
( k3 ]6 Q" p1 Z9 k( c4 x$ R7 {[root@ceph cluster]# ceph -s
; J+ F5 n, J# y2 c0 u9 N cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d
+ _3 O* _* O4 k/ X health HEALTH_WARN/ Y" W! ^9 Q- y: X0 g1 h0 \* @
256 pgs degraded& a- Q4 E: H/ k/ A
256 pgs undersized
- O5 ]1 p* `7 e* ?+ d M8 \ recovery 183984/368006 objects degraded (49.995%)& k( U( i; Y9 P1 Y u- h$ [
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}
9 K2 ]3 O/ L6 s+ I6 `0 | election epoch 182, quorum 0,1,2 con,com,ceph& ]2 O' Y8 S! {
osdmap e106: 6 osds: 6 up, 6 in
- {: U+ H$ r" u) J5 Y& p- M/ T flags sortbitwise,require_jewel_osds' U; x2 G& O3 Q, {: a8 y% _2 J
pgmap v23391: 768 pgs, 3 pools, 403 MB data, 92011 objects* [( v1 Y4 X- y e& [# i. H, U
1523 MB used, 1532 GB / 1533 GB avail
+ L* ]* @8 ?1 }5 ^% M+ Q 183984/368006 objects degraded (49.995%)
% L6 F- ]0 p1 \/ }: f# U# X 512 active+clean/ D z X- j y" `$ R& J
256 active+undersized+degraded+ c3 ]& `# {( ^" w9 B
* N, M6 ~* ^6 ^( Z3 G' ~7 R/ m2 H f7 ]. P8 f
[root@ceph cluster]# ceph osd pool set volumes crush_ruleset 28 Z, g8 n; x% y, }7 ~8 p
set pool 1 crush_ruleset to 28 \1 F2 j% E% U% W+ E" r* t' H
" T" v6 M# r5 ^* W### 等到 volumes 池的所有PG均变为 active+clean 后,将 volumes 池的 crush_ruleset 设置为3, 此步骤后,volumes 池的 PG状态变为 active+remapped。但是不影响集群IO。7 |' l" Y+ L1 ^5 U) P: g" H# h: @+ b
3 f2 g2 U' A2 T+ d& _' u
[root@ceph cluster]# ceph osd pool set volumes crush_ruleset 2" L" Y2 X2 h- n" d& T8 v
set pool 1 crush_ruleset to 2( H1 n' `1 ?9 s* N. k' k- J
: ^8 E N8 G" w9 I0 q2 ?# Wset pool 1 crush_ruleset to 3+ i/ ^8 T. W2 ]* l/ V0 F7 |
$ ^8 S- |5 g! h( {( e2 f### 此时,将 volumes 池的 副本数 设置为2 ,此时 PG 状态经过 peer 之后,很快变为 active+clean ,volumes 池的两副本均落在新的节点下,并且后台在自行删除之前旧节点上的数据。
/ C) l9 E; n2 D/ D5 x5 |2 O8 q l% _& u6 g1 }( b7 A; d3 |* F
[root@ceph cluster]# ceph osd pool set volumes size 2$ F8 q% L) f7 ]6 C s* J0 m9 Y
0 f% W* ~/ S, `( `, Q) L8 f1 w
注意事项
H4 t8 O& @% }: X, X7 k G5 S# ~5 B6 G$ l" w t% ^. W1 Y1 ]* \
: i" j8 {( p! V& C! e4 _3 W e
由于本次数据迁移是在生产环境上执行的,所以没有直接执行将数据从旧节点直接 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 卡住现象。因此我们需要制定完善的回退步骤,在意外发生的时候,能够快速将环境回退到上一步集群正常的状况,而不是在意外发生时惊出一身冷汗,双手颤抖得敲指令。。。所以,下面的表格给出了这个变更操作的每一步的回退步骤:
- _; q- ?+ A' v+ `' z! o4 ]
[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。 |
: x+ l) r# w5 C# w# _% k5 m- V0 y' ^; v
为何不直接迁移数据到新节点?总体来看数据迁移过程,最耗时的地方是数据从两副本变为四副本的过程,其余过程是短暂且可以快速回退的,而如果直接将池的 crush_ruleset 设置为 3 ,数据开始从旧节点直接backfill到新节点上,其实这么做也是可以的,但是这里我们就会遇到一个问题,一旦数据复制了一天或者一段时间后,集群出现了问题,比如性能骤降,要求必须回退到操作之前的状态,此时已经有部分PG完成了迁移,也就是说旧节点上的两副本已经删除了,那么回退到上一步后,还会发生数据从新节点向旧节点的复制,那么之前复制了多久的数据,可能就需要多久来恢复旧节点上删除的数据,这很不友好。而用了我们的方法来复制数据的话,不会存在这个问题,因为这里的方法在最后一步将池的副本设置为2之前,旧节点上的数据始终都是在的,并且不会发生任何迁移,我们可以在任意意外情况下,通过几条指令将集群恢复到变更之前的状态。 : k6 a( N' K' o
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 ! T' }8 G1 I1 ?1 H" M! \# `
qemu 3171 1 17 14:32 ? 00:31:08 /usr/libexec/qemu-kvm -name guest=instance-0000000b.........
$ h/ t8 _3 s+ G- M6 H% ^5 @+ {5 {
[root@con ~(keystone_admin)]# netstat -tnp |grep 3171|grep 6789& V w& \2 p1 O* N E* g) W" g
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
; i- t3 e- t/ B: i2 {1 s0 s' a0 \2 K2 h( K W0 _$ H
[root@con ~(keystone_admin)]# netstat -tnp |grep 3171|grep 67895 @8 `3 r1 G4 J: U2 a2 t: k
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
5 x! p9 g" O! X1 K 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: ### 具体字段为:
7 t' [# E0 N8 K2 Kmysql =>
: U* |. U. [9 K M2 a4 y( Pnova => block_device_mapping => connection_info4 R7 y( S) P! _5 r
9 O) a, N( Q5 B y6 R# L
*************************** 23. row ***************************9 E$ l. [" W6 L6 b6 _2 B
created_at: 2018-03-19 08:50:59
. [) J7 L1 h- ]) D updated_at: 2018-03-26 06:32:06
1 N5 Y, G, o. G) I" W* ]- k deleted_at: 2018-03-26 09:20:02# p/ S5 l3 O" v5 |, b/ G
id: 29; ?; Z. N; N; Y
device_name: /dev/vdb
4 r3 O7 \5 l/ m% Edelete_on_termination: 0
: \* c4 p6 h/ F+ D8 V) q5 y, G snapshot_id: NULL
9 m8 Y$ E" ^: j n- I& n volume_id: 39c76d96-0f95-490c-b7db-b3da6d17331b) c# z: l% H* y2 Z
volume_size: NULL( Q0 L) k/ }1 O( c- b$ N5 d
no_device: NULL) F8 w+ L5 b3 [. V" U
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"]}}0 |2 @5 ~. D/ X, M. h
instance_uuid: 4f52191f-9645-448f-977b-80ca515387f76 [( `- Z% z! ~9 E% ~6 {
deleted: 29/ H/ k) l1 ?8 x# y
source_type: volume
|2 H; Q5 h- Z2 }1 @1 c destination_type: volume# H2 B! T+ T M0 a1 a8 Q% S! N8 J
guest_format: NULL
' l, }* }, b* i8 E! x device_type: disk
; n1 r0 t' S( [+ k# _ disk_bus: virtio0 t3 n9 d4 T' n. E+ ^
boot_index: NULL
1 N7 z5 q1 Y! v* N7 w: a( V# i image_id: NULL迁移指令这里,我们使用 ceph-deploy 来增删 MON 节点,主要是为了操作的简洁和安全性着想。 首先,我们需要将新的三个MON的IP地址加入到所有节点的/etc/ceph/ceph.conf的mon_host 字段中。保证此时mon_host内是六个MON的IP地址。 ### 添加 new_mon_1 - F+ K, F9 z. W8 ^3 R
[root@ceph cluster]# ceph-deploy mon add new_mon_1
& y) K& n4 R/ `6 O8 U* f! O! ]1 L[root@ceph cluster]# ceph -s, |. P s' R* X( S7 R/ G: [
cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d/ [. U1 S3 Y5 _% K* A9 f3 \
health HEALTH_OK3 w5 s E4 K& i& N) b
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}
( q+ ?$ Z, O$ E* u * u3 ^$ `6 S V
### 删除 con 删除完后,建议等待1-3min再添加新的MON,使得qemu-kvm进程可以建立新的socket链接。
3 c+ U' i, |$ _5 b/ |( d) [" U[root@ceph cluster]# ceph-deploy mon destroy con4 o/ E" x. m& [) k. N1 T
[root@ceph cluster]# ceph -s
* f! T! f7 K3 W cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d7 Q$ R- l/ |* ~% D2 x; c
health HEALTH_OK
2 ^, v2 v: n B 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}
/ U+ D0 G' A9 e z; g # \9 t5 L" I, t5 G! A
### 依次添加 new_mon_2 ,删除 com,添加 new_mon_3 , 删除 ceph:/ w1 C! F( k2 K( @* E1 d/ D
[root@ceph cluster]# ceph-deploy mon add new_mon_2
' S4 h& W; d1 X5 k1 k+ [[root@ceph cluster]# ceph-deploy mon destroy com
$ [$ R% i( q2 C8 w2 s### 等待1-3min,
. n" c* n6 \8 |* c[root@ceph cluster]# ceph-deploy mon add new_mon_3
! o9 e+ O$ s1 Y$ C- R3 D+ F[root@ceph cluster]# ceph-deploy mon destroy ceph. X* P5 R* t- S4 G6 y
" v3 c9 b& k" r1 [8 W7 F3 J- t* Z0 z
[root@con ~(keystone_admin)]# ceph -s A7 [2 o5 g) r3 E- ]
cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d
. S7 Z% I9 x8 J4 |" I6 Q* c health HEALTH_OK
" t9 x- {, ?: _3 d; | 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地址。
( J! J( i. v0 V/ m. W& d" ^ Glance & Cinder & Nova 服务重启无需重启 Glance 服务。 需要重启 所有计算节点的 nova-compute 和 控制节点的 Cinder 服务,否则会导致虚机无法创建等问题。 ## 在计算节点 H: B) k ^/ C
openstack-service restart nova-compute0 E" m* l0 E$ D1 c8 \2 X
## 在控制节点
2 `# G- z) }# Z4 E' g% C) F1 Sopenstack-service restart cinderNova由于 nova 不会更新之前的已经挂载的磁盘所连接的MON IP 信息,这会导致虚机在重启等动作时,尝试连接到旧的已经被摧毁的MON的地址,导致动作卡住,因此这里要单独改一下数据库内的MON IP 信息: 这里我们将 192.168.10.10/11/12 改为 192.168.10.13/14/15 mysql >>
6 }1 S4 V0 a; @4 b; M+ l* j$ ]! r( [9 b# N& ]
use nova;. O; N) S9 @2 V
update block_device_mapping set connection_info=(replace(connection_info,'192.168.10.10','192.168.10.13'));
% o1 c. L8 `( i0 B( _ T; p- Y6 iupdate block_device_mapping set connection_info=(replace(connection_info,'192.168.10.11','192.168.10.14'));: v- }2 x3 D) R' I- K4 b) V m
update block_device_mapping set connection_info=(replace(connection_info,'192.168.10.12','192.168.10.15'));
2 j/ k# z. g: Q2 F
1 |* ]- y& v5 J1 j$ J: yexit;更新完数据库后,这次数据迁移算是大功告成了。为何不需要重启虚机服务呢,这里再做一些简单的介绍: qemu-kvm 虚机进程是一个长连接的 Ceph Client,自虚机启动后,进程就和 Ceph 集群保持着连接,在我们更改 MON 后,这些 Client 会自动尝试重连 monmap 里面的其他MON,从而更新 monmap, 来获取最新的 MON 。修改 /etc/ceph/ceph.conf不会对虚机进程产生影响,除非虚机重启等,但是,虚机可以通过更新 monmap 的方式来感知集群MON的改变。 |