Skip to content

云盘的快照与分叉

快照把云盘保存为某一时刻的状态,作为它在所在区域对象存储中的一个版本,无需复制。可以把云盘恢复到快照,从快照分叉出新云盘,任意多个沙箱也都可以以只读方式挂载它。

更新于

云盘的快照把云盘保存为某一时刻的状态。云盘在写入的同时被复制到所在区域的对象存储中,快照就是它在那里的一个版本:创建快照不会再复制任何数据,使用快照也不会。有了快照,你可以:

  • 把云盘原地恢复到它;
  • 分叉它:创建一个内容与快照相同的新云盘,可以立即挂载;
  • 在云盘所在区域的任意机器上,让任意多个沙箱同时以只读方式挂载它。

快照不随云盘消失。云盘删除后,仍然可以列出它的快照、从中分叉,或以只读方式挂载。

每种客户端都有这些调用:命令行(glac drives snapshots、glac drives fork、glac drives restore、glac drives attach --snapshot),SDK(下面的 Python 和 TypeScript,以及 Go、Rust 和 Java SDK),MCP 服务器(drive_snapshot、drive_fork、drive_restore),以及控制台的 Drives 页面,在那里可以为每个云盘创建、分叉、恢复和删除 Snapshots(快照)。

调用

调用 作用
POST /v1/drives/{id}/snapshots {name?, fs_consistent?} 立即为云盘创建快照。返回 201 和快照。
GET /v1/drives/{id}/snapshots 云盘的快照,最新的在前;已删除的云盘,按它的 id 列出。
GET /v1/drive-snapshots 项目中所有云盘的快照,最新的在前:已删除云盘的快照也在这里,附带该云盘的 id。?drive_deleted=true 只列出这些,false 只列出其他的(不指定云盘的 glac drives snapshots ls)。
GET, DELETE /v1/drives/{id}/snapshots/{snapshot} 按 id 查看一个快照;删除它。
POST /v1/drives/{id}/restore {snapshot} 把云盘恢复到快照。返回云盘。
POST /v1/drives/{id}/fork {name?, from_snapshot?} 从快照,或从云盘当前的状态,创建一个新云盘。返回 201 和新云盘。
POST /v1/sandboxes/{id}/drives {drive, path, mode: "snapshot", snapshot} 以只读方式把快照挂载到一个已有的沙箱中。返回快照。
POST /v1/sandboxes 带上 drives: [{drive, path, mode: "snapshot", snapshot}] 以只读方式把它挂载到新沙箱中。
DELETE /v1/sandboxes/{id}/drives/{snapshot} 按快照的 id 卸载只读挂载。

{id} 是云盘的 id 或名称。列出和读取快照需要带 read 权限范围的密钥;其他调用需要 write。API 参考列出了每个调用的所有字段和响应:创建、列出、读取、删除、恢复、分叉、挂载和卸载。

快照包含 id (dsnap-…)、所属云盘的 drive_id、name、consistency(创建时如何暂停云盘的写入:见下文)、used_bytes(创建时云盘中的数据量)、exclusive_bytes(它单独保留的数据量,也是计费依据)和 exclusive_at(最近一次计算该值的时间)、它对应的云盘 version(number、epoch),以及 created_at。

名称可选:1 到 63 个小写字母、数字和连字符,不能以连字符开头或结尾,也不能以 vol- 或 dsnap- 这类 id 前缀开头 (400 invalid_name)。两个快照可以同名,所以各个调用都用 id 指定快照。

创建快照

示例中,云盘是 data,沙箱 web 把它挂载在 /data。glac drives snapshots create 打印快照的 id,这里保存在 SNAP 中:

SNAP=$(glac drives snapshots create data --name before-upgrade --fs-consistent)

