将Nginx作为TCP负载均衡器运行,实现金丝雀发布
首先
为了最小化对服务使用者的影响,我们通常会逐步推出新版本的服务(也被称为金丝雀发布或金丝雀部署)来部署策略。在服务开发中,将负载均衡器置于顶层以实现应用程序的冗余和负载分配是常见做法。如果负载均衡器可以根据权重分配流量,那么只使用负载均衡器即可实现金丝雀发布。然而,由于负载均衡器的功能不足或负载均衡器操作人员与服务开发人员的协作负担等各种因素,这种方法在某些情况下可能会变得困难。
在这种背景下,本文将介绍如何将Nginx配置为TCP负载均衡器,放置在负载均衡器和应用程序之间,实现金丝雀发布。
TCP/UDP 代理模块
为了将Nginx作为TCP负载均衡器运行,需要使用TCP/UDP代理模块。然而,由于官方提供的Nginx二进制文件中没有集成TCP/UDP代理模块,因此需要参考从源代码构建Nginx的指南,自己编译源代码。
建造步骤
从官方网站下载源代码。
cd /usr/local/src
sudo wget https://nginx.org/download/nginx-1.21.0.tar.gz
sudo tar -xf nginx-1.21.0.tar.gz
cd nginx-1.21.0
安装构建所需的库。
sudo yum install gcc
sudo yum install pcre-devel
sudo yum install openssl-devel
创建用于运行Nginx的系统组和系统用户。
sudo groupadd --system nginx
sudo useradd --system --shell /usr/sbin/nologin --gid nginx nginx
使用所需的选项来构建 Nginx 二进制文件。
sudo ./configure \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--pid-path=/var/run/nginx.pid \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--user=nginx \
--group=nginx \
--with-stream
sudo make
sudo make install
在使用Nginx作为TCP负载均衡器时,需要明确包含以下模块,但是本次只指定了将TCP/UDP代理模块包含进去作为最基本的选项。
$ ./configure --help | grep with-stream
--with-stream enable TCP/UDP proxy module
--with-stream=dynamic enable dynamic TCP/UDP proxy module
--with-stream_ssl_module enable ngx_stream_ssl_module
--with-stream_realip_module enable ngx_stream_realip_module
--with-stream_geoip_module enable ngx_stream_geoip_module
--with-stream_geoip_module=dynamic enable dynamic ngx_stream_geoip_module
--with-stream_ssl_preread_module enable ngx_stream_ssl_preread_module
参考NGINX的systemd服务文件,我们也会自己准备systemd的Unit文件。
cat << EOF | sudo tee /lib/systemd/system/nginx.service
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT \$MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
客户模块分割
要在Nginx上进行金丝雀发布,需要使用Split Clients模块。与TCP/UDP代理模块不同的是,Split Clients模块默认已嵌入到Nginx二进制文件中,因此无需自行构建。
分割客户端模块使用MurmurHash2来计算由第一个参数指定的变量(此处支持核心模块的变量),并从该值中返回与比例相关的值。因此,我认为最好指定客户端的IP地址存储为$remote_addr。
split_clients "${remote_addr}" $variant {
10% one;
20% two;
* other;
}
虽然这仅仅是小细节,但在下面的文章中了解到“Split Clients Module”是应用于“map”指令的,这让我受益良多。
设定文件示例
以下是配置,将30%的流量分配给version1组(192.168.100.10、192.168.100.11、192.168.100.12),将70%的流量分配给version2组(192.168.100.20、192.168.100.21、192.168.100.22)。
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
stream {
log_format basic '[$time_local] $remote_addr:$remote_port -> $upstream_addr '
'$protocol $status $bytes_sent $bytes_received '
'$session_time';
access_log /var/log/nginx/access.log basic;
split_clients $remote_addr:$remote_port $upstream {
30% version1;
* version2;
}
upstream version1 {
server 192.168.100.10:8080;
server 192.168.100.11:8080;
server 192.168.100.12:8080;
}
upstream version2 {
server 192.168.100.20:8080;
server 192.168.100.21:8080;
server 192.168.100.22:8080;
}
server {
listen 8080;
proxy_pass $upstream;
}
}
另外,日志配置参考了 ngx_stream_log_module 模块的示例。由于以下变量已被定义为可用的日志格式,可以根据需求进行设计。
-
- https://nginx.org/en/docs/stream/ngx_stream_core_module.html#variables
- https://nginx.org/en/docs/stream/ngx_stream_upstream_module.html#variables
如果配置文件准备好了,可以使用以下命令启动Nginx。
sudo systemctl daemon-reload
sudo systemctl enable nginx
sudo systemctl start nginx
只要从高级负载均衡器中将流量引导到Nginx,就完成了。在启动金丝雀发布之后,建议根据每个版本的应用程序度量和服务使用者的影响效果来逐渐调整比例。
最后
本次介绍了将Nginx作为TCP负载均衡器,实现金丝雀发布的方法。这可能是一项小众的知识,但如果可以对某些人有所帮助,我将感到幸运。
请参考提供的资料。
-
- https://www.nginx.com/blog/nginx-and-devops-methodologies-go-hand-in-hand/
-
- https://www.digitalocean.com/community/tutorials/how-to-target-your-users-with-nginx-analytics-and-a-b-testing
-
- https://docs.nginx.com/nginx/admin-guide/load-balancer/tcp-udp-load-balancer/
- https://engineering.mercari.com/blog/entry/2016-08-17-170114/