puppet-ceph
本节作者:薛飞扬
建议阅读时间 2小时
Ceph是一个分布式存储系统,诞生于2004年,是最早致力于开发下一代高性能分布式文件系统的项目。随着云计算的发展,ceph乘上了OpenStack的春风,进而成为了开源社区受关注较高的项目之一。
Ceph基本结构
自下向上,可以将Ceph系统分为四个层次:
1.基础存储系统RADOS(Reliable, Autonomic, Distributed Object Store,即可靠的、自动化的、分布式的对象存储)
RADOS本身也是分布式存储系统,CEPH所有的存储功能都是基于RADOS实现,RADOS由大量的存储设备节点组成,每个节点拥有自己的硬件资源(CPU、内存、硬盘、网络),并运行着操作系统和文件系统。
2.基础库librados
这一层的功能是对RADOS进行抽象和封装,并向上层提供API,以便直接基于RADOS(而不是整个Ceph)进行应用开发。
3.高层应用接口
这一层包括了三个部分:RADOS GW(RADOS Gateway)、 RBD(Reliable Block Device)和Ceph FS(Ceph File System),其作用是在librados库的基础上提供抽象层次更高、更便于应用或客户端使用的上层接口。
其中,RADOS GW是一个提供与Amazon S3和Swift兼容的RESTful API的gateway,以供相应的对象存储应用开发使用。RADOS GW提供的API抽象层次更高,但功能则不如librados强大。因此,开发者应针对自己的需求选择使用。
RBD则提供了一个标准的块设备接口,常用于在虚拟化的场景下为虚拟机创建volume。目前,Red Hat已经将RBD驱动集成在KVM/QEMU中,以提高虚拟机访问性能。
Ceph FS是一个POSIX兼容的分布式文件系统。由于还处在开发状态,因而Ceph官网并不推荐将其用于生产环境中
4.应用层
这一层就是不同场景下对于Ceph各个应用接口的各种应用方式,例如基于librados直接开发的对象存储应用,基于RADOS GW开发的对象存储应用,基于RBD实现的云硬盘等等。
Ceph基本组件
- Osd
用于集群中所有数据与对象的存储。处理集群数据的复制、恢复、回填、再均衡。并向其他osd守护进程发送心跳,然后向Mon提供一些监控信息。 - Monitor
监控整个集群的状态,维护集群的cluster MAP二进制表,保证集群数据的一致性。ClusterMAP描述了对象块存储的物理位置,以及一个将设备聚合到物理位置的桶列表。 - MDS(可选)
为Ceph文件系统提供元数据计算、缓存与同步。在ceph中,元数据也是存储在osd节点中的,mds类似于元数据的代理缓存服务器。MDS进程并不是必须的进程,只有需要使用CEPHFS>时,才需要配置MDS节点。
CRUSH机制
引入CRUSH的目的
为了去中心化,避免单点故障,Ceph使用了CRUSH(Controlled Replication Under Scalable Hashing)算法,客户端根据它来计算数据被写到哪里去,以及从哪里读取所需数据。
理解CRUSH机制
对Ceph集群的一个读写操作,客户端首先访问Ceph monitor来获取cluster map 的一分副本,它包含五个map,分别是monitor map、OSD map 、 MDS map、CRUSH map 和PG map.
客户端通过这些cluster map知晓Ceph集群的状态和配置。通过CRUSH算法计算出或获取数据的主(primary)、次(secondary)和再次(tertiary)OSD的位置。
所有这些计算操作都是在客户端完成的,因此它们不会影响Ceph集群服务器端的性能。
每个map的简介如下:
- monitor map: 它包含监视节点端到端的信息,包括Ceph集群ID、monitor节点名称(hostname)、IP地址和端口号等。它还保存自monitor map被创建以来的最新版本号(epoch:每种map都维护着其历史版本,每个版本被称为一个epoch,epoch是一个单调递增的序号),以及最后修改时间等。
查看monitor map 命令: ceph mon dump - OSD map: 它保存一些常用的信息,包括集群ID、OSD map自创建以来的最新版本号(epoch)及其最后修改时间,以及存储池相关的信息,包括存储池名称、ID、类型、副本级别(replication level)和PG。它还保存着OSD的信息,比如数量、状态、权重、最后清理间隔(last clean interval)以及OSD节点的信息。
查看OSD map命令:ceph osd dump - PG map:它保存的信息包括PG的版本、时间戳、OSD map的最新版本号(epoch)、容量已满百分比,容量将满百分比等。它还记录了每个PG的ID、对象数量、状态、状态时间戳、up OSD sets、acting OSD sets,以及清理的信息。
查看PG map 命令: ceph pg dump - CRUSH map: 它保存的信息包括集群设备列表、bucket列表、故障域分层结构、故障域定义的规则等。
查看CRUSH map 命令: ceph osd crush dump - MDS map:它保存的信息包括MDS map当前版本号(epoch)、MDS map的创建和修改时间、数据和元数据存储池的ID、集群MDS数量以及MDS状态。
查看MDS map 命令: ceph mds dump
CRUSH Map的内容
CRUSH算法通过计算数据存储位置来确定如何存储和检索。 CRUSH 授权 Ceph 客户端直接连接 OSD ,而非通过一个中央服务器或经纪人。数据存储、检索算法的使用,使 Ceph 避免了单点故障、性能瓶颈、和伸缩的物理限制。
CRUSH图包含 OSD 列表、把设备汇聚为物理位置的“桶”列表和指示 CRUSH 如何复制存储池里的数据的规则列表。
CRUSH图主要有 4 个主要段落:
1.设备
设备的格式:
#devices
device {num} {osd.name}
2.桶类型: 定义了 CRUSH 分级结构里要用的桶类型( types )
如:
# types
type 0 osd
type 1 host
type 2 chassis
type 3 rack
type 4 row
type 5 pdu
type 6 pod
type 7 room
type 8 datacenter
type 9 region
type 10 root
3.桶例程: 定义了桶类型后,还必须声明主机的桶类型、以及规划的其它故障域。
格式:
[bucket-type] [bucket-name] {
id [a unique negative numeric ID]
weight [the relative capacity/capability of the item(s)]
alg [the bucket type: uniform | list | tree | straw ]
hash [the hash type: 0 by default]
item [item-name] weight [weight]
}
Ceph 支持四种桶,每种都是性能和组织简易间的折衷。如果你不确定用哪种桶,我们建议 straw 。关于桶类型的详细讨论请参考http://docs.ceph.org.cn/rados/operations/crush-map
各个桶都用了一种哈希算法,当前 Ceph 仅支持 rjenkins1 ,输入 0 表示哈希算法设置为 rjenkins1 。
例子如下:
定义的桶例程为:
host node1 {
id -1
alg straw
hash 0
item osd.0 weight 1.00
item osd.1 weight 1.00
}
host node2 {
id -2
alg straw
hash 0
item osd.2 weight 1.00
item osd.3 weight 1.00
}
rack rack1 {
id -3
alg straw
hash 0
item node1 weight 2.00
item node2 weight 2.00
}
此例中,机柜桶不包含任何 OSD ,它只包含低一级的主机桶、以及其内条目的权重之和
4.规则: 由选择桶的方法组成。
规则格式如下:
rule <rulename> {
ruleset <ruleset>
type [ replicated | erasure ]
min_size <min-size>
max_size <max-size>
step take <bucket-type>
step [choose|chooseleaf] [firstn|indep] <N> <bucket-type>
step emit
}
各字段含义如下:
4.1ruleset
描述: 区分一条规则属于某个规则集的手段。给存储池设置规则集后激活。
目的: 规则掩码的一个组件。
类型: Integer
是否必需: Yes
默认值: 0
4.2type
描述: 为硬盘(复制的)或 RAID 写一条规则。
目的: 规则掩码的一个组件。
类型: String
是否必需: Yes
默认值: replicated
合法取值: 当前仅支持 replicated 和 erasure
4.3min_size
描述: 如果一个归置组副本数小于此数, CRUSH 将不应用此规则。
类型: Integer
目的: 规则掩码的一个组件。
是否必需: Yes
默认值: 1
4.4max_size
描述: 如果一个归置组副本数大于此数, CRUSH 将不应用此规则。
类型: Integer
目的: 规则掩码的一个组件。
是否必需: Yes
默认值: 10
4.5step take
描述: 选取桶名并迭代到树底。
目的: 规则掩码的一个组件。
是否必需: Yes
实例: step take default
4.6step choose firstn {num} type {bucket-type}
描述: 选取指定类型桶的数量,这个数字通常是存储池的副本数(即 pool size )。
如果 {num} == 0 选择 pool-num-replicas 个桶(所有可用的);
如果 {num} > 0 && < pool-num-replicas 就选择那么多的桶;
如果 {num} < 0 它意为 pool-num-replicas - {num} 。
目的: 规则掩码的一个组件。
先决条件: 跟在 step take 或 step choose 之后。
实例: step choose firstn 1 type row
4.7step chooseleaf firstn {num} type {bucket-type}
描述: 选择 {bucket-type} 类型的一堆桶,并从各桶的子树里选择一个叶子节点。集合内桶的数量通常是存储池的副本数(即 pool size )。
如果 {num} == 0 选择 pool-num-replicas 个桶(所有可用的);
如果 {num} > 0 && < pool-num-replicas 就选择那么多的桶;
如果 {num} < 0 它意为 pool-num-replicas - {num} 。
目的: 规则掩码的一个组件。 它的使用避免了通过两步来选择一设备。
先决条件: Follows step take or step choose.
4.8step emit
描述: 输出当前值并清空堆栈。通常用于规则末尾,也适用于相同规则应用到不同树的情况。
目的: 规则掩码的一个组件。
先决条件: Follows step choose.
实例: step emit
较新版本的 CRUSH (从 0.48 起)为了解决一些遗留值导致几个不当行为,在最前面加入了一些参数值。
一个例子如下:
# begin crush map
tunable choose_local_tries 0 #本地重试次数。以前是 2 ,最优值是 0 。
tunable choose_local_fallback_tries 0 #以前 5 ,现在是 0
tunable choose_total_tries 50 #选择一个条目的最大尝试次数。以前 19 ,后来的测试表明,对典型的集群来说 50 更合适。最相当大的集群来说,更大的值也许必要。
tunable chooseleaf_descend_once 1 #是否重递归叶子选择,或只试一次、并允许最初归置组重试。以前默认 0 ,最优为 1 。
tunable straw_calc_version 1
# devices
device 0 osd.0
device 1 osd.1
device 2 osd.2
# types
type 0 osd
type 1 host
type 2 chassis
type 3 rack
type 4 row
type 5 pdu
type 6 pod
type 7 room
type 8 datacenter
type 9 region
type 10 root
# buckets
host server-250 {
id -2 # do not change unnecessarily
# weight 2.160
alg straw
hash 0 # rjenkins1
item osd.0 weight 0.720
item osd.1 weight 0.720
item osd.2 weight 0.720
}
root default {
id -1 # do not change unnecessarily
# weight 2.160
alg straw
hash 0 # rjenkins1
item server-250 weight 2.160
}
# rules
rule replicated_ruleset {
ruleset 0
type replicated
min_size 1
max_size 10
step take default
step chooseleaf firstn 0 type osd
step emit
}
# end crush map
如何编辑CRUSH map
1.从任意一个monitor节点上获取CRUSH map
ceph osd getcrushmap -o crushmap
2.反编译它,让它成为我们能阅读的格式
crushtool -d crushmap -o crushmap.txt
3.修改相应的内容
vim crushmap.txt
4.重新编译
crushtool -c crushmap.txt -o newcrushmap
5.将重新编译的CRUSH map注入Ceph集群
ceph osd setcrushmap -i newcrushmap
puppet-ceph部署
Ceph集群有多种部署方式,诸如Ansible、Puppet和Chef等配置管理工具都可以按照你喜欢的方式来安装和部署Ceph集群。这里我们只介绍Puppet的部署方式。
准备工作
在puppet master module目录下下载puppet-ceph模块,下载地址为https://github.com/openstack/puppet-ceph/tree/stable/hammer
openstack/puppet-ceph 使用ceph版本为hammer
此例中,我们部署一个mon节点,两个osd节点,hostname分别为:test-ceph-1,test-ceph-2,test-ceph-3
部署指南
各节点加载的类:
node /^test-ceph-1$/ {
$ceph_pools = ['test']
ceph::pool { $ceph_pools: }
class { '::ceph::profile::mon': }
}
node /^test-ceph-[2-3]$/ {
class { '::ceph::profile::osd': }
}
传的hieradata:
common/ceph.yaml:
---
######## Ceph
ceph::profile::params::release: 'hammer'
######## Ceph.conf
ceph::profile::params::fsid: '4b5c8c0a-ff60-454b-a1b4-9747aa737d19'
ceph::profile::params::authentication_type: 'cephx'
ceph::profile::params::mon_initial_members: 'test-ceph-1'
ceph::profile::params::mon_host: '10.0.86.23:6789'
ceph::profile::params::osd_pool_default_size: '2'
######## Keys
ceph::profile::params::mon_key: 'AQATGHJTUCBqIBAA7M2yafV1xctn1pgr3GcKPg=='
ceph::profile::params::client_keys:
'client.admin':
secret: 'AQATGHJTUCBqIBAA7M2yafV1xctn1pgr3GcKPg=='
mode: '0600'
cap_mon: 'allow *'
cap_osd: 'allow *'
cap_mds: 'allow *'
'client.bootstrap-osd':
secret: 'AQATGHJTUCBqIBAA7M2yafV1xctn1pgr3GcKPg=='
keyring_path: '/var/lib/ceph/bootstrap-osd/ceph.keyring'
cap_mon: 'allow profile bootstrap-osd'
test-ceph-2.yaml:
ceph::profile::params::osds:
'/dev/sdb':
journal: ''
test-ceph-3.yaml:
ceph::profile::params::osds:
'/dev/sdc':
journal: ''
参数说明
puppet会执行 ceph-disk prepare /dev/sdc ,如果journal为空,它会把自动把这块盘分成两个分区,一个为ceph data ,一个为ceph journal。journal分区大小默认为5G,剩下的
都分给ceph data.
Journal的作用类似于mysql innodb引擎中的事物日志系统。当有突发的大量写入操作时,ceph可以先把一些零散的,随机的IO请求保存到缓存中进行合并,然后再统一向内核发起IO请求。journal的io是非常密集的,很大程度上也损耗了硬件的io性能,所以通常在生产环境中,推荐使用ssd来单独存储journal文件以提高ceph读写性能。
journal也可以使用单独的数据盘,只需要在hieradata中传递相应的设备名即可。
openstack/puppet-ceph 传osds参数不支持wwn的方式,因为ceph-disk当前不支持使用wwn来作为磁盘标识的输入参数。
如果重启了mon节点,需要执行:
service ceph start mon.server-250
如果重启了osd节点,需要执行:
ceph-disk activate-all
activate-all 靠 /dev/disk/by-parttype-uuid/$typeuuid.$uuid 发现所有分区
parttype-uuid 是在执行activate-prepare 时生成的。通过parttypeuuid,在本机插拔osd盘完全不会导致故障。
puppet执行过程分析
创建mon的大致过程如下:
1.安装包
package { $::ceph::params::packages :
ensure => $ensure,
tag => 'ceph'
}
.是否开启认证
# [*authentication_type*] Activate or deactivate authentication
# Optional. Default to cephx.
# Authentication is activated if the value is 'cephx' and deactivated
# if the value is 'none'. If the value is 'cephx', at least one of
# key or keyring must be provided.
if $authentication_type == 'cephx' {
ceph_config {
'global/auth_cluster_required': value => 'cephx';
'global/auth_service_required': value => 'cephx';
'global/auth_client_required': value => 'cephx';
'global/auth_supported': value => 'cephx';
}
3.生成mon密钥
cat > ${keyring_path} << EOF
[mon.]
key = ${key}
caps mon = \"allow *\"
EOF
chmod 0444 ${keyring_path}
4.生成/etc/ceph/ceph.client.admin.keyring文件
touch /etc/ceph/${cluster_name}.client.admin.keyring
5.初始化monitor服务,创建done,sysvinit空文件
mon_data=\$(ceph-mon ${cluster_option} --id ${id} --show-config-value mon_data)
if [ ! -d \$mon_data ] ; then
mkdir -p \$mon_data
if ceph-mon ${cluster_option} \
--mkfs \
--id ${id} \
--keyring ${keyring_path} ; then
touch \$mon_data/done \$mon_data/${init} \$mon_data/keyring
else
rm -fr \$mon_data
fi
fi
6.启动mon服务:
service ceph start mon.test-ceph-xue-1
建osd的大致过程如下:
1.安装包
package { $::ceph::params::packages :
ensure => $ensure,
tag => 'ceph'
}
2.是否开启认证
# [*authentication_type*] Activate or deactivate authentication
# Optional. Default to cephx.
# Authentication is activated if the value is 'cephx' and deactivated
# if the value is 'none'. If the value is 'cephx', at least one of
# key or keyring must be provided.
if $authentication_type == 'cephx' {
ceph_config {
'global/auth_cluster_required': value => 'cephx';
'global/auth_service_required': value => 'cephx';
'global/auth_client_required': value => 'cephx';
'global/auth_supported': value => 'cephx';
}
3.创建keyring file
if ! defined(File[$keyring_path]) {
file { $keyring_path:
ensure => file,
owner => $user,
group => $group,
mode => $mode,
require => Package['ceph'],
}
}
4.生成管理员密钥环,生成 client.admin 用户并加入密钥环
ceph-authtool \$NEW_KEYRING --name '${name}' --add-key '${secret}' ${caps}
5.把 client.admin 密钥加入 ceph.mon.keyring
ceph ${cluster_option} ${inject_id_option} ${inject_keyring_option} auth import -i ${keyring_path}"
6.ceph 0.94版本下禁用udev rules,否则可能会导致ceph-disk activate失败
mv -f ${udev_rules_file} ${udev_rules_file}.disabled && udevadm control --reload
7.使用ceph-disk prepare 做预处理
预处理用作 Ceph OSD 的目录、磁盘。它会创建 GPT 分区、给分区打上 Ceph 风格的 uuid 标记、创建文件系统、把此文件系统标记为已就绪、使用日志磁盘的整个分区并新增一>分区。可单独使用,也可由 ceph-deploy 用。
if ! test -b ${data} ; then
mkdir -p ${data}
fi
ceph-disk prepare ${cluster_option} ${data} ${journal}
udevadm settle
8.激活 Ceph OSD
激活 Ceph OSD 。先把此卷挂载到一临时位置,分配 OSD 惟一标识符(若有必要),重挂载到正确位置
ceph-disk activate ${data}
小结
在这里,我们介绍了Ceph的一些基础知识和puppet-ceph搭建过程,本篇文章没有涉及到但是需要我们注意的是,在搭建ceph集群之前,要先对集群的硬件进行合理的规划,包括故障域和潜在的性能问题。
动手练习
- 通过puppet部署了ceph集群之后,在本机插拔osd盘,再执行ceph-disk activate-all命令,观察ceph 集群状态
- 导出ceph 集群的crush map图,理解其含义。
- 学习并使用rbd命令创建、显示、对照(introspect)和移除块设备镜像。