QingStor 对象存储发布新版本 Python SDK

QingStor 对象存储发布了新版本的 Python SDK ,并已开源在 https://github.com/yunify/qingstor-sdk-python 。为说明新版本 Python SDK 和 旧版本 SDK 的区别与联系,本日志将会介绍新版本 Python SDK 的历史背景,变化革新和未来规划。在日志的最后,我们将会展示如何使用新版本的 Python SDK。

历史背景

在 QingStor 对象存储于 2016 年 1 月 6 日 开始公测时, 我们便在 qingcloud-sdk-python 中以 面向对象 形式的接口提供了 QingStor 对象存储的 Python SDK (为行文方便,我们在下文中将此 SDK 称为 旧版 Python SDK )。qingcloud-sdk-python 为 手动实现 的 SDK, 旨在提供 所有青云 QingCloud 服务 的访问。

变化革新

为了加快各语言 SDK 的开发效率,及减小各语言 SDK 的运维成本,我们决定将 SDK 的开 发及后期维护自动化。同时,考虑到移动端用户对空间的敏感,我们决定将 QingStor 对象存储的 SDK 与青云 QingCloud 的其它服务进行分离。

最终,我们于 2016 年 12 月 5 日 发布了 SDK 生成工具 Snips , 及使用 Snips 生成的六种语言 (Go, Ruby, JAVA, Swift, PHP, JS) 的 SDK, 如 qingstor-sdk-go

为了统一所有语言 SDK 的生成和维护, 我们于 2017 年 1 月 12 日 发布了 新版 Python SDK qingstor-sdk-python, (为行文方便,我们在下文中将此 SDK 称为 新版 Python SDK )。

  • qingstor-sdk-python 不再与原有的 qingcloud-sdk-python 捆绑,可以灵活地应用于更多场景。
  • qingstor-sdk-python 有着更完善的 API 设计,对开发者屏蔽了底层的细节,开发者无需关注具体的请求拼装,只需要调用对应的接口即可。

未来规划

考虑到所有语言 SDK 使用接口的统一,新版 Python SDK 的接口实现选择了 非面向对象 的形式, 即与 旧版 Python SDK 不兼容。

  • 旧版 Python SDK 除修复 Bug 外,我们将不再维护。
  • 新版 Python SDK 将由 QingStor 直接维护,享受到最及时的新功能增加, BUG 修复和技术支持。

如何使用新版 Python SDK

使用 SDK 之前请先在青云控制台申请 access key 。

准备工作

发起请求前首先建立需要初始化服务:

from qingstor.sdk.service.qingstor import QingStor
from qingstor.sdk.config import Config

config = Config('ACCESS_KEY_ID_EXAMPLE', 'SECRET_ACCESS_KEY_EXAMPLE')
service = QingStor(config)

初始化并创建 Bucket, 需要指定 Bucket 名称和所在 Zone:

bucket = qingstor.Bucket('test-bucket', 'sh1a')
output = bucket.put()

可以使用 dir(output) 的形式来获取全部可用的属性。特别的,output.headers 是一个包含返回中全部 header 的字典;如果请求失败, output.content 将会存储返回的错误信息。

Objects 操作

接下来将会展示创建,下载,删除一个 Object 的全过程:

创建 Object

with open('/tmp/sdk_bin') as f:
output = bucket.put_object(
    'example_key', body=f
)

创建一个 Object 时,允许在body中传入一个 file like 对象,也支持直接传入一个字符串。上传一个比较大的 Object 时,建议使用 file 对象,避免将全部数据一次性读入内存。

下载 Object

output = bucket.get_object('example_key')
with open(local_path, "wb") as f:
    for chunk in output.iter_content():
        f.write(chunk)

下载一个 Object 时,建议使用 output.iter_content() 来获取文件内容。如果访问 output.content ,将会把全部数据一次性读入内存。

删除 Object

output = bucket.delete_object('example_key')

使用中遇到问题请在 项目 Issues 区 或者直接提交工单反馈。

SDN 网络直通服务上线

sdn_passthrough

极大提升 Docker 的网络性能

运行在虚拟机(VM)上的 Docker 可直接使用云平台的 SDN,避免两层网络的重叠带来的数据包容量损耗;

