RBD Image映射到Object

本文详细解析了Ceph中AIO写操作的过程,包括如何将image的更新范围映射到具体对象上,以及Stripe概念的应用。通过深入探讨Striper::file_to_extents函数,揭示了文件偏移量到对象范围映射的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

该函数是将更新的image的范围映射到具体的对象上.这个部分涉及到stripe的概念,需要预先了解这个概念(可以参考官网对其的解释)

void aio_write(ImageCtx *ictx, uint64_t off, size_t len, const char *buf,
              ¦AioCompletion *c, int op_flags)
{            
...
  ¦ // map
  ¦ vector<ObjectExtent> extents;
  ¦ if (len > 0) {
  //将更新的image的范围映射到具体的对象上
  ¦ ¦ Striper::file_to_extents(ictx->cct, ictx->format_string,
                         ¦ ¦ ¦ &ictx->layout, off, clip_len, 0, extents);
  ¦ }

  ¦ for (vector<ObjectExtent>::iterator p = extents.begin(); p != extents.end(); ++p) {
  ¦ ¦ ldout(cct, 20) << " oid " << p->oid << " " << p->offset << "~" << p->length
                ¦ ¦ ¦<< " from " << p->buffer_extents << dendl;
  ¦ ¦ // assemble extent
  ¦ ¦ bufferlist bl;
  ¦ ¦ for (vector<pair<uint64_t,uint64_t> >::iterator q = p->buffer_extents.begin();
         ¦ q != p->buffer_extents.end();
         ¦ ++q) {
        bl.append(buf + q->first, q->second);
  ¦ ¦ }
  }
  ...
}  

void Striper::file_to_extents(CephContext *cct, const char *object_format,
                              const ceph_file_layout *layout,
                              uint64_t offset, uint64_t len, uint64_t trunc_size,
                              map<object_t,vector<ObjectExtent> >& object_extents,
                              uint64_t buffer_offset)
                              //offset 是相对于image的偏移位置, len是影响的范围
{
  ldout(cct, 10) << "file_to_extents " << offset << "~" << len 
                ¦<< " format " << object_format
                ¦<< dendl;
  assert(len > 0);

  /*
  ¦* we want only one extent per object!
  ¦* this means that each extent we read may map into different bits of the 
  ¦* final read buffer.. hence ObjectExtent.buffer_extents
  ¦*/

  __u32 object_size = layout->fl_object_size; //一个对象的字节大小
  __u32 su = layout->fl_stripe_unit;          //一个stripe的大小
  __u32 stripe_count = layout->fl_stripe_count; // 一个stripe中的unit(block)的数量
  assert(object_size >= su);
  uint64_t stripes_per_object = object_size / su;  //一个对象关联的stripe的数量
  ldout(cct, 20) << " su " << su << " sc " << stripe_count << " os " << object_size
                ¦<< " stripes_per_object " << stripes_per_object << dendl;

  uint64_t cur = offset;
  uint64_t left = len;
  while (left > 0) {
  ¦ // layout into objects
  ¦ uint64_t blockno = cur / su;          // which block   //cur位置跨越的block数
  ¦ uint64_t stripeno = blockno / stripe_count;    // which horizontal stripe        (Y) //cur位置跨越的stripe数
  ¦ uint64_t stripepos = blockno % stripe_count;   // which object in the object set (X) // blockno在stripe中的偏移位置
  ¦ uint64_t objectsetno = stripeno / stripes_per_object;       // which object set      //cur位置跨越的object set数
  ¦ uint64_t objectno = objectsetno * stripe_count + stripepos;  // object id            //cur所在的object(image中的object从0开始自然排序)注object从0开始编号

  ¦ // find oid, extent
  ¦ char buf[strlen(object_format) + 32];
  ¦ snprintf(buf, sizeof(buf), object_format, (long long unsigned)objectno);   //获取对象的oid,命名格式是{object_prefix}.%012llx,即对象的前缀+object的序号
  ¦ object_t oid = buf;

  ¦ // map range into object 映射到对象内,相对对象的偏移位置(x_offset)
  ¦ uint64_t block_start = (stripeno % stripes_per_object) * su;
  ¦ uint64_t block_off = cur % su;
  ¦ uint64_t max = su - block_off;

  ¦ uint64_t x_offset = block_start + block_off;
  //从偏移位置开始影响对象的范围
  ¦ uint64_t x_len;
  ¦ if (left > max)
  ¦ ¦ x_len = max;
  ¦ else
  ¦ ¦ x_len = left;

  ¦ ldout(cct, 20) << " off " << cur << " blockno " << blockno << " stripeno " << stripeno
                ¦ ¦<< " stripepos " << stripepos << " objectsetno " << objectsetno
                ¦ ¦<< " objectno " << objectno
                ¦ ¦<< " block_start " << block_start
                ¦ ¦<< " block_off " << block_off
                ¦ ¦<< " " << x_offset << "~" << x_len
                ¦ ¦<< dendl;

  ¦ ObjectExtent *ex = 0;
  ¦ vector<ObjectExtent>& exv = object_extents[oid];
  //如果object对象的该次操作时第一个,或者与之前的操作在object内部地址不联系则重新申请一个ObjectExtent来存储该次操作的一些信息(对象、偏移位置、范围)
  //否则直接更新之前的ObjectExtent,扩大影响的范围
  ¦ if (exv.empty() || exv.back().offset + exv.back().length != x_offset) {
  ¦ ¦ exv.resize(exv.size() + 1);
  ¦ ¦ ex = &exv.back();
  ¦ ¦ ex->oid = oid;
  ¦ ¦ ex->objectno = objectno;
  ¦ ¦ ex->oloc = OSDMap::file_to_object_locator(*layout);

  ¦ ¦ ex->offset = x_offset;
  ¦ ¦ ex->length = x_len;
  ¦ ¦ ex->truncate_size = object_truncate_size(cct, layout, objectno, trunc_size); //trunc_size == 0

  ¦ ¦ ldout(cct, 20) << " added new " << *ex << dendl;
  ¦ } else {
  ¦ ¦ // add to extent
  ¦ ¦ ex = &exv.back();
  ¦ ¦ ldout(cct, 20) << " adding in to " << *ex << dendl;
  ¦ ¦ ex->length += x_len;
  ¦ }
    //将该ObjectExtent与更新的数据做个映射(cur - offset + buffer_offset 相对更新数据的偏移位置,x_len范围)  
  ¦ ex->buffer_extents.push_back(make_pair(cur - offset + buffer_offset, x_len));
  ¦ ¦ ¦
  ¦ ldout(cct, 15) << "file_to_extents  " << *ex << " in " << ex->oloc << dendl;
  ¦ ldout(cct, 15) << "file_to_extents  " << *ex << " in " << ex->oloc << dendl;
  ¦ //ldout(cct, 0) << "map: ino " << ino << " oid " << ex.oid << " osd " << ex.osd << " offset " << ex.offset << " len " << ex.len << " ... left " << left << dendl;
  ¦ //减去已经映射到object对象上的部分,继续后续的映射。
  ¦ left -= x_len;
  ¦ cur += x_len;
  }
}


