OpenStack Glance(镜像)

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:处理镜像数据的存储和检索,支持多种后端存储
  • 数据库:存储镜像元数据信息
plaintext 复制代码
+----------------+    +----------------+    +----------------+
|    客户端      |    |   Nova 服务    |    |    管理员      |
+-------+--------+    +-------+--------+    +-------+--------+
        |                    |                    |
        +--------------------+--------------------+
                             |
                             v
+---------------------------------------------------------+
|                     Glance API                          |
+---------------------------------------------------------+
        |                           |
        v                           v
+----------------+        +---------------------------+
|   数据库       |        |       Glance Store        |
| (元数据存储)  |        | (镜像数据存储)             |
+----------------+        +---------------------------+
                                        |
                +-----------------------+-----------------------+
                |                       |                       |
        +-------v-------+       +-------v-------+       +-------v-------+
        | 本地文件系统    |        |   对象存储    |       |   块存储/分布式|
        | (file)      |        | (Swift/S3)  |       |  文件系统      |
        +---------------+       +---------------+       +---------------+

2、操作示例

2.1 创建镜像

bash 复制代码
openstack image create "ubuntu-22.04" \
  --file ./ubuntu-22.04.qcow2 \
  --disk-format qcow2 \
  --container-format bare \
  --public \
  --property hw_qemu_guest_agent=yes \
  --property os_distro=ubuntu
  • --file: 最重要参数,指定镜像文件路径并直接上传。
  • --disk-format: 必须指定。
  • --container-format: 通常指定为 bare
  • --public: 设置为公共镜像。
  • --property: 设置自定义属性,Nova/Cinder 等组件会利用这些属性。

2.2 查看镜像列表

bash 复制代码
openstack image list

2.3 查看镜像详情

bash 复制代码
openstack image show <image-id-or-name>

2.4 更新镜像属性

bash 复制代码
openstack image set <image-id> \
  --min-disk 20 \
  --min-ram 1024 \
  --property hw_vif_model=virtio

2.5 删除镜像

bash 复制代码
openstack image delete <image-id>

2.6 检查 Glance 服务状态

bash 复制代码
openstack catalog list
# 或者查看服务端点
openstack endpoint list --service image

3、配置与优化

3.1 核心配置文件

Glance 的主要配置文件是 /etc/glance/glance-api.conf,关键配置项包括:

ini 复制代码
[database]
connection = mysql+pymysql://glance:GLANCE_DBPASS@controller/glance

[keystone_authtoken]
www_authenticate_uri = http://controller:5000
auth_url = http://controller:5000
memcached_servers = controller:11211
auth_type = password
project_domain_name = Default
user_domain_name = Default
project_name = service
username = glance
password = GLANCE_PASS

[glance_store]
stores = file,http
default_store = file
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: 返回成功响应

  1. 客户端命令入口 (python-openstackclient)
    文件路径: openstackclient/image/v2/image.py
python 复制代码
class CreateImage(command.ShowOne):
    def get_parser(self, prog_name):
        parser = super(CreateImage, self).get_parser(prog_name)
        parser.add_argument("name", metavar="<image-name>", help="New image name")
        parser.add_argument("--file", metavar="<file>", help="Upload image from local file")
        parser.add_argument("--disk-format", default="qcow2", help="Disk format (default: qcow2)")
        parser.add_argument("--container-format", default="bare", help="Container format")
        return parser

    def take_action(self, parsed_args):
        image_client = self.app.client_manager.image
        
        # 创建镜像元数据
        image = image_client.create_image(
            name=parsed_args.name,
            disk_format=parsed_args.disk_format,
            container_format=parsed_args.container_format
        )
        
        # 如果有文件则上传
        if parsed_args.file:
            with open(parsed_args.file, 'rb') as image_data:
                image_client.upload_image(image.id, image_data)
        
        return self.dict2columns(image)
  1. Glance API - 创建镜像元数据
    文件路径: glance/api/v2/images.py
python 复制代码
class ImagesController(object):
    @utils.mutating
    def create(self, req, **kwargs):
        # 验证输入数据
        image_schema = schemas.image_create
        data = kwargs.pop('body', {})
        self.validate(data, image_schema)
        
        # 准备镜像属性
        image_data = data['image']
        image_properties = {
            'name': image_data.get('name'),
            'disk_format': image_data.get('disk_format'),
            'container_format': image_data.get('container_format'),
            'status': 'queued',  # 初始状态
            'min_disk': image_data.get('min_disk', 0),
            'min_ram': image_data.get('min_ram', 0),
            'protected': image_data.get('protected', False),
            'visibility': image_data.get('visibility', 'private')
        }
        
        # 创建镜像记录
        image_repo = self.gateway.get_repo(req.context)
        image = image_repo.create(image_properties)
        
        # 构建响应
        return web.Response(
            status=201,
            content_type='application/json',
            body=jsonutils.dumps({'image': image}),
            charset='utf-8'
        )
  1. Glance Registry - 数据库操作
    文件路径: glance/db/sqlalchemy/api.py
python 复制代码
def image_create(context, values):
    session = get_session()
    with session.begin():
        # 创建Image对象
        image_ref = models.Image()
        image_ref.update(values)
        image_ref.save(session=session)
        
        # 处理额外属性
        if 'properties' in values:
            for key, value in values['properties'].items():
                prop_ref = models.ImageProperty()
                prop_ref.image_id = image_ref.id
                prop_ref.name = key
                prop_ref.value = value
                prop_ref.save(session=session)
    
    return _image_get(context, image_ref.id, session=session)
  1. Glance API - 上传镜像数据
    文件路径: glance/api/v2/image_data.py
