aoishiの備忘録

備忘録

Zabbix Senderを自前実装する場合にはZabbix Protocolのヘッダーが必要だった

はじめに

以前に下記の記事でZabbix Senderを自前実装していましたが、一つのコネクションで一度に大量のデータを送ろうとするとたびたびエラーになることがわかりました。

実装に不備があることもわかったので、正しいと思われる方法についてまとめておきます。

aoishi.hateblo.jp

原因

私の誤った解釈により、Zabbix Senderは決められたJSONフォーマットのデータをTCPでZabbixサーバに送ればOKだと思っていましたが、実はJSONデータの前にZabbixプロトコルのヘッダーをつける必要がありました。

ヘッダーを含めたZabbix Senderの実装としては下記が参考になります。

AWS Lambda(Python)からZabbix Senderでメトリクス値を送るスクリプト | 外道父の匠

ヘッダーについては下記が参考になります。

検知経緯

原因を突き止めるまでに、下記のような事象を確認していました。

Zabbix Senderのレスポンス

まず、Zabbix Senderでのデータ送信に成功した場合には、下記のようなレスポンスが返されました。

{"response":"success","info":"processed: 39; failed: 0; total: 39; seconds spent: 0.093660"}                                                                   
{"response":"success","info":"processed: 33; failed: 0; total: 33; seconds spent: 0.916001"}                                                                   
{"response":"success","info":"processed: 3; failed: 0; total: 3; seconds spent: 0.131982"}                                                                     
{"response":"success","info":"processed: 56; failed: 0; total: 56; seconds spent: 0.690923"}

次に、失敗した場合のレスポンスが下記の通りです。

[1回目]
{"response":"failed","info":"cannot parse as a valid JSON object: invalid object name/value separator at: ''"}                                                 
{"response":"failed","info":"cannot parse as a valid JSON object: unexpected end of string data"}                                                              
{"response":"success","info":"processed: 3; failed: 0; total: 3; seconds spent: 0.093064"}                                                                     
{"response":"failed","info":"cannot parse as a valid JSON object: unexpected end of string data"}

[2回目]
{"response":"failed","info":"cannot parse as a valid JSON object: unexpected end of string data"}                                                              
{"response":"success","info":"processed: 33; failed: 0; total: 33; seconds spent: 1.029911"}                                                                   
{"response":"success","info":"processed: 3; failed: 0; total: 3; seconds spent: 0.086790"}                                                                     
{"response":"failed","info":"cannot parse as a valid JSON object: unexpected end of string data"}  


[3回目]
{"response":"success","info":"processed: 39; failed: 0; total: 39; seconds spent: 0.074564"}                                                                   
{"response":"failed","info":"cannot parse as a valid JSON object: unexpected end of string data"}                                                              
{"response":"success","info":"processed: 3; failed: 0; total: 3; seconds spent: 0.085845"}                                                                     
{"response":"failed","info":"cannot parse as a valid JSON object: unexpected end of string data"} 

実行する度に成功したり失敗したりと不安定です。

Zabbixサーバのログ

今後は、Zabbixサーバのログを確認してみました。DebugLevel=4に変更しています。

 10279:20180108:010832.540 __zbx_zbx_setproctitle() title:'trapper #2 [processing data]'                                                                                                                                                      
 10279:20180108:010832.540 trapper got '{"data": [{"host": "laptop03", "value": 6.983, "key": "zzz_float_value[dstat.total_cpu_usage.usr]", "clock": "1515341312"}, {"host": "laptop03", "value": 1.496, "key": "zzz_float_value[dstat.total_cpu_usage.sys]", "clock": "1515341312"}, {"host": "laptop03", "value": 91.521, "key": "zzz_float_value[dstat.total_cpu_usage.idl]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.total_cpu_usage.wai]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.total_cpu_usage.stl]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.dsk_nvme0n1.read]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.dsk_nvme0n1.writ]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.paging.in]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.paging.out]", "clock": "1515341312"}, {"host": "laptop03", "value": 1.09, "key": "zzz_float_value[dstat.load_avg.1m]", "clock": "1515341312"}, {"host": "laptop03", "value": 2.84, "key": "zzz_float_value[dstat.load_avg.5m]", "clock": "1515341312"}, {"host": "laptop03", "value": 2.89, "key": "zzz_float_value[dstat.load_avg.15m]", "clock": "1515341312"}, {"host": "laptop03", "value": 12326502400.0, "key": "zzz_float_value[dstat.memory_usage.used]", "clock": "1515341312"}, {"host": "laptop03", "value": 1197580288.0, "key": "zzz_float_value[dstat.memory_usage.free]", "clock": "1515341312"}, {"host": "laptop03", "value": 384909312.0, "key": "zzz_float_value[dstat.memory_usage.buff]", "clock": "1515341312"}, {"host": "laptop03", "value": 3268669440.0, "key": "zzz_float_value[dstat.memory_usage.cach]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.net_wlp4s0.recv]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.net_wlp4s0.send]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.procs.run]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.procs.blk]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.procs.new]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.io_nvme0n1.read]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.io_nvme0n1.writ]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.swap.used]", "clock": "1515341312"}, {"host": "laptop03", "value": 8359243776.0, "key": "zzz_float_value[dstat.swap.free]", "clock": "1515341312"}, {"host": "laptop03", "value": 963.0, "key": "zzz_float_value[dstat.system.int]", "clock": "1515341312"}, {"host": "laptop03", "value": 2470.0, "key": "zzz_float_value[dstat.system.csw]", "clock": "1515341312"}, {"host": "laptop03", "value": 21.0, "key": "zzz_float_value[dstat.tcp_sockets.lis]", "clock": "1515341312"}, {"host": "laptop03", "value": 50.0, "key": "zzz_float_value[dstat.tcp_sockets.act]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.tcp_sockets.syn]", "clock": "1515341312"}, {"host": "laptop03", "value": 4.0, "key": "zzz_float_value[dstat.tcp_sockets.tim]", "clock": "1515341312"}, {"host": "laptop03", "value": 4.0, "key": "zzz_float_value[dstat.tcp_sockets.clo]", "clock": "1515341312"}, {"host": "laptop03", "value": 12.0, "key": "zzz_float_value[dstat.udp.lis]", "clock": "1515341312"}, {"host": "laptop03", "value": 0.0, "key": "zzz_float_value[dstat.udp.act]", "clock": "1515341312"}, {"host": "laptop03", "value": 62.0, "key": "zzz_float_value[dstat.unix_sockets.dgm]", "clock": "1515341312"}, {"host": "laptop03", "value": 1724.0, "key": "zzz_float_value[dstat.unix_sockets.str]", "clock": "1515341312"}, {"host": "laptop03", "value": 638.0, "key": "zzz_float_value[dstat.unix_sockets.lis]", "clock": "1515341312"}, {"host": "laptop03", "value": 1086.0, "key": "zzz_float_value[dstat.unix_sockets.act]", "clock": "1515341312"}, {"host": "laptop03", "value": "{\"data\": [{\"{#KEY_NAME}\": \"dstat.total_cpu_usage.usr\"}, {\"{#KEY_NAME}\": \"dstat.total_cpu_usage.sys\"}, {\"{#KEY_NAME}\": \"dstat.total_cpu_usage.idl\"}, {\"{#KEY_'                  
 10279:20180108:010832.540 In zbx_send_response()          
 10279:20180108:010832.540 zbx_send_response() '{"response":"failed","info":"cannot parse as a valid JSON object: unexpected end of string data"}'                                                                                            
 10279:20180108:010832.540 End of zbx_send_response():SUCCEED                                                          
 10279:20180108:010832.540 received invalid JSON object from 10.0.0.245: cannot parse as a valid JSON object: unexpected end of string data
 10279:20180108:010832.541 __zbx_zbx_setproctitle() title:'trapper #2 [processed data in 0.000432 sec, waiting for connection]'

