Metonymical Deflection

ゆるく日々のコト・たまにITインフラ

CentOS8 ovs(Open vSwitch)+DPDKのビルドとネットワーク設定方法

CentOS8によるovs(Open vSwitch)+DPDKのビルドとネットワーク設定方法について記載しました。
基本的にはCentOS7と同様ですが、CentOS8の場合はdpdkのパッケージがあるため、比較的楽に導入出来ます。

1.構成

1-1.環境
筐体                             : ProLiant DL360e Gen8
System ROM                       : P73 01/22/2018
NIC                              : Intel X540-AT2
OS                               : CentOS8.0(1905)
Kernel                           : 4.18.0-80.el8.x86_64
Installed Environment Groups     : 
  @^graphical-server-environment
  @container-management
  @development
  @virtualization-client
  @virtualization-hypervisor
  @virtualization-tools 
ovs                              : 2.12.90
DPDK                             : 18.11.3
1-2.全体の流れ

事前準備
DPDKインストール
OvSソースビルド
OvS+DPDKのネットワーク設定
仮想マシンのvirsh edit

2.事前準備

2-1.qemu-kvmの実行ユーザをrootへ変更

qemu-kvmの実行ユーザをqemu→rootに変更します。

vi /etc/libvirt/qemu.conf

user = "root"  #コメントを外す
group = "root"  #コメントを外す

viで/etc/libvirt/qemu.confを開き、上記ユーザとグループの行をコメントアウト

2-2.必要なパッケージのインストール
sed -i -e s/enabled=0/enabled=1/g /etc/yum.repos.d/CentOS-PowerTools.repo
dnf -y install libpcap-devel elfutils-libelf-devel numactl-devel libmnl-devel clang
2-3.HugePageとIOMMUの有効化
sed -i -e "/GRUB_CMDLINE_LINUX=/s/\"$/ default_hugepagesz=1G hugepagesz=1G hugepages=16\"/g" /etc/default/grub
sed -i -e "/GRUB_CMDLINE_LINUX=/s/\"$/ intel_iommu=on iommu=pt pci=realloc\"/g" /etc/default/grub
grub2-mkconfig -o /etc/grub2.cfg
reboot

hugepageの有効化
iommuの有効化*1
grubに設定反映
再起動

2-4.HugePageの確認とマウント
grep Huge /proc/meminfo

出力例
# grep Huge /proc/meminfo
AnonHugePages:    135168 kB
HugePages_Total:      16
HugePages_Free:       16
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:    1048576 kB

vi /etc/fstab

最終行に以下を追記
nodev  /mnt/huge_1GB hugetlbfs pagesize=1GB    0 0

HugePageの割り当て状況確認
HugePageの永続化マウント

2-5.vfio-pciの設定
echo "vfio-pci" > /etc/modules-load.d/vfio-pci.conf
echo "options vfio_iommu_type1 allow_unsafe_interrupts=1" > /etc/modules-load.d/iommu.conf

vfio-pciの永続化設定
vfio-pciのiommu利用の永続化

2-5’.uio_pci_generic設定

vfio-pciが利用できない場合は、uio_pci_genericを使用します。

echo "uio_pci_generic" > /etc/modules-load.d/uio_pci_generic.conf

uio_pci_genericの永続化設定

ここまでの準備が整ったら一旦再起動してください。

reboot

3.DPDKインストール

3-1.インストール
dnf -y install dpdk dpdk-devel dpdk-doc dpdk-tools

4.OvSソースビルド

4-1.ビルド環境準備

ビルド環境の準備をします。

cd /usr/src
git clone https://github.com/openvswitch/ovs.git

ディレクトリ移動
git cloneにてソースのダウンロード

4-2.ビルド

ソースからビルドします。

export DPDK_BUILD=/usr/share/dpdk/x86_64-default-linuxapp-gcc && \
cd /usr/src/ovs && \
./boot.sh && \
./configure --with-dpdk=$DPDK_BUILD CC=clang && \
make && \
make install

dpdk-develへのパスを指定
ディレクトリ移動
boot.sh実行
configure実行*2
ビルド
インストール

多少時間は掛かりますが、最後の方は以下のような出力がでれば正常にビルドが完了しています。