每一台 Docker Instance 均可独享一个网卡的转发能力,并使用 IaaS 平台所有网络功能。

大幅简化 Docker 的系统配置

只需通过创建网卡、使用「Hostnic」插件将网卡逐一与 Docker Instance 挂载,便可以完成对 Docker 网络的配置;

支持 Docker Instance 独立挂载 EIP,独立配置防火墙策略(SG),及挂载为负载均衡 (LB)后端。

传统 Docker 部署方案的问题

1.严重的网络性能损耗

传统的基于 VM 的 Docker 部署方案,需要采用 Overlay 或 Bridge 网络插件实现 VM 内部的虚拟网络,多次虚拟化对性能带来了重重损耗。经测试,在云平台上直接使用 Docker Overlay 会损失近 3/4 的性能。

2.配置复杂度高

主流的 Docker 管理平台需要对 IP 地址做转换,以提供业务对内网和外网的地址。本身 Docker 实现这些功能就比较复杂,而在此基础上,Docker 如果运行在云平台上则还需要对云平台的 IP 再做一层地址转换,会带来额外的复杂度。

上述问题的根源在于,云平台和 Docker 平台都有自己的 SDN 实现,功能重合,使用时需要嵌套。

SDN 网络直通解决方案的优势

SDN 网络直通功能使 Docker 可直接使用 IaaS 平台的 SDN,代替其自身的网络功能,从而达到提高性能和简化使用的目地。SDN直通包含两个方面:

1.IaaS 平台网卡管理

提供网卡接口,让虚拟主机能够挂载多个网卡,这些网卡可以属于相同或者不同的网络。每个网卡能够管理自己的私网 IP、公网 IP、负载均衡器和防火墙等功能。

2.Hostnic 插件

Hostnic 是青云开发的 Docker 网络插件。在启动 Docker 实例的时候,可以挂载虚拟主机上的网卡, 并配置 IP 地址和路由。启动之后, Docker 实例就加入了云平台 SDN 提供的网络,能够使用云平台所有的网络功能。

7 步简单操作即可赋予 Docker 应用强大的网络功能

  1. 网络规划:对于一套基于 Docker 的业务系统,有两个网络需求:A. 管理网络, 用于部署和管理云主机;B. 用户网络,用于运行基于 Docker 的业务。
  2. 启动虚拟主机,并安装 Docker。
  3. 创建网卡, 并挂载到虚拟主机上。
  4. 启用并配置 Hostnic 插件,启动 Docker 实例。
  5. 添加 Docker 实例为负载均衡器后端。
  6. 绑定防火墙。
  7. 给网卡绑定公网 IP。

* SDN 网络直通所有组件及功能均不计费。

更多详情请参看《SDN 网络直通》用户指南。

可配置副本策略功能上线

为了让用户获得更好的使用体验,同时降低用户使用成本,青云QingCloud 推出了可配置副本策略。该策略支持用户的主机或者硬盘可以根据需求,灵活地选择同数据中心多副本或者单副本。

创建主机创建硬盘

用户通过配置合适的副本策略,不仅能够降低总体使用成本(用户在选择同数据中心单副本策略后,主机在关机状态下的使用费用降低 20%,硬盘费用亦降低 20%),还能够提升 I/O 性能,性价比获得大幅提升。

在推出可配置副本策略之前,青云QingCloud 默认采用同数据中心多副本策略,即数据存储时,会产生多份实时副本,并存放于同一数据中心的不同物理节点。

通过多副本保护机制,数据将拥有冗余副本,当数据所存放的物理节点宕机时,能够通过其他物理节点的副本对数据进行实时恢复,确保数据存储的高可靠性,以及相关服务的高可用性。

而同数据中心单副本策略,即数据存放时,只会保存原数据,即只有 1 份副本,没有冗余副本进行保护。

对于一些自身拥有副本保护机制的集群应用,如 Hadoop,在主机层面开放单副本策略,将避免两层副本机制的重叠。从而在确保数据安全的前提下,降低副本的总数量,不但提升了应用的运行效率,同时也对总体使用成本进行了优化。同时,采用单副本策略的主机和硬盘避免了网络同步产生的 I/O 开销,因此 I/O 性能也会有所提升。

