yokon's blog

Nginx场景实践

2018.06.30

Nginx(读作:恩静 X)是一个高性能的HTTP和反向代理服务器,一个邮件代理以及通用的TCP/ UDP代理服务器。本篇文章是我最近使用Nginx的记录。你发现本站已经是全站https了,而本站的图片是放在七牛云的。之所以可以使用https://www.yukun.com/qiniu/xxx访问到存放在七牛的图片,是因为使用了nginx的反向代理。

安装

我们使用Docker启动一个ubuntu容器来演示安装过程:

$ sudo docker run -t -i -p 80:80 ubuntu:16.04 /bin/bash
root@bbf4a330635d:/# cd root
root@bbf4a330635d:~#

进入容器后,开始安装nginx:

apt-get安装

$ apt-get update  // 更新软件源
$ apt-get install nginx  // 更新软件源
$ nginx   // 启动
$ ps -ef   // 显示所有进程信息
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 08:26 pts/0    00:00:00 /bin/bash
root      3757     1  0 08:58 ?        00:00:00 nginx: master process nginx
www-data  3758  3757  0 08:58 ?        00:00:00 nginx: worker process
root      3759     1  0 08:58 pts/0    00:00:00 ps -ef

源码编译安装

上一种的安装方式,在生产环境下可能会显得自定义性不强,或者安装包比较老。更多的时候我们需要下载源码编译安装。

$ wget http://nginx.org/download/nginx-1.14.0.tar.gz  // 目前最新稳定版
$ tar -zxvf nginx-1.14.0.tar.gz //解压
$ cd nginx-1.14.0.tar.gz  
$ apt-get install libpcre3 libpcre3-dev zlib1g-dev build-essential libtool  // 编译前安装依赖包
$ ./configure  // 可以在后面定制安装
$ make
$ sudo make install

这样我们就安装成功了,默认情况nginx会安装在/usr/local/nginx目录中启动:

$ /usr/local/nginx/sbin/nginx   // 启动
$ /usr/local/nginx/sbin/nginx -s reload  // 重启
$ /usr/local/nginx/sbin/nginx -s stop   // 关闭进程
$ /usr/local/nginx/sbin/nginx -s quit  // 平滑关闭nginx

反向代理

假如用户 A 要得到网站 C 的内容,而用户 A 因为网络原因又不能直接访问到,而服务器 B 可以访问到网站 C,那服务器可以得到网站 C 的内容再存起来发给用户 A,这整个过程用户 A 是直接和服务器 B 交互的,用户 A 不知道网站 C 的存在。那web服务器 B 就是一台反向代理服务器,C 是上游服务器。

nginx的反向代理是依赖于ngx_http_proxy_module这个自带module来实现的。我们以本站图片请求反向代理为例:

由于本站用的是https协议,在浏览器的地址栏可以看到绿色的锁。而如果图片不是存储在本地服务器,而是放在七牛,又拍云这样的地方,图片的外链是http协议的(当然七牛本身是可以开启https协议的,只是并非免费)。那么在浏览器的地址栏,虽然显示的是https协议,却没有绿色安全锁。

所以我们要把https协议请求的图片地址反向代理到http协议的真实图片上。事实上https协议请求的图片是不存在,而如果使用反向代理,它就有一个指向http协议图片的实际地址。

server {
  server_name    www.yukunweb.com;
  listen         443;  # 443端口用于HTTPS服务
  location /qiniu/post36_0.jpg {
    proxy_pass http://opxib6gmc.bkt.clouddn.com/post36_0.jpg;
    proxy_set_header Host opxib6gmc.bkt.clouddn.com;
  }

  location /example.jpg {
    proxy_pass http://example.com/example.jpg;
  }
}

这样我们就可以通过访问https://www.yukunweb.com/qiniu/post36_0.jpg来请求到http://opxib6gmc.bkt.clouddn.com/post36_0.jpg的图片。

