puppet-ceph

  1. Ceph基本结构
  2. Ceph基本组件
  3. CRUSH机制
  4. puppet-ceph部署
  5. puppet执行过程分析
  6. 小结
  7. 动手练习 - 光看不练假把式

本节作者:薛飞扬

建议阅读时间 2小时

Ceph是一个分布式存储系统,诞生于2004年,是最早致力于开发下一代高性能分布式文件系统的项目。随着云计算的发展,ceph乘上了OpenStack的春风,进而成为了开源社区受关注较高的项目之一。

Ceph基本结构

puppet-ceph - 图1

自下向上,可以将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.设备
设备的格式:

  1. #devices
  2. device {num} {osd.name}

2.桶类型: 定义了 CRUSH 分级结构里要用的桶类型( types )
如:

  1. # types
  2. type 0 osd
  3. type 1 host
  4. type 2 chassis
  5. type 3 rack
  6. type 4 row
  7. type 5 pdu
  8. type 6 pod
  9. type 7 room
  10. type 8 datacenter
  11. type 9 region
  12. type 10 root

3.桶例程: 定义了桶类型后,还必须声明主机的桶类型、以及规划的其它故障域。
格式:

  1. [bucket-type] [bucket-name] {
  2. id [a unique negative numeric ID]
  3. weight [the relative capacity/capability of the item(s)]
  4. alg [the bucket type: uniform | list | tree | straw ]
  5. hash [the hash type: 0 by default]
  6. item [item-name] weight [weight]
  7. }

Ceph 支持四种桶,每种都是性能和组织简易间的折衷。如果你不确定用哪种桶,我们建议 straw 。关于桶类型的详细讨论请参考http://docs.ceph.org.cn/rados/operations/crush-map

各个桶都用了一种哈希算法,当前 Ceph 仅支持 rjenkins1 ,输入 0 表示哈希算法设置为 rjenkins1 。

例子如下:

puppet-ceph - 图2

定义的桶例程为:

  1. host node1 {
  2. id -1
  3. alg straw
  4. hash 0
  5. item osd.0 weight 1.00
  6. item osd.1 weight 1.00
  7. }
  8. host node2 {
  9. id -2
  10. alg straw
  11. hash 0
  12. item osd.2 weight 1.00
  13. item osd.3 weight 1.00
  14. }
  15. rack rack1 {
  16. id -3
  17. alg straw
  18. hash 0
  19. item node1 weight 2.00
  20. item node2 weight 2.00
  21. }

此例中,机柜桶不包含任何 OSD ,它只包含低一级的主机桶、以及其内条目的权重之和

4.规则: 由选择桶的方法组成。

规则格式如下:

  1. rule <rulename> {
  2. ruleset <ruleset>
  3. type [ replicated | erasure ]
  4. min_size <min-size>
  5. max_size <max-size>
  6. step take <bucket-type>
  7. step [choose|chooseleaf] [firstn|indep] <N> <bucket-type>
  8. step emit
  9. }

各字段含义如下:

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 起)为了解决一些遗留值导致几个不当行为,在最前面加入了一些参数值。

一个例子如下:

  1. # begin crush map
  2. tunable choose_local_tries 0 #本地重试次数。以前是 2 ,最优值是 0 。
  3. tunable choose_local_fallback_tries 0 #以前 5 ,现在是 0
  4. tunable choose_total_tries 50 #选择一个条目的最大尝试次数。以前 19 ,后来的测试表明,对典型的集群来说 50 更合适。最相当大的集群来说,更大的值也许必要。
  5. tunable chooseleaf_descend_once 1 #是否重递归叶子选择,或只试一次、并允许最初归置组重试。以前默认 0 ,最优为 1 。
  6. tunable straw_calc_version 1
  7. # devices
  8. device 0 osd.0
  9. device 1 osd.1
  10. device 2 osd.2
  11. # types
  12. type 0 osd
  13. type 1 host
  14. type 2 chassis
  15. type 3 rack
  16. type 4 row
  17. type 5 pdu
  18. type 6 pod
  19. type 7 room
  20. type 8 datacenter
  21. type 9 region
  22. type 10 root
  23. # buckets
  24. host server-250 {
  25. id -2 # do not change unnecessarily
  26. # weight 2.160
  27. alg straw
  28. hash 0 # rjenkins1
  29. item osd.0 weight 0.720
  30. item osd.1 weight 0.720
  31. item osd.2 weight 0.720
  32. }
  33. root default {
  34. id -1 # do not change unnecessarily
  35. # weight 2.160
  36. alg straw
  37. hash 0 # rjenkins1
  38. item server-250 weight 2.160
  39. }
  40. # rules
  41. rule replicated_ruleset {
  42. ruleset 0
  43. type replicated
  44. min_size 1
  45. max_size 10
  46. step take default
  47. step chooseleaf firstn 0 type osd
  48. step emit
  49. }
  50. # end crush map

