Nginx + UpSync + Consul 实现 Dynamic Upstream

简介: Nginx + UpSync + Consul 实现 Dynamic Upstream

Nginx 是一款开源、高性能、高可靠的 Web 和反向代理服务器,性能是 Nginx 最重要的考量,其占用内存少、并发能力强。Nginx 最常见的使用场景就是反向代理,Nginx 接收客户端的请求并通过相应的负载均衡算法将流量转发给后端的多台应用服务器。image.png

传统做法

通常我们先会配置一个 upstream 地址池,包含后端的多台应用服务器,然后通过 proxy_pass 将流量分发给 upstream 中的成员。

http {
    upstream upstream_server{
        server 192.168.1.134:81;
        server 192.168.1.134:82;
    }
    server {
        listen       80;
        server_name localhost;
        location / {
            proxy_pass http://upstream_server;
        }
    }
}

假如现在由于应用服务器压力比较大,要新增一台服务器,那么需要修改 upstream 为:

upstream upstream_server{
    server 192.168.1.134:81;
    server 192.168.1.134:82;
    #新增的服务器
    server 192.168.1.134:83;
}

修改完成之后,需要通过 nginx -s reload 命令重新加载配置,才能使配置生效。虽然 Nginx 可以做到平滑地重载配置,但是每次应用服务器增加或删除时都要改动 Nginx 显得并不是那么智能。如果有大量的 Nginx 需要管理,每次都需要手动操作将会极大地增加运维的负担。

Dynamic Upstream

基于传统做法的弊端,我们引入了注册中心保存应用服务信息,Nginx 通过动态获取注册中心中的服务信息,更新 upstream 配置,无需人为干预和重启。实际生产应用中我们可以将 CMDB 和 注册中心整合,管理人员只需要在 CMDB 上维护应用服务信息即可。

Nginx 第三方模块 nginx-upsync-module 支持通过注册中心动态发现 upstream 信息。目前 nginx-upsync-module 模块支持 Consul 和 Etcd 作为 注册中心。

另外开源版本的 Nginx 默认只支持被动的健康检查,只有当客户端访问时,才会发起对后端节点的探测。假设本次请求中, Nginx 转发的后端节点正好出现了异常,Nginx 会将请求再转交给另一个 upstream 中的节点处理,所以不会影响到这次请求的正常进行,但是会影响效率,因为多了一次转发。并且自带模块无法做到预警。因此我们还使用了第三方模块 nginx_upstream_check_module 用于健康检查,该模块不仅支持主动的健康检查还提供了 WebUI 用于查看健康检查状态。image.png本示例 github 地址:https://github.com/cr7258/nginx-lab/tree/master/dynamic-upstream

目前还有其他产品支持动态配置,不仅仅是 upstream,还包括了其他方面的配置。例如 Nginx 的商业版本 Nginx Plus,和云原生结合得比较好的 Envoy、Kong、Traefik 等等。大家有兴趣可以自行了解。

注册中心 Consul 搭建

我们在一台虚拟机上启动 3 个 Consul 服务,组成一个伪集群。

下载安装包。

wget https://releases.hashicorp.com/consul/1.9.3/consul_1.9.3_linux_amd64.zip
unzip consul_1.9.3_linux_amd64.zip
mv consul /usr/local/bin/

创建相关目录:

mkdir /data/consul && cd $_
mkdir -pv /data/consul/node{1..3}

创建 3 个 Consul 节点使用的配置文件:

node1

vim /data/consul/node1/consul_config1.json

{
  "datacenter": "dev",
  "data_dir": "/data/consul/node1",
  "log_file": "/data/consul/node1/consul.log",
  "log_level": "INFO",
  "server": true,
  "node_name": "node1",
  "ui": true,
  "bind_addr": "192.168.1.134",
  "client_addr": "192.168.1.134",
  "advertise_addr": "192.168.1.134",
  "bootstrap_expect": 3,
  "ports":{
    "http": 8510,
    "dns": 8610,
    "server": 8310,
    "serf_lan": 8311,
    "serf_wan": 8312
    }
}

node2

vim /data/consul/node2/consul_config2.json

{
  "datacenter": "dev",
  "data_dir": "/data/consul/node2",
  "log_file": "/data/consul/node2/consul.log",
  "log_level": "INFO",
  "server": true,
  "node_name": "node2",
  "ui": true,
  "bind_addr": "192.168.1.134",
  "client_addr": "192.168.1.134",
  "advertise_addr": "192.168.1.134",
  "bootstrap_expect": 3,
  "ports":{
    "http": 8520,
    "dns": 8620,
    "server": 8320,
    "serf_lan": 8321,
    "serf_wan": 8322
    }
}