2行目(trapper got)に送信したJSONデータが記録されていましたが、途中で途切れていました。

後続のログでもJSONをパースできない旨のエラーメッセージが記録されていますので、Zabbixサーバが送信したデータを期待通り受け取れていないようです。

tcpdump

最後にtcpdumpで通信の様子を確認してみました。

クライアント側で下記コマンドでtcpdumpします。

$ sudo tcpdump -n -i any port 10051

下記が失敗した場合の結果です。10.0.0.1がZabbixサーバ、10.0.0.245がクライアントです。最後にZabbixサーバからクライアントにリセットパケットが送られているようです。

01:43:13.795474 IP 10.0.0.245.47534 > 10.0.0.1.zabbix-trapper: Flags [S], seq 2393260655, win 29200, options [mss 1460,sackOK,TS val 4115495880 ecr 0,nop,wscale 7], length 0                                                                 
01:43:13.798023 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.47534: Flags [S.], seq 1719041873, ack 2393260656, win 28960, options [mss 1460,sackOK,TS val 1378069742 ecr 4115495880,nop,wscale 7], length 0                                       
01:43:13.798092 IP 10.0.0.245.47534 > 10.0.0.1.zabbix-trapper: Flags [.], ack 1, win 229, options [nop,nop,TS val 4115495883 ecr 1378069742], length 0                                                                                        
01:43:13.798490 IP 10.0.0.245.47534 > 10.0.0.1.zabbix-trapper: Flags [.], seq 1:1449, ack 1, win 229, options [nop,nop,TS val 4115495883 ecr 1378069742], length 1448                                                                         
01:43:13.798513 IP 10.0.0.245.47534 > 10.0.0.1.zabbix-trapper: Flags [.], seq 1449:2897, ack 1, win 229, options [nop,nop,TS val 4115495883 ecr 1378069742], length 1448                                                                      
01:43:13.798519 IP 10.0.0.245.47534 > 10.0.0.1.zabbix-trapper: Flags [.], seq 2897:4345, ack 1, win 229, options [nop,nop,TS val 4115495883 ecr 1378069742], length 1448                                                                      
01:43:13.798523 IP 10.0.0.245.47534 > 10.0.0.1.zabbix-trapper: Flags [.], seq 4345:5793, ack 1, win 229, options [nop,nop,TS val 4115495883 ecr 1378069742], length 1448                                                                      
01:43:13.798527 IP 10.0.0.245.47534 > 10.0.0.1.zabbix-trapper: Flags [P.], seq 5793:5974, ack 1, win 229, options [nop,nop,TS val 4115495883 ecr 1378069742], length 181                                                                      
01:43:13.799338 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.47534: Flags [.], ack 1449, win 249, options [nop,nop,TS val 1378069745 ecr 4115495883], length 0                                                                                     
01:43:13.799373 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.47534: Flags [.], ack 2897, win 272, options [nop,nop,TS val 1378069745 ecr 4115495883], length 0                                                                                     
01:43:13.799386 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.47534: Flags [.], ack 4345, win 295, options [nop,nop,TS val 1378069745 ecr 4115495883], length 0                                                                                     
01:43:13.799683 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.47534: Flags [P.], seq 1:142, ack 4345, win 295, options [nop,nop,TS val 1378069745 ecr 4115495883], length 141                                                                       
01:43:13.799710 IP 10.0.0.245.47534 > 10.0.0.1.zabbix-trapper: Flags [.], ack 142, win 237, options [nop,nop,TS val 4115495885 ecr 1378069745], length 0                                                                                      
01:43:13.799732 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.47534: Flags [.], ack 5974, win 340, options [nop,nop,TS val 1378069745 ecr 4115495883], length 0                                                                                     
01:43:13.799747 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.47534: Flags [F.], seq 142, ack 5974, win 340, options [nop,nop,TS val 1378069745 ecr 4115495883], length 0                                                                           
01:43:13.799760 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.47534: Flags [R.], seq 143, ack 5974, win 340, options [nop,nop,TS val 0 ecr 4115495883], length 0                                                                                    
01:43:13.800474 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.47534: Flags [R], seq 1719042015, win 0, length 0

ちなみに、成功した場合は下記のような結果になりました。