无论有没有沙箱持有,云盘都可以创建快照:

  • 没有运行中的沙箱持有它(没有挂载,或持有它的沙箱在休眠):快照就是云盘当前的样子 (consistency: "at_rest")。
  • 有运行中的沙箱持有它:快照分两步创建。先在沙箱继续写入的同时,保存调用之前写入的内容作为一个版本;再暂停沙箱的写入,把这期间写入的内容保存为一个版本。写得少的沙箱,暂停不到一秒,最多十秒:超过十秒,快照会被拒绝(503 unavailable,可以重试),沙箱照常运行。还没进入对象存储的数据会先发送,所以刚写入大量数据的云盘需要更久。
  • 如何暂停写入:默认暂停沙箱的机器 (consistency: "paused"):云盘处于相当于断电后的状态,文件系统的日志就是为此准备的。设置 fs_consistent: true (--fs-consistent) 时,改由沙箱中的代理程序冻结云盘的文件系统 ("frozen"):文件系统中的内容会先写出,挂载快照时无需重放日志。代理程序无法冻结时,调用会被拒绝 (409 not_frozen):这时请不带这个选项创建快照。

列出云盘的快照、读取一个、删除一个:

glac drives snapshots ls data                 # newest first: how each was taken, what it keeps alone
glac drives snapshots get data $SNAP
glac drives snapshots rm data $SNAP

删除的快照立即消失,计费到那一刻为止。从它分叉出的云盘会保留所需的数据,以只读方式挂载它的沙箱会保留挂载,直到卸载或沙箱被删除。

恢复云盘

恢复把云盘带回某个快照:之后它的内容就是创建快照时的内容。恢复会在云盘最新的版本之后写入一个新版本,内容与快照相同;不复制任何数据,云盘的 id 和名称不变。

glac drives detach data web
glac drives restore data $SNAP
glac drives attach data web:/data
  • 沙箱以读写方式持有的云盘不能恢复 (409 drive_attached):先卸载,恢复后再挂载。
  • 中间的版本只要有快照或分叉保留着就仍然存在:云盘的其他快照(包括在这个快照之后创建的)和它的分叉都保持不变,所以恢复到较旧快照的云盘,之后还可以再恢复到较新的快照。
  • 恢复之前云盘的内容,只有在快照保留它时才会留下:如果之后可能想回来,请先创建一个快照。
  • 恢复之后,云盘的数据块在被读取时从对象存储取来。

分叉云盘

glac drives fork (POST /v1/drives/{id}/fork) 创建一个新云盘,它的第一个版本就是快照的内容(--from-snapshot、from_snapshot),并打印它的 id。不指定快照时,用的是云盘当前的状态:会像创建快照那样为分叉生成一个版本,但不保留它。不复制任何数据:分叉可以立即挂载,数据块在被读取时从对象存储取来。

glac drives fork data --name data-try --from-snapshot $SNAP
glac sandboxes create --name try --drive data-try:/data
glac drives get data-try                      # with parent_drive_id (the id of data) and root_drive_id
  • 分叉出的云盘和其他云盘一样:有自己的 id 和名称,同一时间由一个沙箱写入,按云盘计费并受云盘的限制。它的大小与来源云盘相同,也在来源云盘的区域中。
  • parent_drive_id 是它的来源云盘,root_drive_id 是这一脉分叉中的第一个云盘。不是分叉出的云盘,两者都为 null。
  • 分叉写入的内容只属于它自己:来源云盘和快照都不会改变。两者都可以删除,分叉会保留它需要的数据。
  • 已删除云盘的快照,按该云盘的 id 分叉。
  • 分叉的名称不能与项目中其他云盘或存储卷的名称相同 (409 name_taken)。超出套餐云盘数量的分叉会被拒绝 (403 drive_limit_reached),和新建云盘一样。

以只读方式挂载快照

只读挂载把快照中的云盘内容交给沙箱,挂载在某个路径上。云盘所在区域任意机器上的任意多个沙箱,都可以同时挂载同一个快照,无论云盘被谁持有:一个沙箱甚至可以同时以读写方式持有云盘、以只读方式挂载它的某个快照。云盘删除后,也可以挂载它的快照。

