目录
image.png前言
为什么要搭建一个NAS
因为存储空间不够了。本人有保留文档的习惯,自己经手过的东西,写的代码、自己做的视频、看过的书、看过的电影我都希望一直保留下来。所以数据越来越多,而一个移动硬盘只有4TB的空间,存不下。所以需要搭建一个NAS来扩大存储空间。
而且用移动硬盘还有一个风险,万一哪天这个盘坏掉了,我的数据就全部丢失了。所以除了扩大空间之外,需要做数据冗余。
因此这就引出了我的两大需求:扩大存储空间、数据冗余。那么我如何来满足这两大需求?
Ceph是什么
Ceph是一种为优秀的性能、可靠性和可扩展性而设计的统一的、分布式文件系统。
- 可靠性是指Ceph本身支持容错,数据存储在Ceph系统中可以设置存储副本,当其中一个副本因为硬盘损坏不可访问时,Ceph会通过副本还原出数据。
- 分布式是指Ceph可以部署在多台机器上,并组合成为一个整体。当一台机器的存储空间不够时,可以将多台机器的存储合并到一起。在大型企业中,Ceph可以将成百上千台机器,上万个硬盘的存储空间合并成一个整体的文件系统。
Ceph的这两大特性恰好满足了我的两大需求。
为什么用Ceph
能满足我两大需求的方案很多,比如RAID也可以满足,为什么要用Ceph?
随着硬盘容量逐年上升,数据量越来越大,硬盘变得越来越脆弱容易出现故障导致数据丢失找不回来。因此需要想办法避免数据丢失。
常见的NAS通过RAID或者ZFS实现数据冗余,当其中一份数据丢失之后,冗余的数据能把数据恢复出来。RAID和ZFS在扩容时都不方便,操作不慎容易导致数据丢失。比如RAID1升级到RAID5要做数据重构,重构期间就有数据丢失的风险,一旦丢失就丢掉了所有的数据。ZFS从2盘升级到4盘时,总共需要用到6个硬盘,其中两个盘用于存放中间过程数据,很不方便。
因此为了让扩容更简单,我们通过Ceph实现数据的冗余。在确保数据安全不丢失的前提下实现了扩展的方便性。Ceph的基本原理是把所有数据拆分成4MB的数据块,每个数据块构建冗余,并平均分散到各个硬盘上。并且确保同一份数据在多个硬盘同时保存,避免一个硬盘坏掉之后导致数据丢失。
如何搭建一个基于Ceph的个人NAS
界面效果展示
NAS的核心是文件存储:
Pasted image 20221006195546.png后台管理界面:
Pasted image 20221006180433.png功能很丰富,没有一个个全部截图。
硬件装机
硬件配置
- 主板: 微星Z370M-S01。268元。
- 内存:16Gx2金士顿 2666MHz。 463元。
- CPU:i9-9900T。1620元。
- 系统盘:三星SSD 970 EVO Plus 500G。449元。
- 数据盘:希捷酷狼Pro 16TB x2。 2720元。
- 电源:益衡 7660B。 749元。
- 机箱:拓普龙8盘位。520元。
- 其它杂七杂八配件:
- CPU散热: 杂牌超薄型。70元。
- 网卡:PCIx1接口,2.5Gbps。49元。
- 网卡:USB接口,2.5Gbps。67元。
- 网线:8类万兆线。11元。
- DVI转HDMI: 11元。
费用:
- 总计费用: 6997元。
- 目前装了2盘位平均每TB费用: 218元/TB。
- 将来8盘位全部装满后平均每TB费用: 118元/TB。其中85元是硬盘的费用。
搭建完之后的效果:
image.png能耗测试
使用小米智能插座测试功率,不是很准但是可以参考量级。
- 基础(电源+主板+CPU+内存+SSD+网卡):21瓦
- 基础+内存测试:73瓦
- 基础+1个系统盘空闲状态:26瓦
- 基础+2个系统盘空闲状态:32瓦
- 基础+2个系统盘+1个盘坏道检测:43瓦
全年365x24小时开机大约耗电350度,不是很多。
内存测试
通过MemTest86+,测了半小时,没遇到问题。
硬盘测试
首先看Smart信息:都是全新的硬盘,没有发现问题。
再扫描坏道,扫描了两个小时,没发现问题。
操作系统安装
官方推荐使用22.04版本的Ubuntu。安装之前把网线都拔掉,不然装完之后会自动升级,导致功能不正常。
- 用户名: cpc
- 计算机名称: cpc (不要设置成node0,不然后面安装的时候会出现无法解析node0 IP的错误)
安装完成后重启。关掉cloud-init后门。因为系统每次启动都会连接Ubuntu的服务器,存在后门风险,因此干掉:
apt-get purge cloud-init -y
systemctl disable systemd-networkd-wait-online.service
本教程所有操作都以root身份运行。
为了方便后续管理,我们需要将节点的IP固定下来,不然每次IP不确定都要修改配置文件。在路由器上设置DHCP:
- 设置DHCP动态IP的范围192.168.31.100~199。
- 设置DHPC静态IP规则,将这次新增的节点设置成192.168.31.10。后面192.168.31.11~19是将来扩展节点。
配置hosts文件:
vi /etc/hosts
192.168.31.10 node0 testnode
192.168.31.11 node1
192.168.31.12 node2
192.168.31.13 node3
192.168.31.14 node4
192.168.31.15 node5
192.168.31.16 node6
192.168.31.17 node7
192.168.31.18 node8
192.168.31.19 node9
并且以上hosts配置要放到所有其它电脑上。这样其它电脑就能通过节点名称来访问,不需要指定IP。Ceph提供的Dashboard默认只会通过节点名称来访问。
Ceph的安装
参考文档: https://ceph.com/en/users/getting-started/
内存至少8G。最少要求如下图:
image.png安装cephadm并下载镜像:
# 安装cephadm
hostnamectl set-hostname node1
apt update
apt install -y docker.io uuid cephadm ceph-common
# docker翻墙
mkdir -p /etc/systemd/system/docker.service.d
cat <<EOF > /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://192.168.31.223:8001"
Environment="HTTPS_PROXY=http://192.168.31.223:8001"
EOF
systemctl daemon-reload
systemctl restart docker
systemctl show --property=Environment docker
# 下载Docker镜像
docker pull quay.io/ceph/ceph:v17
设置环境变量:
uuid
vi /root/.bashrc
export fsid=???
alias cephadm='/usr/sbin/cephadm --image quay.io/ceph/ceph:v17'
mon_ip=192.168.31.11
node=node1
source /root/.bashrc
安装集群:
# 安装新集群
password=123456
cephadm bootstrap --mon-ip $mon_ip --skip-pull --dashboard-password-noupdate --initial-dashboard-password $password --fsid $fsid
# 删除之前的集群
cephadm rm-cluster --fsid $fsid --force
# 设置UID的目的是为了和容器内的uid保持一致。不然后面会遇到莫名其妙的错误。
# 由于uid发生变化。删掉集群再重新安装。
usermod -u 167 ceph
groupmod -g 167 ceph
usermod -u 997 cephadm
cephadm bootstrap --mon-ip $mon_ip --skip-pull --dashboard-password-noupdate --initial-dashboard-password $password --fsid $fsid
查看集群状态:
ceph -s
# 或者浏览器访问 https://192.168.31.150:8443/
添加硬盘:
# 格式化所有硬盘
for i in sdb sdc sdd sde sdf sdg ; do
ceph orch device zap node1 /dev/$i --force
done
通过GUI添加OSD
设置集群参数(假设只有一个节点,因此设置了允许数据在同一台机器上的不同硬盘中):
ceph config set global mon_allow_pool_delete true
ceph osd crush rule create-replicated osd-replicate default osd #1
ceph osd erasure-code-profile set ec-22 plugin=jerasure k=2 m=2 technique=reed_sol_van crush-failure-domain=osd
ceph osd crush rule create-erasure ec-22-rule ec-22 #2
ceph osd erasure-code-profile set ec-42 plugin=jerasure k=4 m=2 technique=reed_sol_van crush-failure-domain=osd
ceph osd crush rule create-erasure ec-42-rule ec-42 #3
ceph config set global osd_pool_default_crush_rule 1
ceph config set global osd_pool_default_min_size 2
ceph config set global osd_pool_default_size 2
通过RBD构建文件系统
创建image:
# 创建image
ceph osd pool create rbd
ceph osd pool application enable rbd rbd # 第一个rbd表示poolname
rbd create --size 14TB rbd/myimage --stripe-unit 32M --stripe-count 1 --object-size 32M
# 格式化分区
rbd device map rbd/myimage --id admin
gdisk /dev/rbd0
mkfs -t xfs /dev/rbd0p1
# 挂载
mkdir /mnt/rbd
mount -t xfs -o wsync /dev/rbd0p1 /mnt/rbd
# 退出挂载
#rbd device list
#rbd device unmap /dev/rbd0
初始化文件系统的内容,避免文件系统还没挂上就开始往里写东西了
cd /mnt/rbd
mkdir mounted
chown cpc:cpc -R mounted
cd ..
ln -s /mnt/rbd/mounted samba
开机自启:
touch onstartup.sh
chmod +x onstartup.sh
vi ~/onstartup.sh
while [ 1 ] ; do
sleep 1
echo mapping
device=$(rbd device map rbd/myimage --id admin) || continue
break
done
while [ 1 ] ; do
sleep 1
echo mounting
mount -t xfs -o wsync "${device}p1" /mnt/rbd || continue
break
done
安装cron脚本:
crontab -e
@reboot /usr/bin/bash /root/onstartup.sh 1 > /root/onstartup.log 2>&1
安装Samba服务: 通过Samba提供服务
通过Filesystem构建文件系统(不推荐)
不推荐的理由是性能太差。对于家用小集群,硬盘不多。而Filesystem至少两个Pool,一个Pool负责元数据的保存,一个Pool负责数据的保存。这两个Pool在硬盘上的物理地址相距甚远,当数据写入时,需要同时将元数据和内容数据记录到这两个Pool中,导致磁头在两个Pool之间来回摆动,花费了大量的时间在寻道,所以性能差。
当集群很小,硬盘数量是2时,设置以下参数创建文件系统:
ceph fs volume create myfs
由于客户端和服务端在同一台机器会引发bug,因此要挂载到虚拟机里。
由于服务端和客户端在同一台机器上,挂载的时候服务端还没启动。因此要放到crontab中:
# 设置权限
ceph auth rm client.localmount
secret=$(ceph fs authorize myfs client.localmount / rwp | fgrep key | awk -F ' = ' '{print $2}')
# crontab脚本设置
cat <<EOF > /root/mount-cephfs.sh
mkdir -p /mnt/cephfs
while [ 1 ] ; do
sleep 1
mount -t ceph 192.168.31.10:6789:/ /mnt/cephfs -o name=localmount,secret=$secret || continue
break
done
EOF
chmod +x /root/mount-cephfs.sh
# 安装crontab任务
cd
echo @reboot /root/mount-cephfs.sh > mycron
crontab mycron
rm mycron
./mount-cephfs.sh
为了方便后续迁移,在文件系统里面设置子文件夹:
mkdir /mnt/cephfs/replicated
rm -f /mnt/cephfs/current
ln -sf replicated /mnt/cephfs/current
chown -R cpc:cpc /mnt/cephfs
chmod -R 700 /mnt/cephfs
添加开启自动启动命令VirtualBox:
vboxmanage startvm xxxxx --type headless
通过Samba将分区挂载到Windows上
安装samba并配置:
apt install -y samba
ufw allow samba
smbpasswd -a cpc
vi /etc/samba/smb.conf
[global]
client min protocol = NT1
server min protocol = NT1
……后面内容省略
[root]
comment = cpc
path = /mnt/samba
read only = no
browsable = yes
recycle:repository = .recycle/%T/
vfs objects = recycle
recycle:keeptree = yes
recycle:versions = yes
valid users = cpc
[监控中心]
comment = monitor
valid users = guest
path = /mnt/samba/监控中心
read only = no
browsable = yes
recycle:repository = .recycle/%T/
vfs objects = recycle
recycle:keeptree = yes
recycle:versions = yes
force user = cpc
[下载]
comment = download
valid users = guest
path = /mnt/samba/下载
read only = no
browsable = yes
strict sync = no
recycle:repository = .recycle/%T/
vfs objects = recycle
recycle:keeptree = yes
recycle:versions = yes
force user = cpc
service smbd restart
Windows上映射网络驱动器。\\192.168.31.11\CephFS
。 用户名是cpc。
为了数据安全,监控中心的数据不能随意让外部读取,每分钟转移:
vi /root/onminute.sh
cd /mnt/samba/监控中心/xiaomi_camera_videos/607ea442c3a3 || exit 1
DST=/mnt/samba/个人文件/监控录像
#cd /root/test
#DST=/root/test2
find -type d -exec mkdir -vp "$DST"/{} \; -or -exec mv -fv {} "$DST"/{} \; || exit 1
#find -type d | tail -n +2 | while read line ; do rmdir -v --ignore-fail-on-non-empty "$line" ; done || exit 1
crontab -e
30 * * * * /usr/bin/bash
后期运维操作
如果断电重启会怎样?
重启后所有的挂载、Samba服务都恢复。读写都正常。
如果硬盘接口顺序插错了?
调整SATA接口的顺序,模拟硬盘维护过程中重新插拔顺序错乱。看看ceph集群能否正常恢复。试了,可以正常恢复,并且文件正常读写。
因为每个硬盘上记录了OSD编号,系统会根据OSD编号找到自己的数据盘。
如果一个硬盘坏了会怎样?
硬盘损坏后Ceph不会自动踢掉。而是要人工干预:
i=0
ceph osd crush rm osd.$i
systemctl |fgrep osd.$i
systemctl stop xxx
ceph osd rm osd.$i
rm -R /var/lib/ceph/$fsid/osd.$i
ceph auth rm osd.$i
# 格式化新盘: ceph orch device zap node150 /dev/比如sdb --force
这样才会触发数据重新平衡。等数据全部恢复完成后,才能读取文件。如果EC分区其中一个OSD由于硬盘数量太少无法恢复,也会导致文件无法读取。
重启之后,读写都正常。
替换新的盘之后,新的盘马上有数据写入进去了。
min_size如果=1,那么一个盘坏掉之后仍然可以写入。
坏了一个盘之后,如果min_size=2,那么数据写入不会报错,因为XFS文件系统有写缓存,缓存无法更新到硬盘,此时如果断电重启数据会丢失。为了优化这个问题需要:
ceph osd pool set rbd min_size 2
mount参数要加上-o wsync,保证元数据的一致性
这样设置完之后,当坏掉了一个盘,所有的数据读写操作都会被禁止。哪天硬盘有损坏,马上就知道了。
更进一步可以通过定时任务,再加一层自动降级:
degrades=$(ceph -s|fgrep degrade|wc -l)
if [ "$degrades" != "0" ] ; then
service smbd stop
fi
如果容量不够了要怎么办?
先备份数据,做快照
通过GUI生成image快照
加硬盘:
# ceph orch device zap node0 /dev/$i --force
ceph orch daemon add osd node150:/dev/sdd
ceph orch daemon add osd node150:/dev/sde
Image的扩容:
rbd resize --image rbd/myimage --size 20GB
文件系统扩容:
# 先解除挂载
service smbd stop
umount /mnt/rbd
# 修复文件系统
xfs_repair -n /dev/rbd0p1
# 分区扩容
parted /dev/rbd0
# print
# Fix
# resizepart 1 21.5GB
reboot
# 重新挂载
#mount -t xfs /dev/rbd0p1 /mnt/rbd
#service smbd start
重启之后,文件能正常读写。
如果大小填错了,往小了写。需要加上--allow-shrink防呆。
如何修改副本数量?
当后面扩容增加硬盘,需要修改副本数量,至少3副本才能保证数据安全:
ceph osd pool set cephfs.myfs.meta size 3
ceph osd pool set cephfs.myfs.data size 3
ceph osd pool set .mgr size 3
ceph config set global osd_pool_default_size 3
如果通过EC冗余码提供更多空间?
通过副本实现冗余会导致一份数据占用两份空间,存在较多空间浪费。因此需要想办法节约空间。
EC冗余码是指Erasure Code,中文名是纠删码。比如RAID5利用了EC码实现了1.5倍冗余。也就是说一份数据占用1.5份数据的空间,比副本模式节约空间。而EC码则能够更进一步节约空间,根据需要可以调整参数节约更多空间。核心两大参数M+K。M代表数据的拆分的数量,K代表冗余码的份数。每一份数据都要放到不同的硬盘上。比如RAID5是M=2,K=1实现了2+1的数据模式,至少需要3个硬盘,最多能坏掉M个硬盘,也就是最多坏掉1个硬盘而不影响数据恢复。而RAID6则是6+2,至少需要8个硬盘,最多能坏掉2个硬盘而不影响数据恢复。
方法1: 复制Image(推荐)
# 解除挂载
service smbd stop
umount /mnt/rbd
rbd device unmap /dev/rbd0
# 复制image
ceph osd pool create rbd-ec-22 erasure ec-22 ec-22-rule
ceph osd pool application enable rbd-ec-22 rbd
ceph osd pool set rbd-ec-22 allow_ec_overwrites true
rbd cp rbd/myimage rbd/myimage2-ec-22 --data-pool rbd-ec-22
# 放到回收站
rbd trash rbd/myimage
# 重新挂载
vi /root/onstartup.sh
reboot
# 测试没问题后,删除旧数据
rbd rm rbd/myimage
重启之后可以正常读写文件。
方法2:复制data pool(不可行)
这个方法执行完后会造成数据无法读取、无法写入。因为元数据中记录了数据放在那个pool_id。这种方式执行完之后pool_id发生了变化,导致数据内容无法读取。最终现象是元数据能看到,但是无法读取无法写入。
# 删掉文件系统
ceph fs fail myfs
ceph fs rm myfs --yes-i-really-mean-it
# 将data数据复制一份
ceph osd pool create cephfs.myfs.ec-42 erasure ec-42 ec-42-rule
ceph osd pool set cephfs.myfs.ec-42 allow_ec_overwrites true
rados cppool cephfs.myfs.data cephfs.myfs.ec-42
# 创建新的文件系统
ceph fs new myfs cephfs.myfs.meta cephfs.myfs.ec-42 --force --recover
ceph fs set myfs joinable true
# 删除旧的数据
ceph osd pool rm cephfs.myfs.data cephfs.myfs.data --yes-i-really-really-mean-it
方法3:通过setfattr将数据复制到新的pool
# 停掉SMB服务
service smbd stop
# 创建新的data pool
ceph osd pool create cephfs.myfs.ec-42 erasure ec-42 ec-42-rule
ceph osd pool set cephfs.myfs.ec-42 allow_ec_overwrites true
# 在fs中添加data pool
ceph fs add_data_pool myfs cephfs.myfs.ec-42
mkdir -p /mnt/cephfs/ec-42
chown cpc:cpc -R /mnt/cephfs/ec-42
setfattr -n ceph.dir.layout.pool -v cephfs.myfs.ec-42 /mnt/cephfs/ec-42
# 将文件复制过去
cp -RvP --preserve=mode,ownership,timestamps,links,xattr,all /mnt/cephfs/replicated/* /mnt/cephfs/ec-42/
rm -f /mnt/cephfs/current
ln -sf ec-42 /mnt/cephfs/current
# 重建文件系统
ceph fs fail myfs
ceph fs rm myfs --yes-i-really-mean-it
ceph fs new myfs cephfs.myfs.meta cephfs.myfs.ec-42 --force --recover
ceph fs set myfs joinable true
# 重新开启SMB服务
service smbd start
# 删除旧的数据
rm -R /mnt/cephfs/replicated
ceph osd pool rm cephfs.myfs.data cephfs.myfs.data --yes-i-really-really-mean-it
系统盘坏了怎么办?
如果系统盘发生了故障,只剩下数据盘,而数据盘的分区格式是Ceph定制的BluestoreFS,里面的内容无法直接获取,此时要如何恢复数据?
系统盘里装了什么重要的东西?
集群的元数据都在系统盘上。元数据包括了集群的UUID、集群中包含的节点,集群和OSD的关系、OSD和PG的对应关系、PG和Pool的关系。而我们所有的应用层RBD、Filesystem都是建立在Pool基础上,如果Pool的元数据信息丢失,那么系统就不知道某个Pool的数据在哪,这些应用层都无法使用。
恢复工具制作
目前使用Ceph v17版本搭建了NAS,并且版本在不断更新。等到过了几年到了2025年之后,如果系统盘发生了损坏,那么还能不能找到v17版本是个问题。因此需要把当前的软件版本做个备份,构建一个虚拟机能完整运行Ceph v17系统,在虚拟机里做了一次故障演练,能演练成功。然后将这台虚拟机备份到阿里云上,这样恢复工具就制作完成了。
恢复步骤
参考资料: https://docs.ceph.com/en/quincy/rados/troubleshooting/troubleshooting-mon/#recovery-using-osds
系统盘坏掉之后,先恢复Pool。
首先从阿里云上下载到备份文件: https://caipeichao-doc-backup.oss-cn-hangzhou.aliyuncs.com/-recover/ceph-recover-disk2.part01.rar 总共5个分卷
安装必要工具:
apt install -y jq ceph-osd ceph-mon
找到集群ID:
# 查看虚拟卷关联的硬件设备
lsblk
ls -l /dev/mapper
# 找出集群ID
fsid=$(ceph-bluestore-tool show-label --dev /dev/dm-0 | jq -r '.[].ceph_fsid')
安装集群,fsid和上面获取的集群ID保持一致。然后停掉集群:
systemctl stop ceph.target
复原osd配置:
# 配置
node=node150
mon_ip=192.168.31.150
# alias
alias cephadm='/usr/sbin/cephadm --image quay.io/ceph/ceph:v17'
function recover {
dev=$1
fsid=$(ceph-bluestore-tool show-label --dev $dev | jq -r '.[].ceph_fsid')
cd /var/lib/ceph/$fsid
# 创建文件夹
osd_id=$(ceph-bluestore-tool show-label --dev $dev | jq -r '.[].whoami')
rm -Rf osd.$osd_id
mkdir osd.$osd_id
cd osd.$osd_id
# 创建文件
ln -s /dev/$(dmsetup info $dev|fgrep Name:|egrep -o 'ceph.*'|sed -e 's/--/-/g' -e 's/-osd-block/\/osd-block/g') block
echo $fsid > ceph_fsid
echo [global] > config
echo fsid = $fsid >> config
echo "mon_host = [v2:$mon_ip:3300/0,v1:$mon_ip:6789/0]" >> config
ceph-bluestore-tool show-label --dev $dev | jq -r '.[].osd_uuid' > fsid
key="$(ceph-bluestore-tool show-label --dev $dev | jq -r '.[].osd_key' )"
echo "[osd.$osd_id]" > keyring
echo "key = $key" >> keyring
ceph-bluestore-tool show-label --dev $dev | jq -r '.[].ready' > ready
ceph-bluestore-tool show-label --dev $dev | jq -r '.[].require_osd_release' > require_osd_release
echo bluestore > type
echo $osd_id > whoami
# 将bluestore导出到rocksdb中:
ceph-objectstore-tool --data-path . --no-mon-config --op update-mon-db --mon-store-path /var/lib/ceph/$fsid/mon.$node
}
ls -l /dev/mapper | fgrep ceph-- | egrep -o 'dm-[0-9]+$' | while read line ; do
recover /dev/$line
done
样例输出:
osd.0 : 0 osdmaps trimmed, 837 osdmaps added.
osd.4 : 0 osdmaps trimmed, 0 osdmaps added.
osd.5 : 0 osdmaps trimmed, 0 osdmaps added.
osd.3 : 0 osdmaps trimmed, 0 osdmaps added.
osd.2 : 0 osdmaps trimmed, 0 osdmaps added.
osd.1 : 0 osdmaps trimmed, 0 osdmaps added.
复原认证信息:
cd /var/lib/ceph/$fsid
cat /etc/ceph/ceph.client.admin.keyring mon.$node/keyring mgr.$node.*/keyring > admin.keyring
ceph-authtool admin.keyring -n client.admin \
--cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'
ceph-authtool admin.keyring -n mgr.* \
--cap mon 'allow profile mgr' --cap osd 'allow *' --cap mds 'allow *'
ceph-monstore-tool ./mon.$node$ rebuild -- --keyring admin.keyring --mon-ids $mon_ip --mon-nodes $node
修复权限问题:
chown -R 167:167 /var/lib/ceph/$fsid/mon.*
chown -R 167:167 /var/lib/ceph/$fsid/osd.*
构建osd daemon:
systemctl start ceph.target
#ceph mgr module enable cephadm
#ceph mgr module enable dashboard
#ceph mgr module enable alerts
#ceph dashboard create-self-signed-cert
for i in 0 1 2 3 ; do
cd /var/lib/ceph/$fsid/osd.$i
cephadm deploy --name osd.$i --fsid $fsid --osd-fsid $(cat fsid)
done
# 有时候osd.1不能正常启动。需要重试一下。
然后Image不用恢复。因为默认从rbd的pool中获取元数据。直接就可以用了,我们挂载上去看看,步骤参见 通过RBD挂载到Linux文件夹下
重新挂载之后文件读写正常。
幕后:失败的尝试
找到办法可以仅从OSD复原出整个集群: https://docs.ceph.com/en/quincy/rados/troubleshooting/troubleshooting-mon/#recovery-using-osds
但是要求osd的rocksdb数据要有。怎么办呢?定时同步?不知道多久更新一次啊。看了一下里面的内容,通常不会变化。所以一种办法是定期备份这些元数据。另一种办法是构建多个mon节点。
ms=/root/mon-store
mkdir $ms
从元数据备份中恢复出集群。步骤和搭建新集群相同,区别在于新建集群时指定集群ID:
sudo cephadm bootstrap --mon-ip 192.168.31.10 --fsid 43cdbb7a-3bac-11ed-91d4-834ed8ea0c71 --mgr-id qwohcg
执行恢复:
#关掉系统
systemctl stop ceph.target
#将备份文件复制来
cd /var/lib/ceph
mv 43cdbb7a-3bac-11ed-91d4-834ed8ea0c71 43cdbb7a-3bac-11ed-91d4-834ed8ea0c71.bak
cp -Rd /mnt/old2/var/lib/ceph/43cdbb7a-3bac-11ed-91d4-834ed8ea0c71 ./
#启动系统
systemctl start ceph.target
系统起不来,秒退。可能是keyring不对:
cp /mnt/old2/etc/ceph/* /etc/ceph/
还是秒退。貌似mgrID不对。重建集群:
#重建集群
sudo cephadm rm-cluster --fsid 43cdbb7a-3bac-11ed-91d4-834ed8ea0c71 --force
sudo cephadm bootstrap --mon-ip 192.168.31.10 --fsid 43cdbb7a-3bac-11ed-91d4-834ed8ea0c71 --mgr-id qwohcg
#关掉系统
systemctl stop ceph.target
#将备份文件复制来
cd /var/lib/ceph
mv 43cdbb7a-3bac-11ed-91d4-834ed8ea0c71 43cdbb7a-3bac-11ed-91d4-834ed8ea0c71.bak
cp -Rdv /mnt/old2/var/lib/ceph/43cdbb7a-3bac-11ed-91d4-834ed8ea0c71 ./
cp -v /mnt/old2/etc/ceph/* /etc/ceph/
chown -R cpc:cpc /var/lib/ceph
#启动系统
systemctl start ceph.target
健康状态是mgr没有启动。数据状态都健康:
root@node0:/home/cpc# ceph -s
cluster:
id: 43cdbb7a-3bac-11ed-91d4-834ed8ea0c71
health: HEALTH_WARN
no active mgr
mon node0 is low on available space
22 daemons have recently crashed
services:
mon: 1 daemons, quorum node0 (age 6m)
mgr: no daemons active (since 6m)
mds: myfs:1 {0=myfs.node0.gxjtfo=up:active(laggy or crashed)}
osd: 6 osds: 6 up (since 5h), 6 in (since 5h)
data:
pools: 4 pools, 97 pgs
objects: 197 objects, 695 MiB
usage: 7.2 GiB used, 12 TiB / 12 TiB avail
pgs: 97 active+clean
应该是mgr名字弄错了,正确的应该是 node0.qwohcg。
重新试一下:
#重建集群
sudo cephadm rm-cluster --fsid 43cdbb7a-3bac-11ed-91d4-834ed8ea0c71 --force
sudo cephadm bootstrap --mon-ip 192.168.31.10 --fsid 43cdbb7a-3bac-11ed-91d4-834ed8ea0c71 --mgr-id node0.qwohcg
#关掉系统
systemctl stop ceph.target
docker ps
#将备份文件复制来
cd /var/lib
mv ceph ceph.bak.$(date +%s)
cp -Rdvp /mnt/old2/var/lib/ceph ./
cp -Rdvp /mnt/old2/etc/ceph/* /etc/ceph/
cp -Rdvp /mnt/old2/root/.ssh /root
#启动系统
systemctl start ceph.target
osd没权限,手动添加权限:
ceph auth rm osd.0
ceph auth get-or-create osd.0 mon 'allow profile osd' mgr 'allow profile osd' osd 'allow *'
加了也不行。给osd加上名字,可以了,应该是用户身份的问题:
cephadm shell --name osd.0 <<EOF
cd /var/lib/ceph/osd/ceph-0
ceph-osd --osd-data . --name osd.0
EOF
手动启动虽然可以,但是机器重启之后osd就找不到了。所以需要想办法修复daemon的问题。
问题根源似乎找到了,是因为ceph用了ssh-key,在/etc/ceph/ceph.pub。而ssh-key在系统重装之后发生了变化。因此把之前的key复制来试一下:
cp -Rdvp /mnt/old2/root/.ssh /root
还是不行。研究一下daemon为什么不起来:
不知道从哪里开始研究。日志也没有。先手动起来吧:
for i in 2 3 4 ; do
cephadm shell --name osd.$i ceph-osd -d --osd-data /var/lib/ceph/osd/ceph-$i --name osd.$i &
sleep 1
done
挂载文件系统:
sudo su
#启动MDS
ceph orch reconfig mds.myfs.node0.gxjtfo
cephadm shell --name mds.myfs.node0.gxjtfo ceph-mds --name mds.myfs.node0.gxjtfo -d &
#挂载
#安装samba
apt update
apt install samba
vi /etc/samba/smb.conf
cat <<EOF
#修改内容
usershare allow guests = no
[CephFS]
comment = Samba over CephFS
path = /mnt/samba
read only = no
browsable = yes
EOF
service smbd restart
ufw allow samba
smbpasswd -a cpc
检查文件是否正确:
image.png最后一次MD5计算结果和之前一致,说明数据成功恢复了。
但是现在集群管理有点问题。服务不会自动启动。需要排查原因。虽然数据有了,但是这个集群不能用。
如何通过快照进一步确保数据不丢?
为了让数据更好的保留,我们每天自动进行快照,保留最近30天快照:
vi /root/ondaily.sh
# 每天生成快照
image=rbd/myimage
rbd snap create ${image}@auto-`date +%Y%m%d`
# 每天自动清理,保留30天快照
rbd snap list ${image}|awk '{print $2}'|egrep -o '^auto-[0-9]{8}$' | sort | head -n -30 | while read line ; do
sleep 60 # 休息一下,防止遇到race condition BUG
rbd snap remove ${image}@${line}
done
crontab -e
30 4 * * * /usr/bin/bash /root/ondaily.sh
下面测试一下快照是否能达到预期效果。我们假设某一天误操作格式化了磁盘:
通过GUI创建快照
service smbd stop
umount /mnt/rbd
mkfs -t ext4 /dev/rbd0p1
mount -t ext4 /dev/rbd0p1 /mnt/rbd
可以看到文件全没了。然后通过快照恢复数据:
umount /mnt/rbd
rbd unmap /dev/rbd0
通过GUI将快照rollback
重新挂载:
rbd map rbd/myimage
mount -t xfs /dev/rbd0p1 /mnt/rbd
系统重启之后文件也都能正常读写,说明通过快照彻底恢复了数据。
如果硬盘损坏过多出现unfound object之后如何处理?
强制创建PG:
ceph osd force-create-pg 3.11 --yes-i-really-mean-it
创建完成之后pool恢复正常了,但是文件系统无法恢复。不展开研究。
Ceph的相关测试
支持文件随机读写
结论优先:兼容所有POSIX文件操作。
Ceph作为一款存储引擎,要确保基本的文件操作都能支持。比如文件的读写、移动、随机读写。
之所以做这个测试是因为一些存储引擎比如HDFS、Ceph Ganesha不支持随机写。
- 在这个文件夹里编译一个软件
- tar打包
- 中文文件名
- 中文文件夹
- 移动文件夹
- 移动文件
- 随机写入
- 随机读取
import java.io.RandomAccessFile;
import java.util.Random;
public class Main {
public static void main(String[] argv) throws Exception {
RandomAccessFile f = new RandomAccessFile("test.txt", "rws");
f.seek(16);
f.writeLong(new Random().nextLong());
f.close();
f = new RandomAccessFile("test.txt", "r");
f.seek(16);
System.out.println(f.readLong());
f.close();
}
}
Ceph内部原理
整体架构
image.pngceph-mon
元数据内容分析。先看一下一个普通的集群里面有什么。执行以下命令:
alias kv='ceph-kvstore-tool rocksdb store.db'
kv list | awk '{print $1}' | uniq -c
可以看到整体有以下内容:
19 auth
2 config
12 health
145 kv
2107 logm
4 mds_health
1 mds_metadata
32 mdsmap
24 mgr
1 mgr_command_descs
1 mgr_metadata
102 mgrstat
98 mon_config_key
7 monitor
1 monitor_store
3 monmap
6 osd_metadata
1 osd_pg_creating
397 osdmap
678 paxos
那么每个key代表什么含义呢?
kv get auth 1
(auth, 3)
00000000 01 02 02 0c 00 00 00 00 00 00 00 20 5e 00 00 00 |........... ^...|
00000010 00 00 00 |...|
00000013
value被编码过了。编码格式参见: https://docs.ceph.com/en/latest/dev/encoding/ 。通过工具ceph-dencoder
可以破译这些编码:
# 先修复dencoder的BUG
cd /usr/bin
gzip -d ceph-dencoder.gz
# 先看有哪些类型
ceph-dencoder list_types
# 提取二进制文件
kv get auth 14 out test
# 解码
ceph-dencoder type MonitorDBStore::Transaction import test decode dump_json
Ceph二进制编码系统
Ceph系统在组件之间通信、数据存储时会将数据结构编码成二进制形式再进行传输或者存储。Ceph是如何将一个内存中的struct数据结构序列化成二进制数据流?又是如何将二进制数据流反序列化成内存中的struct数据结构?本小节将会详细介绍。
Ceph元数据编码的基本规则
根据官方的文档 https://docs.ceph.com/en/latest/dev/encoding/ ,标准的序列化、反序列化写法如下:
class AcmeClass
{
int member1 = 0xC0;
std::string member2 = "admin";
void encode(bufferlist &bl)
{
ENCODE_START(1, 1, bl); // 步骤1
::encode(member1, bl); // 步骤2
::encode(member2, bl); // 步骤3
ENCODE_FINISH(bl); // 步骤4
}
void decode(bufferlist::iterator &bl)
{
DECODE_START(1, bl);
::decode(member1, bl);
::decode(member2, bl);
DECODE_FINISH(bl);
}
};
先说结论。encode最终会输出:
步骤1和4输出: 01 01 0D 00 00 00
步骤2输出: C0 00 00 00
步骤3输出: 05 00 00 00 61 64 6D 69 6E
步骤1ENCODE_START(1, 1, bl);
,会在bl中预留一段空白,用于编码结束时回填。第一个参数1表示数据结构的版本号,第二个参数2表示此结构在反序列化时最低能兼容版本号,第三个参数bl用于存放编码的结果。
步骤2encode(member1, bl);
,假设此时member1=0xC0,那么这行语句会把C0 00 00 00
追加到bl。因为member1是int类型,占用4个字节,并且在intel平台上将会按little-endian顺序输出。
步骤3encode(member2, bl);
,假设此时member2="admin",那么这样语句会把05 00 00 00 61 64 6D 69 6E
追加到bl。其中前四个字节05 00 00 00
表示字符串的长度。后面5个字节是字符串的ASCII编码结果。
步骤4ENCODE_FINISH(bl);
不增加额外内容,但会把ENCODE_START预留的空白处填上01 01 0D 00 00 00
追加到bl
。下面是这段二进制数据的解读:
01 表示数据结构的版本号
01 表示此结构在反序列化时最低能兼容版本号
0D 00 00 00 表示此结构的长度,(不包含ENCODE_FINISH时回填的长度)。
具体每个基本类型的编码方式在源代码encoding.h中可以找到。源代码地址: https://github.com/ceph/ceph/blob/6fee777d603aebce492c57b41f3b5760d50ddb07/src/include/encoding.h
通过以上过程就把内存里的一段struct数据结构序列化成了一段二进制数据流。我们知道了序列化的过程之后,反序列化原理大体相同。就不赘述了。
具体例子
我们从ceph-mon的rocksdb中提取出二进制数据,并分析其中包含的内容。首先获取二进制数据,命令如下:
# 先进入ceph环境
cephadm shell
# 先修复dencoder的BUG
cd /usr/bin
gzip -d ceph-dencoder.gz
alias kv='ceph-kvstore-tool rocksdb store.db'
# 先看有哪些类型
ceph-dencoder list_types
# 提取二进制内容
cd /var/lib/ceph/*/mon.*
kv get auth 13 out test
得到了如下内容:
00000000 01 02 02 a7 00 00 00 01 00 00 00 02 00 00 00 9b
00000010 00 00 00 01 01 00 00 00 02 00 00 00 11 00 00 00
00000020 6d 79 66 73 2e 6e 6f 64 65 30 2e 64 64 6c 6f 65
00000030 63 02 ff ff ff ff ff ff ff ff 01 00 9f c7 31 63
00000040 6e a4 22 08 10 00 19 b7 f5 51 33 ed 0c 6a f2 c1
00000050 cf 93 f7 9d 56 84 03 00 00 00 03 00 00 00 6d 64
00000060 73 09 00 00 00 05 00 00 00 61 6c 6c 6f 77 03 00
00000070 00 00 6d 6f 6e 0f 00 00 00 0b 00 00 00 70 72 6f
00000080 66 69 6c 65 20 6d 64 73 03 00 00 00 6f 73 64 1b
00000090 00 00 00 17 00 00 00 61 6c 6c 6f 77 20 72 77 20
000000a0 74 61 67 20 63 65 70 68 66 73 20 2a 3d 2a
000000ae
没有研究下去了。
Ceph 17.2.5 源码编译
为了深入研究ceph,需要下载源码并编译研究。
操作系统: Ubuntu 22.04
下载源码
源代码地址: https://github.com/ceph/ceph/ 通过以下命令下载源码:
git clone https://github.com/ceph/ceph
依赖组件安装
进入代码目录,然后执行以下代码:
./install-deps.sh
如果在国内,由于国情的特殊性,在安装过程中可能会出现网络连接错误。因此需要设置国内apt镜像。具体方法是将/etc/apt/source.list
中的cn.archive.ubuntu.com
全部替换成mirrors.aliyun.com
。
下载submodule的代码
代码库中包含了一堆submodule,需要初始化。进入代码目录,执行以下命令:
git submodule update --init --recursive --progress -j1
由于国情的特殊性,会出现网络连接错误。需要你自己想办法。
通过CLion阅读代码
可以正常阅读。
编译安装
尚未研究。
如何搭建软路由
image.png我们要在软路由上实现网卡1和网卡2的桥接。
第一步配置网桥:
vi /etc/netplan/00-installer-config.yaml
network:
version: 2
renderer: networkd
ethernets:
eno1:
dhcp4: no
enp2s0:
dhcp4: no
bridges:
br0:
dhcp4: yes
interfaces:
- eno1
- enp2s0
其中eno1就是图中的网卡1。enp2s0就是图中的网卡2。这样设置完之后电脑发出去的数据包在软路由接收到只有不会做转发。因此要配上转发规则:
iptables -A FORWARD -i br0 -j ACCEPT
这个配置在下次重启后会失效,因此通过如下方法持久化配置:
iptables-save > /etc/iptables/rules.v4
apt install iptables-persistent
更多功能,比如流量转发,DNS代理,尚未完成。
网友评论