01:47:03.611559 IP 10.0.0.245.48794 > 10.0.0.1.zabbix-trapper: Flags [S], seq 587356983, win 29200, options [mss 1460,sackOK,TS val 4115725695 ecr 0,nop,wscale 7], length 0                                                                  
01:47:03.616337 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.48794: Flags [S.], seq 1228978652, ack 587356984, win 28960, options [mss 1460,sackOK,TS val 1378299561 ecr 4115725695,nop,wscale 7], length 0                                        
01:47:03.616385 IP 10.0.0.245.48794 > 10.0.0.1.zabbix-trapper: Flags [.], ack 1, win 229, options [nop,nop,TS val 4115725700 ecr 1378299561], length 0                                                                                        
01:47:03.616660 IP 10.0.0.245.48794 > 10.0.0.1.zabbix-trapper: Flags [.], seq 1:1449, ack 1, win 229, options [nop,nop,TS val 4115725700 ecr 1378299561], length 1448                                                                         
01:47:03.616674 IP 10.0.0.245.48794 > 10.0.0.1.zabbix-trapper: Flags [.], seq 1449:2897, ack 1, win 229, options [nop,nop,TS val 4115725700 ecr 1378299561], length 1448                                                                      
01:47:03.616676 IP 10.0.0.245.48794 > 10.0.0.1.zabbix-trapper: Flags [.], seq 2897:4345, ack 1, win 229, options [nop,nop,TS val 4115725700 ecr 1378299561], length 1448                                                                      
01:47:03.616678 IP 10.0.0.245.48794 > 10.0.0.1.zabbix-trapper: Flags [.], seq 4345:5793, ack 1, win 229, options [nop,nop,TS val 4115725700 ecr 1378299561], length 1448                                                                      
01:47:03.616685 IP 10.0.0.245.48794 > 10.0.0.1.zabbix-trapper: Flags [P.], seq 5793:5967, ack 1, win 229, options [nop,nop,TS val 4115725700 ecr 1378299561], length 174                                                                      
01:47:03.619438 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.48794: Flags [.], ack 1449, win 249, options [nop,nop,TS val 1378299565 ecr 4115725700], length 0                                                                                     
01:47:03.620498 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.48794: Flags [.], ack 2897, win 272, options [nop,nop,TS val 1378299565 ecr 4115725700], length 0                                                                                     
01:47:03.620749 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.48794: Flags [.], ack 4345, win 295, options [nop,nop,TS val 1378299566 ecr 4115725700], length 0                                                                                     
01:47:03.620785 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.48794: Flags [.], ack 5793, win 317, options [nop,nop,TS val 1378299566 ecr 4115725700], length 0                                                                                     
01:47:03.624183 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.48794: Flags [.], ack 5967, win 340, options [nop,nop,TS val 1378299566 ecr 4115725700], length 0                                                                                     
01:47:03.850201 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.48794: Flags [P.], seq 1:106, ack 5967, win 340, options [nop,nop,TS val 1378299795 ecr 4115725700], length 105                                                                       
01:47:03.850298 IP 10.0.0.245.48794 > 10.0.0.1.zabbix-trapper: Flags [.], ack 106, win 229, options [nop,nop,TS val 4115725934 ecr 1378299795], length 0                                                                                      
01:47:03.850357 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.48794: Flags [F.], seq 106, ack 5967, win 340, options [nop,nop,TS val 1378299795 ecr 4115725700], length 0                                                                           
01:47:03.850643 IP 10.0.0.245.48794 > 10.0.0.1.zabbix-trapper: Flags [F.], seq 5967, ack 107, win 229, options [nop,nop,TS val 4115725934 ecr 1378299795], length 0                                                                           
01:47:03.851771 IP 10.0.0.1.zabbix-trapper > 10.0.0.245.48794: Flags [.], ack 5968, win 340, options [nop,nop,TS val 1378299797 ecr 4115725934], length 0

ApacheのScoreboardをモニタリングする

はじめに

私事ですが、昨年末に運用しているWEBサービスにて、Apacheの同時接続数が上限に達し一時的にサービス提供できなくなる経験をしました。 その際に、エラーログにScoreboardという文字列を含む下記のエラーログが頻発していました。

AH00286: server reached MaxRequestWorkers setting, consider raising the MaxRequestWorkers setting
AH00287: server is within MinSpareThreads of MaxRequestWorkers, consider raising the MaxRequestWorkers setting
AH00288: scoreboard is full, not at MaxRequestWorkers

対応の一環で、Scoreboadのモニタリング設定などをしたので、その際の内容をまとめておきたいと思います。 また、私の理解不足で不正確な内容を記載している箇所もあるかと思いますが、自分の外部記憶装置代わり的な意味でも記載していますのでご了承下さい。

検証環境

用語

Slot

ApacheがHTTPリクエストを処理する実体のことで、MPMがPreforkであればプロセスのことであり、Worker, Eventであればスレッドのこと。

Scoreboard

Apacheが親プロセスと子プロセスとの間で共有するメモリ領域のこと。各Slot毎にさまざまな情報を保持しているらしい。

下記あたりのコードを見れば、Scoreboardについて詳細にわかりそう。

Scoreboardの状態確認方法

調査すると、mod_statusモジュールでステータスページを通じて確認する方法と、共有メモリ上のScoreboardに直接アクセスして情報を取得する方法があるようです。 今回は、前者のmod_statusモジュールを使った方法を記載します。

後者の共有メモリに直接アクセスする方法は情報があまり多くなく、Scoreboardについて実装レベルで把握していないと難しいようだったので、今回は断念しました。 ただ、mod_statusモジュールを使う場合と比較して、モニタリングのためにApacheにアクセスする必要がないため、接続数が飽和している状態でもScoreboardの状態が確認できるという点でメリットがありそうです。

参考までに、下記に記事を紹介しておきます。

mod_statusの有効化とステータスページからのデータ取得

下記の内容をApacheの設定ファイルに追記します。これにより、Apacheの状態がステータスページで確認できるようになります。

LoadModule status_module modules/mod_status.so
ExtendedStatus On

