Author: AWS Team


深入Serverless—让Lambda 和 API Gateway支持二进制数据

1.概述

Serverless即无服务器架构正在迅速举起,AWS Lambda 和AWS API Gateway作为Serverless 架构主要的服务,正受到广泛关注,也有越来越多用户使用它们,享受其带来的便利。传统上来说,Lambda 和API Gateway主要用以实现RESTful接口,其响应输出结果是JSON数据,而实际业务场景还有需要输出二进制数据流的情况,比如输出图片内容。本文以触发式图片处理服务为例,深入挖掘Lambda 和 API Gateway的最新功能,让它们支持二进制数据,展示无服务器架构更全面的服务能力。

先看一个经典架构的案例——响应式主动图片处理服务。

Lambda配合 S3 文件上传事件触发在后台进行图片处理,比如生成缩略图,然后再上传到 S3,这是Lambda用于事件触发的一个经典场景。

http://docs.aws.amazon.com/lambda/latest/dg/with-s3-example.html

在实际生产环境中这套架构还有一些局限,比如:

  • 后台运行的图片处理可能无法保证及时完成,用户上传完原图后需要立即查看缩略图时还没有生成。
  • 很多图片都是刚上传后使用频繁,一段时间以后就使用很少了,但是缩略图还不能删,因为也可能有少量使用,比如查看历史订单时。
  • 客户端设备类型繁多,一次性生成所有尺寸的缩略图,会消耗较多Lambda运算时间和 S3存储。
  • 如果增加了新的尺寸类型,旧图片要再生成新的缩略图就比较麻烦了。

我们使用用户触发的架构来实现实时图片处理服务,即当用户请求某个缩略图时实时生成该尺寸的缩略图,然后通过 CloudFront缓存在CDN上。这其实还是事件触发执行Lambda,只是由文件上传的事件主动触发,变成了用户访问的被动触发。但是只有原图存储在S3,任何尺寸的缩图都不生成文件不存储到S3。要实现此架构方案,核心技术点就是让Lambda和API Gateway可以响应输出二进制的图片数据流。

总体架构图如下:

主要技术点:

  • 涉及服务都是AWS完全托管的,自动扩容,无需运维,尤其是 Lambda,按运算时间付费,省去 EC2 部署的繁琐。
  • 原图存在 S3 上,只开放给 Lambda 的读取权限,禁止其它人访问原图,保护原图数据安全。
  • Lambda 实时生成缩略图,尽管Lambda目前还不支持直接输出二进制数据,我们可以设置让它输出base64编码后的文本,并且不再使用JSON结构。配合API Gateway可以把base64编码后的文本再转换回二进制数据,最终就可以实现输出二进制数据流了。
  • 用 API Gateway 实现 图片访问的URL。我们常见的API Gateway用来做RESTful 的API接口,接口的 URL形式通常是 /resource?parameter=value,其实还可以配置成不用GET参数,而把URL中的路径部分作参数映射成后端的参数。
  • 回源 API Gateway,缓存时间可以用户自定义,建议为24小时。直接支持 HTTPS,支持享用AWS全球边缘节点。
  • CloudFront 上还可使用 Route 53 配置域名,支持用户自己的域名。

相比前述的主动生成,被动触发生成有以下便利或优势:

  • 缩略图都不存储在S3上,节省存储空间和成本。
  • 方便给旧图增加新尺寸的缩略图。

2.部署与配置

本例中使用的 Region 是Oregon(us-west-2),有关文件从以下链接下载:

https://s3.amazonaws.com/snowpeak-share/lambda/awslogo.png

2.1 使用IAM设置权限

打开控制台:

https://console.aws.amazon.com/iam/home?region=us-west-2

创建一个 Policy,名叫CloudWatchLogsWrite,用于确保Lambda运行的日志可以写到 CloudWatch Logs。内容是

{
"Version":
"2012-10-17",
"Statement": [
{
"Effect":
"Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": [
"arn:aws:logs:*:*:*"
]
}
]
}

 

创建 一个Role,名叫 LambdaReadS3,用于Lambda访问S3。Attach Poilcy选AmazonS3ReadOnlyAccess 和刚刚创建的CloudWatchLogsWrite。

记下它的ARN,比如arn:aws:iam::111122223333:role/LambdaReadS3

2.2 使用S3 配置原图存储

打开控制台

https://console.aws.amazon.com/s3/home?region=us-west-2

创建 Bucket,Bucket Name需要填写全局唯一的,比如 img201703,Region 选 US Standard。通常图片的原图禁止直接访问,这里我们设置权限,仅允许 Lambda 访问。

Permissions 下点 Add bucket policy,使用AWS Policy Generator :

Select Type of Policy 选 S3 bucket policy,

Principal 填写前述创建的LambdaReadS3 的 ARN arn:aws:iam::111122223333:role/LambdaReadS3

Actions 下拉选中 GetObjext,

