使用nginx负载均衡+多个云的免费额度打造免费markdown图床

使用nginx负载均衡+多个云的免费额度打造免费markdown图床

一、前言

之前我博客图片是存在又拍云的,由于博客有很多gif图,并且我的gif图有些比较大,最近浏览的人也多,流量一天10几G,一天两三块钱,又拍云赠送的67元根本用不了多久,这样一来,又拍云所谓的每月免费10G存储空间+15G流量就变成了扯蛋,如果用其他云,免费流量迟早也会不够用,因为文章会越来越多,浏览的人也会越来越多,所以就想用多台对象存储服务器来分散流量,用nginx做负载均衡,把本来属于一台机的流量分散到多台,我目前是用了6个云,网易、七牛、腾讯、又拍、Ucloud、青云QingCloud,它们各自的免费额度如下。

网易云对象存储免费额度 (共50G免费存储空间,每月20G免费下载流量,平均每天682M,提工单问过,长期有效)——已不对个人用户开放。
网易云目前只有华东区可用,华北区是灰色,应该差不多就可以启用,不过我猜免费下载流量也是不同区域共享的。

七牛云对象存储免费额度(共10G免费存储空间,每月10G免费下载流量,平均每天341M,很多人用,应该也是长期有效)
七牛云不同区域免费额度是共享的。

腾讯云对象存储免费额度(共50G免费存储空间,每月10G免费下载流量,平均每天341M,提工单问了,是长期有效)—— 已修改规则,只提供半年的免费额度。
腾讯云不同地域的免费流量是共享的。

又拍云免费额度(共10G存储空间,每月15G流量,平均每天512M,长期有效,每年会送67元)
又拍云是使用赠送67元券的方式,比起以上纯免费的方式,这种方式有点坑,但还是可以使用的,注意千万别开启全球cdn,只开国内就好,因为国外cdn超贵。

Ucloud对象存储免费额度(共20G免费存储空间,每个区每月20G免费下载流量,目前我开了北上广三个区,加起来就有60G免费下载流量,香港和国外的不敢开,如果开了应该也是有免费20G的,但就怕什么时候超了,很贵,工单问了,是长期有效,好了,现在ucloud也没免费了,工单问了也没用,公司要调整,客服也没办法)
Ucloud记得在单地域空间里创建存储空间,单地域可以选不同地域,我在北、上、广分别开了一个空间,这样每个空间的都有自己的免费下载流量20G,就相当于有60G了。

Xnip2018-11-29_18-55-41.jpg

特别注意,Ucloud的域名,不能写cdn加速域名(就是.com.cn结尾的),而要写存储空间域名,否则用反代无法解析。

Xnip2018-11-30_00-23-11.jpg

青云QingCloud免费额度(共30G存储空间,每个区每月11G免费下载流量,每个用户可以建两个区的桶,比如我建了北京和广东两个区的,这样每个区分别就11G免费下载流量,unluckily,这个免费额度只有12个月,12个月后正常收费。)
QingCloud要先在右上角选择区域,然后在左侧面板中选择存储——对象存储,然后新建桶,新建桶后要记得右击——设置,然后在访问控制里添加一个所有用户可读权限,否则无法访问图片

Xnip2018-11-29_18-48-10.jpg

二、配置nginx图片服务器

方式一:使用反向代理方式

直接上代码,我博客上的图片,域名都是img.xiebruce.top,但实际上并不是我自己提供这个服务,而是在后台反向代理到真实服务器(即上面说的那些云)。

如代码所示,代码中有10个反向代理服务器(server)+1个负载均衡(upstream)模块组成:
– 由图片服务器提供一个图片域名img.xiebruce.top,该域名即为我博客中的图片所使用的域名。
– 图片服务器使用proxy_pass http://image_server;反向代理到负载均衡(upstream)模块。
– upstream再按权重把请求分配到9个图片反代服务器中的一个。
– 反代服务器再把upstream分配的请求反代到真正的某个云对象存储服务器,获取图片,再依次返回。

