Category: Storage*


S3 Select 和 Glacier Select – 检索对象子集

Amazon Simple Storage Service (S3) 为每个行业的市场领导者使用的数以百万计的应用程序存储数据。其中许多客户还使用 Amazon Glacier 作为安全、持久且成本极低的存档存储。借助 S3,我可以存储任意数量的对象,单一对象最大可达 5 TB。一直以来,对象存储中的数据都是作为整体访问的,也就是说,当您查询一个大小为 5 GB 的对象时,您会获得全部 5 GB 的数据。这是对象存储的工作方式所决定的。今天,我们将宣布 S3 和 Glacier 的两个新功能挑战这一模式 – 它们让您能够使用简单的 SQL 表达式从这些对象中只提取需要的字节。这可从根本上增强访问 S3 或 Glacier 中对象的每一个应用程序。

S3 Select

S3 Select (随预览版发布) 让应用程序能够使用简单的 SQL 表达式只检索对象的数据子集。使用 S3 Select 仅检索应用程序所需的数据可大幅提升性能 – 在许多情况下,您可获得多达 400% 的性能提升。

例如,假设您是一家大型零售商的开发人员,您需要分析某个店铺的每周销售数据,但所有 200 家店铺的数据每天都保存在一个新的经过 GZIP 压缩的 CSV 文件中。如果没有 S3 Select,您需要下载、解压缩并处理整个 CSV 才能获得所需的数据。而利用 S3 Select,您可以使用简单的 SQL 表达式仅返回所需的店铺数据,而不必检索整个对象。这可将需要处理的数据量减少一个数量级,从而提高底层应用程序的性能。

我们来看一个简单的 Python 示例。

import boto3
from s3select import ResponseHandler

class PrintingResponseHandler(ResponseHandler):
    def handle_records(self, record_data):
        print(record_data.decode('utf-8'))

handler = PrintingResponseHandler()
s3 = boto3.client('s3')
response = s3.select_object_content(
    Bucket="super-secret-reinvent-stuff",
    Key="stuff.csv",
    SelectRequest={
        'ExpressionType': 'SQL',
     'Expression': 'SELECT s._1 FROM S3Object AS s'',
        'InputSerialization': {
            'CompressionType': 'NONE',
            'CSV': {
                'FileHeaderInfo': 'IGNORE',
                'RecordDelimiter': '\n',
                'FieldDelimiter': ',',
            }
        },
        'OutputSerialization': {
            'CSV': {
                'RecordDelimiter': '\n',
                'FieldDelimiter': ',',
            }
        }
    }
)
handler.handle_response(response['Body'])

很酷吧!为实现这一行为,S3 Select 使用二进制通信协议返回对象。目前,这需要借助一个小型附加库来帮助处理反序列化。

客户可以利用 S3 Select 加速各类应用程序。例如,这种部分数据检索功能对于使用 AWS Lambda 构建的无服务器应用程序特别有用。我们修改了 Serverless MapReduce 参考架构以使用 S3 Select 仅检索所需数据,结果显示:性能提高了 2 倍,成本则下降了 80%。

S3 Select 团队还创建了一个 Presto 连接器,它能够在不更改查询的情况下立即提高 Amazon EMR 的性能。我们运行了一个对从 S3 检索的接近 99% 的数据进行筛选的复杂查询,借此对连接器进行了测试。在禁用 S3 Select 的情况下,Presto 必须从 S3 扫描和筛选整个对象,而启用 S3 Select 后,Presto 借助 S3 Select 仅检索查询所需的数据。

[hadoop@ip-172-31-19-123 ~]$ time presto-cli --catalog hive --schema default --session hive.s3_optimized_select_enabled=false -f query.sql
"31.965496","127178","5976","70.89902","130147","6996","37.17715","138092","8678","135.49536","103926","11446","82.35177","116816","8484","67.308304","135811","10104"
 
real  0m35.910s
user  0m2.320s
sys   0m0.124s
[hadoop@ip-172-31-19-123 ~]$ time presto-cli --catalog hive --schema default --session hive.s3_optimized_select_enabled=true -f query.sql
"31.965496","127178","5976","70.89902","130147","6996","37.17715","138092","8678","135.49536","103926","11446","82.35177","116816","8484","67.308304","135811","10104"
 
real  0m6.566s
user  0m2.136s
sys   0m0.088s

不启用 S3 Select 时,此查询耗时 35.9 秒;启用 S3 Select 时,只花费 6.5 秒。速度提高了 5 倍!

