nginx学习笔记(二)

"nginx代理服务器配置,多链路nginx负载均衡"

Posted by wangtiegang on June 28, 2019

上次接手一部分基础运维工作的时候学习了下nginx,写了《nginx学习笔记(一)》《网络基础知识》,后来自己参照原来的nginx代理服务器集群配置又增加了一台nginx节点,接触了很多东西,中间很多配置都是复制原有的做些修改,对网络这块的东西一知半解,本来想理清楚了再好好写第二篇,但是一直有各种开发任务排期,下班了就不想动,拖着拖着就两个星期过去了。今天又想到这个事情,觉得我不是很有必要去弄清楚所有的细节,毕竟核心能力应该是开发,而不是基础运维和网络管理,缺乏很多网络方面的基础,即使花时间也还是一知半解,我只要开发完系统能顺带应付日常运维就好了,索性不再拖下去了,今天就把搭建过程中的笔记整理一下。

nginx代理服务器配置

我们的应用大部分是跑在tomcat中的,nginx代理服务器主要是用来代理用户通过域名访问系统时,将请求转发到对应的tomcat服务器,为了保证服务稳定可用,我们配置了nginx集群,总共三个nginx节点。

nginx配置域名代理,3台服务器配置一致

  • nginx.conf配置如下,主要是一些通用配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
