kafka通过nginx对外代理需求配置
网络介绍
在工作中,有一需求将flink中日志投递到kafka中进行消费,由于两个中间件所处网络复杂,之间无法直接互通,因为借助nginx进行代理kafka,供flink进行投递。具体网络详情如下:
flink(后面称客户端,因为这不局限于flink,也可以是任何业务或中间件)和Nginx1位于资源池1
kafka与Nginx2位于资源池2
客户端与Nginx1 相通,Nginx1与Nginx2相通,Nginx2与kakfa相通。
客户端<->nginx1<->nginx2<->kafka
问题背景
刚开始,完成网络配置后,尝试进行消息投递,结果是投递失败。在排查问题时,发现一个现象:客户端所在机器会与kafka所在机器进行三次握手操作,一直卡在SYN_SEND环节。flink所在机器与kafka所在机器必然不通,之间不可能建立连接进行通信。全网很多方案都没讲清这样配置方案,因此本博客详细讲述下。
为解决上述三次握手一直卡在SYN_SEND阶段的问题,研究了下客户端为何去直接请求kafka所在机器IP。原因为:发现在投递时,客户端会根据kafka config/server.properties
文件中advertised.listeners
配置进行二次请求(这个很关键),具体步骤如下:
- 客户端请求nginx1, nginx1会将流量发给nginx2, nginx2再给kafka建立连接,并返回
advertised.listeners
的url - 客户端拿到advertised.listeners的ip,发现请求不通。
advertised.listeners 配置项用于告诉客户端可以如何访问 Kafka。每个条目可以指定不同的协议、主机和端口。
解决方案
- 配置两处nginx,nginx需要支持四层stream代理,若不支持可以先找博客解决下
- kafka配置
- hosts配置,包括客户端和kafka端,如果客户端服务在k8s里,可以修改coredns。非k8s修改 /etc/hosts即可。
首先是两处nginx的配置:
nginx共同处配置
# 配置 nginx.conf
# stream的配置可以不同,大家可以根据自己的配置来,也就是正常的反向代理配置
stream {
log_format proxy '$time_iso8601|$remote_addr|$protocol|$status|'
'$bytes_sent|$bytes_received|$session_time|$upstream_addr|$upstream_bytes_sent|'
'$upstream_bytes_received|$upstream_connect_time';
map $time_iso8601 $logdate {
'~^(?<ymd>\d{4}-\d{2}-\d{2})' $ymd;
default 'date-not-found';
}
access_log /var/log/nginx/tcp_access_$logdate.log proxy;
include /etc/nginx/stream.d/*.conf;
}
nginx1
upstream nginx1 {
server nginx2机器ip:port;
}
server {
listen 9093;
proxy_pass nginx1;
}
nginx2
upstream nginx2 {
server kafka的ip:port
}
server {
listen 9093; (端口自拟)
proxy_connect_timeout 5s;
proxy_timeout 300s;
access_log /var/log/nginx/tcp_access_$logdate.log proxy;
proxy_pass nginx2;
}
kafka配置和hosts配置
kafka配置和hosts配置很关键,也是整个博客精华所在。刚刚上面提到的客户端会拿advertised.listeners
配置的地址直接请求,如果配置成ip+port必然行不通,因此我们采用域名+port方式请求,当客户端拿到域名+port也会原封不动请求。这里客户端和kafka端域名必须相同。
有人就会质疑,那直接将advertised.listeners
配置成客户端所能访问的IP(nginx1 ip + port)不就可以了吗?大家可以试下,我这样kafka启动直接报错。
采用域名方式其实是为了欺骗kakfa和客户端,我们给kafka端配置的域名解析是kafka所能访问的ip+port(可以试试127.0.0.1 + port ,或者nginx2 ip + port 应该都是可以的),而给客户端所配置的域名解析是nginx1 ip + port。这样配置后,可以想象一下:
- 客户端请求nginx1, nginx1会将流量发给nginx2, nginx2再给kafka建立连接,并返回
advertised.listeners
的url - 客户端拿到advertised.listeners的url直接请求,这里的域名直接解析成nginx1的地址,完成通信。