需知信息

  • 在预览版中,S3 Select 支持使用和未使用 GZIP 压缩的 CSV 及 JSON 文件。预览版不支持静态加密对象。
  • 预览版使用期间,S3 Select 不收费。
  • Amazon AthenaAmazon RedshiftAmazon EMR 以及 Cloudera、DataBricks、Hortonworks 等合作伙伴都将为 S3 Select 提供支持。

Glacier Select

在受到严格监管的金融服务、医疗保健等行业中,一些公司将数据直接写入 Amazon Glacier,以满足 SEC Rule 17a-4、HIPAA 等合规性要求。许多 S3 用户制定了生命周期策略,通过将不再需要定期访问的数据移入 Glacier 来节省存储成本。大多数传统存档解决方案 (例如本地磁带库) 对数据检索吞吐量有严格限制,不适合进行快速分析或处理。需要用到存储在磁带上的数据时,您可能需要等待数周时间才能得到有用的结果。相比之下,您现在只需数分钟时间就能轻松查询存储在 Glacier 中的冷数据。

这可帮助您从存档数据中发掘大量的全新业务价值。Glacier Select 让您能够使用标准 SQL 语句直接筛选 Glacier 对象。

Glacier Select 的工作方式与任何其他检索作业一样,但它有一组您可在启动作业请求中传入的额外参数。 SelectParameters

下面是一个简单示例:

import boto3
glacier = boto3.client("glacier")

jobParameters = {
    "Type": "select", "ArchiveId": "ID",
    "Tier": "Expedited",
    "SelectParameters": {
        "InputSerialization": {"csv": {}},
        "ExpressionType": "SQL",
        "Expression": "SELECT * FROM archive WHERE _5='498960'",
        "OutputSerialization": {
            "csv": {}
        }
    },
    "OutputLocation": {
        "S3": {"BucketName": "glacier-select-output", "Prefix": "1"}
    }
}

glacier.initiate_job(vaultName="reInventSecrets", jobParameters=jobParameters)

需知信息

Glacier Select 已在提供 Glacier 的所有商业区域正式发布。

Glacier 定价包含三个要素:

  • 扫描数据量 (GB)
  • 返回数据量 (GB)
  • Select 请求

每个要素的定价取决于您希望返回结果的速度:加急 (1-5 分钟)、标准 (3-5 小时) 和批量 (5-12 小时)。

2018 年,Athena 将很快通过 Glacier Select 与 Glacier 集成。

希望本文对您利用这些功能增强或构建全新应用程序有所助益。

Randall

Amazon S3 深度实践系列之二:如何实现 S3 数据跨区域高效可靠传输

背景

Amazon S3 深度实践系列之一:S3 CLI深度解析及性能测试一文中,我们深度剖析了AWS CLI S3相关命令的实际工作原理及单机下载S3数据的基本性能测试情况。在实际工作场景中,很多客户会在AWS多个区域的S3桶里面存储大量数据,而且会遇到将数据批量从一个区域一次转移到另外一个区域的情形;因此,在本篇中,作者和大家一起来探讨下出现这样的需求我们如何进行架构设计及高效实现。

架构设计

存储在S3中的对象随着时间的推移,对象数量逐渐增加,而且总体的数据量也不断膨胀,如果碰到需要将数据从某一个区域的S3存储桶完全复制到另外一个S3存储桶里面,我们会遇到哪些挑战呢?

  • 网络传输带宽的限制
  • 存储桶里面所有对象的分析和列表
  • 源存储桶和目标存储桶权限的设定
  • 传输失败识别和重试的挑战
  • 如何利用并发来加速传输及降低成本
  • 如何判断目标存储桶中的对象和源存储桶中的对象差异及完整性

在通用架构设计环节,我们将复杂的问题分解成一系列的子问题进行分析,并讨论在不同场景下的具体实现时要考虑的因素。如下图所示,我们将该任务分解成独立的五个环节,从图上我们也可以看出来,如何实现大规模数据或任务的并发执行是每个环节能否高效完成的一个很关键的技术要求;而且,只有在步骤三执行数据传输任务时,才会涉及到具体场景中的技术限制,因此我们在执行数据传输任务章节来讨论,同区域不同存储桶之间,AWS海外不同区域存储桶之间,以及AWS海外和国内不同存储桶之间的具体技术考量点。

S3对象“清单”

了解源和目标存储桶里的S3 对象是非常重要的准备工作,该章节我们讨论,如何获得S3存储桶的所有对象列表,包含对象的基本的信息,比如最新版本的对象大小,ETag等等。

Amazon S3本身提供了存储桶管理功能之清单生成功能,该功能是一个异步的AWS后台定期执行,可以实现每天生成一个存储桶清单保存成Excel格式。

同时我们也看到很多用户提问,如何实现一个自定义的清单功能,满足大家对于对象变化比较频繁的存储桶对象的实时统计场景以及更多高级自定义的业务逻辑。