Amazon Resource Name (ARN) 填写刚刚创建的bucket 的ARN,比如arn:aws:s3:::image201703/*

然后点Add Statement,最后再点 Generate Policy,生成类似

{
"Id": "Policy1411122223333",
"Version":
"2012-10-17",
"Statement": [
{
"Sid": "Stmt1411122223333",
"Action": [
"s3:GetObject"
],
"Effect":
"Allow",
"Resource":
"arn:aws:s3:::img201703/*",
"Principal": {
"AWS": [
"arn:aws:iam::111122223333:role/LambdaReadS3"
]
}
}
]
}

复制粘贴到Bucket Policy Editor里 save即可。

验证S3 bucket配置效果。把前下载图片文件awslogo.png 下载到自己电脑,然后把它上传到这个 bucket里,测试一下。直接访问链接不能下载,需要右键菜单点“Download”才能下载,说明权限配置已经成功。

2.3 创建Lambda函数

AWS Lambda 管理控制台:

https://us-west-2.console.aws.amazon.com/lambda/home?region=us-west-2#/

点击Create a Lambda function 按钮

Select runtime 菜单选Node.js 4.3,然后点Blank Function。

Configure triggers页,点Next。

Configure function 页,在 Name 栏输入ImageMagick,Description 栏输入

Uses ImageMagick to perform simple image processing operations, such as resizing.

 

Lambda function code 里填写以下代码:

'use strict';

var AWS = require('aws-sdk');
const im = require('imagemagick');
const fs = require('fs');

const defaultFilePath = 'awslogo_w300.png';
// 样式名配置,把宽高尺寸组合定义成样式名。
const config = {
'w500':{'width':500,
'height':300},
'w300':{'width':300,
'height':150},
'w50':{'width':50,
'height':40}
};
// 默认样式名
const defaultStyle = 'w50';
// 完成处理后把临时文件删除的方法。
const postProcessResource = (resource, fn) => {
let ret = null;
if (resource) {
if (fn) {
ret = fn(resource);
}
try {

fs.unlinkSync(resource);
} catch (err) {
// Ignore
}
}
return ret;
};
// 生成缩略图的主方法
const resize = (filePathResize, style, data, callback) => {
// Lambda 本地写文件,必须是 /tmp/ 下
var filePathResize =
'/tmp/'+filePathResize;
// 直接用 Buffer 操作图片转换,源文件不写到本地磁盘,但是转换成的文件要写盘,所以最后再用 postProcessResource 把临时文件删了。
var resizeReq = {
srcData: data.Body,
dstPath: filePathResize,
width: style.width,
height: style.height
};
try {
im.resize(resizeReq,
(err) => {
if (err) {
throw err;
} else {

console.log('Resize ok: '+ filePathResize);
// 注意这里不使用JSON结构,直接输出图片内容数据,并且要进行base64转码。
callback(null,
postProcessResource(filePathResize, (file) => new
Buffer(fs.readFileSync(file)).toString('base64')));
}
});
} catch (err) {
console.log('Resize
operation failed:', err);
callback(err);
}
};

exports.handler = (event, context, callback) => {
var s3 = new AWS.S3();
//改成刚刚创建的 bucket 名字,如 img201703
var bucketName = 'image201702';
// 从文件 URI 中截取出 S3 上的 key 和尺寸信息。
// 稳妥起见,尺寸信息应该规定成样式名字,而不是直接把宽高参数化,因为后者会被人滥用。
// 使用样式还有个好处,样式名字如果写错,可以有个默认的样式。
var filepath = (undefined ===
event.filepath ? defaultFilePath:event.filepath);
var tmp =
filepath.split('.');
var fileExt = tmp[1];
tmp = tmp[0].split('_');
var fileName = tmp[0];
var style = tmp.pop();
console.log(style);
var validStyle = false;
for (var i in config)
{
if (style == i)
{
validStyle = true;
break;
}
}
style = validStyle ? style :
defaultStyle;
console.log(style);
var fileKey =
fileName+'.'+fileExt;
var params = {Bucket:
bucketName, Key: fileKey};

// 从 S3 下载文件,成功后再回调缩图
s3.getObject(params,
function(err, data) {
if (err)
{
console.log(err,
err.stack);
}
else
{
resize(filepath,
config[style], data, callback);
}
});
};

注意一定要把

var bucketName = ‘image201702’;

改成刚刚创建的 bucket 名字,如

var bucketName = ‘img201703’;

这个 Lambda 函数就可以运行了。

Lambda function handler and role 部分的Role 选择 Choose an existing role,然后Existing role 选择之前创建的LambdaReadS3。

Advanced settings:Memory (MB)* 选 512,Timeout 选30 sec。

其它保持默认,点 Next;最后一页确认一下,点Create Function。

提示创建成功。

点击 Test 按钮,测试一下。第一次测试时,会弹出测试使用的参数值,这些参数其实我们都不用,也不用管它,点击 Save and test 按钮测试即可。以后再测试就不会弹出了。

显示“Execution result: succeeded”表示测试成功了,右边的 Logs 链接可以点击,前往 CloudWatch Logs,查看详细日志。右下方的Log output是当前测试执行的输出。

可以看到这里 Execution result 下面显示的结果是一个长字符串,已经不是我们以往普通Lambda函数返回的JSON结构了。想做进一步验证的,可以把这个长字符串 base64 解码,会看到一个尺寸变小的图片,那样可以进一步验证我们运行成功。

2.4 配置API Gateway

管理控制台

https://us-west-2.console.aws.amazon.com/apigateway/home?region=us-west-2#

2.4.1 配置API

点击 Create API

API name 填写ImageMagick。
Description 填写Endpoint for Lambda using ImageMagick to perform simple image processing operations, such as resizing.

这时左侧导航链接会显示成 APIs > ImageMagick > Resources。点击 Actions 下拉菜单,选择Create Resource。

Resource Name* 填写filepath

Resource Path* 填写 {filepath},注意要包括大括号。然后点击Create Resource按钮。

这时刚刚创建的{filepath}应该是选中状态,再点击Actions 下拉菜单,选择Create Method,在当时出现的方法菜单里选择GET,然后点后面的对号符确定。

然后在/{filepath} – GET – Setup 页,Integration type 保持Lambda Function 不变,Lambda Region 选 us-west-2,在Lambda Function 格输入ImageMagick,下拉的备选菜单中点中ImageMagick,点击 Save。弹出赋权限提示,点击“OK”。

这时会显示出完整的“/{filepath} – GET – Method Execution”配置页。

点击右上角“Integration Request”链接,进入配置页,点击“Body Mapping Templates”左边的三角形展开之。

Request body passthrough 选择When there are no templates defined (recommended)。

点击最下面“add mapping template”链接,“Content-Type”格,注意即使这里已经有提示文字application/json,还是要自己输入application/json,然后点击右边的对勾链接,下面会弹出模板编辑输入框,输入

{“filepath”: “$input.params(‘filepath’)”}

完成的效果如下图所示:

最后点击“Save”按钮。点击左上角 “Method Execution”链接返回。

点击左下角“Method Response”链接,HTTP Status 下点击第一行200左边的三角形展开之,“Response Headers for 200” 下点击add header链接,Name格输入Content-Type,点击右边的对勾链接保存。Response Body for 200 下已有一行application/json,点击其右边的笔图标编辑,把值改成image/png,点击右边的对勾链接保存。点击左上角 “Method Execution”链接返回。

点击右下角“Integration Response”链接,点击第一行 “- 200 Yes”左边的三角形展开之,“Content handling”选择 Convert to binary(if needed),然后点击“Save”按钮。这项配置是把Lambda返回的base64编码的图片数据转换成二进制的图片数据,是此架构的另一个技术重点。

Header Mappings 下已有一行Content-Type,点击其右边的笔图标编辑,在“Mapping value”格输入’image/png’,注意要带上单引号,点击右边的对勾链接保存。

点击“Body Mapping Templates”左边三角形展开之,点击“application/json”右边的减号符,把它删除掉。点击左上角 “Method Execution”链接返回。

点击最左边的竖条Test链接,来到“/{filepath} – GET – Method Test”页,“{filepath}”格输入awslogo_w300.png,点击 Test 按钮。右侧显示类似下面的结果

Request: /awslogo_w300.png

Status: 200

Latency: 247 ms

Response Body是乱码是正常的,因为我们的返回内容就是图片文件本身。可以查看右下角Logs 部分显示的详细执行情况,显示类似以下的日志表示执行成功。

Thu Mar 09 03:40:11 UTC 2017 : Method response body
after transformations: [Binary Data]

Thu Mar 09 03:40:11 UTC 2017 : Method response
headers: {X-Amzn-Trace-Id=Root=1-12345678-1234567890abcdefghijlkln,
Content-Type=image/png}

Thu Mar 09 03:40:11 UTC 2017 : Successfully completed
execution

Thu Mar 09 03:40:11 UTC 2017 : Method completed with
status: 200

2.4.2 部署API

点击Actions按钮,在下拉菜单中点选Deploy API,Deployment stage 选择[New Stage],Stage name 输入 test,注意这里都是小写。

Stage description 输入 test stage

Deployment description 输入 initial deploy to test.

点击Deploy按钮。然后会跳转到Test Stage Editor 页。

复制Invoke URL: 后面的链接,比如

https://1234567890.execute-api.us-west-2.amazonaws.com/test

然后在后面接上awslogo_w300.png,组成形如以下的链接

https://1234567890.execute-api.us-west-2.amazonaws.com/test/awslogo_w300.png

输入浏览器地址栏里访问,可以得到一张图片,表示 API Gateway已经配置成功。

2.5 配置CloudFront分发

我们在API Gateway前再加上CloudFront,通过 CDN 缓存生成好的图片,就可以实现不需要把缩略图额外存储,而又不用每次都为了图片处理进行计算。这里使用了CDN和其它使用CDN的思路一样,如果更新图片,不建议调用清除CloudFront的API,而是从应用程序生成新的图片标识字符串,从而生成新的URL让CloudFront成为无缓存状态从而回源重新计算。

由于API Gateway仅支持HTTPS访问,而CloudFront同时支持HTTP和HTTPS,所以我们可以配置成CloudFront前端同时支持HTTP和HTTPS,但是实测发现CloudFront前端使用HTTP而回源使用HTTPS时性能不如前端和回源同为HTTPS。所以这里我们也采用同时HTTPS的方式。

我们打开CloudFront的管理控制台

https://console.aws.amazon.com/cloudfront/home?region=us-west-2#

点击Create Distribution按钮,在 Web 下点击Get Started。

Origin Domain Name,输入上述部署出来的 API Gateway 的域名,比如 1234567890.execute-api.us-west-2.amazonaws.com

Origin Path,输入上述 API Gateway 的 Stage 名,如 /test

Origin Protocol Policy 选择HTTPS Only

Object Caching 点选Customize,然后Maximum TTL输入86400

Alternate Domain Names (CNAMEs) 栏本例使用自己的域名,比如img.myexample.com。SSL Certificate选择Custom SSL Certificate (example.com),并从下面的证书菜单中选择一个已经通过 ACM 管理的证书。

注意,如果填写了自己的域名,那么下面的SSL Certificate就不建议使用默认的Default CloudFront Certificate (*.cloudfront.net),因为很多浏览器和客户端会发现证书的域名和图片 CDN 的域名不一致会报警告。

其它项保持默认,点击Create Distribution按钮,然后回到CloudFront Distributions列表,这里刚刚创建的记录Status会显示为 In Progress,我们点击ID的链接,前进到详情页,可以看到Domain Name 显示一个CloudFront分发的URL,比如cloudfronttest.cloudfront.net。大约 10 多分钟后,等待Distribution Status 变成Deployed,我们可以用上述域名来测试一下。注意测试用的URL不要包含API Gateway 的 Stage 名,比如

https://1234567890.execute-api.us-west-2.amazonaws.com/test/awslogo_w300.png

那么 CloudFront 的URL 应该是

https://cloudfronttest.cloudfront.net/awslogo_w300.png

尽管我们已经配置了自己的域名,但是这时自已的域名还未生效。我们还需要到Route 53 去添加域名解析。

2.6  Route 53

最后我们使用Route 53 实现自定义域名关联CloudFront分发。访问 Route 53 管制台

https://console.aws.amazon.com/route53/home?region=us-west-2

在Hosted Zone 下点击一个域名,在域名列表页,点击上方Create Record Set 按钮,页面右侧会弹出创建记录集的面板。

Name 栏输入 img。

Type 保持默认 A – IP4 Address不变。

Alias 点选 Yes,在Alias Target输入前述创建的CloudFront分发的URL cloudfronttest.cloudfront.net。

点击右下角Create按钮完成创建。

3. 效果验证     

现在我们回到CloudFront控制台,等到我们的Distribution 的Status变成Deployed,先用CloudFront自身的域名访问一下。

https://cloudfronttest.cloudfront.net/awslogo_w300.png

顺利的话,会看到咱们的范例图片。再以自定义域名访问一下。

http://img.myexample.com/awslogo_w300.png

还是输出这张图片,那么到此就全部部署成功了。现在可以在S3的bucket里上传更多其它图片,比如 abc.png,然后访问时使用的URL就是

http://img.myexample.com/abc_w300.png

用浏览器打开调试工具,可以看到响应头里已经有

X-Cache: Hit from cloudfront

表示已经经过CloudFront缓存。

4. 监控

这个架构方案使用的服务都可以通过CloudTrail记录管理行为,使用CloudWatch记录用户访问情况。

4.1 Lambda 监控

在Lambda控制台点击我们的ImageMagick 函数,然后点击选项卡最末一个Monitoring,可以看到常用指标的简易图表。

点击任何一个图表,都可以前进到CloudWatch相关指标的指标详细页。然后我们还可以为各个指标配置相关的CloudWatch Alarm,用以监控和报警。

点击View logs in CloudWatch链接,可以前往CloudWatch Log,这里记录了这个Lambda函数每次执行的详细信息,包括我们的函数中自已输出的调试信息,方便我们排查问题。

4.2 API Gateway 监控

在API Gateway控制台找到我们的API ImageMagick,点击它下面的 Dashboard。

如果部署了多个Stage,注意左上角 Stage 菜单要选择相应的Stage。同样下面展示的是常用图表,点击每个图表也可以前往CloudWatch显示指标监控详情。

4.3 CloudFront日志

我们刚刚配置CloudFront时没有启用日志。如果需要日志,可以来到CloudFront控制台,点击我们刚刚创建的分发,在General选项页点击Edit按钮。在Edit Distribution 页找到Logging项,选择On,然后再填写Bucket for Logs和Log Prefix,这样CloudFront的访问日志就会以文件形式存储在相应的S3的bucket 里了。

5. 小结

我们这样一个例子使用了Lambda和API Gateway的一些高级功能,并串联了一系列AWS全托管的服务,演示了一个无服务器架构的典型场景。虽然实现的功能比较简单,但是 Lambda函数可以继续扩展,提供更丰富功能,比如截图、增加水印、定制文本等,几乎满足任何的业务需求。相比传统的的计算能力部署,不论是用EC2还是ECS容器,都要自己管理扩容,而使用 Lambda无需管理扩容,只管运行代码。能够让我们从繁琐的重复工作中解脱,而把业务集中到业务开发上,这正是无服务器架构的真正理念和优势。

作者介绍:

薛峰

AWS解决方案架构师,AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内和全球的应用和推广,在大规模并发应用架构、移动应用以及无服务器架构等方面有丰富的实践经验。在加入AWS之前曾长期从事互联网应用开发,先后在新浪、唯品会等公司担任架构师、技术总监等职位。对跨平台多终端的互联网应用架构和方案有深入的研究。

使用AWS Lambda和Amazon DynamoDB自动管理AWS CloudFormation模板中的Parameters和Mappings

背景介绍

相信AWS的用户对AWS CloudFormation都不会陌生。AWS CloudFormation是实现IAC(Infrastructure as Code)自动化运维的一项重要服务,可以帮助用户对 AWS资源进行建模和设置,以便能花较少的时间管理这些资源。CloudFormation中有两个重要选项:Mappings段和Parameters段,可以帮助用户组织模板里的参数和映射,让用户更好地自定义堆栈,以实现模板的重用和复用。比方说可以用Mappings管理对应AWS上不同region的AMI ID,或者管理企业内部的不同部门。

但是当用户所在的组织越来越多地采用IAC自动化时,mappings和parameters的数量也会急剧增长,给CloudFormation模板的编写和维护带来复杂度。

解决方案

本文里我们介绍一种方法:用当前流行的Serverless计算AWS Lambda 和Amazon DynamoDB自动地管理AWS CloudFormation模板中的Parameters和Mappings。

本文中主要用到了以下几种 AWS服务:

1、DynamoDB表:Amazon DynamoDB是一个NoSQL数据库,这里我们采用它保存CloudFormation模板中所有的mappings和parameters。不仅可以实现集中存放,而且可以依赖DynamoDB的接口实现方便快速地增删和查找。比方说在我们的sample code中,整个企业采用这样一张表:partition key包括组名(比如说team1、team2等)和环境(比如说development、test、production等),sort key保存应用的名字。这个表里的数据类似这样:

当我们把这些数据都insert到DynamoDB中后,可以在AWS console里看到表中的内容是这样的:

2、Lambda方法:AWS Lambda又称为Serverless的计算,通过它你可以运行你的code而不需要预配置或者管理任何服务器。这里我们采用Lambda方法实现CloudFormation和DynamoDB之间的关联,它从CloudFormation模板接收primary key和sort key作为输入,查找DynamoDB表,并且返回所有的key-value数据。

3、Custom lookup resource:这是CloudFormation里的一个自定义资源,与一个Lambda方法绑定。CloudFormation除了可以定义已有的AWS资源,还支持几种自定义资源,包括这种以Lambda方法作为后端的自定义资源。当这种自定义资源创建、更新或者删除时,CloudFormation自动地向Lambda API发起请求,引发方法并将请求的数据传给Lambda方法,本例中所请求的数据是primary key,返回的数据是key-value数据。通常在一个组织中只需要建立这一个custom resource,所有的CloudFormation模板都可以复用它。下图是sample code里建立的custom resource:

让我们将这几种服务组合起来,并且定义好它们之间的交互顺序,整个解决方案就是下图展示的这样:

那么整个的交互顺序如下:

1、用户创建DynamoDB表,插入所需的mappings和parameters数据。

2、CloudFormation模板中的custom resource调用Lambda方法,用组名和环境名称(“teamname-environment”)作为partition key,用应用名称(”appname”)作为sort key。

3、Lambda方法用partition key和sort key作为输入查询DynamoDB表。

4、DyanamoDB将结果返回给Lambda方法。

5、Lambda方法将这些key-value数据作为结果返回给模板中的custom resource。

6、custom resource拿到数据后,堆栈里的其他资源可以通过Fn::GetAtt的方式获得这些数据。

结论

通过这种方式,可以将我们创建堆栈所需的固定部分保存在模板中,而将可变部分mappings和parameters保存在方便增删改查的DynamoDB中,用Lambda实现两者之间的关联。对于大型组织来说,这样可以提高运维效率,也是使用CloudFormation的一种最佳实践。

参考

可以在我们的网站上下载到相关的sample code:https://github.com/awslabs/custom-lookup-lambda

关于AWS Lambda的更多内容请参考网站:https://aws.amazon.com/lambda/

关于CloudFormation自定义资源的更多内容请参考网站:http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html

作者介绍

张芸

AWS云架构咨询顾问,获得AWS解决方案架构师专业级认证和DevOps工程师专业级认证,为企业客户和合作伙伴提供迈向云端的专业服务。目前已为多家跨国公司及本土公司、合作伙伴提供上云迁移、应用优化、架构设计等咨询设计和实施服务。在加入AWS之前有近10年的云计算和大数据开源技术研究和开发经验,拥有多项美国和中国专利及专利申请,涉及云计算及服务,分布式系统,软件定义数据中心和自动化运维等领域,合作编著图书《大数据–战略·技术·实践》。

 

使用AWS Application Load Balancer实现基于主机名的路由分发

负载均衡器在应用架构设计中是重要的组件,负责接收来自客户端的流量,将流量按一定的算法转发给后端的一组实例,并将后端实例的响应再返回给客户端。AWS提供一款托管的负载均衡服务, Elastic Load Balancer(简称ELB),ELB除了能够做负载均衡分发流量之外,还能对后端的实例健康检查,并将流量仅转发给通过健康检查的实例,同时ELB还能与自动扩展组(Auto Scaling Group)以及监控服务(CloudWatch)配合,设置根据后端实例CPU使用率的高低,流量大小,处理时间长短等指标,自动完成添加或缩减实例数量。

ELB又分两种,Classic ELB和Application Load Balancer(简称ALB),ELB的一个重要组件是侦听器,前者支持4层和7层的协议和端口(TCP,SSL,HTTP,HTTPS),对后端实例按轮询(TCP协议)或者最少未完成请求数(HTTP协议)的算法对后端实例进行流量转发。

ALB是应用层负载均衡器,支持HTTP/HTTPS的协议,与Classic ELB不同的是,ALB支持基于请求路径的分发,即根据HTTP标头的请求URL路径的不同,分发给后端不同的目标组(Target Group)。目标组是一个或多个目标(这里的“目标”可以是EC2实例)的集合,通常一组目标运行相同的应用或服务,一个目标可以注册到一个或多个目标组中。在目标组中可以配置运行状况检查,实例监控,等待连接耗尽及粘性会话等等。ALB中的规则决定了如何将流量路由到后端不同的目标组,每条规则对应一个目标组、条件及优先级,一旦匹配规则,则执行相应的流量路由,比如将请求URL中路径是/api的请求路由给运行api服务的目标组,将请求URL中路径是/mobile的访问路由给mobile的目标组。

两者的转发模型可见下图。

过去ALB仅支持基于请求路径的流量分发,客户为了实现基于主机名的分发往往使用多组Classic ELB或Classic ELB + Nginx集群的方式,现在ALB提供了新功能,即可以基于主机名进行路由分发,详情请参考:

https://aws.amazon.com/cn/elasticloadbalancing/applicationloadbalancer/

接下来,我们将详细介绍如何使用ALB完成基于主机名的流量分发。

本例中我们将创建以下资源:

(1)1个ALB;

(2)5个目标组,分别为api-prod,api-sandbox,mobile-prod,mobile-sandbox,default;

(3)每个目标组注册不同的EC2实例,其中api-prod目标组将注册两个EC2实例;

(4)一个侦听器;

(5)创建基于主机名和路径的分发规则,实现流量的分发。

1、创建目标组

2、输入目标组名称,选择协议(HTTP/HTTPS)及端口,ALB所在的VPC,配置运行状况检查(协议,路径,阈值等)。此处我们选择了默认HTTP,路径/。

3、注册实例

目标组的重要实体是目标(比如说EC2实例),在此将实例注册到目标组下面,注意选中实例后点击”添加到已注册”,然后保存。此处我们选中了该目标组对应的实例ALBDemo_api_prod,ALBDemo_api_prod_2两个实例。您可以向一个或多个目标组注册多个目标实例以便满足需求。只要注册过程完成且新注册的目标实例通过初始运行状况检查,负载均衡器就会开始将请求路由至此目标。同样,您也可以从目标组取消目标注册。

同理创建api-sandbox,mobile-prod,mobile-sandbox,default目标组,此处省略创建过程。

创建完成后,选中目标组,在目标页我们可以看见注册到该目标组的实例的状态,healthy表示实例正常,另外该状态还有unhealthy(实例未通过健康检查),initial(实例注册中),unused(无流量传入,该目标组未注册到ALB)等。

4、创建负载均衡器

下面我们来创建ALB

5、选择ELB类型

这里我们选择应用程序负载均衡器ALB

6、配置负载均衡器

输入ALB名称,模式可选择该ALB面向Internet或是内部使用;配置侦听器协议,端口,根据需要可以配置多个侦听器,此处我们选择HTTP/80;同时选择ALB部署在哪个可用区及子网,建议多可用区方式部署,同样将应用部署在多可用区,达到高可用的设计目标。

7、配置安全设置

如果侦听器配置了HTTPS,需要配置证书。使用HTTPS,ELB可以完成与客户端的SSL安全连接的建立与SSL卸载,减轻后端服务器的压力。

如果从ACM(AWS Certificate Manager)申请过证书,则可以直接选择该证书,也可以自己上传证书到IAM或者在此上传证书。由于此次我们仅配置了HTTP 80端口侦听器,此处将不配置证书。配置安全证书的截图如下:

8、配置安全组

安全组的概念及用法此处不做复述,我们可以为ALB配置安全组,仅允许指定的协议、端口及来源的流量进入ALB

9、配置路由

这里可以选择”现有目标组”,选择我们前面的创建的default目标组。此处只能选择一个目标组,我们可以在创建完成后,对此ALB添加编辑规则的时候对应路由规则添加目标组。

10、注册目标

11、审核

创建完成后,可以在负载均衡器的控制台看到该ALB的相关描述与信息。

12、配置规则

选中该ALB,在页面下方的侦听器下按端口选择“查看/编辑规则”。

13、添加/编辑规则

与之前不同的是,现在ALB提供了一个规则编辑器。进入规则编辑器后,我们看到的是一条默认规则,并看到目标组是我们前面创建过程中选择的default组。点击左上方”+”,可以添加规则。

目前路由规则支持三种方式:

a.基于URL路径;

b.基于主机名(New Feature);

c.基于主机名+路径(New Feature);

点击&,可以实现基于主机名+路径的规则;

添加了4条规则,将不同主机及路径的流量分发到不同的目标组,如下:

规则 规则(if) 目标组(then)
1 路径/sandbox/* &主机名为api.shishuai.tech api-sandbox
2 主机名为api.shishuai.tech api-prod
3 路径/sandbox/* &主机名为mobile.shishuai.tech mobile-sandbox
4 主机名为mobile.shishuai.tech mobile-prod

通过左上角的添加、编辑、排序、删除可以对规则进行相应的修改与排序,需要注意的是,流量路由匹配规则的时候按规则的顺序匹配,并按最先匹配到的规则执行相应的路由。最多可以添加75条规则。每条规则在主机名处支持最多3个通配符(”*”或者”?”)。

最后规则配置完成,我们检查下规则是否生效。使用Route53 DNS解析服务,将相应的域名(本例中为api.shishuai.tech和mobile.shishuai.tech)CNAME到该ALB的域名(本例中为ALBDemo-2043343612.us-west-2.elb.amazonaws.com,可在选中该ALB,在描述页中看到该DNS地址)。

本例中每个EC2有一个简单的页面,表明自己主机名,以及instance-id,以此区分是否按规则路由到相应的EC2。

http://api.shishuai.tech

在api-prod目标组,我们注册了两台机器,刷新几次会出现另一台EC2的页面。

http://api.shishuai.tech/sandbox/

http://mobile.shishuai.tech

http://mobile.shishuai.tech/sandbox/

综上所述,使用应用负载均衡器基于主机名/路径的流量分发特性,客户可以仅用一组应用负载均衡器就可实现将流量路由给多个后端服务,这可以大大简化客户的架构、减轻运维负担以及优化成本。

作者介绍

王世帅

AWS解决方案架构师,负责基于AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内教育、医疗行业的应用和推广。在加入AWS之前曾在国航担任系统工程师,负责存储方案的架构设计,在企业私有云方面有丰富经验。

 

在Virtual Private Cloud中自建基于BIND的DNS服务器

Amazon Virtual Private Cloud (Amazon VPC) 是 AWS 提供的虚拟私有网络服务,允许您在 AWS 云中预配置出一个采用逻辑隔离的部分,让您在自己定义的虚拟网络中启动 AWS 资源。您可以完全掌控您的虚拟联网环境,包括选择自有的 IP 地址范围、创建子网,以及配置路由表和网关。

除了提供IP资源及网络连接,Amazon VPC还提供DNS及DHCP等基础设施服务。当您将实例启动到默认 VPC 中时,我们为实例提供与其公有 IPv4 和私有 IPv4 地址对应的公有和私有 DNS 主机名。当您在非默认 VPC 中启动实例时,我们会为实例提供私有 DNS 主机名,并根据您为 VPC 和实例指定的设置来决定是否提供公有 DNS 主机名。

对于 us-east-1 区域,公有 (外部) DNS 主机名采用 ec2-<public-ipv4-address>.compute-1.amazonaws.com 形式,对于其他区域,则采用 ec2-<public-ipv4-address>.region.amazonaws.com 形式。例如,公有IP为54.222.212.110的EC2实例,其公有DNS名为ec2-54-222-212-110.cn-north-1.compute.amazonaws.com.cn。我们将公有 DNS 主机名解析为该实例在所在网络外的公有 IPv4 地址及其在所在网络内的私有 IPv4 地址。

私有 (内部) DNS 主机名解析为实例的私有 IPv4 地址,并对 us-east-1 区域采用 ip-<private-ipv4-address>.ec2.internal 形式,对其他区域采用 ip-<private-ipv4-address>.region.compute.internal 形式 (其中 private.ipv4.address 是反向查找 IP 地址)。例如,私有IP地址为10.206.2.239的EC2实例,其私有DNS名为ip-10-206-2-239.cn-north-1.compute.internal。您可以使用私有 DNS 主机名在同一网络中实现实例之间的通信,但我们无法解析实例所在网络之外的 DNS 主机名。要解析实例所在网络之外的主机名,可自建DNS服务器来为VPC及外部网络提供DNS服务。

常见的应用场景是在混合IT架构下,客户数据中心通过VPN或是Direct Connect专线连接到AWS上的VPC,在VPC中配置1台DNS服务器,在客户数据中心也配置1台DNS服务器,服务器的主从角色客户可自行定义。通过多台DNS服务器为不同位置的客户端提供DNS服务,即能保证服务的高可用,又能就近提供服务,减少DNS查询延迟。

接下来,我将基于上述架构图一步一步说明如何使用BIND搭建DNS服务器。本文不会涉及BIND的高级配置,如需了解BIND的高级配置,可参考BIND官方网站

安装配置DNS主服务器

首选需要准备一台EC2实例用于安装BIND软件,如何创建EC2实例可参考Amazon EC2入门指南。本次示例选用Amazon Linux操作系统的AMI来创建实例,实例类型选用了通用型实例类型: m4.large。BIND对服务器硬件资源要求不高,在不启用DNSSEC的情况下(不在本文讨论范围),普通配置的服务器即可承载DNS服务。m4.large配置有2颗vCPU和8G内存,运行DNS服务能够支持中等规模的DNS请求,当请求增加时,也可方便的调整实例类型到更大的配置。

创建EC2实例时需要指定安全组来开放服务端口,DNS服务通过UDP 53端口提供DNS查询相应,通过TCP 53端口提供区域传送。因此,安全组队VPC网段开放UDP 53端口,对客户数据中心的DNS服务器开放TCP 53端口,如下图所示:

DNS服务器作为基础设施服务的重要性无须多言,Amazon EC2持续监控EC2实例的状态以及底层硬件的状态,分别称为实例状态检查和系统状态检查。我们可创建状态检查报告,当任一状态检查失败时,执行重启操作,并将警报发送至指定邮箱。

创建好警报之后,通过SSH登陆至EC2实例,并yum命令安装bind软件:

yum install bind-utils bind

安装好之后,我们接下来将创建一个示例的DNS域:aws.local,我们首先需要编辑/etc/named.conf文件,修改以下内容:

listen-on 缺省配置为127.0.0.1,DNS服务只会绑定到系统的环回接口,其他客户端无法访问,需要添加EC2实例的私有IP地址,才能提供外部访问

allow-query缺省配置为localhost,即只允许DNS服务器所在的EC2实例对自己进行DNS查询,添加VPC的网段可允许来自VPC内部的主机进行DNS查询。注:也可将此参数设置为0.0.0.0/0,因为前面安全组设置里只允许了VPC内的IP访问UDP 53端口。

此外,还需要增加对aws.local这个域的定义,在/etc/named.conf中增加以下内容,allow-transfer指定了只允许从DNS服务器进行区域传送,限定允许区域传送的范围颗可提高DNS服务的安全性:

上述配置说明aws.local域的具体解析配置在文件/var/named/aws.local.db 里,其内容如下,在这个示例配置中,定义了两条A记录,分别是dns.aws.local对应10.206.0.212和www.aws.local对应10.206.0.213

配置Amazon VPC使用自建DNS服务器

Amazon VPC通过DHCP服务为VPC中的EC2及其他连网组件动态分配IP地址,动态主机配置协议 (DHCP) 提供了将配置信息传递到 TCP/IP 网络中主机的标准。DHCP 消息中的options字段包含配置参数。这些参数包括域名、域名服务器以及“netbios-node-type”。接下来我们将创建一个新的DHCP选项集,并在选项集中将域名服务器指向刚才创建的DNS服务器。

在AWS控制台中选择VPC服务,并在左边的菜单中选择“DHCP选项集”,点击“创建DHCP选项集”按钮,输入以下信息,域名服务器可指定多个DNS服务器,按照顺序第一个为VPC内的DNS服务器,第二个为客户数据中心的DNS服务器:

创建好DHCP选项集之后选择左边菜单中“您的VPC”选项,选中要修改的VPC,从“操作”下拉菜单中选择“编辑DHCP选项集”

选择刚才创建的DHCP选项集并保存:

在您将新的 DHCP 选项集与 VPC 关联之后,任何现有实例以及您在 VPC 内启动的所有新增实例都将使用这些选项。 无需重新开始或重新启动实例。根据实例更新 DHCP 租赁权的频率,它们会在几个小时内自动拾取更改。如果您愿意,您也可以使用实例上的操作系统,直接更新租赁权。

安装配置DNS从服务器

在客户数据中心安装配置DNS服务器的步骤与在Aamazon EC2中安装配置DNS服务器的步骤相同,除了/etc/named.conf的配置稍有差别:

从服务器的type类型为slave,file参数对应的域配置文件会自动根据从主DNS服务器接收到的更新来进行创建和更新,masters指定aws.local域的主域名服务器,最后一个参数allow-transfer禁用了区域传送。

配置好从DNS服务器之后,可将客户数据中心内的连网设备设置为从DNS服务器,第二DNS服务器设置为AWS上的主DNS服务器。

总结

Amazon VPC提供DHCP服务和DNS服务,为VPC中的EC2实例提供IP地址分配和域名解析服务,为每个EC2实例创建特定格式的DNS域名。如果用户希望使用自定义域名,或者希望使用一套域名统一管理云上和云下的资源,可自行搭建DNS服务器来提供DNS解析服务,Amazon VPC能够支持客户。

作者简介

刘旭东,AWS解决方案架构师 。负责基于AWS的云计算方案的咨询和架构设计,具有超过十年以上企业客户服务经验,同时致力于AWS云服务在国内和全球的应用和推广。在大数据解决方案、企业级解决方案,混合云架构,基础设施运维管理,以及Serverless无服务器架构及IoT等领域有着广泛的设计与实践经验。在加入AWS之前曾任职HPE技术顾问,有超过十年的虚拟化/云计算架构设计经验, 始终推动技术实现商业价值。

从永恒之蓝开始,安全防范没有结束

信息安全现状

时钟进入到2017年,物联网安全DDoS攻击勒索软件被越来越多的国内外信息安全预测机构列为年度最大信息安全威胁。上周爆发的勒索软件WannaCry (又称作永恒之蓝)已席卷全球99个国家,让各类技术宅男们和信息安全的童鞋们又错过了一个美好的周末。尽管目前该病毒已被安全人员找到了阻止其传播的方法,但是类似的安全事件依然给我们敲响了云端安全运维的警钟。

WannaCry事件回顾,5月12日晚, WannaCry 蠕虫病毒在全球大肆爆发。据外媒报道,攻击在99个国家实施了超过75000次攻击。该攻击利用漏洞MS17-010中的某些版本的SMB服务器协议进行传播,利用Windows 445 端口的安全漏洞潜入电脑并对多种文件类型加密并使用户无法打开,攻击者称需支付比特币解锁,补丁程序已于2017年3月14日发布,但未打补丁的用户有可能遭受此次攻击。通过这个事件我们发现只要是购买正版操作系统并及时更新补丁的用户,都不会受到影响。永久解决办法有2个,1.开启windows的自动更新功能,将最新补丁打上; 2.对于部分比较老的windows系统可以手动下载更新包并安装。

这样的安全事件不是第一次也不会是最后一次,要确保我们的信息安全,需要做的事情有太多,比如完善的信息安全应对机制,一系列自动化运维工具或服务,坚决的执行能力以及定期的审核机制。如果你恰好开始关心信息安全,而我们又专业,不妨看看AWS是如何看待信息安全的。

AWS信息安全理念

数据属于谁?

虽然是一个老生常谈的问题,但确实一个非常重要的问题,而且答案永远只有一个,数据属于客户。当你上云之后,如何确保你的数据真的属于你是很多客户评估是否上云时最纠结的问题!在AWS,你可以非常清晰的从上图中看到这个界限。所有操作系统以上(包括操作系统本身)的内容都是客户自己自主可控,AWS没有任何权限访问,同时如果你使用的是AWS提供的Windows或者Linux镜像,该镜像 的补丁机制是全球一致管理的,我们会从各个合作厂商那得到补丁,并第一时间更新到镜像里。如果是开篇提到的流氓攻击,在AWS上的客户可以不需要担心了,因为该补丁在镜像中自动更新了。

如何确保我在云上是安全的?

如何数据安全可能是一个说上三天三夜都没法说完的话题,今天我们从AWS信息安全最佳实践出发,给大家从几个层面来分析信息安全的层级,并重点介绍如何帮助客户在理解理念之后的安全落地问题。首先我们可以看到在云上我们需要这么几个维度的安全,底层基础架构的安全网络的安全权限及审计的安全服务本身的安全事先预防等等。总之,信息安全从来都不是靠说的,而是靠做的,所以接下来我们给大家做一些简单示范,让你在使用AWS时更加得心应手。

底层基础架构的安全

熟悉AWS的同学可能知道,AWS底层的虚拟化平台使用的是Xen,套用一句官话,不管我们的工程师是多么的小心,我们依然需要时刻告知我们的客户对于AWS服务底层安全的疑虑,所以大家可以在以下链接找到关于Xen的信息安全公告,同时大家也可以从AWS信息安全白皮书中看到AWS如何对于底层基础架构的安全设计,限于篇幅的原因本文不作展开。

Xen Security Advisiories

https://aws.amazon.com/cn/security/security-bulletins/

AWS Security Best Practices

https://d0.awsstatic.com/whitepapers/aws-security-whitepaper.pdf

网络安全

  • 上云的安全

当你的数据需要上云时,你有三种非常安全的方式来传输您的数据,通过TLS加密的HTTPS协议将数据直接通过互联网传输到S3, 建立专用VPN进行数据传输以及通过专线将AWS与您的数据中心打通,并将AWS纳入您现有的全球专线网络中,让你全球任何一个办公室或者数据中心都通过内网的方式随时的安全访问云端的数据或者系统,如同您多了一个拥有无限扩展能力的远端数据中心。

  • 云上的安全

当您的数据或者系统已经部署在AWS上时,你可以通过上图的简单示意图来对您的系统进行网络隔离及控制。比如,你可以按照不同的应用划分不同的VPC,不同的VPC之间网络隔离,同时在同一个VPC内设置不同安全级别网络区域,如公有访问区,DMZ区,私有网络区。利用VPC内的免费防火墙工具安全组,网络ACL来对云端的机器及网段进行精细化控制。如图上的例子,你可以通过图形界面在安全组上关闭该机器137,139,445 端口就能轻松的规避本文开篇提到的流氓攻击。如果您确实需要开启这些端口,也可以在安全组内指定对具体的源IP开放端口,或者将这些机器放置在私有网段,当这些私有网段的机器需要访问互联网的某些站点进行补丁升级等操作时,通过NAT的方式允许VPC的出向流量。

如果您还需要对VPC内不同私有网段的机器访问进行更精细化控制,还可以通过另外一个免费的防火墙网络ACL来实现。

当您的网络与AWS云端VPC通过VPN或者专线联通时,你还可以通过路由表来控制您哪些数据中心的网段可以访问云端的哪些网段。

权限及审计安全
当你的业务上云后,你可能面临不同人员需要管理你云端资源,这些人可能包括,您公司的开发人员,运维人员,财务部,人事部以及您外部形形色色的服务供应商或者运维外包供应商,如何给予这些人员适当权限去访问他们需要访问的AWS资源以及事后审核这些人员的访问情况,是很多企业客户迫切需要解决问题。

AWS IAM不但可以轻松解决上述所有访问权限问题,还可以帮您打通绝大部分企业遇到的SSO问题,让你数据中心的AD或者第三方机构与AWS建立授权机制,当您的用户在本地AD通过验证后,直接访问AWS上的数据,无需重复创建用户。同时可以给所有IAM用户开启复杂密码功能,并强制用户每隔一段时间修改密码。

内审或者外审通常是一个企业每年必做的事情,在AWS只需开启CloudTrail服务就可以自动记录所有AWS资源的使用情况,下图是一个CloudTrail的简单示例,我们可以看到什么时间哪个用户对什么资源做了什么事情。

同时还可以进一步查看某个事件的详细信息,便于审核,如下图所示:

服务本身的安全机制

由于AWS服务众多,限于篇幅的原因,本文重点讲解AWS虚拟机(EC2)的安全,如您对其他服务也很感兴趣,欢迎阅读AWS安全白皮书。

AWS Security Best Practices

https://d0.awsstatic.com/whitepapers/aws-security-whitepaper.pdf

     Amazon EC2多层安全模型

Amazon EC2多层安全模型包含底层物理服务器的操作系统,虚拟操作系统或者客户操作系统,防火墙和签名的API调用。目标是防止EC2内的数据被未授权的系统或者用户访问,在不牺牲客户配置灵活性的时,尽最大限度保证EC2本身的安全。

Amazon EC2当前利用了一个AWS高度定制化的Xen hpervisor,底层托管主机操作系统执行Ring0(最高权限),客户操作系统执行Ring1,应用执行Ring3(最低权限)在虚拟化层面来隔离客户和hypervisor。

当多台EC2运行在同一台底层物理服务器时,他们被Xen hypervisor隔离,如上图所示,在hypervisor层中有防火墙,且该防火墙位于底层物理网卡和客户EC2虚拟网卡之间。所有流量都需要通过该层,所以任意一个EC2实例无法访问其他EC2实例,底层的内存(RAM)也是使用类似机制实现隔离。

底层硬件的操作系统都必须通过MFA的方式进行访问,且这个系统通过AWS
特殊设计,构建及配置,确保其安全,所有访问都会自动记录且被审计。

客户操作系统完全有客户控制,客户拥有完整的根或者管理员权限,AWS没有任何权限可以访问。

重要数据经常备份,重要数据经常备份,重要数据经常备份,重要的事情说三遍。 在AWS你可以将常用对象数据存放在EBS或者S3,对于存放在EBS的数据每天EBS快照,确保任何重要数据都有一份最近的拷贝。对于S3上的对象数据,通过S3 桶策略进行精细控制,同时开始S3的版本控制功能防止误删或脏数据。

事先预防

如何做好事先预防工作呢?比如某用户处于某种原因开启了一些高端口或者公司命令禁止的网络端口,IT部门如何迅速发现并纠正呢?AWS Config服务可以帮到你。Config可为您提供 AWS 资源的详细库存及其当前配置,并会持续记录对这些资源配置所做的更改(例如,安全组的出/入规则、适用于 VPC 的网络 ACL 规则和 Amazon EC2 实例上标签的值)。

您可以通过创建 AWS Config 规则进行评估,规则规定了您理想的配置设置。AWS Config 能够提供可自定义的预定义规则 (称作托管规则),以帮助您开始进行评估。您还可以创建自己的自定义规则。在 AWS Config 持续跟踪您的资源中出现的配置更改时,它会检查这些更改是否违反了规则中的任何条件。如果某个资源违反了规则,那么 AWS Config 会将该资源和规则标记为不合规。例如,当创建 EC2 卷时,AWS Config 可以按照需要卷加密的规则来评估该卷。如果卷没有加密,AWS Config 会将卷和规则标记为不合规。AWS Config 还可以在您的所有资源中检查有无账户范围内的要求。例如,AWS Config 可以检查账户中 EC2 卷的数量是否在所需总数以内,或者账户是否使用 AWS CloudTrail 进行登录。

如何进行远程系统管理

Amazon EC2 系统管理器是一系列可帮助您自动执行管理任务的功能,例如收集系统清单、应用操作系统补丁、自动创建 Amazon 系统映像 (AMI) 以及大规模配置操作系统和应用程序。您可以使用 Systems Manager 以远程方式安全地管理托管实例的配置。托管实例是您混合环境中已经针对 Systems Manager 配置的任意 Amazon EC2 实例或本地计算机。系统管理器所提供的功能和共享组件无需额外费用。您仅需为实际使用的 Amazon EC2 资源付费。

 

系统管理器支持对下列操作系统进行管理:

Windows操作系统 Windows Server 2003 至 Windows Server 2016,包括 R2 版本
Linux操作系统

64 位和 32 位系统

Amazon Linux 2014.09、2014.03 或更高版本

Ubuntu Server 16.0.4 LTS、14.04 LTS 或 12.04 LTS

Red Hat Enterprise Linux (RHEL) 6.5 或更高版本

CentOS 6.3 或更高版本

仅 64 位系统

Amazon Linux 2015.09、2015.03 或更高版本

Red Hat Enterprise Linux (RHEL) 7.x 或更高版本

CentOS 7.1 或更高版本

如果大家感兴趣,可以参考下面链接进行具体的配置安装。

http://docs.amazonaws.cn/systems-manager/latest/userguide/what-is-systems-manager.html

接下来介绍使用系统管理器来进行补丁安装。由于北京区的系统管理器暂无补丁管理组件,我们使用Run Command组件来进行补丁安装。您可以使用系统管理器的Run Command以远程方式安全地管理托管实例的配置。托管实例是您混合环境中已经针对系统管理器配置的任意 Amazon EC2 实例或本地计算机。利用 Run Command,您可以自动完成常用管理任务以及大规模执行临时配置更改。

管理员使用 Run Command 可以在其托管实例上执行以下类型的任务:安装或引导应用程序,安装系统补丁,获取系统资产信息,构建部署管道,从 Auto Scaling 组终止实例时捕获日志文件,以及将实例加入 Windows 域等等。

写在最后的话

一波热闹的攻击事件已经过去,下一次攻击开始还会远吗?如果您能事先利用AWS提供的信息安全服务(文中提到的很多服务是免费的),配合完善的审核机制,不管是什么类型的信息安全风险来临你都能处乱不惊。

 

数据库迁移服务DMS——手把手教你玩转MongoDB到DynamoDB之间数据库迁移

1. 前言

AWS最近刚刚宣布了一项关于数据库迁移的新feature,支持Mongodb数据库作为源端,迁移到目标端Dynamodb中,这样可以使MongoDB的用户充分利用DynamoDB数据库提供的技术优势,譬如完全托管服务,高性能低延迟(毫秒级),精细化粒度控制等等。由于最近项目中涉及很多数据库迁移的事情,同时也对NOSQL数据库异构平台迁移非常感兴趣,写了这篇文档供大家参考。

2. DMS服务介绍

DMS作为数据迁移服务支持下面三种迁移类型:

  • 迁移源库中存在的数据到目标库
  • 迁移源库中存在的数据并且复制新增加的数据到目标库
  • 只复制新增加的数据库

数据迁移时源端和目标端设置

  • MongoDB作为源端

AWS DMS支持Mongodb作为源端的版本为2.6.x和3.0.x,MongoDB 作为一个基于文档存储的数据库,数据模式非常灵活,支持JSON和BJSON格式进行存储。当前AWS DMS 支持MongoDB作为源端以两种模式进行迁移,它们分别是文档模式和表模式。在文档模式中,需要设置参数extractDocID=true和nestingLevel=none,在复制时不支持collection的重命名。在表模式中需要启用表模式需要设置nestingLevel=one,另外在选择CDC时它不支持添加新的collection和重名collection。

  • DynamoDB作为目标端

使用Dynamodb作为目标端时需要配置partion key和Object mapping。

具体注意事项请参考官方文档链接:

http://docs.aws.amazon.com/dms/latest/userguide/CHAP_Source.MongoDB.html

3. 配置步骤

3.1 安装Mongodb

安装Mongodb的方式有多种方法,可以选择Marketplace或者AWS提供的cloudformation以及手动下载Mongodb软件进行安装,我选择手动安装Mongodb2.6.12版本。

A、登录EC2,获取如下软件:

ubuntu@ip-172-31-60-214:~$ wget http://downloads-distro.mongodb.org/repo/ubuntu-upstart/dists/dist/10gen/binary-amd64/mongodb-org_2.6.12_amd64.deb

ubuntu@ip-172-31-60-214:~$ wget http://downloads-distro.mongodb.org/repo/ubuntu-upstart/dists/dist/10gen/binary-amd64/mongodb-org-mongos_2.6.12_amd64.deb

ubuntu@ip-172-31-60-214:~$ wget http://downloads-distro.mongodb.org/repo/ubuntu-upstart/dists/dist/10gen/binary-amd64/mongodb-org-tools_2.6.12_amd64.deb

ubuntu@ip-172-31-60-214:~$ wget http://downloads-distro.mongodb.org/repo/ubuntu-upstart/dists/dist/10gen/binary-amd64/mongodb-org-server_2.6.12_amd64.deb

ubuntu@ip-172-31-60-214:~$ wget http://downloads-distro.mongodb.org/repo/ubuntu-upstart/dists/dist/10gen/binary-amd64/mongodb-org-shell_2.6.12_amd64.deb

B、安装软件包:

ubuntu@ip-172-31-60-214:~$ sudo dpkg -i mongodb-org*

C、创建数据目录和日志目录:

ubuntu@ip-172-31-60-214:~$ sudo mkdir /data /log

D、启动mongodb数据库服务:

ubuntu@ip-172-31-60-214:~$ sudo mongod –dbpath /data –logpath /log/mongodb.log –smallfiles –oplogSize 50 –fork

打开mongodb的shell, 验证服务启动成功。

ubuntu@ip-172-31-60-214:~$ mongo

MongoDB shell version: 2.6.12

connecting to: test

如下图所示:

3.2 通过脚本生成数据

for (var i = 1; i <= 793308; i++) {

               db.testData.insert( { x : i , name: "MACLEAN" , name1:"MACLEAN", name2:"MACLEAN", name3:"MACLEAN"} )

db.contacts.insert( { name: "Amanda", status:

"Updated" } )

db.contacts.insert( { name: "tom", status: "Updated" } )

db.contacts.insert( { name: "jack", status: "Updated" } )

db.contacts.insert( { name: "jack1", status:    "Updated" } )

db.contacts.insert( { name: "steph", status:    "Updated" } )

3.3 验证数据生成

运行如下命令:

3.4配置mongodb的副本集

A、启动mongodb数据库

ubuntu@ip-172-31-60-214:~$ sudo mongod –dbpath /data –replSet rs0 –logpath /log/mongodb.log –smallfiles –oplogSize 50 -fork

如下图:

B、登录到mongodb中,进行副本的初始化,如下图所示:

ubuntu@ip-172-31-60-214:~$ mongo localhost

MongoDB shell version: 2.6.12

connecting to: localhost

>

rs.initiate()

C、验证部署配置

> rs.conf()

D、创建管理员角色

rs0:PRIMARY> use admin

switched to db admin

rs0:PRIMARY> db.createUser(

…   {

…     user: “root”,

…     pwd: “rootpass”,

…     roles: [ { role: “root”, db: “admin” } ]

…   }

… )

如下图所示:

E、停止mongodb,然后重启mongodb:

ubuntu@ip-172-31-60-214:~$ sudo mongod –dbpath /data –replSet rs0 –logpath /log/mongodb.log –smallfiles –oplogSize 50 -fork –auth

如下图所示:

至此,Mongodb的数据库准备工作完成。

3.5 使用global账号登录到DMS服务,如下图所示:

A、创建复制实例:

指定Name,Instance class,Allocated storage,VPC选择创建,如下图所示:

创建mongodb源端的Endpoint,输入Endpoint identifier,Source engine指定为mongodb,Servername为Mongodb数据库主机IP,Port,Authentication mode,username等信息,如下所示:

注意在高级设置中指定extraDocID=true;nestingLevel=none

点击test connection验证连接成功。

点击创建完成。

B、创建目标端DynamoDB的endpoint

在endpoint console中选择create endpoint,并输入信息:Endpoint identifier,Endpoint type为Target,Target engine为dynamodb,指定Service Aceess Role ARN,并在advanced中设置:extractDOcID=true;nestingLevel=none

如下图所示:

点击test connection,验证成功,选择create 完成创建。

此处主要角色的设置需要指定:dms-vpc-role,同时attached 3个policy,如下图所示:

endpoint创建完成,如下所示:

C、创建task

点击create task,输入如下信息:task name,source endpoint,target endpoint,migrate type,Enable loging,同时根据实际需求进行相应的任务设置。

创建table mappings,点击json tab,进行手动输入设置:

注意,json文件内容需要根据各自创建的表进行手动创建。

点击创建任务,任务创建完成。

D、验证数据导入成功

返回任务列表,选择table statistics,查看表的导入是否成功,如下图所示:

MongoDb记录成功导入到dynamodb中,选择log可以通过cloudwatch查看DMS导入过程的记录。

登录到DynamoDb中,发现表成功创建,并且导入成功,如下图:

至此整个利用DMS进行Mongodb到Dynamodb的数据库迁移完成。

关于如何设置Table mapping 请参阅官方文档:

http://docs.aws.amazon.com/dms/latest/userguide/CHAP_Target.DynamoDB.html

附录

测试中我使用的Table Mapping json内容如下:

{

            "rules": [

                        {

                                    "rule-type": "selection",

                                    "rule-id": "1",

                                    "rule-name": "1",

                                    "object-locator": {

                                                "schema-name": "test",

                                                "table-name": "%"

                                    },

                                    "rule-action": "include"

                        },

            {

          "rule-type": "object-mapping",

          "rule-id": "2",

          "rule-name": "TransformToDDB",

          "rule-action": "map-record-to-record",

          "object-locator": {

            "schema-name": "test",

            "table-name": "contacts"

        },

        "target-table-name": "contacts",

        "mapping-parameters": {

        "partition-key-name": "id",

        "exclude-columns": [

        "_id"

        ],

        "attribute-mappings":[

          {

            "target-attribute-name": "id",

            "attribute-type":"scalar",

            "attribute-sub-type":"string",

            "value": "${_id}"

          }

        ]

        }

            },

            {

          "rule-type": "object-mapping",

          "rule-id": "3",

          "rule-name": "TransformToinventory",

          "rule-action": "map-record-to-record",

          "object-locator": {

            "schema-name": "test",

            "table-name": "inventory"

        },

        "target-table-name": "inventory",

        "mapping-parameters": {

        "partition-key-name": "id",

        "exclude-columns": [

        "_id"

        ],

        "attribute-mappings":[

          {

            "target-attribute-name": "id",

            "attribute-type":"scalar",

            "attribute-sub-type":"string",

            "value": "${_id}"

          }

        ]

        }

            },

            {

          "rule-type": "object-mapping",

          "rule-id": "2",

          "rule-name": "TransformToEC2test",

          "rule-action": "map-record-to-record",

          "object-locator": {

            "schema-name": "test",

            "table-name": "ec2Test"

        },

        "target-table-name": "ec2Test",

        "mapping-parameters": {

        "partition-key-name": "id",

        "exclude-columns": [

        "_id"

        ],

        "attribute-mappings":[

          {

            "target-attribute-name": "id",

            "attribute-type":"scalar",

            "attribute-sub-type":"string",

            "value": "${_id}"

          }

        ]

        }

            },

            {

          "rule-type": "object-mapping",

          "rule-id": "2",

          "rule-name": "TransformToDDB",

          "rule-action": "map-record-to-record",

          "object-locator": {

            "schema-name": "test",

            "table-name": "testData"

        },

        "target-table-name": "testData",

        "mapping-parameters": {

        "partition-key-name": "id",

        "exclude-columns": [

        "_id"

        ],

        "attribute-mappings":[

          {

            "target-attribute-name": "id",

            "attribute-type":"scalar",

            "attribute-sub-type":"string",

            "value": "${_id}"

          }

        ]

        }

            }

  ]

}

 

作者介绍

王友升

AWS解决方案架构师,拥有超过13年的IT从业经验,负责基于AWS的云计算方案架构咨询和设计,推广AWS云平台技术和各种解决方案。在加入AWS之前,曾在中地数码,浪潮,惠普等公司担任软件开发工程师,DBA和解决方案架构师。在服务器,存储,数据库优化方面拥有多年的经验,同时对大数据,Openstack及AI方面也进行一定的研究和积累。

DAX – DynamoDB集成全托管的内存缓存,轻松搞定读取负载!

相信大家已经都知道,Amazon DynamoDB 是一项全托管的NoSQL 数据库服务,适合所有需要一致性且延迟低于 10 毫秒的任意规模的应用程序,支持文档和键值存储模型。使用DynamoDB,可创建数据库表来存储和检索任意量级的数据,并提供任意级别的请求流量。现在,DynamoDB还提供了Auto-Scaling的功能,即可以通过你预先设置的阈值自动扩展和缩减表的吞吐量,做到完全弹性自动伸缩的目的,真正达到让你的数据库按实际吞吐量进行付费。

这么高的并发量却依然可以保持服务器的平均延迟在个位数毫秒,这让DynamoDB受到了非常多用户的青睐。然而随着大数据时代的数据暴增,很多客户的场景比较特殊,他们对数据库的响应时间越来越苛刻,甚至需要达到微秒的级别!这无疑给DynamoDB数据库又带来了一个难题。甚至也有客户会提到,能不能在DynamoDB前面放一层类似Redis的Cache服务呢?如果这样做的话,需要自己搭建缓存数据库,并且解决DynamoDB和Redis之间的数据同步问题;同时还要重写代码实现业务逻辑,比如如果数据在缓存中,则立即返回,如果数据没有在缓存中,则必须从数据库里面读取,将数据写入到缓存中,然后再返回。

当用户还带着这样的担心时,现在,Amazon DynamoDB已经整合了这一特性,推出了一个新的功能,即Amazon DynamoDB Accelerator,简称DAX。这是一种完全托管并且高度可靠的内存缓存,即使每秒种的请求量达到数百万,却依然可以将Amazon DynamoDB的响应时间从数毫秒缩短到数微秒!其实在很多场景都可以用到DAX,比如实时竞拍、秒杀、电商、社交游戏等场景,DAX可以提供快速的内存读取性能;再比如在产品促销日,读取访问量会明显上升,但是销售日结束访问量就会回归正常,诸如此类读取密集型的工作负载但同时又对成本敏感的应用都可以使用DAX服务。像类似于Expedia、Twilio、Genesys、eyeview等客户都已经率先用上了DAX服务

目前,DAX还是处于预览版,您可以点击链接进行申请。接下来,让我们创建一个DAX集群,赶紧体验一下微秒级别的响应测试吧!

1. DAX集群的原理

上图中可以看到,DAX起了一组缓存的节点(目前最多可以是10个节点),并将这些节点置放在VPC内部,应用程序部署在EC2上,这样EC2和DAX Cluster通过内网直接访问。关于DAX的内存缓存,主要是DynamoDB的读和写操作:

(1)最终一致性的读取操作,支持GetItem、BatchGetItem、Query、Scan接口,如果读取在DAX缓存中命中,将直接从DAX集群里读取;如果是第一次读取没有命中,那就从DyanmoDB里面读取。

(2)写入操作支持BatchWriteItem、UpdateItem、DeleteItem、PutItem接口,写入的时候数据先写入到DynamoDB表,确认写入成功之后,然后再写到DAX集群(item cache),这个操作只有在DynamoDB表和DAX集群都写入了数据的时候才算成功。如果由于一些原因这个操作失败了,那么这个item将不会缓存到DAX里面,并且会抛出一个exception。这种方式可以让缓存和数据库的数据保持一致性和完整性,不会出现过期数据在缓存里面。

(3)如果DAX有多个节点时,会选取一个主节点(primary node),多个从节点(read replica node),数据最终会分布到所有节点上,但对于客户端来说,只需要关心唯一的DAX连接地址,已经内置了负载均衡和路由策略,并且自动执行故障检测、故障恢复、软件修补等管理任务。

接下去,我们将模拟这一过程,进行实际测试。

2. 启动DAX集群

首先启动一个DAX集群,指定集群的节点数(目前节点最多为10个),我们建议您在生产环节中启用两个以上的节点,并将这些节点置放在不同的可用区中,从而提高高可用。设置好相应的IAM Role和Policy。Policy可以配置“只读”权限,或者“读和写”权限。更多关于权限配置可以参考:

http://s3.amazonaws.com/dynamodb-preview-dax/DAX.access-control.html

接下去设置DAX集群的子网组,DAX集群的节点会部署在这些子网里面。选定VPC和相对应的子网,并设置安全组。安全组入站需要打开DAX所用到的8111端口。

接下去配置DAX的参数组,指定Cache的Query TTL和Item TTL值。TTL的时间小到可以是“秒”,大到可以到“天”。

也可以自定义选定维护窗口,如果需要的话可以再加一个SNS通知,这样只要集群有维护就会立刻以短信,或者邮件等形式通知到您。

到这里,DAX集群就创建成功了。DAX集群会有一个唯一的endpoint地址,例如,这里是

dax-cluster-demo.bnsilv.clustercfg.dax.usw2.cache.amazonaws.com:8111

另外可以看到在这个例子中DAX集群启动了3个节点。

DAX集群具体的3个节点

3. 启动EC2 ,作为应用程序的server,同时作为DAX的client

如果仅作为测试,可以启动一台t2.micro的小型机器(Amazon Linux)。

EC2通过监控检查,启动成功。

4. 安装Java应用程序

(1)首先通过客户端连接到这台Amazon Linux EC2

(2)安装Java SDK

sudo yum install -y java-devel

(3)下载最新版的AWS Java SDK

wget https://s3-us-west-2.amazonaws.com/danrongdemo/DynamoDB-Accelerator-Test-Demo/aws-java-sdk-1.11.125.zip

unzip aws-java-sdk.zip

(4)下载最新版的DAX Jave客户端

wget https://s3-us-west-2.amazonaws.com/danrongdemo/DynamoDB-Accelerator-Test-Demo/DaxJavaClient-latest.jar

(5)设置CLASSPATH环境变量

export SDKVERSION=1.11.125

export CLASSPATH=.:./DaxJavaClient-latest.jar:aws-

java-sdk-$SDKVERSION/lib/aws-java-sdk-$SDKVERSION.jar:aws-

java-sdk-$SDKVERSION/third-party/lib/*

(6)下载Java应用程序代码

wget https://s3-us-west-2.amazonaws.com/danrongdemo/DynamoDB-Accelerator-Test-Demo/TryDax.zip

unzip TryDax.zip

(7)编译代码:javac TryDax*.java

(8)执行代码:java TryDax

以下是完整的应用程序拓扑图

5. 直接读取DynamoDB表的响应测试

从图中可以看到,直接从DynamoDB表里进行读取数据,分布执行了GetItem、Query、Scan操作,服务器的平均延时在个位数毫秒级别(小于10ms)。

此时查看DAX的监控还没有任何cache指标产生:

6. 加上DAX后进行测试

调整DAX的endpoint,通过代码判断当daxClient不为空时,就会采用DAX方式去查询。

将DAX的endpoint以参数形式传入执行,代码会判断DynamoDB的读取操作启用DAX内存缓存。

再次执行查询操作,发现:

上图中可以发现,第一次通过GetItem读取的时候由于DAX集群中没有数据,所以直接读取DynamoDB表,延迟在4.7ms左右,与此同时,会把数据写入到DAX集群中。当再次进行GetItem读取的时候,会直接从DAX内存缓存中读取,响应时间大概在0.8ms左右。同理,对于Query操作也可以发现,第一次查询DynamoDB表的响应时间是17.2ms,但是第二次读取时直接从DAX进行查询,响应时间缩短到了0.5ms左右。因此,不管是GetItem,Query还是Scan操作,读取操作的响应时间都已经大大提升了!另外,对于读取和写入操作,DAX已经自动集成了CloudWatch进行监控,很容易可以监控例如CPU使用率,Item Cache命中数,Query Cache命中数,总共的请求数等19个指标。

7. 如何将DAX整合到现有的DyanmoDB开发中?

您有可能会关心,我现在的业务代码已经基于DynamoDB开发好了,如何要把DAX整合进去,代码的开动量会不会很大呢?这一问题AWS已经替您考虑到了,例如有一张名为Music的表,主键(partition key)是Artist,排序建(sort key)是SongTitle。直接上代码:

8. 如何增加DAX的集群节点数?

增加DAX的节点数,可以通过API或者命令行操作,点击“Add node”:

然后选择新增加节点的个数,可以自定义DAX节点所在的可用区。如下:

9. 小结

如果您已经在使用Amazon DynamoDB了,集成DAX只需要稍微改动一下DAX client的实现,核心的业务逻辑代码基本上不用做任何改动。

DynamoDB的服务器平均延时在个位数毫秒级别,但是新出的DAX功能和DyanmoDB配合起来使用,则可以性能进一步推到一个新的层级,在处理每秒接收数以百万计请求的读取密集型工作负载时,响应时间缩短到以微秒级别,大概在0.几个ms左右。效果还是蛮惊人的!

更多DAX的介绍: https://aws.amazon.com/cn/dynamodb/dax/

关于DAX的价格: https://aws.amazon.com/cn/dynamodb/pricing/

作者介绍

毛郸榕

AWS解决方案架构师,负责基于AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内的应用和推广,毕业于北京航空航天大学云计算专业,硕士,毕业后直接加入亚马逊AWS中国。在大规模后台架构、企业混合IT和自动化运维等方面有着丰富的实践经验。目前在集中精力学习新一代无服务器架构的开发与设计。

Redshift又添新功能:让用户直接查询S3中的海量数据而无需复制到本地

背景

在Amazon Redshift 数据仓库为核心的用户,常常陷入一个困境,要想利用该MPP架构的云端数据仓库能力,用户通常需要利用Redshift的 copy命令将数据从S3并行拷贝到Redshift中,如果在数据量比较大的情况下,成本上的考量和业务上的诉求的矛盾会让用户犹豫不定; 尤其突出的矛盾是,客户的业务部门的需求涵盖数据范围同时包含数据仓库的数据和放在S3上的中间或者原始数据集,此时,我们能怎么做?

AWS大数据最佳实践的启示

AWS大数据最佳实践告诉我们要将数据的存储和处理、分析相分离,比如在Amazon EMR服务架构中(如下图),要分析的数据集按照一定的格式压缩存储在Amazon S3上,在EMR中通过Hive定义外表关联到S3上的数据,但不复制到EMR本地,从而实现了数据存储和分析处理的解耦;在大量的用户实践中,我们发现如此的架构优化,可以帮助客户节约大量的存储成本,同时,EMR分析集群无状态化,可以按需动态启动和停止EMR集群,从而优化了计算成本。同理,我们能否在Redshift数据仓库中引入类似的外部表的概念呢?

Amazon Redshift Spectrum简介

Amazon Redshift Spectrum是Redshift的一个新特性,它可以帮助客户将Redshift的分析能力从本地存储扩展到Amazon S3数据湖中海量的非结构化数据而不需要加载数据。通过Redshift Spectrum您可以将热数据存储到 Amazon Redshift 群集中,以获得本地磁盘性能;同时使用 Amazon Redshift Spectrum 将您的查询扩展到 Amazon S3 中存储的冷数据,以获得无限的可扩展性和低成本。

详细情况请参考官方介绍:https://aws.amazon.com/cn/redshift/spectrum/

目标人群及应用场景

该新功能的推出完善了Redshift数据仓库用户的大数据分析的应用场景,客户可以直接利用Redshift和Redshift Spectrum的能力同时处理本地和S3上的数据集;所以,目标受众是Redshift数据仓库的用户比如金融,电商,游戏等等行业客户。

从应用场景来看,可以满足如下业务需求:

  • 针对数据仓库本地数据和S3上的数据提供一致的、熟悉的数据仓库操作体验
  • 提供终端用户统一的BI或者SQL客户端接入
  • 跨数据仓库热数据和S3冷数据的复杂混合查询
  • 满足低频的业务全数据的低成本即席查询

大数据处理示例管道

本大数据处理示例管道展示了以Redshift数据仓库为核心的典型用户场景,原始数据,中间结果和ETL处理之后的数据都保存在数据湖Amazon S3上;用户通过BI工具或者熟悉的SQL客户端通过Redshift(包括Redshift Spectrum)操作所有的业务数据,包括大数据量的原始数据和存储在数据仓库本地的热数据;客户无需专门为了某个业务的特殊需求,将数据从冷数据从S3复制到Redshift本地再作分析。

支持的数据格式

Redshift Spectrum 使用您已使用的开发数据格式在 Amazon S3 中直接查询数据,这些格式包括

  • 文本文件如 CSV格式文件
  • 日志文件如TSV格式
  • 列式格式如Apache Parquet和Hive中的RCFile格式文件
  • 二进制文件:Sequence格式文件
  • 压缩格式支持:gzip、snappy、bz2

动手做个试验

我们通过一个动手实验来体验一下,Redshift Spectrum的新功能,该新功能目前在如下三个区域可用:us-east-1、us-east-2和us-west-2;而且Redshift Spectrum只支持本区域的S3数据查询。

创建和关联IAM角色

Redshift Spectrum的功能是针对S3里面的数据,所以需要授权能够只读访问S3,同时定义的外部表默认保存在Athena数据目录里面(详情见下一个章节),所以同时需要授权访问Athena。

本实验创建了一个IAM角色SpectrumRole,权限里面要包含AmazonS3ReadOnlyAccessAmazonAthenaFullAccess 策略。

创建好角色之后,将角色管理到Redshift集群,管理控制台转到Redshift页面,勾选要应用的集群,点击管理角色按钮进行操作:

创建一个External Schema

经过上一个步骤之后,我们Redshift集群的权限已经准备好,接下来和你可以使用你熟悉的SQL客户端来定义表结构和执行查询。与Redshift本地数据表有些差别的地方是,Redshift Spectrum引入了外部Schema和外部表的概念;通过Redshift Spectrum定义的外部数据库存放在外部的数据目录里面,Redshift Spectrum将默认会将该外部数据库定义存放到了Athena的数据目录里,当然也可以显式指定存储在你的EMR集群的Hive的元数据目录里面;

下面我们执行如下SQL语句为Redshift创建一个外部数据库”spectrumdb”:

create external schema spectrum
from data catalog
database 'spectrumdb'
iam_role 'arn:aws:iam::183449792837:role/SpectrumRole'
create external database if not exists;

同时在Athena的数据目录里面可以看到我们刚刚创建的外部数据库:

我们也可以利用以下语法,直接从Redshift Spectrum中引用Athena的已经存在的数据库:

create external schema athena_schema from data catalog
database 'sampledb'
iam_role 'arn:aws:iam::183449792837:role/SpectrumRole'
region 'us-east-1';

更多详情请参考该命令的官方页面:http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/r_CREATE_EXTERNAL_SCHEMA.html

创建演示的External Tables

上一个章节我们在Athena中新建了一个外部Schema “spectrum”,接下来我们就可以在该External Schema中定义关联到S3的外部表(External Table):

create external table spectrum.sales(
salesid integer,
listid integer,
sellerid integer,
buyerid integer,
eventid integer,
dateid smallint,
qtysold smallint,
pricepaid decimal(8,2),
commission decimal(8,2),
saletime timestamp)
row format delimited
fields terminated by '\t'
stored as textfile
location 's3://jxlabs/spectrum/sales/';

该SQL语句在External Schema的spctrum中定义了一个sales表,映射到S3的文本数据文件,该文件内容字段以tab分隔;

具体创建External Table 语句请参考官方文档:http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/r_CREATE_EXTERNAL_TABLE.html

 

用户可以查询SVV_EXTERNAL_TABLES视图查看所定义的所有的External Tables:

select * from SVV_EXTERNAL_TABLES swets;

结果参考如下,从查询结果中,我们可以了解更多细节,比如序列化/反序列化的库等。

查询演示

我们在上一个章节创建了一个sales表,下面我们进行一些查询,来体验下如何混合查询Redshift本地数据和S3上的冷数据:

第一个查询:查询S3上数据表Sales的数据

select count(*) from spectrum.sales;

该表有18万行左右的数据,执行时间大概在2秒左右:

第二个查询:混合查询S3上数据表Sales的数据和本地数据表event

event表的数据结构如下:

select top 10 event.eventname, sum(spectrum.sales.pricepaid) from spectrum.sales, event
where spectrum.sales.eventid = event.eventid
group by eventname
order by 2 desc;

该查询基于eventid把sales和events做了一次join操作找出了总销量Top 10的活动(event):

在该SQL语句前加上Explain来查看Redshift的执行计划,发现,针对S3上的数据,Redshift Spectrum 会负责执行S3 Seq Scan、S3 HashAggregate和S3 Query Scan操作;同时Redshift不会执行外部表的统计信息(statistics),执行计划会提示“Tables missing statistics: spectrum_sales”,该执行计划会默认本地表的数据量要远远少于存储在S3上的外部数据量。

explain
select top 10 event.eventname, sum(spectrum.sales.pricepaid) from spectrum.sales, event
where spectrum.sales.eventid = event.eventid
group by eventname
order by 2 desc;

与 Amazon Athena、EMR及S3的关系

通过前面的章节我们对Redshift Spectrum有了比较直观的理解,这个章节我们从Redshift 的角度来探讨下和Amazon Athena,EMR及S3的关系,我做了一个示意图如下:

Amazon Redshift本身提供了适合企业数据仓库的大数据计算,支持从Amazon S3、ERM、Dynamo DB甚至远程主机通过多节点并行复制数据到Redshift本地表,同时提供了UNLOAD命令帮助客户直接将本地数据卸载保存到Amazon S3;发布Redshift Spectrum功能,进一步帮助客户优化以Redshift为核心的数据仓库场景,进一步将数据和计算分析进行解耦,让用户可以灵活根据具体需求将数据存储在Redshift本地或者S3上,取得性能、可扩展性和成本的最优化结果。

Redshift Spectrum 和Athena的关系通过官方文档可以看出,底层的高级查询优化技术和Athena是独立的,但他们可以共享针对S3上数据的外部数据表定义,这样做的结果是,通过Redshift Spectrum定义的存储在Athena数据目录中的外部表,同时也可以通过Athena直接进行查询;但只有从Redshift才能执行针对S3数据的外部表和Redshift 本地表的混合查询。

监控和查看查询细节

要查看Redshift Spectrum相关查询的相关指标,我们可以通过查询一下两个视图进行了解:

  1. SVL_S3QUERY_SUMMARY

通过该视图,我们可以知道系统中所有运行过的S3 查询,以及该查询涉及到的:

  • 通过Redshift Spectrum返回给集群的数据量(bytes);
  • Redshift Spectrum节点最大和平均请求时间
  • 扫描的S3数据量(bytes),这也是反应到Redshift Spectrum成本的数据量值,比如目前每TB收费5美金,就是指这里的扫描的S3数据量。
  • 扫描的S3的文件数量,以及最大和平均文件大小

下面是该视图的字段供参考:

2. SVL_S3QUERY

该视图可以让用户从segment和node slice角度查看Redshift Spectrum的S3查询细节。与SUMMARY视图相比,粒度更细,多了segment,slice,node等字段:

性能和成本

通常,相对比Redshift本地数据存储在EBS或者本地磁盘的查询,针对S3的数据扫描会慢一些,因为目前Redshift Spectrum没有缓存,没有排序键等等;但对于交互式查询,Redshift Spectrum提供了并行处理和无限扩展的优化,性能足够应对非常多的用户场景。而且,如果存储在S3中的数据格式采用列式结构如Parquet,针对不需要扫描全部字段,仅仅涉及部分列的查询可以极大提升性能和降低成本。

对于一些对性能要求高的业务需求,我们可以定期将计算后的结果经过存储在Redshift本地表当中,使用我们熟悉的Redshift的优化方法,提升处理性能。Redshift Spectrum 的成本计算和Amazon Athena 类似,都是针对处理的S3数据量来计算,每处理TB数据目前5美金;

总结

本文和大家一起学习了Redshift Spectrum的新功能,并动手做了一个实验来进一步体验并思考在实际工作中可能遇到的问题。同时,我们一起梳理了以Amazon Redshift数据仓库为核心的大数据架构,以及和其他Amazon 大数据服务之间的相互关系。相信很多客户都可以从该新功能获益。

作者介绍

薛军

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

 

让你的数据库流动起来 – 利用MySQL Binlog实现流式实时分析架构

数据分析特别是实时数据分析,已经越来越多的成为各行各业的分析要求与标准 – 例如,(新)零售行业可能希望通过­­线下POS数据与实时门店客流流量的进行实时结合与分析,实现商品销售,销量,总类等等的实时预测; 在线广告平台期望通过广告(Impression)总类,数据量以及基于时间的点击(Click)量,计算实时的广告转化率(Conversion Rate);物联网的用户想通过实时分析线下的状态设备与设备采集的数据,进行后台的计算与预判 – 例如做一些设备维修的提前预警(Predicative Failure Analysis)与线下用户的使用习惯;电商平台或者是在线媒体需要给终端用户提供个性化的实时推荐等等。

纵观这些业务系统,从数据流的角­­度看,往往数据架构可以分为前后端两个部分 – 前端的业务数据与日志收集系统(其中业务数据系统一般都是利用关系型数据库实现 –  例如 MySQL,PostgreSQL)与后端的数据分析与处理系统 (例如ElasticSearch 搜索引擎,Redshift数据仓库,基于S3的Hadoop系统 等等,或者基于Spark Stream的实时分析后端)。

“巧妇难为无米炊”,实时数据分析的首要条件是实现实时数据同步,即从上述前端系统到后端系统的数据同步。具体来讲包含两个要求(根据业务场景的不同,实时性会有差异)- 1) 实时 2) 异构数据源的增量同步。实时的要求容易理解 – 无非是前后端系统的实时数据ETL的过程,需要根据业务需求,越快越好。所谓异构数据源的增量同步是指,前端产生的增量数据(例如新增数据,删除数据,更新数据 – 主要是基于业务数据库的场景,日志相对简单,主要是随时间的增量数据)可以无缝的同步到后端的数据系统 – 例如ElasticSearch,S3或者Redshift等。 显然,这里的挑战主要是来自于异构数据源的数据ETL – 直白一点,就是怎么把MySQL(或者其他RDBMS)实时的同步到后端的各类异构数据系统。因为,MySQL的表结构的存储不能简单的通过复制操作实现数据同步。  业界典型的做法大概可以归纳为两类 – 1)通过应用程序双写的架构 (application dual-writes)  2) 利用流式架构实现数据同步,即基于流式数据的Change Data Caputre (CDC) 。 双写架构实现简单,利用应用逻辑实现,但是要保证数据一致性相对复杂(需要通过二阶段提交实现 – two phase commit),而且,架构扩展相对比较困难 – 例如增加新的数据源,数据库等。 利用流式数据重构数据,越来越成为很多用户与公司的实时数据处理的架构演化方向。 MySQL的Binlog,以日志方式记录数据变化,使这种异构数据源的实时同步成为可能。 今天,我们主要讨论的是如何利用MySQL的binlog实现流式数据同步。

MySQL Binlog数据同步原理

讲了这么多,大家看张图。 我们先了解一下MySQL Binlog的基本原理。 MySQL的主库(Master)对数据库的任何变化(创建表,更新数据库,对行数据进行增删改),都以二进制文件的方式记录与主库的Binary Log(即binlog)日志文件中。从库的IO Thread异步地同步Binlog文件并写入到本地的Replay文件。SQL Thread再抽取Replay文件中的SQL语句在从库进行执行,实现数据更新。 需要注意的是,MySQL Binlog 支持多种数据更新格式 – 包括Row,Statement,或者mix(Row和Statement的混合)。我们建议使用Row这种Binlog格式(MySQL5.7之后的默认支持版本),可以更方便更加实时的反映行级别的数据变化。

如前所述,MySQL Binlog是MySQL主备库数据同步的基础,因为Binlog以日志文件的方式,记录了数据库的实时变化,所以我们可以考虑类似的方法 – 利用一些客户端工具,把它们伪装成为MySQL的Slave(备库)进行同步。

基于Binlog的流式日志抽取的架构与原理

在我们这个场景中, 我们需要利用一些客户端工具“佯装”成MySQL Slave,抽取出Binlog的日志文件,并把数据变化注入到实时的流式数据管道中。我们在管道后端对Binlog的变化日志,进行消费与必要的数据处理(例如利用AWS的Lambda服务实现无服务器的代码部署),同步到多种异构数据源中 – 例如 Redshift, ElasticSearch, S3 (EMR) 等等。具体的架构如下图所示。

这里需要给大家介绍一个比较好的MySQL的Binlog的抽取工具 Maxwell’s Daemon。这款由Zendesk开发的开源免费(http://maxwells-daemon.io/) Binlog抽取工具可以方便的抽取出MySQL (包括AWS RDS)的变化数据,方便的把变化数据以JSON的格式注入到后端的Kafka或者Amazon Kinesis Stream中。我们把RDS MySQL中的Binlog输出到控制台如下图所示 –  下图表示从employees数据库的employees数据表中,删除对应的一行数据。

在上述架构中,我们利用Lambda实时读取Amazon Kinesis Stream中的MySQL Binlog日志,通过Kinesis Firehose实时地把MySQL binlog的结构数据自动化地同步到S3和Redshift当中。值得注意的是,整个架构基于高可用和自动扩展的理念 – Kinesis Stream( 高可用),Lambda(Serverless与自动扩展),Kinesis Firehose(兼具高可用与自动扩展)。Kinesis Stream作为统一的一个数据管道,可以通过Lambda把数据分发到更多的数据终点 – 例如,ElasticSearch或者DynamoDB中。

动手构建实时数据系统

好了,搞清楚上面的架构,我们开始动手搭建一个RDS MySQL的实时数据同步系统吧。这里我们将把MySQL的数据变化(包括具体的行操作 – 增删改)以行记录的方式同步到Redshift的一张临时表,之后Redshift会利用这种临时表与真正的目标表进行合并操作(Merge)实现数据同步。

1)      配置AWS RDS MySQL的Binlog同步为Row-based的更新方式: 在RDS的参数组中,设置binglog_format为Row的格式。如下图所示。

2)      另外,我们可以利用AWS RDS提供的存储过程,实现调整Binlog在RDS的存储时间为24个小时。我们在SQL的客户端输入如下命令:

call mysql.rds_set_configuration('binlog retention hours', 24)

3)      在这里我们通过如下的AWS CLI,快速启动一个stream(配置CLI的过程可以参考http://docs.aws.amazon.com/cli/latest/userguide/installing.html,并且需要配置AWS 的用户具有相应的权限,为了方便起见,我们在这个测试中配置CLI具有Administrator权限):这里我们创建了一个名为mysql-binlog的kinesis stream 同时配置对应的shard count为1。

aws kinesis create-stream –stream-name mysql-binlog –shard-count 1

4)      Maxwell’s Daemon提供了Docker的封装方式,在EC2运行如下Docker command就可以方便的启动一个Maxwell’s Daemon的客户端。其中,蓝色字体部分代表对应的数据库,region与kinesis stream等。

docker run -it --rm--name maxwell-kinesis

-v `cd && pwd`/.aws:/root/.aws saidimu/maxwell  sh -c 'cp /app/kinesis-producer-library.properties.example

/app/kinesis-producer-library.properties && echo "Region=AWS-Region-ID" >>

/app/kinesis-producer-library.properties && /app/bin/maxwell

--user=DB_USERNAME --password=DB_PASSWORD --host=MYSQL_RDS_URI

--producer=kinesis --kinesis_stream=KINESIS_NAME '

 

5)      有了数据注入到Kinesis 之后,可以利用Lambda对kinesis stream内部的数据进行消费了。Python代码示例如下。需要注意的是,我们这里设置Lambda的trigger是Kinesis Stream(这里我们对应的Kinesis Stream的名字是mysql-binlog),并且配置对应的Lambda访问Kinesis Stream的batch size 为1。这样,对应的数据实时性可能更快,当然也可以根据需要适度调整Batch Size大小。具体的配置过程可以参考 – http://docs.aws.amazon.com/lambda/latest/dg/get-started-create-function.html

上述代码(Python 2.7 runtime)的主要功能实现从Kinesis Stream内部读取base64编码的默认binlog data。之后遍历Kinesis Stream中的data record,并直接写入 Kinesis Firehose中。

6)      接着,我们可以通过配置Kinesis Firehose把lambda写入的数据同步复制到S3/Redshift 中。具体的配置细节如下。配置细节可以参考 http://docs.aws.amazon.com/firehose/latest/dev/basic-create.html#console-to-redshift

其中,通过S3 buffer interval来指定往S3/Redshift中注入数据的平率,同时,Copy Command用来指定具体的Redshift的Copy的操作与对应的Options,例如(我们指定逗号作为原始数据的分隔符 – 在lambda内部实现)。

至此,我们已经可以实现自动化地从MySQL Binlog同步变量数据到Redshift内部的临时表内。好了,可以试着从MySQL里面delete 一行数据,看看你的Redshift临时表会发生什么变化?

还有问题? 手把手按照github上面的code 走一遍吧 – https://github.com/bobshaw1912/cdc-kinesis-demo

作者介绍

肖凌

AWS解决方案架构师,负责基于AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内和全球的应用和推广,在大规模并发后台架构、跨境电商应用、社交媒体分享 、Hadoop大数据架构以及数据仓库等方面有着广泛的设计和实践经验。在加入AWS之前曾长期从事移动端嵌入式系统开发,IBM服务器开发工程师。并负责IBM亚太地区企业级高端存储产品支持团队,对基于企业存储应用的高可用存储架构和方案有深入的研究。

客户端直连S3实现分片续传思路与实践

Amazon S3是互联网存储解决方案,能让所有开发人员访问同一个具备可扩展性、可靠性、安全性和快速价廉的数据存储基础设施。Amazon S3 提供了一个简单 Web 服务接口,可用于随时在 互联网上的任何位置存储和检索任何数量的数据。开发人员可以利用Amazon提供的REST API接口,命令行接口或者支持不同语言的SDK访问S3服务.

同时S3对于上传功能的API提供也是非常丰富的,与此同时,很多客户对于S3的断点续传也有了很深入的需求,本篇博客将会介绍如何使用S3的Javascript SDK来实现客户端浏览器到S3的断点续传功能.

安全考量

首先我们需要度量在浏览器客户端直连上传到S3这个场景下的安全问题,我们是一定不能把我们的AccessKey暴露到客户端浏览器的,但是上传到S3的API一定要提供AccessKey和SecretKey,因此这里我们将会利用生成临时的AccessKey和SecretKey(结合有效期)的方式来保证客户端的上传,这里介绍一篇关于利用TVM (Token Vending Machine)来生成临时Key并上传S3的文章,本文主要探讨关于S3的分片上传和断点续传的知识点.

Javascript SDK和S3 API简介

从整体编程语言架构的层面上来讲,AWS的各个语言的SDK都主要划分为上层和下层的API, 上层API主要是针对一些用户必要的功能利用下层API所作的一层封装,掌握了这个原则之后我们就可以合理的利用AWS的上层API看能否实现自身的需求.

Javascript SDK文档总结

在掌握SDK之前,我们应该先对SDK的文档和大致的结构有一个了解,这样才能方便我们更好的使用SDK, 下面列出了SDK的官网入门连接和API参考文档.

API参考文档: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/index.html

S3 API参考文档: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html

构建SDK中的S3对象

首先,AWS的SDK都是先需要利用Credentials来构建对象的,这里我们构建S3的对象也是如此,但是请注意一定不能将自己的Key暴露在客户端或者提交到代码中,应该使用 TVM获取了Key之后再利用AWS.Credentials对象来构建S3的对象.

在构建S3对象时,也需要同时指定AWS的Region.

利用上层Javascript API构建简单的分片断点续传功能

接下来,我们一步一步的来创建上层API构建断点续传的实践.

1. 创建工程

这里我们以node.js平台的express来提供简单的静态服务. 本文不会涉及如何安装node.js,关于安装指南,可以参考官网nodejs.org 首先利用npm包管理器安装express模版生成器:

npm install express-generator -g

完成后我们利用命令行生成项目:

mkdir s3upload
express --view=ejs

这里的--view=ejs主要指定ejs作为express的html模版引擎,方便我们的测试. 创建好之后的工程结构如下图:

2. 编写页面UI

这里我们通过引入<script src="https://sdk.amazonaws.com/js/aws-sdk-2.45.0.min.js"></script>来在浏览器端倒入AWS SDK, 并且我们创建了一个id为progress的label来监控进度. 最后我们引入了/javascript/index.js来编写我们页面的业务逻辑.

分片上传控制

接下来我们在public/javascript文件夹中创建index.js

简单的两个静态文件就能够完成分片上传的功能了.

启动项目

由于Express已经建立好模版,只需要在控制台输入npm start就能够在3000端口监听服务,通过访问http://localhost:3000 就能够测试分片上传的功能了.
完整的项目代码可以在Github上访问.

设置CORS

由于SDK通过Ajax提交数据,需要在S3桶策略中配置跨域提交的CORS. 示例中的*建议在生产环境中改成自己的域名.

S3上层API说明

以上实践为大家简单介绍了如何使用S3点上层API自动完成分片上传的功能,并且通过事件监控的方式来了解上传的进度,这里主要有以下几点需要注意.

1). 上传api需要指定Bucket: ‘testupload’, Key: file.name让SDK识别到桶和文件的Key名称.

2). 由于是上层API, 因此上传程序将会实施自动分片,由于S3的最小分片是5M,所以当文件大于5M时此上传程序才会进行自动分片,并且每隔5M为一个切片进行上传工作.

3). SDK使用AJAX方式提交自动分片的文件,因此需要设置S3的跨域提交配置CORSRule.

4). 在整个过程中,不要在客户端暴露AccessKey和Secret,利用TVM (Token Vending Machine)一文来安全获取临时Token.

通过底层API来编写实现更多分片上传的功能.

S3的分片上传主要的原理是通过初始化一次上传的uploadId, 然后在主动取消或者完成前会一直沿用这个uploadId,如果用户使用这个uploadId持续上传分片则会一直保持该文件的上传,最后通过一个complete的操作来结束上传,并且会合并所有分片成为一个文件. 通过上述原理,我们的底层API主要会调用以下来实现自定义的分片上传:

1). 首先调用createMultipartUpload

2). 在返回了uploadId之后,将文件切片后利用uploadPart API指定在同一个uploadId下从第几段开始上传.

3). 最后调用completeMultipartUpload结束上传,同时请求成功后S3将会自动合并所有分片成为一个文件.

关于各个API的详细参数,请参考官方文档.

最后,我们需要注意的是,在整个客户端上传的过程中,难免会有分片并没有全部上传完毕,或者没有最终成功调用了completeMultipartUpload的孤儿分片存在,而且随着时间的推移,这些孤儿分片也会越来越多,并且这些没有被S3合并的分片,在S3的管理控制台中是不可见的,那么我们的最佳实践应该是怎样的呢? 首先我们可以使用abortMultipartUpload来取消分片,那么某一次上传没有完成合并的分片将会被清除, 这个可以结合API来配合使用.

其次,虽然S3控制台中并不会显示未上传完成的孤儿分片,但是我们可以通过listMultipartUploads和listParts来查看未完成的分片有哪些.

最后,也是最自动的方法是我们可以使用S3的桶生命周期Policy来设置自动清除和保留未完成合并的孤儿分片的时间周期,让我们并不需要花精力来处理这些异常的情况.

总结

S3作为轻量简易高可用的存储,结合AWS的SDK,我们在通过临时证书的交互后可以轻易的实现安全的浏览器客户端直接分片断点续传到S3的功能,无论是借助于上层的S3上传API还是利用底层的createMultipartUpload实现方式,Javascript SDK都能够在各个层面给到开发人员灵活轻便的实现这些逻辑,从而让开发人员更专注在自身业务的开发工作中.

作者介绍

李磊
AWS解决方案架构师,负责基于AWS的云计算方案的架构设计,同时致力于AWS云服务在国内和全球的应用和推广。在大规模并发后台架构,电商系统,社交网络平台、互联网领域应用,DevOps以及Serverless无服务器架构等领域有着广泛的设计与实践经验。在加入AWS之前超过十年的开发和架构设计经验, 带领团队攻克各种技术挑战,总是希望站在技术的最前沿。