runsisi's

technical notes

puppet ceph部署

2018-12-25 runsisi#ceph#devops

前言

本文对使用 puppet 进行 ceph 集群的部署流程进行简要总结。全文结构如下:第一、二节分别对 puppet 和 ceph 进行简单介绍;第三节分析 puppet-ceph 模块的结构和实现;第四节描述 ceph 集群部署的操作流程;第五节总结全文;附录为参考文献及代码。

puppet

puppet 是一个开源的配置管理工具(Puppet is an open source configuration management utility),同时也是当前最主流的配置管理自动化工具之一。

Puppet 有自己独立的语言用于编写配置脚本,但不同于一般的脚本语言,该语言只描述被配置对象的状态(即 puppet 中的资源),而不需要指定实现该配置所需要的操作步骤。puppet 自身定义了很多常见的资源类型,但用户也可以自定义资源类型,具体实现细节可以参阅“ Puppet Types and Providers”一书。

Puppet 有两种工作模式:C/S 模式、serverless 模式。在 C/S 模式下 puppet 客户端定时(默认 30 分钟)发起对服务端的连接,下载属于客户端所在节点的脚本(当然这是最简化的描述),然后应用脚本,从而使系统达到脚本所描述的状态。Serverless 模式即无服务端模式,直接在所需配置的节点应用脚本即可,脚本的分发可以使用标准的 svn、git 等版本管理工具或简单粗暴的 scp 拷贝等方式。

ceph

Ceph 号称 “THE FUTURE OF STORAGE”,作为一个统一存储系统,对外提供:文件系统、块设备、对象存储等三种存储访问形式。ceph 自身以集群的形式存在,集群成员包括 MON、OSD MDS 三种类型的节点,如果不需要对外提供文件系统的存储方式,则集群只需要 MON 和 OSD 两种类型的节点。

MON 节点是 ceph 集群的控制中心,OSD 节点的加入、集群管理等都需要依赖 MON 节点,因此为了避免单点故障,一般会存在多个 MON 节点。OSD 节点是实际的数据存储仓库,一份数据可能会在多个 OSD 节点之间进行备份、恢复,这些业务产生的网络数据交互在集群内部网络进行。MDS 节点主要负责在 VFS 中生成文件系统树(tree)。

Ceph 集群对各节点的时间同步有严格的要求,因此在部署 ceph 集群之前,需要先部署 NTP 服务器,并在集群各节点都启用 NTP 服务。

puppet-ceph 模块

第一节中提到编写 puppet 脚本实际上就是在脚本中定义资源,而对于某个功能配置可能会涉及多个资源,如 NTP 服务的配置就包括 NTP 软件包、服务、配置文件等三种资源,这样就产生了模块化的开发方式,一个 puppet 模块就可能包括了对某个功能项的所有资源的管理。

采用 C/S 模式时,模块只需要在服务端进行安装,在客户端开启 /etc/puppet/puppet.conf/agent/pluginsync 选项的情况下会自动将模块从服务端同步到客户端,但如果是 serverless 模式则需要在所有节点手工安装模块。

在 puppet 的官网以及 github 上能搜索到很多开源的 puppet 模块,如用于 ceph 集群部署的 puppet-ceph,对于这些模块有些可以拿来就用,但是有些必须对部分参数进行调整,如 NTP 服务器的地址自然是不同环境有不同的配置。

Puppet-ceph 模块使用了 Roles and Profiles 设计模式,同时通过 hiera 实现代码和数据解耦,ceph 集群的参数由 ::ceph::profile::params 类直接从 hiera 数据中获取,部署 ceph 节点或 ceph 客户端时只需要 include 相应的 profile 类即可(MDS 节点没有定义单独的 profile 类,因此如果需要部署 MDS 节点,需要在puppet 脚本中 include ::ceph::mds 类)。

Ceph 集群的部署所涉及的资源大多都是 puppet 未曾定义的资源,因此代码中充斥着大量的 exec 资源(实际上就是 shell 脚本),把 MON、OSD 等定义成一种资源类型也许会是一个不错的尝试。

Puppet-ceph 自身依赖 puppetlabs/stdlib、puppetlabs/inifile、puppetlabs/apt 等三个模块,因此这三个模块也需要安装(如前文所述,依 puppet 工作模式不同而有不同的安装要求:C/S 模式只需要在服务端安装,serverless 模式需要在所有运行 puppet 脚本的节点安装)。

使用 puppet-ceph 进行部署

