【NGINX】配置反向代理的“CDN”同款缓存

发布于 / 信息应用 / 4 条评论

说到CDN,核心无非就是“缓存”和“加速”两个功能。主流的大规模CDN大多基于Apache Trafffic Server,属于是博主高攀不起的知识盲区。在个人能够配置的小规模CDN场景中,cdnflygoedge是大家比较喜欢的。但是这些工具在更低的需求面前,最致命的缺点就是不够轻量,在单节点使用中(亦或直接称之为反向代理)宛如用高射炮打蚊子一般。

NGINX除了作为最常用的Web Server,也是一个高性能的反向代理工具。事实上,早年的CloudFlare和当下的GCORE都是基于NGINX开发的CDN系统,只是在一键脚本盛行的今天很少有人再去关注它与实现这些CDN功能的联系。上一篇文章提到糖哥送了博主一台5M带宽的腾讯云无忧轻量,正好借此机会对博主的GravatarJSdelivr等资源使用的集中反代api.lty.fun进行一次彻底的改造,顺便也聊聊NGINX反向代理中的一些小技巧。


一、Zone规则

NGINX的http段位于nginx.conf(即NGINX管理-配置修改)文件中,将该参数添加在下方中括号{后的适当位置即可。在这里博主还是建议一站一规则,vhsot.conf文件(即站点修改-配置文件)是include到http段下的,可以如图添加在vhost配置中server段之前,这样较为清晰明了。考虑到当前的受众演示内容的解释以宝塔为例,对NGINX较为熟悉的朋友动动手很容易就能找到。

以下是关于该段参数的解释:

proxy_cache_path:NGINX缓存的储存位置,无需自行新建该文件夹,但不可与其他缓存配置的路径重复,清理时可直接删除该目录或使用第三方nginx_cache_purge模块。
levels=1:2:缓存目录设置,第一级目录名1 byte(A-Z & 0-9)、第二级目录名2 byte(如AA、A5等)。该参数会间接影响缓存读取的性能,当缓存文件数过多时可以适当提升至1:32:3等。
keys_zone=cache_api:键值区域,以cache_api为例可自行命名,是用于反向代理配置中调用的唯一值。
10m:用于缓存响应状态的临时空间,一般大小设置为10m
inactive=100y:不活跃过期时间,当缓存超过该期限没有命中访问时,该缓存会过期以减少占用。可设置为1y1d30m等,若不想让其过期,可以设置为一个极大值如100y
max_size=1g:该区域最大占用的缓存大小,可设置为100m1g等。

在这里可以看到在缓存全局的配置中有inactive参数控制不活跃的缓存过期时间,边缘节点上的该参数是很多小型站点缓存命中率难以提高的重要原因。大规模的CDN(比如腾讯云、阿里云等)边缘节点众多,小体量的博客很难一直维持边缘节点上的有效缓存。也有一个误区是“套上CDN一定就快”,其实不然。以国内腾讯云的北上广BGP为例,在不触及带宽上限的情况下其全国访问性能是明显好于托管在二三线城市的CDN运营商节点的。这方面也非常清晰地体现在成本上,北上广的BGP带宽均价在100/M以上,是边缘节点带宽价格的20倍。

腾讯轻量提供了一个较为廉价的机会让我们接触这样的网络,也为各种需求配备了充裕的流量包。合理利用3-5M带宽能做的事还是很多的,我的博客日PV在400左右、CDN流量约1G,报表回馈的实际峰值带宽很少超过2Mbps。


二、目录缓存

反向代理规则是添加在vhost.confserver段下的,宝塔做了一个include到proxy.conf的文件夹,所以可以直接编辑反向代理页面的配置文件。对整站的反向代理加速一般第一级需要配置一个针对/的全局代理,此处默认不做缓存较为合理,将缓存交由后面更为细致的目录、后缀配置中。本文中的所有示例均以104.20.20.20为服务器、cdnjs.cloudflare.net为反代的目标站点、static.luotianyi.vc为访问站点,请根据自己的实际情况进行修改。

针对目录的缓存以Gravatar为例,该路径下返回的图片并不会以jpg结尾,并且末尾的参数控制了返回图片的大小,所以需要以对目录配置+保留参数的方式来实现。当然你也可以按照这样的方法,只修改缓存段的配置实现对某个路径的不缓存,原理是相同的。

以下是针对路径使用的一些高级配置,用于单站点访问多网站以及修改网页中的内容。proxy_set_header用于增加向源站请求的请求头,proxy_hide_header用于向访客隐藏来自源站的请求头。sub_filter则是用于对网页中内容的修改,具体使用示例见注释。


三、后缀缓存

针对特定后缀的缓存只需要配置好location字段限制好范围即可,如下配置可以实现缓存特定后缀的文件,或缓存特定路径下某后缀的文件。location字段使用正则表达式来圈定范围,若有更为复杂的需求请以NGINX正则表达式为关键词进行检索和学习。需要注意的是,在具有包含关系的正则匹配中,位于配置文件靠前的会优先触发匹配。因此请确保更为细致的配置靠前放置,比如第2行开头的配置是包含于第三行的配置中的,需要安置在以第3行开头的配置之前,以避免被表达更大范围的正则表达式覆盖。


四、防盗链

防盗链的内容很简单,只需要在location中加上valid_referers配置的字段。需要配置的要点有四个:①选定防盗链的location范围、②是否允许空refer、③设置允许的refer、④选择禁止的方式(返回403或返回特定文件)。黑名单的匹配则在valid_referers字段只需包含拉黑的refer,去掉默认的blocked参数即可。示例配置及注释如下,可参考进行修改。


五、WAF

添加WAF(Web Application Firewall)的CDN属于SCDN的范畴,在NGINX中可以使用lua-nginx-module加载lua脚本对请求进行处理,进而实现对异常请求的拦截。该模块由OpenResty项目开发,并未内置在NGINX项目中,因此有这方面的需求尽量选择内置该模块的OpenResty。原版NGINX安装该模块涉及到LuaJIT、NDK的调用,如果有兴趣可以按照官方的说明进行安装(点击前往)。

https://github.com/loveshell/ngx_lua_waf
https://github.com/C0nw0nk/Nginx-Lua-Anti-DDoS

以上两个是较为优秀的LuaWAF项目,能够在一定程度上实现防cc、防注入、ip黑名单、URL黑名单等功能。WAF一般情况下会使得访问性能下降20-40%左右,如果不是容易遭到攻击的站点可以不必配置WAF功能。具体使用方法在这里就不细讲了,若有需要可以根据GitHub项目中的指引进行实践。


五、结语

在修改以上反向代理+缓存的示例时,我将步骤大致总结为以下五步:

◉ 配置全局的缓存的规则,设定缓存路径等参数
◉ location的正则表达式指向配置的目标
◉ 设置好源站的IP、域名,根据实际决定是否添加sni头
◉ 配置缓存细则,决定是否缓存、是否保留参数、缓存有效期等
◉ 添加防盗链、修改双向的请求头等,完善反向代理的配置

以博主的WordPress动态博客为例,页面本身的访问经过CDN反而会拖慢访问速度。因此在在本站的配置中,仅将访问集中且相对较大的本地静态资源如js、css和图片等通过公共CDN缓存。而文件较小、访问相对零散的资源如自用的GravatarJSdelivr等,则通过单点、长有效期的NGINX缓存提供,5M的带宽便足以为这些碎片化的访问提供良好的体验。结果也正如预想的一样,腾讯轻量不错的网络质量加上完全自主控制的缓存,对外访问速度表现非常不错。

最后,也贴上一个较为简单的JSdelivr反代示例供大家参考,其中只对需要的npm和gh路径以不同的方式代理并缓存,请根据自己的实际需要来进行修改。此处无需添加跨域请求头,因为jsdelivr已设置好。

针对海外访客建议通过DNS分线路解析使海外用户解析至CloudFlare,如图通过页面规则设置一个301/302跳转回到JSdelivr官方。这样的话,既节省了提供国内访问服务器的流量,又保障了海外用户的体验。CNAME接入CloudFlare的方法可以参考博客:

【CloudFlare】官方免费CNAME接入教程

题外话,如何优化CDN的使用有很多技巧,需要从①访客>边缘节点、②边缘节点>源站、③缓存有效性这三个方面来综合考虑。

访客到边缘节点网络不佳:典型例子比如CloudFlare和CloudFront的自选IP(即针对性选择对大陆访问速度较为良好的节点解析给访客)。国内节点则不需要担心这个问题,尤其是公有云北上广BGP ≥ 公共CDN的网络质量。

边缘节点到源站的网络不佳:这一点很容易被忽略的,例如使用国内的CDN回源境外站点依然要通过三大运营商的国际出口。对于这样的需求,建议中置一个对大陆友好的中间节点作为中间源。

缓存的有效性不佳:一方面是对边缘节点的高估(一般认为CDN的缓存能力<20m),另一方面来自于访问不达标(即使定时预热,但是访问量不能维持边缘节点缓存有效)。对于少量使用的需求,类似于轻量COS这样的对象存储、亦或是本文如此的单节点缓存能够提供更好的体验。

希望以上博客的内容能够对您有所启发😊,若有不足之处也大家请多指教~


*原创文章,转载请注明出处

转载原创文章请注明,转载自: Luminous' Home » 【NGINX】配置反向代理的“CDN”同款缓存

  1. 写的好棒~ 之前某cdn的时候发现,如果访问量不够大的话,很多内容仍然无法在边缘节点上保持命中,导致cdn节点在每次访问的时候又去取源,导致初次访问速度降低
    不过有没有一种方法可以让靠近源站的cdn节点取源,然后内网同步到各、cdn节点上?

    1. @Miko 有啊,比如CloudFlare的Argo,其他的主流CDN也有L2节点但是不一定满足近源的特点,对源站和外部两端优化的应该归属ECDN的范畴。
  2. 看到moemeta里边的封面图来的, 写的很好, 受教了

    1. @西井QAQ 感谢支持~