onitor-ipsec vtep/ovs-vtep '/usr/local/share/openvswitch/scripts'
make[3]: Leaving directory '/usr/src/ovs'
make[2]: Leaving directory '/usr/src/ovs'
make[1]: Leaving directory '/usr/src/ovs'

5.OvS+DPDKのネットワーク設定

5-1.全体構成

f:id:metonymical:20190106225241j:plain
前回記事の構成図と比較してください。
グレーアウト&斜体文字になっているのが前回設定した箇所です。
今回は(1)~(6)の黒文字になっている箇所を設定していきます。

5-2.全体の流れ ~概要~
  1. DPDKにバインド:(1)(2)
  2. Bridge作成:(3)
  3. Bond作成:(4)
  4. vHostUserClientポート作成:(5)
  5. 仮想マシンの設定:(6)
5-3.コマンド投入前準備1

コマンド投入前には以下のようにパスを通しておいてください。*3

vi /root/.bash_profile

PATH=$PATH:$HOME/bin:/usr/local/share/openvswitch/scripts
export PATH
5-4.コマンド投入前準備2

OvSを起動後、DPDK周りの詳細設定を追加しておきます。

ovs-ctl --system-id=random start
ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true
ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-socket-mem=1024,1024
ovs-vsctl --no-wait set Open_vSwitch . other_config:vhost-iommu-support=true
ovs-vsctl --no-wait set Open_vSwitch . other_config:pmd-cpu-mask=0x33
ovs-vsctl --no-wait get Open_vSwitch . other_config

OvSサービスの開始
DPDKの初期化
NUMA毎のメモリ設定(単位MB)
PMD-CPUマスクの設定
設定内容の確認

上記のうち、dpdk-init=true以外はチューニングパラメータなので、設定しなくてもとりあえずは動きます。

5-5.全体の流れ ~コマンドのみ~

以下のコマンドを投入していきます。
やりたいことが既に決まっている方は、構成図とコマンドの内容を見るだけでもよいと思います。

1.DPDKにバインド
(1)
dpdk-devbind --status
dpdk-devbind --bind=vfio-pci ens1f0
(2)
dpdk-devbind --bind=vfio-pci ens1f1
dpdk-devbind --status

2.Bridge作成
(3)
ovs-ctl --system-id=random stop
ovs-ctl --system-id=random start
ovs-vsctl add-br ovsbr0 -- set bridge ovsbr0 datapath_type=netdev

3.Bond作成
(4)
ovs-vsctl add-bond ovsbr0 bond0 dpdk0 dpdk1 \
 vlan_mode=trunk trunks=11,300-304 \
 bond_mode=balance-tcp lacp=active other_config:lacp-time=fast \
 -- set Interface dpdk0 type=dpdk options:dpdk-devargs=0000:08:00.0 \
 -- set Interface dpdk1 type=dpdk options:dpdk-devargs=0000:08:00.1

4.vHostUserClientポート作成
(5)
mkdir -p /usr/local/openvswitch/
touch /usr/local/openvswitch/vhuc0

ovs-vsctl add-port ovsbr0 vhuc0 \
 vlan_mode=access tag=300 \
 -- set Interface vhuc0 type=dpdkvhostuserclient \
 options:vhost-server-path=/usr/local/openvswitch/vhuc0

5.仮想マシンの設定
(6)
virsh edit Guest3

  <currentMemory unit='KiB'>1048576</currentMemory>
  <memoryBacking>
    <hugepages>
      <page size='1048576' unit='KiB' nodeset='0'/>
    </hugepages>
  </memoryBacking>
  
  <cpu mode='host-passthrough' check='none'>
    <numa>
      <cell id='0' cpus='0' memory='1048576' unit='KiB' memAccess='shared'/>
    </numa>
  </cpu>

  <interface type='vhostuser'>
    <source type='unix' path='/usr/local/openvswitch/vhuc0' mode='server'/>
    <model type='virtio'/>
  </interface>青=追記,緑=置換

6.DPDKにバインド

Kernel上で動作しているX540をDPDK上で動作されるようにバインドします。*4
(1)
dpdk-devbind --status
dpdk-devbind --bind=vfio-pci ens1f0
(2)
dpdk-devbind --bind=vfio-pci ens1f1
dpdk-devbind --status

[root@c80gmas ~]# dpdk-devbind --status

Network devices using kernel driver
===================================
0000:02:00.0 'I350 Gigabit Network Connection 1521' if=eno1 drv=igb unused=vfio-pci,uio_pci_generic *Active*
0000:02:00.1 'I350 Gigabit Network Connection 1521' if=eno2 drv=igb unused=vfio-pci,uio_pci_generic
0000:02:00.2 'I350 Gigabit Network Connection 1521' if=eno3 drv=igb unused=vfio-pci,uio_pci_generic
0000:02:00.3 'I350 Gigabit Network Connection 1521' if=eno4 drv=igb unused=vfio-pci,uio_pci_generic
0000:08:00.0 'Ethernet Controller 10-Gigabit X540-AT2 1528' if=ens1f0 drv=ixgbe unused=vfio-pci,uio_pci_generic
0000:08:00.1 'Ethernet Controller 10-Gigabit X540-AT2 1528' if=ens1f1 drv=ixgbe unused=vfio-pci,uio_pci_generic

[root@c80gmas ~]# dpdk-devbind.py --bind=vfio-pci ens1f0
[root@c80gmas ~]# dpdk-devbind.py --bind=vfio-pci ens1f1
[root@c80gmas ~]# dpdk-devbind.py --status

Network devices using DPDK-compatible driver
============================================
0000:08:00.0 'Ethernet Controller 10-Gigabit X540-AT2 1528' drv=vfio-pci unused=ixgbe,uio_pci_generic
0000:08:00.1 'Ethernet Controller 10-Gigabit X540-AT2 1528' drv=vfio-pci unused=ixgbe,uio_pci_generic

Network devices using kernel driver
===================================
0000:02:00.0 'I350 Gigabit Network Connection 1521' if=eno1 drv=igb unused=vfio-pci,uio_pci_generic *Active*
0000:02:00.1 'I350 Gigabit Network Connection 1521' if=eno2 drv=igb unused=vfio-pci,uio_pci_generic
0000:02:00.2 'I350 Gigabit Network Connection 1521' if=eno3 drv=igb unused=vfio-pci,uio_pci_generic
0000:02:00.3 'I350 Gigabit Network Connection 1521' if=eno4 drv=igb unused=vfio-pci,uio_pci_generic

バインドされると、X540がNetwork devices using DPDK-compatible driverに表示されます。

今回利用するドライバはvfio-pciですが、uio_pci_genericでも利用可能です。
利用したい場合は、以下のように変更すればOKです。

dpdk-devbind --bind=uio_pci_generic ens1f0

また、vfio-pciを利用するにあたり、ProLiant DL360G8を使用している方はRMRR設定が必要になる場合があります。
以下のサイトを参考にしてみてください。
DPDK and RMRR Compatibility Issues on the HP Proliant DL360e G8 | www.jimmdenton.com
https://support.hpe.com/hpsc/doc/public/display?sp4ts.oid=7271259&docId=emr_na-c04781229&docLocale=ja_JP

7.Bridge作成

念のため、ovsの再起動をした後、Bridge作成を行ってください。
(3)
ovs-ctl --system-id=random stop
ovs-ctl --system-id=random start
ovs-vsctl add-br ovsbr0 -- set bridge ovsbr0 datapath_type=netdev

[root@c80gmas ~]# ovs-ctl --system-id=random stop
Exiting ovs-vswitchd (20081)                               [  OK  ]
Exiting ovsdb-server (20063)                               [  OK  ]
[root@c80gmas ~]# ovs-ctl --system-id=random start
Starting ovsdb-server                                      [  OK  ]
Configuring Open vSwitch system IDs                        [  OK  ]
Enabling remote OVSDB managers                             [  OK  ]
[root@c80gmas ~]# ovs-vsctl add-br ovsbr0 -- set bridge ovsbr0 datapath_type=netdev

正常にBridgeが作成されると上記のような出力となります。

8.Bond作成

ovsbr0上にアップリンクポート*5を追加し、Bondを組みます。また、TrunkとLACPの設定も同時に追加します。
(4)
ovs-vsctl add-bond ovsbr0 bond0 dpdk0 dpdk1 \
vlan_mode=trunk trunks=11,300-304 \
bond_mode=balance-tcp lacp=active other_config:lacp-time=fast \
-- set Interface dpdk0 type=dpdk options:dpdk-devargs=0000:08:00.0 \
-- set Interface dpdk1 type=dpdk options:dpdk-devargs=0000:08:00.1
ovs-vsctl show

[root@c80gmas ~]# ovs-vsctl add-bond ovsbr0 bond0 dpdk0 dpdk1 \
>  vlan_mode=trunk trunks=11,300-304 \
>  bond_mode=balance-tcp lacp=active other_config:lacp-time=fast \
>  -- set Interface dpdk0 type=dpdk options:dpdk-devargs=0000:08:00.0 \
>  -- set Interface dpdk1 type=dpdk options:dpdk-devargs=0000:08:00.1
[root@c80gmas ~]# ovs-vsctl show
8daaa733-f6ef-4b67-a1a9-581875f33420
    Bridge "ovsbr0"
        Port "ovsbr0"
            Interface "ovsbr0"
                type: internal
        Port "bond0"
            trunks: [11, 300, 301, 302, 303, 304]
            Interface "dpdk0"
                type: dpdk
                options: {dpdk-devargs="0000:08:00.0"}
            Interface "dpdk1"
                type: dpdk
                options: {dpdk-devargs="0000:08:00.1"}
    ovs_version: "2.12.90"
[root@c80gmas ~]#

上記のように追加されていればOKです。

FullTrunkにしたい場合は、以下の行を削除してください。

vlan_mode=trunk trunks=11,300-304 \

スイッチ側の仕様でLACPが組めない場合、以下のように修正してください。

 bond_mode=balance-tcp lacp=active other_config:lacp-time=fast \
 ↓
 bond_mode=balance-slb \

9.vHostUserClientポート作成

ovsbr0上にダウンリンクポート*6を追加します。
先にSocketファイルとなるvhuc0を作成し、その後ovsbr0にポートを追加します。
(5)
mkdir -p /usr/local/openvswitch/
touch /usr/local/openvswitch/vhuc0

ovs-vsctl add-port ovsbr0 vhuc0 \
vlan_mode=access tag=300 \
-- set Interface vhuc0 type=dpdkvhostuserclient \
options:vhost-server-path=/usr/local/openvswitch/vhuc0

ovs-vsctl show

[root@c80gmas ~]# mkdir -p /usr/local/openvswitch/
[root@c80gmas ~]# touch /usr/local/openvswitch/vhuc0
[root@c80gmas ~]# ovs-vsctl add-port ovsbr0 vhuc0 \
>  vlan_mode=access tag=300 \
>  -- set Interface vhuc0 type=dpdkvhostuserclient \
>  options:vhost-server-path=/usr/local/openvswitch/vhuc0
[root@c80gmas ~]# ovs-vsctl show
8daaa733-f6ef-4b67-a1a9-581875f33420
    Bridge "ovsbr0"
        Port "ovsbr0"
            Interface "ovsbr0"
                type: internal
        Port "bond0"
            trunks: [11, 300, 301, 302, 303, 304]
            Interface "dpdk1"
                type: dpdk
                options: {dpdk-devargs="0000:08:00.1"}
            Interface "dpdk0"
                type: dpdk
                options: {dpdk-devargs="0000:08:00.0"}
        Port "vhuc0"
            tag: 300
            Interface "vhuc0"
                type: dpdkvhostuserclient
                options: {vhost-server-path="/usr/local/openvswitch/vhuc0"}
    ovs_version: "2.12.90"

上記のように追加されていればOKです。
ちなみに、vlan_mode=access tag=300としましたが、tag=300のみでも自動的にaccessポートにしてくれます。
また、trunkポートにしたい場合は、Bond設定で投入した「vlan_mode=trunk trunks=11,300-304」を参考に置換してください。

10.仮想マシンの設定

virshで仮想マシンの設定を編集します。
これにより、以下3点を実施します。
仮想マシン上でHugePageを利用可能にする
仮想マシンに対してCPUのパススルー機能を有効化する
・vHostUserにてNICを追加する
(6)
virsh edit Guest3

<currentMemory unit='KiB'>1048576</currentMemory>
<memoryBacking>
<hugepages>
<page size='1048576' unit='KiB' nodeset='0'/>
</hugepages>
</memoryBacking>


<cpu mode='host-passthrough' check='none'>
<numa>
<cell id='0' cpus='0' memory='1048576' unit='KiB' memAccess='shared'/>
</numa>
</cpu>