在主机或者硬盘创建之后,用户可以随时调整副本策略,每个资源每天最多更改 2 次。更改策略时,主机或硬盘的运行状态均不会发生中断。

instancesvolume

目前,该功能已在上海 1 区正式开放,欢迎大家体验。

通知列表支持添加微信账号

为了用户更方便的接收青云平台的通知, 我们日前对通知列表进行改造,新增了通知类型『微信通知』,用户可以在微信客户端接收监控告警,定时任务,自动伸缩,工单等通知信息。使用方式如下:

1. 在通知列表详情页,点击『绑定微信』,使用微信扫描弹出的二维码 (如果没关注,请关注我们的服务号),扫描验证成功后,保存修改

wx1

2. 通知列表保存后,可以如之前使用方式一样,绑定到『监控告警』『定时器』『自动伸缩』上 (如果之前已绑定,不需要重复绑定),既可以通过微信客户端接收通知

wx2

3. 除了监控告警之外,您也可以利用通知列表多元化的通知方式,绑定到『系统通知』上,可以用来接收青云平台发出的其他通知,目前工单通知已经支持微信,可以在下面的链接中修改系统通知:https://console.qingcloud.com/account/profile/notify_map/

QingStor 对象存储上海1区-A 上线

继 2016 年 1 月 6 日首度在北京3区 -A 开放公测以来,QingStor 对象存储于 2016 年 9 月 13 日正式商用。在这近一年的时间里,来自大数据,金融,社交,医疗,教育等多个行业的上千家客户,已在 QingStor 北京3区-A 管理了百亿数量、上 PB 容量的数据对象。在运营北京3区-A 的过程中,我们积累了丰富的上线运营经验,并收集到很多来自中国南方的客户需求,他们希望能将自己的业务更近距离地与 QingStor 服务对接,因此 QingStor 于 2016 年 12 月 26 日正式上线新区域上海1区 -A。上海1区 -A 的上线标志了 QingStor 对象存储的多区域服务运营正式开始。

为了能够让用户就近处理和分析海量数据,提高数据访问的效率和降低成本,QingStor 对象存储在设计之初即支持多区域部署的架构,同时支持同区域的 QingCloud IaaS 或 PaaS 资源内网访问。在这一年当中,我们逐步完善了各种功能 (如 AWS S3 兼容,外部镜像,数据迁移工具,数据加密,Bucket 访问控制策略,CDN,CORS,数据同步工具,内网加速,多种语言的 SDK,Bucket 域名别名,等等),也逐步加强了计算资源与 QingStor 对象存储的在服务层面的融合度,其中大数据平台已可通过 AWS S3协议与 QingStor 对象存储无缝集成。同时 QingStor 对象存储在上海1区 -A 的上线也丰富了用户的地域性选择,用户可根据业务需求将数据进行更合理的地域性划分。

我们会进一步加强 QingStor 对象存储与青云 QingCloud 各种服务的集成。未来我们也会将 QingStor 对象存储部署于更多的区域,敬请期待。

QingStor 对象存储支持 Fetch 接口和批量迁移工具 qscamel

Fetch 接口

PUT Object – Fetch 接口用于从指定的源链接抓取资源,保存到指定的对象中。 Qingstor 对象存储在抓取时能够自动处理源链接服务器返回的 301/302/307 等重定向请求。

请求示例:

PUT /<object-name> HTTP/1.1
X-QS-Fetch-Source: <source site>
Host: <bucket-name>.pek3a.qingstor.com
Date: <date>
Authorization: <authorization-string>

下面是使用 QingStor Golang SDK 的例子:

import (
    "github.com/yunify/qingstor-sdk-go/config"
    qs "github.com/yunify/qingstor-sdk-go/service"
)
configuration, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
service, err := qs.Init(configuration)
bucket, err := service.Bucket("test-bucket", "pek3a")
inputArgs := qs.PutObjectInput{
    XQSFetchSource: "http://img.example.com/blog.png",
}
bucket.PutObject("new_object", &inputArgs)

具体使用请参考文档和相应的 SDK

 

批量迁移工具 qscamel