node3

vim /data/consul/node3/consul_config3.json

{
  "datacenter": "dev",
  "data_dir": "/data/consul/node3",
  "log_file": "/data/consul/node3/consul.log",
  "log_level": "INFO",
  "server": true,
  "node_name": "node3",
  "ui": true,
  "bind_addr": "192.168.1.134",
  "client_addr": "192.168.1.134",
  "advertise_addr": "192.168.1.134",
  "bootstrap_expect": 3,
  "ports":{
    "http": 8530,
    "dns": 8630,
    "server": 8330,
    "serf_lan": 8331,
    "serf_wan": 8332
    }
}

启动 Consul 集群:

nohup consul agent -config-file=/data/consul/node1/consul_config1.json > /dev/null 2>&1 &
nohup consul agent -config-file=/data/consul/node2/consul_config2.json -retry-join=192.168.1.134:8311 > /dev/null 2>&1 &
nohup consul agent -config-file=/data/consul/node3/consul_config3.json -retry-join=192.168.1.134:8311 > /dev/null 2>&1 &

启动之后,便可以通过 http://192.168.1.134:8510 访问,此处 192.168.1.134:8510 是 Leader 角色。image.png后端服务是用 Nginx 启动的 Web 服务。准备 3 个后端服务,IP 和 Port 分别是:

192.168.1.134:81
192.168.1.134:82
192.168.1.134:83

配置文件内容如下,3 个服务的配置基本一样,只是改了相关的端口和路径:

user root;
events{}
http{
server {
    listen       81 default_server;
    listen       [::]:81 default_server;
    server_name  127.0.0.1;
    root         /root/myapp/nginx/html/81;
    server_tokens off;
    gzip on;
    gzip_buffers 16 8k;
    gzip_comp_level 6;
    gzip_http_version 1.1;
    gzip_min_length 256;
    gzip_proxied any;
    gzip_vary on;
    gzip_types text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml text/javascript application/javascript application/x-javascript text/x-json application/json application/x-web-app-manifest+json text/css text/plain text/x-component font/opentype application/x-font-ttf application/vnd.ms-fontobject image/x-icon;
    gzip_disable "msie6";
    location / {
        expires max;  
        open_file_cache max=1000 inactive=20s; 
        open_file_cache_valid 30s; 
        open_file_cache_min_uses 2; 
        open_file_cache_errors on;
    }
    error_page 404 /404.html;
        location = /40x.html {
    }
    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
  }
}

html 文件如下:

[root@nginx-plus1 nginx]# cat html/81/index.html 
<html>
<head>
    <meta charset="utf-8">
    <title>server1</title>
</head>
<body style="background-color:blue;">
    <h1>Server 1 url 1<h1>
</body>
</html>

启动 3 个后端服务,在启动 Nginx 的时候指定配置文件即可:

sbin/nginx -c /root/myapp/nginx/81.conf
sbin/nginx -c /root/myapp/nginx/82.conf
sbin/nginx -c /root/myapp/nginx/83.conf

访问 3 个应用服务:image.pngimage.png实现 Dynamic Upstream 需要添加 nginx-upsync-modulenginx_upstream_check_module 两个第三方模块,在编译 Nginx 的时候要将这两个模块添加进去。这里准备了一个 Dockerfile,使用 docker build -t 镜像名:标签名 . 就可以构建出一个编译好的 Nginx Docker 镜像。