Listen 81
<VirtualHost *:81>
    <Location /server-status>
        SetHandler server-status
        Require all granted
    </Location>
</VirtualHost>

ここでは、ステータスページのURIがサービスに干渉することを防ぐために、VirtualHostで未使用のポートに対してステータスページを表示させるよう設定しています。 モニタリングや通信制限の運用次第ではありますが、接続元をlocalhostなどに絞るなどのセキュリティ対策は実際の環境に応じて検討したほうが良いと思います。

また、.htaccessなどの設定ファイル内でもステータスページの表示設定ができるようになるため、.htaccessを有効している環境では意図せずステータスページを外部に公開してしまう恐れもあるため注意が必要です。回避策としては、AllowOverrideやAllowOverrideListなどでSetHandlerの使用を許可しないよう設定すれば良さそうです。

https://httpd.apache.org/docs/2.4/mod/core.html#allowoverride

設定ファイルを編集後、Apacheを再起動します。

$ apachectl -t
Syntax OK

$ sudo systemctl restart httpd

ローカルからステータスページにアクセスしてみます。先のVirtualHostで定義したURLに対して?autoパラメタをつけてアクセスすることで、下記のようなkey-value形式でデータを取得できます。

$ curl localhost:81/server-status?auto
localhost
ServerVersion: Apache/2.4.28 (CentOS) OpenSSL/1.0.2k-fips
ServerMPM: worker
Server Built: Oct  9 2017 12:34:18
CurrentTime: Monday, 01-Jan-2018 16:11:40 JST
RestartTime: Monday, 01-Jan-2018 16:08:56 JST
ParentServerConfigGeneration: 1
ParentServerMPMGeneration: 0
ServerUptimeSeconds: 163
ServerUptime: 2 minutes 43 seconds
Load1: 0.04
Load5: 0.12
Load15: 0.22
Total Accesses: 305
Total kBytes: 361
CPUUser: .39
CPUSystem: .18
CPUChildrenUser: 0
CPUChildrenSystem: 0
CPULoad: .349693
Uptime: 163
ReqPerSec: 1.87117
BytesPerSec: 2267.88
BytesPerReq: 1212.01
BusyWorkers: 1
IdleWorkers: 49
Scoreboard: __________________W_______________________________
TLSSessionCacheStatus
CacheType: SHMCB
CacheSharedMemory: 512000
CacheCurrentEntries: 76
CacheSubcaches: 32
CacheIndexesPerSubcaches: 88
CacheTimeLeftOldestAvg: 183
CacheTimeLeftOldestMin: 136
CacheTimeLeftOldestMax: 288
CacheIndexUsage: 2%
CacheUsage: 3%
CacheStoreCount: 76
CacheReplaceCount: 0
CacheExpireCount: 0
CacheDiscardCount: 0
CacheRetrieveHitCount: 0
CacheRetrieveMissCount: 0
CacheRemoveHitCount: 0
CacheRemoveMissCount: 0

ここで、目的のScoreboardは下記のような文字列として表示されており、1文字が1つのSlotの状態を表現しています。

Scoreboard: __________________W_______________________________

各文字とSlotの状態の意味は下記の通りです。

文字 状態名 意味
_ Waiting for Connection プロセス/スレッド起動し、アクセス待ちの状態
S Starting up プロセス/スレッドが起動中の状態
R Reading Request クライアントからのアクセスを受け付けて、リクエストを読み込んでいる状態
W Sending Reply クライアントにレスポンスを返している状態
K Keepalive (read) KeepAliveによりリクエストを待機している状態
D DNS Lookup クライアントのIPの名前解決している状態
C Closing connection 接続を終了している状態
L Logging ログ出力している状態
G Gracefully finishing スレッドのgracefulな停止処理中またはそこからの起動中 ?
I Idle cleanup of worker スレッドが終了中な状態
. Open slot with no current process 空きSlot状態

何度かステータスページにアクセスしていると気づきますが、アクセスが全くない状態でもScoreboardには必ず1つWが表示されます。これはステータスページへのアクセスを処理しているSlotの状態を示しており、そのレスポンス中のScoreboardの状態が取得されステータスページが生成されているためだと思います。

Scorboardの数値化

ステータスページに表示されていたScoreboardは1文字が1つのSlotの状態を表現した文字列として表示されるため、その意味に従って状態毎にカウントして数値化することでモニタリングできるようになります。

ここでは、下記のPythonスクリプトでステータスページのScoreboardの文字列を数値化してみます。

#!/bin/env python

import requests
import argparse

scoreboard_lookup = {
    '_': 'WaitingForConnection',
    'S': 'StartingUp',
    'R': 'ReadingRequest',
    'W': 'SendingReply',
    'K': 'KeepAlive',
    'D': 'DNSLookup',
    'C': 'ClosingConnection',
    'L': 'Logging',
    'G': 'GracefullyFinishing',
    'I': 'IdleCleanupOfWorker',
    '.': 'OpenSlotWithNoCurrentProcess',
}

def main(url):
    r = requests.get(url)

    for key, value in [ tuple(x) for x in [ line.split(': ') for line in r.text.splitlines() ] if len(x) == 2]:
        key = key.replace(' ', '')
        if key == 'Scoreboard':
            print('apache.scoreboard.{},{}'.format('TotalSlot', len(value)))
            for scoreboard_char,scoreboard_label in scoreboard_lookup.items():
                print('apache.scoreboard.{},{}'.format(scoreboard_label, value.count(scoreboard_char)))
        else:
            print('apache.{},{}'.format(key, value))

if __name__ == '__main__':

    argparser = argparse.ArgumentParser()
    argparser.add_argument('-u', '--url', type=str, required=True)

    args = argparser.parse_args()
    main(args.url)

上記スクリプトは、ステータスページの内容を取得して下記の処理をしています。

  • csv形式のkey-value(key,value)形式に整形
  • keyのprefixにapache.を追加
  • keyに含まれていた半角スペースを削除
  • ScoreboardをSlotの状態毎にカウントし、apache.scoreboard.状態名というkeyのvalueに追加
  • Scoreboardの総Slot数(apache.scoreboard.TotalSlot)を追加