python 复制代码
class ImageDataController(object):
    @utils.mutating
    def upload(self, req, image_id, data, content_length):
        # 获取镜像记录
        image_repo = self.gateway.get_repo(req.context)
        image = image_repo.get(image_id)
        
        # 检查状态是否允许上传
        if image.status not in ['queued', 'saving']:
            raise exception.InvalidImageStatus(image_id=image_id)
        
        # 更新状态为saving
        image_repo.update(image_id, {'status': 'saving'})
        
        try:
            # 获取存储后端
            store_api = self.gateway.get_store_api(req.context)
            
            # 分块上传数据
            chunk_size = CONF.glance_store.upload_chunk_size
            bytes_written = 0
            checksum = hashlib.md5()
            
            while True:
                chunk = data.read(chunk_size)
                if not chunk:
                    break
                
                # 写入存储
                store_api.add_chunk(image_id, chunk, bytes_written)
                
                # 更新进度
                bytes_written += len(chunk)
                checksum.update(chunk)
                
                # 更新数据库进度
                if bytes_written % (chunk_size * 10) == 0:
                    image_repo.update(image_id, {
                        'size': bytes_written,
                        'checksum': checksum.hexdigest()
                    })
            
            # 完成上传
            location, size, checksum_value, metadata = store_api.finalize(image_id)
            
            # 更新镜像记录
            image_repo.update(image_id, {
                'status': 'active',
                'size': size,
                'checksum': checksum_value,
                'locations': [{'url': location, 'metadata': metadata}]
            })
            
            return web.Response(status=204)  # No Content
            
        except Exception as e:
            # 出错时标记为killed状态
            image_repo.update(image_id, {'status': 'killed'})
            raise
  1. 存储后端实现 - 文件系统存储
    文件路径: glance_store/_drivers/filesystem.py
python 复制代码
class Store(driver.Store):
    def add_chunk(self, image_id, chunk_data, offset):
        # 确保目录存在
        store_dir = self._option_get('filesystem_store_datadir')
        if not os.path.exists(store_dir):
            os.makedirs(store_dir)
        
        # 打开文件并写入
        image_path = os.path.join(store_dir, image_id)
        with open(image_path, 'ab') as f:
            f.seek(offset)
            f.write(chunk_data)
    
    def finalize(self, image_id):
        # 获取文件信息
        store_dir = self._option_get('filesystem_store_datadir')
        image_path = os.path.join(store_dir, image_id)
        
        # 计算文件大小和校验和
        size = os.path.getsize(image_path)
        with open(image_path, 'rb') as f:
            checksum = hashlib.md5(f.read()).hexdigest()
        
        # 返回存储位置信息
        return (
            f"file://{image_path}",
            size,
            checksum,
            {'fs_path': image_path}
        )
  1. 存储后端实现 - Swift 对象存储
    文件路径: glance_store/_drivers/swift.py
python 复制代码
class Store(driver.Store):
    def __init__(self):
        # 创建Swift连接
        self.conn = swiftclient.Connection(
            authurl=CONF.swift.auth_address,
            user=CONF.swift.user,
            key=CONF.swift.key,
            auth_version='2.0'
        )
    
    def add_chunk(self, image_id, chunk_data, offset):
        # Swift使用分段上传
        if not hasattr(self, 'segments'):
            self.segments = []
            self.container = f"glance_{image_id}"
            self.conn.put_container(self.container)
        
        # 创建分段
        segment_name = f"{image_id}_seg{len(self.segments)}"
        self.conn.put_object(
            self.container,
            segment_name,
            chunk_data
        )
        self.segments.append(segment_name)
    
    def finalize(self, image_id):
        # 创建manifest文件
        manifest = {
            'container': self.container,
            'object': image_id,
            'segments': self.segments
        }
        
        # 上传manifest
        self.conn.put_object(
            CONF.swift.container,
            image_id,
            json.dumps(manifest),
            headers={'X-Object-Manifest': f"{self.container}/{image_id}_seg"}
        )
        
        # 获取对象信息
        obj_info = self.conn.head_object(CONF.swift.container, image_id)
        
        return (
            f"swift://{CONF.swift.container}/{image_id}",
            int(obj_info['content-length']),
            obj_info['etag'],
            {'swift_container': CONF.swift.container}
        )

5、总结

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

相关推荐
感哥5 小时前
OpenStack Keystone详解
openstack
安全菜鸟9 天前
传统方式部署OpenStack具体教程
openstack
哈里谢顿2 个月前
Ironic 中 Clean/deploy Step 延迟执行的原因分析
openstack
哈里谢顿2 个月前
ironic中为什么 IPMI Hardware Type 必须支持 IPMIManagement
openstack
哈里谢顿2 个月前
Ironic 中各个接口的作用详解
openstack
CZIDC2 个月前
博客摘录「 华为云平台-FusionSphere OpenStack 8.2.1 系统加固」2025年7月15日
linux·服务器·笔记·华为云·openstack
行止62 个月前
OpenStack云平台管理
linux·openstack
曼汐 .2 个月前
私有云平台实战-OpenStack
openstack
哈里谢顿2 个月前
python的Mixin设计模式学习,以ironic-python-agent代码为例
openstack