puppet-swift模块

  1. 基础知识
  2. 先睹为快
  3. 核心代码讲解 - 如何做到管理swift服务?
  4. 小结
  5. 动手练习 - 光看不练假把式

0.基础知识

Swift最初源自于Rackspace公司开发的高可用分布式对象存储服务CloudFiles,于2010年贡献给OpenStack开源社区作为最早的核心项目,为用户提供了对象存储,虚机镜像存储,块设备快照存储等诸多功能。Swift可以运行在廉价的标准X86硬件存储上,无需配置RAID(磁盘冗余阵列),通过在软件层面引入一致性哈希算法和数据冗余性,以牺牲一定的数据一致性来最终达到高可用性和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决非结构化数据存储问题。

0.1 Swift基础概念

  • 账户(Account):这里账户不是账号和密码的概念,可以理解为存储区域(Storage area)。
  • 容器(container):容器有自己的metadata,包含了一组object。
  • 对象(object):包含具体数据和metadata。
  • 集群(cluster):表示一个Swift存储集群。
  • 区域(region):表示一个集群中物理隔离的部分。
  • 区(zone):表示物理隔离的节点,可用于控制故障域。
  • 节点(node):表示运行Swift进程的物理服务器。

0.2 Swift组件介绍

Swift服务由众多的组件构成,其架构如下图所示:
puppet-swift - 图2

名称 说明
swift-proxy-server 对外提供对象服务 API,会根据环的信息来查找服务地址并转发用户请求至相应的账户、容器或者对象服务;由于采用无状态的 REST 请求协议,可以进行横向扩展来均衡负载。
swift-account 提供账户元数据和统计信息,并维护所含容器列表的服务,每个账户的信息被存储在一个 SQLite 数据库中。
swift-container 提供容器元数据和统计信息,并维护所含对象列表的服务,每个容器的信息也存储在一个 SQLite 数据库中。
swift-object 提供对象元数据和内容服务,每个对象的内容会以文件的形式存储在文件系统中,元数据会作为文件属性来存储,建议采用支持扩展属性的 XFS 文件系统。
swift-replicator 会检测本地分区副本和远程副本是否一致,具体是通过对比散列文件和高级水印来完成,发现不一致时会采用推式(Push)更新远程副本,例如对象复制服务会使用远程文件拷贝工具 rsync 来同步;另外一个任务是确保被标记删除的对象从文件系统中移除。
swift-updater 当对象由于高负载的原因而无法立即更新时,任务将会被序列化到在本地文件系统中进行排队,以便服务恢复后进行异步更新;例如成功创建对象后容器服务器没有及时更新对象列表,这个时候容器的更新操作就会进入排队中,更新服务会在系统恢复正常后扫描队列并进行相应的更新处理。
swift-auditor 检查对象,容器和账户的完整性,如果发现比特级的错误,文件将被隔离,并复制其他的副本以覆盖本地损坏的副本;其他类型的错误会被记录到日志中。
swift-account-reaper 移除被标记为删除的账户,删除其所包含的所有容器和对象。
authentication server 验证访问用户的身份信息,并获得一个对象访问令牌(Token),在一定的时间内会一直有效;验证访问令牌的有效性并缓存下来直至过期时间。
cache server 缓存的内容包括对象服务令牌,账户和容器的存在信息,但不会缓存对象本身的数据;缓存服务可采用 Memcached 集群,Swift 会使用一致性散列算法来分配缓存地址。

1.先睹为快

不想看下面大段的代码解析,已经跃跃欲试了?

OK,我们开始吧!

Swift服务比较独立,下面编写learn_swift.pp实现all-in-one部署:

  1. $swift_local_net_ip='127.0.0.1'
  2. $swift_shared_secret='changeme'
  3. Exec { logoutput => true }
  4. package { 'curl': ensure => present }
  5. class { '::memcached':
  6. listen_ip => $swift_local_net_ip,
  7. }
  8. class { '::swift':
  9. # not sure how I want to deal with this shared secret
  10. swift_hash_suffix => $swift_shared_secret,
  11. package_ensure => latest,
  12. }
  13. # === Configure Storage
  14. class { '::swift::storage':
  15. storage_local_net_ip => $swift_local_net_ip,
  16. }
  17. # create xfs partitions on a loopback device and mounts them
  18. swift::storage::loopback { '2':
  19. require => Class['swift'],
  20. }
  21. # sets up storage nodes which is composed of a single
  22. # device that contains an endpoint for an object, account, and container
  23. swift::storage::node { '2':
  24. mnt_base_dir => '/srv/node',
  25. weight => 1,
  26. manage_ring => true,
  27. zone => '2',
  28. storage_local_net_ip => $swift_local_net_ip,
  29. require => Swift::Storage::Loopback[2] ,
  30. }
  31. class { '::swift::ringbuilder':
  32. part_power => '18',
  33. replicas => '1',
  34. min_part_hours => 1,
  35. require => Class['swift'],
  36. }
  37. # TODO should I enable swath in the default config?
  38. class { '::swift::proxy':
  39. proxy_local_net_ip => $swift_local_net_ip,
  40. pipeline => ['healthcheck', 'cache', 'tempauth', 'proxy-server'],
  41. account_autocreate => true,
  42. require => Class['swift::ringbuilder'],
  43. }
  44. class { ['::swift::proxy::healthcheck', '::swift::proxy::cache']: }
  45. class { '::swift::proxy::tempauth':
  46. account_user_list => [
  47. {
  48. 'user' => 'admin',
  49. 'account' => 'admin',
  50. 'key' => 'admin',
  51. 'groups' => [ 'admin', 'reseller_admin' ],
  52. },
  53. {
  54. 'user' => 'tester',
  55. 'account' => 'test',
  56. 'key' => 'testing3',
  57. 'groups' => [],
  58. },
  59. ]
  60. }