接下来我们来看看这两种方法的具体实现逻辑。

利用S3 CLI实现高效的清单功能

作者利用AWS S3 CLI实现高效的清单功能基于以下两个事实前提:

  1. s3api 的 list-objects-v2虽然文档中说明最多返回1000个对象,但实测可以获得所有对象列表
  2. 同样利用s3api 的 list-objects-v2的delimiter和prefix参数,我们可以实现类似文件夹目录逐级扫描功能

基于以上两个事实,我们实现桶清单的主要逻辑如下图所示:

  • 输入参数主要是:bucket,region和IAM 配置的profile名字,profile默认为default;另外depth控制扫描的“目录”层级
  • 当depth为零时,我们直接尝试利用list-objects-v2一次性获取存储桶中所有对象列表并生成一个json格式的文件(但当桶里面对象太多时,该操作会超时)
  • 当depth为零即单线程无法直接生成存储桶清单时,我们就尝试如下迭代逻辑:
    • 生成存储桶当前“目录”里面的所有对象和该目录中所有“子目录”列表
    • 遍历上一步的“子目录”列表,迭代生成该目录下的对象列表和“子目录”列表
    • 如果遍历的深度等于输入参数depth=n,或者“子目录”列表为空,那么停止遍历子目录,直接生成该层级“目录”里面所有的对象列表

以下是几个关键点实现的代码说明,首先,生成某个“目录”前缀下所有对象列表的AWS S3 CLI命令参考,如下命令将在操作系统后台执行并生成存储桶jason中“目录”前缀“qwikLabs/”下的所有对象列表(包括所有嵌套“子目录”中的所有对象):

$ nohup aws s3api list-objects-v2 --bucket "jason" --prefix "qwikLabs/" --profile bjs > 0.obj. 2>&1

其次,如下命令将仅仅生成指定“目录”前缀“qwikLabs/”下的对象列表(不包括嵌套“子目录“的对象)和所有下一层“子目录“列表,为了加强”子目录“输出格式,我们增加了query参数:

$ nohup aws s3api list-objects-v2 --bucket "jason" --prefix "qwikLabs/" --delimiter "/" --query "{Keys:Contents[].{Key: Key, Size: Size,ETag:ETag},CommonPrefixes:CommonPrefixes[].Prefix}" --profile bjs > 0.1.obj. 2>&1

另外,为了实现并发我们利用了迭代算法以及操作系统后台异步执行AWS S3 CLI命令的方法,最终程序会生成一系列的json文件结果,存储桶中所有的对象列表分布在这些文件当中。

S3自带的清单功能

在了解了我们通过AWS CLI S3命令行工具实现自定义的清单功能之后,我们再来对比下,Amazon S3自带的清单功能。

在存储桶页面,导航到“管理“标签,Amazon S3目前提供了四项S3管理功能,其中跟本文相关的是”清单“功能。该功能支持我们对某一个存储桶,定义多个清单,用户可以根据需要,定义针对不同S3对象前缀生成各自的清单列表,并可以存储在独立的存储桶中:

同时,自带清单功能还支持定义生成清单的频率及清单中包含的对象字段,检查并确定好清单选项之后,服务会帮助我们在保存清单的目标存储桶中设置好相应的IAM策略:

清单任务保存之后,后台会异步定期执行,每次都会按时间生成manifest.json 文件和一系列的清单文件,manifest.json 里面包含这次生成的所有清单文件列表:

S3对象清单小结

Amazon S3 存储桶已经内置了清单功能,基本可以满足我们的日常需求,我们不需要重复造轮子;本章节所讨论的利用AWS S3 CLI 命令行自定义实现清单功能,更多的是作者好奇的发现,AWS S3 CLI 本身非常好用,也可以帮助我们实现类似文件目录的逐级对象列表功能,提供给有特殊场景需求的用户参考。

对象清单分解成传输任务

有了存储桶中所有的对象清单,接下来,我们就看看如何设计传输任务。设计传输任务的原则如下:

  • 如果网络条件非常良好,比如同区域的不同存储桶之间,按照作者的测试,复制带宽平均可以达到xxMB/s,如此可以直接利用S3 cp命令
  • 尽可能将单进程的复制任务分解成多个子任务并发执行,任务分解后进入到Amazon SQS队列,这样将任务分解和任务执行进行解耦
  • 如果网络条件非常一般,比如平均在10KB/s并且网络抖动大的情况下,对于超过一定大小的文件需要切割成小文件,组成子任务并发执行

