找回密码
 立即注册
首页 业界区 业界 OpenStack Glance(镜像)

OpenStack Glance(镜像)

墨佳美 2025-9-23 19:08:49
Glance 是 OpenStack 的镜像服务(Image Service),它负责虚拟机镜像的发现、注册、检索和交付。它提供了一个 RESTful API,允许用户查询虚拟机镜像元数据并检索实际镜像。
1、概述

1.1 核心概念


  • 镜像(Image):虚拟机的模板,包含操作系统和必要的软件
  • 镜像元数据(Metadata):描述镜像的信息,如名称、大小、格式、操作系统类型等
  • 镜像格式:支持多种格式,如 QCOW2、RAW、VMDK、VHD 等
  • 存储后端:存储镜像数据的位置,可以是本地文件系统、对象存储等
1.2 架构


  • Glance API:接收用户请求,提供 RESTful API 接口
  • Glance Registry:管理镜像元数据(Queens 版本开始已被整合到 API 服务中)
  • Glance Store:处理镜像数据的存储和检索,支持多种后端存储
  • 数据库:存储镜像元数据信息
  1. +----------------+    +----------------+    +----------------+
  2. |    客户端      |    |   Nova 服务    |    |    管理员      |
  3. +-------+--------+    +-------+--------+    +-------+--------+
  4.         |                    |                    |
  5.         +--------------------+--------------------+
  6.                              |
  7.                              v
  8. +---------------------------------------------------------+
  9. |                     Glance API                          |
  10. +---------------------------------------------------------+
  11.         |                           |
  12.         v                           v
  13. +----------------+        +---------------------------+
  14. |   数据库       |        |       Glance Store        |
  15. | (元数据存储)  |        | (镜像数据存储)             |
  16. +----------------+        +---------------------------+
  17.                                         |
  18.                 +-----------------------+-----------------------+
  19.                 |                       |                       |
  20.         +-------v-------+       +-------v-------+       +-------v-------+
  21.         | 本地文件系统    |        |   对象存储    |       |   块存储/分布式|
  22.         | (file)      |        | (Swift/S3)  |       |  文件系统      |
  23.         +---------------+       +---------------+       +---------------+
复制代码
2、操作示例

2.1 创建镜像
  1. openstack image create "ubuntu-22.04" \
  2.   --file ./ubuntu-22.04.qcow2 \
  3.   --disk-format qcow2 \
  4.   --container-format bare \
  5.   --public \
  6.   --property hw_qemu_guest_agent=yes \
  7.   --property os_distro=ubuntu
复制代码

  • --file: 最重要参数,指定镜像文件路径并直接上传。
  • --disk-format: 必须指定。
  • --container-format: 通常指定为 bare。
  • --public: 设置为公共镜像。
  • --property: 设置自定义属性,Nova/Cinder 等组件会利用这些属性。
2.2 查看镜像列表
  1. openstack image list
复制代码
2.3 查看镜像详情
  1. openstack image show <image-id-or-name>
复制代码
2.4 更新镜像属性
  1. openstack image set <image-id> \
  2.   --min-disk 20 \
  3.   --min-ram 1024 \
  4.   --property hw_vif_model=virtio
复制代码
2.5 删除镜像
  1. openstack image delete <image-id>
复制代码
2.6 检查 Glance 服务状态
  1. openstack catalog list
  2. # 或者查看服务端点
  3. openstack endpoint list --service image
复制代码
3、配置与优化

3.1 核心配置文件

Glance 的主要配置文件是 /etc/glance/glance-api.conf,关键配置项包括:
  1. [database]
  2. connection = mysql+pymysql://glance:GLANCE_DBPASS@controller/glance
  3. [keystone_authtoken]
  4. www_authenticate_uri = http://controller:5000
  5. auth_url = http://controller:5000
  6. memcached_servers = controller:11211
  7. auth_type = password
  8. project_domain_name = Default
  9. user_domain_name = Default
  10. project_name = service
  11. username = glance
  12. password = GLANCE_PASS
  13. [glance_store]
  14. stores = file,http
  15. default_store = file
  16. filesystem_store_datadir = /var/lib/glance/images/
复制代码
3.2 性能优化建议


  • 使用分布式存储(如 Ceph)作为后端,提高可用性和性能
  • 配置镜像缓存,减少重复下载
  • 启用镜像压缩,节省存储空间和网络带宽
  • 对大镜像采用分块上传方式
4、源码

4.1 重要源码目录


  • glance/api/: RESTful API 的实现,是请求的入口。
  • glance/common/: 通用工具和中间件(如 Keystone 认证、策略检查)。
  • glance/db/: 数据库模型和抽象层(SQLAlchemy)。
  • glance/domain/: 核心领域模型(如 Image, Task)。
  • glance/async/: 异步任务(如复制镜像、迁移存储后端)的实现。
  • glance/store/: 存储后端抽象层 的实现。这是 Glance 最核心的模块之一,每个支持的存储类型(file, swift, rbd, s3)在此都有对应的 driver.py。
  • glance/cmd/: 服务启动脚本(如 api.py)。
