通过Cookie Insertion实现Nginx会话保持

2017-07-29  本文已影响499人  bluexiii

Nginx负载均衡的几种策略

使用开源版本的Nginx可以轻松实现7层(HTTP)或4层(TCP)的负载均衡。

当做为7层负载时,最常用的有以下几种策略:

前两种方案(轮询和最少连接)同一客户端的请求会被随机分配后不同的后端服务器上,如果后端是传统的有状态的WEB应用,则需要在后端WEB容器上配置Session共享,非常麻烦。

后一种方案(IP哈希)可以识别请求端的IP,固定将其分配到某一台后端服务器上,轻松解决了会话保持的问题。但IP哈希存在一个比较严重缺陷,即:客户端必须能够直连Nginx服务器,他们之间不能再插入其它层级,否则Nginx就识别不到客户端的IP了。

那除了IP HASH外,还有没有其它方式能够进行会话保持呢?

Cookie Insertion

以下是官网的关于Cookie Insertion的介绍:

Cookie Insertion:NGINX Plus adds a session cookie to the first response from the upstream group to a given client, identifying which server generated the response (in an encoded fashion). Subsequent requests from the client include the cookie value and NGINX Plus uses it to route the request to the same upstream server:

Cookie植入:

  1. 客户端第一次访问时,负载均衡服务在返回请求中植入cookie(在HTTP/HTTPS响应报文中插入)
  2. 下次客户端携带此cookie访问,负载均衡服务会将请求定向转发给之前记录到的后端服务器上。

Cookie植入原理上就是劫持HTTP请求,伪造cookie,它可以在Nginx无法获取客户端IP时,也依然能够完成会话保持的功能,只要客户端支持Cookie即可。

购买并使用Nginx Plus

要使用Cookie Insertion,最简便的方式是使用商业版本的Nginx Plus,默认支持。

以下是官网关于会话保持的介绍:
https://www.nginx.com/products/session-persistence/

Cookie Insertion的配置非常简单:

upstream backend {
    server webserver1;
    server webserver2;
    sticky cookie srv_id expires=1h domain=.example.com path=/;
}

于是顺道了解了一下Nginx Plus的来龙去脉:

NGINX Plus is the all-in-one application delivery platform for the modern web.
All-In-One的NGINX PLUS对比开源版本重点增加了若干企业特性,包括更完善的七层、四层负载均衡,会话保持,健康检查,实时监控和管理等。

可看了一下最便宜的BASIC版本也要2500刀每年,泪奔,果断PASS。
https://www.nginx.com/products/buy-nginx-plus/

使用第三方模块nginx-sticky-module

流传最广的是一个名为 nginx-sticky-module 的第3方的模块 ,不过已经有多年没有维护。

GoogleCode页面(需扶墙): https://code.google.com/archive/p/nginx-sticky-module/downloads

下载 nginx-sticky-module-1.1.tar.gz ,看看能否配合较新的 nginx-1.12.1 使用。

Nginx下载地址: https://nginx.org/en/download.html

选择Stable version中的nginx-1.12.1

简单编译一下试试:

./configure --prefix=/nginx/nginx-1.12.1 --add-module=/nginx/src/nginx-sticky-module-1.1  
make && make install

果然报错:

/nginx/src/nginx-sticky-module-1.1/ngx_http_sticky_module.c: In function ‘ngx_http_get_sticky_peer’:
/nginx/src/nginx-sticky-module-1.1/ngx_http_sticky_module.c:333: error: assignment makes pointer from integer without a cast
make[1]: *** [objs/addon/nginx-sticky-module-1.1/ngx_http_sticky_module.o] Error 1
make[1]: Leaving directory `/nginx/src/nginx-1.12.1'
make: *** [build] Error 2

代码太老旧,没有细查的必要了,弃用。

使用第三方模块nginx-sticky-module-ng

另外还有一个比较新的模块 nginx-sticky-module-ng ,源码托管在BitBucket上:
https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/

在Downloads/Tags下,找到1.2.6的源码并下载,或者直接下载Master分支上最新的代码。

在编译之前,首先来动手修复一个BUG。。。
找到ngx_http_sticky_misc.c,添加以下两个include:

#include <openssl/sha.h>
#include <openssl/md5.h>

第三方的东西凑和着用吧。。。

编译一下试试:

./configure --prefix=/nginx/nginx-1.12.1 --add-module=/nginx/src/nginx-sticky-module-ng
make && make install

编译很顺利,配置一下试试吧:
配置上非常简单,只需要在upstream{}中加入sticky即可。

upstream {
  sticky;
}

Sticky的详细配置说明:
sticky [name=route] [domain=.foo.bar] [path=/] [expires=1h]
[hash=index|md5|sha1] [no_fallback] [secure] [httponly];

下面给出一个完整的配置:

worker_processes  4;

events {
    worker_connections 1024;
}

http {
    upstream proxy_admin {
        #ip_hash;
        sticky;
        server 192.168.1.61:19022 weight=10 max_fails=3 fail_timeout=20s;
        server 192.168.1.62:19022 weight=10 max_fails=3 fail_timeout=20s;
    }

    server {
        listen       29022;
        server_name  proxy_admin;
        location / {
            root   html;
            proxy_pass   http://proxy_admin;
            proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
            proxy_redirect off;
            proxy_ignore_client_abort on;
            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_connect_timeout 60;
            proxy_send_timeout 60;
            proxy_read_timeout 60;
            proxy_buffer_size 4k;
            proxy_buffers 4 32k;
            proxy_busy_buffers_size 64k;
            proxy_temp_file_write_size 64k;
            client_body_buffer_size 128k;
        }
    }
}

测试,用浏览器访问一下试试:
http://IP:29022/mobagent-admin/
如果在控制台中的Cookie中看到一个名为route的键,就说明基本上成功了。
然后多请求几次,看看后端服务的日志,最终确认一下,是不是只指向了同一台服务器。

注意事项

最后,请注意,因为nginx-sticky-module-ng的可靠性未经长时间验证,请勿直接用于生产系统。

上一篇 下一篇

猜你喜欢

热点阅读