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 等计算服务提供可靠的镜像支持。

相关推荐
努力打怪升级15 小时前
云计算介绍
云计算·openstack
哈里谢顿6 天前
Nova parse_args 函数详解
openstack
哈里谢顿8 天前
OpenStack 中的 nova-conductor 与 ironic-conductor 及其分布式锁机制详解
openstack
哈里谢顿12 天前
OpenStack oslo-config 详解
openstack
感哥17 天前
OpenStack Cinder 创建卷
openstack
感哥18 天前
OpenStack Cinder 架构
openstack
感哥18 天前
OpenStack Nova Scheduler 计算节点选择机制
openstack
感哥20 天前
OpenStack Nova 创建虚拟机
openstack
感哥21 天前
OpenStack Keystone详解
openstack