#图片反向代理服务器1-Qiniu
server {
    listen unix:/var/tmp/img_server1.sock;
    server_name localhost;
    error_log /data/wwwlogs/img_server1.error.log error;
    access_log /data/wwwlogs/img_server1.access.log combined;

    location / {
        proxy_pass http://img.img_server1.cn/;
    }
}

#图片反向代理服务器2-Upyun
server {
    listen unix:/var/tmp/img_server2.sock;
    server_name localhost;
    error_log /data/wwwlogs/img_server2.error.log error;
    access_log /data/wwwlogs/img_server2.access.log combined;

    location / {
        #proxy_pass http://image.qhjack.cn/;
        proxy_pass http://blog-markdown.test.upcdn.net/;
    }
}

#图片反向代理服务器3-Netease
server {
    listen unix:/var/tmp/img_server3.sock;
    server_name localhost;
    error_log /data/wwwlogs/img_server3.error.log error;
    access_log /data/wwwlogs/img_server3.access.log combined;

    location / {
        proxy_pass http://markdown-bucket.nos-eastchina1.126.net/;
    }
}

#图片反向代理服务器4-Tecent
server {
    listen unix:/var/tmp/img_server4.sock;
    server_name localhost;
    error_log /data/wwwlogs/img_server4.error.log error;
    access_log /data/wwwlogs/img_server4.access.log combined;

    location / {
        proxy_pass http://img_server1.cos.ap-guangzhou.myqcloud.com/;
    }
}

#图片反向代理服务器5-Ucloud
server {
    listen unix:/var/tmp/img_server5.sock;
    server_name localhost;
    error_log /data/wwwlogs/img_server5.error.log error;
    access_log /data/wwwlogs/img_server5.access.log combined;

    location / {
        proxy_pass http://markdown-blog.cn-gd.ufileos.com/;
    }
}

#图片反向代理服务器6-Ucloud-bj
server {
    listen unix:/var/tmp/img_server6.sock;
    server_name localhost;
    error_log /data/wwwlogs/img_server6.error.log error;
    access_log /data/wwwlogs/img_server6.access.log combined;

    location / {
        proxy_pass http://blog-markdown.cn-bj.ufileos.com/;
    }
}

#图片反向代理服务器7-Ucloud-sh2
server {
    listen unix:/var/tmp/img_server7.sock;
    server_name localhost;
    error_log /data/wwwlogs/img_server7.error.log error;
    access_log /data/wwwlogs/img_server7.access.log combined;

    location / {
        proxy_pass http://markdown-sh2.cn-sh2.ufileos.com/;
    }
}

#图片反向代理服务器8-QingCloud-gd2
server {
    listen unix:/var/tmp/img_server8.sock;
    server_name localhost;
    error_log /data/wwwlogs/img_server8.error.log error;
    access_log /data/wwwlogs/img_server8.access.log combined;

    location / {
        proxy_pass http://blog-markdown.gd2.qingstor.com/;
    }
}

#图片反向代理服务器9-QingCloud-bj3
server {
    listen unix:/var/tmp/img_server9.sock;
    server_name localhost;
    error_log /data/wwwlogs/img_server9.error.log error;
    access_log /data/wwwlogs/img_server9.access.log combined;

    location / {
        proxy_pass http://markdown.pek3b.qingstor.com/;
    }
}

#负载均衡
upstream image_server {
    #Qiniu => 每月10G免费下载流量,每天341M
    server unix:/var/tmp/img_server1.sock weight=7;
    #Upyun=>每月15G免费下载流量,每天512M
    server unix:/var/tmp/img_server2.sock weight=11;
    #Netease=>每月20G免费下载流量,每天682M
    server unix:/var/tmp/img_server3.sock weight=15;
    #Tecent=>每月10G免费下载流量,每天341M
    server unix:/var/tmp/img_server4.sock weight=7;
    #Ucloud-gz=>每月20G免费下载流量,每天682M
    server unix:/var/tmp/img_server5.sock weight=15;
    #Ucloud-bj=>每月20G免费下载流量,每天682M
    server unix:/var/tmp/img_server6.sock weight=15;
    #Ucloud-sh2=>每月20G免费下载流量,每天682M
    server unix:/var/tmp/img_server7.sock weight=15;
    #QingCloud-gd2=>每月11G免费下载流量,每天375M
    server unix:/var/tmp/img_server8.sock weight=8;
    #QingCloud-bj3=>每月11G免费下载流量,每天375M
    server unix:/var/tmp/img_server9.sock weight=8;
}

