Skip to main content

17.基于IP地理位置配置访问规则

使用 GeoIP2 动态模块,根据客户端的地理位置控制访问或将流量转发到不同的upstream服务器。

开源版NGINX 使用GeoLite2数据库来匹配用户的IP地址及其位置。

GeoIP2或GeoLite2数据库可以从MaxMind下载页面获得。在此示例中,使用 GeoLite2 免费下载数据库。

要获取和解压缩 GeoLite2 国家/地区数据库,请执行以下操作:

wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz
gunzip GeoLite2-Country.mmdb.gz

要获取和解压缩GeoLite2 City数据库,请执行以下操作:

wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz
gunzip GeoLite2-City.mmdb.gz

了解数据库结构(Understanding Database Structure)

要查看可用的地理数据,您可以使用 mmdblookup 实用程序查询 GeoLite2-Country 和 GeoLite2-City 数据库。地理数据表示为 JSON 树。

安装 libmaxminddb 数据库实用程序:

  • 对于 Amazon Linux、CentOS、Oracle Linux 和 RHEL:
    yum install libmaxminddb-devel

可以按以下格式发送对数据库的查询:

mmdblookup –file [FILE PATH] –ip [IP ADDRESS] [DATA PATH]

例如,要获取 IP 地址的所有可用地理数据,请发送以下命令:8.8.8.8

mmdblookup --file /usr/local/etc/geoip2/GeoLite2-Country.mmdb --ip 8.8.8.8

输出将是:

{
"continent":
{
"code":
"NA" <utf8_string>
"geoname_id":
6255149 <uint32>
"names":
{
"de":
"Nordamerika" <utf8_string>
"en":
"North America" <utf8_string>
"es":
"Norteamérica" <utf8_string>
"fr":
"Amérique du Nord" <utf8_string>
"ja":
"北アメリカ" <utf8_string>
"pt-BR":
"América do Norte" <utf8_string>
"ru":
"Северная Америка" <utf8_string>
"zh-CN":
"北美洲" <utf8_string>
}
}
"country":
{
"geoname_id":
6252001 <uint32>
"iso_code":
"US" <utf8_string>
"names":
{
"de":
"USA" <utf8_string>
"en":
"United States" <utf8_string>
"es":
"Estados Unidos" <utf8_string>
"fr":
"États-Unis" <utf8_string>
"ja":
"アメリカ合衆国" <utf8_string>
"pt-BR":
"Estados Unidos" <utf8_string>
"ru":
"США" <utf8_string>
"zh-CN":
"美国" <utf8_string>
}
}
"registered_country":
{
"geoname_id":
6252001 <uint32>
"iso_code":
"US" <utf8_string>
"names":
{
"de":
"USA" <utf8_string>
"en":
"United States" <utf8_string>
"es":
"Estados Unidos" <utf8_string>
"fr":
"États-Unis" <utf8_string>
"ja":
"アメリカ合衆国" <utf8_string>
"pt-BR":
"Estados Unidos" <utf8_string>
"ru":
"США" <utf8_string>
"zh-CN":
"美国" <utf8_string>
}
}
}

要获取特定的地理数据,例如,仅获取特定国家/地区的 ISO 代码,请将参数添加到命令末尾:country iso_code

mmdblookup --file /usr/local/etc/geoip2/GeoLite2-Country.mmdb --ip 8.8.8.8 country iso_code

在NGINX中配置GeoIP2