for (vector<ObjectExtent>::iterator p = extents.begin(); p != extents.end(); ++p) {
  ldout(cct, 20) << " oid " << p->oid << " " << p->offset << "~" << p->length
                 << " from " << p->buffer_extents << dendl;
  // assemble extent
  bufferlist bl;
  for (vector<pair<uint64_t,uint64_t> >::iterator q = p->buffer_extents.begin();
       q != p->buffer_extents.end();
       ++q) {
     //聚合同一个object上且object内地址连续的更新数据
    bl.append(buf + q->first, q->second);
  }
  ....
} 
<think>好的,用户问的是关于如何将RBD设备映射到LUN的问题。首先,我需要明确用户的具体需求是什么。RBD是Ceph的块存储设备,而LUN通常指存储区域网络(SAN)中的逻辑单元号,用于标识存储设备。用户可能是在配置Ceph存储以便通过iSCSI或其他协议让客户端识别为LUN。 接下来,我得考虑用户的使用场景。可能的情况是用户在使用Ceph集群,并希望将RBD镜像作为块设备提供给外部服务器使用,比如通过iSCSI或Fibre Channel。这时候需要将RBD设备映射为LUN,以便客户端可以挂载使用。 用户的身份可能是系统管理员或存储管理员,负责搭建维护存储基础设施。他们可能需要详细的步骤来配置映射,或者需要了解相关工具命令。此外,用户可能对Ceph存储协议有一定的基础,但需要更具体的操作指导。 用户的需求可能包括具体的配置步骤、工具推荐(如tgt或LIO),以及可能遇到的问题解决方法。他们可能没有提到但需要了解的部分包括权限设置、多路径配置、性能优化等深层内容。此外,安全方面的考虑,如认证访问控制,也可能是用户关心的点。 现在需要组织回答的结构。首先解释基本概念,然后分步骤说明如何通过不同方法实现映射,比如使用tgt-admin或rbd-target-api。接着讨论高级配置注意事项,最后给出验证管理的命令。确保语言简洁,步骤清晰,并包含示例命令,这样用户可以直接复制使用。 需要验证每个步骤的正确性,特别是命令中的参数路径是否正确。例如,使用tgt-admin时,确保指定正确的RBD镜像名称,以及正确的LUN ID。另外,提醒用户在配置iSCSI目标时的网络设置防火墙配置,避免连接问题。 可能遇到的问题包括权限不足、路径错误、服务未启动等,应该在注意事项中提及。同时,如果用户使用的是较新版本的Ceph,可能需要使用不同的工具或配置方法,比如Ceph的iSCSI网关,这可能需要额外说明。 最后,确保回答结构清晰,分点明确,使用用户容易理解的语言,避免过多专业术语,或在必要时解释术语。这样用户无论是新手还是有经验的管理员都能从中获得有用的信息。</think>### 一、概念解析 **RBD(RADOS Block Device)** Ceph分布式存储系统中的虚拟块设备服务,提供云端硬盘式存储能力 **LUN(Logical Unit Number)** 存储网络(SAN)中标识存储单元的逻辑编号,通过iSCSI/FC协议暴露给客户端 --- ### 二、映射原理示意图 ```mermaid graph LR Ceph集群-->|创建RBD镜像| RBD_Image[rbd_image] RBD_Image-->|通过iSCSI网关映射| LUN[LUN 0] LUN-->|客户端识别| Initiator[iSCSI Initiator] ``` --- ### 三、标准操作流程(基于Linux环境) #### 1. 创建RBD镜像 ```bash rbd create --size 1024 mypool/myimage # 创建1GB镜像 rbd info mypool/myimage # 验证创建 ``` #### 2. 安装iSCSI网关组件 ```bash # Ceph iSCSI网关(推荐方式) ceph-ansible install --tags iscsi-gateway # 或传统方式 yum install tgt # 使用TGT iSCSI target ``` #### 3. 配置iSCSI Target(以tgt为例) ```bash cat > /etc/tgt/conf.d/ceph.conf <<EOF <target iqn.2023-08.com.example:ceph> backing-store rbd/mypool/myimage # RBD路径格式:rbd/池名/镜像名 lun 1 # 指定LUN编号 initiator-address 192.168.1.0/24 # 允许访问的客户端IP段 </target> EOF ``` #### 4. 启动服务并验证 ```bash systemctl restart tgtd tgtadm --mode target --op show # 显示配置详情 ``` --- ### 四、高级配置方法 #### 1. 使用Ceph原生iSCSI网关 ```bash # 启用iSCSI网关 ceph dashboard iscsi-gateway create \ --name gw1 \ --portal 10.1.1.10:3260 \ --ssl false # 创建LUN映射 rbd target create iqn.2023-08.com.example:ceph rbd target portal create 10.1.1.10 rbd target group create mygroup rbd target group add mygroup iqn.2023-08.com.example:client ``` #### 2. 多路径配置示例 ```bash # /etc/multipath.conf devices { device { vendor "LIO-ORG" product "RBD" path_grouping_policy "failover" path_checker "tur" } } ``` --- ### 五、客户端连接流程 ```bash # 发现目标 iscsiadm -m discovery -t st -p 10.1.1.10 # 登录目标 iscsiadm -m node -T iqn.2023-08.com.example:ceph -p 10.1.1.10 -l # 查看新磁盘 lsblk | grep -i iscsi ``` --- ### 六、关键注意事项 1. **权限控制** ```bash # 创建Ceph客户端密钥 ceph auth get-or-create client.iscsi mon 'allow r' osd 'allow rwx' ``` 2. **性能优化参数** ```ini # /etc/ceph/iscsi-gateway.cfg [config] queue_depth = 128 numa_cpu_nodes = 0-1 ``` 3. **故障排查命令** ```bash journalctl -u ceph-iscsi # 查看网关日志 tcpdump -i eth0 port 3260 # 抓取iSCSI流量 rbd status mypool/myimage # 检查RBD状态 ``` --- ### 七、不同协议对比 | 协议 | 传输层 | 典型延迟 | 适用场景 | |---------|--------|----------|-----------------------| | iSCSI | TCP/IP | 0.5-2ms | 通用SAN | | NVMe-oF| RDMA | <0.1ms | 超低延迟需求 | | SRP | InfiniBand | 0.2ms | 高性能计算集群 | --- ### 八、数学表达 当配置多路径时,可用性计算公式: $$ \text{可用性} = 1 - (1 - p)^n $$ 其中: - $p$ = 单路径可用性(典型值0.999) - $n$ = 独立路径数量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值