如何编辑CRUSH map

1.从任意一个monitor节点上获取CRUSH map

  1. ceph osd getcrushmap -o crushmap

2.反编译它,让它成为我们能阅读的格式

  1. crushtool -d crushmap -o crushmap.txt

3.修改相应的内容

  1. vim crushmap.txt

4.重新编译

  1. crushtool -c crushmap.txt -o newcrushmap

5.将重新编译的CRUSH map注入Ceph集群

  1. 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

部署指南

各节点加载的类:

  1. node /^test-ceph-1$/ {
  2. $ceph_pools = ['test']
  3. ceph::pool { $ceph_pools: }
  4. class { '::ceph::profile::mon': }
  5. }
  6. node /^test-ceph-[2-3]$/ {
  7. class { '::ceph::profile::osd': }
  8. }

传的hieradata:

common/ceph.yaml:

  1. ---
  2. ######## Ceph
  3. ceph::profile::params::release: 'hammer'
  4. ######## Ceph.conf
  5. ceph::profile::params::fsid: '4b5c8c0a-ff60-454b-a1b4-9747aa737d19'
  6. ceph::profile::params::authentication_type: 'cephx'
  7. ceph::profile::params::mon_initial_members: 'test-ceph-1'
  8. ceph::profile::params::mon_host: '10.0.86.23:6789'
  9. ceph::profile::params::osd_pool_default_size: '2'
  10. ######## Keys
  11. ceph::profile::params::mon_key: 'AQATGHJTUCBqIBAA7M2yafV1xctn1pgr3GcKPg=='
  12. ceph::profile::params::client_keys:
  13. 'client.admin':
  14. secret: 'AQATGHJTUCBqIBAA7M2yafV1xctn1pgr3GcKPg=='
  15. mode: '0600'
  16. cap_mon: 'allow *'
  17. cap_osd: 'allow *'
  18. cap_mds: 'allow *'
  19. 'client.bootstrap-osd':
  20. secret: 'AQATGHJTUCBqIBAA7M2yafV1xctn1pgr3GcKPg=='
  21. keyring_path: '/var/lib/ceph/bootstrap-osd/ceph.keyring'
  22. cap_mon: 'allow profile bootstrap-osd'

test-ceph-2.yaml:

  1. ceph::profile::params::osds:
  2. '/dev/sdb':
  3. journal: ''

test-ceph-3.yaml:

  1. ceph::profile::params::osds:
  2. '/dev/sdc':
  3. 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节点,需要执行:

  1. service ceph start mon.server-250

如果重启了osd节点,需要执行:

  1. ceph-disk activate-all

activate-all 靠 /dev/disk/by-parttype-uuid/$typeuuid.$uuid 发现所有分区

parttype-uuid 是在执行activate-prepare 时生成的。通过parttypeuuid,在本机插拔osd盘完全不会导致故障。

puppet执行过程分析

创建mon的大致过程如下:

1.安装包

  1. package { $::ceph::params::packages :
  2. ensure => $ensure,
  3. tag => 'ceph'
  4. }

