浅析Nginx实践常用配置:设置二级域名虚拟主机并通过Nginx配置访问、如何配置反向代理、如何配置CORS跨域、如何配置Gzip压缩、如何配置负载均衡、如何配置动静分离、如何配置高可用集群(双机热备)

一、设置二级域名虚拟主机

  在某某云上购买了域名之后,就可以配置虚拟主机了,一般配置的路径在 域名管理 -> 解析 -> 添加记录 中添加二级域名,配置后某某云会把二级域名也解析到我们配置的服务器 IP 上,然后我们在 Nginx 上配置一下虚拟主机的访问监听,就可以拿到从这个二级域名过来的请求了。

  现在我自己的服务器上配置了一个 fe 的二级域名,也就是说在外网访问 fe.sherlocked93.club 的时候,也可以访问到我们的服务器了。

  由于默认配置文件 /etc/nginx/nginx.conf 的 http 模块中有一句 include /etc/nginx/conf.d/*.conf 也就是说 conf.d 文件夹下的所有 *.conf 文件都会作为子配置项被引入配置文件中。

  为了维护方便,我在 /etc/nginx/conf.d 文件夹中新建一个 fe.sherlocked93.club.conf :

# /etc/nginx/conf.d/fe.sherlocked93.club.conf

server {
  listen 80;
    server_name fe.sherlocked93.club;

    location / {
        root  /usr/share/nginx/html/fe;
        index index.html;
    }
}

  然后在 /usr/share/nginx/html 文件夹下新建 fe 文件夹,新建文件index.html,内容随便写点,改完 nginx -s reload 重新加载,浏览器中输入fe.sherlocked93.club,发现从二级域名就可以访问到我们刚刚新建的 fe 文件夹:

二、配置反向代理

  反向代理是工作中最常用的服务器功能,经常被用来解决跨域问题,下面简单介绍一下如何实现反向代理。

  首先进入 Nginx 的主配置文件:vim /etc/nginx/nginx.conf,然后我们去 http 模块的 server 块中的 location /,增加一行将默认网址重定向到最大学习网站 Bilibili 的 proxy_pass 配置

  改完保存退出,nginx -s reload 重新加载,进入默认网址,那么现在就直接跳转到 B 站了,实现了一个简单的代理。

  实际使用中,可以将请求转发到本机另一个服务器上,也可以根据访问的路径跳转到不同端口的服务中。比如我们监听 9001 端口,然后把访问不同路径的请求进行反向代理:

  1. 把访问 http://127.0.0.1:9001/edu 的请求转发到 http://127.0.0.1:8080
  2. 把访问 http://127.0.0.1:9001/vod 的请求转发到 http://127.0.0.1:8081

  这种要怎么配置呢,首先同样打开主配置文件,然后在 http 模块下增加一个 server 块:

server {
  listen 9001;
  server_name *.sherlocked93.club;

  location ~ /edu/ {
    proxy_pass http://127.0.0.1:8080;
  }
  
  location ~ /vod/ {
    proxy_pass http://127.0.0.1:8081;
  }
}

  反向代理还有一些其他的指令,可以了解一下:

  1. proxy_set_header:在将客户端请求发送给后端服务器之前,更改来自客户端的请求头信息;
  2. proxy_connect_timeout:配置 Nginx 与后端代理服务器尝试建立连接的超时时间;
  3. proxy_read_timeout:配置 Nginx 向后端服务器组发出 read 请求后,等待相应的超时时间;
  4. proxy_send_timeout:配置 Nginx 向后端服务器组发出 write 请求后,等待相应的超时时间;
  5. proxy_redirect:用于修改后端服务器返回的响应头中的 Location 和 Refresh。

 三、跨域 CORS 配置

  关于简单请求、非简单请求、跨域的概念,前面已经介绍过了,还不了解的可以看看前面的讲解。现在前后端分离的项目一统天下,经常本地起了前端服务,需要访问不同的后端地址,不可避免遇到跨域问题。要解决跨域问题,我们来制造一个跨域问题。

  首先和前面设置二级域名的方式一样,先设置好 fe.sherlocked93.club 和 be.sherlocked93.club 二级域名,都指向本云服务器地址,虽然对应 IP 是一样的,但是在 fe.sherlocked93.club 域名发出的请求访问 be.sherlocked93.club 域名的请求还是跨域了,因为访问的 host 不一致。

1、使用反向代理解决跨域

  在前端服务地址为 fe.sherlocked93.club 的页面请求 be.sherlocked93.club 的后端服务导致的跨域,可以这样配置:

server {
  listen 9001;
  server_name fe.sherlocked93.club;

  location / {
    proxy_pass be.sherlocked93.club;
  }
}

  这样就将对前一个域名 fe.sherlocked93.club 的请求全都代理到了 be.sherlocked93.club,前端的请求都被我们用服务器代理到了后端地址下,绕过了跨域。

  这里对静态文件的请求和后端服务的请求都以 fe.sherlocked93.club 开始,不易区分,所以为了实现对后端服务请求的统一转发,通常我们会约定对后端服务的请求加上 /apis/ 前缀或者其他的 path 来和对静态资源的请求加以区分,此时我们可以这样配置:

# 请求跨域,约定代理后端服务请求path以/apis/开头
location ^~/apis/ {
    # 这里重写了请求,将正则匹配中的第一个分组的path拼接到真正的请求后面,并用break停止后续匹配
    rewrite ^/apis/(.*)$ /$1 break;
    proxy_pass be.sherlocked93.club;
  
    # 两个域名之间cookie的传递与回写
    proxy_cookie_domain be.sherlocked93.club fe.sherlocked93.club;
}

  这样,静态资源我们使用 fe.sherlocked93.club/xx.html,动态资源我们使用 fe.sherlocked93.club/apis/getAwo,浏览器页面看起来仍然访问的前端服务器,绕过了浏览器的同源策略,毕竟我们看起来并没有跨域。也可以统一一点,直接把前后端服务器地址直接都转发到另一个 server.sherlocked93.club,只通过在后面添加的 path 来区分请求的是静态资源还是后端服务,看需求了。

2、配置 header 解决跨域

  当浏览器在访问跨源的服务器时,也可以在跨域的服务器上直接设置 Nginx,从而前端就可以无感地开发,不用把实际上访问后端的地址改成前端服务的地址,这样可适性更高。比如前端站点是 fe.sherlocked93.club,这个地址下的前端页面请求 be.sherlocked93.club 下的资源,就会跨域。

  在 /etc/nginx/conf.d/ 文件夹中新建一个配置文件,对应二级域名 be.sherlocked93.club :

# /etc/nginx/conf.d/be.sherlocked93.club.conf

server {
  listen       80;
  server_name  be.sherlocked93.club;
  
    add_header 'Access-Control-Allow-Origin' $http_origin;   # 全局变量获得当前请求origin,带cookie的请求不支持*
    add_header 'Access-Control-Allow-Credentials' 'true';    # 为 true 可带上 cookie
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';  # 允许请求方法
    add_header 'Access-Control-Allow-Headers' $http_access_control_request_headers;  # 允许请求的 header,可以为 *
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
    
  if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Max-Age' 1728000;   # OPTIONS 请求的有效期,在有效期内不用发出另一条预检请求
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
    
        return 204;                  # 200 也可以
    }
  
    location / {
        root  /usr/share/nginx/html/be;
        index index.html;
    }
}

四、配置 gzip

  使用 gzip 不仅需要 Nginx 配置,浏览器端也需要配合,需要在请求消息头中包含 Accept-Encoding: gzip(IE5 之后所有的浏览器都支持了,是现代浏览器的默认设置)。一般在请求 html 和 css 等静态资源的时候,支持的浏览器在 request 请求静态资源的时候,会加上 Accept-Encoding: gzip 这个 header,表示自己支持 gzip 的压缩方式,Nginx 在拿到这个请求的时候,如果有相应配置,就会返回经过 gzip 压缩过的文件给浏览器,并在 response 相应的时候加上 content-encoding: gzip 来告诉浏览器自己采用的压缩方式(因为浏览器在传给服务器的时候一般还告诉服务器自己支持好几种压缩方式),浏览器拿到压缩的文件后,根据自己的解压方式进行解析。

  先来看看 Nginx 怎么进行 gzip 配置,和之前的配置一样,为了方便管理,还是在 /etc/nginx/conf.d/ 文件夹中新建配置文件 gzip.conf :

# /etc/nginx/conf.d/gzip.conf

gzip on; # 默认off,是否开启gzip
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

# 上面两个开启基本就能跑起了,下面的愿意折腾就了解一下
gzip_static on;
gzip_proxied any;
gzip_vary on;
gzip_comp_level 6;
gzip_buffers 16 8k;
# gzip_min_length 1k;
gzip_http_version 1.1;

  稍微解释一下:

  1. gzip_types:要采用 gzip 压缩的 MIME 文件类型,其中 text/html 被系统强制启用;
  2. gzip_static:默认 off,该模块启用后,Nginx 首先检查是否存在请求静态文件的 gz 结尾的文件,如果有则直接返回该 .gz 文件内容;
  3. gzip_proxied:默认 off,nginx做为反向代理时启用,用于设置启用或禁用从代理服务器上收到相应内容 gzip 压缩;
  4. gzip_vary:用于在响应消息头中添加 Vary:Accept-Encoding,使代理服务器根据请求头中的 Accept-Encoding 识别是否启用 gzip 压缩;
  5. gzip_comp_level:gzip 压缩比,压缩级别是 1-9,1 压缩级别最低,9 最高,级别越高压缩率越大,压缩时间越长,建议 4-6;
  6. gzip_buffers:获取多少内存用于缓存压缩结果,16 8k 表示以 8k*16 为单位获得;
  7. gzip_min_length:允许压缩的页面最小字节数,页面字节数从header头中的Content-Length 中进行获取。默认值是 0,不管页面多大都压缩。建议设置成大于 1k 的字节数,小于 1k 可能会越压越大;
  8. gzip_http_version:默认 1.1,启用 gzip 所需的 HTTP 最低版本;

  这个配置可以插入到 http 模块整个服务器的配置里,也可以插入到需要使用的虚拟主机的 server 或者下面的 location 模块中,当然像上面我们这样写的话就是被 include 到 http 模块中了。

  其他更全的配置信息可以查看 <官网文档ngx_http_gzip_module>

  注意了,一般 gzip 的配置建议加上 gzip_min_length 1k,不加的话

  由于文件太小,gzip 压缩之后得到了 -48% 的体积优化,压缩之后体积还比压缩之前体积大了,所以最好设置低于 1kb 的文件就不要 gzip 压缩了 

1、Webpack 的 gzip 配置

  当前端项目使用 Webpack 进行打包的时候,也可以开启 gzip 压缩:

// vue-cli3 的 vue.config.js 文件
const CompressionWebpackPlugin = require('compression-webpack-plugin')

module.exports = {
  // gzip 配置
  configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      // 生产环境
      return {
        plugins: [new CompressionWebpackPlugin({
          test: /\.js$|\.html$|\.css/,    // 匹配文件名
          threshold: 10240,               // 文件压缩阈值,对超过10k的进行压缩
          deleteOriginalAssets: false     // 是否删除源文件
        })]
      }
    }
  },
  ...
}

  可以看到某些打包之后的文件下面有一个对应的 .gz 经过 gzip 压缩之后的文件,这是因为这个文件超过了 10kb,有的文件没有超过 10kb 就没有进行 gzip 打包,如果你期望压缩文件的体积阈值小一点,可以在 compression-webpack-plugin 这个插件的配置里进行对应配置。

  那么为啥这里 Nginx 已经有了 gzip 压缩,Webpack 这里又整了个 gzip 呢,因为如果全都是使用 Nginx 来压缩文件,会耗费服务器的计算资源,如果服务器的 gzip_comp_level 配置的比较高,就更增加服务器的开销,相应增加客户端的请求时间,得不偿失。

  如果压缩的动作在前端打包的时候就做了,把打包之后的高压缩等级文件作为静态资源放在服务器上,Nginx 会优先查找这些压缩之后的文件返回给客户端,相当于把压缩文件的动作从 Nginx 提前给 Webpack 打包的时候完成,节约了服务器资源,所以一般推介在生产环境应用 Webpack 配置 gzip 压缩。

五、配置负载均衡

  负载均衡在之前已经介绍了相关概念了,主要思想就是把负载均匀合理地分发到多个服务器上,实现压力分流的目的。主要配置如下:

http {
  upstream myserver {
    # ip_hash;  # ip_hash 方式
    # fair;   # fair 方式
    server 127.0.0.1:8081;  # 负载均衡目的服务地址
    server 127.0.0.1:8080;
    server 127.0.0.1:8082 weight=10;  # weight 方式,不写默认为 1
  }
 
  server {
    location / {
        proxy_pass http://myserver;
        proxy_connect_timeout 10;
    }
  }
}

  Nginx 提供了好几种分配方式,默认为轮询,就是轮流来。有以下几种分配方式:

  1. 轮询,默认方式,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务挂了,能自动剔除;
  2. weight,权重分配,指定轮询几率,权重越高,在被访问的概率越大,用于后端服务器性能不均的情况;
  3. ip_hash,每个请求按访问 IP 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决动态网页 session 共享问题。负载均衡每次请求都会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的;
  4. fair(第三方),按后端服务器的响应时间分配,响应时间短的优先分配,依赖第三方插件 nginx-upstream-fair,需要先安装;

六、配置动静分离

  动静分离在之前也介绍过了,就是把动态和静态的请求分开。方式主要有两种:

  一种是纯粹把静态文件独立成单独的域名,放在独立的服务器上,也是目前主流推崇的方案。

  另外一种方法就是动态跟静态文件混合在一起发布, 通过 Nginx 配置来分开

  通过 location 指定不同的后缀名实现不同的请求转发;通过 expires 参数设置,可以使浏览器缓存过期时间,减少与服务器之前的请求和流量。

  具体 expires 定义:是给一个资源设定一个过期时间,也就是说无需去服务端验证,直接通过浏览器自身确认是否过期即可,所以不会产生额外的流量。此种方法非常适合不经常变动的资源。(如果经常更新的文件,不建议使用 expires 来缓存),我这里设置 3d,表示在这 3 天之内访问这个URL,发送一个请求,比对服务器该文件最后更新时间没有变化。则不会从服务器抓取,返回状态码 304,如果有修改,则直接从服务器重新下载,返回状态码 200。

server {
  location /www/ {
      root /data/;
      index index.html index.htm;
  }
  
  location /image/ {
      root /data/;
      autoindex on;
  }
}

七、配置高可用集群(双机热备)

  当主 Nginx 服务器宕机之后,切换到备份 Nginx 服务器

 

  首先安装 keepalived,yum install keepalived -y

  然后编辑 /etc/keepalived/keepalived.conf 配置文件,并在配置文件中增加 vrrp_script 定义一个外围检测机制,并在 vrrp_instance 中通过定义 track_script 来追踪脚本执行过程,实现节点转移:

global_defs{
   notification_email {
        acassen@firewall.loc
   }
   notification_email_from Alexandre@firewall.loc
   smtp_server 127.0.0.1
   smtp_connect_timeout 30 // 上面都是邮件配置,没卵用
   router_id LVS_DEVEL     // 当前服务器名字,用hostname命令来查看
}
vrrp_script chk_maintainace { // 检测机制的脚本名称为chk_maintainace
    script "[[ -e/etc/keepalived/down ]] && exit 1 || exit 0" // 可以是脚本路径或脚本命令
    // script "/etc/keepalived/nginx_check.sh"    // 比如这样的脚本路径
    interval 2  // 每隔2秒检测一次
    weight -20  // 当脚本执行成立,那么把当前服务器优先级改为-20
}
vrrp_instanceVI_1 {   // 每一个vrrp_instance就是定义一个虚拟路由器
    state MASTER      // 主机为MASTER,备用机为BACKUP
    interface eth0    // 网卡名字,可以从ifconfig中查找
    virtual_router_id 51 // 虚拟路由的id号,一般小于255,主备机id需要一样
    priority 100      // 优先级,master的优先级比backup的大
    advert_int 1      // 默认心跳间隔
    authentication {  // 认证机制
        auth_type PASS
        auth_pass 1111   // 密码
    }
    virtual_ipaddress {  // 虚拟地址vip
       172.16.2.8
    }
}

  其中检测脚本 nginx_check.sh,这里提供一个

#!/bin/bash
A=`ps -C nginx --no-header | wc -l`
if [ $A -eq 0 ];then
    /usr/sbin/nginx # 尝试重新启动nginx
    sleep 2         # 睡眠2秒
    if [ `ps -C nginx --no-header | wc -l` -eq 0 ];then
        killall keepalived # 启动失败,将keepalived服务杀死。将vip漂移到其它备份节点
    fi
fi

  复制一份到备份服务器,备份 Nginx 的配置要将 state 后改为 BACKUPpriority 改为比主机小。

  设置完毕后各自 service keepalived start 启动,经过访问成功之后,可以把 Master 机的 keepalived 停掉,此时 Master 机就不再是主机了 service keepalived stop,看访问虚拟 IP 时是否能够自动切换到备机 ip addr再次启动 Master 的 keepalived,此时 vip 又变到了主机上。

posted @ 2020-05-12 23:33  古兰精  阅读(742)  评论(0编辑  收藏  举报