Nginx: SNMPTRAPの流量制御と負荷分散を試みたけどダメでした。

やりたいこと

大量のTrapを受信するsnmptrapdの負荷を軽減するため、
負荷分散と送信元IPアドレス別の流量制御を行いたい。
BIG-IP等の高級品を使わず最近マイブームのNginxで無料でやりたい。

nginx.conf

stream {
    limit_req_zone $binary_remote_addr zone=snmp_zone:5m rate=10r/s;
    limit_req zone=snmp_zone burst=30 nodelay;

    server {
        listen 162 udp
        proxy_pass snmptrapd-server;

    }
    upstream snmptrapd-server {
        server snmptrapd-server-1:162;
        server snmptrapd-server-2:162;
    }
}

これで行ける…?
※ダメでした。上記configは利用できません。

検証

検証用環境を既存のDockerImageを使ってサクッと作成。

version: '3.3'
services:
  snmptrapd-1:
    container_name: snmptrapd-server-1
    image: sig9/snmptrapd:latest

  snmptrapd-2:
    container_name: snmptrapd-server-2
    image: sig9/snmptrapd:latest

  nginx-snmptrap:
    container_name: nginx-snmptrap
    image: nginx:latest
    volumes:
     - ./nginx.conf:/etc/nginx/nginx.conf

  trap-sender:
    image: elcolio/net-snmp:latest
    command:
     - /bin/bash
     -  -c
     - 'sleep 5; for i in `seq 1 10` ; do snmptrap -v 2c -c public nginx-snmptrap "" linkDown ; done'
    depends_on:
     - snmptrapd-1
     - snmptrapd-2

実行結果

nginx-snmptrap    | 2023/02/07 23:56:53 [emerg] 1#1: "limit_req_zone" directive is not allowed here in /etc/nginx/nginx.conf:14
nginx-snmptrap    | nginx: [emerg] "limit_req_zone" directive is not allowed here in /etc/nginx/nginx.conf:14

limit_req_zoneディレクティブはここ(streamコンテキスト)に書くのダメだよ とのこと。
Module ngx_http_limit_req_module (nginx.org)
不勉強でした。limit_reqモジュールはhttpコンテキスト内のみで有効でした。
streamコンテキスト内で利用できるlimit_reqのモジュールを探したが、見つからず。
モジュールを自作するか、別で流量制御するアプリケーション挟むかしかなさそう。

limit_reqを外せば、ちゃんと動くので負荷分散は問題なくできた。

snmptrapd-server-1 | 2023-02-08 00:22:03 nginx-snmptrap.20230207snmptrapdtest_default [UDP: [10.220.8.2]:37611->[10.220.8.4]:162]:
snmptrapd-server-1 | DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (56492540) 6 days, 12:55:25.40    SNMPv2-MIB::snmpTrapOID.0 = OID: IF-MIB::linkDown
snmptrapd-server-2 | 2023-02-08 00:22:03 nginx-snmptrap.20230207snmptrapdtest_default [UDP: [10.220.8.2]:54096->[10.220.8.3]:162]:
snmptrapd-server-2 | DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (56492542) 6 days, 12:55:25.42    SNMPv2-MIB::snmpTrapOID.0 = OID: IF-MIB::linkDown
snmptrapd-server-1 | 2023-02-08 00:22:03 nginx-snmptrap.20230207snmptrapdtest_default [UDP: [10.220.8.2]:54507->[10.220.8.4]:162]:
snmptrapd-server-1 | DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (56492544) 6 days, 12:55:25.44    SNMPv2-MIB::snmpTrapOID.0 = OID: IF-MIB::linkDown

ちゃんとラウンドロビンで振り分けされている。
ただし、送信元IPがnginxサーバになってるので監視で使うには、使いづらい。

流量制御できないのも相まって、nginxでsnmptrap扱うのは、
向いていない(そもそもhttpサーバだし)ので、新しくアプリケーション作ってみましょうか、となった。