Nginxsever块主要是服务器的配置(域名,IP,端口等),在一个Nginx的配置文件里面,我们可以有多个sever块配置。而location块则是在sever块里面,对不同请求路径进行的配置。因为一个站点中的URI通常会非常多,所以在location块设置这部分,同样可以写多个location的配置。当然我们不能为每一个URI都进行配置,我们来看一些动态匹配语法:

location语法说明

syntax: location [=|~|~*|^~|@] /uri/ { ... }
default: no
context: server
语法说明
=表示精确匹配,不支持正则
^~表示uri以某个常规字符串开头,不支持正则,理解为匹配uri路径即可
~和~*表示区分大小写的和不区分大小写的正则匹配
!~和!~*表示区分大小写不匹配和不区分大小写不匹配的正则匹配
/通用匹配,任何请求都会匹配

匹配优先级 := > ^~ > ~/~*/!~/!~* > /。优先级的意思是当以~/~*/!~/!~*/匹配到符合结果路径后,会继续往下判断其他配置项的匹配,如果^~语法也匹配到符合的则使用^~匹配到的结果,而^~匹配到结果后就不会再往下判断其他配置项是否满足。

proxy_set_header配置重写请求上游服务器头的内容,我们这里设置了请求头Host参数。

现在我们重新写上面的配置:

server {
  server_name    www.yukunweb.com;
  listen         443;  # 443端口用于HTTPS服务
  location ^~ /qiniu/ {
    proxy_pass http://opxib6gmc.bkt.clouddn.com/;
    proxy_set_header Host opxib6gmc.bkt.clouddn.com;
  }

  # 反向代理avatar.com的头像
  location ^~ /avatar/ {
    proxy_pass http://www.avatar.com/;
    proxy_set_header Host www.avatar.com;
  }
}

静态文件缓存

以本战flask程序为例,由于静态文件(比如css/js/图片等),都是不经常更新的。可以使用nginxproxy_cache将这些静态文件的响应结果缓存到本地一个目录。这样不仅可以减少服务器处理请求的压力,还有一定程度上提高程序响应速度。

下面是本站缓存配置:

# nginx.conf
http{
    ...

    proxy_temp_path /tmp/temp_dir;  # 从后端服务器接收的临时文件的存放路径
    # 设置缓存的路径和其他参数
    # levels 设置缓存文件目录层次;levels=1:2 表示两级目录
    # keys_zone 设置缓存名字和共享内存大小
    # inactive 在指定时间内没人访问则被删除
    # max_size 最大缓存空间,如果缓存空间满,默认覆盖掉缓存时间最长的资源。
    proxy_cache_path /tmp/cache levels=1:2 keys_zone=tmp_cache:100m inactive=1d max_size=10g;
    
    ...

    server {
        ...
    }
}

本站程序的sever块配置在sites-available/default中:

# default
# 缓存 flask 的static文件夹下所有静态文件
server ^~ /static/ {
    root /root/project/YuBlog/app/; # 指定文件路径,root不需要在后面加static,alias需要
    proxy_set_header Host $host;
    proxy_cache tmp_cache;  # 使用名为tmp_cache的对应与http中的缓存配置
    expires 30d;  # 缓存时间
}

配置完我们需要重启nginx/usr/local/sbin/nginx -s reload

如果没有报错就说明成功了,如果有错误可以使用/usr/local/sbin/nginx -t 查看错误原因。这是打开浏览器可以查看静态文件是否进行缓存了,看响应头中Expires的时间。如果响应头中没有expires信息,而 nginx 也没有出错,那么可能是 nginx 的缓存没有写入权限。这是要在nginx.conf顶部配置有写入权限的用户。

post36_0.jpg

当然,上面反向代理的图片也是可以缓存的,只需要在他们的location配置中加入对应配置就可以了。

最后

参考文章:nginx proxy_cache 缓存配置{:target="_blank"}

推荐教程:从入门到放弃{:target="_blank"}