Swift 深度解析:OpenStack 对象存储原理与实践
定位与职责
Swift 是 OpenStack 的对象存储服务,设计目标是存储海量非结构化数据:
- 存储和检索任意大小的对象(文件)
- 高可用、高持久性(多副本)
- 最终一致性模型(AP 系统)
- 兼容 S3 API(通过 swift3 中间件)
- 典型用途:镜像存储、备份、日志归档、静态资源
架构总览
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 客户端请求 │ ▼ swift-proxy(代理层,无状态,可水平扩展) │ ├── 认证中间件(Keystone) ├── 速率限制中间件 └── 路由层(一致性哈希环) │ ├── swift-account-server ← 账户元数据 ├── swift-container-server ← 容器元数据(对象列表) └── swift-object-server ← 对象数据存储 │ └── 后台进程 ├── swift-object-replicator ← 副本同步 ├── swift-object-auditor ← 数据完整性检查 └── swift-object-expirer ← 过期对象清理
|
核心概念
三层命名空间
1 2 3 4 5 6
| Account(账户) └── Container(容器,类似目录) └── Object(对象,文件 + 元数据)
URL 格式: https://swift.example.com/v1/{account}/{container}/{object}
|
存储策略(Storage Policy)
1 2 3 4 5
|
openstack container create --storage-policy ec-policy my-container
|
一致性哈希环(Ring)
这是 Swift 最核心的设计,解决了分布式存储中数据定位问题。
哈希环原理
1 2 3 4 5 6 7
| 哈希空间:0 ~ 2^32 - 1(约 43 亿)
将哈希空间均匀分成 2^部分数 个分区(partition) 每个分区分配到具体的存储节点(device)
对象定位: MD5(account/container/object) → 哈希值 → 分区 → 节点
|
1 2 3 4 5 6 7 8 9 10
|
class Ring: def get_nodes(self, account, container=None, obj=None): """返回存储该对象的节点列表(副本数个节点)""" key = hash_path(account, container, obj) part = bisect_left(self._part_shift, key) >> self._part_shift return part, self._get_part_nodes(part)
|
副本放置策略
1 2 3 4 5 6 7
| 分区 P 的 3 个副本放置规则: - 副本 1:节点 A(zone 1, region 1) - 副本 2:节点 B(zone 2, region 1) ← 不同 zone - 副本 3:节点 C(zone 1, region 2) ← 不同 region(如果有)
Zone 隔离:不同机架/机柜 Region 隔离:不同数据中心
|
节点扩容(Ring 重平衡)
1 2 3 4 5 6 7 8 9 10
| swift-ring-builder object.builder add \ --region 1 --zone 1 --ip 10.0.0.5 --port 6200 \ --device sdb --weight 100
swift-ring-builder object.builder rebalance
rsync object.ring.gz all-nodes:/etc/swift/
|
扩容时只有部分分区需要迁移,不会全量重新分配(一致性哈希的优势)。
数据写入流程
1 2 3 4 5 6 7 8 9 10 11 12 13
| 客户端 PUT /v1/account/container/object │ ▼ swift-proxy │ 1. 计算哈希,找到 3 个目标节点 │ 2. 并发向 3 个节点发送写请求 │ ├──► Node-1: 写入成功 ├──► Node-2: 写入成功 └──► Node-3: 写入超时(网络问题) │ ▼ 2/3 节点写入成功 → 返回客户端 201 Created (最终一致性:Node-3 稍后通过 replicator 同步)
|
写入仲裁(Quorum)
1 2 3 4 5 6 7
|
min_conns = quorum_size(self.app.object_ring.replica_count)
|
数据一致性保障
Replicator(副本同步)
1 2 3 4 5 6 7 8 9 10 11
|
class ObjectReplicator: def replicate(self): for partition in self.get_local_partitions(): nodes = self.ring.get_part_nodes(partition) for node in nodes: self.sync(partition, node)
|
Auditor(数据审计)
1 2 3 4 5 6 7 8 9 10 11 12 13
|
class ObjectAuditor: def audit_all_objects(self): for obj_path in self.get_all_objects(): computed_md5 = md5_of_file(obj_path) stored_md5 = get_stored_etag(obj_path) if computed_md5 != stored_md5: self.quarantine(obj_path)
|
纠删码(Erasure Coding)
EC 策略用更少的存储空间实现相同的持久性:
1 2 3 4 5 6
| 3 副本:存储开销 3x,可容忍 1 节点故障 EC 10+4:存储开销 1.4x,可容忍 4 节点故障
EC 原理: 原始数据 → 分成 10 个数据片段 + 4 个校验片段 任意 10 个片段可以恢复完整数据
|
1 2 3 4 5 6 7 8
|
[storage-policy:1] name = ec-policy policy_type = erasure_coding ec_type = liberasurecode_rs_vand ec_num_data_fragments = 10 ec_num_parity_fragments = 4
|
大对象支持
Swift 单个对象默认最大 5GB,大文件通过分段上传:
1 2 3 4 5 6 7 8 9
|
swift upload mycontainer --segment-size 1G largefile.tar.gz
|
中间件体系
Swift 的功能通过 WSGI 中间件栈扩展:
1 2 3 4 5 6 7 8 9
| [pipeline:main] pipeline = catch_errors gatekeeper healthcheck \ proxy-logging cache listing_formats \ bulk tempurl ratelimit authtoken \ keystoneauth staticweb copy \ container-quotas account-quotas \ slo dlo versioned_writes symlink \ proxy-server
|
| 中间件 |
功能 |
authtoken |
Keystone Token 验证 |
ratelimit |
请求速率限制 |
slo |
静态大对象支持 |
tempurl |
临时 URL(无需认证的临时访问) |
staticweb |
静态网站托管 |
versioned_writes |
对象版本控制 |
生产部署要点
1 2 3 4 5 6 7 8 9 10 11 12 13
| mkfs.xfs -f -i size=1024 /dev/sdb mount -o noatime,nodiratime,nobarrier /dev/sdb /srv/node/sdb
swift-recon --all
|