qscamel 是把 HTTP(s) 形式的数据高效地批量迁移到 QingStor 的命令行工具。其输入可以是包含源链接的文件,也可以是其他对象存储平台的 Bucket 名称。更多介绍请参考 qscamel 文档。qscamel 已开源,请见 github 项目地址

 qscamel 有如下特点:

1.支持并行迁移,即同时迁移多个对象。

2.支持给迁移任务命名,用来继续迁移未完成的迁移任务。qscamel 会记录本次迁移任务中成功迁移的源链接。在退出后、重新执行时,qscamel 会跳过已经成功迁移的源链接,迁移剩下未完成的源链接。

3.支持灵活的覆盖模式。qscamel 默认进行增量迁移 (比较源站资源和 QingStor Bucket 中对象的最后更新时间),只迁移 Bucket 中已存在但未更新的对象,和 Bucket 中不存在的对象。但同时提供选项 “-i, –ignore-existing” 来忽略 QingStor Bucket 中已经存在的对象,及选项 “-o, –overwrite” 来强制覆盖 QingStor Bucket 中已经存在的对象。

4.支持指定日志文件。qscamel 默认输出到 stdout,可以指定输出到指定的日志文件。

 

下面给出 qscamel 的运行示例:

(1) 读取源站列表文件,进行批量迁移

(2) 退出后、重新执行时,根据记录文件,跳过成功迁移的源链接。示例跳过了成功迁移的两条源链接,只迁移 https://img3.doubanio.com/lpic/s29154751.jpg

(3) 下面演示 -i –ignore-existing 跳过迁移 Bucket 中已经存在的对象,和-n –dry-run

可以看到 Bucket 中已经有 3 个对象。 源站列表文件中,共有 4 个源链接。其中前 3 个在 Bucket 中已存在,使用 -i 选项时跳过了这 3 个。使用 -n 选项时,qscamel 会尝试运行,并输出运行结果,但不做实际迁移。

 

QingStor 对象存储支持请求参数签名

用户通过 API 请求 QingStor 时,需要对请求进行签名,现有的签名方式是首先计算签名串,然后添加到请求头 (Request Headers),和 API 请求一起发送到 QingStor 服务端。

然而并不是所有客户端都有能力设置请求头字段,又或者用户想要分享某个私有权限的文件链接给其它用户,基于这样的需求,QingStor 新增了基于请求参数 (Query String) 的签名方式。

请求参数签名的方式不需要在 HTTP 请求头中附加任何内容,只需要在请求参数中添加以下三项必要参数:

  • access_key_id – 在 QingCloud 控制台申请的 Access Key ID
  • expires – 签名过期时间,该时间为 Unix Time (也称为 Epoch Time), 表示方法是自历元(1970-01-01 00:00, The Epoch) 之后的秒数, 类型为整数。在过期时间之后到达的请求将被 QingStor 拒绝
  • signature – 对签名串 string_to_sign 经过 HMAC-SHA256 加密后,再使用 Base64 编码,最后使用 URI 编码后的结果

请求示例:

GET /music.mp3?access_key_id=PLLZOBTTZXGBNOWUFHZZ&expires=1479107162&signature=tuXu/KcggHWPAfEmraUHDwEUdiIPSXVRsO%2BT2rxomBQ%3D
Host: mybucket.pek3a.qingstor.com
Date: Mon, 14 Nov 2016 14:05:00 GMT

相比于请求头签名方法,签名串 string_to_sign 的生成过程只有一点不同,即把 Date 替换为 Expires:

string_to_sign = Verb + "\n"
               + Content-MD5 + "\n"
               + Content-Type + "\n"
               + Expires + "\n"
              (+ Canonicalized Headers + "\n")
               + Canonicalized Resource

通过上述方法得到签名串 string_to_sign 以后计算 signature (以 Python 为例):

import hmac
import base64
import urllib
from hashlib import sha256

h = hmac.new(secret_access_key, digestmod=sha256)
h.update(string_to_sign)
signature = urllib.quote(base64.b64encode(h.digest()).strip())

最后拼接成请求参数,追加到请求地址的后面:

access_key_id=PLLZOBTTZXGBNOWUFHZZ&expires=1479107162&signature=tuXu/KcggHWPAfEmraUHDwEUdiIPSXVRsO%2BT2rxomBQ%3D