本节使用上节提到的 puppet-ceph 模块对 ceph 集群进行部署,且暂时只考虑对 MON、OSD 节点进行部署,采用的部署方式为 C/S 模式。该模块虽然使用了 hiera, 但模块自带的例子并没有使用 hiera 作为 ENC,我们这里也不准备使用其作为 ENC,而是使用更为传统的 site.pp 方式。

在部署之前先结合前文内容对 ceph 集群部署所需的准备工作进行简要总结如下(环境以 Centos 7.0 x86_64 为例):

  1. puppet-ceph 依赖 stdlib、inifile、apt 等三个模块

因此需要在服务端安装包括 puppet-ceph 在内的四个模块:puppet module install xxx(或者直接下载模块的压缩包并解压到 /etc/puppet/modules/ 目录下,注意解压后必须修改文件夹名字分别对应模块名:stdlib、inifile、apt);

  1. ceph 集群严重依赖 NTP 进行时间同步

因此网络上必须存在 NTP 服务器,并且在每个节点开启 NTP 服务(当然可以在部署 ceph 集群的同时部署 NTP 服务,但需要注意设置好资源间的依赖关系);

  1. ceph 的 OSD 节点依赖于 MON 节点

因此合理的部署步骤应该是先部署 MON 节点,后部署 OSD 节点,否则如果 OSD 节点在第一次部署不成功后如果没有人工干预,需要等 30 分钟(默认值)才会尝试下一次部署操作。当然由于 exec 资源在应用过程中有一定的超时时间,因此即使所有节点同时开始部署也应该不会存在太大问题;

  1. puppet-ceph 的 OSD 部署依赖于 ceph-disk

如果 OSD 使用整个硬盘作为日志盘,且该硬盘是全新的(没有分区表)话,ceph-disk 在给日志盘分区时存在 BUG(目前最新的代码仍然存在该问题),需要对代码稍作修改(见附 2),当然如果直接使用分区或文件或不单独指定日志盘就不存在该问题;

  1. ceph 个节点间通信需要开放相应的防火墙端口,puppet 服务端也相应需要开放端口,当然测试环境下可以先把防火墙都关闭,否则还需要增加防火墙规则的部署。

言归正传,下面给出正式部署的环境如下:

  1. 10 个节点,每个节点已安装 Centos 7.0 x86_64 的系统;
  2. 每个节点两块网卡,前端网络 192.168.133.0/24,后端网络 192.168.134.0/24;
  3. 每个节点两块硬盘,sda 为系统盘,sdb 作为 OSD,大小为 50GB(注意不要太小,否则添加到 CRUSH 里的 OSD 权重会是 0,默认 1TB 空间的权重是 1),日志盘不单独指定;
  4. 第一个节点作为内部网络的 NTP 服务器,同时作为 puppet 服务端;
  5. 剩下的九个节点前三个作为 MON 节点,后六个作为 OSD 节点;
  6. 每个节点的 hostname 如下:ceph-master.test,ceph-mon[0-2].test,ceph-osd[0-5].test;
  7. ceph-master.test 节点已安装 puppet-server 软件包,其它节点已安装 puppet 软件包;
  8. 由于是测试环境,关闭所有防火墙( Centos 7.0 是 firewalld);
  9. 由于这里只测试 ceph 集群的部署,前期先部署好各节点的 NTP 服务(Centos 7.0 默认 NTP 服务器是 chronyd,当然也可以用 ntpd);
  10. 将所有的 hostname 添加到 /etc/hosts 中, 暂时通过在 /etc/hosts 添加条目的方式实现 DNS;
  11. Puppet 服务端对客户端证书的授权使用 autosign 的方式;

具体的部署步骤如下,先配置 ceph-master.test 节点:

  1. 安装 puppetlabs/stdlib、puppetlabs/inifile、puppetlabs/apt 三个模块;
  2. echo “*.test” > /etc/puppet/autosign.conf,开启 autosign;
  3. 修改 /etc/puppet/hiera.yaml 以支持 hiera,内容见附 3;
  4. /var/lib/hiera 目录下添加 common.yaml 文件,内容见附 4;
  5. /etc/puppet/manifests 目录下新建 site.pp,内容见附 5;
  6. systemctl restart puppetmasterd 启动 puppetmasterd 服务;

配置 ceph-mon[0-2].test、 ceph-osd[0-5].test 节点:

  1. 设置 /etc/puppet/puppet.conf/agent/pluginsync 成 true;
  2. 设置 /etc/puppet/puppet.conf/agent/server 为 ceph-master.test;
  3. 确保当前 puppet agent 服务已停止,systemctl stop puppet
  4. 在各节点手动执行 puppet agent --onetime --verbose --ignorecache --nodaemonize --no-usecacheonfailure --no-splay --show_diff --debug 开始部署;