4.2 上传流程

sequenceDiagram    participant Client as 客户端 (openstack CLI)    participant GlanceAPI    participant GlanceRegistry    participant DB as 数据库    participant Store as 存储后端        Client->>GlanceAPI: POST /v2/images (创建镜像元数据)    GlanceAPI->>DB: 创建镜像记录 (queued状态)    DB-->>GlanceAPI: 返回镜像ID    GlanceAPI-->>Client: 返回镜像ID和位置        Client->>GlanceAPI: PUT /v2/images/{image_id}/file (上传镜像数据)    GlanceAPI->>DB: 更新状态为 saving    GlanceAPI->>Store: 分块上传镜像数据    Store-->>GlanceAPI: 上传完成确认    GlanceAPI->>DB: 更新状态为 active    DB-->>GlanceAPI: 确认更新    GlanceAPI-->>Client: 返回成功响应

  • 客户端命令入口 (python-openstackclient)
    文件路径: openstackclient/image/v2/image.py
  1. class CreateImage(command.ShowOne):
  2.     def get_parser(self, prog_name):
  3.         parser = super(CreateImage, self).get_parser(prog_name)
  4.         parser.add_argument("name", metavar="<image-name>", help="New image name")
  5.         parser.add_argument("--file", metavar="<file>", help="Upload image from local file")
  6.         parser.add_argument("--disk-format", default="qcow2", help="Disk format (default: qcow2)")
  7.         parser.add_argument("--container-format", default="bare", help="Container format")
  8.         return parser
  9.     def take_action(self, parsed_args):
  10.         image_client = self.app.client_manager.image
  11.         
  12.         # 创建镜像元数据
  13.         image = image_client.create_image(
  14.             name=parsed_args.name,
  15.             disk_format=parsed_args.disk_format,
  16.             container_format=parsed_args.container_format
  17.         )
  18.         
  19.         # 如果有文件则上传
  20.         if parsed_args.file:
  21.             with open(parsed_args.file, 'rb') as image_data:
  22.                 image_client.upload_image(image.id, image_data)
  23.         
  24.         return self.dict2columns(image)
复制代码

  • Glance API - 创建镜像元数据
    文件路径: glance/api/v2/images.py
  1. class ImagesController(object):
  2.     @utils.mutating
  3.     def create(self, req, **kwargs):
  4.         # 验证输入数据
  5.         image_schema = schemas.image_create
  6.         data = kwargs.pop('body', {})
  7.         self.validate(data, image_schema)
  8.         
  9.         # 准备镜像属性
  10.         image_data = data['image']
  11.         image_properties = {
  12.             'name': image_data.get('name'),
  13.             'disk_format': image_data.get('disk_format'),
  14.             'container_format': image_data.get('container_format'),
  15.             'status': 'queued',  # 初始状态
  16.             'min_disk': image_data.get('min_disk', 0),
  17.             'min_ram': image_data.get('min_ram', 0),
  18.             'protected': image_data.get('protected', False),
  19.             'visibility': image_data.get('visibility', 'private')
  20.         }
  21.         
  22.         # 创建镜像记录
  23.         image_repo = self.gateway.get_repo(req.context)
  24.         image = image_repo.create(image_properties)
  25.         
  26.         # 构建响应
  27.         return web.Response(
  28.             status=201,
  29.             content_type='application/json',
  30.             body=jsonutils.dumps({'image': image}),
  31.             charset='utf-8'
  32.         )
复制代码

  • Glance Registry - 数据库操作
    文件路径: glance/db/sqlalchemy/api.py
  1. def image_create(context, values):
  2.     session = get_session()
  3.     with session.begin():
  4.         # 创建Image对象
  5.         image_ref = models.Image()
  6.         image_ref.update(values)
  7.         image_ref.save(session=session)
  8.         
  9.         # 处理额外属性
  10.         if 'properties' in values:
  11.             for key, value in values['properties'].items():
  12.                 prop_ref = models.ImageProperty()
  13.                 prop_ref.image_id = image_ref.id
  14.                 prop_ref.name = key
  15.                 prop_ref.value = value
  16.                 prop_ref.save(session=session)
  17.    
  18.     return _image_get(context, image_ref.id, session=session)