適当にファイル名でスクリプトを作成し、実行権限を付与して実行します。

$ vi apache.py
$ chmod 755 apache.py
$ apache.py -u http://pxy01.private:81/server-status?auto
apache.ServerVersion,Apache/2.4.28 (CentOS) OpenSSL/1.0.2k-fips                                                                
apache.ServerMPM,worker                                        
apache.ServerBuilt,Oct  9 2017 12:34:18                        
apache.CurrentTime,Monday, 01-Jan-2018 17:53:38 JST            
apache.RestartTime,Monday, 01-Jan-2018 16:08:56 JST            
apache.ParentServerConfigGeneration,1                          
apache.ParentServerMPMGeneration,0                             
apache.ServerUptimeSeconds,6282                                
apache.ServerUptime,1 hour 44 minutes 42 seconds               
apache.Load1,0.00                                              
apache.Load5,0.01                                              
apache.Load15,0.05                                             
apache.TotalAccesses,11589                                     
apache.TotalkBytes,13357                                       
apache.CPUUser,16.41                                           
apache.CPUSystem,6.45                                          
apache.CPUChildrenUser,0                                       
apache.CPUChildrenSystem,0                                     
apache.CPULoad,.363897                                         
apache.Uptime,6282                                             
apache.ReqPerSec,1.84479                                       
apache.BytesPerSec,2177.26                                     
apache.BytesPerReq,1180.22                                     
apache.BusyWorkers,1                                           
apache.IdleWorkers,49                                          
apache.scoreboard.TotalSlot,50                                 
apache.scoreboard.ClosingConnection,0                          
apache.scoreboard.DNSLookup,0                                  
apache.scoreboard.GracefullyFinishing,0                        
apache.scoreboard.IdleCleanupOfWorker,0                        
apache.scoreboard.KeepAlive,0                                  
apache.scoreboard.Logging,0                                    
apache.scoreboard.StartingUp,0                                 
apache.scoreboard.ReadingRequest,0                             
apache.scoreboard.SendingReply,1                               
apache.scoreboard.WaitingForConnection,49                      
apache.scoreboard.OpenSlotWithNoCurrentProcess,0               
apache.CacheType,SHMCB                                         
apache.CacheSharedMemory,512000                                
apache.CacheCurrentEntries,137                                 
apache.CacheSubcaches,32                                       
apache.CacheIndexesPerSubcaches,88                             
apache.CacheTimeLeftOldestAvg,65                               
apache.CacheTimeLeftOldestMin,0                                
apache.CacheTimeLeftOldestMax,222                              
apache.CacheIndexUsage,4%                                      
apache.CacheUsage,5%                                           
apache.CacheStoreCount,2883                                    
apache.CacheReplaceCount,0                                     
apache.CacheExpireCount,2746                                   
apache.CacheDiscardCount,0                                     
apache.CacheRetrieveHitCount,0                                 
apache.CacheRetrieveMissCount,0                                
apache.CacheRemoveHitCount,0                                   
apache.CacheRemoveMissCount,0

上記apache.scoreboard.あたりがScoreboardの状態が数値化したものになります。 私の場合は、上記のkey-valueのデータをZabbix SenderでZabbixサーバに送ることでグラフ化したりしています。

モニタリングツールでScoreboardをモニタリングする参考リンク

私自身ですべて試したことがあるわけではありませんが、有名どころのモニタリングツールを使ってScoreboardをモニタリングする方法の関連リンクを紹介しておきます。

Zabbix

Datadog

mackerel

Prometheus

Pythonでネストを減らす(if文によるdictのキーチェック編)

やりたいこと

  • PythonJSONないしはYAMLで記述された設定ファイルを読み込みたい
  • 読み込んだ設定ファイルをdictとして扱い、キーが存在したら何らかの処理をしたい
  • キーチェックのためのネストを減らしたい

元の方法

YAMLで記述された設定ファイルを読み込み、item_listキーが存在する場合にのみ、キーに対するリストに対してループ処理をするサンプルです。

for文でループ処理する前にif文でキーチェックをしているため、ネストが深くなってしまっています。

with open(config_file) as f:
    config = yaml.load(f)
    if 'item_list' in config:
        for item in config['item_list']:
            # ここでやりたい処理を記述

三項演算子を使って改善した方法

Python三項演算子を使うことで、if文のキーチェックをfor文の行に持ってくることができます。

with open(config_file) as f:
    config = yaml.load(f)
    for item in config['item_list'] if 'item_list' in config else list():
        # ここでやりたい処理を記述

上記のように記述することで、

  • item_listキーが存在する場合は、そのキーに対するリストに対してループ処理
  • item_listキーが存在しない場合は、空のリストに対してループ処理(つまり、処理をスキップさせる)

ことができます。

dictのgetを使って改善した方法

dictのgetメソッドを使うことで、さらにシンプルに記載できます。

with open(config_file) as f:
    config = yaml.load(f)
    for item in config.get('item_list', list()):
        # ここでやりたい処理を記述

dictのgetメソッドでは、get(key[, default]) のように記述でき、下記の振る舞いをします。

  • dictにkeyが存在していれば、それに対する値を返す
  • dictにkeyが存在していなければ、defaultを返す
    • defaultが与えられない場合は、Noneを返す

参考

Pythonとcronで秒単位で定期処理する

やりたいこと

  • Pythonスクリプトを定期的に実行したい
  • cronで手軽に定期処理したい
  • 秒単位で定期実行したい

解決方法

Pythonスクリプト作成

下記のように、実行間隔(秒)と実行回数をコマンドライン引数で与えて、Pythonスクリプトの中で繰り返し実行できるようにします。

import argparse
from datetime import datetime
import time

# ここに定期実行したい処理を記載
def main():
    pass

if __name__ == '__main__':
    argparser = argparse.ArgumentParser()
    argparser.add_argument('-i', '--interval', type=int, default=1)
    argparser.add_argument('-c', '--count', type=int, default=1)

    args = argparser.parse_args()
    _start_time = 0
    for i in range(args.count):
        while int(datetime.now().strftime('%s')) - _start_time < args.interval:
            time.sleep(0.1)
        _start_time = int(datetime.now().strftime('%s'))

        # ここで定期実行したい処理を呼び出す
        main()

