zzxworld

配置 Nginx 反代实现 OSS 内网访问

手头有个项目的磁盘占用空间日益见涨,已经逼近 90% 的警戒线。登上去排查了下,一半都是被图片给占用了。既然是图片,我琢磨着是不是可以把这些文件以及相关功能都迁移到云存储上去。服务器是阿里云的,所以这是一份 OSS 的折腾记录。

选择同区域 OSS

阿里云的 OSS 收费项分的很细,存储是存储的,流量是流量的。存储费按使用的容量来计算,用多少收多少,这个很直观,也比较好掌控。流量费就不好说了,图片一不小心被盗链,或是其他原因流量突然暴增,会带来一些预料之外的开支。

好在 OSS 提供了一个比较良心的选择:内网流量免费。通过这个规则,我只需要让访问 OSS 的流量都走内网,就不用担心流量方面的开支了。那么问题来了:如何打通 OSS 的内网访问流量?

两个前提:

  1. 阿里云服务器 1 个。
  2. 同区域的 OSS 1 个。

注意我加粗了的「同区域」。举个例子,如果服务器在杭州,OSS 买到了深圳,按照 OSS 的收费规则,这是跨区域,存在流量费用。如果非要这么安排,但又想减省一点流量费,那就只有在 OSS 所在区域再买一台服务器,然后再通过公网流量连接另一台服务器。这还是会有点得不偿失,因为阿里云加的服务器也不便宜。

Nginx 代理配置

准备好了同区域的服务器和 OSS 后,接下来就是找个软件,让服务器转发一下访问 OSS 文件的请求。这里我用的是 Nginx。在 Nginx 上要实现这个功能只需要在 server 区域中添加下面几行代理配置:

location /upload/ {
    proxy_pass http://test.oss-cn-hangzhou-internal.aliyuncs.com/;
    proxy_hide_header Content-Disposition;
}

注意上面配置中的 test 要替换为自己在 OSS 创建的 Bucket 名称。通过浏览器访问 OSS 上的图片文件时,默认是下载的形式,我希望直接在浏览器显示,proxy_hide_header Content-Disposition; 就是这个用途。

完成以上配置后重启 Nginx,通过服务器的地址 + /upload/ + OSS 上存在的文件名,就能走内网方式访问 OSS 的文件了。

这一步可能会碰到 OSS 输出 AccessDenied 类似的错误,如果错误消息中有类似 ACL 之类的字眼,可以检查一下创建的 Bucket 是不是「私有」级别权限。这个权限安全级别较高,需要请求中附带一些认证字段才能正常访问。那相应的也需要特殊版本的 Nginx 配合相关插件才能搞定。我不想这么麻烦,所以需要把 Bucket 的 ACL 权限设置为「公共读」。

禁止 OSS 直访

在使用了「公共读」的权限后,就违背了折腾的初衷。因为这意味着 OSS 上的文件都有了一个公网可访问的地址,这是一个会导致 OSS 流量费的隐患,所以需要堵住这个入口。

OSS 支持通过 Referer 方式设置防盗链功能。通过此功能设置一个 IP 白名单,内容就是服务器的访问地址,然后完善一下刚才的 Nginx 代码:

location /upload/ {
    proxy_pass http://test.oss-cn-hangzhou-internal.aliyuncs.com/;
    proxy_hide_header Content-Disposition;
    proxy_set_header referer $host;
}

这样就只能通过服务器的请求来访问 OSS 的文件了。

使用 OSS 缩略图功能

OSS 自带了缩略图功能,但使用的方式有点特别。是通过在文件地址后面添加处理参数的方式来操作的。类似于这样:

/upload/1.jpg?x-oss-process=image/resize,m_fill,w_100,h_100

这会输出长宽都是 100 像素的小图。不可否认,这种方式用起来是很方便,但就像我在开头所说的,OSS 的收费项很细,这个图片处理也会产生费用。如果让这个功能直接暴露出来,一样是一个费用隐患。所以我想把这个功能包装起来,让用法看起来如下:

/upload/1-100.jpg

这要用到 Nginx 的 Rewrite 功能。调整后的 Nginx 配置如下:

location /upload/ {
    rewrite /(.*)-100.(.*) /$1.$2?x-oss-process=image/resize,m_fill,w_100,h_100  break;

    proxy_pass http://test.oss-cn-hangzhou-internal.aliyuncs.com/;
    proxy_hide_header Content-Disposition;
    proxy_set_header referer $host;
}

这样缩略图的处理就在自己掌控之中了。