复制代码

  • Glance API - 上传镜像数据
    文件路径: glance/api/v2/image_data.py
  1. class ImageDataController(object):
  2.     @utils.mutating
  3.     def upload(self, req, image_id, data, content_length):
  4.         # 获取镜像记录
  5.         image_repo = self.gateway.get_repo(req.context)
  6.         image = image_repo.get(image_id)
  7.         
  8.         # 检查状态是否允许上传
  9.         if image.status not in ['queued', 'saving']:
  10.             raise exception.InvalidImageStatus(image_id=image_id)
  11.         
  12.         # 更新状态为saving
  13.         image_repo.update(image_id, {'status': 'saving'})
  14.         
  15.         try:
  16.             # 获取存储后端
  17.             store_api = self.gateway.get_store_api(req.context)
  18.             
  19.             # 分块上传数据
  20.             chunk_size = CONF.glance_store.upload_chunk_size
  21.             bytes_written = 0
  22.             checksum = hashlib.md5()
  23.             
  24.             while True:
  25.                 chunk = data.read(chunk_size)
  26.                 if not chunk:
  27.                     break
  28.                
  29.                 # 写入存储
  30.                 store_api.add_chunk(image_id, chunk, bytes_written)
  31.                
  32.                 # 更新进度
  33.                 bytes_written += len(chunk)
  34.                 checksum.update(chunk)
  35.                
  36.                 # 更新数据库进度
  37.                 if bytes_written % (chunk_size * 10) == 0:
  38.                     image_repo.update(image_id, {
  39.                         'size': bytes_written,
  40.                         'checksum': checksum.hexdigest()
  41.                     })
  42.             
  43.             # 完成上传
  44.             location, size, checksum_value, metadata = store_api.finalize(image_id)
  45.             
  46.             # 更新镜像记录
  47.             image_repo.update(image_id, {
  48.                 'status': 'active',
  49.                 'size': size,
  50.                 'checksum': checksum_value,
  51.                 'locations': [{'url': location, 'metadata': metadata}]
  52.             })
  53.             
  54.             return web.Response(status=204)  # No Content
  55.             
  56.         except Exception as e:
  57.             # 出错时标记为killed状态
  58.             image_repo.update(image_id, {'status': 'killed'})
  59.             raise
复制代码

  • 存储后端实现 - 文件系统存储
    文件路径: glance_store/_drivers/filesystem.py
  1. class Store(driver.Store):
  2.     def add_chunk(self, image_id, chunk_data, offset):
  3.         # 确保目录存在
  4.         store_dir = self._option_get('filesystem_store_datadir')
  5.         if not os.path.exists(store_dir):
  6.             os.makedirs(store_dir)
  7.         
  8.         # 打开文件并写入
  9.         image_path = os.path.join(store_dir, image_id)
  10.         with open(image_path, 'ab') as f:
  11.             f.seek(offset)
  12.             f.write(chunk_data)
  13.    
  14.     def finalize(self, image_id):
  15.         # 获取文件信息
  16.         store_dir = self._option_get('filesystem_store_datadir')
  17.         image_path = os.path.join(store_dir, image_id)
  18.         
  19.         # 计算文件大小和校验和
  20.         size = os.path.getsize(image_path)
  21.         with open(image_path, 'rb') as f:
  22.             checksum = hashlib.md5(f.read()).hexdigest()
  23.         
  24.         # 返回存储位置信息
  25.         return (
  26.             f"file://{image_path}",
  27.             size,
  28.             checksum,
  29.             {'fs_path': image_path}
  30.         )
复制代码

  • 存储后端实现 - Swift 对象存储
    文件路径: glance_store/_drivers/swift.py
  1. class Store(driver.Store):
  2.     def __init__(self):
  3.         # 创建Swift连接
  4.         self.conn = swiftclient.Connection(
  5.             authurl=CONF.swift.auth_address,
  6.             user=CONF.swift.user,
  7.             key=CONF.swift.key,
  8.             auth_version='2.0'
  9.         )
  10.    
  11.     def add_chunk(self, image_id, chunk_data, offset):
  12.         # Swift使用分段上传
  13.         if not hasattr(self, 'segments'):
  14.             self.segments = []
  15.             self.container = f"glance_{image_id}"
  16.             self.conn.put_container(self.container)
  17.         
  18.         # 创建分段
  19.         segment_name = f"{image_id}_seg{len(self.segments)}"
  20.         self.conn.put_object(
  21.             self.container,
  22.             segment_name,
  23.             chunk_data
  24.         )
  25.         self.segments.append(segment_name)
  26.    
  27.     def finalize(self, image_id):
  28.         # 创建manifest文件
  29.         manifest = {
  30.             'container': self.container,
  31.             'object': image_id,
  32.             'segments': self.segments
  33.         }
  34.         
  35.         # 上传manifest
  36.         self.conn.put_object(
  37.             CONF.swift.container,
  38.             image_id,
  39.             json.dumps(manifest),
  40.             headers={'X-Object-Manifest': f"{self.container}/{image_id}_seg"}
  41.         )
  42.         
  43.         # 获取对象信息
  44.         obj_info = self.conn.head_object(CONF.swift.container, image_id)
  45.         
  46.         return (
  47.             f"swift://{CONF.swift.container}/{image_id}",
  48.             int(obj_info['content-length']),
  49.             obj_info['etag'],
  50.             {'swift_container': CONF.swift.container}
  51.         )
复制代码
5、总结

Glance 作为 OpenStack 的镜像服务,为整个云平台提供了统一的镜像管理解决方案。它支持多种镜像格式和存储后端,具有良好的灵活性和扩展性。通过 Glance,用户可以方便地管理虚拟机镜像,为 Nova 等计算服务提供可靠的镜像支持。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册