user  nginx;
worker_processes  8;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  4096;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" '
                      '"$upstream_addr" "$upstream_status" "$upstream_response_time"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    client_body_buffer_size 32k;
    client_header_buffer_size 512k;
    client_header_timeout 25;
    client_max_body_size 2048M;
    etag off;
    keepalive_timeout  65;
    large_client_header_buffers 4 512k;
    max_ranges 10;
    send_timeout 15;
    server_name_in_redirect off;
    server_tokens off;

    gzip  on;
    gzip_buffers 32 4k;
    gzip_comp_level 9;
    gzip_disable "msie6";
    gzip_http_version 1.0;
    gzip_min_length 800;
    gzip_proxied any;
    gzip_types text/css application/x-javascript text/plain application/json;
    gzip_vary on;

    proxy_buffers 64 4k;
    proxy_connect_timeout 1s;
    proxy_hide_header ETag;
    proxy_http_version 1.1;
    proxy_intercept_errors on;
    proxy_max_temp_file_size 0;
    #proxy_next_upstream error timeout http_502 http_503 http_504;
    #proxy_next_upstream_tries 2;
    proxy_read_timeout 8s;
    proxy_send_timeout 3s;
    proxy_set_header Connection Keep-Alive;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;

    include /etc/nginx/conf.d/*.conf;
}
  • 因为要启用https,所以增加证书配置文件

申请https证书,这个可以去 Let’s Encrypt申请三个月免费证书,到期后需要再次申请,或者可以去购买收费证书,证书申请下来之后会有两个配置文件,例如:test.wtg.com.crt , test.wtg.com.key

新建https_test.wtg.com.conf文件

1
2
3
4
5
6
7
8
9
    ssl_certificate /etc/nginx/test.wtg.com.crt;
    ssl_certificate_key /etc/nginx/test.wtg.com.key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers '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;
  • conf.d文件夹中新建test.wtg.com.conf配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 应用地址
upstream backend_test_wtg {
    server 10.xxx.xxx.10:8080;
    keepalive 32;
}

# 监听http,转为https请求
server {
    listen 80;
    server_name test.wtg.com;
    return 301 https://$host$request_uri;
}

# https请求转到应用
server {
    listen 443 ssl;
    server_name test.wtg.com;

    include https_test.wtg.com.conf;

    access_log logs/access_test.wtg.com.ssl.log main;
    error_log logs/error_test.wtg.com.ssl.log warn;

    proxy_read_timeout 300s;
    
    location / {
        expires -1;
        proxy_pass http://backend_test_wtg;
    }
}

完成这三部配置之后,重启nginx服务器,域名代理就好了,当然这个时候在浏览器中输入test.wtg.com是无法访问系统的,因为test.wtg.com并不指向你的nginx服务器ip,如果想测试的话,可以在本机的hosts文件中加上 test.wtg.com 10.xxx.xxx.xxx 指向nginx服务器ip,然后进行访问。如果想用户直接通过域名访问的话,那就必须在dns域名系统中申请test.wtg.com指向对应的10.xxx.xxx.xxx了。实际上配置了nginx集群的时候,每台nginx都配置了两个ip,其中一个ip是公共的,域名都指向这个公共都ip,这个下面再细说。

多链路负载均衡

以前做负载均衡的时候,都是做应用服务器的负载均衡,请求通过nginx或apache代理服务器然后分配到其中一个应用服务,虽然应用是集群了,但是代理服务器是单点的。这样可以提高应用的并发,但是不能避免代理服务器宕机导致服务整体不可用的风险。这次是通过基础网络配置把代理服务器也做成集群,提升服务的可用性。

多链路负载均衡是通过frr(快速重路由)来实现的,比如三台nginx服务器都启用了eth0网卡,ip分别是10.10.10.1,10.10.10.2,10.10.10.3。三台服务器另外再启用一块网卡叫dummy0,dummy0都配置同一个ip:10.10.10.10。在网关服务器和三台nginx服务器上都通过frr配等价路由。然后将所有的域名都指向10,当访问域名的请求到达网关服务器的时候,就会被均衡分配到1,2,3中的某一台nginx服务器。

  • 在.1,.2,.3服务器上启用一块dummy0的网卡

    服务器启用网卡必须有对应的实体网卡才行,可以执行 ip addrifconfig 查看服务器的网卡信息,此处通过命令找到eth1这块未被启用的网卡

    nginx2-1

    进入/etc/sysconfig/network-scripts ,找到ifcfg-eth1文件,重命名为ifcfg-dummy0,并修改内容如下,其中IP地址为10.10.10.10

    nginx2-2

    进入/etc/udev/rules.d,修改70-persistent-net.rules文件,将eth1改为dummy0

    nginx2-3

    重启服务器,执行 ifconfig,可以看到新的IP已经添加成功

    nginx2-4

  • 在三台服务器上安装frr

    frr在yum默认仓库中找不到,我是从公司私有仓库中装的 yum install https://repo.ms.xxxxxxx.com/custom/rhel/7/x86_64/frr-6.0.3-01.el7.x86_64.rpm ,如果没有私有仓库,去github上直接下载安装包进行本地安装也行: frr官方GitHub

  • 在三台nginx服务器上配置frr

    到了frr配置这一步就有点复杂了,只知道frr是通过BGP ECMP协议来实现负载均衡的,网管不愿意给我们讲解这一块,网上资料也特别少,一头雾水,只能依葫芦画瓢进行配置。

    frr的配置通常是使用 vtysh 命令进进入控制台,然后使用专有命令进行配置,命令语法跟华为,思科这些厂商的硬件路由差不多,由于不会命令,所以没法一步步操作。好在frr会把所有配置写入配置文件中,可以把文件全部复制过来修改内容。

    通过yum安装的frr配置文件全部在/etc/frr文件夹中,复制过来之后对比发现只需要修改frr.conf和bgpd.conf两个文件,其他配置文件未启用功能所以不需要改。

    看配置文件意思大概是在nginx服务器上配置网关服务器作为邻居,同样的在网关服务器上也需要配置nginx服务器做为邻居,这样两边服务器一启动,网关服务器就能自动学习到相关路由,将请求均衡到三台nginx服务器上。

    配置文件很长又不懂原理,没法细说,在修改完成之后重启frr服务,vtysh 进入控制台,执行 show running 可以看到相关配置已经生效就行了。

    如果一切配置正确的话,在网关服务器上执行 ip route show 可以看到对应的nginx服务器路由,权重都是1

    nginx2-6

配置nginx监控服务

刚刚说到frr配置之后,网关服务器会自动生成到三台nginx服务器的路由,仔细想想这个实际上跟nginx服务没有任何关系,网关只是将请求均衡发送到对应三台nginx服务器端口,它并不知道nginx服务是不是正常的,即使其中一个nginx崩溃了,nginx服务器和端口还是正常的,请求一样会发送过来,那么分配到这台服务器的用户还是显示系统挂了,这就不是我们想要到效果了,我们想要的是一台nginx崩溃之后,就不往这台服务器发送请求了,改发到其他正常到nginx服务器。

那么怎么做呢?其实很简单,网关自动学习到nginx服务器的路由是通过frr配置的邻居来的,如果其中一个邻居“不见”了,那么自然没有到这个邻居的路由了,要让这个邻居“不见”,只需要把dummy0网卡卸载掉就好了,这样网关就不会收到这台服务器对外的广播,就不会生成对应的路由了。

linux卸载某块网卡的命令是 ifdown dummy0 ,启用网卡的命令是 ifup dummy0,通过shell写一个每5秒就执行一次的定时任务,每次执行都去请求niginx服务,一旦请求结果不对,就卸载网卡,请求正常就启用网卡。

在/home/tools目录下新建一个monitor_nginx可执行文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  #!/usr/bin/env bash
  PATH=/sbin:/bin:/usr/sbin:/usr/bin
  md5=`md5sum /etc/sysconfig/network-scripts/ifcfg-dummy0 | awk '{print $1}'`
  status="1"
  while :
  do
    curl --max-time 2 --retry 1 127.0.0.1 >/dev/null 2>&1
    if [ $? -ne 0 ] ; then
        ifdown dummy0
        logger -t `basename $0` "ifdown dummy0"
        status="2"
    else
        if [ $status == "2" -o `md5sum /etc/sysconfig/network-scripts/ifcfg-dummy0 | awk '{print $1}'` != $md5 ] ; then
            ifup dummy0
            logger -t `basename $0` "ifup dummy0"
            md5=`md5sum /etc/sysconfig/network-scripts/ifcfg-dummy0 | awk '{print $1}'`
        fi
        status="1"
    fi
    echo $$ >/var/run/`basename $0`.pid
    sleep 4
  done

可以将monitor_nginx配置到/etc/rc.d,这样开机会自动执行,就不用每次重启linux都去自己手动启动了。

nginx2-5

到此就结束nginx负载均衡都配置了,通过域名请求应用,在nginx的access日志文件中可以看到对应的记录,不同用户的请求被分配在不同的nginx服务器中。