glac drives attach data web:/then --snapshot $SNAP          # in a sandbox that runs
glac sandboxes exec web -- ls /then
glac drives detach $SNAP web                                # by the snapshot's id
glac sandboxes create --name reader --drive data:/then:dsnap-…   # in a new sandbox: the snapshot's id after the path
  • 给出的云盘(API 中为 drive)必须是快照所属的云盘:它的 id 或名称,或已删除云盘的 id。其他云盘的快照会被拒绝 (400 invalid_field)。
  • 新沙箱在它的 drives 中放入同样的对象来挂载快照(POST /v1/sandboxes;命令行中为 --drive DRIVE:/PATH:SNAPSHOT,因为那里的 --snapshot 是沙箱要从中继续运行的沙箱快照)。只读挂载不决定沙箱创建在哪台机器上,但沙箱会创建在云盘所在的区域:指定了其他区域,或用到两个区域的云盘的创建请求,都会被拒绝 (409 not_on_one_machine)。
  • 运行在与云盘不同区域的机器上的沙箱,也会被拒绝 (409 not_on_one_machine)。
  • 文件系统以只读方式挂载:沙箱不能写入。
  • 数据块在沙箱读取时从对象存储取来,并保存在沙箱所在的机器上。读取它们不收费(见下文)。保存下来的数据块计入一个账户在该机器磁盘上可占用的份额:超出时挂载会被拒绝(403 account_share_reached,并说明超出多少)。
  • 它计入沙箱挂载的 8 个云盘和存储卷之内 (409 too_many_drives)。
  • 它不出现在云盘的 attached_to 中,也不会发出 drive.attached 或 drive.detached 事件。
  • 要按快照的 id 卸载它:用云盘的 id 或名称只会卸载读写挂载 (404 drive_not_attached)。删除沙箱也会卸载它。

沙箱的快照与它的云盘

为以读写方式持有云盘的沙箱创建快照时,每个云盘都会保存为一个版本,不复制任何数据。从这个快照创建的沙箱,会在相同路径上挂载每个云盘的一个分叉,内容是创建快照时云盘中的内容。云盘本身仍属于第一个沙箱,它之后写入的内容不会进入这些分叉。

这些分叉都是项目中的普通云盘:会被列出,parent_drive_id 是它的来源云盘,按 used_bytes 计费,新沙箱被删除后仍会保留,直到你删除它。

用 Python 和 TypeScript

from glac import DriveMount, Glac

glac = Glac()  # GLAC_API_KEY and GLAC_API_URL
drive = glac.drives.get("data")
web = glac.sandboxes.get("web")  # the sandbox that has it

snap = drive.snapshot(name="before-upgrade", fs_consistent=True)
for s in drive.snapshots():
    print(s.id, s.name, s.consistency, s.exclusive_bytes)

# a new Drive made of the snapshot, in a new sandbox
fork = glac.drives.fork(drive, name="data-try", from_snapshot=snap)
trial = glac.sandboxes.create(drives=[DriveMount("/data", fork)])

# the snapshot, read-only: in a new sandbox, and in one that runs
reader = glac.sandboxes.create(drives=[DriveMount("/then", drive, snapshot=snap)])
glac.drives.attach_snapshot(trial, snap, "/then")
print(trial.run("ls /then").stdout)
glac.drives.detach_snapshot(trial, snap)

# the Drive back to the snapshot, while no sandbox has it read-write
glac.drives.detach(web, drive)
glac.drives.restore(drive, snap)
glac.drives.attach(web, drive, "/data")

snap.delete()
import { Glac } from "glac";

const glac = new Glac();                                    // GLAC_API_KEY and GLAC_API_URL
const drive = await glac.drives.get("data");
const web = await glac.sandboxes.get("web");                // the sandbox that has it

const snap = await drive.snapshot({ name: "before-upgrade", fsConsistent: true });
for (const s of await drive.snapshots()) console.log(s.id, s.name, s.consistency, s.exclusiveBytes);

// a new Drive made of the snapshot, in a new sandbox
const fork = await glac.drives.fork(drive, { name: "data-try", fromSnapshot: snap });
const trial = await glac.sandboxes.create({ drives: [{ path: "/data", drive: fork.id }] });