FROM debian:stretch-slim
RUN useradd  www && \
mkdir -p /logs/nginx/  /webserver/nginx /webserver/nginx/conf/upsync && \
chown -R www:www /logs/nginx/  /webserver/nginx && \
echo 'deb http://mirrors.163.com/debian/ stretch main non-free contrib' > /etc/apt/sources.list && \
echo 'deb http://mirrors.163.com/debian/ stretch-updates main non-free contrib' >> /etc/apt/sources.list && \
echo 'deb-src http://mirrors.163.com/debian/ stretch main non-free contrib' >> /etc/apt/sources.list && \
echo 'deb-src http://mirrors.163.com/debian/ stretch-updates main non-free contrib' >> /etc/apt/sources.list && \
echo 'deb-src http://mirrors.163.com/debian/ stretch-backports main non-free contrib' >> /etc/apt/sources.list && \
echo 'deb-src http://mirrors.163.com/debian-security/ stretch/updates main non-free contrib' >> /etc/apt/sources.list && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
apt-get update && \
apt-get install -y wget vim net-tools unzip libjemalloc-dev && \
apt-get build-dep -y nginx
RUN \
cd /usr/local/src/ && \
wget -c http://nginx.org/download/nginx-1.14.2.tar.gz && \
wget -c https://www.openssl.org/source/old/1.0.2/openssl-1.0.2m.tar.gz && \
wget -c https://github.com/simplresty/ngx_devel_kit/archive/v0.3.1rc1.tar.gz && \
wget -c https://github.com/openresty/lua-nginx-module/archive/v0.10.11.tar.gz && \
wget -c https://github.com/xiaokai-wang/nginx_upstream_check_module/archive/master.zip -O nginx_upstream_check_module.zip && \
wget -c https://github.com/weibocom/nginx-upsync-module/archive/master.zip -O nginx-upsync-module.zip && \
tar zxf ./nginx-1.14.2.tar.gz && rm nginx-1.14.2.tar.gz && \
tar zxf ./openssl-1.0.2m.tar.gz && rm openssl-1.0.2m.tar.gz && \
tar zxf ./v0.3.1rc1.tar.gz && rm v0.3.1rc1.tar.gz && \
tar zxf ./v0.10.11.tar.gz && rm v0.10.11.tar.gz &&  \
unzip ./nginx_upstream_check_module.zip && rm nginx_upstream_check_module.zip && \
unzip ./nginx-upsync-module.zip && rm nginx-upsync-module.zip
RUN \
cd /usr/local/src/nginx-1.14.2 &&\
patch -p1 < /usr/local/src/nginx_upstream_check_module-master/check_1.12.1+.patch &&\
./configure \
--prefix=/webserver/nginx \
--user=www --group=www --with-pcre \
--with-stream \
--with-http_v2_module \
--with-http_ssl_module \
--with-ld-opt=-ljemalloc \
--with-http_realip_module \
--with-http_gzip_static_module \
--with-http_stub_status_module \
--http-log-path=/logs/nginx/access.log \
--error-log-path=/logs/nginx/error.log \
--with-openssl=/usr/local/src/openssl-1.0.2m \
--add-module=/usr/local/src/ngx_devel_kit-0.3.1rc1 \
--add-module=/usr/local/src/lua-nginx-module-0.10.11 \
--add-module=/usr/local/src/nginx_upstream_check_module-master \ 
--add-module=/usr/local/src/nginx-upsync-module-master && \
make && \
make install

另外我也准备了一个已经构建好的镜像:registry.cn-shanghai.aliyuncs.com/public-namespace/nginx-dynamic-upstream:v1.0.0 ,可以直接拿来使用。

准备 Nginx 动态更新的配置文件

配置 nginx.conf 文件:

worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    upstream app {
        upsync 192.168.1.134:8510/v1/kv/upstreams/app/ upsync_timeout=6m upsync_interval=500ms upsync_type=consul strong_dependency=off;
        upsync_dump_path /webserver/nginx/conf/app.conf; # 当consul故障时候,就可以把此作为备份配置文件
        include /webserver/nginx/conf/app.conf; # 准备一个兼容的nginx测试文件,如果没有第一次启动会起不来
        check interval=1000 rise=2 fall=2 timeout=3000 type=http default_down=false;
        check_http_send "HEAD / HTTP/1.0\r\n\r\n";
        check_http_expect_alive http_2xx http_3xx;
        }
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://app;
        }
        location /upstream_list {
            upstream_show;
        }
        location /upstream_status {
            check_status;
            access_log off;
        }
    }
}

app.conf 文件里随便写上一个 IP 和 Port 信息,可以是无法访问的服务,因为 Nginx 的 upstream 中必须要有地址才能启动 Nginx。我们后面会通过在 Consul 上注册服务让 Nginx 动态更新 Upstream。

server 0.0.0.0:12345 weight=1 max_fails=2 fail_timeout=10s;

在本地的 Mac 电脑上通过 Docker 启动 Nginx 容器:

docker run -d --name nginx-dynamic-upstream \
-v /Users/chengzhiwei/lab/docker-lab/nginx/dynamic-upstream/nginx.conf:/webserver/nginx/conf/nginx.conf \
-v /Users/chengzhiwei/lab/docker-lab/nginx/dynamic-upstream/app.conf:/webserver/nginx/conf/app.conf \
-p 80:80 -p 443:443 \
registry.cn-shanghai.aliyuncs.com/public-namespace/nginx-dynamic-upstream:v1.0.0 \
/webserver/nginx/sbin/nginx  -g "daemon off;"

