备份Cassandra的挑战
备份Apache Cassandra数据库很困难,并不复杂。您可以使用nodetool snapshot手动做快照,并将其从节点移到另一个节点。现有的开放源代码工具(例如tablenap)就是这样做的。但是,它们都往往缺少线上生产中所需的某些功能,尤其是在还原数据时,这是对备份解决方案的终极测试。
为Cassandra提供灾难恢复有一些有趣的挑战和机遇:
- 每个SSTable中的数据都是不可变的,从而允许进行高效的差异备份,该差异备份仅复制自上次备份以来的更改。
- 每个SSTable都包含该节点负责的数据,还原过程必须确保将其放置在也负责该数据的节点上。否则,客户端可能无法访问。
- 要恢复到不同的群集配置,节点数或令牌数的变化,需要按照Cassandra的规则将数据重新分配到新的拓扑中。
medusa(美杜莎)介绍
Medusa是一个命令行备份和还原工具,可了解Cassandra的工作方式。
该项目最初由Spotify创建,以替换其旧式备份系统。TLP在不久之后被聘来接管开发工作,使其可以投入生产并进行开源。
它已在小型和大型集群上使用,并提供了运营团队所需的大多数功能。
美杜莎支持:
- 备份单个节点。
- 恢复单个节点。
- 还原整个群集。
- 选择性还原keyspace和表。
- 支持单个令牌和vnode群集。
- 清除旧数据的备份集。
- 完整或增量备份模式。
- 自动验证还原的数据。
使用Python 3.6版的命令行工具,需要安装在要备份的所有节点上。它支持2.1.0之后的所有版本的Cassandra,并且由于Apache libcloud项目可以在许多平台上存储备份,包括:
- Amazon Web服务S3。
- Google Cloud Platform GCS。
- 本地文件存储。
- 任何支持libcloud的provider都只需最少的努力。
使用Medusa备份单个节点
一旦安装并配置了medusa,就可以使用一个简单的命令来备份节点:
当这样执行时,美杜莎将:medusa backup --backup-name=<backup name>
- 使用Cassandra nodetool命令创建快照。
- 将快照上载到配置的存储提供程序。
- 从本地节点清除快照。
与SSTables一起,Medusa将为每个备份存储三个元文件:
- 完整的CQL schema。
- 令牌映射,节点列表及其令牌所有权。
- 清单,带有md5哈希值的备份文件列表。
完整备份和差异备份
所有Medusa备份仅从节点复制新的SSTable,从而减少了所需的网络流量。然后,它有两种方法来管理备份目录中的文件,我们称之为完全备份或差异备份。对于差异备份,每个新备份仅保留对SSTables的引用,因此每个SStable无论其有多少个备份,仅有一份数据,。差异备份是默认的,在Spotify中的操作减少了某些群集的备份大小高达80%
完全备份每次运行时都会在节点上创建所有SSTable的完整副本。自上次备份以来未更改的文件将在备份目录中复制到新备份中(而不是从节点上复制)。与仅创建对文件的引用的差分方法相反。当您需要制作完整副本并将所有文件放在一个位置时,完全备份非常有用。
差异备份利用了由Cassanda使用的LSM存储引擎创建的不可变SSTable的特点。在这种模式下,Medusa会检查SSTable之前是否已经备份过,并且仅复制新文件(就像往常一样)。但是,该节点的所有SSTable然后都存储在一个公用文件夹中,并且备份清单仅包含元数据文件和对SSTable的引用。
使用Medusa备份群集
Medusa当前缺少业务流程层来为您在所有节点上运行备份。实际上,我们一直在使用crontab进行群集范围的备份。虽然我们一直在考虑了自动化执行此操作的最佳方法,但我们建议使用以下技术:
- 通过crontab在每个节点上调度。
- 使用pssh在所有节点上手动进行。
- 使用cstar编写脚本。
列出备份
具有相同“备份名称”的所有备份均被视为群集的同一备份的一部分。Medusa可以提供群集的所有备份的清单,包括备份的开始和结束时间以及所有节点是否都已完成备份。
要列出集群的所有现有备份,请在一个节点上运行以下命令:
在上面的示例中,名为“ 2019081307”的备份被标记为未完成,因为180个节点中有1个节点未能完成备份。$ medusa list-backups 2019080507 (started: 2019-08-05 07:07:03, finished: 2019-08-05 08:01:04) 2019080607 (started: 2019-08-06 07:07:04, finished: 2019-08-06 07:59:08) 2019080707 (started: 2019-08-07 07:07:04, finished: 2019-08-07 07:59:55) 2019080807 (started: 2019-08-08 07:07:03, finished: 2019-08-08 07:59:22) 2019080907 (started: 2019-08-09 07:07:04, finished: 2019-08-09 08:00:14) 2019081007 (started: 2019-08-10 07:07:04, finished: 2019-08-10 08:02:41) 2019081107 (started: 2019-08-11 07:07:04, finished: 2019-08-11 08:03:48) 2019081207 (started: 2019-08-12 07:07:04, finished: 2019-08-12 07:59:59) 2019081307 (started: 2019-08-13 07:07:03, finished: Incomplete [179 of 180 nodes]) 2019081407 (started: 2019-08-14 07:07:04, finished: 2019-08-14 07:56:44) 2019081507 (started: 2019-08-15 07:07:03, finished: 2019-08-15 07:50:24)
还可以验证是备份中所有期望的文件,并且它们的内容与备份时生成的哈希值匹配。更多操作在Medusa README文件中进行了详细说明。恢复备份
当缺少备份的编排时,Medusa协调还原整个群集,因此您只需要运行一个命令即可。该过程通过SSH连接到节点,并根据需要启动和停止Cassandra,直到可以使用集群为止。还原过程处理三种不同的用例。
- 还原到同一群集。
- 还原到具有相同数量节点的其他群集。
- 还原到具有不同数量节点的其他群集。
案例1-还原到同一群集
这是最简单的情况:将备份还原到同一群集。群集的拓扑结构未更改,创建备份时存在的所有节点仍在群集中运行。
使用以下命令运行就地还原:
$ medusa restore-cluster --backup-name=<name of the backup> \
--seed-target node1.domain.net
种子目标节点将用作发现群集中其他节点的联系点。Medusa将发现集群中节点和令牌分配的数量,并检查其是否与源集群的拓扑匹配。
要完成此还原,每个节点将:
- 将备份数据下载到
/tmp
目录中。 - 停止cassandra。
- 删除提交日志,保存的缓存和数据目录(包括系统keyspace)。
- 将下载的SSTables移到数据目录中。
- 启动Cassandra。
不需要重新创建schema,因为它包含在系统keyspace中,并且可以从备份中复制。
情况2-还原到具有相同节点数的其他群集
还原到具有相同数量节点的其他群集会有些困难,因为:
- 目标群集可能具有不同的名称,该名称存储在system.local表中。
- 节点可以具有不同的名称。
- 节点可以具有不同的令牌分配。
使用以下命令运行远程还原:
$ medusa restore-cluster --backup-name=<name of the backup> \
--host-list <mapping file>
该host-list
参数告诉Medusa如何从原始备份节点映射到新集群中的目标节点,该新集群假定为正在工作的Cassandra集群。映射文件必须是具有以下各列的命令分隔文件(无标题行):
- is_seed:
True
或False
指示目标节点是否是种子节点。因此,我们可以首先还原并启动种子节点。 - target_node:目标集群中节点的主机名。
- source_node:要从中复制备份数据的源节点的主机名。
例如:
True,new_node1.foo.net,old_node1.foo.net
True,new_node2.foo.net,old_node2.foo.net
False,new_node3.foo.net,old_node3.foo.net
除了上面为案例1列出的步骤之外,在执行到远程集群的备份时,还执行以下步骤:
- 不会修改system.local和system.peers表以保留集群名称并防止目标集群连接到源集群。
- 除非将
--keep-auth
标志传递给restore命令,否则从备份中还原system_auth keyspace。 - 通过在
-Dcassandra.initial_token
重新启动节点时传递JVM参数,在目标节点上更新令牌所有权以匹配源节点。这将中更新本地system keyspace,会引起重新更改数据所有权。情况#3-还原到具有不同数量节点的其他群集
恢复到具有不同数量节点的其他集群是最难处理的情况,因为:
- 目标群集可能具有不同的名称,该名称存储在system.local表中。
- 节点可以具有不同的名称。
- 节点可以具有不同的令牌分配。
- 令牌范围永远不能相同,因为节点数不同。
最后一点是问题的症结所在。我们无法获得相同的令牌分配,因为我们有不同数量的节点,并且令牌被分配为在节点之间平均分配数据。但是,我们备份的SSTables包含与源群集中定义的令牌范围对齐的数据。还原过程必须确保根据新令牌分配将数据放置在作为副本的节点上,否则数据似乎已丢失。
为了支持将数据还原到其他拓扑中,Medusa使用了Cassandra代码库中的sstableloader
工具。尽管比从备份中复制文件要慢,但是sstableloader能够将数据“修复”到目标群集中。它通过读取令牌分配并将SSTable中与新令牌范围匹配的部分流式传输到集群中的所有副本来实现。
使用以下命令来运行到具有不同拓扑的集群的还原:
$ medusa restore-cluster --backup-name=<name of the backup> \
--seed-target target_node1.domain.net
使用此技术还原数据有一些短板:
- 还原将花费更长的时间。
- 加载到群集中的数据量将是备份集的大小乘以复制因子。例如,具有复制因子3的群集的备份将具有9个数据副本的副本。多余的副本将通过压缩删除,但是在还原过程中磁盘上的总负载将比还原结束时的总负载高。参见下面的进一步讨论。
- 将删除集群中的当前schema,并使用备份中的schema创建一个新的。默认情况下,当删除schema时,Cassandra将做一个快照,该快照由
auto_snapshot
配置设置控制,Medusa或Cassandra不会清除该快照,这个快照将占用额外的磁盘空间。这是明智的安全预防措施,一种简单的解决方法是手动确保目标群集中没有任何数据。
使用sstableloader还原时,有一些关于数据放大的额外说明。备份具有多副本的数据,假如我们的复制因子为3,大致来说每个分区有3个副本。这些副本散布在我们从每个节点收集的SSTable上。当我们处理每个SSTable时,sstableloader将数据修复回群集,并将其发送到3个新副本。因此,备份包含3个副本,我们将处理每个副本,然后将每个副本发送到3个新副本,这意味着:
- 还原将九份数据副本发送到群集。
- 每个节点获得三个数据副本,而不是一个。
运行这种类型的还原时,将发生以下操作序列:
- 删除schema并重新创建它们(一次用于整个集群)
- 将备份数据下载到/ tmp目录中
- 为备份中的每个表运行sstableloader
现在可以在GitHub上使用
Medusa现在可以在GitHub上使用,并且很快将通过PyPi提供。使用此博客文章和存储库中的自述文件,您应该能够在开始的几分钟内进行备份。与往常一样,如果您有任何问题,请在GitHub项目中创建一个问题以获取帮助。它已经在Spotify中使用了几个月,并在Google Cloud Storage(GCS)中存储了PB的备份,并且我们感谢Spotify向社区捐赠了该软件,以使其他人也可以放心地对其数据进行安全备份。
最后一件事,我们很乐于地接受社区贡献patch,尤其是增加支持新对象存储源,如阿里OSS
入群邀约
为了营造一个开放的 Cassandra 技术交流环境,社区建立了微信群公众号和钉钉群,为广大用户提供专业的技术分享及问答,定期开展专家技术直播,欢迎大家加入。另外阿里云提供免费Cassandra试用:https://www.aliyun.com/product/cds