Ceph N的新功能:PG合并和自动调整,扩容自动调整PG和PGM,以及手动调整PG和PGM方案。
从一开始,在Ceph中选择和调整PG数量一直是管理Ceph集群中最令人沮丧的部分之一。 选择适当资源池的指南令人困惑,来源之间不一致,并且经常被警告和例外所包围。 最重要的是,如果选择了错误的值,獭并不总是可以进行纠正,并且性能和/或稳定性会受到影响。 Nautilus之前的Ceph版本中的关键限制是,如果pg_num值太小,则总是可以增加它,但是如果它太高,则不能减小它。
不再!。在Nautilus中,池的pg_num值可以降低。更重要的是,可以启用pg autotuner,该组件允许系统自动进行任何适当的调整,以便用户可以完全忽略pg_num调整的前期技巧。
PG是什么?
RADOS(Ceph的底层存储层)中存储的数据对象被分组到逻辑池中。 池具有复制因子,纠删码方案之类的属性,并且可能具有仅将数据放置在HDD或SSD上的规则。 在内部,根据存储在池中对象名称的哈希值将池分为pg_num placement groups或PG。 因此,每个placement group都是整个池的伪随机slice(切片),shard(分段)或fragment (片段),并且特定PG中的所有对象都被分组并一起存储在同一组存储设备(OSD)上。
对象很少的小池只需要几个PG。大型池(例如,包含集群中存储的大部分数据的池)需要大量PG,以确保数据分布在多个设备上,从而平衡所有OSD上的空间利用率,并在系统负载时提供良好的并行性。
PG太少会对少数存储设备的性能造成瓶颈,而PG过多会使Cave的行为效率低下-在极端情况下甚至不稳定-这是因为管理每个PG需要进行内部跟踪。
标准的经验法则是,我们希望每个OSD大约有100个PG,但要计算出系统中每个池的PG数量-同时考虑复制和纠删码等因素-可能是一项挑战。
PG分裂和合并
自2012年以来,Ceph就一直支持PG“分裂”,使现有PG可以将其内容“拆分”为许多较小的PG,从而增加了池中PG的总数。 这使集群开始时很小,然后随时间增长而扩展。 从Nautilus开始,我们现在还可以将两个现有PG“合并”到一个更大的PG中,从而减少PG的总数。 如果池中的相对数据量随着时间的推移而减少,从而需要或适当地减少了PG,或者整个集群都缩小了,或者所选的PG的初始数量太大,则这很有用。
传统上,当PG数量增加时,PG分裂是一气呵成的。例如,要将池的pg_num值从16调整为64,只需
$ ceph osd pool set foo pg_num 64
- 1
集群将16个PG中的每个PG一次性分割成4个片段。 以前,还需要第二步来调整这些新PG的位置,以便将它们存储在新设备上:
$ ceph osd pool set foo pgp_num 64
- 1
这是移动实际数据的开销最大的部分。从Nautilus开始,第二步不再是必需的:只要pgp_num和pg_num当前匹配,pgp_num就会自动跟踪任何pg_num更改。更重要的是,根据新的target_max_misplace_rate配置选项(默认为0.05或5%),逐步调整pgp_num以迁移数据并(最终)收敛到pg_num,以限制系统上的数据迁移负载。也就是说,默认情况下,Ceph会尝试让不超过5%的数据处于“错位”状态,并排队等待迁移,从而限制对客户端工作负载的影响。
PG合并的工作方式与分裂类似,不同之处在于内部pg_num值总是每次一次减少一个PG。 合并是一个更为复杂和精细的过程,需要将PG的IO暂停几秒钟,并且一次合并一个PG可使系统将影响最小化并简化整个过程。 当使用以下命令减少foo池的pg_num时,将设置一个内部pg_num_target值,该值指示pg_num的所需值,并且集群会慢慢收敛于该值。 例如,要将pool foo的PG从64降低到4,
$ ceph osd pool set foo pg_num 4
$ ceph osd pool ls detail | grep foo
pool 1 'foo' replicated size 3 min_size 2 crush_rule 0 object_hash rjenkins pg_num 63 pgp_num 62 pg_num_target 4 pgp_num_target 4 autoscale_mode warn last_change 391 lfor 0/391/389 flags hashpspool stripe_width 0
- 1
- 2
- 3
#查看状态
ceph balancer status
#计算当前集群得分
ceph balancer eval
#选择模式,crush-compat 模式执行命令的数据迁移过程,相应pg会处于active+remapped+backfill状态,pg重新映射且数据回填之中,内部osd之间数据的读写过程,并不会影响客户端的读写操作。
ceph balancer mode crush-compat
PG auto-scaler
调整pg_num值的能力是向前迈出的关键一步,但它并未解决PG调整问题,对于大多数用户而言,这似乎是不可思议的事情。 Nautilus包括一个称为pg_autoscaler的新管理器模块,该模块允许集群考虑每个池中实际存储(或预期要存储)的数据量,并自动选择适当的pg_num值。
由于为pg_autoscaler是新的,因此需要在Nautilus中显式启用它:
$ ceph mgr module enable pg_autoscaler
- 1
autoscaler是按每个池配置的,可以在以下几种模式下运行:
- 警告(warn) 模式:如果建议的pg_num值与当前值相差太大,则会发出健康警告。 这是新池和现有池的默认设置。
- 启用(on) 模式:无需任何管理员交互即可自动调整池pg_num。
- 禁用(off) 模式:还可以为任何给定池关闭autoscaler,让管理员像以前一样手动管理pg_num。
要为特定池启用autoscale,
$ ceph osd pool set foo pg_autoscale_mode on
- 1
启用后,可以通过CLI查询所有池的当前状态和建议的调整。 例如,在我们的实验集群上,我们有:
$ ceph osd pool autoscale-status
POOL SIZE TARGET SIZE RATE RAW CAPACITY RATIO TARGET RATIO PG_NUM NEW PG_NUM AUTOSCALE
device_health_metrics 18331k 3.0 431.3T 0.0000 1 warn
default.rgw.buckets.non-ec 0 3.0 431.3T 0.0000 8 warn
default.rgw.meta 2410 3.0 431.3T 0.0000 8 warn
default.rgw.buckets.index 38637k 3.0 431.3T 0.0000 8 warn
default.rgw.control 0 3.0 431.3T 0.0000 8 warn
default.rgw.buckets.data 743.5G 3.0 431.3T 0.0050 32 on
.rgw.root 1113 3.0 431.3T 0.0000 8 warn
djf_tmp 1169G 3.0 431.3T 0.0079 4096 32 off
libvirt-pool 2048M 3.0 431.3T 0.0000 3000 4 off
data 66692G 3.0 431.3T 0.4529 4096 warn
default.rgw.log 8146k 3.0 431.3T 0.0000 8 warn
metadata 54050M 4.0 431.3T 0.0005
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
您会注意到,大部分数据位于存储所有QA结果的CephFS的“元数据”和“数据”池中。大多数其他池是各种测试遗留下来的,(大部分)是空的。
让我们看一下每一列:
- SIZE列仅报告存储在池中的数据总量(以字节为单位)。 这包括对象数据和omap键/值数据。
- TARGET SIZE报告有关池的预期大小的任何管理员输入。如果池刚刚创建,则最初不会存储任何数据,但管理员通常对最终将存储多少数据有所了解。如果提供,则使用此值或实际大小中的较大者来计算池的理想PG数。
- RATE值是原始存储占用与存储的用户数据的比率,对于复本桶(bucket)来说,它只是一个复制因子,对于纠删码池来说,它是一个(通常)较小的比率。
- RAW CAPACITY是池由CRUSH映射到的存储设备上的原始存储容量。
- RATIO是池占用的总存储空间的比率。
- TARGET RATIO是管理员提供的值,类似于TARGET SIZE,它表示用户预计池将占用集群总存储的多大部分。
- 最后,PG_NUM是池的当前PG数量,而NEW PG_NUM(如果存在)是建议值。
- AUTOSCALE列指示池的模式,可以是禁用、警告或启用。
建议的值考虑了几个输入,包括池占用(或预期占用)的整体群集的比例、池分布在哪些OSD上,以及每个OSD的目标PG数(由mon_target_pg_per_osd配置选项定义,默认为100)。autoscaler将始终选择一个2的幂的pg_num值,因为这对于Ceph而言效率更高(这意味着同一池中的所有PG的大小都大致相同),并且只有当建议的值大于实际值的三倍时,autoscaler才会建议更改pg_num。这意味着,在大多数情况下,不断增长的池的pg_num值在每次更改时都会跃升4倍,并且除非其大小有非常显著的变化,否则将倾向于保持相同的值。
首次设置集群时,通常设置池的target ratio很有帮助,以便autoscaler可以做出良好的初始决策。 例如,如果集群的主要用途是用于块存储,则可以将rbd池的target ratio设置为.8并启用PG auto-scaling:
$ ceph osd pool create foo 1
$ rbd pool init foo
$ ceph osd pool set foo target_size_ratio .8 #表示此pool占整体空间的80%
$ ceph osd pool set foo pg_autoscale_mode on
- 1
- 2
- 3
- 4
此时,集群将自行选择一个pg_num并将其应用于后台。
查看ceph 详细的pgm 和pg信息
ceph osd pool ls detail
您可以使用以下命令控制将pg_autoscale_mode用于新创建的池
$ ceph config set global osd_pool_default_autoscale_mode <mode>
- 1
最后扩容完后使用:
ceph osd pool set volumes pg_num_min 1024
ceph osd pool set volumes pg_num_max 1024
固定池的pgm大小,防止其随意伸缩。
我可以完全放手吗?
是。如果您为新池启用自动扩展,并在现有池上启用它,则随着数据存储在集群中,系统将向上扩展PG。
这种方法的唯一问题是在存储数据后调整PG计数会在集群中移动数据,这很昂贵。 根据经验,如果创建了一个空池,然后将其填充以占用集群的所有可用空间,那么写入的所有数据将在写入后大约移动一次。 这不是理想的选择-最好在创建池时提供target ratio或target bytes值,以便选择适当的初始PG计数-但无知的开销至少是有限且合理的。
手动重平衡方案:
Ceph Balancer
从Luminous 开始,Ceph 新增的了balancer ,可以将PG 在各个OSD上自动迁移,已达到均衡的效果。推荐使用。
1)查看balancer 模块状态
ceph mgr module ls # 查看各个模块的状态
ceph balancer status
2)启用balancer 模块(默认enable)
ceph mgr module enable balancer
3)启用均衡(默认active 是false)
ceph balancer on
4)设置模式(修改PG mapping)
ceph balancer mode upmap
或设置模式(修改weight)
ceph balancer mode crush-compat
其中upmap 模式设置兼容版本
ceph osd set-require-min-compat-client luminous
Luminous 需要手动设置均衡计划?
ceph balancer eval #对集群所有pool进行权重调整计算,针对特定pool 采用 ceph balancer eval <POOL_NAME>
ceph balancer optimize plan2 #生成一个调优配置,或指定存储池 ceph balancer optimize plan2 default.rgw.buckets.data
ceph balancer eval plan2 #执行调优计算
ceph balancer show plan2 #查看调优计算最终的结果
ceph balancer execute plan2 #根据上面模拟计算的结果,执行最终的权重调整
5)查看调整结果
ceph osd df
6)关闭自动调整
ceph balancer off
ceph balancer status
OSD PG 数统计脚本:包含osd pool的排序,包含osd的排序,输出平均pg数目,输出最大的osd编号,输出最大超过平均值的百分比,输出最少pg的osd编号,输出最小低于平均值的百分比,
用于辅助查看集群各个OSD 的PG 分布情况,参考武汉-磨渣的博客《查询osd上的pg数》
ceph pg dump | awk ‘
/^PG_STAT/ { col=1; while($col!=”UP”) {col++}; col++ }
/^[0-9a-f]+\.[0-9a-f]+/ { match($0,/^[0-9a-f]+/); pool=substr($0, RSTART, RLENGTH); poollist[pool]=0;
up=$col; i=0; RSTART=0; RLENGTH=0; delete osds; while(match(up,/[0-9]+/)>0) { osds[++i]=substr(up,RSTART,RLENGTH); up = substr(up, RSTART+RLENGTH) }
for(i in osds) {array[osds[i],pool]++; osdlist[osds[i]];}
}
END {
printf(“\n”);
slen=asorti(poollist,newpoollist);
printf(“pool :\t”);for (i=1;i<=slen;i++) {printf(“%s\t”, newpoollist[i])}; printf(“| SUM \n”);
for (i in poollist) printf(“——–“); printf(“—————-\n”);
slen1=asorti(osdlist,newosdlist)
delete poollist;
for (j=1;j<=slen;j++) {maxpoolosd[j]=0};
for (j=1;j<=slen;j++) {for (i=1;i<=slen1;i++){if (array[newosdlist[i],newpoollist[j]] >0 ){minpoolosd[j]=array[newosdlist[i],newpoollist[j]] ;break } }};
for (i=1;i<=slen1;i++) { printf(“osd.%i\t”, newosdlist[i]); sum=0;
for (j=1;j<=slen;j++) { printf(“%i\t”, array[newosdlist[i],newpoollist[j]]); sum+=array[newosdlist[i],newpoollist[j]]; poollist[j]+=array[newosdlist[i],newpoollist[j]];if(array[newosdlist[i],newpoollist[j]] != 0){poolhasid[j]+=1 };if(array[newosdlist[i],newpoollist[j]]>maxpoolosd[j]){maxpoolosd[j]=array[newosdlist[i],newpoollist[j]];maxosdid[j]=newosdlist[i]};if(array[newosdlist[i],newpoollist[j]] != 0){if(array[newosdlist[i],newpoollist[j]]<=minpoolosd[j]){minpoolosd[j]=array[newosdlist[i],newpoollist[j]];minosdid[j]=newosdlist[i]}}}; printf(“| %i\n”,sum)} for (i in poollist) printf(“——–“); printf(“—————-\n”);
slen2=asorti(poollist,newpoollist);
printf(“SUM :\t”); for (i=1;i<=slen;i++) printf(“%s\t”,poollist[i]); printf(“|\n”);
printf(“Osd :\t”); for (i=1;i<=slen;i++) printf(“%s\t”,poolhasid[i]); printf(“|\n”);
printf(“AVE :\t”); for (i=1;i<=slen;i++) printf(“%.2f\t”,poollist[i]/poolhasid[i]); printf(“|\n”);
printf(“Max :\t”); for (i=1;i<=slen;i++) printf(“%s\t”,maxpoolosd[i]); printf(“|\n”);
printf(“Osdid :\t”); for (i=1;i<=slen;i++) printf(“osd.%s\t”,maxosdid[i]); printf(“|\n”);
printf(“per:\t”); for (i=1;i<=slen;i++) printf(“%.1f%\t”,100*(maxpoolosd[i]-poollist[i]/poolhasid[i])/(poollist[i]/poolhasid[i])); printf(“|\n”);
for (i=1;i<=slen2;i++) printf(“——–“);printf(“—————-\n”);
printf(“min :\t”); for (i=1;i<=slen;i++) printf(“%s\t”,minpoolosd[i]); printf(“|\n”);
printf(“osdid :\t”); for (i=1;i<=slen;i++) printf(“osd.%s\t”,minosdid[i]); printf(“|\n”);
printf(“per:\t”); for (i=1;i<=slen;i++) printf(“%.1f%\t”,100*(minpoolosd[i]-poollist[i]/poolhasid[i])/(poollist[i]/poolhasid[i])); printf(“|\n”);
}’
———————————–
Ceph OSDs 间的数据均衡
https://blog.51cto.com/dengchj/2944207