传输任务分解算法的设计,涉及到几个关键参数:

  • max_task_size_mb:单个任务的对象总大小上限,比如最大大小限制在100MB,那么单个任务最多有100MB的对象列表,或者该任务就一个对象,该对象本身大小就超过了100MB
  • max_task_objects:单个任务的对象数量上限,比如数量上限为50,那么单个任务中最多有50个对象需要传输
  • multipart_threshold及multipart_chunksize:对象太大时,需要分割成多个小对象传输任务,那么多大的对象需要进行分解?分段的单位大小是多少?比如阈值是10MB,单位大小是2MB,那么大于10MB的对象都需要再分解成2MB的多个子对象并发续传

Amazon SQS任务队列设计与实现

传输任务在设计时分成两大类,一种是本身对象就是小文件,我们按照max_task_size_mb 和 max_task_objects 进行分组,即每个任务总数据量大小不会超过max_task_size_mb,而且对象数量也不超过 max_task_objects ;这些任务我们会发送到自动创建的S3Task_NormalQueue开头的SQS队列中,每个队列的消息数量上限本文设为80000条;另外一类是,对象大小超过multipart_threshold 限制的,我们会进一步把该对象分解成 multipart_chunksize大小的独立对象,同样按照 max_task_size_mb 和 max_task_objects 的算法进行分组,但这些任务会保存到自动创建的以S3Task_BigSizeQueue开头的队列中。

另外遵循Amazon SQS操作的最佳实践,我们分别为这两类任务队列设定了同样的死信队列,当消息被读取10次而没被处理成功的会自动转移到S3Task_DeadQueue进行存储和后续处理。

执行数据传输任务

当任务队列产生之后,接下来,就到了如何高效执行如何多的传输任务的阶段,很多网络和客观条件的限制,我们都放到了如何分解传输任务的算法里面进行了实现,在执行数据传输任务环节,逻辑非常简单:

  • 读取一条SQS任务
  • 根据任务中的具体对象,每个对象利用独立线程进行复制或者下载再上传
  • 只有该任务中所有的对象都传输成功,才把该任务消息从SQS队列中删除

同区域S3数据复制

同区域不同S3存储桶之间数据复制,由于网络条件较好,IAM权限简单,可以尽量利用boto3的copy-object方法直接利用S3服务本身能力,进行快速数据复制,该方法数据无需经过命令执行的机器中转。因此在任务分解环节,multipart_threshold 的值需要设置一个比较大而且合理的值,避免大文件被分片之后,需要先下载后上传,这样会消耗更多的流量。

AWS海外不同区域S3数据复制

海外不同区域的S3存储桶之间复制,和同区域的不同存储桶复制场景类似,但由于跨区域传输,网络状况取决于两个区域的位置及它们之间的互联网状况。

AWS海外区域和BJS区域S3数据复制

该情况最复杂,AWS海外和国内是独立的区域,需要不同的账号权限体系,因此,对象需要先进行下载再上传,这样就需要占用执行命令的机器的内存和网络带宽;

  • 在任务分解时,需要尽量把大对象分解成小的片段比如2MB或者1MB的大小以提高单次数据传输的成功率。
  • 在任务执行时,需要尽量并发,以单任务小带宽累积成可以接受的总体平均传输速度,并充分AWS出口带宽的优势

数据跨区域迁移实践

基于我们之前的架构设计,那我们分阶段来具体动手实践一个具体场景, Amazon官网有很多公开的数据集,我们选定Next Generation Weather Radar (NEXRAD) 作为数据源,该数据源在美东(us-east-1)区域;目标存储桶我们选择在BJS区域。该实验仅仅为了验证技术可行性,完整的参考代码见s3deepdive github;代码不作为生产用途仅仅用来学习用途。

准备环境

为了简单地完成技术验证,我们所有的测试环境基于一台r4.2xlarge的Amazon Linux机型展开,系统需要准备好:

  • 在AWS Global 美东区域创建一台EC2实例
  • 关联一个IAM Role,需要有访问S3及SQS相关的管理权限
  • Python2.7.x
  • Boto3
  • 300GB gp2 EBS 磁盘

配置好目标存储桶的IAM Profile及修改默认获取IAM Role的临时Token的默认超时时间和重试次数:

[ec2-user@youserver]$ aws configure --profile bjs
AWS Access Key ID [None]: AKI***************A
AWS Secret Access Key [None]: 7j+R6*****************oDrqU
Default region name [None]: cn-north-1
Default output format [None]:
[ec2-user@youserver]$ vi ~/.aws/configure
[default]
region = us-east-1
metadata_service_timeout = 5
metadata_service_num_attempts = 5
[profile bjs]
region = cn-north-1
metadata_service_timeout = 5
metadata_service_num_attempts = 5

生成对象清单

