OpenStack系列(十三) Swift对象存储介绍与实践
1 | 作者:李晓辉 |
Swift对象存储架构
在 OpenStack Swift 中,有三种主要类型的服务器:
- 帐户服务器 (swift_account_server)
帐户服务器就像是整个对象存储的“大管家”,它负责管理所有的帐户。你可以把帐户想象成一个个“用户空间”,每个用户都有自己的帐户。帐户服务器的主要任务是处理和帐户相关的各种请求,比如创建新帐户、删除帐户,或者管理帐户的元数据(比如帐户的名称、权限设置等等)。简单来说,它就是负责“管人”的,确保每个用户都能顺利地使用自己的存储空间。
- 容器服务器 (swift_container_server)
容器服务器的作用有点像“文件夹管理员”。在 Swift 里,容器就像是一个个“大文件夹”,用来分类存储对象(也就是文件)。容器服务器的工作就是处理和容器相关的请求,比如创建新的容器、删除容器,或者列出某个帐户下的所有容器。它还会管理容器的元数据,比如容器的名称、大小限制等等。有了容器服务器,用户就能很方便地组织和管理自己的文件啦!
- 对象服务器 (swift_object_server)
对象服务器才是真正的“干活主力”,它负责管理对象存储里的实际对象(也就是文件)。对象服务器的工作包括处理上传文件、下载文件、删除文件的请求,以及管理对象的元数据(比如文件的大小、类型、创建时间等等)。简单来说,对象服务器就是用来“管文件”的,确保文件能够安全地存进去,也能快速地取出来。
总结一下
帐户服务器:负责管理帐户,处理和帐户相关的请求,相当于“管人”的。
容器服务器:负责管理容器,相当于“管文件夹”的,帮助用户组织文件。
对象服务器:负责管理实际的对象(文件),处理文件的上传、下载和删除,相当于“管文件”的。
OpenStack 对象存储的结构
OpenStack 对象存储(Swift)采用了一种分层的方式来组织数据,主要分为三个层次:帐户(Account)、容器(Container) 和 对象(Object)。这种分层结构就像是一棵“数据树”,每个层次都有自己的职责和作用。
1. 帐户(Account)
帐户是整个数据层次结构的“根”,它就像是一个“大仓库”,用来存放所有的容器和对象。每个用户都有一个或多个帐户,帐户是数据存储的起点。你可以把帐户想象成一个公司的总文件柜,里面存放了所有部门的文件夹和文件。
2. 容器(Container)
容器就像是“文件夹”,用来存储和隔离对象。每个帐户下可以有多个容器,容器的作用是帮助用户更好地组织和管理数据。容器还有一个重要的功能,就是可以设置访问控制列表(ACL),用来控制谁可以访问这个容器里的数据。这种访问控制是在容器级别进行的,而不是在对象级别,也就是说,一旦设置了容器的访问权限,里面的所有对象都会受到这个权限的保护。
3. 对象(Object)
对象就是实际存储的数据内容,比如文件、图片、视频等。每个对象都有自己的元数据(比如文件大小、类型、创建时间等),这些元数据帮助系统更好地管理和访问对象。你可以把对象想象成文件夹里的具体文件,它们是存储在容器里的实际数据。
OpenStack 对象存储的 API 格式
OpenStack 对象存储的 API 采用了一种特殊的格式来表示资源路径,这种格式非常直观,方便用户理解和使用。API 的基本格式如下:
1 | /v1/{account}/{container}/{object} |
{account}
:表示帐户的名称或 ID。{container}
:表示容器的名称。{object}
:表示对象的名称。
示例
假设你有一个名为 654321
的帐户,一个名为 container1
的容器,你上传了一个名为 object1
的对象。那么,这个对象的完整 API 路径就是:
1 | https://myobjectserver.example.com:8080/v1/654321/container1/object1 |
Ring Database
今天来给你唠唠 OpenStack 对象存储系统里的“环数据库”(Ring Database),这可是 Swift 里用来管理数据存储位置的“秘密武器”。别看它名字有点绕,其实原理挺简单的,就是用来算出数据应该存在集群里的哪个地方。咱们来一步步拆解一下它用到的几个关键概念:
- 地区(Region)
你可以把地区想象成不同地方的“大仓库”,它是为了在地理上把存储隔离开来。比如,你可能在北京有一个数据中心,在上海有一个数据中心,那这两个地方就可以被设置成不同的地区。每个地区都有自己的服务器端点,就好比是每个“大仓库”都有自己的大门,数据进出都要通过这个大门。
- 区域(Zone)
区域就更细一些了,它通常表示一组设备、服务器,甚至可以是一个数据中心里的一部分。你可以把它想象成“大仓库”里的不同区域,比如一楼、二楼或者 A 区、B 区之类的。这样做的好处是,即使一个区域出问题了,其他区域的数据也不会受影响。
- 设备(Device)
设备就是实际用来存数据的“硬盘”或者“存储单元”。这些设备上有一个后端文件系统,通常会挂载到 /srv/node
目录下。就好比你在电脑上插了一个移动硬盘,然后把数据存进去,Swift 里的设备就是干这个用的。
- 分区(Partition)
分区是对象存储服务运行的最小单元,你可以把它想象成一个个“小格子”。所有的帐户、容器和对象数据都会被放进这些“小格子”里,然后这些“小格子”会在集群里被分发和复制。分区的作用是确保数据的完整性和持久性,即使某个设备坏了,数据也不会丢,因为还有其他副本。
- 副本(Replica)
副本就是数据的“备份”。在 OpenStack 对象存储系统里,默认情况下,一个对象会有三个副本。这就像是你把一份文件复印了三份,分别放在不同的地方,这样即使有一份丢了或者坏了,你还有另外两份可以用来恢复数据。默认副本数是 3,意味着集群里的每个对象都会有三个副本,分布在不同的地方,确保数据的安全性。
总结一下
地区(Region):地理上的隔离,每个地区有自己的服务器端点。
区域(Zone):地区里的细分,可以是一组设备或者服务器。
设备(Device):实际存储数据的硬盘或存储单元。
分区(Partition):最小的数据存储单元,用来确保数据的完整性和持久性。
副本(Replica):数据的备份,确保数据的安全性,默认副本数是 3。
通过这些概念,OpenStack 对象存储系统就能巧妙地把数据分布到集群里,既保证了数据的安全性,又提高了系统的可靠性。
ring
在 OpenStack Swift 中,环(Ring) 是一个非常重要的概念,它是用来管理和定位数据存储位置的核心组件。简单来说,环就像是一个“地图”,帮助 Swift 系统知道数据应该存储在哪里,以及如何在集群中分布和访问这些数据。
环的作用
环的主要作用是将数据的逻辑结构(帐户、容器、对象)映射到物理存储位置(设备)。它通过一种特殊的算法(通常是哈希算法)来计算数据应该存储在哪些设备上,并且确保数据的分布是均匀的和冗余的。
环的组成部分
环主要由以下几个关键部分组成:
分区(Partition):
分区是数据存储的最小单元。所有的帐户、容器和对象数据都会被分配到这些分区中。
分区是 Swift 中数据分布和管理的基本单位。
设备(Device):
设备是实际存储数据的物理位置,通常是服务器上的硬盘。
每个设备都有一个唯一的标识符,环会根据这个标识符来确定数据的存储位置。
副本(Replica):
为了保证数据的高可用性和持久性,Swift 会为每个分区创建多个副本。
默认情况下,Swift 会为每个分区创建 3 个副本,这些副本会被分布到不同的设备上。
权重(Weight):
权重用来表示设备的存储容量和性能。权重越高,设备被分配的分区就越多。
权重可以帮助 Swift 在不同设备之间均匀地分布数据。
环的工作原理
哈希算法:
Swift 使用哈希算法(通常是 MD5 或 SHA-1)来计算数据的哈希值。
这个哈希值会被映射到一个环上,环是一个从 0 到 2^32-1 的虚拟空间。
分区分配:
环会被分成多个分区,每个分区对应一个哈希值范围。
数据的哈希值会被映射到某个分区,然后这个分区会被分配到具体的设备上。
副本分布:
每个分区的副本会被分布到不同的设备上,以确保数据的冗余和高可用性。
默认情况下,每个分区有 3 个副本,这些副本会被分布到不同的设备、区域和区域中。
环的类型
Swift 有三种类型的环,分别对应不同的数据层次:
帐户环(Account Ring):
- 用于管理帐户数据的存储位置。
容器环(Container Ring):
- 用于管理容器数据的存储位置。
对象环(Object Ring):
- 用于管理对象数据的存储位置。
环的文件结构
环的信息通常存储在一系列文件中,这些文件包含了分区到设备的映射关系。这些文件通常位于 /etc/swift/
目录下,文件名类似于 account.ring.gz
、container.ring.gz
和 object.ring.gz
。
总结
环(Ring) 是 Swift 中用来管理和定位数据存储位置的核心组件。
它通过哈希算法将数据映射到分区,然后将分区分配到具体的设备上。
环确保数据的分布是均匀的,并且通过副本机制保证数据的高可用性和持久性。
Swift 有三种环:帐户环、容器环和对象环,分别管理不同层次的数据。
环数据结构的关键字段
- 设备相关字段
这些字段是关于存储设备的详细信息,Swift 通过这些信息来确定数据应该存储在哪个设备上。具体包括:
设备 ID(Device ID):每个设备都有一个唯一的标识符,就像设备的“身份证号码”。
设备名称(Device Name):设备的名称,通常是设备的路径,比如
/dev/sdb1
。地区(Region):设备所在的地区,用于地理隔离。比如,北京的数据中心和上海的数据中心可以分别设置为不同的地区。
区域(Zone):设备所在的区域,用于进一步细分存储资源。比如,一个数据中心里可以有多个区域。
设备权重(Device Weight):表示设备的存储容量和性能。权重越高,设备被分配的分区就越多。
存储服务器 IP 地址(Storage Server IP Address):设备所在的服务器的 IP 地址,Swift 通过这个地址来访问设备。
存储服务器端口(Storage Server Port):设备所在的服务器监听的端口号,Swift 通过这个端口来与设备通信。
其他元数据(Other Metadata):可能还包括设备的类型、状态等其他信息。
这些字段的作用是让 Swift 系统知道每个设备的具体位置和能力,从而合理地分配数据。
2. 分区至设备分配字段
这些字段是关于分区如何分配到设备上的信息,Swift 通过这些信息来确定每个分区的具体存储位置。具体包括:
- 设备 ID 列表(Device ID List):表示每个分区分配到的设备 ID 列表。因为每个分区有多个副本,所以这个列表会包含多个设备 ID。比如,一个分区可能有三个副本,分别存储在三个不同的设备上。
这些字段的作用是让 Swift 系统知道每个分区的副本具体存储在哪些设备上,从而确保数据的高可用性和持久性。
总结
设备相关字段:帮助 Swift 系统了解每个设备的具体信息,包括设备的位置、容量、性能等。
分区至设备分配字段:帮助 Swift 系统了解每个分区的副本存储在哪些设备上,确保数据的分布和冗余。
重新平衡环
准备环境
对 Swift 对象存储进⾏更改(如添加或删除节点或磁盘)需要重新平衡对象存储环
打一下lab,帮我们准备好资料 lab storage-object start
1 | [student@workstation ~]$ lab storage-object start |
登录controller,因为swift运行在这里
1 | [student@workstation ~]$ ssh root@controller0 |
查询ring状态
先进入到swift容器的位置看看这3个类型的ring现状
1 | [root@controller0 ~]# cd /var/lib/config-data/puppet-generated/swift/etc/swift |
以下通用参数解释:
1024 partitions
:表示当前环被分成了 1024 个分区。分区是 Swift 中数据分布的最小单位。1.000000 replicas
:表示每个分区的副本数量为 1。通常情况下,Swift 的副本数量默认为 3,但这里显示为 1,可能是因为这是一个测试环境或者特殊配置。1 regions
:表示当前环中有 1 个地区。地区用于地理隔离,这里只有一个地区。1 zones
:表示当前环中有 1 个区域。区域用于进一步细分存储资源,这里只有一个区域。1 devices
:表示当前环中有 1 个设备。设备是实际存储数据的物理位置。0.00 balance
:表示当前环的平衡度为 0.00。平衡度用于衡量数据在设备之间的分布是否均匀。值越接近 0,表示分布越均匀。0.00 dispersion
:表示当前环的分散度为 0.00。分散度用于衡量数据副本的分布情况。值越接近 0,表示副本分布越均匀。The minimum number of hours before a partition can be reassigned is 1
:表示分区重新分配的最小间隔时间为 1 小时。这个参数用于防止频繁的分区重新分配,避免对系统性能造成影响。The overload factor is 0.00%
:表示当前环的过载因子为 0.00%。过载因子用于处理设备过载的情况,这里显示为 0,表示没有过载。object.ring.gz
:表示当前对象环文件的名称。is up-to-date
:表示环文件是最新的,没有需要更新的内容。id
:设备的唯一标识符,这里是0
。region
:设备所在的地区,这里是1
。zone
:设备所在的区域,这里是1
。ip address:port
:设备所在的服务器的 IP 地址和端口号,这里是172.24.4.1:6000
。replication ip:port
:设备所在的服务器的复制 IP 地址和端口号,这里是172.24.4.1:6000
。name
:设备的名称,这里是d1
。weight
:设备的权重,这里是100.00
。权重越高,设备被分配的分区就越多。partitions
:设备上分配的分区数量,这里是1024
。balance
:设备的平衡度,这里是0.00
。平衡度用于衡量设备上的数据分布是否均匀。flags
:设备的标志,这里没有显示任何标志。meta
:设备的元数据,这里没有显示任何元数据。
1 | [root@controller0 swift]# swift-ring-builder object.builder |
1 | [root@controller0 swift]# swift-ring-builder container.builder |
1 | [root@controller0 swift]# swift-ring-builder account.builder |
添加设备到ring
swift-ring-builder object.builder
:指定操作的对象环构建文件是object.builder
。add
:表示要添加一个新的设备到环中。z1-172.24.4.1:6000/d2
:指定要添加的设备的详细信息:z1
:表示设备所在的区域(Zone)是1
。172.24.4.1:6000
:设备所在的服务器的 IP 地址和端口号。/d2
:设备的名称,这里是d2
。
100
:设备的权重,表示设备的存储容量和性能。
1 | [root@controller0 swift]# swift-ring-builder object.builder add z1-172.24.4.1:6000/d2 100 |
1 | [root@controller0 swift]# swift-ring-builder container.builder add z1-172.24.4.1:6001/d2 100 |
1 | [root@controller0 swift]# swift-ring-builder account.builder add z1-172.24.4.1:6002/d2 100 |
设置副本数量为2
既然有2个设备了,设置副本数为2,提高可靠性
1 | [root@controller0 swift]# swift-ring-builder object.builder set_replicas 2 |
它提示我们重新平衡后有效,那我们现在来看看啥情况了
Ring file object.ring.gz is obsolete
提示我们需要平衡
1 | [root@controller0 swift]# swift-ring-builder object.builder |
1 | [root@controller0 swift]# swift-ring-builder container.builder |
1 | [root@controller0 swift]# swift-ring-builder account.builder |
触发重平衡
退到director上,来发起重平衡任务
1 | [root@controller0 swift]# ssh stack@director |
下载重平衡的yaml
1 | (undercloud) [stack@director ~]$ wget http://materials.example.com/storage-swiftrings/swift_ring_rebalance.yaml |
执行playbook完成重新平衡
1 | (undercloud) [stack@director ~]$ ansible-playbook -i /usr/bin/tripleo-ansible-inventory swift_ring_rebalance.yaml |
1 |
|
验证是否重平衡
再登录controller,直接给这些swift重启,看看加载的新配置是什么
1 | (undercloud) [stack@director ~]$ ssh root@controller0 |
1 | [root@controller0 ~]# podman restart swift_proxy swift_account_server swift_container_server swift_object_server |
Ring file account.ring.gz is up-to-date
提示我们已经是最新了
1 | [root@controller0 ~]# cd /var/lib/config-data/puppet-generated/swift/etc/swift |
1 | [root@controller0 swift]# swift-ring-builder object.builder |
1 | [root@controller0 swift]# swift-ring-builder account.builder |
创建文件测试
我们创建一个文件,最后如何存在后端是2份,那就是两副本了
1 | [root@controller0 ~]# scp student@workstation:~/developer1-finance-rc . |
先创建一个存东西的容器
1 | [root@controller0 ~(developer1-finance)]# openstack container create finance-container1 |
上传一个文件看看
1 | [root@controller0 ~(developer1-finance)]# echo hello lxh lixiaohui > hello.txt |
看看数据吧
1 | [root@controller0 ~(developer1-finance)]# find /srv/node/ -iname *.data |
果然有两个数据,看看都是什么内容
1 | [root@controller0 ~(developer1-finance)]# cat /srv/node/d1/objects/899/255/e0f6591fe97a7bdc7124e6b8c049f255/1744871969.29184.data |
very good,我们的两副本成功了~