.是否开启认证

  1. # [*authentication_type*] Activate or deactivate authentication
  2. # Optional. Default to cephx.
  3. # Authentication is activated if the value is 'cephx' and deactivated
  4. # if the value is 'none'. If the value is 'cephx', at least one of
  5. # key or keyring must be provided.
  6. if $authentication_type == 'cephx' {
  7. ceph_config {
  8. 'global/auth_cluster_required': value => 'cephx';
  9. 'global/auth_service_required': value => 'cephx';
  10. 'global/auth_client_required': value => 'cephx';
  11. 'global/auth_supported': value => 'cephx';
  12. }

3.生成mon密钥

  1. cat > ${keyring_path} << EOF
  2. [mon.]
  3. key = ${key}
  4. caps mon = \"allow *\"
  5. EOF
  6. chmod 0444 ${keyring_path}

4.生成/etc/ceph/ceph.client.admin.keyring文件

  1. touch /etc/ceph/${cluster_name}.client.admin.keyring

5.初始化monitor服务,创建done,sysvinit空文件

  1. mon_data=\$(ceph-mon ${cluster_option} --id ${id} --show-config-value mon_data)
  2. if [ ! -d \$mon_data ] ; then
  3. mkdir -p \$mon_data
  4. if ceph-mon ${cluster_option} \
  5. --mkfs \
  6. --id ${id} \
  7. --keyring ${keyring_path} ; then
  8. touch \$mon_data/done \$mon_data/${init} \$mon_data/keyring
  9. else
  10. rm -fr \$mon_data
  11. fi
  12. fi

6.启动mon服务:

  1. service ceph start mon.test-ceph-xue-1

建osd的大致过程如下:

1.安装包

  1. package { $::ceph::params::packages :
  2. ensure => $ensure,
  3. tag => 'ceph'
  4. }

2.是否开启认证

  1. # [*authentication_type*] Activate or deactivate authentication
  2. # Optional. Default to cephx.
  3. # Authentication is activated if the value is 'cephx' and deactivated
  4. # if the value is 'none'. If the value is 'cephx', at least one of
  5. # key or keyring must be provided.
  6. if $authentication_type == 'cephx' {
  7. ceph_config {
  8. 'global/auth_cluster_required': value => 'cephx';
  9. 'global/auth_service_required': value => 'cephx';
  10. 'global/auth_client_required': value => 'cephx';
  11. 'global/auth_supported': value => 'cephx';
  12. }

3.创建keyring file

  1. if ! defined(File[$keyring_path]) {
  2. file { $keyring_path:
  3. ensure => file,
  4. owner => $user,
  5. group => $group,
  6. mode => $mode,
  7. require => Package['ceph'],
  8. }
  9. }

4.生成管理员密钥环,生成 client.admin 用户并加入密钥环

  1. ceph-authtool \$NEW_KEYRING --name '${name}' --add-key '${secret}' ${caps}

5.把 client.admin 密钥加入 ceph.mon.keyring

  1. ceph ${cluster_option} ${inject_id_option} ${inject_keyring_option} auth import -i ${keyring_path}"

6.ceph 0.94版本下禁用udev rules,否则可能会导致ceph-disk activate失败

  1. mv -f ${udev_rules_file} ${udev_rules_file}.disabled && udevadm control --reload

7.使用ceph-disk prepare 做预处理
预处理用作 Ceph OSD 的目录、磁盘。它会创建 GPT 分区、给分区打上 Ceph 风格的 uuid 标记、创建文件系统、把此文件系统标记为已就绪、使用日志磁盘的整个分区并新增一>分区。可单独使用,也可由 ceph-deploy 用。

  1. if ! test -b ${data} ; then
  2. mkdir -p ${data}
  3. fi
  4. ceph-disk prepare ${cluster_option} ${data} ${journal}
  5. udevadm settle

8.激活 Ceph OSD
激活 Ceph OSD 。先把此卷挂载到一临时位置,分配 OSD 惟一标识符(若有必要),重挂载到正确位置

  1. ceph-disk activate ${data}

小结

在这里,我们介绍了Ceph的一些基础知识和puppet-ceph搭建过程,本篇文章没有涉及到但是需要我们注意的是,在搭建ceph集群之前,要先对集群的硬件进行合理的规划,包括故障域和潜在的性能问题。

动手练习

  1. 通过puppet部署了ceph集群之后,在本机插拔osd盘,再执行ceph-disk activate-all命令,观察ceph 集群状态
  2. 导出ceph 集群的crush map图,理解其含义。
  3. 学习并使用rbd命令创建、显示、对照(introspect)和移除块设备镜像。