总结

本文对 puppet-ceph 部署过程进行了简单总结,由于 ceph 集群的部署具有其一定的复杂性,因此使用纯粹的 puppet 可能存在一定的局限,如:MON 节点与 OSD 节点部署的依赖; puppet 服务端主动通知客户端进行配置同步的机制不健全(puppet kick 能实现类似这样的功能,但配置麻烦,而且在新版本中已被官方废弃,Mcollective 很好很强大,但需要各种配置);因此如果想在实验环境下简单的部署一个 ceph 集群的话,我的建议是不使用 puppet。

附 1 参考文献

http://en.wikipedia.org/wiki/Puppet_(software)

https://docs.puppetlabs.com/puppet/latest/reference/lang_visual_index.html

https://docs.puppetlabs.com/learning/agent_master_basic.html

http://ceph.com/

http://forge.puppetlabs.com/

https://github.com/stackforge/puppet-ceph

http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-2/

https://docs.puppetlabs.com/hiera/1/

https://docs.puppetlabs.com/references/stable/type.html#exec

https://docs.puppetlabs.com/puppet/latest/reference/modules_installing.html

https://docs.puppetlabs.com/guides/external_nodes.html

https://docs.puppetlabs.com/puppet/latest/reference/ssl_autosign.html

https://docs.puppetlabs.com/references/latest/man/kick.html

http://puppetlabs.com/mcollective

附 2 ceph-disk patch

$ git diff ceph-disk
diff --git a/src/ceph-disk b/src/ceph-disk
index 6072c7a..2b90d97 100755
--- a/src/ceph-disk
+++ b/src/ceph-disk
@@ -952,6 +952,16 @@ def get_free_partition_index(dev):
        raise Error('parted failed to output anything')
     lines = str(lines).splitlines(True)
+    idiot_prefix0 = '\x42\x59\x54\x3b\x0a'
+    for line in lines:
+        line = line.strip()
+        if line.startswith(idiot_prefix0):
+            line = line[5:]
+    if len(line) == 0:
+        lines = lines[1:]
+    else:
+        break
+
     # work around buggy libreadline(?) library in rhel/centos.
     idiot_prefix = '\x1b\x5b\x3f\x31\x30\x33\x34\x68'
     if lines[0].startswith(idiot_prefix):

附 3 hiera.yaml

---
:backends:
  - yaml
:yaml:
  :datadir: /var/lib/hiera
:hierarchy:
  - "nodes/%{::hostname}"
  - common

附 4 common.yaml

---
######## Ceph
ceph::profile::params::release: 'giant'

######## Ceph.conf
ceph::profile::params::fsid: '4b5c8c0a-ff60-454b-a1b4-9747aa737d19'
ceph::profile::params::authentication_type: 'cephx'
ceph::profile::params::mon_initial_members: 'ceph-mon0, ceph-mon1, ceph-mon2'
ceph::profile::params::mon_host: 'ceph-mon0.test:6789, cephmon1.test:6789, ceph-mon2.test:6789'
ceph::profile::params::osd_pool_default_pg_num: '200'
ceph::profile::params::osd_pool_default_pgp_num: '200'
ceph::profile::params::osd_pool_default_size: '2'
ceph::profile::params::osd_pool_default_min_size: '1'
ceph::profile::params::cluster_network: '192.168.134.0/24'
ceph::profile::params::public_network: '192.168.133.0/24'

######## Keys
ceph::profile::params::mon_key:
'AQATGHJTUCBqIBAA7M2yafV1xctn1pgr3GcKPg=='
# as an alternative to specifying the mon key you can provide an exising keyring
#ceph::profile::params::mon_keyring: '/etc/ceph/ceph.mon.keyring'
ceph::profile::params::admin_key: 'AQBMGHJTkC8HKhAAJ7NH255wYypgm1oVuV41MA=='
ceph::profile::params::admin_key_mode: '0600'
ceph::profile::params::bootstrap_osd_key: 'AQARG3JTsDDEHhAAVinHPiqvJkUi5Mww/URupw=='
ceph::profile::params::bootstrap_mds_key: 'AQCztJdSyNb0NBAASA2yPZPuwXeIQnDJ9O8gVw=='
ceph::profile::params::osds:
  '/dev/sdb':
    journal:

附 5 site.pp

node /^ceph-mon[012]\.test$/ {
  include ceph::profile::mon
}
node /^ceph-osd[0-5]\.test$/ {
  include ceph::profile::osd
}