restic笔记
Restic
一、简介
Restic是基于Go语言开发的开源备份工具,使用BSD-2-Clause开源协议。 它的核心理念是提供安全、快速、可验证的备份。其架构设计遵循了简单和高效的原则,主要由以下几个核心组件构成:
-
存储后端
Restic 支持多种存储后端,包括本地文件系统、SFTP、Amazon S3、Backblaze B2、Google Cloud Storage、Microsoft Azure Blob Storage 等等。这使得它在部署时具有非常灵活的适应性,能够满足不同企业或个人的存储需求。 -
数据加密与去重
Restic 在进行备份时,会自动对数据进行加密,采用AES-256和Poly1305进行安全加密和完整性验证,确保数据即使在不可信的存储后端也能保持安全。同时Restic使用内容可寻址存储和增量备份技术,通过数据块级别的去重机制,大幅减少重复数据的存储占用。 -
快照管理
Restic使用快照(Snapshot)的概念来管理备份。每次备份都会生成一个新的快照,用户可以方便地恢复到任意历史快照。并且它可以通过简单的命令查看、清理和合并快照,保持备份系统的高效性。
二、工作流程
2.1 初始化存储库
存储库是一个包含所有备份数据、快照以及元数据的存储单元。Restic支持多种存储后端,用户可以选择将存储库存放在本地文件系统、SFTP服务器、Amazon S3、Backblaze B2等位置。
- 初始化本地存储库
$ restic init --repo /path/to/repo
- 初始化S3存储库
$ export AWS_ACCESS_KEY_ID=<YOUR-MINIO-ACCESS-KEY-ID>
$ export AWS_SECRET_ACCESS_KEY=<YOUR-MINIO-SECRET-ACCESS-KEY>
$ restic -r s3:http://server:port/restic init
2.2 备份
将指定的文件或目录加密并上传到存储库
$ restic --repo /path/to/repo backup /path/to/data
Restic会将文件分成多个数据块(Chunk),对每个数据块进行哈希计算来判断是否已经备份过。如果某个数据块已经存在于存储库中,Restic不会重新备份它;每个数据块都会使用AES-256和Poly1305进行加密。
2.3 快照管理
每次成功的备份操作都会生成一个新的快照。快照记录了备份时的文件系统状态,包括文件的路径、修改时间和权限等信息。
$ restic snapshots --repo /path/to/repo
enter password for repository:
ID Date Host Tags Directory Size
-------------------------------------------------------------------------
40dc1520 2015-05-08 21:38:30 kasimir /home/user/work 20.643GiB
79766175 2015-05-08 21:40:19 kasimir /home/user/work 20.645GiB
2.4 恢复
当需要从备份中恢复文件时,Restic 允许用户指定要恢复的快照,将备份数据还原到原位置或其他目录。
$ restic --repo /path/to/repo restore 40dc1520 --target /restore/path
三、支持的命令
- backup: 创建新的文件或目录备份
- cache: 管理本地缓存目录
- cat: 打印内部对象到标准输出(stdout)
- check: 检查存储库是否存在错误
- copy: 将快照从一个存储库复制到另一个存储库
- diff: 显示两个快照之间的差异
- dump: 将备份文件打印到标准输出
- find: 查找文件、目录或 Restic 的 ID
- forget: 从存储库中移除快照
- init: 初始化一个新的存储库
- key: 管理密钥(密码)
- list: 列出存储库中的对象
- ls: 列出快照中的文件
- prune: 移除存储库中不再需要的数据
- recover: 从存储库中恢复未被快照引用的数据
- restore: 从快照中提取数据
- snapshots: 列出所有快照
- stats: 扫描存储库并显示基本统计信息
- tag: 修改快照上的标签
- unlock: 移除其他进程创建的锁
四、存储库
存储库结构
➜ tree /path/to/repo
/path/to/repo
├── config
├── data
│ ├── 07
│ │ └── 074047e8bb4ba3164fbcd01c13427f3db78562ef7a8da83be11a713411a205b9
│ ├── 54
│ │ └── 54987a8a6b24e4d11e8981f22f3e54c326ac8e213b5459ab461908fa5ab35e6e
├── index
│ └── b8ac8778b1dea7175421cfb71cf63b10253ad4e285b479c7827a5e9ea66de542
├── keys
│ └── b4373b48cbff48019323111067b7c859dfe9570d1870fc08b725e2f5672bde10
├── locks
└── snapshots
└── ae3ce93ba29260e9b0a0b152685eeb4ca7f503375c148250c8ddf1ba45f3f1f2
config
保存了存储格式的版本,存储库的唯一识别ID,以及拆分数据块的分块算法参数chunker_polynomial
(内容定义分块算法CDC)。
➜ restic -r ./repo cat config
{
"version": 2,
"id": "23e5b8af09c55f1128cc263d0f160ce3d6520687b94f6e5cb2a3196839de5733",
"chunker_polynomial": "2228213490fe8f"
}
keys
key文件中存储了用来加密数据的密钥(使用AES-256加密算法实现),还存储了用来身份验证的密钥(使用Poly1305-AES加密算法实现)。
➜ restic -r ./repo cat key b4373b48cbff48019323111067b7c859dfe9570d1870fc08b725e2f5672bde10
{
"created": "2024-09-05T23:28:23.473945+08:00",
"username": "greene",
"hostname": "greenedeMacBook-Pro.local",
"kdf": "scrypt",
"N": 32768,
"r": 8,
"p": 8,
"salt": "Zphd4TsAEm/jYe6dNzkme+qPLgEnIV3H7bEfbyjYDw/RKH1yuk0nC631itxH5iGhMiacT1aUnMppEn3wSOBnxg==",
"data": "r7QXMHya7ihEkwEE+FfTjYHhxUQ6jjS9qQD14SWT2DrL7JTpP6/xCkiXaUD8Vqj4o0dKsSx6WRpd5h1u2ovUfX202NLMgnHSexncrHsrcN8N2dXo8zabzYdxFzE12jomAks24GpKK2F+ZA50DIvm0bAavdetNRifCh789kfn6ogv6oNVuksa3XYipVwUG75IgDHmgwWhuOsrdqFfnVN1IQ=="
}
-
kdf(Key Derivation Function): 密钥派生函数,这里使用的是scrypt算法,它是一种用于增强密码强度的算法,避免密码被轻易破解。
-
N、r、p: scrypt算法的参数,分别控制计算成本、内存需求和并发度。
N: 表示计算成本。
r: 表示内存块大小。
p: 表示并发度。 -
salt: 加密时使用的盐值。salt 是一个随机生成的字符串,用来增强加密的安全性,防止相同的密码生成相同的密钥。
-
data: 经过加密的主密钥数据。通过kdf和salt生成派生密钥用来解密此字段的内容,从而获取用于加密备份数据的实际密钥。
➜ restic --repo repo cat masterkey
{
"mac": {
"k": "jpq94QwW1CP08Pd/Lv3IhA==",
"r": "dcK4Csi9jguE/fsCREPhCQ=="
},
"encrypt": "157F8y11GbnFjO50tkZ3UB8MYOhVvsWOqSrGmhEPA+4="
}
仓库的masterkey是不变的
- mac 字段:"k" 和 "r"是消息认证码 (MAC) 的两个组件。MAC用于验证数据的完整性和真实性,确保加密数据未被篡改。
- encrypt 字段:包含加密的主密钥,这是实际用于加密和解密备份数据的密钥。使用它和一个随机生成的向量值IV就可以加解密数据。
index
索引文件包含有关数据和目录树的信息,并将这些信息存储在存储库中。当本地缓存中的索引不可访问时,可以下载索引文件并用于重建索引。索引文件也通过加密存储。
➜ restic -r ./repo cat index b8ac8778b1dea7175421cfb71cf63b10253ad4e285b479c7827a5e9ea66de542
{
"packs": [
{
"id": "54987a8a6b24e4d11e8981f22f3e54c326ac8e213b5459ab461908fa5ab35e6e",
"blobs": [
{
"id": "148aeb448ff7d2cfe6628b48f351a729ac04677c098b4da5ff5799f8d2b2ee7e",
"type": "tree",
"offset": 278,
"length": 266,
"uncompressed_length": 381
},
{
"id": "20a9db106a1bd2f5d49895f57059d42ed94125d901b017fa136a3d3de6637cb0",
"type": "tree",
"offset": 0,
"length": 278,
"uncompressed_length": 591
}
]
},
{
"id": "074047e8bb4ba3164fbcd01c13427f3db78562ef7a8da83be11a713411a205b9",
"blobs": [
{
"id": "07854d2fef297a06ba81685e660c332de36d5d18d546927d30daad6d7fda1541",
"type": "data",
"offset": 0,
"length": 96,
"uncompressed_length": 524288
}
]
}
]
}
pack&blob
Pack文件可能包含一个或多个数据Blob,以IV || Ciphertext || MAC格式存储,Pack文件的末尾有一个标头,用于描述内容。标头经过加密和验证。Header_Length是加密标头的长度,以小端编码方式编码为四字节整数。Pack文件默认16M大小,可以通过--pack-size参数调整。
有两种主要的 Blob:
- Data Blob:
备份文件的数据块。当文件备份时,Restic 会将文件拆分为若干数据块,每个数据块会进行加密和去重后存储在存储库中。 - Tree Blob:
存储文件的元数据,如文件名、权限、创建修改时间等信息。它可以被看作是文件系统的快照,描述了文件及目录在某个时间点的具体结构。
Restic采用内容寻址的方式处理Blob。每个Blob都会根据其内容生成一个唯一的哈希值(SHA256),如果多个文件的某个部分内容相同,则它们会共享相同的Blob。这种去重机制可以显著节省存储空间,尤其是在多次备份相同或类似文件时。
对于版本2的备份仓库,支持压缩功能,使用zstandard压缩算法。支持auto和max两种级别,auto压缩速度最快,max压缩比最高。
➜ restic -r ./repo cat blob 148aeb448ff7d2cfe6628b48f351a729ac04677c098b4da5ff5799f8d2b2ee7e
{
"nodes": [
{
"name": "testdata",
"type": "dir",
"mode": 2147484141,
"mtime": "2024-09-06T00:17:04.957176395+08:00",
"atime": "2024-09-06T00:17:04.957176395+08:00",
"ctime": "2024-09-06T00:17:04.957176395+08:00",
"uid": 501,
"gid": 20,
"user": "greene",
"group": "staff",
"inode": 5929028,
"device_id": 16777233,
"content": null,
"subtree": "20a9db106a1bd2f5d49895f57059d42ed94125d901b017fa136a3d3de6637cb0"
}
]
}
➜ restic -r ./repo cat blob 20a9db106a1bd2f5d49895f57059d42ed94125d901b017fa136a3d3de6637cb0
{
"nodes": [
{
"name": "testfile-2m",
"type": "file",
"mode": 420,
"mtime": "2024-09-06T00:17:04.958712137+08:00",
"atime": "2024-09-06T00:17:04.958712137+08:00",
"ctime": "2024-09-06T00:17:04.958712137+08:00",
"uid": 501,
"gid": 20,
"user": "greene",
"group": "staff",
"inode": 5929351,
"device_id": 16777233,
"size": 2097152,
"links": 1,
"content": [
"07854d2fef297a06ba81685e660c332de36d5d18d546927d30daad6d7fda1541",
"07854d2fef297a06ba81685e660c332de36d5d18d546927d30daad6d7fda1541",
"07854d2fef297a06ba81685e660c332de36d5d18d546927d30daad6d7fda1541",
"07854d2fef297a06ba81685e660c332de36d5d18d546927d30daad6d7fda1541"
]
}
]
}
snapshot
一个快照代表某个备份时间点一个目录下的所有文件和子目录,每次备份默认都会生成一个新的快照。
➜ restic -r ./repo cat snapshot ae3ce93ba29260e9b0a0b152685eeb4ca7f503375c148250c8ddf1ba45f3f1f2
{
"time": "2024-09-06T00:19:05.281825+08:00",
"tree": "148aeb448ff7d2cfe6628b48f351a729ac04677c098b4da5ff5799f8d2b2ee7e",
"paths": [
"/Users/greene/Desktop/testdata"
],
"hostname": "greenedeMacBook-Pro.local",
"username": "greene",
"uid": 501,
"gid": 20,
"tags": [
"TAG1"
],
"original": "c85875eeeece3a6ddfa5a0393a32f1ca7741b33fa3df1366a6ab252b36f473bc",
"program_version": "restic 0.16.5 (v0.16.5-0-gfe9f142b5)"
}
lock
restic存储库结构的设计允许多个restic实例并行访问,甚至并行写入。也有些操作需要对存储库进行独占访问,例如执行prune操作清理存储库中不需要的数据。为了实现这些功能,restic进程需要在执行任何操作之前对存储库创建锁定。
锁有两种类型:独占锁和非独占锁。最多一个进程可以对存储库拥有独占锁,在此期间不能有任何其他锁(独占锁和非独占锁)被创建。但是可以并行存在多个非独占锁。
{
"time": "2024-09-06T01:24:13.182534674+08:00",
"exclusive": false,
"hostname": "greenedeMacBook-Pro.local",
"username": "greene",
"pid": 877837,
"uid": 1000,
"gid": 1000
}
当要创建新锁时,restic会检查存储库中的所有锁。
当存在锁时, 会检查当前需要创建的锁和和已存在的锁类型决定是否需要可以创建。如果指定--retry-lock参数,restic将定期重试创建锁,直到成功或超时。
五、缓存
Restic在本地保留来自存储库的一些文件的缓存。这样可以加快操作速度,因为不需要从远程存储库加载元数据。缓存会自动创建,通常在特定于操作系统的缓存文件夹中:
- Linux:~/.cache/restic
- Windows:%LOCALAPPDATA%/restic
- MacOS:~/Library/Caches/restic
$ tree ~/.cache/restic
├── 23e5b8af09c55f1128cc263d0f160ce3d6520687b94f6e5cb2a3196839de5733
│ ├── data
│ │ ├── 2e
│ │ ├── 54
│ │ │ └── 54987a8a6b24e4d11e8981f22f3e54c326ac8e213b5459ab461908fa5ab35e6e
│ │ ├── a1
│ │ └── f9
│ │ └── f901393bbb9a41d9cd6b2fce5fa0f715e26244fb7c97f6c17ad1d64508c6ecf2
│ ├── index
│ │ ├── 3d
│ │ │ └── 3db400ac59af62152ca4f14391f09b8eafce36597cc351c53b3a813399d80d9e
│ │ ├── 4d
│ │ │ └── 4d1df177c003897ea7425712301c997fce1649fa4b0391ea2e4ee99897086631
│ │ ├── 61
│ │ ├── 6d
│ │ ├── b8
│ │ └── ed
│ ├── snapshots
│ │ ├── 84
│ │ │ └── 8403bd57a70694e2b6ec861b44e10be54c74e92c464fbe13bc922de9f34437d9
│ │ ├── 9b
│ │ ├── ae
│ │ │ └── ae3ce93ba29260e9b0a0b152685eeb4ca7f503375c148250c8ddf1ba45f3f1f2
│ │ ├── d9
│ │ └── dc
│ └── version
└── CACHEDIR.TAG
初始化存储库设置--cache-dir参数或设置环境变量$RESTIC_CACHE_DIR可以定义缓存路径。也可以设置--no-cache参数禁用缓存,在这种情况下,所有数据都从存储库加载。当无法从缓存中读取到需要的文件时,就从存储库中加载它。
在缓存目录中,缓存所使用的每个存储库都有一个子目录。Restic每次使用存储库目录时(无论读写)都会更新其时间戳,因此通过查看缓存目录子目录的时间戳,它可以确定哪些子目录是旧的并且可能不再需要。
六、应用场景
-
备份数据库的逻辑导出文件
Restic 可以很好地备份数据库的逻辑导出文件(如 mysqldump、pg_dump 生成的 SQL 文件)。这些导出文件通常比较大,但 Restic 的去重功能可以优化存储空间,特别是在只存在部分数据变化的情况下。 -
离线备份和长时间保留
Restic支持端到端加密和高效存储,对需要长期保留或归档的数据库备份非常适合。企业可以将数据库冷备份存储在离线介质或云端。
参考文档:
https://files.cnblogs.com/files/greene/restic-wiki.tar.gz?t=1726921501&download=true
本文来自博客园,作者:GreeneGe,转载请注明原文链接:https://www.cnblogs.com/greene/p/18374686