// the snapshot, read-only: in a new sandbox, and in one that runs
const reader = await glac.sandboxes.create({ drives: [{ path: "/then", drive: drive.id, snapshot: snap }] });
await glac.drives.attachSnapshot(trial, snap, "/then");
console.log((await trial.run("ls /then")).stdout);
await glac.drives.detachSnapshot(trial, snap);

// the Drive back to the snapshot, while no sandbox has it read-write
await glac.drives.detach(web, drive);
await glac.drives.restore(drive, snap);
await glac.drives.attach(web, drive, "/data");

await snap.delete();

快照在两者中都是 DriveSnapshot,自带 refresh、fork、restore、attach、detach 和 delete。attach 以读写方式挂载云盘:快照用 attach_snapshot (attachSnapshot) 以只读方式挂载,或在新沙箱的 drives 中带上 snapshot。所有调用见 Python 和 TypeScript SDK 页面。

用命令行

命令 作用
glac drives snapshots create DRIVE [--name NAME] [--fs-consistent] 创建快照并打印它的 id。
glac drives snapshots ls DRIVE, get DRIVE SNAPSHOT, rm DRIVE SNAPSHOT… 云盘的快照,最新的在前;查看一个;删除。已删除的云盘,按它的 id 指定。
glac drives restore DRIVE SNAPSHOT 把云盘恢复到快照。
glac drives fork DRIVE [--name NAME] [--from-snapshot SNAPSHOT] 从快照,或从云盘当前的状态,创建一个新云盘;打印它的 id。
glac drives attach DRIVE SANDBOX:/PATH --snapshot SNAPSHOT, glac drives detach SNAPSHOT SANDBOX 以只读方式把快照挂载到正在运行的沙箱中;卸载它。
glac sandboxes create --drive DRIVE:/PATH:SNAPSHOT 创建一个以只读方式挂载了快照的新沙箱。
glac drives get DRIVE 分叉的 parent_drive_id 和 root_drive_id,以及云盘的其他信息。
glac usage --snapshot SNAPSHOT 单个快照保留了多少。

计费内容

  • 快照按它单独保留的数据 (exclusive_bytes) 计费:即它的版本需要、而云盘当前状态、较新的快照和分叉都不需要的数据。两个快照都保留的数据计在较旧的那个上。云盘自那以后没有变化时,快照单独保留的数据为零。云盘删除后,它原有的数据计在它的快照上。
  • 它按小时计费,每 GB·月 $0.02,和云盘保存的数据一起计入数据计量项,从第一次算出它单独保留的数据 (exclusive_at) 起,到它被删除为止。付费版每月包含 20 GB·月 的数据,由快照、云盘和休眠沙箱共用。免费版同时最多保存 15 GB 的数据,其中包括快照:超出时,新建快照会被拒绝 (403 storage_quota_reached),直到账户保存的数据减少。参见价格与套餐。
  • GET /v1/usage?snapshot_id=dsnap-… 报告单个快照保留了多少,glac usage --snapshot dsnap-… 也一样。
  • 分叉是云盘,按云盘计费,依据它的 used_bytes:即你读到的全部内容,包括它与来源快照共享的数据。从一个存有 10 GB 数据的云盘的快照分叉出的云盘,从一开始就按 10 GB 的云盘计费,尽管它的大部分数据块在底层是共享的。它的 NVMe 缓存和任何云盘一样计费;写入和读取都不收费。参见云盘的工作方式。
  • 只读挂载:为它从分层存储取来的数据块不收费,两个沙箱挂载同一个快照,各自读取也都不收费。它保存在机器上的数据块是快照的缓存,快照按上文计费。

