转自:https://blog.csdn.net/csdnhxs/article/details/80205831
亲爱的读者:我们最近添加了一些个人消息定制功能,您只需选择感兴趣的技术主题,即可获取重要资讯的邮件和网页通知。
编者按:本文系InfoQ中文站向陈天的约稿,这是AWS系列文章的第二篇。以后会有更多文章刊出,但并无前后依赖的关系,每篇都自成一体。读者若要跟随文章来学习AWS,应该至少注册了一个AWS账号,事先阅读过当期所介绍服务的简介,并在AWS management console中尝试使用过该服务。否则,阅读的效果不会太好。在这篇文章里,介绍了尝试用 S3 创建公司内部的文件服务器,保存员工私人/共享文件,并以类似Dropbox的方式双向同步。
S3是 AWS 最早发布的诸多服务之一,用作可信存储。所谓可信,AWS给出的概念是:「在指定年度内为对象提供 99.999999999% 的持久性和高达 99.99% 的可用性」,换句话说就是任何存储于S3的数据基本不可能丢失,在一个年度内,不超过1小时(3153.6s)的宕机时间。除此之外,S3还提供如下特性:
S3 的用户可以使用 AWS management console 来创建 bucket(类比文件系统的根目录),以及 bucket 内部的目录树,并上传文件,但这不是使用 S3 的最佳方式。日常的主要操作应该使用 AWS CLI 和 AWS SDK 完成。
安装 AWS CLI 可以使用 pip / brew 等安装工具,不再详述。AWS CLI 是 AWS 官方提供的 CLI 工具,简单好用,我会另行撰文深度介绍 AWS CLI。AWS CLI 目前不支持命令和参数的自动补全,从 AWS re:invent 2015 透露出来的信息,其团队在做一些自动补全的尝试,未来会变得更加人性化。如果你想现在就用得更舒服一些,可以使用 sAws。
使用 AWS CLI 操作 S3 非常简单,创建/删除 bucket 可以使用 aws s3api
:
$ aws s3api create-bucket --bucket <name>
$ aws s3api delete-bucket --bucket <name>
如果要像一般的文件系统一样操作 S3,可以使用 aws s3
命令:
$ aws s3 ls
$ aws s3 cp
$ aws s3 rm
此外,aws s3
还提供了 sync
,方便本地文件和 S3 上的文件互相 sync,比如我本地用 pandoc 编译出了 markdown 撰写的 reveal.js 的 slides,可以这样同步到 S3:
$ aws s3 sync ./output s3://eng-assets/slides
AWS SDK 提供了对几乎所有主流语言的支持,在程序里使用 S3,一般的流程是:
这里列一个 JavaScript 的例子:
const aws = require('aws-sdk');
const Promise = require("bluebird");
const s3 = Promise.promisifyAll(new aws.S3());
s3.createBucketAsync({Bucket: 'test-myBucket'}).then(function() {
var params = {Bucket: 'test-myBucket', Key: 'myKey', Body: 'Hello!'};
s3.putObjectAsync(params).then(function(data) {
console.log('successfully uploaded data');
}).error(function(err) {
console.log(err);
})
});
S3的一些典型使用场景如下:
我们简单介绍一下 S3 实现静态网站托管,然后以一个例子讲述如何使用 S3 实现一个能最大程度保证数据安全同时又价格低廉的团队内部的文件服务器。
经常使用 GitHub 的朋友对 GitHub pages 服务一定不会陌生,你只要把各种静态网站生成工具的生成的目标放入 gh-
pages 的 branch,GitHub pages 就会帮你做静态网站的托管。得益于如今越来越强大的 JavaScript 和各种 API,静态网站其实早已脱离了展示 HTML 的基本范畴。
GitHub pages 有一个缺点就是,只要你使用,它就是开放的,无法变成一个私有网站,存放公司内部的私密文件。公司内部的一些私有内容,比如:
你无论如何都不会想将其暴露给外界。这个时候,GitHub pages 就不适用,我们可以使用 S3 Web Hosting + IAM policy 来完成。
使能 S3 Web Hosting 是件很简单的事情,只需在 AWS console 中,为对应的 bucket 打开这个选项即可,然后添加如下 IAM policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:AWS:s3:::team-assets/*"
}
]
}
S3 Web Hosting 会告诉你一个用于访问的域名,你也可将你自己的私有域名指定一个 CNAME
指向该域。这样配置下来,只要域名和要访问的文件夹没有暴露,文件内容就是安全的。适用于安全等级不高的内容。
如果需要更高的安全级别,可以配合 VPC + IAM policy。一般而言,使用 VPC 的用户,都会将 VPC 设置成私有网络(比如 10.0.0.x 的网络),然后在网络边界配置一台 VPN 服务器,用于内外网的交互。任何用户要访问内网,必须先接入 VPN。我们可以设置用于 Web Hosting 的 S3 的 bucket 的 IAM 仅允许 VPN 服务器的 IP 访问,如下:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:AWS:s3:::team-assets/*",
"Condition" : {
"IpAddress" : {
"aws:SourceIp" : ["5.5.5.5/32"]
}
}
}
]
}
那么,只用当用户接入 VPN 之后,才能访问 Web Hosting 的域名下的文件,进一步提高了安全性。当然,由于不是在路由层面控制访问,所以没办法防止 ip spoofing,还是有一些潜在风险的,不过风险不大(攻击者需要知道要访问的文件所在的域名和路径,并且知道仅允许哪个源IP访问,进而进行 IP spoofing,而公网上 IP spoofing 的难度很大,基本上所有的路由器都会做 reverse route check)。
很多公司都会为员工提供私人和共享的文件存储。比如作为一个用户,我可以把我的私人文件存放在:fileserver://home/tyrchen/*
下,把一些共享文件存放在 fileserver://public/tyrchen/*
下。为了能够安全的存储这些文件,公司的 IT 部门一般会使用昂贵的 SAN(Storage Area Network)来保证一定程度的 SLA(Service Level Agreement),同时,还要做各种各样的备份(和恢复)。如果我们使用 S3 来实现类似的文件服务器,其代价和未来的维护成本会小得多。此外,我们还可以做一些额外的开发,使得文件服务器的使用体验类似于 Dropbox。
大致的想法是这样的:
- 新员工入职后会为其在S3上建立 home folder,用来保存重要的私人文件和共享文件。
- 员工电脑的本地文件中会有一个目录 corp-fs-box,里面包含三个子目录:
- private:存放任意文件,私有,会自动sync到私人目录,别人无法访问。
- photos:存放各种媒体文件,公开,会自动sync到共享目录,并生成合适的尺寸放在供Web访问的S3 bucket中。
- 员工只要在本地目录中存放文件,就会按照上述规则自动同步,类似Dropbox。
解决思路:
- 创建两个 S3 bucket:corp-fs-team 和 corp-fs-web。corp-fs-web打开 Web Hosting 功能。
- 使用IAM policy来设置 home folder 的权限。
- 使用
aws s3
sync 来同步文件夹:
- 对本地
corp-fs-box/private
里的文件,同步到S3://corp-fs-team/home/{AWS:username}/
中。这个目录只有当前用户可以访问,其他用户不能访问。- 对本地
corp-fs-box/pub/photos
里的文件,同步到S3:web//corp-fs-team/pub/photos
中。这个目录任何用户都可以访问并修改。- S3 配置 Events,使得对于
S3://corp-fs-team/pub/photos/{AWS:username}/
的任何更新行为(添加/删除)都会触发 lambda 函数。- lambda 函数扫描上传的文件,如果是
*.jpg
或者*.mp4 / *.mov
,则将其进行 resize / transcoding 等处理,并将编译的结果放在S3://corp-fs-web/pub/photos/{AWS:username}/*
下,供内网的用户浏览。
涉及的 IAM policy 如下:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowGroupToSeeBucketList",
"Action": ["s3:ListAllMyBuckets", "s3:GetBucketLocation"],
"Effect": "Allow",
"Resource": ["arn:AWS:s3:::*"]
}, {
"Sid": "AllowRootLevelList",
"Action": ["s3:ListBucket"],
"Effect": "Allow",
"Resource": ["arn:AWS:s3:::corp-fs-team"],
"Condition": {
"StringEquals": {
"s3:prefix": ["", "home/"],
"s3:delimiter": ["/"]
}
}
}, {
"Sid": "AllowListForUserPrefix",
"Action": ["s3:ListBucket"],
"Effect": "Allow",
"Resource": ["arn:AWS:s3:::corp-fs-team"],
"Condition": {
"StringLike": {
"s3:prefix": ["home/${AWS:username}/*"]
}
}
}, {
"Sid": "AllowUserFullAccessToUserPrefix",
"Action": ["s3:*"],
"Effect": "Allow",
"Resource": [
"arn:AWS:s3:::corp-fs-team/home/${AWS:username}",
"arn:AWS:s3:::corp-fs-team/home/${AWS:username}/*"
]
}
]
}
以及访问 pub 目录的 IAM policy:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowPublicLevelList",
"Action": ["s3:ListBucket"],
"Effect": "Allow",
"Resource": ["arn:AWS:s3:::corp-fs-team-bucket"],
"Condition": {
"StringLike": {
"s3:prefix": ["pub/*"]
}
}
}, {
"Sid": "AllowUserFullAccessToPublicPrefix",
"Action": ["s3:*"],
"Effect": "Allow",
"Resource": [
"arn:AWS:s3:::corp-fs-team-bucket/pub",
"arn:AWS:s3:::corp-fs-team-bucket/pub/*"
]
}
]
}
具体的 lambda 函数不在本文讨论的范围之内。
除此之外,我们还需要一个类似于 Dropbox 的客户端软件来监控本地目录(S3目录)的更改,以便在合适的时候进行同步。思路如下:
corp-fs-box/*
和 S3 bucket 的修改,并按上述规则同步。由于涉及的目录都是个人目录,不太会产生冲突(除非同一用户在多个 device 下载没有 sync 的前提下修改同一文件。所以在这里,为简单起见,我们可以不涉及到 diff / merge,简单遵循 last writer wins 进行处理就可以了。另外 S3 自带 versioning,也可以使能这一功能,保存历史版本,在冲突发生的时候,让用户选择。
S3 是一个非常强大的文件服务,如果使用得当,可以带来非常大的收益,建议大家多多深入研究。AWS 的很多服务,如 Elastic Beanstalk,Elastic Transcoder,CloudFormation 实际上都在使用 S3 作为服务的关键一环。