通过 curl 命令发送 HTTP 请求往 Consul 中注册两个新的服务。

curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10, "down":0}' http://192.168.1.134:8510/v1/kv/upstreams/app/192.168.1.134:81
curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10, "down":0}' http://192.168.1.134:8510/v1/kv/upstreams/app/192.168.1.134:82

通过 http://localhost/upstream_list 查看 upstream 主机:image.pngimage.pngimage.png

#查看监听 81 端口的进程号
[root@nginx-plus1 nginx]# lsof -i:81
COMMAND   PID USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
nginx   26047 root    6u  IPv4 202389912      0t0  TCP *:81 (LISTEN)
nginx   26047 root    7u  IPv6 202389913      0t0  TCP *:81 (LISTEN)
nginx   26048 root    6u  IPv4 202389912      0t0  TCP *:81 (LISTEN)
nginx   26048 root    7u  IPv6 202389913      0t0  TCP *:81 (LISTEN)
#停止服务
[root@nginx-plus1 nginx]# kill 26047

此时查看健康检查状态,发现 81 端口的服务已经被置为 down 了。image.png

参考链接


目录
相关文章
|
负载均衡 算法 应用服务中间件
Nginx系列教程(08) - Upstream Server 负载均衡
Nginx系列教程(08) - Upstream Server 负载均衡
678 0
9kr
|
应用服务中间件 网络安全 nginx
通过宝塔Nginx反代HomeAssistant并添加SSL实现隐藏端口号与域名访问
HomeAssistant默认使用8123端口,带端口访问既不美观也不方便。 通过宝塔Nginx默认反代配置会出现各种意外错误,本文将通过修改HomeAssistant与反代配置解决该问题。
9kr
3937 1
通过宝塔Nginx反代HomeAssistant并添加SSL实现隐藏端口号与域名访问
|
7月前
|
网络协议 应用服务中间件 nginx
解决 nginx 启动错误host not found in upstream "XXXX.com“
在前置机上利用nginx进行反向代理的时候,我们会配置proxy_pass。在启动nginx的会报如下错误:host not found in upstream "XXXX.com“
4089 0
|
1月前
|
负载均衡 应用服务中间件 nginx
基于Nginx和Consul构建自动发现的Docker服务架构——非常之详细
通过使用Nginx和Consul构建自动发现的Docker服务架构,可以显著提高服务的可用性、扩展性和管理效率。Consul实现了服务的自动注册与发现,而Nginx则通过动态配置实现了高效的反向代理与负载均衡。这种架构非常适合需要高可用性和弹性扩展的分布式系统。
37 4
|
1月前
|
负载均衡 应用服务中间件 nginx
基于Nginx和Consul构建自动发现的Docker服务架构——非常之详细
通过使用Nginx和Consul构建自动发现的Docker服务架构,可以显著提高服务的可用性、扩展性和管理效率。Consul实现了服务的自动注册与发现,而Nginx则通过动态配置实现了高效的反向代理与负载均衡。这种架构非常适合需要高可用性和弹性扩展的分布式系统。
55 3
|
7月前
|
负载均衡 应用服务中间件 nginx
解决nginx配置负载均衡时invalid host in upstream报错
在Windows环境下,配置Nginx 1.11.5进行负载均衡时遇到问题,服务无法启动。错误日志显示“invalid host in upstream”。检查发现上游服务器列表中,192.168.29.128的主机地址无效。负载均衡配置中,两个服务器地址前误加了&quot;http://&quot;。修正方法是删除上游服务器列表和proxy_pass中的&quot;http://&quot;。问题解决后,Nginx服务应能正常启动。
575 4
解决nginx配置负载均衡时invalid host in upstream报错
|
缓存 负载均衡 Unix
Nginx深入详解之upstream分配方式
Nginx深入详解之upstream分配方式
701 0
|
负载均衡 应用服务中间件 nginx
Nginx之upstream被动式重试机制解读z
Nginx之upstream被动式重试机制解读z
|
应用服务中间件 nginx 数据中心
理解Registrator、Nginx、Consul架构与SpringCloud Feign、grpc、rest通信之间的不同点
在互联网应用领域,服务的动态性需求十分常见,这就对服务的自动发现和可动态扩展提出了很高的要求。
168 0
|
应用服务中间件 nginx
Nginx 实战系列之四:upstream 的 max_fails 和 fail_timeout 指标和实战经验
Nginx 实战系列之四:upstream 的 max_fails 和 fail_timeout 指标和实战经验