Amazon公共数据集没有提供清单列表,因此,我们利用前文的逻辑,并尝试利用AWS S3 CLI命令生成该存储桶的对象清单。该数据集按照年月进行数据分区,我们设定对象的Prefix的迭代深度为3,后台执行以下命令,并观察执行日志:

[ec2-user@youserver]$ cd NEXRAD_Demo/inventory
[ec2-user@youserver]$ nohup python ../../s3deepdive/s3_inventory.py -b noaa-nexrad-level2 -r us-east-1 -d 3 > noaa-nexrad-level2.log 2>&1 & 

由于数据集非常大,该命令执行需要点时间,最终,3层的扫描帮助我们并发生成了2751个对象清单文件,总大小4.5GB:

[ec2-user@youserver]$ cd NEXRAD_Demo/inventory
[ec2-user@youserver]$ ls noaa-nexrad-level2.*obj* | wc –l
2751

设计并提交传输任务

由于该场景下,源存储桶和目标存储桶之间的单次传输的速度非常有限,实测该场景下大概在9KB/s左右,而且网络抖动比较厉害,因此,我们尽量缩小单个任务的总数据量大小,并设定大对象的大小阈值设置为2MB;具体参数需要在Python常量参数中修改:

由于清单文件太多,总数据量太大,因此,我们可以数据清单分成多个目录,分别进行计算,比如如下命令:

  • 我们把大小小于800000 bytes的文件放到目录./1/里面
  • 把大小小于2MB的文件放到./2/里面
  • 把大小小于6MB的文件放到./3/里面

大家可以根据自己的需要,分成不同的对象清单文件夹

[ec2-user@youserver]$ cd NEXRAD_Demo/inventory
[ec2-user@youserver]$ mkdir 1 2
[ec2-user@youserver]$ find ./ -size -800000c -print0 | xargs -0 -I {} mv {} ../1/
[ec2-user@youserver]$ find ./ -size -2M -print0 | xargs -0 -I {} mv {} ../2/
[ec2-user@youserver]$ find ./ -size -6M -print0 | xargs -0 -I {} mv {} ../3/
[ec2-user@youserver]$ cd NEXRAD_Demo/tasksubmit
[ec2-user@youserver]$ nohup python ../../s3deepdive/s3_task_submit.py -d ../inventory/1/ -r us-east-1 > noaa-nexrad-level2.task1.log 2>&1 &
[ec2-user@youserver]$ nohup python ../../s3deepdive/s3_task_submit.py -d ../inventory/2/ -r us-east-1 > noaa-nexrad-level2.task2.log 2>&1 &
[ec2-user@youserver]$ nohup python ../../s3deepdive/s3_task_submit.py -d ../inventory/3/ -r us-east-1 > noaa-nexrad-level2.task3.log 2>&1 &

为了演示,我们没有生成所有对象清单的传输任务,仅仅选取了其中某连个文件夹,生成的传输任务如下图所示,有些队列的消息数为0,表示我们后台还有传输任务消息没有发送到队列中:

我们来看看队列里面的一个任务的结构组成,S3Task_Bigsize*队列中的任务相比于普通队列中的任务多了一组分片的Range范围:

设计并执行传输任务

在并发执行数据传输任务之前,我们先看看单个任务执行情况,任务执行需要指明任务队列,源和目的存储桶以及访问目标存储桶的IAM Profile名:

[ec2-user@youserver]$ cd NEXRAD_Demo/taskexec
[ec2-user@youserver]$ nohup python ../../s3deepdive/s3_task_exec.py -q S3Task_NormalQueue15098144850121 -source_bucket noaa-nexrad-level2 -dest_bucket bjsdest -dest_profile bjs > S3Task_NormalQueue15098144850121.exec1.log 2>&1 &
[ec2-user@youserver]$ nohup python ../../s3deepdive/s3_task_exec.py -q S3Task_BigSizeQueue1 -source_bucket noaa-nexrad-level2 -dest_bucket bjsdest -dest_profile bjs > S3Task_BigSizeQueue1.exec1.log 2>&1 & 

从执行日志可以分析出,对于NormalQueue中的单个任务,由于是小对象,而且数量是10,因此我们的执行代码可以并发执行,总体执行时间是26秒;对比BigsizeQueue中的任务,虽然总体数据大小和NormalQueue差不多,但由于只有2个对象并发复制,该任务的总体执行时间是363秒。

