Cinder 的卷创建(create volume)是块存储服务的核心操作,涉及从请求接收、调度决策到存储后端实际创建的完整流程。
1、流程概览
创建卷的完整流程涉及 Cinder 多个组件的协同工作,整体流程如下:- 客户端 → cinder-api → 消息队列 → cinder-scheduler → 消息队列 → cinder-volume → 存储后端
- ↓ ↓
- 数据库 数据库(更新状态)
复制代码
- 客户端发起请求:通过 CLI、Dashboard 或 API 调用创建卷。
- API 服务处理:验证请求、解析参数、初始化卷元数据。
- 调度器选择后端:基于过滤和权重策略选择最优存储节点 / 后端。
- 卷服务执行操作:调用存储后端驱动创建实际卷,并更新状态。
- 返回结果:将卷状态(如 available)返回给客户端。
2、源码分析
2.1 客户端请求发起
客户端通过 OpenStack API 发起卷创建请求,示例 CLI 命令:- openstack volume create --size 10 --type lvm-type my-volume
复制代码 该命令会向 cinder-api 发送 HTTP POST 请求(默认端点:http://:8776/v3//volumes)。
2.2 API 服务处理(cinder-api)
cinder-api 负责接收并验证请求cinder/api/v3/volumes.py,关键逻辑:
- 验证请求合法性(权限、配额、参数格式)。
- 生成卷元数据并保存到数据库(初始状态 creating)。
- 通过 volume_api.create_volume 向消息队列发送异步任务。
- # cinder/api/v3/volumes.py
- class VolumeController(wsgi.Controller):
- @wsgi.response(202)
- @validation.schema(volumes_schema.create)
- def create(self, req, body):
- """处理卷创建请求"""
- context = req.environ['cinder.context']
- volume_data = body['volume']
-
- # 1. 参数解析与验证
- size = volume_data.get('size') # 卷大小(GB)
- volume_type = volume_data.get('volume_type') # 卷类型(关联存储后端)
- availability_zone = volume_data.get('availability_zone') # 可用区
-
- # 验证权限、配额(如是否允许创建10GB卷)
- self._check_volume_quota(context, size)
-
- # 2. 初始化卷元数据
- volume = {
- 'size': size,
- 'status': 'creating', # 初始状态为创建中
- 'volume_type_id': self._get_volume_type_id(volume_type),
- 'availability_zone': availability_zone,
- # 其他元数据:名称、描述、项目ID等
- }
-
- # 3. 保存初始状态到数据库
- volume_ref = objects.Volume(context=context, **volume)
- volume_ref.create() # 调用ORM保存到数据库
-
- # 4. 发送创建任务到消息队列,由scheduler或volume服务处理
- self.volume_api.create_volume(
- context,
- volume_ref,
- filter_properties=self._get_filter_properties(req, volume_data)
- )
-
- # 5. 返回卷信息(状态为creating)
- return self._view_builder.detail(req, volume_ref)
复制代码 2.3 调度器选择存储后端(cinder-scheduler)
cinder-scheduler 从消息队列接收任务,选择最优存储后端, cinder/scheduler/filter_scheduler.py- # cinder/scheduler/filter_scheduler.py
- class FilterScheduler(scheduler.Scheduler):
- def schedule_create_volume(self, context, request_spec, filter_properties):
- """选择存储后端创建卷"""
- # 1. 获取候选存储后端
- hosts = self.host_manager.get_all_host_states(context)
-
- # 2. 过滤后端(排除不符合条件的存储节点)
- filtered_hosts = self.host_manager.filter_hosts(
- hosts, request_spec, filter_properties)
- if not filtered_hosts:
- raise exception.NoValidHost(reason="No suitable storage backend found")
-
- # 3. 计算权重并排序(选择最优后端)
- weighed_hosts = self.host_manager.weigh_hosts(
- filtered_hosts, request_spec, filter_properties)
- best_host = weighed_hosts[0] # 得分最高的后端
-
- # 4. 向目标存储节点的cinder-volume发送创建请求
- self._schedule_create_volume(context, best_host, request_spec)
复制代码 2.3.1 过滤阶段(Filters)
- CapacityFilter(cinder/scheduler/filters/capacity_filter.py):检查后端可用容量是否满足卷大小。
- class CapacityFilter(filters.BaseHostFilter):
- def host_passes(self, host_state, filter_properties):
- # 所需容量 = 卷大小 + 预留空间
- required = filter_properties['request_spec']['volume_size']
- # 可用容量 = 总容量 - 已用容量 - 预留容量
- available = host_state.free_capacity_gb - host_state.reserved_percentage
- return available >= required
复制代码
- AvailabilityZoneFilter(cinder/scheduler/filters/availability_zone_filter.py):过滤不在指定可用区的后端。
- CapabilitiesFilter(cinder/scheduler/filters/capabilities_filter.py):检查后端是否支持卷类型要求的特性(如 thin_provisioning)
2.3.2 权重阶段(Weighters)
默认启用 CapacityWeigher(cinder/scheduler/weights/capacity.py),优先选择可用容量多的后端- class CapacityWeigher(weights.BaseWeigher):
- def weigh(self, host_state, weight_properties):
- # 可用容量越高,权重越大
- return host_state.free_capacity_gb * self.weight_multiplier
复制代码 2.4 卷服务执行创建操作(cinder-volume)
目标存储节点的 cinder-volume 服务从消息队列接收任务,调用存储后端驱动创建卷,cinder/volume/manager.py- # cinder/volume/manager.py
- class VolumeManager(manager.SchedulerDependentManager):
- def create_volume(self, context, volume, host):
- """执行卷创建操作"""
- # 1. 锁定卷(避免并发操作)
- volume = self._get_volume_and_lock(context, volume['id'])
-
- try:
- # 2. 调用存储后端驱动创建卷
- driver = self.driver # 如LVM驱动、Ceph驱动
- # 驱动创建卷(返回卷在后端的路径/标识)
- provider_location = driver.create_volume(volume)
-
- # 3. 更新卷状态为可用(available)
- volume.update({
- 'status': 'available',
- 'provider_location': provider_location, # 存储后端中的卷标识
- })
- volume.save()
-
- except Exception as e:
- # 出错时更新状态为error
- volume.update({'status': 'error'})
- volume.save()
- raise exception.VolumeCreationFailed(reason=str(e))
复制代码 2.5 存储后端驱动实现(以 LVM 为例)
LVM 驱动代码位于 cinder/volume/drivers/lvm.py,create_volume 方法实际调用 LVM 命令创建逻辑卷:- # cinder/volume/drivers/lvm.py
- class LVMVolumeDriver(volume_driver.VolumeDriver):
- def create_volume(self, volume):
- """通过LVM创建卷"""
- # 卷大小(转换为MB)
- size_in_mb = volume['size'] * 1024
- # 卷名(通常为volume-<uuid>)
- volume_name = self._get_volume_name(volume)
- # 卷组名(从配置读取,如cinder-volumes)
- vg_name = self.configuration.lvm_volume_group
-
- # 执行lvcreate命令:创建指定大小的逻辑卷
- self._execute(
- 'lvcreate', '-L', f'{size_in_mb}M', '-n', volume_name, vg_name,
- run_as_root=True
- )
-
- # 返回卷路径(如/dev/cinder-volumes/volume-xxx)
- return f"/dev/{vg_name}/{volume_name}"
复制代码 3、常用配置(cinder.conf)
3.1 核心配置([DEFAULT] 段)
- [DEFAULT]
- # 默认卷类型(未指定时使用)
- default_volume_type = lvm-type
- # 卷大小限制(最小/最大GB)
- min_volume_size = 1
- max_volume_size = 10240
- # 并发创建卷的最大数量
- volume_api_workers = 4 # 建议与CPU核心数一致
- # 消息队列配置(与其他组件通信)
- transport_url = rabbit://openstack:password@controller:5672/
复制代码 3.2 调度器配置([scheduler] 段)
- [scheduler]
- # 启用的过滤器(按执行顺序)
- enabled_filters = CapacityFilter,AvailabilityZoneFilter,CapabilitiesFilter
- # 启用的权重器
- enabled_weighters = CapacityWeigher
- # 容量权重乘数(值越大,可用容量影响越强)
- capacity_weight_multiplier = 1.0
- # 调度器缓存过期时间(秒)
- scheduler_cache_expiry = 60
复制代码 3.3 存储后端配置(以 LVM 为例)
- # 定义LVM存储后端
- [lvm]
- volume_driver = cinder.volume.drivers.lvm.LVMVolumeDriver
- volume_group = cinder-volumes # LVM卷组名
- target_protocol = iscsi # 共享协议(iscsi或fc)
- target_helper = tgtadm # iSCSI目标管理工具
- volume_backend_name = lvm-backend # 后端名称(用于卷类型关联)
- # 可选:配置thin provisioning(瘦分配)
- lvm_type = thin
- thin_pool_name = cinder-thin-pool # 瘦池名称
复制代码 3.4 多后端部署
通过配置多个后端段(如 [lvm]、[ceph])支持混合存储,示例:- [DEFAULT]
- enabled_backends = lvm,ceph
- [lvm]
- # LVM后端配置...
- [ceph]
- volume_driver = cinder.volume.drivers.rbd.RBDDriver
- rbd_pool = volumes
- rbd_ceph_conf = /etc/ceph/ceph.conf
- volume_backend_name = ceph-backend
复制代码 4、常见问题
日志位置:
- /var/log/cinder/api.log(API 层)
- /var/log/cinder/scheduler.log(调度层)
- /var/log/cinder/volume.log(卷服务层)。
常见原因:
- 存储后端容量不足(CapacityFilter 过滤)。
- 卷类型与后端不匹配(CapabilitiesFilter 过滤)。
- 驱动执行失败(如 LVM 卷组不存在)。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |