Docker コンテナで InfiniBand を動かす(性能も測定しました)


漢なら Docker(LXC) で InfiniBand を動かしてみたいですね!

一応すでに LXC で動いている論文がありますので [1], Docker でも実現してみましょう.

## 手順

### 環境

* ホスト側: CentOS 6.5
* コンテナ側: CentOS 6.5

と仮定して作業を進めます. もしかしたら Ubuntu12.04 or higher でもほぼ同じ手順でできるかもしれません.

ホスト OS で IB(InfiniBand)が問題なく動いているとします.

Docker で centos6 イメージを取得して IB 周りのパッケージを入れます.

```
$ sudo docker pull centos
$ sudo docker run -i -t centos /bin/bash
# yum update
# yum install infiniband-diags
# その他 IB パッケージをいろいろ
```

検証した環境の HCA は ConnectX-2(mlx4) なので libmlx4 を忘れずにインストールしておきます.

```
# yum install libmlx4
```

/etc/libibverbs.d/mlx4.driver

があるのを確認します.

HCA に対応したドライバパッケージを入れておかないと, ibv_devices などをしたときに

```
Warning: no userspace device-specific driver found for
/sys/class/infiniband_verbs/uverbs0
```

と出るので注意しましょう.

この時点でホスト側で

```
$ sudo docker commit XXXXXX(コンテナのID) centos6-ib
```

としてイメージのスナップショットを作っておきます.

ibstat を実行してホスト OS と同じ結果であるのを確認します.

```
# ibstat
```

ibv_devinfo を実行してみます


```
# ibv_devinfo

hca_id:	mlx4_0
	transport:			InfiniBand (0)
	fw_ver:				2.9.1000
	node_guid:			XXXX:XXXX:XXXX:XXXX
	sys_image_guid:			XXXX:XXXX:XXXX:XXXX
	vendor_id:			0xXXXX
	vendor_part_id:			XXXX
	hw_ver:				0xXX
	board_id:			MT_XXXXXXX
	phys_port_cnt:			2
		port:	1
			state:			PORT_DOWN (1)
			max_mtu:		4096 (5)
			active_mtu:		4096 (5)
			sm_lid:			1
			port_lid:		1
			port_lmc:		0x00
			link_layer:		InfiniBand

		port:	2
			state:			PORT_DOWN (1)
			max_mtu:		4096 (5)
			active_mtu:		4096 (5)
			sm_lid:			0
			port_lid:		0
			port_lmc:		0x00
			link_layer:		InfiniBand
```

素敵です.

ibhosts をしてみます.

```
# ibhosts         
src/query_smp.c:227; can't open UMAD port ((null):0)
/usr/sbin/ibnetdiscover: iberror: failed: discover failed
```

うむむ… strace で ibnetdiscover を追って見ます.

```
# strace ibnetdiscover

…
open("/dev/infiniband/umad0", O_RDWR|O_NONBLOCK) = -1 ENOENT (No such file or directory)
write(2, "src/query_smp.c:227; can't open "..., 53src/query_smp.c:227; can't open UMAD port ((null):0)
...
```

なんたるちや! そもそも /dev/infiniband が存在していませんでした….

## /dev/infiniband の設定.

Docker 的に他に推奨の方法がありそうですが, とりあえず無理やりホストの /dev/infniband をコンテナの /dev/infiniband にバインドさせてコンテナを起動します.

```
$ sudo docker run -privileged -v /dev/infiniband:/dev/infiniband -i -t centos6-ib /bin/bash
```

```
# ibhosts
Ca	: 0xXXXXXXXXXXXXXX ports 2 "xeon HCA-1"
```

Cool! 素敵です. ついに Docker(LXC) で IB にアクセスすることができました!

## 性能

ib_read_bw, ib_write_bw, ib_read_lat, ib_read_bw で計測しました. 一方が LXC コンテナ内と Native, もう一方が Native で動いています. ConnectX2 QDR(40Gbps) で計測.

![ib-lxc-performance.gif](https://qiita-image-store.s3.amazonaws.com/0/36275/93281322-ddde-be34-4a2e-d339b5755037.gif "ib-lxc-performance.gif")


Native(ホスト OS で直接実行)とほとんど変わりませんね! プロセスは LXC で動いているわけですがそのオーバーヘッドはまず無いと言えます. Cool.

実行ログは以下のような感じです.

### Docker 内(LXC container)で実行

```
# ib_read_bw 192.168.11.16
…
-----------------------------------
 #bytes     #iterations    BW peak[MB/sec]    BW average[MB/sec]   MsgRate[Mpps]
 65536      1000           3008.83            3008.80             0.048141
----------------------------------------------------

# ib_write_bw 192.168.11.16
…
-----------------------------------
 #bytes     #iterations    BW peak[MB/sec]    BW average[MB/sec]   MsgRate[Mpps]
 65536      5000           3066.07            3066.05             0.049057
---------------------------------------------------------------------------------------


# ib_read_lat 192.168.11.16
…
-----------------------------------
 #bytes #iterations    t_min[usec]    t_max[usec]  t_typical[usec]
 2       1000          1.66           20.13        2.65  
---------------------------------------------------------------------------------------


# ib_write_lat 192.168.11.16
…
-----------------------------------
 #bytes #iterations    t_min[usec]    t_max[usec]  t_typical[usec]
 2       1000          0.80           7.12         0.86  
---------------------------------------------------------------------------------------
```

### ホスト OS(Native)で実行

```
$ ib_read_bw 192.168.11.16
…
-----------------------------------
 #bytes     #iterations    BW peak[MB/sec]    BW average[MB/sec]   MsgRate[Mpps]
 65536      1000           3015.43            3011.54             0.048185
---------------------------------------------------------------------------------------


$ ib_write_bw 192.168.11.16
…
-----------------------------------
 #bytes     #iterations    BW peak[MB/sec]    BW average[MB/sec]   MsgRate[Mpps]
 65536      5000           3065.79            3065.63             0.049050
--------------------------------------------------------------------------------


$ ib_read_lat 192.168.11.16
…
-----------------------------------
 #bytes #iterations    t_min[usec]    t_max[usec]  t_typical[usec]
 2       1000          1.68           39.48        2.67  
---------------------------------------------------------------------------------------


$ ib_write_lat 192.168.11.16
…
-----------------------------------
 #bytes #iterations    t_min[usec]    t_max[usec]  t_typical[usec]
 2       1000          0.80           4.70         0.86  
---------------------------------------------------------------------------------------
```


## TODO

* /dev のセキィリティはどうなるのかな? /dev/infiniband のよりうまいバインディング方法はないかな?

## さらなる活用

IB HCA 以外にも, Docker で特権モード + LXC コンフィグをいじることで GPU をパススルーできるようです.

https://github.com/dotcloud/docker/issues/682

頑張れば PCIex デバイス一般が Docker で動くやもしれません.



## 参考文献

* [1] Performance Evaluation of Container-based Virtualization for High Performance Computing Environments http://marceloneves.org/papers/pdp2013-containers.pdfk