关于并发任务执行,本质上是一个批处理的业务逻辑,假定有1000个任务列表,

  • 每个任务数据量上限20MB,如果传输速度在10KB/s那么一个任务需要大概需要2048秒即34分钟,但我们的的任务执行是多线程并发操作,按每个任务最多10个对象算,在10KB/s的速度下,一个任务最快需要执行3.4分钟左右(10个对象并发上传),最慢34分钟(一个对象的情况下)
  • 如果同时100个并发执行,完成所有任务,需要至少执行10次,总时长在34分钟到340分钟之间
  • 如果并发1000个,完成所有任务需要至少执行1次;总时长3.4分钟到34分钟之间

本实验为了学习的目的,我们在测试机r4.2xlarge的机器上,后台并发执行100个任务,并观察数据传输的实际状况,

[ec2-user@youserver]$ cd NEXRAD_Demo/taskexec
[ec2-user@youserver]$ vi parallel_run.sh
#!/bin/bash

for((i=2; i<52;i++))

do

  nohup python ../../s3deepdive/s3_task_exec.py -q S3Task_BigSizeQueue1 -source_bucket noaa-nexrad-level2 -dest_bucket bjsdest -dest_profile bjs > S3Task_BigSizeQueue1.exec_$i.log 2>&1 &

done

for((i=2; i<52;i++))

do

  nohup python ../../s3deepdive/s3_task_exec.py -q S3Task_NormalQueue15098144850121 -source_bucket noaa-nexrad-level2 -dest_bucket bjsdest -dest_profile bjs > S3Task_NormalQueue15098144850121.exec_$i.log 2>&1 &

done
[ec2-user@youserver]$ chmod +x parallel_run.sh
[ec2-user@youserver]$ ./parallel_run.sh

针对下面这两个队列,每个运行了50个并发任务,因此在SQS界面上,可以看到传输中的消息是50,也就是同时有50个消息任务正在被处理:

可以看到BigsizeQueue队列一次就成功完成的数据传输任务总数为 79949+50-79971=28;NormalQueue队列一次就成功完成的数据传输任务总数为79950+50-79958=42;我们定义任务的成功与否,为该任务中所有的对象都成功传输完成;该实验我们对于分段采取的大小是2MB,在9KB/s左右的互联网传输速度下,还是有点大,容易失败;普通队列上中的任务,对象大小都在几百KB左右,一次传输成功的概率大很多。

数据完整性检查

本文不对单个对象的完整性问题展开探讨,对于用户首先最关心的问题是,源存储桶的对象有没有完全迁移到目前存储桶中;因此,可以定期生成目标存储桶的对象清单,并比对源存储桶的对象清单,在自定义的清单程序中,我们是逐级生产对象清单文件,有一定的规律,如果两个存储桶使用同样的depth参数生成,生成的对象清单文件个数首先一致的;具体到识别出有没有遗漏的迁移对象,可以进一步对比清单中的对象列表。

总结

本文就跨区域S3数据迁移整体架构作了基本探讨,并在架构的基础上,学习和实践了利用AWS S3 CLI以及boto3库如何实现自定义的对象清单,传输任务分解及执行逻辑。现实的生产场景下,还需要更多细节的思考和实践,接下来,我们会继续在大规模批处理,大规模对象集的完整性校验方面和大家继续探讨。

 

作者介绍:

薛军

AWS 解决方案架构师,获得AWS解决方案架构师专业级认证和DevOps工程师专业级认证。负责基于AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内的应用和推广,在互联网金融、保险、企业混合IT、微服务等方面有着丰富的实践经验。在加入AWS之前已有接近10年的软件开发管理、企业IT咨询和实施工作经验。

如何使用Amazon EC2 Systems Manager自动创建数据一致的EBS快照(Part 2)

作者:王宇

上一期我们讨论了如何在不关机的前提下实现AWS上实例的数据一致性快照问题,传送门:《如何使用Amazon EC2 Systems Manager自动创建数据一致的EBS快照(Part 1)

在本文的介绍中,我们将共同探索如何利用Amazon EC2 Systems Manager(SSM)和Microsoft VSS (Volume Shadow Copy Service)来创建数据一致的EBS快照。

SSM + VSS数据一致快照的原理

VSS (Volume Shadow Copy Service)是一个Windows操作系统内置的服务,用来协调与VSS兼容的应用程序(如SQL Server、Exchange Server等)的备份工作,如冻结或释放这些应用程序的I/O操作等。

VSS服务能够启动和监督副本拷贝的创建。“副本拷贝”是指一个逻辑卷在某一个时间点上的数据一致快照。比如:“C:”是一个逻辑卷,它与EBS快照不同。创建副本拷贝的步骤包括:

  • 请求方向VSS发出创建副本拷贝的请求。
  • VSS provider创建并维护副本拷贝。
  • VSS写入器(writer)保证数据的一致性。写入器负责在VSS provider创建副本拷贝之前固化临时数据并冻结I/O操作,并在VSS provider完成创建后释放I/O操作。通常情况下,会为每一个与VSS兼容的应用程序分配一个独立的写入器(writer)。

