写点什么

AWS 系列:S3 不仅仅是存储

  • 2015-12-21
  • 本文字数:5195 字

    阅读完需:约 17 分钟

编者按:本文系 InfoQ 中文站向陈天的约稿,这是 AWS 系列文章的第二篇。以后会有更多文章刊出,但并无前后依赖的关系,每篇都自成一体。读者若要跟随文章来学习 AWS,应该至少注册了一个 AWS 账号,事先阅读过当期所介绍服务的简介,并在 AWS management console 中尝试使用过该服务。否则,阅读的效果不会太好。在这篇文章里,介绍了尝试用 S3 创建公司内部的文件服务器,保存员工私人 / 共享文件,并以类似 Dropbox 的方式双向同步。

S3 介绍

S3 是 AWS 最早发布的诸多服务之一,用作可信存储。所谓可信,AWS 给出的概念是:「在指定年度内为对象提供 99.999999999% 的持久性和高达 99.99% 的可用性」,换句话说就是任何存储于 S3 的数据基本不可能丢失,在一个年度内,不超过 1 小时(3153.6s)的宕机时间。除此之外,S3 还提供如下特性:

  • 跨区域复制:只需要简单的配置,存储于 S3 中的数据会自动复制到选定的不同区域中。当你的数据对象的收集分散在不同的区域,而处理集中在某些区域时非常有用。
  • 事件通知:当数据对象上传到 Amazon S3 中或从中删除的时候会发送事件通知。事件通知可使用 SQS 或 SNS 进行传送,也可以直接发送到 AWS Lambda 进行处理。比如说,上传到 S3 的图片的 resize。
  • 版本控制:数据对象可以启用版本控制,这样你就可以很方便地进行回滚。对于应用开发者来说,这是个特别有用的特性。
  • 加密:S3 的访问本身是支持 SSL(HTTPS)的,保证传输的安全,对于数据本身,你可以通过 Server side encryption(AES256)来加密存储在 S3 的数据。
  • 访问管理:通过 IAM/VPC 可以控制 S3 的访问粒度,你甚至可以控制一个 bucket(S3 对数据的管理单元,一个 bucket 类似于一组数据的根目录)里面的每个 folder,甚至每个文件的访问权限。
  • 可编程:可以使用 AWS SDK 进行客户端或者服务端的开发。
  • 成本监控和控制:S3 有几项管理和控制成本的功能,包括管理成本分配的添加存储桶标签和接收账单警报的 Amazon Cloud Watch 集成。
  • 灵活的存储选项:除了 S3 Standard,还有低成本的 Standard – Infrequent Access 选项可用于非频繁访问数据,存储的价格大概是 Standard 的 2/5。至于那些访问不了多少次的冷数据(如 1 年前的 Log),可以存储在 Glacier 中,价格在 Standard 的 1/4(1T $7/ 月),缺点是需要几个小时来恢复数据(估计是存放于离线的磁带中)。

基本用法

S3 的用户可以使用 AWS management console 来创建 bucket(类比文件系统的根目录),以及 bucket 内部的目录树,并上传文件,但这不是使用 S3 的最佳方式。日常的主要操作应该使用 AWS CLI 和 AWS SDK 完成。

AWS CLI

安装 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

AWS SDK 提供了对几乎所有主流语言的支持,在程序里使用 S3,一般的流程是:

  • 创建 AWS connection(这一步需要用到你的 access key)。
  • 使用 connection 创建 S3 对象。
  • 使用 S3 API 进行各种 API 操作,比如创建 bucket,上传文件等。

这里列一个 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 的一些典型使用场景如下:

  • 存储用户上传的文件,如头像,照片,视频等静态内容。
  • 当作一个的 key value store,承担简单的数据库服务功能。
  • 数据备份。
  • 静态网站的托管:你可以对一个 bucket 使能 Web Hosting。

我们简单介绍一下 S3 实现静态网站托管,然后以一个例子讲述如何使用 S3 实现一个能最大程度保证数据安全同时又价格低廉的团队内部的文件服务器。

使用 S3 实现安全的静态网站托管

经常使用 GitHub 的朋友对 GitHub pages 服务一定不会陌生,你只要把各种静态网站生成工具的生成的目标放入 gh-pages 的 branch,GitHub pages 就会帮你做静态网站的托管。得益于如今越来越强大的 JavaScript 和各种 API,静态网站其实早已脱离了展示 HTML 的基本范畴。