开源版NGINX不自带GeoIP2模块,需要重新手动编译:

  1. 获取 geoip2 模块源代码: 下载并解压 geoip2 模块的源代码。你可以从模块的 GitHub 仓库获取:

    git clone https://github.com/leev/ngx_http_geoip2_module.git

    ngx_http_geoip2_module 目录放置在 Nginx 源代码目录的 ./modules 下。

  2. 配置编译选项: 在 Nginx 源代码目录下执行配置命令,包含 geoip2 模块。

    ./configure --add-module=./modules/ngx_http_geoip2_module
  3. 编译和安装: 执行编译和安装命令。

    make
    make install
  4. 将国家和城市数据库的路径添加到 NGINX 配置中,其中包含 http {}upstream {} 或两者的 geoip2 {}块:

    http {
    #...
    geoip2 GeoIP2/GeoLite2-Country.mmdb {
    #...
    }

    geoip2 GeoIP2/GeoLite2-City.mmdb {
    #...
    }
    }

    stream {
    #...
    geoip2 GeoIP2/GeoLite2-Country.mmdb {
    #...
    }

    geoip2 GeoIP2/GeoLite2-City.mmdb {
    #...
    }
    }
  5. 基于 GeoIP 数据库结构,创建自定义变量,这些变量将保留 GeoIP2 数据库中的数据,然后将数据传递给地图split_clients 指令(可以在 <span> </span>http {}upstream {} 上下文中应用):

    geoip2 GeoIP2/GeoLite2-City.mmdb {
    $geoip2_data_city_name city names en;
    $geoip2_data_postal_code postal code;
    $geoip2_data_latitude location latitude;
    $geoip2_data_longitude location longitude;
    $geoip2_data_state_name subdivisions 0 names en;
    $geoip2_data_state_code subdivisions 0 iso_code;
    }

    geoip2 GeoIP2/GeoLite2-Country.mmdb {
    $geoip2_data_continent_code continent code;
    $geoip2_data_country_iso_code country iso_code;
    }

    #...

方案:选择最近的服务器(Scenario: Choosing the Nearest Server)

使用来自创建的变量的地理位置数据,可以将客户端连接重定向到最近的服务器,从而减少网络延迟并提高连接速度。

这可以通过在变量中使用GeoIP2数据库中的大陆(continent)代码来实现,地图模块(map module)将创建另一个变量,该变量的值将是基于大陆位置的最接近的服务器。基于此值,NGINX 会将请求传递给相应的upstream服务器组。

  1. 确保已为每个大洲配置服务器或upstream服务器组,例如,eu对于欧洲、na对于北美,all对于 IP 地址无法与 GeoIP 数据库匹配的情况:
    upstream all {
    server all1.example.com:12345;
    server all2.example.com:12345;
    }

    upstream eu {
    server eu1.example.com:12345;
    server eu2.example.com:12345;
    }

    upstream na {
    server na1.example.com:12345;
    server na2.example.com:12345;
    }
  2. 添加具有任何名称的变量(例如 $geoip2_data_continent_code)的 geoip2 {}块,以获取GeoIP2数据库的大陆代码:
    geoip2 GeoIP2/GeoLite2-Country.mmdb {
    $geoip2_data_continent_code continent code;
    }

    #...
  3. 创建地图(map)块将创建 $nearest_server变量
    #...
    map $geoip2_data_continent_code $nearest_server {
    default all;
    EU eu;
    NA na;
    AS as;
    AF af;
    }
    #...
  4. 创建 server{} 块,该块将根据 $nearest_server变量中传递的值将请求传递给upstream服务器组之一:
    server {
    listen 12346;
    proxy_pass http://$nearest_server;
    }

如果大陆是欧洲,那么 的值将是 ,并且连接将通过 proxy_pass 指令传递给上游:$nearest_servereueu

#...
server {
listen 12346;
proxy_pass http://$nearest_server;
}

upstream all {
server all1.example.com:12345;
server all2.example.com:12345;

upstream eu {
server eu1.example.com:12345;
server eu2.example.com:12345;
}
upstream na {
server na1.example.com:12345;
server na2.example.com:12345;
}
#...

示例

此示例可以应用于 httpupstream上下文。

# can be either "http {}" or "stream {}"
#...
geoip2 GeoIP2/GeoLite2-Country.mmdb {
$geoip2_data_continent_code continent code;
}

map $geoip2_data_continent_code $nearest_server {
default all;
EU eu;
NA na;
AS as;
AF af;
}

server {
listen 12346;
proxy_pass http://$nearest_server;
}

upstream all {
server all1.example.com:12345;
server all2.example.com:12345;
}

upstream eu {
server eu1.example.com:12345;
server eu2.example.com:12345;
}

upstream na {
server na1.example.com:12345;
server na2.example.com:12345;
}

在此示例中,将在数据库 GeoLite2-Country.mmdb中检查IP地址,结果将写入变量 $geoip2_data_continent_code。NGINX 会将变量的值与map指令中的值进行匹配,并将结果写入自定义变量 $nearest_server中,在我们的示例中。根据 $nearest_server的值,proxy_pass 指令将选择相应的upstream服务器。