Pythonスクリプトをcronに設定

例えば、5秒毎に処理をしたい場合には、下記のようにcronの設定をします。

$ crontab -e
----
* * * * * /path/to/python_script -i 5 -c 12
----

Zabbix Sender + LLD(ローレベルディスカバリ) + dstat + Grafanaでリソースモニタリング

はじめに

Zabbix Advent Calendar 2017 の3日目の投稿です。

リソースモニタリング方法については、色々な手法が提案されていると思いますが、今回はZabbix SenderとLLD(ローレベルディスカバリ)を使い、dstatで得られるデータを収集・グラフ化してみたいと思います。

こだわりポイントとしては2つあります。

  • dstatはオプション次第でさまざまなデータを取得することが可能です。そこで、オプション変更の都度テンプレートを修正しなくても良いように、LLDを使ってdstatのデータからアイテムをディスカバリさせます。

  • モニタリングするための導入物を減らすためにZabbixで提供されているzabbix_senderコマンドは使わず、Zabbix SenderのプロトコルPythonで独自*1に実装します。

目次

Zabbixサーバ側の準備

まず、Zabbixサーバ側の設定をします。

LLD(ローレベルディスカバリ)用の汎用的なテンプレートを作成します。

ホストグループ作成

今回の環境準備のために、ホストグループ作成します。

特にこの名前のグループでないとダメといったことはありませんので、既存のホストグループを利用しても結構です。

ここでは、下記の名前でグループを作成することとします。

項目 設定値
グループ名 Universal Sender servers

テンプレートの作成

ディスカバリルールを作成するためのテンプレートを作成します。

ホストに対してディスカバリルールを作成することもできそうですが、複数台展開のことを考慮してテンプレート化した方が後々楽できると思います。

下記のテンプレート名、そして先ほど作成したホストグループに所属させます。

項目 設定値
テンプレート名 Template Universal Sender
グループ Universal Sender servers

ディスカバリルールの作成

先に作成したテンプレートに対してディスカバリルールを作成します。

「作成したテンプレートを選択 > ディスカバリルール > ディスカバリルールの作成」で作成画面に遷移できます。

Zabbix Senderを利用するので、タイプはZabbix トラッパーとします。キーはスクリプトでZabbixサーバにデータを送る際に使うため、間違えないよう注意してください。

項目 設定値
名前 universal sender discovery
タイプ Zabbix トラッパー
キー universal.discovery

アイテムプロトタイムの作成

ディスカバリルールと同じく、先に作成したテンプレートに対してアイテムプロトタイプを作成します。

「作成したディスカバリルールを選択 > アイテムのプロトタイプ > アイテムのプロトタイプ作成」で作成画面に遷移できます。

ディスカバリルールと同様に、タイプはZabbix トラッパーとします。後にデータを送るときやグラフ化する際に名前とキーを使うため、ここも間違えないよう注意してください。

データ型は、数値データをまとめて扱うために数値(浮動小数)としています。