我们可以使用Run Command在windows实例中运行一个PowerShell脚本:

$EbsSnapshotPsFileName = "C:/tmp/ebsSnapshot.ps1"

$EbsSnapshotPs = New-Item -Type File $EbsSnapshotPsFileName -Force

Add-Content $EbsSnapshotPs '$InstanceID = Invoke-RestMethod -Uri http://169.254.169.254/latest/meta-data/instance-id'

Add-Content $EbsSnapshotPs '$AZ = Invoke-RestMethod -Uri http://169.254.169.254/latest/meta-data/placement/availability-zone'

Add-Content $EbsSnapshotPs '$Region = $AZ.Substring(0, $AZ.Length-1)'

Add-Content $EbsSnapshotPs '$Volumes = ((Get-EC2InstanceAttribute -Region $Region -Instance "$InstanceId" -Attribute blockDeviceMapping).BlockDeviceMappings.Ebs |? {$_.Status -eq "attached"}).VolumeId'

Add-Content $EbsSnapshotPs '$Volumes | New-EC2Snapshot -Region $Region -Description " Consistent snapshot of a Windows instance with VSS" -Force'

Add-Content $EbsSnapshotPs 'Exit $LastExitCode'

首先创建了一个名为“ebsSnapshot.ps1”的PowerShell脚本文件,脚本中为实例的每一个EBS卷创建一个快照。

$EbsSnapshotCmdFileName = "C:/tmp/ebsSnapshot.cmd"

$EbsSnapshotCmd = New-Item -Type File $EbsSnapshotCmdFileName -Force

Add-Content $EbsSnapshotCmd 'powershell.exe -ExecutionPolicy Bypass -file $EbsSnapshotPsFileName'

Add-Content $EbsSnapshotCmd 'exit $?'

再创建第二个名为“ebsSnapshot.cmd”脚本文件,用来执行之前创建的PowerShell脚本。

$VssScriptFileName = "C:/tmp/scriptVss.txt"

$VssScript = New-Item -Type File $VssScriptFileName -Force

Add-Content $VssScript 'reset'

Add-Content $VssScript 'set context persistent'

Add-Content $VssScript 'set option differential'

Add-Content $VssScript 'begin backup'

$Drives = Get-WmiObject -Class Win32_LogicalDisk |? {$_.VolumeName -notmatch "Temporary" -and $_.DriveType -eq "3"} | Select-Object DeviceID

$Drives | ForEach-Object { Add-Content $VssScript $('add volume ' + $_.DeviceID + ' alias Volume' + $_.DeviceID.Substring(0, 1)) }

Add-Content $VssScript 'create'

Add-Content $VssScript "exec $EbsSnapshotCmdFileName"

Add-Content $VssScript 'end backup'

$Drives | ForEach-Object { Add-Content $VssScript $('delete shadows id %Volume' + $_.DeviceID.Substring(0, 1) + '%') }

Add-Content $VssScript 'exit'

第三个名为“scriptVss.txt”的文件包含了DiskShadow命令。DiskShadow是一个包含在Windows Server 2008及以上版本中的VSS工具。这个脚本在EBS上为每一个逻辑卷创建一个副本拷贝,再为这个EBS创建一个快照,最后删除副本拷贝来释放磁盘空间。

diskshadow.exe /s $VssScriptFileName
Exit $LastExitCode

最终,在脚本模式中来运行DiskShadow命令。

这个脚本将保存在一个新的SSM Document中并关联到一个维护窗口中,在每天的午夜时间在每一台标签“consistentsnapshot”等于“windowsvss”的实例上运行。

在AWS console中快速实践

1.  使用AWS CloudFormation快速创建一组资源,包括:

a)   VPC和互联网网关

b)   VPC中创建一个子网和一个新的路由表,来实现互联网连接和AWS APIs

c)   创建一个IAM角色来赋予EC2实例相应的权限

d)   创建一个安全组,来允许来自internet的RDP访问,稍后将要通过远程登录到这个EC2实例中

e)   在子网中使用IAM角色创建和启动一个Windows实例,并分配好安全组

f)   创建一个包含上面例子中脚本的SSM document文件,来创建数据一致EBS快照

g)   创建另一个SSM document文件,其中的脚本来恢复逻辑卷中的数据,这些脚本将在下面的章节中详细说明

h)   创建一个能够生成Maintenance Windows的IAM role

2.  创建一个Maintenance Window

a)   在EC2 Console中选择:Systems Manager Shared Resources -> Maintenance Windows -> Create a Maintenance Window

