[FreeBSD Advent Calendar]
(https://qiita.com/advent-calendar/2018/freebsd) 17日目です。
ソケット API を前提としたネットワークスタックの設計は、20 年以上昔のコンピュータやネットワークの速度を前提に設計されています。現在では CPU はマルチコア化がすすみ、ネットワークは 10Gbps や 40Gbps といった具合に数桁高速になり、またストレージもディスクから、数桁高速でアクセス方法すら違う不揮発性メモリになりつつあるなど、大きな変化が起こっています。OS のカーネルは新たなデバイスドライバのサポートに加え、こういったハードウェアの高速化、あるいはそれらの関係の変化に対処するために新たな I/O サブシステムやAPIを実装したりと、日々進化を遂げています。
こうした新たなカーネルの仕組みを試すのにはいつも大掛かりなハードウェアのセットアップが必要なわけではなく、ものによっては VM を利用して簡単に試すことができます。この記事では、既存の http ベンチマークツールと簡易 web サーバを使って、まだ本家にはマージされてませんが、PASTE という高速ネットワークスタックを体感してみたいと思います。ホストは Mac か Linux, 仮想化ソフトウェアには Virtualbox, 仮想化ソフトウェアの管理には Vagrant を使います。
まずは VM を作りましょう。freebsd という適当な作業ディレクトリを作って、その中に以下のような内容の Vagrantfile を作ります。
Vagrant.configure("2") do |config|
config.vm.network :forwarded_port, guest: 22, host: 3332, id: "ssh"
config.vm.guest = :freebsd
config.vm.synced_folder ".", "/vagrant", id: "vagrant-root", disabled: true
config.vm.box = "freebsd/FreeBSD-13.0-CURRENT"
config.vm.base_mac = "080027D14C26"
config.ssh.shell = "sh"
# config.vm.network :"private_network", ip: "192.168.18.18"
config.vm.provider :virtualbox do |vb|
vb.gui = true
vb.customize ["modifyvm", :id, "--memory", "2048"]
vb.customize ["modifyvm", :id, "--cpus", "2"]
vb.customize ["modifyvm", :id, "--hwvirtex", "on"]
vb.customize ["modifyvm", :id, "--audio", "none"]
vb.customize ["modifyvm", :id, "--uart1", "0x3F8", "4"]
vb.customize ["modifyvm", :id, "--uartmode1", "server", "/tmp/freebsd-ttyS1.sock"]
vb.customize ["modifyvm", :id, "--nictype1", "virtio"]
# vb.customize ["modifyvm", :id, "--nictype2", "82545em"]
# vb.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"]
end
config.vm.provision :shell, :inline => "[ ! -f /usr/local/bin/python ] && /usr/sbin/pkg install -y python || true"
config.vm.provision :shell, :inline => "ln -sf /usr/local/bin/python /usr/bin/python || true"
end
end
CPUコアの数やメモリの量は環境に応じて変えてください。メモリは最低 1GB 割り当てるとよいと思います。
同じディレクトリで、vagrant up
すると、VM が起動します (ネットワーク越しにセットアップするので少し時間がかかります)。起動したら /etc/rc.conf
の中にある、ifconfig_DEFAULT="SYNCDHCP"
の行を、ifconfig_em0="SYNCDHCP"
に変更します。そしたら今度は、Vagrantfile
の中の三箇所のコメントアウト (# で始まる行) を外します。そして再度 vagrant reload
すると、VM の em0 に 192.168.18.18 のアドレスが付いていて、ホスト側から ping が届くはずです。また、vagrant ssh-config --host va0
で出力される内容をホスト側の .ssh/config に追記すると、その VM に ssh va0
でログインできるようになります。
ここまでできたら、カーネル再構築の準備です。まずはユーザ権限でカーネルがコンパイルできるように以下のようにします。
sudo chown vagrant /usr/src
sudo mkdir /usr/obj
sudo chown vagrant /usr/obj
次に https://github.com/freebsd/freebsd からとってきた最新の FreeBSD CURRENT のコードを展開しましょう。展開後は、以下のようなディレクトリレイアウトになってると思います。
vagrant@freebsd:~ % ls /usr/src/
COPYRIGHT cddl secure
LOCKS contrib share
MAINTAINERS crypto stand
Makefile etc sys
Makefile.inc1 gnu targets
Makefile.libcompat include tests
Makefile.sys.inc kerberos5 tools
ObsoleteFiles.inc lib usr.bin
README libexec usr.sbin
README.md release xen
UPDATING rescue
bin sbin
vagrant@freebsd:~ %
次に、PASTE という netmap を拡張して作られた高速ネットワークスタックのパッチを当てます。VM 内で、
git clone git@github.com:micchie/netmap.git
cp netmap/sys/net/* /usr/src/sys/net/
cp netmap/sys/dev/netmap/netmap* /usr/src/sys/dev/netmap/
echo dev/netmap/netmap_bdg.c optional netmap >> /usr/src/sys/conf/files
echo dev/netmap/netmap_stack.c optional netmap >> /usr/src/sys/conf/files
とすれば OK です。
Vagrant での VM 起動時に最新のコードのスナップショットで起動していますので、普通に build kernel できるはずです。
cd /usr/src
make buildkernel KERNCONF=GENERIC=NODEBUG # 複数コアあれば -j3 のようにして複数スレッド使いましょう
sudo make installkernel KERNCONF=GENERIC-NODEBUG
新しいカーネルを有効にするために一旦 VM を再起動して、まず NIC を設定します。
sudo ifconfig em0 up
sudo ifconfig em0 -lro -tso -txcsum -rxcsum
簡易 web サーバ (phttpd) のディレクトリに移動してコンパイルします。
cd netmap/apps/phttpd
make clean; make
まずは phttpd を普通のソケットAPIを使って動かしてみます。中身は、kqueue
ベースのイベントループが動いていて、read()
/write()
でネットワークデータを読み書きしています。
./phttpd
ホスト側から、wrk
を使って、100 本のコネクションを使って HTTP リクエストを送ってみます。
michio@client:~ >wrk -d 3 -c 100 -t 100 http://192.168.18.18:60000
Running 3s test @ http://192.168.18.18:60000
100 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 6.77ms 1.30ms 23.04ms 90.36%
Req/Sec 148.17 17.83 282.00 89.99%
45375 requests in 3.11s, 6.53MB read
Requests/sec: 14612.95
Transfer/sec: 2.10MB
サーバ側は、`Ctrl+C`で終了します。
次に、PASTE を有効にした簡易 web サーバを動かしてみます。
./phttpd -i em0
ホスト側から同じコマンドで HTTP トラフィックを生成します。
michio@client:~ >wrk -d 3 -c 100 -t 100 http://192.168.18.18:60000
Running 3s test @ http://192.168.18.18:60000
100 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.37ms 640.18us 9.06ms 92.63%
Req/Sec 762.45 67.41 0.92k 71.13%
235389 requests in 3.10s, 33.90MB read
Socket errors: connect 0, read 25, write 0, timeout 0
Requests/sec: 75867.18
Transfer/sec: 10.93MB
かなり高速になりますね。
VM なのであくまで目安ですが、カーネルの技術だけでこんなにネットワークサーバの性能が変わるのかということを体感していただければと思います。
FreeBSD には netmap や vale をはじめ、TCP スタックを部分的に変更できるようにしてある TCP Function Block という仕組みや、最近では eBPF や不揮発性メモリドライバがマージされたりと、ネットワークスタックの研究に向いている機能がたくさん入っているので、ネットワークの研究に興味がある人とかもぜひ見てみると面白いと思います。