nginx geo信息分析

使用场景

在 Nginx 中启用 GeoIP2 模块,记录用户访问日志,并通过 Python 脚本进行二次分析。

模块安装

在 Ubuntu 20+ / 22+ 系统中,GeoIP2 模块可以直接通过 apt 安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 装模块(Ubuntu 20+/22+ 自带)
sudo apt install libnginx-mod-http-geoip2

# 下载免费的城市库
sudo mkdir -p /usr/share/GeoIP
sudo wget -O /usr/share/GeoIP/GeoLite2-City.mmdb \
https://git.io/GeoLite2-City.mmdb

# 自动补缺失依赖
sudo apt install -f

sudo dpkg --configure -a

文件配置

  1. 在 nginx.conf 中启用 geo 变量

在 http {} 段中加入如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
http {
...
geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb {
auto_reload 5m;
$geo_country country names en;
$geo_region subdivisions 0 names en;
$geo_city city names en;
}

log_format geo_combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'Country=$geo_country Region=$geo_region City=$geo_city';

access_log /var/log/nginx/access_geo.log geo_combined;
...
}

  1. 在 server 块中单独启用日志
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    server {
    listen 443 ssl http2;
    ...
    ....
    ...
    # ⭐ 记录带有地理信息的日志
    access_log /var/log/nginx/xxxx_geo.log geo_combined;

    location / {
    try_files $uri $uri/ =404;
    }
    }

3.启动与验证

1
2
3
4
5
6
7
8
9
10
# 检查配置是否正确
sudo nginx -t

# 重新加载配置
sudo systemctl reload nginx

# 查看实时日志
tail -f /var/log/nginx/xxxx_geo.log


可能端口已经被占用,可以先暂停端口监听:

1
2
3
sudo lsof -ti:80  | xargs -r sudo kill -9
sudo lsof -ti:443 | xargs -r sudo kill -9

如果输出为空说明端口已释放

日志分析

  • 自定义日志解析脚本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    #!/usr/bin/env python3
    import re, csv, os, glob, gzip
    from user_agents import parse
    from datetime import datetime, timedelta

    LOG_DIR = '/var/log/nginx'
    LOG_PAT = 'access_geo.log*' # 支持 *.gz
    OUT_DIR = os.path.expanduser('~/nginx_geo_daily')

    # 昨天的日期
    day_str = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
    out_csv = os.path.join(OUT_DIR, f'{day_str}.csv')

    # 正则:IP、时间、UA、Country、Region、City
    line_re = re.compile(
    r'(?P<ip>\d+\.\d+\.\d+\.\d+) .*?\[(?P<dt>[^\]]+)\] '
    r'".*?" \d+ \d+ ".*?" "(?P<ua>[^"]+)" '
    r'Country=(?P<country>[^ ]+) Region=(?P<region>[^ ]+) City=(?P<city>[^ ]+)'
    )

    rows = []
    for path in glob.glob(os.path.join(LOG_DIR, LOG_PAT)):
    opener = gzip.open if path.endswith('.gz') else open
    with opener(path, 'rt', encoding='utf-8', errors='ignore') as f:
    for line in f:
    m = line_re.search(line)
    if not m:
    continue
    ua = parse(m.group('ua'))
    rows.append({
    'datetime' : m.group('dt'),
    'ip' : m.group('ip'),
    'device' : ua.device.family,
    'os' : ua.os.family,
    'browser' : ua.browser.family,
    'country' : m.group('country'),
    'region' : m.group('region'),
    'city' : m.group('city'),
    })

    # 去重 & 排序
    rows = sorted({(r['ip'], r['datetime']): r for r in rows}.values(),
    key=lambda r: r['datetime'])

    os.makedirs(OUT_DIR, exist_ok=True)
    with open(out_csv, 'w', newline='', encoding='utf-8') as f:
    writer = csv.DictWriter(f,
    fieldnames=['datetime','ip','device','os','browser','country','region','city'])
    writer.writeheader()
    writer.writerows(rows)

    print(f'{len(rows)} records written to {out_csv}')


    安装与运行
    1
    2
    3
    4
    5
    6
    7
    sudo mkdir -p /usr/local/bin /var/log/nginx/geo_daily
    sudo tee /usr/local/bin/nginx_geo_daily.py < nginx_geo_daily.py
    sudo chmod +x /usr/local/bin/nginx_geo_daily.py

    # 手动执行 在/home/xxx/nginx_geo_daily/ 生成文件
    /usr/local/bin/nginx_geo_daily.py

  • 定时任务

每天23:05执行

1
2
3
4
5
crontab -e

5 23 * * * /usr/bin/python3 /usr/local/bin/nginx_geo_daily.py >> ~/nginx_geo_daily/cron.log 2>&1