nginx安装、转发tcp、udp请求配置与遇到的问题 - Go语言中文社区

nginx安装、转发tcp、udp请求配置与遇到的问题


最近在折腾转发tcp请求,原本我是用的是HAproxy,换了服务器之后本来想继续使用HAproxy的,不知怎么的在阴差阳错之下查到了nginx(版本号在1.11之后的)也支持tcp、udp请求的转发和负载均衡了,本着无限折腾的精神,我就上手了nginx。本文由以下几个部分组成:

本文使用的系统是ubuntu16.04,其他linux系统具体的操作请参阅linux下安装nginx

  • ubuntu下nginx最新版的安装
  • tcp转发与负载均衡的配置
  • 根据请求域名转发tcp之我的尝试与屈服

一、ubuntu下的nginx最新安装

nginx现在最新的stable的版本号是1.12.2,如果偷懒直接在ubuntu下面使用

apt-get install nginx

进行安装,很不幸,安装出来的版本是1.10.*,差两个大版本啊。所以只能安装官方给的指导安装啦。

1.1 为ubuntu增加源

1.1.1 源内容

## Replace $release with your corresponding Ubuntu release.
deb http://nginx.org/packages/ubuntu/ $release nginx
deb-src http://nginx.org/packages/ubuntu/ $release nginx

请注意这里面的$release是变量,具体的值根据不同的ubuntu版本来填写

ubuntu版本$release
16.04xenial
14.04trusty
12.04precise
17.04zesty

所以我们比较常用的16.04的源如下:

deb http://nginx.org/packages/ubuntu/ xenial nginx
deb-src http://nginx.org/packages/ubuntu/ xenial nginx

1.1.2 增加源

这里有两种办法

  1. 直接在/etc/apt/sources.list后面加
  2. 在/etc/apt/sources.list.d这个文件夹里面新建一个list文件,比如新建nginx.list

我在这里选择第二种,新建nginx.list,在里面粘贴16.04的源的内容

1.2 开始安装

执行以下bash命令

sudo apt-get update
sudo apt-get install nginx

如果报错

W: GPG error: http://nginx.org/packages/ubuntu xenial Release: The following signatures couldn’t be verified because the public key is not available: NO_PUBKEY ABF5BXXXXXD9BF6

注意报错的最后,是PUBKEY的值,后续的操作需要使用到这个值,我们把这个值使用$key来表示。例如,$key=ABF5BQWEDSD9BF6。有了这个值之后我们执行以下的命令:

## 使用你的NO_PUBKEY的值来替换掉这里的$key,例如:ABF5BQWEDSD9BF6
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $key
sudo apt-get update
sudo apt-get install nginx

安装完成了之后可以运行,有可能需要执行两遍apt-key的命令,我安装的时候就是执行了这个命令两次,有两个不同的$key的值。

nginx -v

来查看nginx的版本,不出意外因该是1.12.2

二、tcp转发与负载均衡的配置

2.1 tcp转发的配置

nginx的tcp转发比较简单,具体的配置文件如下:

stream {

    server {
        ## ipv6监听80端口 listen [::1]:80;
        listen 2018; ##这个没有具体测试,不知道是否能够监听ipv6,ipv4肯定能够监听
        proxy_pass nas.mm**.win:2020;##把请求转发至nas.mm**.win这个服务器
    }
}

stream这个块在nginx的配置文件的顶级块里面,和原来http的块同属一个层级,如果需要监听udp的端口,在listen 2018的后面添加udp,具体如下:

server {
        ## 监听udp端口
        listen 2019 udp; 
        proxy_pass *.com;
}

其他的高级设置参见nginx tcp、udp转发的其他设置

2.2 tcp负载均衡的配置

负载均衡的设置也是非常简单的,值是把原来的proxy_pass由一个服务器转移到一整个服务器组。具体的配置如下:

stream{ 
    upstream nas_group {
        #可以根据hash值来选择去某个服务器
        server 127.0.0.1:28000;#这里还可以配置weight
        server 127.0.0.1:28001;
    }


    server {
        listen nas.****.win:80;
        proxy_pass nas_group;
    }
}

更加详细的配置请参考nginx tcp负载均衡配置

三、nginx根据请求域名转发tcp请求之我的尝试与屈服

我捣鼓nginx的目的不是为了做tcp的负载均衡,对于一个个人的玩家来说,我也没有那么大的并发需求,需要用得到负载均衡。我的主要目的是实现监听某个端口,例如80端口,当不同的请求从不同的域名上面来的时候,我们分配到不同的后端服务器去处理。

这个目标在nginx做http请求转发的时候很容易就实现出来,直接在server里面增加server_name这个字段,指定域名的访问。但是我的目标是转发tcp请求也实现一样的效果。于是我开始各种资料查询,stackoverflow、官方文档、知乎。。。

3.1 摸索过程

3.1.1 增加server_name字段

这个很容易想到,nginx转发http的时候,在server里面增加server_name字段,就可以实现监听同一个端口,不同域名请求转发到不同的后端服务器。所以我在转发tcp请求的时候也增加server_name字段,结果可想而知,配置文件错误,nginx没法运行起来。