項目 設定値
名前 {#KEY_NAME}
タイプ Zabbix トラッパー
キー universal_sender[{#KEY_NAME}]
データ型 数値(浮動小数

ホストの作成とテンプレート割り当て

最後に、ホストを作成して先に作成したテンプレートを割り当てます。

項目 設定値
ホスト名 test_server
グループ Universal Sender servers
テンプレート Template Universal Sender

クライアント側の準備

スクリプトの配置

下記コマンドで、モニタリング対象のホストにPythonスクリプトを配置します。 dstatだけインストールされていれば、Python2,3どちらの環境でも追加パッケージ不要で実行できるはずです。

ZABBIX_SERVERとZABBIX_PORTについては、各環境に合わせて変更してください。

$ cat <<EOF > universal_sender_dstat.py
import json
import socket
import re
import csv
import subprocess
import tempfile
from datetime import datetime

# change for your environment.
ZABBIX_SERVER = "10.0.0.1"
ZABBIX_PORT = 10051


ZABBIX_HOST = 'test_server'
DSTAT = 'dstat -cdglmnprsy --tcp --udp --unix --nocolor --noheaders --output {} 1 2'
KEY_NAME = '{#KEY_NAME}'
DISCOVERY_KEY = 'universal.discovery'
ITEM_KEY = 'universal_sender[{}]'

def get_data():

  with tempfile.NamedTemporaryFile('rw') as f:

      command = DSTAT.format(f.name)
      subprocess.check_output(command, shell=True)

      f.seek(2)
      data = csv.reader(f)

      # skip header rows
      data.next()
      data.next()

      first_header_list = list()
      prev_header = ""
      for row_item in data.next():
          if row_item:
              prev_header = re.sub('\s|/', '_', row_item)
          first_header_list.append(prev_header)

      second_header_list = data.next()

      key_list = [
        'dstat.{}.{}'.format(
          first_header_list[i],
          second_header_list[i]
        ) for i in range(len(first_header_list)) ]

      # discovery data
      discovery_data = [
        {
          'host': ZABBIX_HOST,
          'key': DISCOVERY_KEY,
          'value': json.dumps({'data': [ { KEY_NAME: key } for key in key_list ]})
        }
      ]

      # sender data
      # skip first sample data
      data.next()
      item_data = [
        {
          'host': ZABBIX_HOST,
          'key': ITEM_KEY.format(key_list[i]),
          'clock': datetime.now().strftime('%s'),
          'value': value,
        } for i, value in enumerate(data.next())
      ]

      return (discovery_data, item_data)

def send(data):

    sender_data = {
        'request': 'sender data',
        'data': data,
    }
    # print(json.dumps(sender_data, indent=2, ensure_ascii=False))

    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((ZABBIX_SERVER, ZABBIX_PORT))
    client.sendall(json.dumps(sender_data))
    response = client.recv(4096)

    print('response: {}'.format(response))


if __name__ == '__main__':
  discovery_data, sender_data = get_data()

  send(discovery_data)
  send(sender_data)
EOF

なお、Zabbix Senderのプロトコルは下記フォーマットのJSONデータをSocketで送るだけの非常にシンプルな作りになっています。

[LLDのデータを送信する場合]
{
  "request": "sender data",
  "data": [
    {
      "host": "test_server", 
      "value": "{\"data\": [{\"{#KEY_NAME}\": \"dstat.total_cpu_usage.usr\"}, ... ]}", 
      "key": "universal.discovery"
    }
  ]
}

[アイテムのデータを送信する場合]
{
  "request": "sender data",
  "data": [
    {
      "host": "test_server", 
      "value": "39.547", 
      "key": "universal_sender[dstat.total_cpu_usage.usr]", 
      "clock": "1512231561"
    },
    .
    .
    .

  ]
} 

どちらのデータも基本的には同じフォーマットですが、LLDのvalueはディスカバリによって作成されるアイテム名のJSONオブジェクトが、dumpされた文字列となっているので注意してください。

モニタリング開始

データ取得の開始

下記のコマンドで、データ収集を開始します。

$ watch -n 10 python universal_sender_dstat.py

正常に動作した場合、Zabbix Senderプロトコルで通信した際のレスポンスとして、下記のメッセージが出力されます。

ZBXD\{"response":"success","info":"processed: 1; failed: 0; total: 1; seconds spent: 0.006614"}
ZBXD\{"response":"success","info":"processed: 38; failed: 0; total: 38; seconds spent: 0.000635"}

データ送信に失敗した場合は、下記のメッセージが出力されます。

ZBXD\{"response":"success","info":"processed: 0; failed: 1; total: 1; seconds spent: 0.006340"}
ZBXD\{"response":"success","info":"processed: 0; failed: 38; total: 38; seconds spent: 0.000724"}

プロトコルとしては正しく通信できているが、キーが異なるかZabbixサーバ側でまだアイテムが作成されていないために、データを受け取れていないと思われます。

また、プロトコルが不正(dstatコマンドの結果のパースに失敗してJSONデータがおかしくなった場合など)の場合には、下記のようなメッセージが表示されます。

ZBXDa{"response":"failed","info":"cannot parse as a valid JSON object: unexpected end of string data"}

データ取得結果の確認

まずは、ホストのアイテム一覧でディスカバリで作成されたアイテムが存在することを確認します。

ステータスが正常となっていれば問題ありません。

f:id:akihisa_oishi:20171202232537p:plain

次に、ホストの最新データを確認し、ディスカバリで作成されたアイテムについてデータが取得できていることを確認します。

f:id:akihisa_oishi:20171202233318p:plain

データが取得できていれば、取得時刻と値が表示されます。

取得したデータのグラフ化

Grafanaでダッシュボードを作成して取得したデータをグラフ化します。

Grafanaの導入および、Zabbixのデータソース設定方法は既に記事が多数ありますのでここでは割愛します。

最終的な見た目は下記のようになります。 f:id:akihisa_oishi:20171203003002p:plain

ダッシュボードはテンプレート機能を利用し、Zabbixのクエリでホストを選択できるようにします。

項目 設定値 説明
Name Group Host
Type Query Query
Data source Zabbixのデータソースを指定 Zabbixのデータソースを指定
Query {Universal Sender servers} {Universal Sender servers}{*}
Query の意味 Universal Sender servers のみ選択できる Universal Sender servers グループのホストが選択できる

f:id:akihisa_oishi:20171203003644p:plain

グラフ作成画面にて、テンプレート機能で作成した変数およびディスカバリで作成されたアイテム名を入力し、グラフを作成します。

項目 設定値 説明
Group $Group テンプレート機能で選択されたホストグループの変数
Host $Host テンプレート機能で選択されたホストの変数
Item /dstat.*cpu.*/ グラフ化したいアイテム名(ここではCPU系のデータを正規表現でまとめて指定)

f:id:akihisa_oishi:20171203003016p:plain

まとめ

Zabbix Senderでdstatのデータをモニタリングしてみました。

今回の方法には下記の課題がある思いますが、数値データのモニタリングに限ってしまえば妥協できるものかと思います。

  • アイテムプロトタイプを数値(浮動小数)としているため、文字列データが扱えない。
  • トリガーの設定をしようとすると大変。

まだカレンダーに空きがあるようなので、次回は今回の内容を下記観点でブラッシュアップした記事がかければと思っています!

  • watchコマンドではなくcronなどで定期実行されるようにする。
  • dstatでのデータ取得とZabbix Senderでデータ送信する処理を粗結合にし、モニタリング項目追加に拡張性を持たせる。

参考

Zabbix Sender 無しで Zabbix Server にデータを送る - mattintosh note

*1:偉そうなことを言っていますが、既に先人がおりますので、それらを参考にさせていただいています

SSH秘密鍵から公開鍵を作成する

はじめに

SSH秘密鍵から公開鍵って生成できるのかと思って調べてみたので、手順をまとめておきます。

環境

秘密鍵から公開鍵を生成できるか確認する方法

SSH秘密鍵と公開鍵のペア作成

カレントディレクトリに、test_keyという名前で秘密鍵を、test_key.pubという名前で公開鍵を作成します。

$ ssh-keygen -t rsa -b 4096 -f test_key

$ ls -al test_key*
-rw------- 1 aoishi aoishi 3247 12月  2 00:35 test_key
-rw-r--r-- 1 aoishi aoishi  746 12月  2 00:35 test_key.pub

SSH秘密鍵から公開鍵を作成

ssh-keygenコマンドに-yオプションを付与し、-fオプションで秘密鍵ファイルを指定することで、そのペアとなる公開鍵が生成できます。

$ ssh-keygen -y -f test_key

復元したSSH公開鍵との比較

試しに、ペアで作成した時の公開鍵と、後から生成した公開鍵とで差分を比較してみます。

$ ssh-keygen -y -f test_key | diff test_key.pub -
1c1
< ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDTSzQjqHpUxl2tDhqawT3HFrvq5ACq+7IoTIGxUapiX1bmeV9f1EJHHAuAp4d2YJvaOfawxJoRAgUKnwPxYn5o62ANffyzo36C9H0SsIg+/QVTqfErTUpNGzGo/ATP99Oin1XY3hyCOdghhR4fU2cWbTKwGpPzAMeED012qwgOG12UaA5Us9xdgtyo8MXax36IYrletbsSUtS90K1zw+WWYhJBpBa54R6T/0SQdtA0sIxVrYhZ/mbv3VgP2atI7kMiCHA4MHbZe4rFbHnwG5QkyCGi+2+leoH81u8FF5pSxgyT+iY8tnq7mqqojFYAnyj2tbOfk3nOKBzftfTg05CjNrs4wZyqXEgpsY6BgxMxDdE6mWwjC2PqejjmXkfhjDoywaClRpFEqJV6/3zKzidilajJMH2suowio2dYT01rUdUFyUdQRi2a2ynlX1y0tKnAc0E86evC+sTeddRukc1ka6tFT0CVBsP+MYHe2v6Qq8OwOKItKPSsoUv95Ufw2Hagv4Q4MdrasowilDNdeg4B78jcZektAZ3ofEvfTIia6ae6XxDE8PNHrrhQkDB7kuzPH9wuaixChMt2D3IyHgEFXmWNkEAz2Nj1p+MRGTwNrnUBAWMROO/r2ejMpgbbi44HfLcsBd2Zay+dyJgo+VHe7RIph9CzIEg8S1NozoznpQ== aoishi@ope01.private
---
> ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDTSzQjqHpUxl2tDhqawT3HFrvq5ACq+7IoTIGxUapiX1bmeV9f1EJHHAuAp4d2YJvaOfawxJoRAgUKnwPxYn5o62ANffyzo36C9H0SsIg+/QVTqfErTUpNGzGo/ATP99Oin1XY3hyCOdghhR4fU2cWbTKwGpPzAMeED012qwgOG12UaA5Us9xdgtyo8MXax36IYrletbsSUtS90K1zw+WWYhJBpBa54R6T/0SQdtA0sIxVrYhZ/mbv3VgP2atI7kMiCHA4MHbZe4rFbHnwG5QkyCGi+2+leoH81u8FF5pSxgyT+iY8tnq7mqqojFYAnyj2tbOfk3nOKBzftfTg05CjNrs4wZyqXEgpsY6BgxMxDdE6mWwjC2PqejjmXkfhjDoywaClRpFEqJV6/3zKzidilajJMH2suowio2dYT01rUdUFyUdQRi2a2ynlX1y0tKnAc0E86evC+sTeddRukc1ka6tFT0CVBsP+MYHe2v6Qq8OwOKItKPSsoUv95Ufw2Hagv4Q4MdrasowilDNdeg4B78jcZektAZ3ofEvfTIia6ae6XxDE8PNHrrhQkDB7kuzPH9wuaixChMt2D3IyHgEFXmWNkEAz2Nj1p+MRGTwNrnUBAWMROO/r2ejMpgbbi44HfLcsBd2Zay+dyJgo+VHe7RIph9CzIEg8S1NozoznpQ==

差分を確認する限り、当初ペアで作成した公開鍵にはコメント(aoishi@ope01.private)が付いていますが、後から生成した公開鍵にはついていようです。

コメントがあったほうが何かと便利なので、適宜追加しておくと良いですね。

ちなみに、diffコマンドはパイプで標準入力を渡すことで - でその入力を参照して差分比較することができます。

CentOS7のtcpdumpでインターフェースを指定しないとエラーになる

はじめに

CentOS7のtcpdumpで、ネットワークインターフェースを明示的に指定しない場合に下記のメッセージが出力されたので、その際に対応したことをまとめます。

$ sudo tcpdump
tcpdump: packet printing is not supported for link type NFLOG: use -w

環境

tcpdumpがエラーになった原因

tcpdumpはネットワークインターフェースが指定されなかった場合、インターフェース一覧の先頭のものに対して動作するようです。

利用可能なネットワークインターフェース一覧は、下記コマンドで確認できます。

$ sudo tcpdump -D
1.nflog (Linux netfilter log (NFLOG) interface)
2.nfqueue (Linux netfilter queue (NFQUEUE) interface)
3.usbmon1 (USB bus number 1)
4.usbmon2 (USB bus number 2)
5.ens160
6.any (Pseudo-device that captures on all interfaces)
7.lo [Loopback]

このため、1. のnflogというインターフェースで動作しており、このインターフェースでtcpdumpしようとしたことでエラーとなっていたようです。

なお上記の仕様については、manコマンドで確認できます。

$ man tcpdump
~~~
-i interface
--interface=interface
       Listen on interface.  If unspecified, tcpdump searches the sys‐
       tem  interface  list  for  the  lowest  numbered, configured up
       interface (excluding loopback), which may turn out to  be,  for
       example, ``eth0''.

       On  Linux systems with 2.2 or later kernels, an interface argu‐
       ment of ``any'' can be used to capture packets from all  inter‐
       faces.   Note  that  captures on the ``any'' device will not be
       done in promiscuous mode.

       If the -D flag is supported, an interface number as printed  by
       that  flag  can be used as the interface argument, if no inter‐
       face on the system has that number as a name.
~~~

tcpdumpでインターフェースを指定する方法

今回確認したかったのは、ens160(CentOS7では一般的?)なので、インターフェース名か一覧の番号を指定してtcpdumpし直します。

$ sudo tcpdump -i ens160
or 
$ sudo tcpdump -i 5

また、manの説明にもありましたが、anyを指定すると全インターフェースに対して動作させることができるようです。

$ sudo tcpdump -i any

まとめ

CentOS7のtcpdumpでインターフェースを明示的にしないことで発生したメッセージに対する対応方法をまとめてみました。

普段何気なく使っているtcpdumpですが、改めてmanを読むと新たな気づきとか勉強になることが沢山ありますね。

参考

��2898 CentOS7のtcpdumpがなにやらおかしい - Web Patio - CentOSで自宅サーバー構築