GitHub pages 有一个缺点就是,只要你使用,它就是开放的,无法变成一个私有网站,存放公司内部的私密文件。公司内部的一些私有内容,比如:

  • 使用 reveal.js 生成的 slides。
  • 使用 new relic 生成的各种嵌入式报表和图表。
  • 使用 JavaScript + AWS SDK 做的各种内部工具(由于 AWS SDK 提供了 JavaScript SDK,所以你可以用静态网站的方式访问数据库等服务,实现 server less 的效果)。

你无论如何都不会想将其暴露给外界。这个时候,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)。

使用 S3 实现文件服务器

很多公司都会为员工提供私人和共享的文件存储。比如作为一个用户,我可以把我的私人文件存放在: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 s3sync 来同步文件夹:
    • 对本地 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 目录)的更改,以便在合适的时候进行同步。思路如下:

  • 客户端软件做成一个开机启动的 daemon。
  • 随时监控本地目录 corp-fs-box/* 和 S3 bucket 的修改,并按上述规则同步。

由于涉及的目录都是个人目录,不太会产生冲突(除非同一用户在多个 device 下载没有 sync 的前提下修改同一文件。所以在这里,为简单起见,我们可以不涉及到 diff / merge,简单遵循 last writer wins 进行处理就可以了。另外 S3 自带 versioning,也可以使能这一功能,保存历史版本,在冲突发生的时候,让用户选择。

小结

S3 是一个非常强大的文件服务,如果使用得当,可以带来非常大的收益,建议大家多多深入研究。AWS 的很多服务,如 Elastic Beanstalk,Elastic Transcoder,CloudFormation 实际上都在使用 S3 作为服务的关键一环。


感谢魏星对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群(已满),InfoQ 读者交流群(#2))。

2015-12-21 17:1718677

评论

发布
暂无评论
发现更多内容

3.3 Go语言从入门到精通:包管理工具之Govendor

xcbeyond

Go 语言 4月日更 vendor

架构实战营-课后作业-模块1

Redis 集群

escray

redis 学习 极客时间 Redis 核心技术与实战 4月日更

GitHub开源城市结构公交路线数据可视化

不脱发的程序猿

GitHub 开源 智慧交通 4月日更 公交路线数据可视化

喂~ 办章吗?Python OpenCV 互联网+ 项目,图像处理取经之旅第 18 天

梦想橡皮擦

Python OpenCV 4月日更

与JVM做朋友系列(3)又见Class字节码

码界西柚

JVM X86 stack register

架构实战营 模块1 课后作业

唐高为

架构实战营

模块一作业

c

架构实战营

元数据管理—动态表单设计器在crudapi系统中完整实现

crudapi

API crud crudapi 动态表单 表单设计

大数据计算生态之数据计算(二)

小舰

4月日更

【LeetCode】森林中的兔子Java题解

Albert

算法 LeetCode 4月日更

与JVM做朋友系列(2)再见类加载器

码界西柚

JVM ClassLoader 类加载器

去面试,公司问我生辰八字。

yes

面试

Linux df命令

一个大红包

4月日更

「Android Binder」AIDL中的 in / out 到底是啥?

李小四

android aidl binder inout

近期某大厂的技术面试题及答案整理

程序员架构进阶

面试 28天写作 算法面经 线上问题 4月日更

与JVM做朋友系列(1)你好,Class字节码

码界西柚

JVM class bytecode 字节码

Flink TaskManager 内存模型详解

JasonLee实时计算

flink

Android面试你必须要知道的那些知识,重难点整理

欢喜学安卓

android 程序员 面试 移动开发

关于Webpack4 基础配置介绍

Chalk

Vue webpack 4月日更

树莓派第一天的各种坑

IT蜗壳-Tango

4月日更

架構設計訓練營作業1

海罗沃德

架构实战营

Vue3源码 | 读懂keep-alive组件以及缓存机制

梁龙先森

源码分析 大前端 Vue3

【LeetCode】最长公共子序列Java题解

Albert

算法 LeetCode 4月日更

路过春天

小天同学

思考 个人感悟 4月日更

Netty 核心源码解读 —— EventLoop 篇

松然聊技术

【译】JavaScript: 带你彻底搞懂 this

清秋

JavaScript 翻译 4月日更 this

Android性能优化之启动优化实战篇!架构师必备技能

欢喜学安卓

android 程序员 面试 移动开发

ARST- 日常打卡2

pjw

如何在Deno中使用 Node 模块?

Sakura

4月日更

「Android渲染」为什么alpha渲染性能低?

李小四

Android渲染 Alpha 渲染

AWS系列:S3不仅仅是存储_服务革新_陈天_InfoQ精选文章