场景介绍:在我们的IDC中,存在着运行了3-6年的Ceph集群的服务器,这些服务器性能和容量等都已经无法满足当前业务的需求,在购入一批高性能机器后,希望将旧机器上的集群整体迁移到新机器上,当然,是保证业务不中断的前提下,再将旧机器下架回收。本文就介绍了一种实现业务不中断的数据迁移方案,并已经在多个生产环境执行。
本文的环境均为:Openstack+Ceph 运行虚拟机的场景,即主要使用RBD,不包含RGW,MDS。虚机的系统盘(Nova),云硬盘(Cinder),镜像盘(Glance)的块均保存在共享存储Ceph中。
主机名 | IP地址 | Openstack 组件 | Ceph 组件 |
---|---|---|---|
con | 192.168.100.110 | nova,cinder,glance,neutron | mon,osd*1 |
com | 192.168.100.111 | nova,neutron | mon,osd*1 |
ceph | 192.168.100.112 | mon,osd*1 |
主机名 | IP地址 | Openstack 组件 | Ceph 组件 |
---|---|---|---|
con | 192.168.100.110 | nova,cinder,glance,neutron | |
com | 192.168.100.111 | nova,neutron | |
ceph | 192.168.100.112 | ||
new_mon_1 | 192.168.100.113 | mon,osd*1 | |
new_mon_2 | 192.168.100.114 | mon,osd*1 | |
new_mon_3 | 192.168.100.115 | mon,osd*1 |
[root@con ~(keystone_admin)]# nova list
+--------------------------------------+---------+--------+------------+-------------+-------------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+---------+--------+------------+-------------+-------------------------+
| 4f52191f-9645-448f-977b-80ca515387f7 | vm-test | ACTIVE | - | Running | provider=192.168.88.111 |
+--------------------------------------+---------+--------+------------+-------------+-------------------------+
[root@con ~(keystone_admin)]# cinder list
+--------------------------------------+--------+--------------+------+-------------+----------+--------------------------------------+
| ID | Status | Display Name | Size | Volume Type | Bootable | Attached to |
+--------------------------------------+--------+--------------+------+-------------+----------+--------------------------------------+
| 39c76d96-0f95-490c-b7db-b3da6d17331b | in-use | cinder-rbd | 1 | None | false | 4f52191f-9645-448f-977b-80ca515387f7 |
+--------------------------------------+--------+--------------+------+-------------+----------+--------------------------------------+
[root@con ~(keystone_admin)]# ip netns exec `ip netns` ssh [email protected]
[email protected]'s password:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
vda 253:0 0 1G 0 disk
`-vda1 253:1 0 1011.9M 0 part /
vdb 253:16 0 1G 0 disk
$
[root@ceph cluster]# ceph -s
cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d
health HEALTH_OK
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}
election epoch 174, quorum 0,1,2 con,com,ceph
osdmap e57: 3 osds: 3 up, 3 in
flags sortbitwise,require_jewel_osds
pgmap v6577: 768 pgs, 3 pools, 45659 kB data, 23 objects
178 MB used, 766 GB / 766 GB avail
768 active+clean
[root@ceph cluster]# ceph osd tree
ID WEIGHT TYPE NAME UP/DOWN REWEIGHT PRIMARY-AFFINITY
-1 0.74876 root default
-2 0.24959 host ceph
0 0.24959 osd.0 up 1.00000 1.00000
-3 0.24959 host con
1 0.24959 osd.1 up 1.00000 1.00000
-4 0.24959 host com
2 0.24959 osd.2 up 1.00000 1.00000
[root@ceph cluster]# ceph osd pool ls detail
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
removed_snaps [1~3]
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
removed_snaps [1~3]
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 0
removed_snaps [1~9]
原理简介
当副本数减少时,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上。
迁移思路
我们首先会将新的节点的所有OSD初始化完毕,然后将这些OSD 加入到另一棵osd tree下 (这里简称原先的集群的osd tree叫做old_tree,新建的包含新OSD的 osd tree 叫做 new_tree),这样部署完毕后,不会对原有集群有任何影响,也不会涉及到数据迁移的问题,此时新的OSD下还没有保存数据。
导出 CRUSHMAP,编辑,添加三条 CRUSH rule:
注入新的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上。
• 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 选出两副本(也是前两副本)其实是**一样的**。
迁移指令
1.初始化新的OSD
### 前往部署目录
cd /root/cluster
echo "osd_crush_update_on_start = false " >> ceph.conf
ceph-deploy --overwrite-conf osd prepare new_mon_1:sdb new_mon_2:sdb new_mon_3:sdb --zap-disk
ceph-deploy --overwrite-conf osd activate new_mon_1:sdb1 new_mon_2:sdb1 new_mon_3:sdb1
[root@ceph ~]# ceph osd tree
ID WEIGHT TYPE NAME UP/DOWN REWEIGHT PRIMARY-AFFINITY
-1 0.75000 root default
-2 0.25000 host ceph
0 0.25000 osd.0 up 1.00000 1.00000
-3 0.25000 host con
1 0.25000 osd.1 up 1.00000 1.00000
-4 0.25000 host com
2 0.25000 osd.2 up 1.00000 1.00000
3 0 osd.3 up 1.00000 1.00000
4 0 osd.4 up 1.00000 1.00000
5 0 osd.5 up 1.00000 1.00000
ceph osd crush add-bucket new_root root
ceph osd crush add-bucket new_mon_1 host
ceph osd crush add-bucket new_mon_2 host
ceph osd crush add-bucket new_mon_3 host
ceph osd crush move new_mon_1 root=new_root
ceph osd crush move new_mon_2 root=new_root
ceph osd crush move new_mon_3 root=new_root
ceph osd crush add osd.3 0.25 host=new_mon_1
ceph osd crush add osd.4 0.25 host=new_mon_2
ceph osd crush add osd.5 0.25 host=new_mon_3
[root@ceph ~]# ceph osd tree
ID WEIGHT TYPE NAME UP/DOWN REWEIGHT PRIMARY-AFFINITY
-5 0.75000 root new_root
-6 0.25000 host new_mon_1
3 0.25000 osd.3 up 1.00000 1.00000
-7 0.25000 host new_mon_2
4 0.25000 osd.4 up 1.00000 1.00000
-8 0.25000 host new_mon_3
5 0.25000 osd.5 up 1.00000 1.00000
-1 0.75000 root default
-2 0.25000 host ceph
0 0.25000 osd.0 up 1.00000 1.00000
-3 0.25000 host con
1 0.25000 osd.1 up 1.00000 1.00000
-4 0.25000 host com
2 0.25000 osd.2 up 1.00000 1.00000
2.编辑 CRUSH MAP
### 导出CRUSH MAP
ceph osd getcrushmap -o map
crushtool -d map -o map.txt
### 在map.txt 最后添加以下内容
vim map.txt
# rules
rule replicated_ruleset {
ruleset 0
type replicated
min_size 1
max_size 10
step take default
step chooseleaf firstn 0 type host
step emit
}
>>>>>>>>>>>>>> 添加开始 >>>>>>>>>>
rule replicated_ruleset1 {
ruleset 1
type replicated
min_size 1
max_size 10
step take default
step chooseleaf firstn 2 type host
step emit
}
rule replicated_ruleset2 {
ruleset 2
type replicated
min_size 1
max_size 10
step take default
step chooseleaf firstn 2 type host
step emit
step take new_root
step chooseleaf firstn 2 type host
step emit
}
rule replicated_ruleset3 {
ruleset 3
type replicated
min_size 1
max_size 10
step take new_root
step chooseleaf firstn 2 type host
step emit
}
<<<<<<<<<<<< 添加结束 <<<<<<<<<<<<
# end crush map
### 编译 CRUSH MAP,并注入到集群中
crushtool -c map.txt -o map.bin
ceph osd setcrushmap -i map.bin
3. 开始迁移数据
###确认当前 volumes 池使用的是 crush_ruleset 0
[root@ceph cluster]# ceph osd pool get volumes crush_ruleset
crush_ruleset: 0
### 将 volumes 池的 crush_ruleset 设置为 1,设置完后,集群一切正常,PG均为active+clean
[root@ceph cluster]# ceph osd pool set volumes crush_ruleset 1
set pool 1 crush_ruleset to 1
### 将 volumes 池的 副本数设置为4, 设置完后,PG经过短暂 Peer,变为 active+undersized+degraded
[root@ceph cluster]# ceph osd pool set volumes size 4
set pool 1 size to 4
[root@ceph cluster]# ceph -s
cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d
health HEALTH_WARN
256 pgs degraded
256 pgs undersized
recovery 183984/368006 objects degraded (49.995%)
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}
election epoch 182, quorum 0,1,2 con,com,ceph
osdmap e106: 6 osds: 6 up, 6 in
flags sortbitwise,require_jewel_osds
pgmap v23391: 768 pgs, 3 pools, 403 MB data, 92011 objects
1523 MB used, 1532 GB / 1533 GB avail
183984/368006 objects degraded (49.995%)
512 active+clean
256 active+undersized+degraded
[root@ceph cluster]# ceph osd pool set volumes crush_ruleset 2
set pool 1 crush_ruleset to 2
### 等到 volumes 池的所有PG均变为 active+clean 后,将 volumes 池的 crush_ruleset 设置为3, 此步骤后,volumes 池的 PG状态变为 active+remapped。但是不影响集群IO。
[root@ceph cluster]# ceph osd pool set volumes crush_ruleset 2
set pool 1 crush_ruleset to 2
set pool 1 crush_ruleset to 3
### 此时,将 volumes 池的 副本数 设置为2 ,此时 PG 状态经过 peer 之后,很快变为 active+clean ,volumes 池的两副本均落在新的节点下,并且后台在自行删除之前旧节点上的数据。
[root@ceph cluster]# ceph osd pool set volumes size 2
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 卡住现象。因此我们需要制定完善的回退步骤,在意外发生的时候,能够快速将环境回退到上一步集群正常的状况,而不是在意外发生时惊出一身冷汗,双手颤抖得敲指令。。。所以,下面的表格给出了这个变更操作的每一步的回退步骤:
变更步骤 | 实际影响 | 回退指令 | 回退影响 |
---|---|---|---|
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 4 | PG 变为active+remapped。 |
原理介绍
Glance :上传下载镜像等时,需要新建一个调用 librbd 的 Client 来连接 Ceph集群。
Cinder :
创建删除云盘时,新建一个调用 librbd 的 Client 来连接 Ceph 集群。
挂载卸载云盘时,由Nova调用librbd来实现该操作。
Nova : 虚机(qemu-kvm进程)相当于一个始终在调用librbd的Client,并且进程始终都在。
[root@con ~(keystone_admin)]# ps -ef|grep kvm
qemu 3171 1 17 14:32 ? 00:31:08 /usr/libexec/qemu-kvm -name guest=instance-0000000b.........
[root@con ~(keystone_admin)]# netstat -tnp |grep 3171|grep 6789
tcp 0 0 192.168.100.110:59926 192.168.100.112:6789 ESTABLISHED 3171/qemu-kvm
[root@con ~(keystone_admin)]# ssh 192.168.100.112 systemctl stop ceph-mon.target
[root@con ~(keystone_admin)]# netstat -tnp |grep 3171|grep 6789
tcp 0 0 192.168.100.110:48792 192.168.100.111:6789 ESTABLISHED 3171/qemu-kvm
原先有三个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
Nova 侧的一个问题
### 具体字段为:
mysql =>
nova => block_device_mapping => connection_info
*************************** 23. row ***************************
created_at: 2018-03-19 08:50:59
updated_at: 2018-03-26 06:32:06
deleted_at: 2018-03-26 09:20:02
id: 29
device_name: /dev/vdb
delete_on_termination: 0
snapshot_id: NULL
volume_id: 39c76d96-0f95-490c-b7db-b3da6d17331b
volume_size: NULL
no_device: NULL
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.100.110", "192.168.100.111", "192.168.100.112"], "auth_enabled": true, "access_mode": "rw", "auth_username": "cinder", "ports": ["6789", "6789", "6789"]}}
instance_uuid: 4f52191f-9645-448f-977b-80ca515387f7
deleted: 29
source_type: volume
destination_type: volume
guest_format: NULL
device_type: disk
disk_bus: virtio
boot_index: NULL
image_id: NULL
迁移指令
### 添加 new_mon_1
[root@ceph cluster]# ceph-deploy mon add new_mon_1
[root@ceph cluster]# ceph -s
cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d
health HEALTH_OK
monmap e48: 4 mons at {ceph=192.168.100.112:6789/0,com=192.168.100.111:6789/0,con=192.168.100.110:6789/0,new_mon_1=192.168.100.113:6789/0}
### 删除 con 删除完后,建议等待1-3min再添加新的MON,使得qemu-kvm进程可以建立新的socket链接。
[root@ceph cluster]# ceph-deploy mon destroy con
[root@ceph cluster]# ceph -s
cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d
health HEALTH_OK
monmap e49: 3 mons at {ceph=192.168.100.112:6789/0,com=192.168.100.111:6789/0,new_mon_1=192.168.100.113:6789/0}
### 依次添加 new_mon_2 ,删除 com,添加 new_mon_3 , 删除 ceph:
[root@ceph cluster]# ceph-deploy mon add new_mon_2
[root@ceph cluster]# ceph-deploy mon destroy com
### 等待1-3min,
[root@ceph cluster]# ceph-deploy mon add new_mon_3
[root@ceph cluster]# ceph-deploy mon destroy ceph
[root@con ~(keystone_admin)]# ceph -s
cluster 166889ab-fa7b-4a07-83da-6dfc92913a3d
health HEALTH_OK
monmap e53: 3 mons at {new_mon_1=192.168.100.113:6789/0,new_mon_2=192.168.100.114:6789/0,new_mon_3=192.168.100.115:6789/0}
Glance & Cinder & Nova 服务重启
## 在计算节点
openstack-service restart nova-compute
## 在控制节点
openstack-service restart cinder
Nova
mysql >>
use nova;
update block_device_mapping set connection_info=(replace(connection_info,'192.168.100.110','192.168.100.113'));
update block_device_mapping set connection_info=(replace(connection_info,'192.168.100.111','192.168.100.114'));
update block_device_mapping set connection_info=(replace(connection_info,'192.168.100.112','192.168.100.115'));
exit;
参考文档
[如何更改基于rbd块设备的虚机的monitor ip] [https://opengers.github.io/openstack/how-to-change-guest-monitor-ip-with-rbd-disk/#%E6%9F%A5%E7%9C%8B%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%BD%93%E5%89%8D%E8%BF%9E%E6%8E%A5%E7%9A%84monitor-ip]
[Ceph Monitor hardcoded IPs in Nova database] [https://bugzilla.redhat.com/show_bug.cgi?id=1414124]