#图片服务器
server {
    listen 443;
    server_name img.xiebruce.top;
    error_log /data/wwwlogs/img.xiebruce.top.error.log error;
    access_log /data/wwwlogs/img.xiebruce.top.access.log combined;

    #https start
    ssl on;
    ssl_certificate /etc/letsencrypt/live/xiebruce.top/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/xiebruce.top/privkey.pem;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHellA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
    #https end

    #请求转发到image_server定义的服务器列表
    location / {
        proxy_pass http://image_server;
        proxy_set_header Host localhost;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        #代理缓存,STATIC是缓存的keys_zone名称,是在http模块下统一设置了(即在nginx.conf文件中)
        proxy_cache STATIC;
        proxy_cache_key $host$uri$is_args$args;
        proxy_cache_valid 200 304 10m;
    }
}

其中proxy_cache STATIC;中的STATIC是一个自定义名字,需要在http模块里设置:

http {
    # ……
    # 设置代理缓存路径
    proxy_cache_path /data/wwwcache/nginx levels=1:2 keys_zone=STATIC:200m inactive=24h max_size=1g;

    include vhost/*.conf;
}

关于反向代理缓存,具体请参考:Nginx配置反向代理缓存proxy_cache

方案二:使用return 302

其实就是把9个图片反代服务器的proxy_pass改成return 302的方式,比如把:

location / {
    proxy_pass http://markdown-bucket.nos-eastchina1.126.net/;
}

修改为:

location / {
    return 302 http://markdown-bucket.nos-eastchina1.126.net$uri;
}

其实就是把proxy_pass修改为return 302,然后把域名最后的/去掉,并添加上$uri即可。

其实用301也一样可以跳转,但301跳转会被浏览器或搜索引擎记住,无法做负载均衡,而302不会被记住。

return 302方式的优点: 会跳转到对应的对象存储服务商本身指定的链接上,这样能充分利用cdn的优点,也不会把所有流量都加到你自己的服务器上。

return 302方式的缺点: 有些服务商的https不免费,有些服务商提供的默认域名是非https的,如果你自己的站点是https,又混入非https的图片,会导致你https标识显示感叹号,有些服务器显示“不安全”之类的。


按照计算,我目前每天平均可用流量达到4.6G之多:
341+512+682+341+682+682+682+375+375=4672M

按流量大小,大致确定各个云的请求权重weight的大小:
341/4672=7.3% => weight=7
512/4672=11%=11 => weight=11
682/4672=14.6% => weight=15
375/4672=8% => weight=8

有人可能会有疑问,为什么要设置这么多图片反代服务器?为什么不直接把反代服务器中的地址直接放到负载均衡(upstream)模块里?而且实际上一般都是这么做的呀?

原因在于upsream里server关键字指定的一般是ip,但我现在是域名(没办法,真正的图片服务器不是自己的,访问图片用的就是域名),而请求域名,它会自动解析域名,也就是说upstream最终会请求域名对应的ip,而不是域名本身,而请求云服务器的ip是无法获取到图片的。

比如我的网易云有这张图片:

https://markdown-bucket.nos-eastchina1.126.net/2018/08/30/9b294b8c1bff144fc9912465ac774297.jpg

在我博客里是用这样的方式请求:

https://img.xiebruce.top/2018/08/30/9b294b8c1bff144fc9912465ac774297.jpg

假设upstream刚好把这个请求分配到了网易云,那么理论上upstream请求的地址是这样的:

https://markdown-bucket.nos-eastchina1.126.net/2018/08/30/9b294b8c1bff144fc9912465ac774297.jpg

然而如果你直接在upstream里写server markdown-bucket.nos-eastchina1.126.net,upstream实际请求的地址却是这样的:

http://220.181.29.232/2018/08/30/9b294b8c1bff144fc9912465ac774297.jpg

这就是我上面说的,如果直接把目的服务器域名写在upstream里,upstream最终请求的将会是该域名解析后的ip,而以ip开头的地址去访问云对象存储服务器中的图片,会被它拒绝,无法访问。所以,我才把upstream指向本地的设置的图片反代服务器,然后由本地的反代服务器再分别去反代到真实的云对象存储服务器,而直接用proxy_pass,是不会导致域名被解析为ip的,这样就能实现用多个反代服务器来做负载均衡,把本来对一个服务器的流量,分散到多台服务器中,这样每台服务器承受的流量就不会超过或者说比较不容易超过云服务器提供的免费下载流量额度以及get次数,如果哪个云使用量快超了,你还可以调低它的访问比重甚至把它关闭,能非常灵活的使用各浏览器的免费流量,另外由于我本地图片服务器是走unix socket而不是http,所以速度上还是比较快的!

而且,使用nginx反代的方式(即使只有一个云)可以避免以后换存储地址时,需要批量修改图片域名,我现在图片域名就统一用自己的,如果图片实际域名修改了,只需要修改反代的域名即可。

三、怎样做服务器迁移?

如果你以前已经用着某台云对象存储服务器了(比如七牛云对象存储),你看到了我这个文章也想用我这种架构,于是把网易、腾讯、又拍、Ucloud、QingCloud五个云全注册了,然后使用它们的对象存储服务来存储图片,怎样把你原来服务器上的图片迁移到这五个云上呢?因为负载均衡方式要求6台云对象存储服务器都有同样的数据,以达到改一个域名就能把请求切换到另一个云的效果。
关于服务器数据迁移,请看这篇文章:七牛/腾讯/网易云/Ucloud/QingCloud对象存储数据迁移(批量上传/下载)

四、每次上传图片要传多个云太麻烦?

别急,有两种方法
方法一:使用我自己开发的图床工具:PicUploader,只要你把云服务器注册好,配置好相关参数,你想一次上传多少个云都可以。
方法二:只上传一个云服务器(我把它叫源服务器),Windows系统可用mpic,其他云服务器使用镜像回源功能,关于这个,请看这篇文章:七牛/腾讯/网易云/Ucloud/QingCloud对象存储数据迁移(批量上传/下载),缺点,如果负载均衡分配到请求非源服务器,第一次请求的图片都需要走源服务器抓取,会造成源服务器下载流量增大及get次数增大,另外,首次请求的图片如果被分配到非源服务器,速度也会稍慢一点。

五、这么做的优缺点

方案一:反向代理方式

优点:
– 充分利用免费资源,说白了就是省钱,省钱才是硬道理哈哈!
– 节省服务器空间(对于小空间的小鸡服务器最合适,毕竟图片这种东西日积月累会越来越多);
– 统一图片域名,服务器有变动,直接改nginx反向代理的地址即可,文章中的链接不需要替换;
– 假设你自己的网站/博客要换服务器部署,无需理会图片资源(因为如果是直接上传,一般都会在自己博客的uploads目录下,如果日积月累有个好几G,说实话服务器间转移起来也麻烦);
– 数据更安全,因为都是有账号的,什么时候要整体迁移什么的,图片都在自己手上,如果用微博图床之类的毕竟图片不在自己手上,总有点担心丢失问题;
– 把nginx反向代理配置练习了一把,学到一个技术,有可能之前你一直没玩过nginx反向代理怎么配置;

缺点:
– 全部走自己服务器,失去了cdn优势,如果服务器在国外,在国内访问的话,相当于图片从国内走到国外又回到国内,绕一大圈,速度变慢。
– 流量全部走自己服务器,并且是双倍流量(服务器先从cdn获取图片,是一个上行流量,服务器再把图片传给客户端,又是一个下行流量),但因为有缓存,所以上行流量其实是隔一段时间才有一次(比如缓存有效期是一天,则一天才会有一次上行流量)

关于失去cdn优势:由于我这么做的主要目的是为了节省我小鸡服务器的存储空间,所以并不在乎cdn,根据实际使用体验,图片加载并不会很慢,是可以接受的,因为我反向代理是做了缓存的。

关于流量全部走自己服务器:由于我是国外服务器,一个月1T流量,说实话,我一个月能剩八九百G,所以流量方面确实不是问题(也许是我博客访问量太小吧哈哈),而且我也不清楚服务器流量是不是把上行和下行加在一起算的,就算是加在一起算,我的流量确实剩很多,所以这个倒是无需要担心,当然如果你服务器是按流量计费的,那就要看具体流量了,也许会省了空间但省不了流量。

方案二:return 302方式

优点:
– 流量直走cdn,不走服务器,可充分利用cdn的加速功能
– 不使用图片服务器流量,对于按流量计费的服务器很有用。

缺点:
– 有些云服务器的免费额度不支持https,要用https就要收费,比如七牛云:
screenshot_upload_tmp.jpeg

除了七牛,很多云默认域名都不支持https,百度、阿里、UCloud、QingCloud的默认域名都不支持https。
– 如果302的目标服务器不支持https,会导致https安全标识(浏览器地址栏边上的“小锁”)不显示,取而代之的是“不安全的显示”:
谷歌浏览器显示不安全:
Xnip2019-04-12_18-38-13.jpg

火狐浏览器显示不安全:
Xnip2019-04-12_19-00-25.jpg

打赏
订阅评论
提醒
guest

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

32 评论
内联反馈
查看所有评论
菜鸟xH
菜鸟xH
2 年 前

UCLOUD的免费额度已经在十月份没有了

hello world
hello world
2 年 前

楼主,如果存储空间需要500G,是不是你这种方式还适用?因为每个存储空间都需要一份元数据,如果多个云平台,虽然宽带降下去了,但是存储费用又上来了!烦请解答

zhang fei
zhang fei
3 年 前

好文 收藏

anno
anno
3 年 前

其实应该用 307 而不是 302

yaoyue
yaoyue
4 年 前

在吗大佬~

yaoyue
yaoyue
4 年 前

博主您好,对反向代理流量不太明白,我的理解是:
1. 用户请求负载均衡服务器地址
2. 服务器返回真实图床链接给用户
3. 用户请求真实图床链接得到图片
这样的话,图片流量只在用户与真实图床服务器之间产生,负载均衡服务器只做了重定向的作用,而非进行图片内容中转。

可能我想的是重定向,与反向代理两码事,看了您与快乐人儿的评论,貌似是讨论类似的问题,不过还是没明白为什么不这做,

ps:博客的评论功能有点闹心,每次翻页都要刷新页面,对长文章体验不太友好。

yaoyue
yaoyue
4 年 前
回复给  xiebruce

明白了,301和302说的就是重定向,没转过弯来,另外您在文中不是说302临时重定向不会被浏览器「记住」吗?

aoinokodou
aoinokodou
4 年 前

博主你好,对于ucloud免费流量我有个疑问,他账单是每天出一次的,也就是说每月20g实际上是按每天682mb计算的吗?

zhangwei
zhangwei
4 年 前

Ucloud现在好像不免费了。

aoinokodou
aoinokodou
4 年 前
回复给  zhangwei

ucloud我刚刚才注册,还是每月20g免费流量啊?

buvidcai
4 年 前

对于反向代理方式存在疑问,按博主的说法,图片从服务器走了两遍,那么对于带宽的负荷应该很大吧。

buvidcai
4 年 前
回复给  xiebruce

不,我不是说流量。我说的是带宽,带宽就是,就是。。。
服务器带宽指的是在固定的单位时间内,能够通过的最大数据,这个跟高速公路上的车道是一样的道理,带宽就相当于是车道,带宽越大就相对于是并排通过的车辆也多。 ——来自百度
图片流量从服务器走了两遍,加载一个页面的流量变多,对于带宽的占用也就多了吧,用户会不会很卡之类的。

buvidcai
4 年 前
回复给  xiebruce

请问,如果使用js直接修改图片的链接又是如何呢?

快乐人儿
快乐人儿
5 年 前

博主把nginx给用错了
像你proxy_pass用在你的图床代码里面就是多此一举,甚至比图片直接放在你自己的服务器上还要糟糕..

proxy_pass原理是反向代理,意思就是你的服务器充当了中转服务

请求图片走的是你服务器流量,proxy_pass再服务器再去走一次其他云存储的流量
用户访问一次服务器就走了最少两次,甚至多次,多少个云存储就+1..因为每次访问都要去云存储获取图片

如果图片直接放你服务器上提供服务的话就是:用户访问一次服务器就走一次流量而已…

你想真正达到免费额度图床就应该做跳转方式

http://img/1.jpg
服务器直接返回 这样就只用了1次服务器流量

http://img/1.jpg 跳转 http://qiniu/1.jpg 这样才是正确用法…
这样就只用了服务器的跳转流量

http://img/1.jpg proxy_pass http://qiniu/1.jpg
这样就用了服务器proxy_pass云存储获取+返回给用户的流量 = 双倍流量

快乐人儿
快乐人儿
5 年 前
回复给  xiebruce

#图片服务器nginx配置文件中的
location / {
#proxy_pass http://image_server;
#proxy_set_header Host localhost;
#proxy_set_header X-Real-IP $remote_addr;
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

rewrite  "^/(.*)$"  http://image_server$1 break;
}

#last 本条规则匹配完成后继续向下匹配新的location URI规则
#break 本条规则匹配完成后终止,不在匹配任何规则
#redirect 返回302临时重定向
#permanent 返回301永久重定向

#last 和proxy_pass 类似,都是代理中转,其他几种都可以,一般都是用302跳转,详细可以百度一下nginx重定向

#负载均衡
upstream image_server {
#Qiniu => 每月10G免费下载流量,每天341M

#server unix:/var/tmp/img_server1.sock weight=7;
#这里的各个云存储直接写地址就行了
server http://img.img_server1.cn/ weight=7;

#Upyun=>每月15G免费下载流量,每天512M
server unix:/var/tmp/img_server2.sock weight=11;
#Netease=>每月20G免费下载流量,每天682M
server unix:/var/tmp/img_server3.sock weight=15;
#Tecent=>每月10G免费下载流量,每天341M
server unix:/var/tmp/img_server4.sock weight=7;
#Ucloud-gz=>每月20G免费下载流量,每天682M
server unix:/var/tmp/img_server5.sock weight=15;
#Ucloud-bj=>每月20G免费下载流量,每天682M
server unix:/var/tmp/img_server6.sock weight=15;
#Ucloud-sh2=>每月20G免费下载流量,每天682M
server unix:/var/tmp/img_server7.sock weight=15;
#QingCloud-gd2=>每月11G免费下载流量,每天375M
server unix:/var/tmp/img_server8.sock weight=8;
#QingCloud-bj3=>每月11G免费下载流量,每天375M
server unix:/var/tmp/img_server9.sock weight=8;

}

代码自行测试

快乐人儿
快乐人儿
5 年 前
回复给  xiebruce

负载均衡我没用过所以不清楚,但要是没有负载均衡的代码单单跳转是没问题的,对了负载均衡的配置代码有没有顺序之分?试试放在最前面

快乐人儿
快乐人儿
5 年 前
回复给  xiebruce

也不一定非要在nginx实现,用php写个轮流跳转也方便,需要哪个权重高就插多一次地址进去轮流就行

32
0
希望看到您的想法,请您发表评论x

扫码在手机查看
iPhone请用自带相机扫
安卓用UC/QQ浏览器扫

使用nginx负载均衡+多个云的免费额度打造免费markdown图床