用户还可以使用 QingStor SDK 生成参数签名的 URL 链接,下面以 QingStor Go SDK 为例:

import (
    "github.com/yunify/qingstor-sdk-go/config"
    qs "github.com/yunify/qingstor-sdk-go/service"
)

configuration, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
qsService, err := qs.Init(configuration)
bucket, err := qsService.Bucket("test-bucket", "pek3a")

request, _, err := bucket.GetObjectRequest("test-object", nil)
err = request.SignQuery(3600) // Expires in 1 hour
signedURL = request.HTTPRequest.URL.String()
* 注:QingStor 各语言 SDK 均已支持请求参数签名,SDK 的安装和使用方法请参考文档

参数签名使用的场合较为广泛,它所能达成的效果跟请求头签名完全一样,只是让附带签名的过程更加方便和易于使用。更多 QingStor 签名有关的内容请参考文档

 

SDK 代码生成工具 Snips 发布

Snips 是一个在命令行界面使用的 SDK 代码生成工具,借助 Snips,可以在短时间内开发出 QingCloud 或 QingStor 的 SDK。Snips 已在 GitHub 开源,更多详情可参见 https://github.com/yunify/snips

Snips 读取 API Specifications(API 描述,例如 QingStor 对象存储的 API Specification: https://github.com/yunify/qingstor-api-specs)文件和模版文件来生成 SDK 中 API 调用部分的代码,再加上与各语言相关的代码,便可组成一个完整的 SDK。

另外,为了保证 SDK 的可用性和功能一致性,我们还引入了测试场景描述项目 https://github.com/yunify/qingstor-sdk-test-scenarios

注: QingCloud 的 API 描述及测试场景描述项目将稍后开源在 GitHub 上。

使用 Snips 开发 SDK 的流程如下图:

Snips Workflow

 使用 Snips 增加一门新语言的 SDK

1. 编写语言相关的、不适合生成的代码

2. 编写 API 代码模版

3. 使用 Snips 生成代码

4. 测试

5. 发布

使用 Snips 更新现有 SDK

1. 更新 API Specifications

2. 使用 Snips 重新生成代码

3. 测试

4. 发布

使用 Snips 生成 SDK 演示

下面以 QingStor 的 Go SDK(qingstor-sdk-go)为例演示 Snips 的使用方法:

1. 安装

可以直接使用 go get 安装:

go get -u github.com/yunify/snips

也可以从 Snips 项目的 GitHub Releases 页面下载编译好的二进制文件,支持 macOS、Linux 和 Windows 平台。

2. 使用 git submodule 引入 QingStor API Specifications 和 QingStor SDK test scenarios

3. 编写语言相关的不适合生成的代码,编写模版文件。

4. 使用 Snips 生成代码,并格式化代码。

$ snips --service=qingstor \
        --service-api-version=latest \
        --spec="./specs" \
        --template="./template" \
        --output="./service"
Loaded templates from ./template
4 template(s) detected.
Loaded service QingStor (2016-01-06) from ./specs

Generating to: ./service/qingstor.go
Generating to: ./service/object.go
Generating to: ./service/bucket.go
Generating to: ./service/types.go

Everything looks fine.
$ gofmt -w .

5. 使用生成好的代码实现测试用例,并运行场景化测试

$ pushd ./test
$ go run *.go
...
38 scenarios (38 passed)
84 steps (84 passed)
1m2.408357076s
$ popd

6. 以后每次 API 发生变更,只需要更新 git submodule,然后重新生成代码,通过场景化测试即可。

QingStor 对象存储支持数据加密服务

QingStor 对象存储已支持由用户提供密钥的数据加密方式。用户在上传对象时提供密钥,对象存储服务端用该密钥对上传的对象进行加密处理。相应地,在下载加密过的对象时,用户也须提供密钥,对象存储服务端用该密钥对下载的对象进行解密处理后返回。

NOTE: QingStor 对象存储服务端不保存用户的密钥,只会保存加密算法和密钥的 MD5 值。用户需要自己妥善保管上传时指定的密钥。

支持加密的 APIs 有:PUT ObjectMultipart Upload。支持解密的 APIs 有:GET ObjectHEAD ObjectPUT Object – Copy 。更详细的信息请参考QingStor 对象存储官方文档中数据加密一节。

1.加密过程

1. 客户端提供密钥及密钥的 MD5 值,在上传对象的请求的请求头中指定加密算法、密钥、和密钥 MD5 值。目前支持的加密算法是 AES256 。

2. 服务端根据密钥生成 MD5 值,与用户上传的密钥 MD5 值比对,以确认密钥在传输过程中的完整性。

3. 服务端对上传的对象进行加密。

4. 服务端丢弃用户上传的密钥,只保存加密算法和密钥 MD5 值。

5. 用户需要自己保存给该对象加密的密钥。

2.解密过程

1. 客户端在获取对象的请求的请求头中指定加密算法、密钥、密钥 MD5 值。密钥须为上传时所指定的加密密钥。

2. 服务端根据密钥生成 MD5 值,与用户上传的密钥 MD5 值比对,以确认密钥在传输过程中的完整性。

3. 服务端比对上传对象时保存的 MD5 值和请求中的密钥的 MD5 值,以验证密钥是否正确。

4. 服务端将对象用请求头中指定的密钥解密并返回。

3.示例

下面通过 PUT Object 和 GET Object 接口,演示数据加密服务流程。

1.上传对象时指定加密算法,密钥和密钥 MD5。

PUT /myphoto.jpg HTTP/1.1
Host: mybucket.pek3a.qingstor.com
Date: Sun, 16 Aug 2016 10:15:00 GMT
Content-Length: 7987
Authorization: authorization string
X-QS-Encryption-Customer-Algorithm: AES256
X-QS-Encryption-Customer-Key: M9Thi2bO7tsdbsuMo679ojaq0G6L5laHbxwjo0JVBeI=
X-QS-Encryption-Customer-Key-MD5: OTIxZjI3YzNhYjA4MTAyMzI3ZmViOGIxNWYzMjRhODA=
[7987 bytes of object data]

2.获取对象时提供密钥,由对象存储服务端解密

GET /myphoto.jpg HTTP/1.1
Host: mybucket.pek3a.qingstor.com
Date: Sun, 16 Aug 2016 10:15:02 GMT
Authorization: authorization string
X-QS-Encryption-Algorithm: AES256
X-QS-Encryption-Customer-Key: M9Thi2bO7tsdbsuMo679ojaq0G6L5laHbxwjo0JVBeI=
X-QS-Encryption-Customer-Key-MD5: OTIxZjI3YzNhYjA4MTAyMzI3ZmViOGIxNWYzMjRhODA=

QingStor 对象存储支持移动对象和批量删除对象

移动对象

QingStor 对象存储已经支持从一个存储空间中移动对象到另一个的存储空间中去,类似 *nix 系统中的 `mv` 命令,这个功能可以用于对象的重命名等操作。

下面是使用 QingStor Golang SDK 的例子:

import (
    "github.com/yunify/qingstor-sdk-go/config"
    qs "github.com/yunify/qingstor-sdk-go/service"
)

configuration, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
qsService, err := qs.Init(configuration)
bucket, err := qsService.Bucket("test-bucket", "pek3a")

inputArgs := qs.PutObjectInput{
    XQSMoveSource: "/" + "your_bucket" + "/" + "old_object",
}
bucket.PutObject("new_object", &inputArgs)

具体使用请参考文档和相应的 SDK

批量删除对象

现在用户可以一次删除最多 1000 个对象,配合 List Objects API , 可以实现快速地清空存储空间等操作。

下面是使用 QingStor Golang SDK 的例子:

import (
    "github.com/yunify/qingstor-sdk-go/config"
    qs "github.com/yunify/qingstor-sdk-go/service"
)

configuration, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
qsService, err := qs.Init(configuration)
bucket, err := qsService.Bucket("test-bucket", "pek3a")

inputArgs := qs.DeleteMultipleObjectsInput{
    Objects: []*qs.KeyType{
       {
          Key: "test-object",
       },
    },
}
bucket.DeleteMultipleObjects(&inputArgs)

具体使用请参考文档和相应的 SDK