账户欠费或被限制时

  • 欠费的账户可以列出、读取和删除云盘的快照,以免欠款继续增加。它不能创建快照、分叉、恢复或挂载 (402 account_in_debt)。如果第一封邮件发出 30 天后仍然欠费,它存储的一切都会被删除,其中包括快照 (drive.snapshot.deleted)。参见欠费的账户。
  • 被支出上限限制的项目,或者账户已用完付费版当期包含的 CPU 时间或内存且没有余额时,不能创建快照,也不能分叉(403 spend_limit_reached、balance_used_up);在免费版上,当天 CPU 时间或内存的配额已用完的账户也不能,直到 00:00 UTC (429 quota_used_up)。快照保留的数据会继续计量(计量项用完时)。

限制与错误

  • 一个云盘可以有任意多个快照;每个快照按它单独保留的数据计费。快照不计入项目的云盘数量。
  • 分叉按其大小算作一个新云盘:参见项目能保有多少。
  • 只读挂载计入沙箱的 8 个云盘和存储卷之内。
响应 情形
400 invalid_name 名称不是 1 到 63 个小写字母、数字和连字符,或以 id 前缀开头。
400 missing_field 恢复时没有给出 snapshot;挂载时设置了 mode: "snapshot" 却没有给出 snapshot。
400 invalid_field snapshot 指定的是另一个云盘的快照,或者给出时没有设置 mode: "snapshot"。
402 account_in_debt 账户欠费期间,除列出、读取和删除之外的任何操作。
403 drive_limit_reached 超出套餐允许项目保有的云盘数量的分叉。
403 spend_limit_reached, balance_used_up 项目被限制期间的快照或分叉。
403 storage_quota_reached 在免费版上,账户保存的数据已达到免费版同时可保存的上限时创建快照。
429 quota_used_up 在免费版上,当天的 CPU 时间或内存配额用完后创建快照或分叉,直到 00:00 UTC。
403 account_share_reached 在账户占用的磁盘份额已达到单个账户上限的机器上进行分叉、恢复或只读挂载。错误信息会说明占用了多少。
404 drive_not_found, snapshot_not_found 没有这个 id 或名称的云盘,或者云盘没有这个 id 的快照。已删除云盘的快照要按云盘的 id 访问。
404 drive_not_attached 用云盘的 id 或名称而不是快照的 id 卸载只读挂载,或者沙箱没有这个挂载。
409 drive_attached 沙箱以读写方式持有云盘时进行恢复。
409 not_frozen 设置了 fs_consistent: true,但沙箱中的代理程序无法冻结文件系统。
409 name_taken 分叉的名称已被另一个云盘或存储卷使用。
409 not_on_one_machine 在与云盘不同区域的沙箱中只读挂载,或新沙箱的区域与它的云盘不同。
409 too_many_drives 沙箱已经有 8 个云盘和存储卷。
503 unavailable 现在无法生成版本:在十秒的暂停时间内没能读完沙箱的写入,或者对象存储中还没有云盘的最新状态。稍后再试。
503 drive_in_store 此刻没有机器保存这个云盘:它正在搬往另一台机器。稍后再试。
503 node_unavailable 无法访问保存这个云盘的机器。

事件

drive.snapshot.created 和 drive.snapshot.deleted 会出现在项目的事件和 Webhook 中,带有 snapshot_id、drive_id、name 和 consistency。因账户未付款而被删除的快照也会发出 drive.snapshot.deleted。只读挂载及其卸载不会发出 drive.attached 或 drive.detached。

尚未支持

  • 方块的版本只能以读写方式挂载云盘:带 mode: "snapshot" 的版本会被拒绝 (400 invalid_field)。只读挂载只供沙箱使用。
  • 有只读挂载的沙箱不能迁到另一台机器。
  • 同一台机器上挂载同一个快照的多个沙箱,各自保存它的数据块:它们之间不共享。
  • 从沙箱快照创建的沙箱所得到的分叉,在创建时不检查套餐的云盘限制;从那以后才开始计入。
  • 快照只能在创建它的区域中以只读方式挂载(云盘本身可以在其他区域挂载:跨区域)。已删除云盘的快照,平台不知道它属于哪个区域:它挂载在沙箱创建的地方,向其他区域对象存储复制数据的机器会拒绝它。
导航

输入以搜索……

↑↓ 切换↵ 选择Esc 关闭