分类目录归档:S3

S3 bucket Cross-Region Replication

s3 bucket现在支持跨region的自动备份,设置好之后,在A region的bucket里面上传内容,会自动被复制到B region的设定bucket里面.

设置基本方法:

  1. 在源bucket上点右键Properties属性页可以看到Cross-Region Replication设定区域
  2. 目前只能给每个bucket设置一个repication bucket
  3. 开启cross-region replication的两个bucket自动会被设置versioning功能
  4. 可以只对源bucket下的某个目录设置跨区备份(设置prefix)
  5. 设置之后新上传的内容会被自动复制到备份bucket,但是之前上传的不会被复制过去
  6. 对象被上传到源bucket之后,并不是立马在备份bucket中就能看到,他是aws复制过去的,所以有延时
  7. 数据从源bucket中删除后,备份bucket中的对象也会被删除
  8. 但是在备份bucket中进行上传和删除操作,不会应用到源bucket,也就是说这个是单向的行为
  9. 设置过程中会自动创建一个IAM角色,用于向备份bucket复制数据,上传操作只是上传到源bucket,之后的备份是aws的功能后台实现
  10. 既然是IAM角色控制的,跨账号的bucket备份也是可以的,跨账号设置需要用命令行做,参考官方文档http://docs.aws.amazon.com/zh_cn/AmazonS3/latest/dev/crr-walkthrough-2.html

需要注意的地方:

  1. replication操作是有延时的,所以需要做好预期,做用户上传的数据自动备份到别的region还是不错的,需要数据实时同步实时可见的,需要哦慎重考虑延时问题
  2. A自动备份到B,再设置B自动备份到C,然后A中上传,自动备份到B和C行不行? 经测试不行!A中上传的会自动备份到B,但是备份的数据不会自动到C,在B中主动上传的会被自动备份到C,不知以后会不会改进

S3文件的时区和时间

好多人问S3上文件的时区和时间这类问题,今天总结下:

简单说就是不同工具不同客户端看到的时间是不同的:
例如s3cmd 显示的就是UTC时间,而awscli 显示的就是根据客户端时区换算之后的时间,CloudBerry客户端也显示的是换算之后的时间。

(1)bucket的时间是bucket的创建时间,object时间是它被上传的时间(cloudberry显示成‘修改时间’);s3cmd上传后,再用s3cmd查看,显示的是上传时间对应的UTC时间,但用awscli查看,就能以本地时间显示。所以上传是的时间就是上传那个时刻的时间,不过不同客户端会以不同时区显示而已。
(2)object被下载之后,就出现问题了。s3cmd get下来的文件的时间会显示用s3cmd ls看到的时间,也就是UTC时间;而aws s3 cp下来的时间,也是使用aws s3 ls看到的时间,即本地时间。这样使用两种客户端下载通一个文件,查看下载后的文件的时间将会有两种不同结果(awscli的更准确些,s3cmd下载保存文件是时间没换算)。
(3)使用s3cmd上传大文件时,会被切割成15MB每个分片上传再组装,使用s3cmd获取到的md5sum并不是文件的真实MD5值,这点需要注意,下载大文件的时候会显示md5sum does not match,使用awscli时无此问题。

关于什么时候用哪种工具的问题:
(1)s3cmd上传和下载速度都比较适中,并未充分占满实例的带宽,因此比较适合做小文件或者较高频率的文件上传下载和sync。但是它上传几个GB的单个文件时,可靠性就差一下,有时会失败报错。
(2)awscli工具上传大文件的时候能充分利用网络性能,基本能跑满实例的带宽,上传下载速度特别快,适合在非线上服或者非高峰时间上传单个大文件,优点是速度非常快,缺点是会耗尽实例带宽,还没找到限速方法。
(3)在用到sync功能时,由于s3cmd的MD5问题,同步包含大文件时有时会有报错,建议包含大文件使用aws s3 sync;只有小文件时s3cmd sync或者aws s3 sync都行。

用S3做资源下载和P2P种子

有时候我们想分享个资源给别人,但是放在线上业务的Nginx上也不太合适,这是可以上传到S3,然后把object设置ACL为public-read,然后拼装下下载的url就可以提供给任何人下载,同时,bucket可以设置lifecycle,可以设置该bucket下的某个目录下的资源的生存天数,到期后自动被删除。

上传S3的部分就不说了,详见这个页面(python版的)
http://boto.readthedocs.org/en/latest/s3_tut.html#storing-large-data

然后我们可以设置ACL给object,然后拼出下载的url和torrent的url