<interface type='vhostuser'>
<source type='unix' path='/usr/local/openvswitch/vhuc0' mode='server'/>
<model type='virtio'/>
</interface>


青=追記,緑=置換

[root@c80gmas ~]# virsh edit Guest3

  <currentMemory unit='KiB'>1048576</currentMemory>
  <memoryBacking>
    <hugepages>
      <page size='1048576' unit='KiB' nodeset='0'/>
    </hugepages>
  </memoryBacking>
  
  <cpu mode='host-passthrough' check='none'>
    <numa>
      <cell id='0' cpus='0' memory='1048576' unit='KiB' memAccess='shared'/>
    </numa>
  </cpu>

  <interface type='vhostuser'>
    <source type='unix' path='/usr/local/openvswitch/vhuc0' mode='server'/>
    <model type='virtio'/>
  </interface>青=追記,緑=置換

xmlファイルのため、virsh editで開くと、最初はどこを編集していいか探したり、迷ったりすると思いますが、通常のviエディタと同様に編集可能なため、「cpu mode」などで検索すればすぐに見つかると思います。
あと、vhostuserですが、mode=serverとなっています。以前はmode=clientだったのですが、現在はmode=serverが推奨されています。*7

11.仮想マシンの起動

仮想マシンを起動し、新規に追加されたインターフェースにIP設定後、疎通確認を行ってみてください。

virsh start Guest3

起動時にPermission Denyなどのエラーが出力される場合があります。
その場合、「2-2.qemu-kvmの実行ユーザをrootへ変更」を参照し、Qemuの実行ユーザが確実にrootとなっていることを確認してください。
その上で、

systemctl restart libvirtd.service

を実行し、それでもダメなら、ホストOSを再起動してみてください。

以上です。

12.最後に

CentOS7からの改編となります。

大きく変わっている点は、

  • qemu-kvm-evが不要*8
  • dpdkのビルドが不要*9
  • gccの代わりにclangを使用*10

といったあたりです。

一度勘所がわかると、いくらでも応用が効くようになるので、その積み重ねが大切なのかなと思います。
CentOS8にてopenstackはまだ各種パッケージなどが準備されていないようですが、ovs-dpdkは実装できたので諸々移行できそうです。
LinuxBridge&ovsにて、それぞれsriov&dpdkが使用できれば、概ねやりたいことはできるので。
また、nmcliコマンドはCentOS7でも8でも共通なので、そのまま転用可能です。

ちなみに、元々の動機はCentOS8でこれ↓がやりたかったので、CentOS8でもovs-dpdkを実装してみました。
f:id:metonymical:20191116061552p:plain
f:id:metonymical:20191116061611p:plain
github.com
これができれば、dpdk+dockerやk8sも包含されるため、現段階における仮想化されたネットワークの様々アーキテクチャは網羅されるかな、と考えています。
さらにその次のステップとして、SRv6によるネットワークスライシングなどにも踏み込めれば、楽しそうだなと考えています。

*1:IOMMUについては、DL360G8特有の設定(RMRRの設定)があるため、途中で補足を入れます。

*2:CentOS7のときは、gccを使用していましたが、今回はclangを使用しています。理由はコンパイル時間の短縮です。x86_64アーキテクチャのみの環境であれば、clang/llvmの方が早かったので。ちなみにgccでも問題ありませんが、3倍以上の時間を要しました、また、Warningメッセージが出まくるので少々不安になるかもしれません。

*3:.bash_profileに追記した場合は、ログアウト&ログインを忘れずに。またCentOS7のときとは異なり、dnfコマンドにてdpdkをインストールしているため、dpdk関連コマンドへのパス通しは不要となりました。

*4:CentOS7とは異なり、.pyの拡張子が無くなっています。.pyを入力してしまうと、No such a file or diretory~が出力されます。

*5:DPDK上で稼働しているX540のens1f0とens1f1

*6:仮想マシンが接続されるポート

*7:詳細は、 Data Plane Development Kit vHost User Client Mode with Open vSwitch* | Intel® Softwareに記載されています。

*8:CentOS8の場合、ver2.12となっているため

*9:dnfコマンドでインストールが可能なため

*10:コンパイル時間の短縮のため