nginx

nginx虚拟主机解决企业内外网访问

在企业里面部署服务,需要面临的一个问题就是不同企业复杂的网络环境。通常来说,私有云只需要在企业内部使用,但是也有很多企业需要通过外网能访问。同时,对于不同网络的访问请求,系统也需要进行不同的处理。譬如内网用户请求下载直接可以rewrite到对应的内网下载机上,但外网用户请求下载则可能需要通过代理进行。

因为我们的系统使用nginx作为网络总的入口,所以,自然通过部署nginx来解决内外网的访问问题。对于私有云产品来说,内网的nginx server是很好配置的,难点在于如何配置外网的server,因为外网有很多种网络环境,需要分别考虑。

基础知识

在进行配置之前,首先列举一些nginx的配置需要了解的基本知识。

首先,来看一个最简单的nginx配置

http {
    server {
        listen 192.168.1.10:80;
        server_name www.domain.com;
        location / {
            return 200 "Hello World";
        }
    }
}

在上面这个例子中,nginx启动了一个server,该server监听192.168.1.10的80端口,server_name为 www.domain.com。

ip和port大家很好理解,对于server_name,可以认为就是发送http请求header里面的host。

当外部发送 http://192.168.1.10:80/hello 这个http请求的时候,监听80端口的这个server会处理。

同时,我们也可以通过域名来访问,如http://www.domain.com/hello,因为www.domain.com跟server_name配置一样,所以nginx也能处理响应。当然,前提是企业必须配置dns将该域名指定到192.168.1.10上面。

对于任何http请求,nginx都是首先获取一个匹配的server,而具体nginx选择哪一个server,则是如下流程:

  • 通过listen的ip以及port确定server
  • 如果有多个server,则通过server_name再次确定
  • 如果仍然有多个,则按照配置顺序选择第一个

所以通过nginx来响应内外网的请求,也就是配置不同server的过程。

对于外网来说,通常来说有2种情况,具有独立的外网IP以及NAT映射的外网IP,如果提供了外网域名,通过域名解析的IP也仍然是上述两种情况。

独立外网IP

假设内网ip为192.168.1.10,实际的外网ip为10.20.189.217。

无域名

对于具有独立外网IP的机器来说,nginx很好配置。如下

server {
    listen 192.168.1.10:80;
    server_name 192.168.1.10;
}

server {
    listen 10.20.189.217:80;
    server_name 10.20.189.217;
}

可以看到,我们直接可以通过listen监听不同的ip来配置内外网server,

或者我们也可以通过如下方式:

server {
    listen 80;
    server_name 192.168.1.10;
}

server {
    listen 80;
    server_name 10.20.189.217;
}

这里,nginx监听同一个端口,通过对应的server_name来进行区分内外网。对于有独立外网IP的情况,建议使用前一种方式,直接listen ip:port。

有域名

如果有域名,那么我们可以在server_name中填入相应的域名信息。

server {
    listen 192.168.1.10:80;
    server_name www.domain.com;
}

server {
    listen 10.20.189.217:80;
    server_name www.domain.com;
}

可以看到,如果内外网都有相同的域名,那么listen的时候就必须得填入ip信息,如果只监听端口,nginx没法通过server_name区分内外网。

NAT外网IP

如果外网IP为NAT映射的,那么nginx是不能直接listen这个IP的。假设NAT映射端口仍然为80,外网NAT地址为10.20.189.217。

无域名

server {
    listen 80;
    server_name 192.168.1.10;
}

server {
    listen 80;
    server_name 10.20.189.217;
}

可以看到,无域名情况比较简单,我们可以通过server_name来区分内外网。

有不同域名

如果内外网有不同域名,那么情况也跟无域名一样,通过配置server_name区分。

server {
    listen 80;
    server_name www.domain1.com;
}

server {
    listen 80;
    server_name www.domain2.com;
}

有相同域名

如果内外网有相同域名,那么就不能通过监听同一个端口来区分内外网了。笔者能想到的做法就是nginx监听不同的端口。

server {
    listen 80;
    server_name www.domain1.com;
}

server {
    listen 10080;
    server_name www.domain2.com;
}

这里,通过监听10080来响应外网的请求,这样NAT的映射端口就需要改变。

end

可以看到,内外网的配置,其实就是nginx vhost的配置,而关键点就在于listen以及server_name。详细可以参考How nginx processes a requestServer names

版权声明:自由转载-非商用-非衍生-保持署名 Creative Commons BY-NC-ND 3.0

nginx性能优化

最近在测试服务器压力的时候,发现使用tornado的服务benchmark上不去,顶多1500左右,nginx即使开了8个进程,在响应请求的时候有一个work进程的cpu超高,达到100%的情况。

对于cpu超高的情况,当初我们都认为是2.6.18网卡中断只能在一个cpu上处理,导致cpu高,这虽然是一个原因,但是短期内升级整个系统是一个不太可能的事情。

鉴于官方说tornado性能很高,所以总觉得我们在某些地方使用有问题,看了nginx以及tornado的源码,发现有几个地方我们真没注意。

  • listen backlog,nginx默认的backlog是511,而tornado则是100,对于这种设置,如果并发量太大,因为backlog不足会导致大量的丢包。

    将nginx以及tornado listen的时候backlog改大成20000,同时需要调整net.ipv4.tcp_max_syn_backlog,net.ipv4.tcp_timestamps,net.ipv4.tcp_tw_recycle等相关参数。

  • accept_mutex,将其设置为off,nginx默认为on,是为了accept的解决惊群效应,但是鉴于nginx只有8个进程,同时并发量大,每个进程都唤醒都能被处理,所以关闭。

做了上面简单的两个操作之后,ab benchmark发现nginx的cpu负载比较平均,同时不会出现upstream request timeout以及cannot assign requested address等错误。

同时,直接压tornado也第一次达到了4000的rps,通过nginx proxy到tornado则在3200左右。

虽然只是修改了几个配置,性能就提升了很多,后续对于nginx,还有很多需要研究的东西。

版权声明:自由转载-非商用-非衍生-保持署名 Creative Commons BY-NC-ND 3.0