缘起

Open edX跟S3有许多整合,包括日志的存储,成绩单的存储,静态文件的存储和加速等,当然需要开启才被采用.亚马逊的服务真是业界标杆式的存在,一旦用过,便曾经沧海。

可惜我们与S3无缘。

后来想做edxapp负载均衡的时候,又想到这个问题,edxapp是有状态的,主要是涉及静态文件的时候,所以想用s3的开源实现,来替代S3,如此一来可以让代码的修改降到最低。把静态文件挪到云中,edxapp就无状态了

github一番,也没找到特别合适的,所以用了七牛来替代,七牛的直传功能很不错,而且有各个平台的sdk!

S3的开源实现

fake-s3

The goal of Fake S3 is to minimize runtime dependencies and be more of a development tool to test S3 calls in your code rather than a production server looking to duplicate S3 functionality. Trying RiakCS, ParkPlace/Boardwalk, or Ceph might be a place to start if that is your goal.

由此可知,fake-s3适合看做一个开发测试环境,而不是生产可用的环境,类s3的生产系统,可以试试:RiakCS, ParkPlace/Boardwalk, or Ceph

minio

Minio is an object storage server compatible with Amazon S3

minio

minio是什么

Unlike databases, Minio stores objects such as photos, videos, log files, backups, container / VM images and so on. Minio is best suited for storing blobs of information ranging from KBs to 5 TBs each. In a simplistic sense, it is like a FTP server with a simple get / put API over HTTP.

minio是Go实现的一个类s3服务,和大多go项目一样,干净小巧,没有依赖,整个就一可运行。便利之极

不足

目前minio还很年轻,使用过程我遇到过一些bug

  • web端没有用户概念,当然可以采用sdk来自行构建,稍后我会演示python的例子
  • 上传的文件不完整(损坏)
  • 提示上传成功后,却不予显示

构建外部存储系统的实践

Object storage in practice: Creating a reliable data store

这篇文章讨论了这样一种思路, 把存储任务分离出去,只留下 putObject()和 getObject()接口

files sit in the object store, metadata goes to the database

不过这里没有用到直传

安装与启动

过程简单至极

minio

配置

Minio Server Configuration Files Guide

存储结构

就是原始目录,很好地与其他整合(诸如nginx)

可能的场景

  • 作为内容管理平台是完备的
  • 作为用户级别的资源管理平台(诸如视频的管理平台,播放使用nginx),还有些问题
  • 没有用户级别的权限
  • 没有用户系统
  • 可能的出路 * 期待之后有用户系统,可以对接到cas/oauth2 * 在nginx这里加一层?
  • 也许你期待的云盘,可以试试seafile和owncloud
  • 作为抽象存储服务,采用sdk才做这个服务,可以想象为局域网中的七牛,不足是没有webhook(callback)

client/sdk

采用s3客户端

有没有可能采用s3的生态链,诸如上传前端

s3cmd

sudo pip install s3cmd

创建~/.s3cfg

1
2
3
4
5
6
host_base = http://s3.just4fun.site/
host_bucket = http://s3.just4fun.site/
access_key = xxx
secret_key = xxx
signature_v2 = False
bucket_location = us-east-1
1
2
3
4
s3cmd mb s3://www.just4fun.com  #create
s3cmd ls
s3cmd ls s3://www.just4fun.com 
s3cmd put some-file.xml s3://www.just4fun.com /somefile.xml

awscli

pip install awscli

文档:

1
2
3
4
5
6
7
8
aws configure //配置文件位置在哪里 ~/.aws/
aws configure set default.s3.signature_version s3v4
aws --endpoint-url http://s3.just4fun.site s3 ls
aws --endpoint-url http://s3.just4fun.site s3 ls  //list your buckets
aws --endpoint-url http://s3.just4fun.site s3 ls  s3://edx  // list contents inside bucket
aws --endpoint-url http://s3.just4fun.site  s3 mb s3://mybucket   // make a bucket
aws --endpoint-url http://s3.just4fun.site s3 cp /tmp/aa.html s3://mybucket //add an object to a bucket
aws  s3api list-objects --bucket edx --query Contents[].[Key,Size] --endpoint-url http://s3.just4fun.site  //列出文件大小

前端上传:fine-uploader

  • demo:是否支持回调 类似七牛 s3支持,minio不确定

前端通过凭证操作,这是后端sdk构建的,和存储层无关

boto

edx是用boto,不过boto和s3耦合,不容易替换为s3开源实现

1
2
3
4
from boto.s3.connection import S3Connection  #可查看细节,可以不连默认host
key = "xxx" 
secret = "xxx"
conn = S3Connection(key, secret, host='s3.just4fun.site', is_secure=False)

可以试试这里


2016.09.18更新

How to use AWS SDK for Python with Minio Server给出了boto3中可以如何十一minio替代s3


构建带有身份的存储系统

  • minio仅仅视为存储层
  • 采用Presigned Operations,控制细粒度的存储,上传凭证是核心所在
  • 之后采用通用的上传前端

凭证相关:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from datetime import datetime, timedelta

from minio import Minio
from minio import PostPolicy
from minio.error import ResponseError

post_policy = PostPolicy()
# set bucket name location for uploads.
post_policy.set_bucket_name('test')
# set key prefix for all incoming uploads.
post_policy.set_key_startswith('test.html')
# set content length for incoming uploads.
post_policy.set_content_length_range(10, 1024)

# set expiry 10 days into future.
expires_date = datetime.utcnow()+timedelta(days=10)
post_policy.set_expires(expires_date)

client = Minio('s3.just4fun.site', # 默认采用的是https,否则报错
               access_key='AN0GSB29FGW7UXQ6WIEM',
               secret_key='Wyyzr8/D5lEOoCfZymID4ZG/PNhb6th7YAdVT/Zs')

try:
    url_str, signed_form_data = client.presigned_post_policy(post_policy)
    print(url_str, signed_form_data)
    curl_str = 'curl -X POST {0}'.format(url_str)
    curl_cmd = [curl_str]
    for field in signed_form_data:
        curl_cmd.append('-F {0}={1}'.format(field, signed_form_data[field]))

        # print curl command to upload files.
        curl_cmd.append('-F file=@/tmp/aa.html')
        print(' '.join(curl_cmd))
except ResponseError as err:
    print(err)

问题

支持多用户吗?

ACCESSKEYID是全局只有一个?无法天然支持多client?需要在应用逻辑里做?

可以使用这个机制:Upload files from browser using pre-signed URLs,类似七牛

参考