3.1.2 通过使用map这种相对而言比较高级的东东

在server_name的失败尝试之后,我在stackoverflow上面看到了跟我一样需求的人的问题,别人下面给出了解决方案。
这个问题的地址如下:
https://stackoverflow.com/questions/34741571/nginx-tcp-forwarding-based-on-hostname/44821204#44821204
第一个回答者告诉提问者,是做不到的,然后给出了一堆堆东西。但是后面两个回答者给出了非常具有参考价值的回答,具体内容如下:

stream {
    upstream pod53{
        server 10.1.5.3:3306;
    }
    upstream pod54{
        server 10.1.5.4:3306;
    }

    map $server_addr $x {
        192.168.168.238 pod53;
        192.168.168.239 pod54;
    }
    server {
        listen 3306;
        proxy_pass $x;
    }
}

一看到这个答案,很容就可以理解map,map 后面是两个变量,经查询文档得知第一个变量一般是系统内置的变量,第二个变量应该是我们可以随便起名字然后使用的变量,楼主在这个地方使用了$server_addr的这个系统变量,这个变量是指当前服务器的ip地址,这里有两个ip地址,所以可以猜测到这个服务器有两个ip地址,这两个ip地址都是指到这个服务器的。这里实现了不同的服务器ip地址转发到不同的服务器上面。

这个答案就与我需要的不同的域名到不同的后端服务器非常相近了,这里是不同的服务器地址,于是我就去查询文档,希望看到更多的内置变量,拿到更多的数据。经过一番查找之后,我找到了stream这个块下面的所有内置变量,具体的请参阅http://nginx.org/en/docs/stream/ngx_stream_core_module.html,在这个里面我找到了几个可能跟我们需求相关的变量:

变量名解释
hostname主机名称
remote_addr远程地址
server_addr服务器地址

很高兴我看到了hostname这个变量,这不就是域名么,于是我就开始尝试了,我把配置文件修改如下:

map $hostname $x {
        nas.****.win pod53;
        vps.****.win pod54;
}

修改完了之后就开始高高兴兴的测试,发现了nginx没有报错,正常跑起来了,可是测试请求转发的时候发现请求没有转发成功。于是我就去检查日志,看到了日志里面是这样的:

2018/03/16 05:58:45 [info] 31767#31767: *66 client 123.179.5.121:8386 connected to 0.0.0.0:80
2018/03/16 05:58:45 [error] 31767#31767: *66 no host in upstream "", client: 123.179.5.121, server: 0.0.0.0:80, bytes from/to client:0/0, bytes from/to upstream:0/0

这个里面不断提示no host in upstream,这个时候我就慢慢明白了自己的错误。

3.2 释怀与理解

为啥在转发http时候有server_name这个参数,为啥在转发tcp时候没有这个参数,为啥获取tcp的hostname获取不到,为啥server_addr又是可以拿得到的?这个跟http和tcp本身有关,tcp在7层网络结构的第4层,而http在网络结构的第7层。下面我们仔细分析一下:

3.2.1 http报文

下面我们来看一下http的典型报文结构:
http报文
报文的结构比较复杂,一般我们使用到的字段不定,所以报文头的长度也是不定的,下面我们看一下具体的实际的报文内容:

GET /biyeymyhjob/archive/2012/07/28/2612910.html HTTP/1.1
Host: www.cnblogs.com
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: https://www.google.com/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
If-Modified-Since: Fri, 16 Mar 2018 06:36:28 GMT

这是一段真正的http报文头,里面有一个很重要的头部字段Host,这个字段说明了我们请求的是什么域名,所以nginx可以根据这个字段来分析我们需要被转发到哪个具体的后端服务器。完整的http协议的解释参见http报文详解

3.2.2 tcp报文

看完了应用层的报文,我们再看看传输层的tcp报文tcp报文这个里面最重要的就是端口号到端口的,压根就没有host之类的字段,那么只有端口,数据是怎么传递的呢?这个需要我们再往前面看一层,也就是网络结构的第三层:
这里写图片描述
ip
由上面这两张图片,我们就很清楚了,tcp的整个报文封装在ip报文的数据部分,ip报文头里面有详细的源ip,目的ip,所以我们拿得到tcp报文的时候是可以查询得到ip请求从哪里来,当前服务器的ip地址。但是,我们完全不知道当前请求的服务器ip对应的域名是多少,这个东西只在http报文里面封装过,但是我们是tcp请求的转发,所以拿不到域名信息。所以我们想根据域名来转发tcp请求的目标落空了,但是根据服务器ip来转发还是可以实现的。

四、结束语

虽然最后我想做的没有成功,但是我还是很高兴,这次摸索又学到了不少的知识。其实对于网络的分层结构和各层都在干什么我们在计算机网络里面详细地学了,长时间不用只是忘记了而已。这次学到了不少的东西。

本文如若存在疏漏,欢迎指出,作者水平有限,疏漏在所难免。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/zhang201322/article/details/79580184
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-12-11 19:12:33
  • 阅读 ( 1994 )
  • 分类:Go Web框架

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