本文所述内容不具普遍性,可能因操作环境差异而与实际有所出入,故请勿照搬照抄,仅供参考。
CDN 的相关概念可以参考之前的文章《 如何加快网站访问速度》,了解了什么是 CDN 后,我们知道 为了提高网站相关体验,网站一般都会上 CDN,但线路质量好一点的 CDN 费用一般也不低,所以就会想 着,从技术角度上看我们自己是否能够搭建 CDN?
概念
Nginx
Nginx 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 服务器,Nginx 具有很多非常优越的特性:
- 在连接高并发的情况下,具有较好的性能,支持负载均衡,实现可扩展的浏量管理;
- 使Web服务器更灵活,更高效,更安全;
- 能够快速灵活且可靠的传送流视频和音频内容;
- 支持强大的 Web 加速和移动端性能的解决方案;
- 不仅只保护数据安全,也保证网站在面对恶意流量的攻击中能正常运行,从而保护应用安全;
- 可管理的安全的基于 HTTP API 流量的可信平台,为 API 提供安全保障和使用策略;
- 完整的软件应用分发平台,能够取代昂贵的 ADC 硬件负载平衡器,节省成本。
智能解析
域名智能解析是指域名解析服务器根据来访者的 IP 类型,对同一域名作出相应不同解析。
例如:
对 IP 来自电信的访问者,将域名解析到该域名对应 IP 地址为电信的服务器上;
对 IP来自联通的访问者,将域名解析到该域名对应 IP 地址为联通的服务器上;
对 IP 来自香港的访问者,将域名解析到该域名对应 IP 地址为香港的服务器上;
以保证访问者不因电信线路瓶颈而造成联通、香港的访客无法访问。
反向代理
反向代理(Reverse Proxy)是指用代理服务器来接收 Internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从内部服务器上得到的结果返回给 Internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
CDN 工作流程
如上图所示,这是一个完整的流程图:客户端访问域名时,先向 DNS 请求域名 IP,DNS 查询到 CNAME 记录(如果没有 DNS 直接回复源服务器 IP),则进一步解析 CNAME 智能解析服务器, 智能解析收到请求根据客户端来源按规则判断并回复 CDN 节点 IP,客户端此时访问域名就连接到了回复的 CDN 节点,如果 CDN 节点没有缓存,则 CDN 就会发起连接到负载均衡器(如果没有则直接连接源服务器,一般在有多个源服务器后端时才会有负载均衡器),然后负载均衡器根据规则分流到源服务器,将内容返回给 CDN 节点,CDN 节点再返回给客户端,完成整个访问流程。
从流程上看使用 CDN 后整个过程中增加了很多路由,看起来客户端访问速度可能会变慢,但实际上,从智能解析到负载均衡,客户端几乎是没有感知的,也就是说客户端能感受到的速度就是到 CDN 节点的速度。
搭建
通过概念讲解我们知道了 CDN 工作方式,其中智能解析、负载均衡都是可以单独拿出来长篇讲解的比较复杂的内容,我们这里先以如下简化版的流程来讲解 Nginx 搭建 CDN 节点的过程,后续有时间再一起来看看智能解析、负载均衡的内容。
编译安装
ngx_cache_purgengx_http_substitutions_filter_moduleheaders-more-nginx-module
参考以下命令编译
# 新建 Nginx 运行用户及用户组
$ groupadd www
$ useradd -s /sbin/nologin -g www www
# 下载 Nginx 源码
$ wget https://nginx.org/download/nginx-1.14.2.tar.gz
$ tar zxf nginx-1.14.2.tar.gz && cd nginx-1.14.2/src/
# 下载并编译 pcre 运行库
$ wget https://ftp.pcre.org/pub/pcre/pcre-8.42.tar.bz2
$ tar jxf pcre-8.42.tar.bz2 && cd pcre-8.42 && ./configure
$ make && make install && cd .. && rm -rf pcre-8.42*
$ echo /usr/local/lib >> /etc/ld.so.conf.d/usrlib.conf
$ ldconfig
# 下载并解压缓存清除插件 ngx_cache_purge
$ wget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz && tar zxf 2.3.tar.gz && rm -rf 2.3.tar.gz
# 下载并解压字符串替换插件 ngx_http_substitutions_filter_module/
$ wget https://github.com/yaoweibin/ngx_http_substitutions_filter_module/archive/v0.6.4.tar.gz && tar zxf v0.6.4.tar.gz && rm -rf v0.6.4.tar.gz
# 下载并解压头部参数设置插件 headers-more-nginx-module
$ wget https://github.com/openresty/headers-more-nginx-module/archive/v0.33.tar.gz && tar zxf v0.33.tar.gz && rm -rf v0.33.tar.gz && cd ..
# 编译并安装
$ ./configure--user=www --group=www --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_gzip_static_module --with-http_sub_module --with-stream --with-stream_ssl_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --with-pcre --add-module=/usr/local/nginx/src/src/ngx_cache_purge-2.3 --add-module=/usr/local/nginx/src/src/ngx_http_substitutions_filter_module-0.6.4 --add-module=/usr/local/nginx/src/src/headers-more-nginx-module-0.33
$ make && make install
配置缓存
配置
主要是为了缓存静态文件,而缓存需要指定位置及大小,参见如下配置:
......
http {
......
proxy_cache_path /usr/local/nginx/cache levels=1:2 keys_zone=mycache:20m max_size=5g inactive=1d;
proxy_temp_path /usr/local/nginx/cache/tmp;
.......
由于缓存是写入硬盘的,硬盘的 I/O 将会对速度产生重大影响,故建议选择 I/O 好一点的主机,比如华为云学生机。
说明
/usr/local/nginx/cache//usr/local/nginx/cache/levelsHITMISScache manager10mCache-Controlinactiveinactiveuse_temp_path=offuse_temp_path=off proxy_temp_pathproxy_temp_pathproxy_cache_path
配置代理
配置
$ sudo echo '1.1.1.1 www.vircloud.net' >> /etc/hosts
server {
listen 80;
server_name www.vircloud.net;
more_set_headers "X-Node: CN-BJ01";
location / {
proxy_pass https://www.vircloud.net;
proxy_redirect off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache mycache;
proxy_cache_key $uri$is_args$args;
}
location ~ /clear(/.*) {
proxy_cache_purge mycache $1$is_args$args;
error_page 405 =200 /clear$1;
}
}
说明
hosts
这一段反代服务的配置可以说是最简单的配置了,我们来看一下各个参数的作用:
headers-more-nginx-moduleproxy_redirect http://localhost:8000/blog/ /;http://localhost:8000/blog//https://www.vircloud.net/http://localhost:8000/blog/proxy_cache_path/clear/ngx_cache_purge
完整配置
到这里,不出意外反代已经可以正常工作了,即已经实现了 CDN 节点的搭建。出于实际作业需求,我们可能还需要更多的配置来实现,比如源站挂了,而节点有缓存,那么我们就会希望让节点正常返回,而不是跟着源站返回错误。
下面我们就来看看 Nginx 对反代还提供了哪些参数,以实际完整配置为例:
server {
# 监听端口。
listen 80;
listen 443 ssl http2;
# 监听 SNI 域名。
server_name www.vircloud.net;
# SSL 配置。
ssl_certificate vircloud.net.crt;
ssl_certificate_key vircloud.net.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers 'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 获取真实 IP。
include realip.conf;
# 反垃圾。
include deny-ip.conf;
include deny-bots.conf;
# SSL 跳转。
if ($server_port !~ 443){
rewrite ^(/.*)$ https://$host$1 permanent;
}
# 指定错误页面,并从当前服务器取,而不向源站服务器请求。
proxy_intercept_errors on;
error_page 403 404 500 502 /error.html;
location /error.html {
root /usr/local/nginx/html;
}
# 添加响应头信息。
more_set_headers "X-Node: CN-BJ01";
# 设置缓存清除入口。
location ~ /clear(/.*) {
# 只允许特定 IP 执行清除动作。
include allowip.conf;
deny all;
# 缓存 key 转换。
proxy_cache_purge mycache $1$is_args$args;
error_page 405 =200 /clear$1;
}
# 反代配置正式开始。
location / {
proxy_pass https://www.vircloud.net;
proxy_redirect off;
# 向源站传送主机头、客户端真实 IP 等特定信息。
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 忽略源站响应的 Cache-Control 头信息。
proxy_ignore_headers Cache-Control
# 与源站建立连接的超时时间,通常不要超过 75s。
proxy_connect_timeout 60;
# 与源站通信时节点发送请求的超时时间,超时只在两次连续的写入操作之间作用, 而不是用于传输整个请求,如果源站在此时间内没有收到任何内容,则连接将关闭。
proxy_send_timeout 60;
# 与源站通信时源站响应数据的超时时间,超时只在两次连续的读操作之间起作用,而不是用于传输整个响应,如果源站在此时间内没有传输任何内容,则连接将关闭。
proxy_read_timeout 60;
# 开启代理缓冲区。
proxy_buffering on;
# 响应头的缓冲区大小。
proxy_buffer_size 128k;
# 网页内容缓冲区大小(4*256k)。
proxy_buffers 4 256k;
# Nginx 会在没有完全读完后端响应的时候就开始向客户端传送数据,所以它会划出一部分缓冲区来专门向客户端传送数据,然后它继续从后端取数据,缓冲区满了之后就写到磁盘的临时文件中。
proxy_busy_buffers_size 512k;
# 指定当响应内容大于 proxy_buffers 指定的缓冲区时,写入硬盘的临时文件的大小,如果超过了这个值,Nginx 将与源服务器同步的传递内容,而不再缓冲到硬盘,设置为 0 时则直接关闭硬盘缓冲。
proxy_max_temp_file_size 512k;
# 一次访问能写入的临时文件的大小。
proxy_temp_file_write_size 512k;
# 源站返回错误信息自动重试。
proxy_next_upstream error timeout invalid_header http_500 http_503;
# 若后端返回错误则返回已缓存的。
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
# 指定缓存使用的空间。
proxy_cache mycache;
# 缓存支持的方法。
proxy_cache_methods GET HEAD;
# 添加 If-Modified-Since 头信息,如果客户端的请求项已经被缓存过了,但是在缓存控制头部中定义为过期,那么 Nginx 就会在向源站 GET 请求中包含 If-Modified-Since 字段,源站只会在该文件请求头中 Last-Modified 记录的时间内被修改时才将全部文件一起发送。
proxy_cache_revalidate on;
# 多个 MISS 只有第一个会连接服务器。
proxy_cache_lock on;
# 客户端设置 Pragma:no-cache 时节点应当直接请求源站,添加此配置可使 Nginx 支持该请求(默认忽略)。
proxy_cache_bypass $http_pragma $cookie_nocache;
# 指定对 200、301 或者 302 等有效代码缓存的时间长度,特定参数 any 表示对任何响应都缓存一定时间长度。
proxy_cache_valid 200 24d;
proxy_cache_valid 301 24h;
proxy_cache_valid 302 304 403 1h;
proxy_cache_valid any 1s;
# 指定缓存使用的 key 值,方便定位清除缓存。
proxy_cache_key $uri$is_args$args;
# 响应头信息添加当前请求资源的缓存状态,如命中 HIT、没有缓存 MISS、过期 UPDATING 等。
add_header X-Cache $upstream_cache_status;
# 指定请求多少次才开始缓存。
proxy_cache_min_uses 1;
# 统一添加请求的资源过期时间头信息
expires max;
}
真实 IP
使用反代功能后,实际上对于源站来说,访客是代理服务器而不是真实的访客,Nginx 针对这种情况也给了解决方案,在源站 Nginx 配置中添加如下配置:
......
set_real_ip_from 反代服务器 IP;
real_ip_recursive on;
......