Linuxサーバ上でホスト間コネクションを集約表示するツール lstf をつくった - ゆううきメモ にて紹介したlstfのホスト上のTCPコネクション情報の取得処理において、/proc/net/tcp
を読みだす代わりに、Netlinkソケットを利用することで、実行速度が1.6倍になった。lstfのバージョン0.4.0で使えるようになる。
実験
約40,000接続あるWebサーバ上にて、lstfコマンドの実行時間を名前解決時間を含まずに比較する。 実験環境はEC2のc4.2xlarge、Debian 8.10、Linuxカーネル3.16であり、リバースプロキシとしてnginxが動作している。
接続数は次のコマンドよりだいたい40,000接続であることを確認する。
[y_uuki@hoge ~]$ ss -tan | wc -l 39264
/proc/net/tcp
を読む実装では、次のように実行時間は約500msであり、
[y_uuki@hoge ~]$ time ./lstf_old -n >/dev/null real 0m0.532s user 0m0.432s sys 0m0.140s
Netlinkソケットを利用する実装では、次のように実行時間は約300msとなっており、約1.6倍の速度向上である。10回実行の平均値をとっても、1.68倍の速度向上となった。
[y_uuki@hoge ~]$ time ./lstf -n > /dev/null real 0m0.318s user 0m0.272s sys 0m0.052s
netlinkを使った同様のパフォーマンス改善については、kernel: add a netlink interface to get information about processes (v2) [LWN.net]が参考になる。 この記事を真似て、perfにより解析したが、有意な結果が得られたなかったため、宿題としたい。
実装
Netlinkは、ユーザ空間のプロセスとカーネルとの通信をソケットインタフェースにより提供する。 ssコマンドやipコマンドを含むiproute2パッケージでは、netlinkを利用することで、デバイスを操作し、カーネル内の情報を取得している。*1 Netlinkソケットからソケットの関する情報を取得するには、Socket Monitoring Interfaceを使う。*2
Socket Monitoring Interfaceは、昔はinetファミリーのみのサポートだったが、Linuxカーネル3.3から様々なソケットタイプをサポートするようになった(raw socketなど, 1,2,3) だいたい新しいものをsock_diag、古いものをinet_diagとしているようにみえる。 それに伴い、若干インタフェースが変更されており、netlinkファミリーとして、TCPDIAG_FAMILYを指定していたところをSOCK_DIAG_BY_FAMILYとして指定したり、ユーザ空間からカーネルに送信するリクエスト構造体がinet_diag_req, inet_diag_req_v2となっている。 CentOS5などの古いカーネルでは、inet_diagのみ利用できる。 lstfでは、古いOSをサポートしたいため、inet_diagを利用した。
ユーザ空間のプログラミングモデルは、大雑把には、まずnetlinkソケットを作成し、Socket Monitoringのためのリクエスト構造体を作成し、netlinkメッセージ構造体として、sendto
/sendmsg
システムコールでカーネルに送信する。次にrecvmsg
などで受信し、バイト列をパースし必要な情報を得るという流れになる。
Go言語では、netlinkを扱うためのライブラリとして vishvananda/netlinkが有名である。
ただし、vishvananda/netlinkはSocket Monitoring Interfaceを提供していないため、ソケット情報を取得しようと思うと、コア部分の生に近いインタフェースでコードを書くことになる。
vishvananda/netlinkのコア部分(nl
パッケージ)を使いつつ、inet_diagをひとしきり実装したが、
最終的には、Goのelastic/gosigarのlinuxパッケージを発見したため、これを利用し実装した。
ちなみに、lstfのv0.4.0には、ソケット情報の取得処理を2回走らせてしまっていたため、その無駄を省いた改善も入っている。これとnetlinkの改善を合わせると、全体で3倍強の速度改善になっている。
参考文献
- RFC3549, Linux Netlink as an IP Services Protocol, 2003, https://tools.ietf.org/html/rfc3549
- Rami Rosen, Linux Kernel Networking: Implementation and Theory, Apress, 2014
- Christian Benvenuti, Understanding Linux Network Internals, O'Reilly Media, 2005
- Passive monitoring of sockets on Linux
- Using Netlink to Optimize Socket Statistics
- Linux, Netlink, and Go — Part 1: netlink – Matt Layher – Medium
- Linuxでcからnetstatっぽいことをしたかった - や
- Add support for tcp diags. by sebbov · Pull Request #199 · vishvananda/netlink · GitHub
- iproute2 misc/ss.c
いただいた反応
procのtcpを見るパターンもコネクション数が少なければ当然速いので、コネクション数の変化に応じてどのあたりでnetlinkが上回るかわかると便利そう。そいやnetlinkはマルチスレッドに対応してるのかなぁ / “TCP接続…” https://t.co/jcCoabYfWW
— 松本 亮介 / まつもとりー (@matsumotory) 2018年6月17日
netlinkの方が早いの意外だった。ちょい前にこの辺り触ってたなぁ。
— P山 (@pyama86) 2018年6月17日
RT: TCP接続を集約表示するlstfでNetlinkにより実行速度が1.6倍になった https://t.co/IbjvLYJKrb
すごい、lstfだけでも便利だったのに性能向上嬉しい https://t.co/eCaNCmQATn
— mook (@mookjp) 2018年6月17日