b)   Name:ConsistentSnapshots

c)   Specify with:CRON/Rate expression

d)   CRON/Rate expression:cron(0 0 * * ? *)

e)   Duration:2 hours

f)   Stop initiating tasks:0 hour

g)   选择Create maintenance window

3.  为Maintenance Window关联一组目标:

a)   在Maintenance Window列表中选择刚刚创建的维护窗口

b)   在Actions中选择Register targets

c)   Owner information:WindowsVSS

d)   Select targets by:Specifying tags

e)   Tag Name:ConsistentSnapshot

f)   Tag Value:WindowsVSS

g)   选择Register targets

4.  给Maintenance Window分配一个任务

a)   在Maintenance Window列表中选择刚刚创建的维护窗口

b)   在Actions中选择Register targets

c)   在Document中选择此前创建的用于创建EBS快照的SSM document文件名

d)   在Target by中选择刚刚创建的目标

e)   在Role中,选择在CloudFormation中创建的IAM Role

f)   在Execute on中,Targets:1,Stop after:1 errors

g)   选择Register task

运维窗口和一致性快照操作已经创建完毕,你可以在Maintenance Windows窗口中的History页面查看每一次任务的执行情况。

如何将逻辑卷恢复到数据一致状态

在本文上面的例子中,我们会注意到,在给EBS进行快照的脚本中,我们实际上是先使用DiskShadow进行了副本拷贝操作,这个操作中已经包含了对I/O操作的冻结和释放,在此之后再进行了EBS的快照操作。而在这两个操作之间的瞬间如果数据发生改变,那么EBS快照中的数据就可能不能保持数据一致状态。

解决这个问题有两个方向,其一是自己创建一个VSS provider来创建EBS快照,而不是使用DiskShadow采用的windows内置的VSS provider来执行,在自己创建的VSS provider中做到先创建EBS快照再释放I/O操作,来保持数据一致性。

本文会介绍另一个解决方向,就是检查在EBS快照中的每一个逻辑卷数据,如果发现数据不一致的情况,就将其恢复到此前的副本拷贝(副本拷贝中的数据被VSS writer保持了数据一致性)。

步骤如下:

1.  在EC2 console中,选择Instances

2.  搜索获得EBS进行了快照的实例,注意这个实例所在的AZ

3.  选择Snapshots

4.  选择最新的快照,再选择Actions,Create Volume

5.  选择与此前相同的AZ,然后选择Create, Volumes

6.  选择刚刚创建的Volume,然后选择Actions, Attach Volume

7.  在Instances中选择进行了EBS快照的实例,然后选择Attach

8.  选择Run Command, Run a command

9.  在Command document中选择恢复EBS的脚本Document,在Target中选择这个Windows实例,然后选择Run

恢复EBS的脚本如下:

$OfflineDisks = (Get-Disk |? {$_.OperationalStatus -eq "Offline"})

foreach ($OfflineDisk in $OfflineDisks) {

  Set-Disk -Number $OfflineDisk.Number -IsOffline $False

  Set-Disk -Number $OfflineDisk.Number -IsReadonly $False

  Write-Host "Disk " $OfflineDisk.Signature " is now online"

}

$ShadowCopyIds = (Get-CimInstance Win32_ShadowCopy).Id

Write-Host "Number of shadow copies found: " $ShadowCopyIds.Count

foreach ($ShadowCopyId in $ShadowCopyIds) {

  "revert " + $ShadowCopyId | diskshadow

}

foreach ($OfflineDisk in $OfflineDisks) {

  $CurrentSignature = (Get-Disk -Number $OfflineDisk.Number).Signature

  if ($OfflineDisk.Signature -eq $CurrentSignature) {

    Set-Disk -Number $OfflineDisk.Number -IsReadonly $True

    Set-Disk -Number $OfflineDisk.Number -IsOffline $True

    Write-Host "Disk " $OfflineDisk.Number " is now offline"

  }

  else {

    Set-Disk -Number $OfflineDisk.Number -Signature $OfflineDisk.Signature

    Write-Host "Reverting to the initial disk signature: " $OfflineDisk.Signature

  }

}

通过比较,将数据不一致的逻辑卷恢复到了数据一致状态,这个EBS就回到了数据一致的状态。

本次的介绍就到这里。如对AWS混合云架构解决方案感兴趣,请联系我们:yuwangcn@amazon.com

 

作者介绍:

王宇,AWS企业容灾解决方案业务拓展经理,目前负责AWS中国区的混合云、容灾和DevOps产品和解决方案。曾服务于VMware等传统私有云厂商,熟悉传统IT架构和私有云、混合云、公有云的解决方案融合。