zoukankan      html  css  js  c++  java
  • 探索 OpenStack 之(10):深入镜像服务Glance

    本篇博文来探讨下镜像服务Glance。

    0.  基本概念

    0.1 基本功能

    Glance提供REST API来支持以下镜像操作:

    • 查询
    • 注册
    • 上传
    • 获取
    • 删除
    • 访问权限管理

    0.2 Glance REST API的版本V1和V2

    0.2.1功能差别

    Glance有两个版本的REST API V1 和 V2,两者之间还是有蛮大的不同:

    (1). V1只提供了基本的image和member操作功能:镜像创建、删除、下载、列表、详细信息查询、更新,以及镜像tenant成员的创建、删除和列表。

    (2). V2除了支持V1的所有功能外,主要是增加了如下功能:

    • 镜像 location 的添加、删除和修改等操作
    • metadata  namespace 操作
    • image tag 操作

    (3).V1 和V2对 image store 的支持是相同的。

    0.2.2 实现差别

    V1的实现上,有glance-api和glance-registry两个WSGI 服务,都提供REST API,只不过glance-API的REST API对外使用,glance-registry的API只由glance-api使用。

    而 V2在实现上,把 glance-registry 的功能合并到了 glance-api 中,减少了一个中间环节。

    0.2.3 使用差别

    目前,Glance Cli和Horizon默认还是使用V1版本的API。究其原因,

    • 一方面,似乎网上有看到说法说V2的性能没有V1好,我们也能看到使用V2的API的时候,需要多次调用REST API;
    • 另一方面,我认为V2里面新添加的功能似乎也不是镜像管理的必须功能,所以暂时没怎么被使用也是情理之中。

    0.3 image 的数据存放

    image 的元数据 通过glance-registry 存放在 db 中; image 的chunk 数据 通过 glance-store 存放在各种 backend store 中,并从中获取。

    0.4 image 的访问权限

    image 的 访问权限分为:

    • public 公共的:可以被所有的 tenant 使用。
    • private 私有的/项目的:只能被 image owner 所在的 tenant 使用。
    • shared 共享的:一个非共有的image 可以 共享给另外的 tenant,可通过member-* 操作来实现。
    • protected 受保护的:protected 的 image 不能被删除。

    0.5 image 的各种状态

    • queued:没有上传 image 数据,只有db 中的元数据。
    • saving:正在上传 image data
    • active:正常状态
    • deleted/pending_delete: 已删除/等待删除
    • killed:image 元数据不正确,等待被删除。

    状态图:

    • 'queued' => ('saving', 'active', 'deleted')
      'saving' => ('active', 'killed', 'deleted', 'queued')
      'active' => ('queued', 'pending_delete', 'deleted')
      'killed' => ('deleted')
      'pending_delete' => ('deleted')
      'deleted' => ()

    1. V1版本的代码实现

    1.1 代码模块

    从上图也可以看出,Glance V1主要是三个模块:

    • glance/common/
         wsgi.py -- WSGI server,开启glance-(api|registry)服务,接受请求,比如GET /images/bb8838d5-06b5-4f7e-b6ef-87c908f04cc7 HTTP/1.1
    • glance-api:提供对外REST API,同时调用glance-registry进行数据库操作,以及调用glance-store进行store操作
      • images.py -- 实现image-* 操作
        memebers.py --实现 member-* 操作
        router.py -- 将WSGI 收到的 HTTP 操作映射到 glance-api 模块的 Controller 类的方法.
    • glance-registry:向 glance-api 提供元数据数据库操作 REST API,默认在端口9191监听。 主要文件:
      • client/v1/
                api.py -- registry API 入口,调用client.py中的函数
                client.py --调用 images.py 和 members.py 中的function
        api/v1/
                images.py -- 调用 glance/db/sqlalchemy/api.py 实现 images 数据库相关操作
                memebers.py --调用 glance/db/sqlalchemy/api.py 实现 members数据库表的相关操作
        glance/db/sqlalchemy/
           api.py -- db操作api入口
           models.py -- 数据库表模型,每个表对应一个类
    • glance-store:这部分代码不是在 glance github 代码项目中,不理解为什么不是在一起,cinder也是类似的架构,但是cinder的所有代码都是在一个github项目中。
      • glance-store 向 glance-api 提供文件 backend.py 作为 store 操作的统一入口。主要函数包括:
        • create_stores:调用store.configure_add,glance-api服务启动的时候根据用户配置调用该函数来注册各个store
        • get_from_backend:调用store.get,从store中获取data chunks
        • get_size_from_backend:调用store.get_size,从store中获取image size
        • delete_from_backend:调用store.delete,从store中删除image data
        • store_add_to_backend:调用store.add,添加image到store
        • set_acls:调用store.set_acls,设置image的读写权限
      • glance-store 提供一个需要各个 store vendor 实现的基类Store,定义了几个需要实现的操作:
        • configure_add:根据用户配置来配置该 backend store
        • get:获取 image chunk data
        • get_size:获取image大小
        • add: 添加 image 到store中
        • delete: 从 store 中删除 image chunk data
        • set_acls: 设置 image 的读写权限
      • 目前支持的 backend store 见上图。各backend store所支持的功能差别很大,比如
        • cinder.py 只实现了方法 get_size,因为目前的 Glance 不支持 upload image  到 Cinder volume 中。
        • http.py 只实现了 get 和 get_size, 因此只能从http store download image 和查询 image size。
        • swift.py 的实现可以说是最全的,filesystem 次之。
      • image 存在于不同store 中时不同的 Location url 格式
        • cinder://volume-id
        • https://user:pass@example.com:80/images/some-id
          http://images.oracle.com/123456
          swift://example.com/container/obj-id
          swift://user:account:pass@authurl.com/container/obj-id
          wift+http://user:account:pass@authurl.com/container/obj-id
          s3://accesskey:secretkey@s3.amazonaws.com/bucket/key-id       
          file:///var/lib/glance/images/1
          s3+https://accesskey:secretkey@s3.amazonaws.com/bucket/key-id
      • 使用 backend store
        • 默认使用的 store 由配置文件 glance-api.conf 的配置项 default_store=<STORE> 指定
        • 可以使用的 stores 有配置文件 glance-api.conf 的配置项 stores=<STORES> 指定
        • 在Glance Cli image-create 中可使用参数 --store 指定目标backend store

    1.2 操作流程

    Glance Cli glanceclient SDK调用的REST API Glance 内部函数
     
     Gance 内部函数的具体实现

    image-create

    创建image

    POST /images
     
     
    glance/api/v1/images.py
     
    def create(self, req, image_meta, image_data):

    Adds a new image to Glance

    1. api/v1/images.py: _reserve.

    添加image 元数据到db,获取 image id,设置 image 状态为 queued。

    1.1. registry/client/v1/client.py:   def add_image(self, image_metadata)

    1.2. registry/api/v1/images.py: def create(self, req, body)

    1.3. db/sqlalchemy/api.py:   image_create


    2. api/v1/images.py: _handle_source

    处理 image data,根据image data 来源分为三种情况:

    2.1 如果 image_data // 如果直接传入了 image data

    2.1.1 def _upload(self, req, image_meta):

    将image data 存入指定的 backend store。注意Content-Type 必须为 application/octet-stream。

    2.1.1.1 registry.update_image_metadata // 设置 image 状态 为 saving

    2.1.1.2 upload_utils.upload_data_to_store // 
    check_quota //检查用户quota
    store_api.store_add_to_backend //调用 backend.py 的方法
    store.add //调用 backend store 实现的方法
    filesystems.py. def add //如果是filesystem的话,将 image chunk data 写入到一个以 image id 为文件名的文件

    2.1.2 def _activate //调用 glance-registry 设置 image 状态为 active

    2.2 如果是 copy_from,则异步执行上面2.1.1 ~ 2.1.2

    2.3 如果是指定 location的话,

    2.3.1 从location 指定的backend store 中获取 image size

    2.3.2 设置 image 状态 为 active

    image-update

    修改image metadata属性或者数据

    PUT /images/<ID>
     
     
    glance/api/v1/images.py
     

    def update(self, req, id, image_meta, image_data)


    Updates an existing image with the registry

    注意:

    (1)只允许使用 Location|Copy-From 来 修改状态为 queued 的 image

    (2)可修改image 访问权限属性,以及元数据。

    1. 修改 image 访问权限,调用 registry.get_image_members,再调用 store.set_acls 方法,但是它只有swift支持。

    2. 修改image 元数据,调用 registry.update_image_metadata 方法

    3. 上传 image data,调用 self._handle_source,见上面 #2.

    image-delete

    根据image id 删除 指定image

    DELETE /images/<ID>

    glance/api/v1/images.py
     

    def delete(self, req, id)


    Deletes the image and all its chunks from the Glance

    1. 调用 registry.get_image_metadata,获取 image 元数据


    2. 检查 image 是否可以被删除,比如是否是 protected等


    3. 调用 registry.update_image_metadata 设置状态为 deleted 或者 pending_delete

    4. 调用 upload_utils.initiate_deletion 开始 删除操作

    4.1 调用backend.py 的delete_from_backend 方法,它会调用 store.delete ,如果image存放在文件系统中的话,调用os.unlink(fn) 方法 删除文件。

    4.2 db_api.get_api().image_location_delete // 设置表image_locations的id和image_id对应item的 deleted,status,updated_at,deleted_at


    5. 调用 registry.delete_image_metadata 删除 image 元数据 

    5.1 registry/images.py: def delete(self, req, id)

    5.1.1 db/sqlalchemy/api.py: _image_locations_delete_all //Delete all image entries in db table image_locations for the given image id

    5.1.2 db/sqlalchemy/api.py: _image_child_entry_delete_all //Deletes all the  entries in db table image_properties for the given image id

    5.1.3 db/sqlalchemy/api.py: _image_child_entry_delete_all //Deletes all the entries in db table image_members for the given image id
     
    5.1.4 db/sqlalchemy/api.py: _image_child_entry_delete_all //Deletes all the entries in db table image_tags for the given image id

    image-list

    获取image列表

    GET /images -- Returns a set of brief metadata about images
     
     
    glance/api/v1/images.py
     

    def index(self, req)


    Returns the following information for all public, available images:

    • id -- The opaque image identifier
    • name -- The name of the image
    • disk_format -- The disk image format
    • container_format -- The "container" format of the image
    • checksum -- MD5 checksum of the image data
    • size -- Size of image data in bytes

    比如:

    {
                "name": "name3",
                "container_format": null,
                "disk_format": null,
                "checksum": null,
                "id": "1e7b0a5a-7b78-4673-8d56-3abff7b491ae",
                "size": 12336128
    }

    调用 registry.get_images_list ,从 db 中获取 image 元数据
     获取所有image的详细信息 GET /images/detail -- Returns a set of detailed metadata about  images
    glance/api/v1/images.py
     
    def detail(self, req)

    Returns detailed information for all available images。比如:
    {
                "status": "queued",
                "deleted_at": null,
                "name": "name3",
                "deleted": false,
                "container_format": null,
                "created_at": "2015-01-21T23:05:54",
                "disk_format": null,
                "updated_at": "2015-01-21T23:05:54",
                "min_disk": 0,
                "protected": false,
                "id": "1e7b0a5a-7b78-4673-8d56-3abff7b491ae",
                "min_ram": 0,
                "checksum": null,
                "owner": "fa2046aaead44a698de8268f94759fc1",
                "is_public": false,
                "virtual_size": null,
                "properties": {},
                "size": 12336128
            },

    直接从 db 中获取 image 的各项数据。

    1. 调用 registry.get_images_detail
    1.1 调用 registry/api/v1/images.py 中的 def detail
    1.1.1 调用 db/sqlalchemy/api.py 中的 image_get_all

    glance image-show

    根据image id显示指定image的元数据

    HEAD /images/<ID> -- Return metadata about an image with id <ID>
    glance/api/v1/images.py
     

    def meta(self, req, id)


    Returns metadata about an image in the HTTP headers of the response object

    从 db 中获取 image 部分元数据

    1. 调用 self.get_image_meta_or_404(self, request, image_id)
    1.1 调用 registry.get_image_metadata
    1.1.1 调用 db_api.image_get

    image-download

    下载image

    GET /images/<ID> 
    -- Return image data for image with id <ID>
    glance/api/v1/images.py
     

    def show(self, req, id)


    Returns an iterator that can be used to retrieve an image's  data along with the image metadata

    调用 glance-store 方法返回 image data。
     
    1. 调用 registry.get_image_metadata,再调用 self.db_api.image_get 来获取 image 元数据
     
    2. 调用 self._get_from_store 获取 image 数据
     
    2.1 调用 glance_store.location.get_location_from_uri, 从 image uri 中获取 location
    2.2 调用 backend.py 的 get_store_from_uri, 获取所用的 store
    2.3 调用 store.get 获取 image data
     

    member-list --image-id

    获取可访问指定image的tenant成员列表

    GET /images/{image_id}/members
    glance/api/v1/members.py
     

    def index(self, req, image_id)


    Return a list of dictionaries indicating the members of the  image, i.e., those tenants the image is shared with

    通过 glance-registry 执行 数据库操作。

    1.1 registry.get_image_members

    1.1.1 registry/api/v1/members.py: def index

    1.1.1.1db/sqlalchemy/api.py: image_get

    1.1.1.2db/sqlalchemy/api.py: image_member_find

    member-list --tenant-id

    获取指定tenant的可访问image列表

    GET /shared-images/{id}
    glance/api/v1/members.py
     

    def index_shared_images(self, req, id)


    Retrieves list of image memberships for the given member.

    通过 glance-registry 执行 数据库操作。

    1.1 registry.get_member_images

    1.1.1 registry/api/v1/members.py: def index_shared_images

    1.1.1.1 db/sqlalchemy/api.py: image_member_find

    member-create

    向指定image添加指定tenant可访问成员

    PUT /images/{image_id}/members/{id}
    glance/api/v1/members.py
     
    def update(self, req, image_id, id, body=None)
    Adds a membership to the image, or updates an existing one.

    通过 glance-registry 执行 数据库操作。

    1.1 registry.add_member

    1.1.1 registry/api/v1/members.py: def delete

    1.1.1.1 db/sqlalchemy/api.py: image_update

    member-delete

    删除指定image的指定可访问tenant成员

    DELETE /images/{image_id}/members/{id}
    glance/api/v1/members.py
     
    def delete(self, req, image_id, id) //Removes a membership from the image

    通过 glance-registry 执行 数据库操作。

    1.1 registry.delete_member

    1.1.1 registry/api/v1/members.py: def delete

    1.1.1.1db/sqlalchemy/api.py: image_get

    1.1.1.2 db/sqlalchemy/api.py: image_member_find

    1.1.1.3 db/sqlalchemy/api.py: image_member_delete

     2 小结

    Glance 的原理和实现相比较其它组件较简单、直接。V2 REST API 内容有不少增加,还需要进一步的研究。

  • 相关阅读:
    Compression algorithm (deflate)
    tcpip数据包编码解析(chunk and gzip)_space of Jialy_百度空间
    What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
    gzip压缩算法: gzip 所使用压缩算法的基本原理
    Decompressing a GZip Stream with Zlib
    Frequently Asked Questions about zlib
    how to decompress gzip stream with zlib
    自己动手写web服务器四(web服务器是如何通过压缩数据,web服务器的gzip模块的实现)
    What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
    C语言抓http gzip包并解压 失败 C/C++ ChinaUnix.net
  • 原文地址:https://www.cnblogs.com/sammyliu/p/4249151.html
Copyright © 2011-2022 走看看