在终端执行以下命令:

  1. $ puppet apply -v learn_swift.pp

等待Puppet命令执行完成,一个单节点的Swift节点部署完成,可以通过swift命令行工具来使用你的对象存储系统了。

2.核心代码讲解

2.1 class swift

swift类用于完成以下工作:

  • 安装Swift软件包
  • 管理swift.conf配置文件
  • 管理swift相关目录

2.2 class swift::proxy

swift::proxy用于配置swift proxy服务:

  1. class swift::proxy(
  2. $proxy_local_net_ip,
  3. $port = '8080',
  4. $pipeline = ['healthcheck', 'cache', 'tempauth', 'proxy-server'],
  5. $workers = $::processorcount,
  6. $allow_account_management = true,
  7. ...
  8. $package_ensure = 'present',
  9. $service_provider = $::swift::params::service_provider
  10. ) inherits ::swift::params {
  11. #所有swift_config资源的执行将触发swift-proxy-server的重启
  12. Swift_config<| |> ~> Service['swift-proxy-server']
  13. # validate函数用于检查参数的数据类型,若不匹配将抛出异常
  14. validate_bool($account_autocreate)
  15. validate_bool($allow_account_management)
  16. validate_array($pipeline)
  17. if($write_affinity_node_count and ! $write_affinity) {
  18. fail('Usage of write_affinity_node_count requires write_affinity to be set')
  19. }
  20. # member函数用于判断右侧参数是否在左侧参数中
  21. if(member($pipeline, 'tempauth')) {
  22. $auth_type = 'tempauth'
  23. } elsif(member($pipeline, 'swauth')) {
  24. $auth_type = 'swauth'
  25. } elsif(member($pipeline, 'keystone')) {
  26. $auth_type = 'keystone'
  27. } else {
  28. warning('no auth type provided in the pipeline')
  29. }
  30. if(! member($pipeline, 'proxy-server')) {
  31. warning('pipeline parameter must contain proxy-server')
  32. }
  33. if($auth_type == 'tempauth' and ! $account_autocreate ){
  34. fail('account_autocreate must be set to true when auth_type is tempauth')
  35. }
  36. if ($log_udp_port and !$log_udp_host) {
  37. fail ('log_udp_port requires log_udp_host to be set')
  38. }
  39. package { 'swift-proxy':
  40. ensure => $package_ensure,
  41. name => $::swift::params::proxy_package_name,
  42. tag => ['openstack', 'swift-package'],
  43. }
  44. concat { '/etc/swift/proxy-server.conf':
  45. owner => 'swift',
  46. group => 'swift',
  47. require => Package['swift-proxy'],
  48. }
  49. $required_classes = split(
  50. inline_template(
  51. "<%=
  52. (@pipeline - ['proxy-server']).collect do |x|
  53. 'swift::proxy::' + x.gsub(/-/){ %q(_) }
  54. end.join(',')
  55. %>"), ',')
  56. concat::fragment { 'swift_proxy':
  57. target => '/etc/swift/proxy-server.conf',
  58. content => template('swift/proxy-server.conf.erb'),
  59. order => '00',
  60. before => Class[$required_classes],
  61. }
  62. Concat['/etc/swift/proxy-server.conf'] -> Swift_proxy_config <||>
  63. ...
  64. }

2.3 class swift::storage

swift::storage完成了配置rsync server的工作, 我们在基础模块章节中已介绍过,这里不再赘述。

2.4 class swift::storage::disk

swift::storage::disk是一个重要的类,用于管理磁盘分区和文件系统的创建。

puppet```
define swift::storage::disk(
$base_dir = ‘/dev’,
$mnt_base_dir = ‘/srv/node’,
$byte_size = ‘1024’,
$ext_args = ‘’,
) {

include ::swift::deps

if(!defined(File[$mnt_base_dir])) {
file { $mnt_base_dir:
ensure => directory,
owner => ‘swift’,
group => ‘swift’,
require => Anchor[‘swift::config::begin’],
before => Anchor[‘swift::config::end’],
}
}

exec { “create_partition_label-${name}”:
command => “parted -s ${base_dir}/${name} mklabel gpt ${ext_args}”,
path => [‘/usr/bin/‘, ‘/sbin’,’/bin’],
onlyif => [“test -b ${base_dir}/${name}”,”parted ${base_dir}/${name} print|tail -1|grep ‘Error’”],
before => Anchor[‘swift::config::end’],
}

swift::storage::xfs { $name:
device => “${base_dir}/${name}”,
mnt_base_dir => $mnt_base_dir,
byte_size => $byte_size,
loopback => false,
subscribe => Exec[“create_partition_label-${name}”],
}

}

  1. 首先,`swift::storage::disk`会调用"create_partition_label-${name}"exec创建分区表,若存在则跳过,默认会使用整块磁盘, sdb
  2. 也可以通过ext_args参数来传递额外的parted参数。例如,传入'mkpart primary 0% 100%'来创建第一个分区,例如sdb1
  3. 其次,通过声明`swift::storage::xfs`来指定磁盘xfs文件系统的格式化,并将其挂载到默认为'/srv/node'目录下。
  4. 接下来,继续解析`swift::storage::xfs`代码。
  5. ### 2.5 swift::storage::xfs
  6. `swift::storage::xfs`完成以下三项功能:
  7. 1.完成xfs文件系统的创建:
  8. ```puppet
  9. exec { "mkfs-${name}":
  10. command => "mkfs.xfs -f -i size=${byte_size} ${target_device}",
  11. path => ['/sbin/', '/usr/sbin/'],
  12. unless => "xfs_admin -l ${target_device}",
  13. before => Anchor['swift::config::end'],
  14. }

2.判断设备的挂载类型

  1. case $mount_type {
  2. 'path': { $mount_device = $target_device }
  3. 'uuid': { $mount_device = dig44($facts, ['partitions', $target_device, 'uuid'])
  4. unless $mount_device { fail("Unable to fetch uuid of ${target_device}") }
  5. }
  6. default: { fail("Unsupported mount_type parameter value: '${mount_type}'. Should be 'path' or 'uuid'.") }
  7. }

3.挂载文件系统

  1. swift::storage::mount { $name:
  2. device => $mount_device,
  3. mnt_base_dir => $mnt_base_dir,
  4. loopback => $loopback,
  5. }

接下来,进入到swift::storage::mount中。

2.6 swift::storage::mount

swift::storage::mount执行真正的文件系统挂载操作:

1.管理指定的挂载目录

  1. file { "${mnt_base_dir}/${name}":
  2. ensure => directory,
  3. owner => 'swift',
  4. group => 'swift',
  5. require => Anchor['swift::config::begin'],
  6. before => Anchor['swift::config::end'],
  7. }

2.调用mount资源和exec做双重挂载

  1. mount { "${mnt_base_dir}/${name}":
  2. ensure => present,
  3. device => $device,
  4. fstype => $fstype,
  5. options => "${options},${fsoptions}",
  6. }
  7. # double checks to make sure that things are mounted
  8. exec { "mount_${name}":
  9. command => "mount ${mnt_base_dir}/${name}",
  10. path => ['/bin'],
  11. unless => "grep ${mnt_base_dir}/${name} /etc/mtab",
  12. # TODO - this needs to be removed when I am done testing
  13. logoutput => true,
  14. before => Anchor['swift::config::end'],
  15. }

3.管理挂载目录权限

  1. exec { "fix_mount_permissions_${name}":
  2. command => "chown -R swift:swift ${mnt_base_dir}/${name}",
  3. path => ['/usr/sbin', '/bin'],
  4. refreshonly => true,
  5. before => Anchor['swift::config::end'],
  6. }

注意,refreshonly属性表明该资源的执行只能通过被其他资源触发。

2.7 swift::ringbuilder

swift::ringbuilder用于创建account, container以及object的builder文件,并且调用swift::ringbuilder::rebalance对ring文件进行rebalance操作。

  1. class swift::ringbuilder(
  2. $part_power = undef,
  3. $replicas = undef,
  4. $min_part_hours = undef
  5. ) {
  6. include ::swift::deps
  7. Class['swift'] -> Class['swift::ringbuilder']
  8. swift::ringbuilder::create{ ['object', 'account', 'container']:
  9. part_power => $part_power,
  10. replicas => $replicas,
  11. min_part_hours => $min_part_hours,
  12. }
  13. Swift::Ringbuilder::Create['object'] -> Ring_object_device <| |> ~> Swift::Ringbuilder::Rebalance['object']
  14. Swift::Ringbuilder::Create['container'] -> Ring_container_device <| |> ~> Swift::Ringbuilder::Rebalance['container']
  15. Swift::Ringbuilder::Create['account'] -> Ring_account_device <| |> ~> Swift::Ringbuilder::Rebalance['account']
  16. swift::ringbuilder::rebalance{ ['object', 'account', 'container']: }

2.8 swift::ringserver

此类的工作:

  • 通过创建一个rsync服务器来启动一个ringdatabase服务

小结

本章咱们介绍了swift的相关概念,通过一个小列子来部署一套all-in-one的swift环境,并且讲解了puppet-swift相关核心代码中的一些小知识点。

动手练习

如何部署一个多节点的swift的集群?要求一个API节点,一个Stoage节点。