import boto
c = boto.connect_s3()
b = c.get_bucket(bucket_name)
b.set_acl('public-read', object_key)
object_download_url = "http://" + bucket_name + ".s3.amazonaws.com/" + object_key
object_torrent_url = object_download_url + "?torrent"

这个S3的下载url后面加上?torrent就可以以P2P协议下载,直接点这个url会下载下来一个BT种子。

使用HTTP POST在浏览器上传文件至S3

我们可以在http页面中构造一个预先授权的http post表单,实现在浏览器端使用POST方式上传文件到S3。表单内容如下:

<html> 
  <head>
    <title>S3 POST Form</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>

  <body> 
    <form action="https://cypay-test.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
      <input type="hidden" name="key" value="upload/${filename}">
      <input type="hidden" name="AWSAccessKeyId" value="xxxxxxxx"> 
      <input type="hidden" name="acl" value="private"> 
      <input type="hidden" name="success_action_redirect" value="http://your-success-page">
      <input type="hidden" name="policy" value="xxxx">
      <input type="hidden" name="signature" value="xxxx">
      <input type="hidden" name="Content-Type" value="image/jpeg">
      <!-- Include any additional input fields here -->

      File to upload to S3: 
      <input name="file" type="file"> 
      <br> 
      <input type="submit" value="Upload File to S3"> 
    </form> 
  </body>
</html>

其中,value="upload/${filename}"为目的地址,目录会自动创建
name="success_action_redirect" value="http://your-success-page",上传成功之后的跳转页面
name="policy" value="xxxx"base64加密的policy字符串
name="signature" value="xxxx",用SECRET_KEY使用base64加密的签名
加密算法如下python版:

import base64
import hmac, hashlib

AWS_SECRET_ACCESS_KEY = r"xxxxxx"
policy_document ='''
{
  "expiration": "2020-01-01T00:00:00Z",
  "conditions": [ 
    {"bucket": "your-bucket"}, 
    ["starts-with", "$key", ""],
    {"acl": "private"},
    {"success_action_redirect": "http://your-success-page"},
    ["starts-with", "$Content-Type", ""],
    ["content-length-range", 0, 1048576000]
  ]
}
'''
policy = base64.b64encode(policy_document)
signature = base64.b64encode(hmac.new(AWS_SECRET_ACCESS_KEY, policy, hashlib.sha1).digest())
print policy
print signature

设置IAM用户权限允许访问特定S3的bucket或者目录

通过设置IAM用户权限,可以限制IAM用户访问特定的S3 bucket或者目录的权限。

(1) case1:只允许IAM用户通过API或者s3cmd命令行工具访问特定S3 bucket
{
  "Statement": [
    {
       "Effect": "Allow",
       "Action": ["s3:ListBucket" ],
       "Resource": [ "arn:aws:s3:::mod-backup"]
    },
    {
        "Effect": "Allow",
        "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
        "Resource": [ "arn:aws:s3:::mod-backup/*"]
    }
  ]
}

这样虽然直接 s3cmd ls会报Access Deny,但是s3cmd ls s3://mod-backup还是可以看到的

(2) case2: 使用CloudBerry这类图形化工具,只允许用户访问特定的S3bucket或者特定目录
IAM user的授权跟API方式的一样
{
  "Statement": [
    {
       "Effect": "Allow",
       "Action": ["s3:ListBucket" ],
       "Resource": [ "arn:aws:s3:::mod-backup"]
    },
    {
        "Effect": "Allow",
        "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
        "Resource": [ "arn:aws:s3:::mod-backup/*"]
    }
  ]
}
在配置CloudBerry的时候,通过配置External Bucket来只显示该Bucket或者某个目录
<1>配置整个bucket是External Bucket填写bucket name,例如mod-backup
<2>若只有改Bucket下某个目录的权限,就填写mod-backup/wangfei
(3) case3: 允许IAM用户通过AWS console(web管理页面)访问且仅能访问特定S3 bucket
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetBucketLocation", "s3:ListAllMyBuckets"],
      "Resource": "arn:aws:s3:::*"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket" ],
      "Resource": [ "arn:aws:s3:::mod-backup"]
    },
    {
      "Effect": "Allow",
      "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
      "Resource": [ "arn:aws:s3:::mod-backup/*"]
    }
  ]
}

这样IAM用户只能读写mod-backup这个bucket,但是又个问题,虽然其他的bucket无权限访问到,但是在bucket列表中依然能看到,但看不到里面的内容。修改成

     "Action": ["s3:GetBucketLocation", "s3:ListAllMyBuckets"],
      "Resource": "arn:aws:s3:::mod-backup"

这样也不行,这样什么bucket都列举不出来,因为s3:GetBucketLocation这个api操作的对象只能是AllBucket。