Metonymical Deflection

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

CentOS7 FD.io/VPP セットアップ VMware Workstation pro編

FD.io/VPP(以下、VPP)のセットアップと自動起動設定までの構築方法を記載します。
TRexのDUTとして使用するため、前回記事のVyOSをVPPに変更しています。
なお、今回はVMware Workstation 15 Pro(以下、VMWareWrk)上のCentOS7.7で構築していきますが、KVMの場合と異なる点があります。
このため、KVM編は別の機会に記載したいと思います。

1.環境

1-1.VMWare
筐体                             : 自作PC(Win10pro)
CPU                           : Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz
VMWare              : VMware(R) Workstation 15 Pro 15.5.6 build-16341506
OS                               : CentOS7.7
TRex                             : v20.05.1
1-2.全体構成

f:id:metonymical:20200802122331j:plain
FD.io/VPPの環境を構築していきます。
VMWareWrk上では上手くいきましたが、ESXi6.7上では上手く行かず。。

1-3 .全体の流れ ~概要~
  1. 事前準備とインストール
  2. 初期設定
  3. 動作確認

2.事前準備とインストール

2-1.事前準備

NICバイスをe1000→vmxnet3に変更しますので、vmxファイルを開いてください。
仮想マシンがVMWareWrkのライブラリに登録されていると、vmxファイルが掴まれたままとなる場合があるため、一旦ライブラリからは登録を削除してください。

以下の通り、ethernet1とethernet2をvmxnet3に変更してください。
f:id:metonymical:20200802093047j:plain
ethernet0はマネジメント用となるため、そのままe1000で構いません。

2-2.インストール

以前に比べたら、たいぶ楽になりました。

yum -y install epel-release dpdk-tools
curl -s https://packagecloud.io/install/repositories/fdio/release/script.rpm.sh | sudo bash
yum -y install vpp vpp-plugins vpp-devel vpp-api-python vpp-api-lua vpp-debuginfo vpp-devel

必要なパッケージのインストール
リポジトリ設定
VPPのインストール

3.初期設定

3-1.インターフェースのbsf確認

VPPにアサインするインターフェースのbfs(Bus,Slot,Function)番号とインターフェース名を確認します。
赤文字で記載した「03:00.0」の部分が「bus:slot.function」になります。*1

[root@c77g126 ~]# dpdk-devbind -s

Network devices using kernel driver
===================================
0000:02:00.0 '82545EM Gigabit Ethernet Controller (Copper) 100f' if=ens32 drv=e1000 unused= *Active*
0000:03:00.0 'VMXNET3 Ethernet Controller 07b0' if=ens160 drv=vmxnet3 unused= 
0000:0b:00.0 'VMXNET3 Ethernet Controller 07b0' if=ens192 drv=vmxnet3 unused= 

以下のコマンドでも確認可能ですので、どちらでも構いません。

[root@c77g126 ~]# lshw -businfo -c network
Bus info          Device      Class      Description
====================================================
pci@0000:02:00.0  ens32       network    82545EM Gigabit Ethernet Controller (Copper)
pci@0000:03:00.0  ens160      network    VMXNET3 Ethernet Controller
pci@0000:0b:00.0  ens192      network    VMXNET3 Ethernet Controller
                  virbr0-nic  network    Ethernet interface
                  virbr0      network    Ethernet interface
3-2.OS起動時の設定

OS起動時にvfio-pciの読込みとインターフェースダウンを実施しますので、以下の通り設定を最下行に追記してください。
設定完了後、実行権限を付与します。

vi /etc/rc.local

[root@c77g126 ~]# vi /etc/rc.local

modprobe vfio-pci
ifconfig ens160 down
ifconfig ens192 down
exit 0

[root@c77g126 ~]# chmod 755 /etc/rc.local
3-3.VPPのstartup.conf設定

VPPのstartup.confに設定を追記します。
青文字箇所を追記してください。*2

vi /etc/vpp/startup.conf

[root@c77g126 ~]# vi /etc/vpp/startup.conf
unix {
  nodaemon
  log /var/log/vpp/vpp.log
  full-coredump
  cli-listen /run/vpp/cli.sock
  gid vpp
  startup-config /root/vpp.cfg
}

dpdk {
        dev default {
                num-rx-desc 512
                num-tx-desc 512
        }
        no-multi-seg
        dev 0000:03:00.0 {
                name ens160
                num-rx-queues 2
                num-tx-queues 2
        }
        dev 0000:0b:00.0 {
                name ens192
                num-rx-queues 2
                num-tx-queues 2
        }
}

上記の設定はVPPサービス起動時にインターフェースをアタッチする設定やvpp.cfgファイルの参照先パスの設定となります。*3

3-4.VPPのvpp.cfg設定

vpp.cfgファイルはCiscoで言うところのstartup-configファイルに相当します。
これを設定しておかないと、起動するたびにIPアドレスの設定などを都度実施することになりますので、最低限の設定を投入します。

vi /root/vpp.cfg

[root@c77g126 ~]# vi /root/vpp.cfg

set int ip address ens160 192.168.7.1/24
set int ip address ens192 192.168.8.1/24
ip route add 16.0.0.0/24 via 192.168.7.2
ip route add 48.0.0.0/24 via 192.168.8.2
set int state ens160 up
set int state ens192 up

IPの設定とStatic Routeの設定、最後にインターフェースアップ設定を入れておきます。*4

3-5.VPPサービス自動起動設定と再起動

VPPサービス自動起動設定と再起動を実施します。

systemctl enable vpp

reboot

4.動作確認

4-1.インターフェース周りの確認

以下のコマンドでVPPコンソールに入ります。

vppctl

出力例

[root@c77g126 ~]# vppctl
    _______    _        _   _____  ___
 __/ __/ _ \  (_)__    | | / / _ \/ _ \
 _/ _// // / / / _ \   | |/ / ___/ ___/
 /_/ /____(_)_/\___/   |___/_/  /_/

vpp#

VPPコンソールに入ったら、以下4つのコマンドでステータスを確認します。

show pci
show hardware-interfaces
show interface
show interface addr

出力例
確認するポイントを赤文字で記載します。

vpp# show pci
Address      Sock VID:PID     Link Speed   Driver          Product Name                    Vital Product Data
0000:02:00.0   0  8086:100f   unknown      e1000
0000:03:00.0   0  15ad:07b0   5.0 GT/s x32 vfio-pci
0000:0b:00.0   0  15ad:07b0   5.0 GT/s x32 vfio-pci


vpp# show hardware-interfaces
              Name                Idx   Link  Hardware
ens160                             1     up   ens160
  Link speed: 10 Gbps
  Ethernet address 00:0c:29:35:dd:19
  VMware VMXNET3
    carrier up full duplex mtu 9206
    flags: admin-up pmd
    Devargs:
    rx: queues 2 (max 16), desc 512 (min 128 max 4096 align 1)
    tx: queues 1 (max 8), desc 512 (min 512 max 4096 align 1)
    pci: device 15ad:07b0 subsystem 15ad:07b0 address 0000:03:00.00 numa 0
    max rx packet len: 16384
    promiscuous: unicast off all-multicast on
    vlan offload: strip off filter off qinq off
    rx offload avail:  vlan-strip udp-cksum tcp-cksum tcp-lro vlan-filter
                       jumbo-frame scatter rss-hash
    rx offload active: none
    tx offload avail:  vlan-insert udp-cksum tcp-cksum tcp-tso multi-segs
    tx offload active: multi-segs
    rss avail:         ipv4-tcp ipv4 ipv6-tcp ipv6
    rss active:        none
    tx burst function: vmxnet3_xmit_pkts
    rx burst function: vmxnet3_recv_pkts

    tx frames ok                                           1
    tx bytes ok                                           42
    extended stats:
      tx good packets                                      1
      tx good bytes                                       42
      tx q0packets                                         1
      tx q0bytes                                          42
ens192                             2     up   ens192
  Link speed: 10 Gbps
  Ethernet address 00:0c:29:35:dd:23
  VMware VMXNET3
    carrier up full duplex mtu 9206
    flags: admin-up pmd
    Devargs:
    rx: queues 2 (max 16), desc 512 (min 128 max 4096 align 1)
    tx: queues 1 (max 8), desc 512 (min 512 max 4096 align 1)
    pci: device 15ad:07b0 subsystem 15ad:07b0 address 0000:0b:00.00 numa 0
    max rx packet len: 16384
    promiscuous: unicast off all-multicast on
    vlan offload: strip off filter off qinq off
    rx offload avail:  vlan-strip udp-cksum tcp-cksum tcp-lro vlan-filter
                       jumbo-frame scatter rss-hash
    rx offload active: none
    tx offload avail:  vlan-insert udp-cksum tcp-cksum tcp-tso multi-segs
    tx offload active: multi-segs
    rss avail:         ipv4-tcp ipv4 ipv6-tcp ipv6
    rss active:        none
    tx burst function: vmxnet3_xmit_pkts
    rx burst function: vmxnet3_recv_pkts

    tx frames ok                                           1
    tx bytes ok                                           42
    extended stats:
      tx good packets                                      1
      tx good bytes                                       42
      tx q0packets                                         1
      tx q0bytes                                          42
local0                             0    down  local0
  Link speed: unknown
  local


vpp# show interface
              Name               Idx    State  MTU (L3/IP4/IP6/MPLS)     Counter          Count
ens160                            1      up          9000/0/0/0     tx packets                     1
                                                                    tx bytes                      42
ens192                            2      up          9000/0/0/0     tx packets                     1
                                                                    tx bytes                      42
local0                            0     down          0/0/0/0


vpp# show interface addr
ens160 (up):
  L3 192.168.7.1/24
ens192 (up):
  L3 192.168.8.1/24
local0 (dn):
4-2.疎通確認

自身のIPにPingが飛ばないため、対向機器にPingを飛ばすか、対向機器からPingを飛ばしてください。

vpp# ping 192.168.7.1

Statistics: 5 sent, 0 received, 100% packet loss
自身のIPにはPingが飛びません。

vpp# ping 192.168.7.2
116 bytes from 192.168.7.2: icmp_seq=1 ttl=64 time=.3834 ms
116 bytes from 192.168.7.2: icmp_seq=2 ttl=64 time=.3127 ms
116 bytes from 192.168.7.2: icmp_seq=3 ttl=64 time=.2928 ms
116 bytes from 192.168.7.2: icmp_seq=4 ttl=64 time=.3206 ms
116 bytes from 192.168.7.2: icmp_seq=5 ttl=64 time=.2619 ms

Statistics: 5 sent, 5 received, 0% packet loss
vpp#

ここまでOKであれば、前回&前々回記事を参考にTRexから負荷を印加してみてください。

以上です。

5.最後に

以下のサイトを参考にさせて頂きました。
VPP事始め
Setup the FD.io Repository - Centos 7 — The Vector Packet Processor 20.01 documentation
VPP with VMware/Vmxnet3 — The Vector Packet Processor 20.05 documentation

先にも記載しましたが、VMware Workstation 15 Proでは正常に動作しましたが、ESXi6.7上では外部と疎通できずでした。
仮想マシンはCentOS7に加え、Ubuntu18.04でも試行しましたがNGでした。
CPUパフォーマンスカウンタを仮想化、IOMMUを仮想化の有効化も試しましたがNGでした。*5

なお、KVM上での動作については以下の構成で全てOKだったので、今後記載していきたいと思います。

  • ベアメタル
  • Linux Bridge
  • OvS-DPDK
  • SR-IOV

SR-IOVではMTUを1501以上にしないと外部疎通が不可など、割とクセがあるので、細かく記載できればと考えています。

仮想マシン上でDPDKが動作するものも増えてきたため、性能を測る上での比較元として、VPPを活用していきたいなと考えています。
あとは、DPDK利用時のチューニングの勘所などを知る上で、VPPはとてもよい教材だと思うので、もう少し掘り下げて動作を確認していければと考えています。
加えて、以下の構成も試してみたいなと考えています。
f:id:metonymical:20200802133701p:plain

*1:ちなみに、先頭の「0000」は、PCIのDomain番号となります。

*2:DPDKの設定項目はstartup.confの下方に存在しますが、全てコメントアウトされているため、そのまま追記して構いません。

*3:ファイル名や格納パスは任意でOKなようです。

*4:今回はTRexのDUTとして動作させるため、本設定のみで充分です。

*5:何か他に良い方法はないかと模索中ですが、ESXiはKVMよりも苦手です。。

CentOS7 Cisco TRex利用方法 その2

前回記事の続きです。
metonymical.hatenablog.com

前回記事の「5.その他便利機能」をもう少し拡張して、痒いところに手が届く使い方について記載しようと思います。
なお、手順に沿って動作確認するという記事ではなく、小技集のように記載するため、まとまり感はありませんが、あしからず。

1.環境

1-1.VMWare
筐体                             : 自作PC(Win10pro)
CPU                           : Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz
VMWare              : VMware(R) Workstation 15 Pro 15.5.6 build-16341506
OS                               : CentOS7.7
TRex                             : v2.82
1-2.全体構成

f:id:metonymical:20200724145242j:plain
前回記事と同様の環境です。

1-3 .小技集
  1. WireEditによるPcapファイルの編集
  2. 1ポートのみで負荷印加
  3. 任意のプロトコルをHEXで書いて負荷印加
  4. GTP-Uパケットの生成

2.WireEditによるPcapファイルの編集

まず、前準備として「Pcapファイルの中身を少しだけ変えたい」といったケースで、サクッとPcapファイルを編集する方法を記載します。

私は「Wire Edit 1.10.118」を使用しています。
最新版のWire Editは有償版となっており、高度な機能を備えていますが、そこまでの機能は不要という場合は、「Wire Edit 1.10.118」で充分だと思います。
もしくは、本編では取り上げませんが、「Ostinato」も同様のことができると思います。

以下スクリーンショットになりますが、下図の通り、任意の値を直感的に編集することが可能です。
f:id:metonymical:20200801154138j:plain
この後の項で、Wire Editとの合わせ技について記載します。

3.1ポートのみで負荷印加

これまでの例では、DUTに対して、ClientポートとServerポートの2ポートを利用して負荷印加していましたが、ここではClient用の1ポートのみを使用して負荷印加する方法を記載します。

3-1.1ポートのみ使用する場合の構成例

f:id:metonymical:20200801155420j:plain

3-2.trex_cfg.yamlの設定

1ポートのみを使用する場合は以下のようにdummyポートの設定をします。

vi /etc/trex_cfg.yaml

[root@c77g231 ~]# vi /etc/trex_cfg.yaml
### Config file generated by dpdk_setup_ports.py ###

- version: 2
#  interfaces: ['02:01.0', '02:03.0']
  interfaces: ['02:01.0', 'dummy']
  memory:
     dp_flows: 4048576
  port_info:
      - ip: 192.168.7.2
        default_gw: 192.168.7.1
#      - ip: 192.168.8.2
#        default_gw: 192.168.8.1

  platform:
      master_thread_id: 0
      latency_thread_id: 1
      dual_if:
        - socket: 0
          threads: [2,3]
3-3.dns.yamlの設定

サーバアドレスを固定します。
前回記事で紹介したserver_addr と one_app_serverオプションを使用してもよいと思います。

vi ./cap2/dns.yaml

[root@c77g231 trex]# vi ./cap2/dns.yaml
- duration : 10.0
  generator :
          distribution : "seq"
          clients_start : "16.0.0.0"
          clients_end   : "16.0.255.255"
          servers_start : "192.168.8.2"
          servers_end   : "192.168.8.2"
          clients_per_gb : 201
          min_clients    : 101
          dual_port_mask : "0.0.0.0"
          tcp_aging      : 1
          udp_aging      : 1
  cap_info :
     - name: cap2/dns.pcap
       cps : 1.0
       ipg : 10000
       rtt : 10000
       w   : 1

そして、負荷印加。

./t-rex-64 -f ./cap2/dns.yaml -d 30 -m 1

恐らく、失敗すると思います。
なぜなら、Defaultで格納されているdns.pcapファイルには、クエリとレスポンスの2パケットが含まれているためです。
f:id:metonymical:20200801160445j:plain
このため、Wire Editを使って不要なパケット(DNSレスポンス)を削除します。また、クエリがwww.cisco.comになっていますが、これも編集します。

3-4.Wire Editによるdns.pcapの編集

DNSレスポンスのパケットを削除します。
f:id:metonymical:20200801160946j:plain
続いて、qnameを編集します。DNS: Queryのツリーを展開し、「QNAME[0]」の辺りをダブルクリックすると、画面下のEdit PDU画面が表示されます。
f:id:metonymical:20200801161216j:plain
あとは直感的に操作できると思いますが、www, cisco, comなどを直接クリックして編集してください。
なお、レイヤ2-4はTRexが環境に合わせて設定してくれるため、アプリ層のみ編集すればOKです。(IPアドレスやL4Port番号を編集する必要はありません。)

再び、負荷を印加すれば一方的にDNSクエリのみを投げてくれます。

./t-rex-64 -f ./cap2/dns.yaml -d 30 -m 1

今回の構成ではDNSサーバを準備していますので、DNSサーバがレスポンスを返してくれます。

3-5.異なる2つのDNSクエリを送信する場合

上記方法の場合、TRexは1つのDNSクエリだけ繰り返し送信しますが、キャッシュヒット率を確認したいケースなどでは、キャッシュにヒットするパケットとキャッシュにヒットしないパケットの2つのDNSクエリを送信する必要が出てきたりします。
そんなときの設定方法を記載します。

vi ./cap2/dns.yaml

[root@c77g231 trex]# vi ./cap2/dns.yaml
- duration : 10.0
  generator :
          distribution : "seq"
          clients_start : "16.0.0.0"
          clients_end   : "16.0.255.255"
          servers_start : "192.168.8.2"
          servers_end   : "192.168.8.2"
          clients_per_gb : 201
          min_clients    : 101
          dual_port_mask : "0.0.0.0"
          tcp_aging      : 1
          udp_aging      : 1
  cap_info :
     - name: cap2/dns.pcap
       cps : 1.0
       ipg : 10000
       rtt : 10000
       w   : 1
     - name: cap2/dns2.pcap
       cps : 9.0
       ipg : 10000
       rtt : 10000
       w   : 1

ここでは例として、dns2.pcapと記載しましたが、先に説明したWire Editを用いて、pcapファイルのqnameを編集することにより、

cap2/dns.pcap CPS1 キャッシュにヒットしないパケット
cap2/dns2.pcap CPS9 キャッシュにヒットするパケット

といった形で負荷を印加することが可能です。
ヒットする/しないはTTLなどで調整してください。

4.任意のプロトコルをHEXで書いて負荷印加

上記の方法はいずれもSTFモードにより設定してきました。

しかし、「-c 14」オプションにより複数CPUコアを利用して、より大きな負荷をかけたい場合、ASTFモードが必要になってきます。

また、「3-1.1ポートのみ使用する場合の構成例」に示した構成の場合、DNSサーバ側が負荷に耐え切れなくなり、DNSサーバの数を増やそうとすると、今度はLBを間に噛ませる必要が出てくるなど、本来の目的とは異なるポイントも考慮する必要が出てきます。
このため、元々の構成に戻し、DUTに対して、TRexのClientとServerポートを接続します。
f:id:metonymical:20200724145242j:plain

なお、「3-5.異なる2つのDNSクエリを送信する場合」に記載した方法をASTFモードで実施しようとすると失敗します。
ASTFモードではデフォルトで、一つのPcapファイルにつき、一つのサーバL4Portしか設定できないためです。

例えば、
 http.pcapでL4Port80、dns.pcapでL4Port53 を同時に負荷印加
といった設定は比較的容易に設定できますが、
 dns.pcapでL4Port53、dns2.pcapでL4Port53 を同時に負荷印加
としたい場合は、前述の通り失敗します。

このため、以下に紹介する方法で実現可能です。

4-1.http_simple.pyの設定 その1

ここでは例として、http_simple.pyをベースに編集していきます。
ますは、「http.pcapでL4Port80、dns.pcapでL4Port53」のパターン

vi astf/http_simple.py

[root@c77g232 trex]# vi astf/http_simple.py
from trex.astf.api import *

class Prof1():
    def __init__(self):
        pass

    def get_profile(self, **kwargs):
        # ip generator
        ip_gen_c = ASTFIPGenDist(ip_range=["16.0.0.0", "16.0.0.255"], distribution="seq")
        ip_gen_s = ASTFIPGenDist(ip_range=["48.0.0.0", "48.0.255.255"], distribution="seq")
        ip_gen = ASTFIPGen(glob=ASTFIPGenGlobal(ip_offset="0.0.0.0"),
                           dist_client=ip_gen_c,
                           dist_server=ip_gen_s)

        return ASTFProfile(default_ip_gen=ip_gen,
                        cap_list=[
                                ASTFCapInfo(file="../avl/delay_10_http_browsing_0.pcap",cps=1),
                                ASTFCapInfo(file="../cap2/dns.pcap",cps=1)
                        ])


def register():
    return Prof1()

赤文字箇所を追記しています。
httpのPcapファイルの後に「,」を入れて、dnsのPcapファイルを設定している点に注意してください。

ASTFCapInfo(file="../avl/delay_10_http_browsing_0.pcap",cps=1),
ASTFCapInfo(file="../cap2/dns.pcap",cps=1)

上記設定では特にL4DstPortを指定していませんが、Pcapファイルから自動的に読み取ってくれるため問題ありません。

4-2.http_simple.pyの設定 その2

次に「dns.pcapでL4Port53、dns2.pcapでL4Port53」のパターン

cap2/dns.pcap CPS1 キャッシュにヒットしないパケット
cap2/dns2.pcap CPS9 キャッシュにヒットするパケット

<失敗パターン>

vi astf/http_simple.py

[root@c77g232 trex]# vi astf/http_simple.py
from trex.astf.api import *

class Prof1():
    def __init__(self):
        pass

    def get_profile(self, **kwargs):
        # ip generator
        ip_gen_c = ASTFIPGenDist(ip_range=["16.0.0.0", "16.0.0.255"], distribution="seq")
        ip_gen_s = ASTFIPGenDist(ip_range=["48.0.0.0", "48.0.255.255"], distribution="seq")
        ip_gen = ASTFIPGen(glob=ASTFIPGenGlobal(ip_offset="0.0.0.0"),
                           dist_client=ip_gen_c,
                           dist_server=ip_gen_s)

        return ASTFProfile(default_ip_gen=ip_gen,
                        cap_list=[
                                ASTFCapInfo(file="../avl/dns.pcap",cps=1),
                                ASTFCapInfo(file="../avl/dns2.pcap",cps=9)
                        ])


def register():
    return Prof1()

上記のように「4-1.http_simple.pyの設定 その1」と同様に設定すると、負荷を印加した際にエラーが出て失敗します。

<成功パターン>
とても複雑になっていますが、まずは見てください。

これは「astf/shared_port.py」のサンプルファイルをベースにしています。
L2-L4部分はTRexに任せつつ、L4より上位層(DNSプロトコル)部分をHEXで書いています。*1

dns_2flow.pyとして、新規にファイルを作成します。

vi astf/dns_2flow.py

[root@c77g232 trex]# vi astf/dns_2flow.py

# Example for creating your program by specifying buffers to send, without relaying on pcap file
from trex.astf.api import *

# we can send either Python bytes type as below:
dns_req1 = b'\x69\xE6\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x05\x68\x69\x74\x30\x30\x04\x74\x65\x73\x74\x03\x6C\x61\x62\x00\x00\x01\x00\x01\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00'
dns_req2 = b'\x85\xA2\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x05\x68\x69\x74\x30\x31\x04\x74\x65\x73\x74\x03\x6C\x61\x62\x00\x00\x01\x00\x01\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00'
# or we can send Python string containing ascii chars, as below:
dns_res1 = b'\x69\xE6\x81\x80\x00\x01\x00\x01\x00\x00\x00\x01\x05\x68\x69\x74\x30\x30\x04\x74\x65\x73\x74\x03\x6C\x61\x62\x00\x00\x01\x00\x01\x05\x68\x69\x74\x30\x30\x04\x74\x65\x73\x74\x03\x6C\x61\x62\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\xC0\xA8\x00\x64\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00'
dns_res2 = b'\x85\xA2\x81\x80\x00\x01\x00\x01\x00\x00\x00\x01\x05\x68\x69\x74\x30\x31\x04\x74\x65\x73\x74\x03\x6C\x61\x62\x00\x00\x01\x00\x01\x05\x68\x69\x74\x30\x31\x04\x74\x65\x73\x74\x03\x6C\x61\x62\x00\x00\x01\x00\x01\x00\x01\x51\x80\x00\x04\xC0\xA8\x00\x65\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00'

class Prof1():
    def __init__(self):
        pass  # tunables

    def _udp_client_prog(self, req, res):
        prog_c = ASTFProgram(stream=False)
        prog_c.send_msg(req)
        prog_c.recv_msg(1)
        return prog_c

    def _udp_server_prog(self, req, res):
        prog_s = ASTFProgram(stream=False)
        prog_s.recv_msg(1)
        prog_s.send_msg(res)
        return prog_s

    def create_profile(self, proto='all', temp='all'):
        # UDP client and server commands
        udp_prog_c1 = self._udp_client_prog(dns_req1, dns_res1)
        udp_prog_c2 = self._udp_client_prog(dns_req2, dns_res2)

        udp_prog_s1 = self._udp_server_prog(dns_req1, dns_res1)
        udp_prog_s2 = self._udp_server_prog(dns_req2, dns_res2)

        # ip generator
        ip_gen_c = ASTFIPGenDist(ip_range=["16.0.0.0", "16.0.0.255"], distribution="seq")
        ip_gen_s = ASTFIPGenDist(ip_range=["48.0.0.0", "48.0.255.255"], distribution="seq")
        ip_gen = ASTFIPGen(glob=ASTFIPGenGlobal(ip_offset="1.0.0.0"),
                           dist_client=ip_gen_c,
                           dist_server=ip_gen_s)

        # use default port 80
        assoc_by_l7 = ASTFAssociationRule(ip_start="48.0.0.0", ip_end="48.0.255.255", port=53,
                                          l7_map={"offset":[0,1,2,3]})

        templates = []
        # UDP templates
        udp_temp_c1 = ASTFTCPClientTemplate(program=udp_prog_c1, ip_gen=ip_gen, cps=1, port=53)
        udp_temp_s1 = ASTFTCPServerTemplate(program=udp_prog_s1, assoc=assoc_by_l7)
        udp_template1 = ASTFTemplate(client_template=udp_temp_c1, server_template=udp_temp_s1, tg_name='udp1')

        udp_temp_c2 = ASTFTCPClientTemplate(program=udp_prog_c2, ip_gen=ip_gen, cps=1, port=53)
        udp_temp_s2 = ASTFTCPServerTemplate(program=udp_prog_s2, assoc=assoc_by_l7)
        udp_template2 = ASTFTemplate(client_template=udp_temp_c2, server_template=udp_temp_s2, tg_name='udp2')

        if proto == 'all' or proto == 'udp':
                templates += [ udp_template1, udp_template2 ]

        # profile
        profile = ASTFProfile(default_ip_gen=ip_gen, templates=templates)
        return profile

    def get_profile(self, **kwargs):
        proto = kwargs.get('proto','all').lower()
        temp = kwargs.get('temp','all').lower()
        return self.create_profile(proto, temp)


def register():
    return Prof1()

実際に負荷を印加する際は以下のようにします。詳細は前回記事を参照してください。

ASTFにてインタラクティブモードで起動

[root@c77g232 trex]# ./t-rex-64 -i --astf

TRexコンソール上にログイン

./trex-console -s 192.168.11.232

TRexコンソール上で負荷印加を開始

trex>start -f astf/dns_2flow.py -d 30 -m 1

実際に流れたトラフィックのPcapファイルは、こちらになります。

4-3.astf/dns_2flow.pyの解説

DNSプロトコル部分
dns_req1とdns_res1の変数にHEXで書かれたプロトコル部分を、それぞれ格納しています。

dns_req1 = b'\x69\xE6\x01~一部省略~\x00\x00'
dns_res1 = b'\x69\xE6\x81~一部省略~\x00\x00'

Wire Editでqnameなど必要な箇所を編集後、以下のようにDNSプロトコル部分のHEXのみを「Copy Selected」にて、コピーしてテキストエディタなどに貼り付けます。*2
f:id:metonymical:20200801192816j:plain

69 E6 01 20 00 01 00 00 00 00 00 01 05 68 69 74 30 30 04 74 65 73 74 03 6C 61 62 00 00 01 00 01 00 00 29 10 00 00 00 00 00 00 00

「 (スペース)」を「\x」で全て置換します。

\x69\xE6\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x05\x68\x69\x74\x30\x30\x04\x74\x65\x73\x74\x03\x6C\x61\x62\x00\x00\x01\x00\x01\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00

' '」(シングルコーテーション)で括られた青文字部分を置き換えれば、DNSプロトコル部分が作成できます。

dns_req1 = b'\x69\xE6\x01~一部省略~\x00\x00'

以下のようになります。

dns_req1 = b'\x69\xE6\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x05\x68\x69\x74\x30\x30\x04\x74\x65\x73\x74\x03\x6C\x61\x62\x00\x00\x01\x00\x01\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00'

DNSレスポンスについても同様の手順で作成してください。

L3-L4部分
環境により設定変更する部分を赤文字で記載しておきます。

ip_gen_c = ASTFIPGenDist(ip_range=["16.0.0.0", "16.0.0.255"], distribution="seq")
ip_gen_s = ASTFIPGenDist(ip_range=["48.0.0.0", "48.0.255.255"], distribution="seq")
ip_gen = ASTFIPGen(glob=ASTFIPGenGlobal(ip_offset="1.0.0.0"),
                   dist_client=ip_gen_c,
                   dist_server=ip_gen_s)

# use default port 80
assoc_by_l7 = ASTFAssociationRule(ip_start="48.0.0.0", ip_end="48.0.255.255", port=53,
                                  l7_map={"offset":[0,1,2,3]})

templates = []
# UDP templates
udp_temp_c1 = ASTFTCPClientTemplate(program=udp_prog_c1, ip_gen=ip_gen, cps=1, port=53)
udp_temp_s1 = ASTFTCPServerTemplate(program=udp_prog_s1, assoc=assoc_by_l7)
udp_template1 = ASTFTemplate(client_template=udp_temp_c1, server_template=udp_temp_s1, tg_name='udp1')

udp_temp_c2 = ASTFTCPClientTemplate(program=udp_prog_c2, ip_gen=ip_gen, cps=1, port=53)
udp_temp_s2 = ASTFTCPServerTemplate(program=udp_prog_s2, assoc=assoc_by_l7)
udp_template2 = ASTFTemplate(client_template=udp_temp_c2, server_template=udp_temp_s2, tg_name='udp2')

その他の部分について解説は割愛しますが、VSCodeなどで変数名を追っていけば、概ね、どんなことをやっているかは把握できると思います。

<補足1>
ベースとなっている「astf/shared_port.py」のサンプルファイルを参照すると、HEXで書いた変数部分はStringで書かれています。

http_req = b'GET /3384 HTTP/1.1\r\nHost: 22.0.0.3\r\n~一部省略~compress\r\n\r\n'
http_response = 'HTTP/1.1 200 OK\r\nServer: Microsoft-IIS/6.0\r\n~一部省略~<\html>'

ASTFモードでは、やはりTCPがメインとなってきますので、上記のようにHTTPのパケットを生成することも当然可能です。
むしろHTTPのサンプルファイルの方が圧倒的に多いため、astf/配下のpyファイルは、ぜひ参考にしてみてください。

5.GTP-Uパケットの生成

おまけとして、最後にGTP-Uパケットを生成してみます。
HEX部分については、以下の過去記事で生成したPcapファイルを元に生成します。
metonymical.hatenablog.com

5-1.GTPヘッダ以下の抽出

先の方法で抽出しますので、ポイントだけ記載します。

Wire EditがGTPのParseに対応していないため、UDPのData部がHEXで表示されますので、そのままコピーします。
f:id:metonymical:20200801210710j:plain
もし、不安であれば、事前にWiresharkなどで確認しておくと良いと思います。
「30 ff 00 54」から開始されていることが確認できます。
f:id:metonymical:20200801210855j:plain

テキストエディタなどに貼り付けて、先の方法で加工します。
上記例では、Echo Request部分のみとなるため、Echo Replyも同様に実施してください。

5-2.GTP-Uパケットの生成

astf/gtpu1.pyというファイルを新規作成します。
udp_reqとudp_res変数に抽出したHEXを格納しています。

vi astf/gtpu1.py

[root@c77g232 trex]# vi astf/gtpu1.py

from trex.astf.api import *

udp_req = b'\x30\xFF\x00\x54\x00\x00\x00\x01\x45\x00\x00\x54\x08\x63\x40\x00\x40\x01\x0E\x3D\x19\x00\x00\x02\x0A\x0A\x00\xFE\x08\x00\x79\x1A\x07\xBA\x00\x17\xD7\x89\x0D\x5E\x00\x00\x00\x00\xC5\x59\x0E\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37'
udp_res = b'\x30\xFF\x00\x54\xCA\x6F\xE0\xDD\x45\xE0\x00\x54\x34\x1C\x40\x00\x3F\x01\xE2\xA3\x0A\x0A\x00\xFE\x19\x00\x00\x02\x00\x00\x81\x1A\x07\xBA\x00\x17\xD7\x89\x0D\x5E\x00\x00\x00\x00\xC5\x59\x0E\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37'

class Prof1():
    def __init__(self):
        pass  # tunables

    def create_profile(self):
        # client commands
        prog_c = ASTFProgram(stream=False)
        prog_c.send_msg(udp_req)
        prog_c.recv_msg(1)

        prog_s = ASTFProgram(stream=False)
        prog_s.recv_msg(1)
        prog_s.send_msg(udp_res)

        # ip generator
        ip_gen_c = ASTFIPGenDist(ip_range=["16.0.0.0", "16.0.0.255"], distribution="seq")
        ip_gen_s = ASTFIPGenDist(ip_range=["48.0.0.0", "48.0.255.255"], distribution="seq")
        ip_gen = ASTFIPGen(glob=ASTFIPGenGlobal(ip_offset="1.0.0.0"),
                           dist_client=ip_gen_c,
                           dist_server=ip_gen_s)


        # template
        temp_c = ASTFTCPClientTemplate(program=prog_c,ip_gen=ip_gen,cps=1,port=2152)
        temp_s = ASTFTCPServerTemplate(program=prog_s, assoc=ASTFAssociationRule(port=2152))
        template = ASTFTemplate(client_template=temp_c, server_template=temp_s)

        # profile
        profile = ASTFProfile(default_ip_gen=ip_gen, templates=template, # add templates 
                               )
        return profile

    def get_profile(self, **kwargs):
        return self.create_profile()


def register():
    return Prof1()

cpsは任意の値に設定の上、Port=2152は忘れずに。

<補足2>
DNSGTP-Uにて、HEX部分の構成例を記載しましたが、他のプロトコル(RADIUSとかDIAMETERなど)にも応用が効くと思いますので、色々試してみてください。

STLモードであれば、scapyが使えるようなのですが、ASTFモードのサンプルファイルでは、scapyによるパケット生成例が無かったように思います。*3
やりたいこととしては、L4よりも上位層のプロトコル部分のパケットを生成したいので、scapyの方がわかり易く設定できるだろうと考えています。*4

<補足3>
GTP-Uの場合、OuterのL4Src/DstPortは共に2152にて通信しているケースがほとんどだと思いますが、ASTFモードではSrcPortの固定ができません。
STFモードであれば、「keep_src_port」オプションがあるのですが。

このため、イマイチ実用性に欠ける部分があります。
ASTFのAPIリファレンスも確認したのですが、該当する設定項目がなく、githubで探してみたところ、以下の関数でランダム化されてしまうようです。

generate_rand_sport()

もし、どなたか、これの回避策などご存じでしたら教えて頂けると助かります。*5

以上です。

6.最後に

以下のサイトを参考にさせて頂きました。
https://trex-tgn.cisco.com/trex/doc/trex_astf.html

ここに紹介した例は、TRex利用方法の中でも、本当に氷山の一角です。
まだまだ応用例は多数存在しますので、またちょくちょく追加の記事を書いていくかもしれません。

また、VLANやNATにも対応していますので、その辺も追々書きたいなと考えています。
さらに、SSL終端について、もう少し理解できたら書きたいと思います。

現段階では、以下のようにHTTPをSSL(HTTPS)に変換した後、DUTにトラフィックを印加することで実現できています。

TRex Client --- HTTP->SSL(HTTPS)変換装置 --- DUT --- TRex Server

実際、大きな負荷をかける場合「TRex Client --- HTTP->SSL(HTTPS)変換装置」を仮想マシンなどで多数準備して、一台のDUTに一気に負荷を印加します。

ちなみに、SSLのPcapファイルをそのまま流しても、SSLを終端するようなDUT(例えば、LBなど)に対しては意味がありません。*6

今後は、DUTをVyOSからFD.io/VPPに変更してみたいなと思います。

*1:もちろん、手書きではなくWire Editを使用してHEX部分をコピーしています。

*2:L2-4はTRexが自動的に設定してくれるため無視してください。

*3:私も完全に全てを把握できていないため、もしかしたら存在するかもしれません。

*4:今回はscapyの学習時間よりも、Wire EditによるHEX編集&貼り付けの力業の方が早かったため、scapyは今後の課題とさせてください。

*5:ASTFモードに「keep_src_port」オプション相当の機能実装をリクエストしてみようかと少し真面目に悩んでいます。

*6:RSA KeyやPre Master Secret logが無いと復号できないので。。。

CentOS7 Cisco TRex利用方法 その1

Cisco TRexのセットアップから利用方法について記載します。
また、実用面を考慮し、PingやPcap方法についても記載します。

1.環境

1-1.VMWare
筐体                             : 自作PC(Win10pro)
CPU                           : Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz
VMWare              : VMware(R) Workstation 15 Pro 15.5.6 build-16341506
OS                               : CentOS7.7
TRex                             : v2.82
1-2.全体構成

f:id:metonymical:20200724145242j:plain
ベアメタル環境でも構築可能です。

1-3 .全体の流れ ~概要~
  1. インストール手順と事前準備
  2. 簡易負荷試験(STFモード)
  3. ASTFモード
  4. その他便利機能(PingやPcap方法など)

2.インストール手順と事前準備

全体構成図に記載したようなNWを構築しておいてください。
TRexとDUT間でPing疎通が確認できるところまでは各自で実施してください。
TRexはDPDKを利用して動作しますので、KernelからDPDKにNICの制御が委譲されると、bash上ではIPアドレスなどの確認操作ができなくなります。*1

2-1.TRexのインストール

tar.gzファイルをDLして任意のディレクトリに展開するだけです。
ここでは以下のコマンドにより、TRex環境を構築しました。
以降は、/root/trex配下にて作業を行います。

mkdir -p /opt/trex && \
cd /opt/trex && \
wget --no-check-certificate --no-cache https://trex-tgn.cisco.com/trex/release/v2.82.tar.gz && \
tar -xzvf v2.82.tar.gz && \
ln -s /opt/trex/v2.82 /root/trex && \
cd /root/trex
2-2.事前準備

以下のコマンドにより、DPDKで使用するPortのIP設定を行っていきます。

./dpdk_setup_ports.py -i

出力例

[root@c77g127 trex]# ./dpdk_setup_ports.py -i
By default, IP based configuration file will be created. Do you want to use MAC based config? (y/N) Enter
+----+------+---------+-------------------+----------------------------------------------+--------+----------+----------+
| ID | NUMA |   PCI   |        MAC        |                     Name                     | Driver | Linux IF |  Active  |
+====+======+=========+===================+==============================================+========+==========+==========+
| 0  | -1   | 02:00.0 | 00:0c:29:12:ff:5a | 82545EM Gigabit Ethernet Controller (Copper) | e1000  | ens32    | *Active* |
+----+------+---------+-------------------+----------------------------------------------+--------+----------+----------+
| 1  | -1   | 02:01.0 | 00:0c:29:12:ff:64 | 82545EM Gigabit Ethernet Controller (Copper) | e1000  | ens33    |          |
+----+------+---------+-------------------+----------------------------------------------+--------+----------+----------+
| 2  | -1   | 02:03.0 | 00:0c:29:12:ff:6e | 82545EM Gigabit Ethernet Controller (Copper) | e1000  | ens35    |          |
+----+------+---------+-------------------+----------------------------------------------+--------+----------+----------+
Please choose even number of interfaces from the list above, either by ID , PCI or Linux IF
Stateful will use order of interfaces: Client1 Server1 Client2 Server2 etc. for flows.
Stateless can be in any order.
Enter list of interfaces separated by space (for example: 1 3) : 1 2

For interface 1, assuming loopback to it's dual interface 2.
Putting IP 1.1.1.1, default gw 2.2.2.2 Change it?(y/N).y
Please enter IP address for interface 1: 192.168.7.2
Please enter default gateway for interface 1: 192.168.7.1
For interface 2, assuming loopback to it's dual interface 1.
Putting IP 2.2.2.2, default gw 1.1.1.1 Change it?(y/N).y
Please enter IP address for interface 2: 192.168.8.2
Please enter default gateway for interface 2: 192.168.8.1
Print preview of generated config? (Y/n) Enter
### Config file generated by dpdk_setup_ports.py ###

- version: 2
  interfaces: ['02:01.0', '02:03.0']
  port_info:
      - ip: 192.168.7.2
        default_gw: 192.168.7.1
      - ip: 192.168.8.2
        default_gw: 192.168.8.1

  platform:
      master_thread_id: 0
      latency_thread_id: 1
      dual_if:
        - socket: 0
          threads: [2,3]


Save the config to file? (Y/n) Enter
Default filename is /etc/trex_cfg.yaml
Press ENTER to confirm or enter new file:
Saved to /etc/trex_cfg.yaml.
2-3.設定ファイルのチューニング

/etc/trex_cfg.yamlに設定ファイルが格納されています。
そこそこ大きな負荷をかける場合(例えば、10万CPS以上)、メモリが不足する場合があるため追加しておきます。

2020/12/20追記
dest_mac: にてdefault_gwのMACアドレスを追記しました。
これはarp処理に失敗して負荷が掛けられないことがあった際、その対処方法として、とても有効だったため追記しました。

vi /etc/trex_cfg.yaml

[root@c77g127 trex]# vi /etc/trex_cfg.yaml

### Config file generated by dpdk_setup_ports.py ###

- version: 2
  interfaces: ['02:01.0', '02:03.0']
  memory:
     dp_flows: 4048576
  port_info:
      - ip: 192.168.7.2
        default_gw: 192.168.7.1
        dest_mac: '00:00:00:01:00:00'
      - ip: 192.168.8.2
        default_gw: 192.168.8.1
        dest_mac: '00:00:00:02:00:00'

  platform:
      master_thread_id: 0
      latency_thread_id: 1
      dual_if:
        - socket: 0
          threads: [2,3]

3.簡易負荷試験

細かい説明は後述しますので、とりあえず、トラフィックを印加してみます。

3-1.DNSトラフィック印加

デフォルトで入っている ./cap2/dns.yamlファイルを利用し、30秒間、multiplierレート×1倍にて、DNSトラフィックを印加します。

./t-rex-64 -f ./cap2/dns.yaml -d 30 -m 1

出力例

[root@c77g127 trex]# ./t-rex-64 -f ./cap2/dns.yaml -d 30 -m 1

-Per port stats table
      ports |               0 |               1
 -----------------------------------------------------------------------------------------
   opackets |              29 |              29
     obytes |            2233 |            2697
   ipackets |              28 |              29
     ibytes |            2053 |            1973
    ierrors |               0 |               0
    oerrors |               0 |               0
      Tx Bw |     588.91  bps |     711.29  bps

-Global stats enabled
 Cpu Utilization : 0.1  %
 Platform_factor : 1.0
 Total-Tx        :       1.30 Kbps
 Total-Rx        :     776.93  bps
 Total-PPS       :       1.91  pps
 Total-CPS       :       0.96  cps

 Expected-PPS    :       2.00  pps
 Expected-CPS    :       1.00  cps
 Expected-BPS    :       1.36 Kbps

 Active-flows    :        0  Clients :      511   Socket-util : 0.0000 %
 Open-flows      :       29  Servers :      255   Socket :       13 Socket/Clients :  0.0
 drop-rate       :     523.27  bps
 current time    : 31.3 sec
 test duration   : 0.0 sec

30秒間経過すると以下のように統計情報が出力されます。

~一部省略~
 Cpu Utilization : 0.1  %
 Platform_factor : 1.0
 Total-Tx        :     631.23  bps
 Total-Rx        :     834.98  bps
 Total-PPS       :       0.93  pps
 Total-CPS       :       0.46  cps

 Expected-PPS    :       2.00  pps
 Expected-CPS    :       1.00  cps
 Expected-BPS    :       1.36 Kbps

 Active-flows    :        0  Clients :      511   Socket-util : 0.0000 %
 Open-flows      :       29  Servers :      255   Socket :        0 Socket/Clients :  0.0
 drop-rate       :       0.00  bps
 summary stats
 --------------
 Total-pkt-drop       : 0 pkts
 Warning : number of rx packets exceeds 101% of tx packets!
 Total-tx-bytes       : 4930 bytes
 Total-tx-sw-bytes    : 0 bytes
 Total-rx-bytes       : 4112 byte

 Total-tx-pkt         : 58 pkts
 Total-rx-pkt         : 59 pkts
 Total-sw-tx-pkt      : 0 pkts
 Total-sw-err         : 0 pkts
 Total ARP sent       : 4 pkts
 Total ARP received   : 2 pkts
3-3.HTTPトラフィック印加

デフォルトで入っている ./cap2/http_simple.yaml ファイルを利用し、30秒間、multiplierレート×1倍にて、HTTPトラフィックを印加します。

./t-rex-64 -f ./cap2/http_simple.yaml -d 30 -m 1

出力例はDNSと同様です。

トラフィックフローとしては以下の通りです。
f:id:metonymical:20200724152945j:plain

3-4.yaml設定ファイルの中身

上記の試験で使用した dns.yamlとhttp_simple.yamlの中身を確認してみます。
設定変更の機会が多い箇所を赤文字にしました。

dns.yaml

vi ./cap2/dns.yaml

[root@c77g127 trex]# vi ./cap2/dns.yaml

- duration : 10.0
  generator :
          distribution : "seq"
          clients_start : "16.0.0.1"
          clients_end   : "16.0.1.255"
          servers_start : "48.0.0.1"
          servers_end   : "48.0.0.255"
          clients_per_gb : 201
          min_clients    : 101
          dual_port_mask : "1.0.0.0"
          tcp_aging      : 1
          udp_aging      : 1
  cap_info :
     - name: cap2/dns.pcap
       cps : 1.0
       ipg : 10000
       rtt : 10000
       w   : 1

http_simple.yaml

vi ./cap2/http_simple.yaml

[root@c77g127 trex]# vi ./cap2/http_simple.yaml

- duration : 0.1
  generator :
          distribution : "seq"
          clients_start : "16.0.0.1"
          clients_end   : "16.0.0.255"
          servers_start : "48.0.0.1"
          servers_end   : "48.0.255.255"
          clients_per_gb : 201
          min_clients    : 101
          dual_port_mask : "1.0.0.0"
          tcp_aging      : 0
          udp_aging      : 0
  cap_ipg    : true
  cap_info :
     - name: avl/delay_10_http_browsing_0.pcap
       cps : 2.776
       ipg : 10000
       rtt : 10000
       w   : 1
clients_start クライアントIP開始アドレス 補足1
clients_ens クライアントIP終了アドレス 補足1
servers_start サーバIP開始アドレス 補足1
servers_end サーバIP終了アドレス 補足1
name pcapファイルへのパス 補足2
cps connection per seconds 補足3

<補足1>
DUT側のRouting設定に依存しますが、基本的に変える必要はあまりないと思います。
f:id:metonymical:20200724154732j:plain
なお、上図に記載の通り、TRexとDUT間のNWアドレス(192.168.7.0/24, 192.168.8.0/24)と同一のNWアドレスにする必要はありません。
むしろ、同一にすべきではないです。
なぜなら、同一にした場合、DUT側にRouting設定を入れなくてもよい、というメリットがある一方、DUTがarpによりアドレス解決をしなければならなくなるため、DUTに無駄な負荷が掛かります。
/24程度であればよいかもしれませんが、/16のアドレス数(65000程度)をarpによりアドレス解決させることは、DUT本来の性能に影響を及ぼす可能性も考えられるからです。

<補足2>
今回の例では、作業ディレクトリは、ln -sにて、/root/trex配下としているため、このパスを起点にして、ファイルパスを記載しています。実際のパスは、/opt/trex/v2.82配下となります。
このため、パスを記載すると以下の通りとなります。

/root/trex/cap2/dns.pcap
/root/trex/avl/delay_10_http_browsing_0.pcap

加えて、よく使うディレクトリを以下に記載します。

/root/trex/astf astf(アドバンスドステートフル)モード用Pythonファイル
/root/trex/avl サンプルPcapファイル
/root/trex/cap2 stf(ステートフル)モード用yamlファイル
/root/trex/cfg Trexのサンプルcfgファイル
/root/trex/stl stl(ステートレス)モード用Pythonファイル

いずれもサンプルファイルがとても充実しているため、時間があるときに一通り確認することを強くお勧めします。

<補足3>
Float型で定義されています。
このCPS値と「3-1.DNSトラフィック印加」に記載した「-m」の値で負荷を調節します。

./t-rex-64 -f ./cap2/http_simple.yaml -d 30 -m 1

例えば、「cps : 10」と「-m 100」であれば、乗算してCPS1000となります。

3-5.yaml設定ファイルのオプション
[root@c77g127 trex]# vi ./cap2/dns.yaml

- duration : 10.0
  generator :
          distribution : "seq"
          clients_start : "16.0.0.1"
          clients_end   : "16.0.1.255"
          servers_start : "48.0.0.1"
          servers_end   : "48.0.0.255"
          clients_per_gb : 201
          min_clients    : 101
          dual_port_mask : "1.0.0.0"
          tcp_aging      : 1
          udp_aging      : 1
  cap_info :
     - name: cap2/dns.pcap
       cps : 1.0
       ipg : 10000
       rtt : 10000
       w   : 1
       server_addr : "48.0.0.7"
       one_app_server : true
       keep_src_port: true
server_addr 固定した際に使用するIPアドレスの設定
one_app_server サーバアドレスを1つに固定する設定
keep_src_port Pcapファイルから読み込んだL4SrcPortに固定する設定 補足4

<補足4>
Defaultはfalseで、Pcapファイルから読み込んだL4SrcPortを無視して、L4SrcPortがランダムに変化します。LBやFWなどコネクション(フロー)を管理しているDUTの場合、CPSが増加するとコネクション数(Concurrent Connection)も増加します。このため、コネクションを増やさずに負荷を増加させたい場合、Pcapファイルから読み込んだL4SrcPortに固定することが可能です。もちろん、SrcIPを固定しても同様の効果が得られるケースであれば問題ありません。しかし、IPアドレス1つあたり、L4SrcPortが最大65535となる=65535Concurrent Connectionとなる可能性があるため、例えば、1000Concurrent Connectionをキープしながら、CPSを増加させたい、といった場合には重宝します。

4.ASTFモード

4-1.各種モードについて
STL(ステートレス)モード Pythonファイル L2L3SWなどに対して一方的にパケットを送り付ける場合に利用します。ストリーミングなどパケットベースのトラフィックを印加。詳細は割愛します。
STF(ステートフル)モード yamlファイル 「3.簡易負荷試験」にて実施したモードです。フローベースのトラフィックを印加。DUTにおいて、L7までのインスペクトは可能ですが、TCPや上位レイヤの各種フラグやオプションの書き換えは不可。補足5
ASTF(アドバンスドステートフル)モード Pythonファイル 本章で解説します。STFに加えて、DUTがTCPSSLの終端を行う場合においても、フローベースのトラフィックを印加可能です。

<補足5>
「3.簡易負荷試験」にてDNSの試験を実施しましたが、「DNSUDPだからステートレスではないの?」という疑問が湧くと思いますが、「コネクション型(TCP)/コネクションレス型(UDP, IP)」と「ステートレス/ステートフル」は異なる概念だと考えた方が良いのかなと思います。*2

4-2.ASTFモードによるTRexサーバ起動

ターミナル画面を2つ準備してください。

1つ目のターミナル(Term1とします)で、以下のコマンドにより、ASTFモードで起動します。

Term1にて実行
./t-rex-64 -i --astf

CPUコア数を指定する場合
./t-rex-64 -i --astf -c 14

ベアメタル環境などでCPUコア数が潤沢にある場合、「-c」オプションによりコア数を指定することができます。

<出力例>

[root@c77g127 trex]# ./t-rex-64 -i --astf
Trying to bind to vfio-pci ...
Trying to compile and bind to igb_uio ...
ERROR: We don't have precompiled igb_uio.ko module for your kernel version
Will try compiling automatically...
Success.

/usr/bin/python dpdk_nic_bind.py --bind=igb_uio 0000:02:01.0 0000:02:03.0
The ports are bound/configured.
Starting  TRex v2.82 please wait  ...
 set driver name net_e1000_em
 driver capability  : TCP_UDP_OFFLOAD
 set dpdk queues mode to ONE_QUE
 Number of ports found: 2
zmq publisher at: tcp://*:4500
EAL: Error enabling interrupts for fd 20 (Input/output error)
EAL: Error enabling interrupts for fd 22 (Input/output error)
 wait 1 sec .
port : 0
------------
link         :  link : Link Up - speed 1000 Mbps - full-duplex
promiscuous  : 0
port : 1
------------
link         :  link : Link Up - speed 1000 Mbps - full-duplex
promiscuous  : 0
 number of ports         : 2
 max cores for 2 ports   : 1
 tx queues per port      : 3
 -------------------------------
RX core uses TX queue number 65535 on all ports
 core, c-port, c-queue, s-port, s-queue, lat-queue
 ------------------------------------------
 1        0      0       1       0      0
 -------------------------------

~~~しばらくすると以下の出力になる~~~

-Per port stats table
      ports |               0 |               1
-----------------------------------------------------------------------------------------
   opackets |               0 |               0
     obytes |               0 |               0
   ipackets |               0 |               0
     ibytes |               0 |               0
    ierrors |               0 |               0
    oerrors |               0 |               0
      Tx Bw |       0.00  bps |       0.00  bps

-Global stats enabled
 Cpu Utilization : 0.0  %
 Platform_factor : 1.0
 Total-Tx        :       0.00  bps
 Total-Rx        :       0.00  bps
 Total-PPS       :       0.00  pps
 Total-CPS       :       0.00  cps

 Expected-PPS    :       0.00  pps
 Expected-CPS    :       0.00  cps
 Expected-L7-BPS :       0.00  bps

 Active-flows    :        0  Clients :        0   Socket-util : 0.0000 %
 Open-flows      :        0  Servers :        0   Socket :        0 Socket/Clients :  -nan
 drop-rate       :       0.00  bps
 current time    : 1.8 sec
 test duration   : 0.0 sec
4-3.TRexコンソールへのログイン

もう1つのターミナル画面(Term2とします)から以下のコマンドにより、TRexサーバへコンソールログインします。以下のコマンドは、上述した通り、/root/trex配下で実行してください。

Term2にて実行
./trex-console -s 192.168.11.127

<出力例>

[root@c77g127 trex]# ./trex-console -s 192.168.11.127

Using 'python' as Python interpeter

Connecting to RPC server on 192.168.11.127:4501              [SUCCESS]

Connecting to publisher server on 192.168.11.127:4500        [SUCCESS]

Acquiring ports [0, 1]:                                      [SUCCESS]

Server Info:
Server version:   v2.82 @ ASTF
Server mode:      Advanced Stateful
Server CPU:       1 x Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz
Ports count:      2 x 1Gbps @ 82545EM Gigabit Ethernet Controller (Copper)

-=TRex Console v3.0=-

Type 'help' or '?' for supported actions

trex>

4-4.トラフィック印加

デフォルトで入っている .astf/http_simple.py ファイルを利用し、30秒間、multiplierレート×1倍にて、HTTPトラフィックを印加します。

Term2にて実行
start -f astf/http_simple.py -d 30 -m 1

<出力例>

Term2にて出力
trex>start -f astf/http_simple.py -d 30 -m 1

Loading traffic at acquired ports.                           [SUCCESS]

Starting traffic.                                            [SUCCESS]

59.67 [ms]

trex>

プロンプトが「trex>」に戻ると、印加開始です。
すると、Term1画面にて統計情報が動き始めます。
<出力例>

Term1画面にて出力

-Per port stats table
      ports |               0 |               1
 -----------------------------------------------------------------------------------------
   opackets |              91 |             451
     obytes |           10990 |          609400
   ipackets |             451 |              91
     ibytes |          609400 |           10990
    ierrors |               0 |               0
    oerrors |               0 |               0
      Tx Bw |      13.22 Kbps |     738.66 Kbps

-Global stats enabled
 Cpu Utilization : 0.2  %  0.7 Gb/core
 Platform_factor : 1.0
 Total-Tx        :     751.88 Kbps
 Total-Rx        :     751.62 Kbps
 Total-PPS       :      82.26  pps
 Total-CPS       :       2.73  cps

 Expected-PPS    :       0.00  pps
 Expected-CPS    :       0.00  cps
 Expected-L7-BPS :       0.00  bps

 Active-flows    :        1  Clients :        0   Socket-util : 0.0000 %
 Open-flows      :       18  Servers :        0   Socket :        0 Socket/Clients :  -nan
 drop-rate       :       0.00  bps
 current time    : 19.9 sec
 test duration   : 0.0 sec
4-5.pyファイルの中身

デフォルトで入っている astf/http_simple.py ファイルを確認してみます。
赤文字箇所が「3-4.yaml設定ファイルの中身」の赤文字箇所にそれぞれ対応しています。

[root@c77g127 trex]# vi astf/http_simple.py

from trex.astf.api import *


class Prof1():
    def __init__(self):
        pass

    def get_profile(self, **kwargs):
        # ip generator
        ip_gen_c = ASTFIPGenDist(ip_range=["16.0.0.0", "16.0.0.255"], distribution="seq")
        ip_gen_s = ASTFIPGenDist(ip_range=["48.0.0.0", "48.0.255.255"], distribution="seq")
        ip_gen = ASTFIPGen(glob=ASTFIPGenGlobal(ip_offset="1.0.0.0"),
                           dist_client=ip_gen_c,
                           dist_server=ip_gen_s)

        return ASTFProfile(default_ip_gen=ip_gen,
                            cap_list=[ASTFCapInfo(file="../avl/delay_10_http_browsing_0.pcap",
                            cps=1)])


def register():
    return Prof1()

またASTFモードでは「3-5.yaml設定ファイルのオプション」で紹介したオプションが無いようです。ASTFモードのPython APIも確認してみたのですが、該当する項目が見つからずでした。
特に「keep_src_port: true」のオプションについて、ある特定のケースにおいては必須となる機能だったりするので、もう少し調査してみようと思います。

5.その他便利機能

Trexコンソールの操作方法やPing, Pcap方法について解説していきます。

5-1.Trexコンソールの操作その1

「4-4.トラフィック印加」からの続きとして、Term1画面に統計情報の詳細を出力させます。

<出力例>

Term1画面にて「t」キーを押下。元の画面に戻すときは「0(ゼロ)」キーを押下。
                       |          client  |           server  |
 -----------------------------------------------------------------------------------------
       m_active_flows  |               2  |                2  |  active open flows
          m_est_flows  |               2  |                2  |  active established flows
         m_tx_bw_l7_r  |       5.64 Kbps  |      750.63 Kbps  |  tx L7 bw acked
   m_tx_bw_l7_total_r  |       5.64 Kbps  |      738.64 Kbps  |  tx L7 bw total
         m_rx_bw_l7_r  |     738.64 Kbps  |        5.64 Kbps  |  rx L7 bw acked
           m_tx_pps_r  |      14.43  pps  |       71.96  pps  |  tx pps
           m_rx_pps_r  |      74.88  pps  |       17.35  pps  |  rx pps
           m_avg_size  |         1.04 KB  |          1.06 KB  |  average pkt size
           m_tx_ratio  |       100.00  %  |        101.62  %  |  Tx acked/sent ratio
                    -  |             ---  |              ---  |
   m_traffic_duration  |     302.38  sec  |      302.38  sec  |  measured traffic duration
                    -  |             ---  |              ---  |
                  TCP  |             ---  |              ---  |
                    -  |             ---  |              ---  |
     tcps_connattempt  |             840  |                0  |  connections initiated
         tcps_accepts  |               0  |              840  |  connections accepted
        tcps_connects  |             840  |              840  |  connections established
          tcps_closed  |             838  |              838  |  conn. closed (includes drops)
       tcps_segstimed  |            2520  |             3360  |  segs where we tried to get rtt
      tcps_rttupdated  |            2520  |             3360  |  times we succeeded
          tcps_delack  |             840  |                0  |  delayed acks sent
        tcps_sndtotal  |            4200  |            21000  |  total packets sent
         tcps_sndpack  |             840  |            19320  |  data packets sent
         tcps_sndbyte  |          209160  |         26958960  |  data bytes sent by application
      tcps_sndbyte_ok  |          209160  |         26958960  |  data bytes sent by tcp
         tcps_sndctrl  |             840  |                0  |  control (SYN|FIN|RST) packets sent
         tcps_sndacks  |            2520  |             1680  |  ack-only packets sent
         tcps_rcvpack  |           20160  |             1680  |  packets received in sequence
         tcps_rcvbyte  |        26958960  |           209160  |  bytes received in sequence
      tcps_rcvackpack  |            1680  |             3360  |  rcvd ack packets
      tcps_rcvackbyte  |          209160  |         26958960  |  tx bytes acked by rcvd acks
   tcps_rcvackbyte_of  |             840  |             1680  |  tx bytes acked by rcvd acks - overflow acked
         tcps_preddat  |           18480  |                0  |  times hdr predict ok for data pkts
                    -  |             ---  |              ---  |
                  UDP  |             ---  |              ---  |
                    -  |             ---  |              ---  |
                    -  |             ---  |              ---  |
           Flow Table  |             ---  |              ---  |
                    -  |             ---  |              ---  |
       redirect_rx_ok  |              12  |               10  |  redirect to rx OK
5-2.Trexコンソールの操作その2

「4-4.トラフィック印加」からの続きとして、Term2画面に統計情報の詳細を出力させます。

Term2にて実行
tui

出力例

Term2にて出力
trex>tui

Global Statistitcs

connection   : 192.168.11.127, Port 4501             total_tx_L2  : 745.45 Kb/sec ▼▼
version      : ASTF @ v2.82                          total_tx_L1  : 758.43 Kb/sec ▼▼
cpu_util.    : 0.41% @ 1 cores (1 per dual port)     total_rx     : 745.45 Kb/sec ▼▼
rx_cpu_util. : 0.0% / 0.25 pkt/sec                   total_pps    : 81.12 pkt/sec ▼▼
async_util.  : 0.04% / 1.35 KB/sec                   drop_rate    : 0 b/sec
total_cps.   : 2.95 cps/sec                          queue_full   : 0 pkts

Port Statistics

   port    |         0         |         1         |       total
-----------+-------------------+-------------------+------------------
owner      |              root |              root |
link       |                UP |                UP |
state      |      TRANSMITTING |      TRANSMITTING |
speed      |            1 Gb/s |            1 Gb/s |
CPU util.  |             0.41% |             0.41% |
--         |                   |                   |
Tx bps L2  |      ▼ 13.5 Kbps |  ▼▼ 731.95 Kbps |    ▼▼ 745.45 Kbps
Tx bps L1  |   ▼▼ 15.62 Kbps |   ▼▼ 742.8 Kbps |    ▼▼ 758.43 Kbps
Tx pps     |    ▼▼ 13.27 pps |    ▼▼ 67.85 pps |      ▼▼ 81.12 pps
Line Util. |               0 % |            0.07 % |
---        |                   |                   |
Rx bps     |   ▼▼ 731.7 Kbps |     ▼ 13.75 Kbps |    ▼▼ 745.45 Kbps
Rx pps     |    ▼▼ 67.35 pps |    ▼▼ 13.77 pps |      ▼▼ 81.12 pps
----       |                   |                   |
opackets   |               213 |              1063 |              1276
ipackets   |              1063 |               213 |              1276
obytes     |             25955 |           1438624 |           1464579
ibytes     |           1438624 |             25955 |           1464579
tx-pkts    |          213 pkts |        1.06 Kpkts |        1.28 Kpkts
rx-pkts    |        1.06 Kpkts |          213 pkts |        1.28 Kpkts
tx-bytes   |          25.95 KB |           1.44 MB |           1.46 MB
rx-bytes   |           1.44 MB |          25.95 KB |           1.46 MB
-----      |                   |                   |
oerrors    |                 0 |                 0 |                 0
ierrors    |                 0 |                 0 |                 0

status:  -

Press 'ESC' for navigation panel...
status:

tui>

上記の出力となったら「ESC」キーを押下すると、画面下にナビゲーションパネルが表示。

Term2にて出力

browse:     'ESC' - console, 'q' - quit, 'd' - dashboard, 'u' - util, 't' - astf, 'l' - latency,
dashboard:  'n' - reset view, 'o' - owned ports, 'a' - all ports, 'c' - clear,

例えば、「t」キーを押下すると、以下の画面が出力されます。

Term2にて出力

Global Statistitcs

connection   : 192.168.11.127, Port 4501             total_tx_L2  : 774.93 Kb/sec ▼
version      : ASTF @ v2.82                          total_tx_L1  : 788.42 Kb/sec ▼
cpu_util.    : 0.36% @ 1 cores (1 per dual port)     total_rx     : 774.93 Kb/sec ▼
rx_cpu_util. : 0.0% / 0 pkt/sec                      total_pps    : 84.35 pkt/sec ▼
async_util.  : 0.04% / 1.36 KB/sec                   drop_rate    : 0 b/sec
total_cps.   : 2.8 cps/sec                           queue_full   : 0 pkts

Cpu Util(%)

  Thread   | Avg | Latest | -1  | -2  | -3  | -4  | -5  | -6  | -7  | -8  | -9  | -10 | -11 | -12 | -13 | -14
-----------+-----+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
 0  (0,1)  |   0 |      0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0

Mbuf Util

           |   64b   |  128b   |  256b   |  512b   |  1024b  |  2048b  |  4096b  |   9kb   | RAM(MB)
-----------+---------+---------+---------+---------+---------+---------+---------+---------+--------
Total:     |   81900 |   40950 |   16380 |   16380 |   16380 |   37879 |    1024 |    1024 |     130
Used:      |         |         |         |         |         |         |         |         |
Socket 0:  |     620 |    1258 |       1 |       0 |       0 |    1040 |       0 |       0 |       2
Percent:   |      0% |      3% |      0% |      0% |      0% |      2% |      0% |      0% |

status:  -

browse:     'ESC' - console, 'q' - quit, 'd' - dashboard, 'u' - util, 't' - astf, 'l' - latency,
ustats:
5-3.TrexコンソールからPing

以下コマンド実行後、NICの制御がKernelからDPDKに委譲されるため、bash上ではNICが見えなくなります。このため、Pingを打つ場合、TRexコンソール上から実行します。

./t-rex-64 -i --astf

「-p」でNICのポート番号*3を指定し、「-d」で宛先IPアドレスを指定します。

trex>ping -p 0 -d 192.168.7.1

出力例

Term2にて実行
trex>ping -p 0 -d 192.168.7.1

Pinging 192.168.7.1 from port 0 with 64 bytes of data:
Reply from 192.168.7.1: bytes=64, time=1.90ms, TTL=64
Reply from 192.168.7.1: bytes=64, time=0.83ms, TTL=64
Reply from 192.168.7.1: bytes=64, time=1.12ms, TTL=64
Reply from 192.168.7.1: bytes=64, time=1.97ms, TTL=64
Reply from 192.168.7.1: bytes=64, time=1.46ms, TTL=64

補足6
上記PingコマンドにてDUTとの疎通確認を行った後、負荷を印加した方が良いと考える方もいらっしゃると思います。しかし、何度かPingしてもダメなときは、とりあえず負荷を印加してみることをお勧めします。どのような条件のとき「Pingが飛ばなくても負荷をかけることができるのか」までは確認しきれていませんが、Pingが飛ばなくても負荷を印加できたケースがあったためです。*4

5-4.Trexコンソールからパケットキャプチャ

DPDKが動作しているため、当然ですがKernel上で動作するtcpdumpもできませんので、TRexコンソール上でパケットキャプチャを実施します。

trex>capture record start --tx 0 --rx 0 -l 10000
trex>capture record stop -i 1 -o /root/test.pcap

「--tx」と「--rx」でNICのポート番号を指定し、「-l」でPcapファイル数のリミットを指定。
-i」はCapturing IDとなり、Pcap開始+停止毎にカウントアップする。「-o」で出力先パスとファイル名を指定。

出力例

Term2にて実行
Pcap開始
trex>capture record start --tx 0 --rx 0 -l 10000

Starting packet capturing up to 10000 packets                 [SUCCESS]

*** Capturing ID is set to '1' ***
*** Please call 'capture record stop --id 1 -o ' when done ***

Pcap停止&Pcapファイル出力
trex>capture record stop -i 1 -o /root/test.pcap

Stopping packet capture 1                                    [SUCCESS]

Writing up to 720 packets to '/root/test.pcap'               [SUCCESS]

Removing PCAP capture 1 from server                          [SUCCESS]
5-5.徐々に負荷を上げる方法

トラフィック印加後、「-m」オプションを任意に変更することで、徐々に負荷を上げることが可能です。

Term2にて実行
トラフィック印加開始
trex>start -f astf/http_simple.py -d 30 -m 1

途中から「-m」値を1→5に変更
trex>update -m 5

出力例

Term2にて出力
trex>update -m 5

Updating traffic.                                            [SUCCESS]
5-6.各種コンソールコマンド

出力例

Term2にて出力
trex>help

Console Commands:

capture -                      Manage PCAP captures
debug -                        Internal debugger for development.
events -                       Shows events log
help -                         Shows This Help Screen
history -                      Manage the command history
plugins -                      Show / load / use plugins
quit -                         Exit the console
tui -                          Shows a graphical console
verbose -                      Shows or set verbose mode

Common Commands:

acquire -                      Acquire ports
arp -                          Performs a port ARP resolution
clear -                        Clear cached local statistics
connect -                      Connects to the TRex server and acquire ports
disconnect -                   Disconnect from the TRex server
ipv6 -                         Configures IPv6 of a port
l2 -                           Configures a port in L2 mode
l3 -                           Configures a port in L3 mode
map -                          Maps ports topology
ns -                           Network namespace
ping -                         Pings the server / specific IP
pkt -                          Sends a Scapy format packet
portattr -                     Sets port attributes
reset -                        Reset ports
scan6 -                        Search for IPv6 neighbors
shutdown -                     Shutdown the server
stats -                        Show various statistics
vlan -                         Configures VLAN tagging for a port.

Advanced Stateful Commands:

latency -                      Latency-related commands
profiles -                     Get loaded to profiles information
service -                      Configures port for service mode.
start -                        Start traffic command
stop -                         Stop traffic command
template_group -               Template group commands
topo -                         Topology-related commands
update -                       Update traffic multiplier


以上です。

7.最後に

以下のサイトを参考にさせて頂きました。
https://trex-tgn.cisco.com/trex/doc/index.html
https://www.ciscolive.com/c/dam/r/ciscolive/emea/docs/2018/pdf/DEVNET-1120.pdf

実際に使い始めてみて、その良さを日々実感しています。
IXIAやAvalancheといった専用の測定装置には及ばない点がある一方で、サクッと性能を確認してみたいといったケースや、背景呼として使用したいといったケースには重宝すると思います。
また、公式Docはとても充実しているのですが、重要なポイントが点在して記載されている内容もあるため、必要な箇所だけをピックアップして使えるようにまとめました。
なお、<補足2>にも記載しましたが、よく使うディレクトリ内には多くのサンプルpyファイルが格納されているため、熟読されることを重ねてお勧めします。

次回は応用編とまではいかないですが、もう少し突っ込んだ利用方法について記載できればいいなと考えています。

*1:ip add showやnmcliコマンドが効かなくなります。

*2:かなり脱線してしまいますので詳細は割愛しますが、例えば、SIP, RADIUS, DIAMETER, GTP-Cの場合、DUTによっては、UDPであっても実質的にステート管理をしている=ステートフルである、と言えるケースが存在します。

*3:今回の構成では、Port0=ens33, port1=ens35

*4:根拠を示せずなので申し訳ないのですが、これに気付くのに1時間超も時間を溶かしたので、とりあえずダメ元でやってみる、というのは大事かもしれません。

外資企業への転職時に事前準備した方がよいこと

日系企業から初めて外資企業に転職したいと考えている方に向けて、事前に準備しておいた方が良いな、と感じたことを書きたいと思います。*1

特に日系企業では、あまり実施されていない信用調査については、深掘りしたいと思います。
逆にレジュメの書き方や英語面接対策には、あまり触れませんので、あしからず。

外資企業へ転職する際の大まかな流れとしては、以下のようになると思います。*2
それぞれのフェーズで準備しておいた方が良いものを記載していきます。

  1. 書類選考
  2. 面接
  3. オファー
  4. 信用調査

1.書類選考

エントリーする上で、主に以下の書類が必要になります。

  • レジュメ(Resume)
  • CV(Curriculum Vitae)
  • カバーレター(Cover Letter)
  • 職務経歴書
  • 履歴書
レジュメ or CV エントリーしたい企業に合わせて準備しましょう。レジュメを求められているのにCVを出したりしないように。
カバーレター 任意ですが準備するに越したことはありません
履歴書&職務経歴書 日本法人のある外資企業や直属の上司が日本人の可能性がある場合は、レジュメと合わせ参考資料として提出してもよいと思います。

レジュメやCVの違いや書き方については、ググればたくさん出てきます。
例えば、以下のようなサイトは参考になるかと思います。
Curriculum Vitae(CV)とは?英文レジュメとCV(職務経歴書)の違いについて | Worth Working

2.面接

面接をする上で必要なことは、日系企業と大差はありませんが、

  • 論理的思考力

は、特に重視されると思います。

当然、企業によって面接内容は異なりますが、特にIT関連企業に関して言うと、技術面接はとても大切です。
なぜなら、職歴に記載された技術的経験やスキルについて、どの程度理解しているのか?を見られるからです。

では、どのようにして理解度を見られるかというと、面接官から投げかけられる業務に関する質問に対して、自分の言葉で説明できるかを見られます。

本当に理解しているということは、他人に対しても理解してもらえる説明ができること、だからです。

このため、これまで経験してきた業務に関する知識やスキルなどは、今一度棚卸しの意味も含めて、説明できるレベルまでに昇華させておくべきです。

その他、一般的な英語面接のHow toなどは、ググればたくさん出てきますので、ここでは割愛します。
例えば、以下のようなサイトは参考になるかと思います。
英語面接:そのまま聞かれる質問50題と回答例文-挨拶、自己紹介、志望動機、外資系企業の転職面接対策など

3.オファー

雇用契約書や労働条件通知書などに相当するものがオファーレターです。
雇用条件や職務範囲、注意事項や禁止事項など、詳細に記載された書類が発行されることにより内定となります。

しかし、オファーレターが発行される前に、前職給与を考慮するなどの目的で、以下の提示を求められる場合があります。

  1. 前年度の源泉徴収票
  2. 直近の給与明細 etc.

また、スキャナ付プリンタはあった方が良いと思います。
というのも、いちいち郵送していたら時間が掛かり過ぎてしまうからです。
ましてや人事(Human Resource)部門のオフィスが海外にある場合は尚更です。

さらに付け加えると、オファーレターには直筆署名を求められる場合があるため、印刷して、署名して、スキャナで取り込んで、メール添付して返送する、といったときにも、スキャナ付プリンタは重宝します。

また、この後に記載する信用調査においても、多くの書類提出を求められるため、スキャナ付プリンタがあれば何かと便利です。*3

4.信用調査

信用調査(Background Check)といった場合、概ね以下のような項目を調査されるようです。

  • 学歴
  • 職歴
  • 犯罪歴
  • リファレンス(照会) etc.*4

企業が直接実施する場合もあれば、企業が委託した信用調査会社にて実施する場合もあります。
また、上記のような項目をすべて調査する場合もあれば、一部の項目のみの場合もあります。

ただし、往々にして多いのはリファレンスです。外資企業の場合、リファレンスは必須と考えておいた方がよいと思いますが、詳細は後述します。

それぞれ必要な書類などは異なりますが、仕事をしながら集めるとなると、予想以上に時間を要するものもあるため、事前に準備できるものはしておきましょう。

4-1.学歴

以下のいずれかを準備しておきましょう。

  1. 卒業証書(Diploma)
  2. 卒業証明書(Degree Certificate)
  3. 成績証明書(Transcript)

1.は、卒業式でもらえますが、日本の大学であれば日本語だと思います。
このため、可能な限り英語の2. or 3. を事前に発行しておきましょう。
また、英語の各種証明書は、発行までに1~2週間程度掛かることもあるため、早めに取得しておくべきです。*5

また、大学により異なると思いますが、私が卒業した大学の証明書を確認した結果、記載内容が以下の通り微妙に異なっていました。

証明書 記載内容
日本語 卒業証書 専攻・学位・卒業年月日
日本語 卒業証明書 専攻・卒業年月日
日本語 成績証明書 専攻・卒業年月・入学年月
英語 卒業証明書 専攻・学位・卒業年月日・入学年月日
英語 成績証明書 専攻・学位・卒業年月日・入学年月日

このため、多少お金が掛かっても英語の証明書の方が良いかもしれません。

4-2.職歴

以下のいずれかを準備しておきましょう。場合により複数になることもあります。

  1. 給与明細
  2. 雇用契約
  3. 退職証明書
  4. リファレンスレター

1.給与明細
自分が就業した全企業の全期間を保管しておくことが望ましいです。しかし、何度か転職をしているのであれば、各会社の最初と最後に発行された給与明細だけでも良いと思います。

また、派遣会社に登録後、派遣先で就業している場合、給与明細に派遣元企業と派遣先企業が記載されていることを確認してください。なぜなら、給与明細の発行者は派遣元企業になりますが、職務経歴書に記載する職務内容は派遣先での業務実績となるためです。

2.雇用契約
現職の職歴を証明するものとして保管しておくことが望ましいです。
というのも、職歴調査では企業や信用調査会社が現職や前職の勤め先に連絡を入れる場合があります。

このため、現職の場合、オファーが出る前に転職のことを上司や同僚に知られたくないor話したくないことがあると思いますので、現職の職歴証明として、雇用契約書は保管しておきましょう。
最悪、雇用契約書を紛失してしまった場合は、最初と直近の給与明細があれば問題ありません。

3.退職証明書
転職をしている人はわかると思いますが、何も言わずに発行してくれる会社もあれば、言わないと発行してくれない会社もあります。大事なのは、いつからいつまで就業していたか?が判別できる記載内容になっていることです。

4.リファレンスレター
レアケースですが、会社が倒産した場合や個人事業主(フリーランス)として仕事を行っていた場合、勤め先に連絡することができないため、提出を求められることがあります。

当時の上司や同僚、取引先の方などに依頼して書いてもらうことになるため、人間関係によっては、上記1.~3.よりも準備の敷居が高くなります。*6

4-3.犯罪歴

  1. パスポート

パスポートは、日本国籍があることの証明やパスポート番号から海外も含む犯罪歴の照会に使用されるようです。*7
取得に2週間くらい掛かるため、早めにパスポート取得申請を行ってください。

また、企業や信用調査会社が本人の承諾を得て犯罪歴を照会するため、犯罪歴照会の承諾書に直筆署名を求められる場合があります。このときにもスキャナ付きプリンタは活躍してくれます。

ちなみに、スピードや駐禁違反などの軽微な交通違反であれば特に問題ありません。

4-4.リファレンス(照会)

現職or前職で最低3名くらい居てくれたら安心です。

  1. 信頼のおける上司
  2. 信頼のおける同僚
  3. 信頼のおける取引先の方

リファレンス(照会)は、Reference CheckやProfessional Referenceなどと呼ばれている場合もあります。
日系企業ではあまり一般的ではないため以下に諸々記載します。

目的
リファレンスは、現職や前職などにおける業務実績や勤務状況に偽りがないかなど、履歴書や職務経歴書、面接では把握しきれないことを確認するために実施されます。

進め方
企業や信用調査会社からRefereeへ電話やメールでコンタクトを取ります。そして、業務実績や勤務状況、人間関係などに関する質問をRefereeに対して行います。

Refereeとは
Referee(レフェリー)とは、現職や前職の上司や同僚、取引先の方など、あなたの身元照会先となる人物のこと。

Refereeの英語力
基本的にRefereeの英語力は求められません。
なので「電話する場合は日本語でお願いします」などと、リクエストするなり相談してもよいと思います。
英語のできないRefereeに英語で質問した結果、まともな回答が得られなければ本末転倒なので。*8

Refereeの選出
一般的に2~3名程度の選出が求められます。
そして、選出したRefereeの連絡先を企業や信用調査会社に伝えることになります。
このため、Refereeから事前に承諾を得ることはもとより、それ相応の人間関係を構築できていなければ、そもそもRefereeを選出できない、ということになり兼ねません。

さらに付け加えると、Refereeを選出できるような人物か?という点は、見られているのだと思います。
但し、社会人経験が浅く転職経験が無い方に限り、自分の出身大学の教授に依頼してもよい、という場合もあるようですので、企業や信用調査会社に相談してみましょう。

リファレンスのタイミング
リファレンスは、最終面接後のオファーレターが出る前に実施される場合と、オファーレターが出た後に実施される場合があるようです。

基本的にRefereeの回答内容によって選考が中止されたり、オファーが取り消されることはないようですが、経歴詐称などが発覚した場合は、この限りではありません。

現職の上司や同僚について
オファーレターが出る前にリファレンスされる場合、現職の上司や同僚には転職のことを話したくないということもあるかと思います。

その場合は、正直にその旨を企業や信用調査会社に伝えて、前職の上司や同僚、現職の取引先の方を選出してもよいと思います。

職歴との関連性
リファレンスの質問内容が、職歴の調査と被りる面も若干ありますが、別物と考えるべきです。

職歴は、あくまでも履歴書や職務経歴書の裏付けとして、時系列に沿って、いつからいつまで、どの企業に、どんな立場(正社員or派遣社員など)で勤務していたか?が確認されます。
一方、リファレンスは、職務経歴上の任意の期間(概ね1年以上)における業務実績などの質問に対して、Refereeから得られた回答内容が確認されます。

リファレンスは、その当時に勤務していた企業の職歴の一部を裏付けることができます。
しかし、それは職歴そのものを証明するものではないことに注意してください。

以上です。

5.最後に

信用調査などの話を初めて聞いた方は、とても厳しいなと感じられる方もいるかと思います。
しかし、その一方で、これから仕事をする就業先の方々は皆、信用調査をクリアしてこられたと考えれば、むしろ安心感が得られるのではないでしょうか。
外資企業にとっての信用調査とは、自社にそぐわない人を雇わないための最低限の施策、だと考えれば、自ずと腑に落ちるのではないかと思います。

また最近、終身雇用制度や年金制度の崩壊が決定付けられてきました。
今後、人材流動性を上げるための施策などが次々と出てくると想定されます。
このような状況を踏まえた上で転職する際には、選択肢の一つとして、日系企業だけでなく外資企業に目を向けることも良いのではないかと思います。

なぜなら、

  • 転職先の企業数が一気に(ワールドワイドに)広がる
  • 外資の勤務経験があれば他の外資にも転職し易くなる

といったことなどが挙げられるためです。

少なくても、日系企業では経験出来なかった大きな世界が広がっている、ということは言えると思います。

*1:私の職業柄ですが、技術職に特化している部分がありますので、あしからず。

*2:信用調査は、面接とオファーの間に実施される場合もあるようです。

*3:時間があるときはコンビニの複合機でスキャンして自分のメアド宛に送信するのもありかもしれません。また最悪、急いでいるときなどは、スマホで写真でもいいのかもしれませんが、正式文書として扱われる書類なので、よく考えてください。

*4:病歴や借金の有無を調査される場合もあるようです。

*5:大学の窓口でしか受け付けてないとか、受付時間が平日日中のみなど、社会人的には様々な制約がある可能性も考慮して、早めに発行してもらいましょう。

*6:前提として、当時の上司や取引先の方の連絡先を知っていることはもとより、こちらの依頼に対して、快く引き受けてもらえる人間関係が何よりも重要となってくるためです。

*7:話が少し逸れますが、外資企業の本社採用の場合(例えば、米国での勤務となる場合)は、パスポートに加えて、当然ビザも必要になります。就労ビザの取得方法は「海外転職」などでググってください。

*8:もちろん、英語ができて信頼のおける上司が理想的ですが、日系企業にしか勤務したことがないことを伝えれば、充分考慮してもらえるはずです。

CentOS8 小ネタ集その6:ディスクの拡張方法

CentOS8におけるLVMディスクの拡張方法を記載します。

基本手順はCentOS7と同一なのですが、xfs_growfsをする際、
CentOS7の場合

xfs_growfs /dev/cl/root

CentOS8 の場合

xfs_growfs /

の違いにより、とてもハマったのでその辺のポイントを記載したいと思います。

ちなみに、CentOS8で以下のコマンドを実行すると、
以下のようにエラーが出力されて拡張領域が反映されません。

[root@c80g167 ~]# xfs_growfs /dev/cl/root
xfs_growfs: /dev/cl/root is not a mounted XFS filesystem

以下、fdiskやLVMなど細かい説明は書きませんが、赤文字箇所だけ見てもらえればポイントは掴めると思います。

1.デバイスの拡張

今回はVMWareで以下のように拡張しますが、KVMではqemu-img resizeコマンドにより拡張してください。
f:id:metonymical:20200223001059p:plain
ここでは例として、200GB→250GBに拡張する場合とします。

2.現状確認とfdisk

[root@c80g167 ~]# df -hT
Filesystem          Type      Size  Used Avail Use% Mounted on
devtmpfs            devtmpfs  888M     0  888M   0% /dev
tmpfs               tmpfs     904M     0  904M   0% /dev/shm
tmpfs               tmpfs     904M  9.4M  894M   2% /run
tmpfs               tmpfs     904M     0  904M   0% /sys/fs/cgroup
/dev/mapper/cl-root xfs       197G  6.6G  191G   4% /
/dev/sda1           ext4      976M  134M  776M  15% /boot
tmpfs               tmpfs     181M   20K  181M   1% /run/user/0


[root@c80g167 ~]# fdisk -l
Disk /dev/sda: 250 GiB, 268435456000 bytes, 524288000 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x47600f97

Device     Boot   Start       End   Sectors  Size Id Type
/dev/sda1  *       2048   2099199   2097152    1G 83 Linux
/dev/sda2       2099200 419430399 417331200  199G 8e Linux LVM

Disk /dev/mapper/cl-root: 197 GiB, 211472613376 bytes, 413032448 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

Disk /dev/mapper/cl-swap: 2 GiB, 2197815296 bytes, 4292608 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


[root@c80g167 ~]# fdisk /dev/sda

Welcome to fdisk (util-linux 2.32.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Command (m for help): p
Disk /dev/sda: 250 GiB, 268435456000 bytes, 524288000 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x47600f97

Device     Boot   Start       End   Sectors  Size Id Type
/dev/sda1  *       2048   2099199   2097152    1G 83 Linux
/dev/sda2       2099200 419430399 417331200  199G 8e Linux LVM

Command (m for help): n
Partition type
   p   primary (2 primary, 0 extended, 2 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (3,4, default 3): 空Enter
First sector (419430400-524287999, default 419430400): 空Enter
Last sector, +sectors or +size{K,M,G,T,P} (419430400-524287999, default 524287999): 空Enter

Created a new partition 3 of type 'Linux' and of size 50 GiB.

Command (m for help): t
Partition number (1-3, default 3): 3
Hex code (type L to list all codes): 8e

Changed type of partition 'Linux' to 'Linux LVM'.

Command (m for help): p
Disk /dev/sda: 250 GiB, 268435456000 bytes, 524288000 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x47600f97

Device     Boot     Start       End   Sectors  Size Id Type
/dev/sda1  *         2048   2099199   2097152    1G 83 Linux
/dev/sda2         2099200 419430399 417331200  199G 8e Linux LVM
/dev/sda3       419430400 524287999 104857600   50G 8e Linux LVM

Command (m for help): w
The partition table has been altered.
Syncing disks.

3.LVM設定

[root@c80g167 ~]# vgextend cl /dev/sda3
  Volume group "cl" successfully extended
[root@c80g167 ~]# lvextend /dev/cl/root /dev/sda3
  Size of logical volume cl/root changed from <196.95 GiB (50419 extents) to <246.95 GiB (63218 extents).
  Logical volume cl/root successfully resized.

4.xfs_growfsとdf確認

[root@c80g167 ~]# xfs_growfs /
meta-data=/dev/mapper/cl-root    isize=512    agcount=4, agsize=12907264 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1
data     =                       bsize=4096   blocks=51629056, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=25209, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
data blocks changed from 51629056 to 64735232

[root@c80g167 ~]# df -hT
Filesystem          Type      Size  Used Avail Use% Mounted on
devtmpfs            devtmpfs  888M     0  888M   0% /dev
tmpfs               tmpfs     904M     0  904M   0% /dev/shm
tmpfs               tmpfs     904M  9.4M  894M   2% /run
tmpfs               tmpfs     904M     0  904M   0% /sys/fs/cgroup
/dev/mapper/cl-root xfs       247G  7.0G  240G   3% /
/dev/sda1           ext4      976M  134M  776M  15% /boot
tmpfs               tmpfs     181M   16K  181M   1% /run/user/0

以上です。

5.最後に

以下のサイトを参考にさせて頂きました。
Extending Centos 8 root size - CentOS

冒頭にも書きましたが、最初は

xfs_growfs /

に気付かず、
resize2fsを試したりと、時間を浪費してしまったので、今回の記事を書きました。

OAI L2 nFAPI + Free5GCによるNSA 5GC構築方法

OAI(Open Air Interface)のL2 nFAPIシミュレータとFree5GCによる5GCoreの構築方法を記載します。
UEはOAIのLTE-UESoftModem、eNBはOAIのLTE-SoftModem、残り*1はFree5GCにて構築します。
構成としては、NSA(Non Stand Alone)型Option5のような構成となります。
以前に紹介した記事NSA型5GC版と考えて頂ければ、とっつきやすいかなと思います。
なお、以前の記事では、S5/S8区間で分離させましたが、今回は最小構成で構築する方法を記載した後、分離に関しては、別な機会に記載したいと考えています。*2

1.環境

1-1.VMWare
筐体                             : 自作PC(Win10pro)
CPU                           : Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz
VMWare              : VMware(R) Workstation 15 Pro 15.1.0 build-13591040  
OS                               : Ubuntu 18.04.3 LTS
Kernel                           : 4.15.0-72-generic
1-2.全体構成

f:id:metonymical:20200103094645p:plain
f:id:metonymical:20200103094710p:plain

1-3 .全体の流れ ~概要~
  1. 事前準備
  2. Free5GC設定
  3. OAI L2nFAPI設定
  4. 起動と動作確認

2.事前準備

ntp時刻同期、ホスト名設定などは省略します。

2-1.u18c131:L2nFAPIの事前設定

ens33は、マネジメントNW(aptやgitで使う)なのでインターネットに出られればOKです。

vi /etc/systemd/network/01-ens33.network

[Match]
Name=ens33

[Network]
Address=192.168.11.131/24
Gateway=192.168.11.1
DNS=192.168.11.1


vi /etc/systemd/network/03-ens35.network

[Match]
Name=ens35

[Network]
Address=10.20.0.131/24


vi /etc/hosts

127.0.0.1       localhost.localdomain   localhost u18c131
127.0.0.2       u18c131
2-2.u18c132:Free5GCの事前設定

こちらもens33は、同様です。

vi /etc/systemd/network/01-ens33.network

[Match]
Name=ens33

[Network]
Address=192.168.11.132/24
Gateway=192.168.11.1
DNS=192.168.11.1


vi /etc/systemd/network/02-ens34.network

[Match]
Name=ens34

[Network]
Address=10.10.0.136/24


vi /etc/systemd/network/03-ens35.network

[Match]
Name=ens35

[Network]
Address=10.20.0.132/24
Address=10.20.0.133/24
Address=10.20.0.134/24
Address=10.20.0.135/24
Address=10.20.0.136/24

3.Free5GC設定

3-1.MongoDBのインストール
apt-get update && \
apt-get -y install mongodb wget git && \
systemctl start mongodb && \
systemctl enable mongodb

systemctl status mongodb
3-2.Golangと各種パッケージのインストール
wget -q https://storage.googleapis.com/golang/getgo/installer_linux && \
chmod +x installer_linux && \
./installer_linux && \
source ~/.bash_profile

go get -u -v "github.com/gorilla/mux" && \
go get -u -v "golang.org/x/net/http2" && \
go get -u -v "golang.org/x/sys/unix"
3-3.uptunの設定

GTP終端インターフェースを作成します。*3

sh -c "cat << EOF > /etc/systemd/network/99-free5gc.netdev
[NetDev]
Name=uptun
Kind=tun
EOF"

systemctl enable systemd-networkd && \
systemctl restart systemd-networkd


vi /etc/network/interfaces

auto uptun
iface uptun inet static
	address 25.0.0.1
	netmask 255.255.0.0

ifup/downコマンドなどが必要であれば、事前にインストールしてください。*4

apt -y install ifupdown2
3-3.依存関係パッケージのインストール
apt-get -y install autoconf libtool gcc pkg-config git flex bison libsctp-dev \
libgnutls28-dev libgcrypt-dev libssl-dev libidn11-dev libmongoc-dev libbson-dev libyaml-dev
3-4.Free5GCのビルド
cd ~/ && \
git clone https://bitbucket.org/nctu_5g/free5gc-stage-1.git && \
cd free5gc-stage-1 && \
autoreconf -iv && \
./configure --prefix=`pwd`/install && \
make -j `nproc` && \
make install
3-5.IP forwarding設定
sh -c "cat << EOF > /etc/init.d/ngc-network-setup
#!/bin/sh
### BEGIN INIT INFO 
# Provides:          ngc-network-setup 
# Required-Start:    networkd 
# Required-Stop:     networkd 
# Default-Start:     networkd 
# Default-Stop:      networkd 
# Short-Description: 
# Description:       
# 
### END INIT INFO

sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
iptables -I INPUT -i uptun -j ACCEPT
EOF"


chmod 755 /etc/init.d/ngc-network-setup && \
/etc/init.d/ngc-network-setup

ln -s /etc/init.d/ngc-network-setup /etc/rc3.d/S99ngc-network-setup && \
ln -s /etc/init.d/ngc-network-setup /etc/rc4.d/S99ngc-network-setup && \
ln -s /etc/init.d/ngc-network-setup /etc/rc5.d/S99ngc-network-setup

<補足>
公式手順ではMasqueradeの設定も入っていますが、私の経験上、N6(SGi)区間にはNATやDPI装置などが配置されており、UPF(PGW)でPATすることは皆無だったので削除しています。
これに伴い、L3SW上では以下のような戻りのルートを忘れずに設定しておいてください。

ip route 25.0.0.0 255.255.0.0 10.10.0.136
3-6.WebUIのインストール
apt-get -y install curl
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
apt-get -y install nodejs

cd /root/free5gc-stage-1/webui && \
npm install

#サービス起動
npm run dev

#アクセス先URL
http://192.168.11.132:3000/

インストール完了後、以下のページが表示されればOKです。
Username/Passwdは以下の通り。
admin/1423
f:id:metonymical:20200103103443p:plain

3-6.SIM情報の登録

上記のアクセス先URLから以下のSIM情報を登録します。*5

IMSI 208930100001111
Subscriber Key(Ki) 8baf473f2f8fd09487cccbd7097c6862
Operator Key(OPc) e734f8734007d6c5ce7a0508809e7e9c
USIM Type OPc
APN internet
MSISDN 33611123456
IMSI 208930100001112
Subscriber Key(Ki) 8baf473f2f8fd09487cccbd7097c6862
Operator Key(OPc) e734f8734007d6c5ce7a0508809e7e9c
USIM Type OPc
APN internet
MSISDN 33611123457

f:id:metonymical:20200103104030p:plain
IMSI, Subscriber Key(Ki), Operator Key(OPc), USIM Typeが必須項目となります。
APNは任意で構いませんが、この後のfree5gc.conf設定で使用します。
MSISDNは、ここでは設定不要ですが、OAISIMのビルド前に必要となりますので、念のため記載しています。

3-7.nextepc.conf設定

元からコメントされていた箇所は全て削除しています。
ens34, ens35のIP設定が同一環境であれば以下のconfをそのまま貼り付けでOKです。

vi /root/free5gc-stage-1/install/etc/free5gc/free5gc.conf

db_uri: mongodb://localhost/free5gc

logger:
    file: /root/free5gc-stage-1/install/var/log/free5gc/free5gc.log
    trace:
        app: 1
        s1ap: 1
        nas: 1
        diameter: 1
        gtp: 1
        pfcp: 1
        sbi: 1

parameter:
    no_ipv6: true

amf:
    freeDiameter: amf.conf

    s1ap:
      addr: 10.20.0.132

    gummei:
      plmn_id:
        mcc: 208
        mnc: 93
      mme_gid: 1
      mme_code: 1

    tai:
      plmn_id:
        mcc: 208
        mnc: 93
      tac: 1

    security:
        integrity_order : [ EIA1, EIA2, EIA0 ]
        ciphering_order : [ EEA0, EEA1, EEA2 ]

    network_name:
        full: free5GC

hss:
    freeDiameter: hss.conf

pcrf:
    freeDiameter: pcrf.conf

smf:
    freeDiameter: smf.conf

    pfcp:
      - addr: 10.20.0.134
#      - addr: ::1

    upf:
      - addr: 10.20.0.136

    http:
      addr: 10.20.0.134
      port: 8080

    ue_pool:
      - addr: 25.0.0.1/16
        apn: internet
#      - addr: cafe::1/64

    dns:
      - 8.8.8.8
#      - 8.8.4.4
#      - 2001:4860:4860::8888
#      - 2001:4860:4860::8844

upf:
    pfcp:
      addr:
        - 10.20.0.136
#        - ::1

    gtpu:
      - addr: 10.20.0.132
#      - addr: ::1

    ue_pool:
      - addr: 25.0.0.1/16
#      - addr: cafe::1/64

    dns:
      - 8.8.8.8
#      - 8.8.4.4
#      - 2001:4860:4860::8888
#      - 2001:4860:4860::8844
3-8.freeDiameter関連confの設定

各種設定ファイルは以下となります。

vi /root/free5gc-stage-1/install/etc/free5gc/freeDiameter/amf.conf
vi /root/free5gc-stage-1/install/etc/free5gc/freeDiameter/hss.conf
vi /root/free5gc-stage-1/install/etc/free5gc/freeDiameter/smf.conf
vi /root/free5gc-stage-1/install/etc/free5gc/freeDiameter/pcrf.conf

AMF→MME、SMF→SGWと読み換えれば基本的にNextEPCと変わりませんので、変更箇所がさほど気にならなければ以下の通り流し込んでください。

#AMFの設定
sed -i -e /^ListenOn/s/127.0.0.2/10.20.0.132/g \
/root/free5gc-stage-1/install/etc/free5gc/freeDiameter/amf.conf
sed -i -e /^ConnectPeer/s/127.0.0.4/10.20.0.133/g \
/root/free5gc-stage-1/install/etc/free5gc/freeDiameter/amf.conf

#HSSの設定
sed -i -e /^ListenOn/s/127.0.0.4/10.20.0.133/g \
/root/free5gc-stage-1/install/etc/free5gc/freeDiameter/hss.conf
sed -i -e /^ConnectPeer/s/127.0.0.2/10.20.0.132/g \
/root/free5gc-stage-1/install/etc/free5gc/freeDiameter/hss.conf

#SMFの設定
sed -i -e /^ListenOn/s/127.0.0.3/10.20.0.134/g \
/root/free5gc-stage-1/install/etc/free5gc/freeDiameter/smf.conf
sed -i -e /^ConnectPeer/s/127.0.0.5/10.20.0.135/g \
/root/free5gc-stage-1/install/etc/free5gc/freeDiameter/smf.conf

#PCRFの設定
sed -i -e /^ListenOn/s/127.0.0.5/10.20.0.135/g \
/root/free5gc-stage-1/install/etc/free5gc/freeDiameter/pcrf.conf
sed -i -e /^ConnectPeer/s/127.0.0.3/10.20.0.134/g \
/root/free5gc-stage-1/install/etc/free5gc/freeDiameter/pcrf.conf
3-9.Free5GCの起動

「-d」オプションはDaemonとして起動させます。

cd ~/free5gc-stage-1 && \
./free5gc-ngcd -d

出力例
以下のようにDaemonが起動していればOKです。

root@u18c132:~/free5gc-stage-1# ./free5gc-ngcd -d
free5GC daemon v1.0.0 - Jan  3 2020 05:14:03

  PID[1803] : '/root/free5gc-stage-1/install/var/run/free5gc-ngcd/pid'
  File Logging : '/root/free5gc-stage-1/install/var/log/free5gc/free5gc.log'
  MongoDB URI : 'mongodb://localhost/free5gc'
  Configuration : '/root/free5gc-stage-1/install/etc/free5gc/free5gc.conf'
[01/03 10:56:58.093] PCRF try to initialize
root@u18c132:~/free5gc-stage-1# [01/03 10:56:58.140] PCRF initialize...done
[01/03 10:56:58.140] UPF try to initialize
[01/03 10:56:58.159] UPF initialize...done
[01/03 10:56:58.161] SMF try to initialize
[01/03 10:56:58.199] SMF initialize...done
[01/03 10:56:58.200] pfcp_server() [10.20.0.134]:8805
[01/03 10:56:58.200] pfcp_connect() [10.20.0.136]:8805
[01/03 10:56:58.200]  pfcp_xact_local_create 1 not freed in pfcp_xact_pool[64] of PFCP Transaction
[01/03 10:56:58.202] HSS try to initialize
[01/03 10:56:58.214] INFO: CONNECTED TO 'smf.localdomain' (TCP,soc#10): (fd_logger.c:93)
[01/03 10:56:58.215] INFO: CONNECTED TO 'pcrf.localdomain' (TCP,soc#16): (fd_logger.c:93)
[01/03 10:56:58.220] HSS initialize...done
[01/03 10:56:58.220] AMF try to initialize
[01/03 10:56:58.369] AMF initialize...done


[01/03 10:56:58.369] INFO: free5GC daemon start (main.c:157)
[01/03 10:56:58.371] INFO: CONNECTED TO 'amf.localdomain' (TCP,soc#8): (fd_logger.c:93)
[01/03 10:56:58.371] INFO: CONNECTED TO 'hss.localdomain' (TCP,soc#11): (fd_logger.c:93)
[01/03 10:56:58.376] s1ap_server() [10.20.0.132]:36412

終了させるときは、ps -auxなどで確認の上*6、kill -9などで停止させてください。

個別に起動させたい場合は以下の通りです。

cd ~/free5gc-stage-1
./free5gc-upfd -d
./nextepc-pcrfd -d
./free5gc-smfd -d
./free5gc-amfd -d
./nextepc-hssd -d

4.OAI L2nFAPI設定

4-1.git リポジトリの取得

UE用とeNB用でディレクトリを別けます。

git clone https://gitlab.eurecom.fr/oai/openairinterface5g/ enb_folder && \
cd enb_folder && \
git checkout -f v1.0.3 && \
cd .. && \
cp -Rf enb_folder ue_folder

<補足>
v1.1.x以降のブランチに格納されているbuild_oai では、-t ETHERNET オプションに対応していないため、v1.0系の最新版としています。

4-2.SIM情報の設定

変更箇所のみ抜粋。UEを2台アタッチさせられるように1台追加しています。

cd ~/ue_folder
vi openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf

#画面下の方にスクロール

UE0:
{
    USER: {
        IMEI="356113022094149";
        MANUFACTURER="EURECOM";
        MODEL="LTE Android PC";
        PIN="0000";
    };

    SIM: {
        MSIN="0100001111";
        USIM_API_K="8baf473f2f8fd09487cccbd7097c6862";
        OPC="e734f8734007d6c5ce7a0508809e7e9c";
        MSISDN="33611123456";
    };

    # Home PLMN Selector with Access Technology
    HPLMN= "20893";

    # User controlled PLMN Selector with Access Technology
    UCPLMN_LIST = ();

    # Operator PLMN List
    OPLMN_LIST = ("00101", "20810", "20811", "20813", "20893", "310280", "310028");

    # Operator controlled PLMN Selector with Access Technology
    OCPLMN_LIST = ("22210", "21401", "21406", "26202", "26204");

    # Forbidden plmns
    FPLMN_LIST = ();

    # List of Equivalent HPLMNs
#TODO: UE does not connect if set, to be fixed in the UE
#    EHPLMN_LIST= ("20811", "20813");
    EHPLMN_LIST= ();
};

#以下を追加
UE1:
{
    USER: {
        IMEI="356113022094150";
        MANUFACTURER="EURECOM";
        MODEL="LTE Android PC";
        PIN="0000";
    };

    SIM: {
        MSIN="0100001112";
        USIM_API_K="8baf473f2f8fd09487cccbd7097c6862";
        OPC="e734f8734007d6c5ce7a0508809e7e9c";
        MSISDN="33611123457";
    };

    # Home PLMN Selector with Access Technology
    HPLMN= "20893";

    # User controlled PLMN Selector with Access Technology
    UCPLMN_LIST = ();

    # Operator PLMN List
    OPLMN_LIST = ("00101", "20810", "20811", "20813", "20893", "310280", "310028");

    # Operator controlled PLMN Selector with Access Technology
    OCPLMN_LIST = ("22210", "21401", "21406", "26202", "26204");

    # Forbidden plmns
    FPLMN_LIST = ();

    # List of Equivalent HPLMNs
#TODO: UE does not connect if set, to be fixed in the UE
#    EHPLMN_LIST= ("20811", "20813");
    EHPLMN_LIST= ();
};
4-3.eNBの設定
cd ~/enb_folder
vi ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf

#画面下の方までスクロール

    ////////// MME parameters:
    mme_ip_address      = ( { ipv4       = "10.20.0.132";
                              ipv6       = "192:168:30::17";
                              active     = "yes";
                              preference = "ipv4";
                            }
                          );

    enable_measurement_reports = "no";

    ///X2
    enable_x2 = "no";
    t_reloc_prep      = 1000;      /* unit: millisecond */
    tx2_reloc_overall = 2000;      /* unit: millisecond */

    NETWORK_INTERFACES :
    {
        ENB_INTERFACE_NAME_FOR_S1_MME            = "ens35";
        ENB_IPV4_ADDRESS_FOR_S1_MME              = "10.20.0.131/24";
        ENB_INTERFACE_NAME_FOR_S1U               = "ens35";
        ENB_IPV4_ADDRESS_FOR_S1U                 = "10.20.0.131/24";
        ENB_PORT_FOR_S1U                         = 2152; # Spec 2152
        ENB_IPV4_ADDRESS_FOR_X2C                 = "10.20.0.131/24";
        ENB_PORT_FOR_X2C                         = 36422; # Spec 36422

    };
  }
);

MACRLCs = (
        {
        num_cc = 1;
        local_s_if_name  = "lo";
        remote_s_address = "127.0.0.2";
        local_s_address  = "127.0.0.1";
        local_s_portc    = 50001;
        remote_s_portc   = 50000;
        local_s_portd    = 50011;
        remote_s_portd   = 50010;
        tr_s_preference = "nfapi";
        tr_n_preference = "local_RRC";
        }
);
4-4.UEの設定
cd ~/ue_folder
vi ci-scripts/conf_files/ue.nfapi.conf

L1s = (
        {
        num_cc = 1;
        tr_n_preference = "nfapi";
        local_n_if_name  = "lo";
        remote_n_address = "127.0.0.1";
        local_n_address  = "127.0.0.2";
        local_n_portc    = 50000;
        remote_n_portc   = 50001;
        local_n_portd    = 50010;
        remote_n_portd   = 50011;
        }
);

<補足>
Loアドレスを使用している箇所は同一仮想マシンのためデフォルトのconfファイルから変更不要ですが、公式手順の図に合わせて、eNB:127.0.0.1、UE:127.0.0.2としています。*7
なお、eNBとUEを別筐体で稼働させる場合には、例として以下のように設定します。

#eNB:10.20.0.131 ens35
#UE :10.20.0.130 ens35

#eNBの設定
MACRLCs = (
        {
        num_cc = 1;
        local_s_if_name  = "ens35";
        remote_s_address = "10.20.0.130";
        local_s_address  = "10.20.0.131";
        local_s_portc    = 50001;
        remote_s_portc   = 50000;
        local_s_portd    = 50011;
        remote_s_portd   = 50010;
        tr_s_preference = "nfapi";
        tr_n_preference = "local_RRC";
        }
);

#UEの設定
L1s = (
        {
        num_cc = 1;
        tr_n_preference = "nfapi";
        local_n_if_name  = "ens35";
        remote_n_address = "10.20.0.131";
        local_n_address  = "10.20.0.130";
        local_n_portc    = 50000;
        remote_n_portc   = 50001;
        local_n_portd    = 50010;
        remote_n_portd   = 50011;
        }
);
4-5.eNBのビルド

初回は依存関係を解決するため、-I オプションを追加しています。
なお、-h にて全てのオプションが確認できます。

cd ~/enb_folder && \
source oaienv && \
cd cmake_targets && \
./build_oai --eNB -t ETHERNET -c -I

出力例
いくつかWarningが出力されますが、以下のような出力であれば正常にビルドが完了しています。

~~~一部省略~~~
WARNING: Parameterized type X2AP-PROTOCOL-IES expected for X2AP-PROTOCOL-IES at line 4997 in /root/enb_folder/openair2/X2AP/MESSAGES/ASN1/R14/x2ap-14.6.0.asn1
DEADLINE_SCHEDULER flag  is False
CPU_Affinity flag is False
flexran.proto: warning: Import control_delegation.proto but not used.
-- Found Yaml: /usr/lib/x86_64-linux-gnu/libyaml.so
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.29.1")
-- Checking for one of the modules 'libxml-2.0'
-- Checking for one of the modules 'libxslt'
-- Checking for one of the modules 'openssl'
-- Checking for one of the modules 'libconfig'
-- Checking for one of the modules 'libcrypto'
-- Checking for one of the modules 'openpgm-5.1;openpgm-5.2'
-- Checking for one of the modules 'nettle'
NETTLE VERSION_INSTALLED  = 3.4
NETTLE_VERSION_MAJOR = 3
NETTLE_VERSION_MINOR = 4
-- Checking for one of the modules 'xpm'
-- Configuring done
-- Generating done
-- Build files have been written to: /root/enb_folder/cmake_targets/lte_build_oai/build
Compiling lte-softmodem
Log file for compilation has been written to: /root/enb_folder/cmake_targets/log/lte-softmodem.Rel14.txt
lte-softmodem compiled
WARNING: 9 warnings. See /root/enb_folder/cmake_targets/log/lte-softmodem.Rel14.txt
Log file for compilation has been written to: /root/enb_folder/cmake_targets/log/params_libconfig.Rel14.txt
params_libconfig compiled
Log file for compilation has been written to: /root/enb_folder/cmake_targets/log/coding.Rel14.txt
coding compiled
Log file for compilation has been written to: /root/enb_folder/cmake_targets/log/oai_eth_transpro.Rel14.txt
oai_eth_transpro compiled
WARNING: 1 warnings. See /root/enb_folder/cmake_targets/log/oai_eth_transpro.Rel14.txt
liboai_transpro.so is linked with ETHERNET library
10. Bypassing the Tests ...
4-6.UEのビルド

同一筐体でのビルドとなるため、-I オプションは不要です。

cd ~/ue_folder && \
source oaienv && \
cd cmake_targets && \
./build_oai --UE -t ETHERNET -c

#文字化けするため一旦ログアウト
exit

<補足>
ビルドの最後の方で、以下のように文字化けしますが、ビルドは正常に完了している(ハズなので)ので、exitで抜けて、再度ログインし直してください。

出力例
文字化けしますが、4-2で追加したSIM情報(赤文字箇所)が反映されていればOKです。

~~~一部省略~~~
WARNING: Parameterized type X2AP-PROTOCOL-IES expected for X2AP-PROTOCOL-IES at line 4997 in /root/ue_folder/openair2/X2AP/MESSAGES/ASN1/R14/x2ap-14.6.0.asn1
DEADLINE_SCHEDULER flag  is False
CPU_Affinity flag is False
flexran.proto: warning: Import control_delegation.proto but not used.
-- Found Yaml: /usr/lib/x86_64-linux-gnu/libyaml.so
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.29.1")
-- Checking for one of the modules 'libxml-2.0'
-- Checking for one of the modules 'libxslt'
-- Checking for one of the modules 'openssl'
-- Checking for one of the modules 'libconfig'
-- Checking for one of the modules 'libcrypto'
-- Checking for one of the modules 'openpgm-5.1;openpgm-5.2'
-- Checking for one of the modules 'nettle'
NETTLE VERSION_INSTALLED  = 3.4
NETTLE_VERSION_MAJOR = 3
NETTLE_VERSION_MINOR = 4
-- Checking for one of the modules 'xpm'
-- Configuring done
-- Generating done
-- Build files have been written to: /root/ue_folder/cmake_targets/lte_build_oai/build
Compiling lte-uesoftmodem
Log file for compilation has been written to: /root/ue_folder/cmake_targets/log/lte-uesoftmodem.Rel14.txt
lte-uesoftmodem compiled
WARNING: 10 warnings. See /root/ue_folder/cmake_targets/log/lte-uesoftmodem.Rel14.txt
Log file for compilation has been written to: /root/ue_folder/cmake_targets/log/params_libconfig.Rel14.txt
params_libconfig compiled
Log file for compilation has been written to: /root/ue_folder/cmake_targets/log/coding.Rel14.txt
coding compiled
Compiling UE specific part
Log file for compilation has been written to: /root/ue_folder/cmake_targets/log/ue_ip.Rel14.txt
ue_ip compiled
WARNING: 1 warnings. See /root/ue_folder/cmake_targets/log/ue_ip.Rel14.txt
-- The C compiler identification is GNU 7.4.0
-- The CXX compiler identification is GNU 7.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.29.1")
-- Checking for one of the modules 'libconfig'
-- Configuring done
-- Generating done
-- Build files have been written to: /root/ue_folder/cmake_targets/nas_sim_tools/build
Log file for compilation has been written to: /root/ue_folder/cmake_targets/log/usim.Rel14.txt
usim compiled
Log file for compilation has been written to: /root/ue_folder/cmake_targets/log/nvram.Rel14.txt
nvram compiled
Log file for compilation has been written to: /root/ue_folder/cmake_targets/log/conf2uedata.Rel14.txt
conf2uedata compiled
generate .ue_emm.nvram .ue.nvram

UE's non-volatile data:

IMEI            = 356113022094149
manufacturer    = EURECOM
model           = LTE Android PC
PIN             = 0000
UE identity data file: /root/ue_folder/targets/bin/.ue.nvram0

EMM non-volatile data:

IMSI            = 208.930.100001111
RPLMN           = 20893
EPS Mobility Management data file: /root/ue_folder/targets/bin/.ue_emm.nvram0

UE's non-volatile data:

IMEI            = 356113022094150
manufacturer    = EURECOM
model           = LTE Android PC
PIN             = 0000
UE identity data file: /root/ue_folder/targets/bin/.ue.nvram1

EMM non-volatile data:

IMSI            = 208.930.100001112
RPLMN           = 20893
EPS Mobility Management data file: /root/ue_folder/targets/bin/.ue_emm.nvram1
generate .usim.nvram

~~~一部省略~~~

        ヤ鳫褪゚ヤウイエオ゚ツ裴磋鴆           コ ーー
ユモノヘ 蓊 跌・コ ッ・ッ゚跣・褪ッ襁粳鉧ョ鳫ョⅷ敎
テ・鰀 ユナ 裙鱶鱚   ッ・ッ゚跣・褪ッ肬硴裃襁・裃糢鴈葹・魃糢鴈・
ァッ・ッ゚跣・褪ッ襁粳鉧ョ゚褊摠ⅷ擎ァ ュセ ァッ・ッ゚跣・褪ッ肬硴裃襁・裃糢鴈葹・魃糢鴈莟ョ゚褊摠ⅷ擎ァ
ァッ・ッ゚跣・褪ッ襁粳鉧ョョⅷ擎ァ ュセ ァッ・ッ゚跣・褪ッ肬硴裃襁・裃糢鴈葹・魃糢鴈莟ョョⅷ擎ァ
ァッ・ッ゚跣・褪ッ襁粳鉧ョ鳫ョⅷ擎ァ ュセ ァッ・ッ゚跣・褪ッ肬硴裃襁・裃糢鴈葹・魃糢鴈莟ョ鳫ョⅷ擎ァ
フ・ 跌・ 跣・胥瀇鴈磑鴆・鞦・粢褓 鴟・コ ッ・ッ゚跣・褪ッ肬硴裃襁・遽・鱇襁鞜碚ョメ褌アエョ・
・鱇襁鞜碚 胥瀇鴈裝
ラチメホノホヌコ ア 鴃銛ョ モ裹 ッ・ッ゚跣・褪ッ肬硴裃襁・遽・鱇襁鞜碚ョメ褌アエョ・
・糀硅゚碚ョ 鴣 ・鸙裝  ナヤネナメホナヤ ・糘碪・
アーョ ツ碯鰀 ・ヤ褫 ョョョ
・タク羈ウアコッ゚跣・褪ッ肬硴裃襁
・タク羈ウアコッ゚跣・褪ッ肬硴裃襁 褸鴟
・鉐

#文字化けしたままの状態となるため、exit と入力して*8ターミナル画面からログアウトしてください。

再度ログインした後、SIM関連ファイルをコピーします。

#SIM関連ファイルのコピー

cd ~/ue_folder/targets/bin/ && \
cp .u* ../../cmake_targets/ && \
cp usim ../../cmake_targets/ && \
cp nvram ../../cmake_targets/

5.起動と動作確認

起動の流れとしては以下の通りです。

  1. Free5GCの起動
  2. NAS UEレイヤーの初期化
  3. eNBの起動
  4. UEの起動
  5. 経路設定とPing疎通確認

ターミナル画面を複数起動した方が良いので以下に定義しておきます。

項番 工程 ターミナル ホスト名
1 Free5GCの起動 A u18c132
2 NAS UEレイヤーの初期化 B u18c131
3 eNBの起動 C u18c131
4 UEの起動 D u18c131
5 経路設定とPing疎通確認 B u18c131
5-1.Free5GCの起動
cd ~/free5gc-stage-1
./free5gc-ngcd -d

出力例
3-9に記載した出力例と同様です。
以下の状態は、eNBからのアクセスを待機しています。

♯ターミナルA:u18c132の出力
~~~3-9に記載した出力例と同様 一部省略~~~

[01/03 10:56:58.220] AMF try to initialize
[01/03 10:56:58.369] AMF initialize...done


[01/03 10:56:58.369] INFO: free5GC daemon start (main.c:157)
[01/03 10:56:58.371] INFO: CONNECTED TO 'amf.localdomain' (TCP,soc#8): (fd_logger.c:93)
[01/03 10:56:58.371] INFO: CONNECTED TO 'hss.localdomain' (TCP,soc#11): (fd_logger.c:93)
[01/03 10:56:58.376] s1ap_server() [10.20.0.132]:36412
5-2.NAS UEレイヤーの初期化

loアドレスの追加とoipインターフェース*9の追加を行います。

ifconfig lo: 127.0.0.2 netmask 255.0.0.0 up && \
cd ~/ue_folder/cmake_targets/tools && \
source init_nas_s1 UE

出力例

♯ターミナルB:u18c131の出力
root@u18c131:~# ifconfig lo: 127.0.0.2 netmask 255.0.0.0 up && \
> cd ~/ue_folder/cmake_targets/tools && \
> source init_nas_s1 UE
loading ue_ip
bring up oip1 interface for UE
net.ipv4.conf.all.log_martians = 1
Disabling reverse path filtering
net.ipv4.conf.all.rp_filter = 0
root@u18c131:~#
root@u18c131:~/ue_folder/cmake_targets/tools# ip add show
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet 127.0.0.2/8 scope host secondary lo:
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens33:  mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:1a:a7:0b brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.131/24 brd 192.168.11.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe1a:a70b/64 scope link
       valid_lft forever preferred_lft forever
3: ens34:  mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:1a:a7:15 brd ff:ff:ff:ff:ff:ff
    inet 100.64.0.132/24 brd 100.64.0.255 scope global ens34
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe1a:a715/64 scope link
       valid_lft forever preferred_lft forever
4: ens35:  mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:1a:a7:1f brd ff:ff:ff:ff:ff:ff
    inet 10.20.0.131/24 brd 10.20.0.255 scope global ens35
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe1a:a71f/64 scope link
       valid_lft forever preferred_lft forever
5: oip1:  mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 100
    link/generic 00:00:00:00:00:00:00:00 brd 00:00:00:00:00:00:00:00
6: oip2:  mtu 1500 qdisc noop state DOWN group default qlen 100
    link/generic 00:00:00:00:00:00:00:00 brd 00:00:00:00:00:00:00:00

~~~一部省略~~~

20: oip16:  mtu 1500 qdisc noop state DOWN group default qlen 100
    link/generic 00:00:00:00:00:00:00:00 brd 00:00:00:00:00:00:00:00
5-3.eNBの起動

以下のコマンドでeNBを起動させます。
初回はログ出力ありの方がエラーを見つけやすいと思います。
しかし、大量にログが出力される=CPU負荷が増大するため、慣れてきたらログ出力なしにしてください。

♯ログ出力あり
cd ~/enb_folder/cmake_targets
sudo -E ./lte_build_oai/build/lte-softmodem -O ../ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf

♯ログ出力なし
cd ~/enb_folder/cmake_targets
sudo -E ./lte_build_oai/build/lte-softmodem -O ../ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf > enb.log 2>&1

出力例

♯ターミナルC:u18c131の出力
root@u18c131:~# cd ~/enb_folder/cmake_targets
root@u18c131:~/enb_folder/cmake_targets# sudo -E ./lte_build_oai/build/lte-softmodem -O ../ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf

[CONFIG] get parameters from libconfig ../ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf , debug flags: 0x00000000
[CONFIG] function config_libconfig_init returned 0
[CONFIG] config module libconfig loaded
[LIBCONFIG] config: 1/1 parameters successfully set, (1 to default value)
# /dev/cpu_dma_latency set to 0us
[LIBCONFIG] log_config: 3/3 parameters successfully set, (1 to default value)
[LIBCONFIG] log_config: 38/38 parameters successfully set, (32 to default value)
[LIBCONFIG] log_config: 38/38 parameters successfully set, (38 to default value)
[LIBCONFIG] log_config: 15/15 parameters successfully set, (15 to default value)
[LIBCONFIG] log_config: 15/15 parameters successfully set, (15 to default value)
log init done
Reading in command-line options
[LIBCONFIG] (root): 19/19 parameters successfully set, (16 to default value)
[LIBCONFIG] (root): 4/4 parameters successfully set, (4 to default value)
Getting ENBSParams
[LIBCONFIG] (root): 3/3 parameters successfully set, (1 to default value)
[LIBCONFIG] list L1s not found in config file ../ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf
[LIBCONFIG] list RUs not found in config file ../ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf
[LIBCONFIG] THREAD_STRUCT.[0]: 2/2 parameters successfully set, (0 to default value)
[LIBCONFIG] THREAD_STRUCT.[0]: 2/2 parameters successfully set, (0 to default value)
Configuration: nb_rrc_inst 1, nb_L1_inst 0, nb_ru 0
[LIBCONFIG] loader: 2/2 parameters successfully set, (2 to default value)
[LIBCONFIG] loader.NB_IoT: 2/2 parameters successfully set, (1 to default value)
[LOADER] library libNB_IoT.so is not loaded: libNB_IoT.so: cannot open shared object file: No such file or directory
               nb_nbiot_rrc_inst 0, nb_nbiot_L1_inst 0, nb_nbiot_macrlc_inst 0
[LIBCONFIG] TTracer: 4/4 parameters successfully set, (4 to default value)
configuring for RAU/RRU
CPU Freq is 3.605184
ITTI init, useMME: 1

~~~一部省略~~~

Waiting for PHY_config_req
Waiting for PHY_config_req
Waiting for PHY_config_req

♯UEがアタッチしてくるまで、Waiting for~のメッセージが出力され続けます。

♯ターミナルA:u18c132の出力
♯以下の出力が追加で表示されます
[01/03 12:27:38.048] eNB-S1 accepted[10.20.0.131]:36412 in s1_path module
[01/03 12:27:38.048] eNB-S1 accepted[10.20.0.131] in master_sm module
5-4.UEの起動

以下のコマンドでUEを起動させます。
eNBと同様に初回はログ出力あり慣れてきたらログ出力なしにしてください。
eNBよりも遥かに多くのログが出力されます。

♯ログ出力あり
cd ~/ue_folder/cmake_targets
sudo -E ./lte_build_oai/build/lte-uesoftmodem -O ../ci-scripts/conf_files/ue.nfapi.conf --L2-emul 3

♯ログ出力なし
sudo -E ./lte_build_oai/build/lte-uesoftmodem -O ../ci-scripts/conf_files/ue.nfapi.conf --L2-emul 3 > ue.log 2>&1

♯ログ出力なし+複数UEを起動する場合
sudo -E ./lte_build_oai/build/lte-uesoftmodem -O ../ci-scripts/conf_files/ue.nfapi.conf --L2-emul 3  --num-ues 2 --nums_ue_thread 2 > ue.log 2>&1

<補足>
複数UEを起動する場合、スレッド数がUE数を超えないようにしてください。

UE数   :  --num-ues 2 
スレッド数:  --nums_ue_thread 2

出力例

♯ターミナルD:u18c131の出力
root@u18c131:~# cd ~/ue_folder/cmake_targets
root@u18c131:~/ue_folder/cmake_targets# sudo -E ./lte_build_oai/build/lte-uesoftmodem -O ../ci-scripts/conf_files/ue.nfapi.conf --L2-emul 3

[CONFIG] get parameters from libconfig ../ci-scripts/conf_files/ue.nfapi.conf , debug flags: 0x00000000
[CONFIG] function config_libconfig_init returned 0
[CONFIG] config module libconfig loaded
[LIBCONFIG] config: 1/1 parameters successfully set, (1 to default value)
# /dev/cpu_dma_latency set to 0us
[LIBCONFIG] log_config: 3/3 parameters successfully set, (1 to default value)
[LIBCONFIG] log_config: 38/38 parameters successfully set, (32 to default value)
[LIBCONFIG] log_config: 38/38 parameters successfully set, (38 to default value)
[LIBCONFIG] log_config: 15/15 parameters successfully set, (15 to default value)
[LIBCONFIG] log_config: 15/15 parameters successfully set, (15 to default value)
log init done
Reading in command-line options
[LIBCONFIG] (root): 19/19 parameters successfully set, (16 to default value)
[LIBCONFIG] (root): 4/4 parameters successfully set, (4 to default value)
Running with 1 UE instances
NFAPI_MODE value: 3
[LIBCONFIG] TTracer: 4/4 parameters successfully set, (4 to default value)
CPU Freq is 3.602254
ITTI init

~~~一部省略~~~

(null)(null)(null)[PHY]   () 94087899677169.4294967296 POPULATE UL_CONFIG_REQ sfn_sf:-1947431856 buffer_index:16
(null)[PHY]   Warning pack_tlv tag 0x8e7fb9a0 does not match expected 0x8bd681f1
(null)(null)[PHY]   () 94087899677169.139871851118624 POPULATE TX_REQ sfn_sf:-1947431560 buffer_index:16
(null)(null)(null)[PHY]   () 94087899677169.6241124384 POPULATE DL_CONFIG_REQ sfn_sf:-1947432120 buffer_index:16
(null)(null)(null)[PHY]   () 94087899677169.139871851118624 POPULATE TX_REQ sfn_sf:-1947431560 buffer_index:16
(null)(null)(null)[PHY]   () 94087899677169.6241124384 POPULATE DL_CONFIG_REQ sfn_sf:-1947432120 buffer_index:16
(null)[MAC]   dl_config_req_UE_MAC 2 Received data: sfn/sf:5747 PDU[0] size:39, TX_PDU index: 0, tx_req_num_elems: 1
♯上記のログが大量に出力され続けます。


♯ターミナルC:u18c131の出力
Waiting for PHY_config_req
Waiting for PHY_config_req
(null)(null)[PHY]   Accepting connection from PNF...
=椒:979172368 ull)[PHY]   PNF connection (fd:979172368) accepted from 貳
(null)(null)(null)[PHY]   MALLOC nfapi_vnf_pnf_info_t for pnf_list pnf:0x7fca3a5cfc10
(null)[VNF] pnf connection indication idx:0
[VNF] RC.eNB[0][0]. Mod_id:0 CC_id:0 nb_CC[0]:1 abstraction_flag:0 single_thread_flag:0 if_inst:(nil)
RC.eNB[0][0]->if_inst->PHY_config_req is not installed - install it
oai_create_enb() Waiting for eNB to become configured (by RRC/PHY) - need to wait otherwise NFAPI messages won't contain correct values
oai_create_enb() Waiting for eNB to become configured (by RRC/PHY) - need to wait otherwise NFAPI messages won't contain correct values
Waiting for PHY_config_req
[PHY]   Configuring MIB for instance 0, CCid 0 : (band 7,N_RB_DL 50, N_RB_UL 50, Nid_cell 0,eNB_tx_antenna_ports 1,Ncp 0,DL freq 3400,phich_config.resource 0, phich_config.duration 0)
[PHY]   Initializing frame parms for N_RB_DL 50, Ncp 0, osf 1
[PHY]   lte_parms.c: Setting N_RB_DL to 50, ofdm_symbol_size 1024
[LIBCONFIG] loader.coding: 2/2 parameters successfully set, (1 to default value)
[LOADER] library libcoding.so successfully loaded
oai_create_enb() Waiting for eNB to become configured (by RRC/PHY) - need to wait otherwise NFAPI messages won't contain correct values
oai_create_enb() Waiting for eNB to become configured (by RRC/PHY) - need to wait otherwise NFAPI messages won't contain correct values

~~~一部省略~~~

[S1AP]   [eNB 0] Chose MME '(null)' (assoc_id 1) through selected PLMN Identity index 0 MCC 208 MNC 93
[S1AP]   Found usable eNB_ue_s1ap_id: 0x06692d 420141(10)
[SCTP]   Successfully sent 96 bytes on stream 1 for assoc_id 1
[SCTP]   Found data for descriptor 59
[SCTP]   Received notification for sd 59, type 32777
[SCTP]   Found data for descriptor 59

(null)[PHY]   [eNB] sfn/sf:7000 old_sfn/sf:7000 proc[rx:6999]
(null)(null)[PHY]   (962386544/1185663553) 1006643512.1187473240 PNF to VNF phy_id:16 (t1/2/3/4:962386768, 962386576, 2720839680,       80) txrx:335546600 procT:713 latency(us):   8(avg:1138) offset(us):354025839 filtered(us):       0 wrap[t1:962386944 t2:0]
(null)(null)(null)[PHY]   PNF to VNF phy_id:962386640 adjustment1185663553 phy->previous_sf_offset_filtered:0 phy->previous_sf_offset_filtered:1187473480 phy->sf_offset_trend:16
(null)(null)(null)[PHY]   () LARGE SFN/SF DELTA between PNF and VNF delta:1185663553 VNF:962386912 PNF:1187474184

(null)(null)(null)[PHY]   (962386544/1185663553) 1006643512.1187473240 PNF to VNF phy_id:16 (t1/2/3/4:962386768, 962386576, 2720839680,       80) txrx:335546600 procT:765 latency(us):   0(avg:1138) offset(us):867330225 filtered(us):       0 wrap[t1:962386944 t2:0]
(null)[MAC]   UE  rnti 14e2 : in synch, PHR 21 dB DL CQI 0 PUSCH SNR 63 PUCCH SNR 63
(null)(null)[PHY]   () LARGE SFN/SF DELTA between PNF and VNF delta:1185663553 VNF:962386912 PNF:1187474184

(null)[PHY]   [eNB] sfn/sf:8000 old_sfn/sf:8000 proc[rx:7999]
(null)(null)[PHY]   () LARGE SFN/SF DELTA between PNF and VNF delta:1185663553 VNF:962386912 PNF:1187474184
♯上記のログが散発的に出力され続けます。


♯ターミナルA:u18c132の出力
♯以下の出力が追加で表示されます*10
[01/03 12:31:20.916] INFO: N11 message enter (smf_sm.c:210)
[01/03 12:31:20.916] INFO: APN_type: 2 (smf_context.c:1466)
[01/03 12:31:20.918]  pfcp_xact_local_create 1 not freed in pfcp_xact_pool[64] of PFCP Transaction
[01/03 12:31:20.920] INFO: PAA dst addr: 33554457 (amf_n11_handler.c:47)
[01/03 12:31:20.920] INFO: ipv4: 1, ipv6: 0 (amf_n11_handler.c:64)
[01/03 12:31:20.920] INFO: ipv4: 1, ipv6: 0 (amf_n11_handler.c:65)
[01/03 12:31:20.920] INFO: create Session (amf_n11_handler.c:71)
[01/03 12:31:20.919] PDN type: 0[01/03 12:31:20.919] PDN PAA PDn Type: 1[01/03 12:31:21.157] INFO: SMF Recieve Sm Context Update (smf_sbi_path.c:57)
[01/03 12:31:21.157] INFO: N11 message enter (smf_sm.c:210)
[01/03 12:31:21.157] smf_n4_seid : 1[01/03 12:31:21.157]  pfcp_xact_local_create 1 not freed in pfcp_xact_pool[64] of PFCP Transaction
[01/03 12:31:21.157] smf_n11_build_update_session_response SM 1
[01/03 12:31:21.157] smf_n11_build_update_session_response SM addr -1337395080
[01/03 12:31:21.157] INFO: smf_sbi_send_sm_context_update (smf_sbi_path.c:234)
[01/03 12:31:21.157] INFO: smf_sbi_send_sm_context_update end (smf_sbi_path.c:237)
[01/03 12:31:21.158] INFO: AMF Update Session Done (amf4g_sm.c:830)
5-5.経路設定とPing疎通確認

複数UEを起動する場合、明示的にインターフェース名を指定した方が無難です。*11

route add default gw 25.0.0.1 dev oip1
ping 10.10.0.254 -I oip1

出力例

♯ターミナルB:u18c131の出力
root@u18c131:~# route add default gw 25.0.0.1 dev oip1
root@u18c131:~# ping 10.10.0.254 -I oip1
PING 10.10.0.254 (10.10.0.254) from 25.0.0.3 oip1: 56(84) bytes of data.
64 bytes from 10.10.0.254: icmp_seq=1 ttl=63 time=35.6 ms
64 bytes from 10.10.0.254: icmp_seq=2 ttl=63 time=26.1 ms
64 bytes from 10.10.0.254: icmp_seq=3 ttl=63 time=21.4 ms
64 bytes from 10.10.0.254: icmp_seq=4 ttl=63 time=24.0 ms
64 bytes from 10.10.0.254: icmp_seq=5 ttl=63 time=23.1 ms
64 bytes from 10.10.0.254: icmp_seq=6 ttl=63 time=22.8 ms
64 bytes from 10.10.0.254: icmp_seq=7 ttl=63 time=28.3 ms

♯アドレスや経路情報の確認
root@u18c131:~# ip add show
5: oip1:  mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 100
    link/generic 00:00:00:00:00:00:00:00 brd 00:00:00:00:00:00:00:00
    inet 25.0.0.4/8 brd 25.255.255.255 scope global oip1
       valid_lft forever preferred_lft forever
6: oip2:  mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 100
    link/generic 00:00:00:00:00:00:00:00 brd 00:00:00:00:00:00:00:00
    inet 25.0.0.5/8 brd 25.255.255.255 scope global oip2
       valid_lft forever preferred_lft forever

root@u18c131:~# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         _gateway        0.0.0.0         UG    0      0        0 oip1
default         nvr500.md.jp    0.0.0.0         UG    0      0        0 ens33
10.20.0.0       0.0.0.0         255.255.255.0   U     0      0        0 ens35
25.0.0.0        0.0.0.0         255.0.0.0       U     0      0        0 oip1
25.0.0.0        0.0.0.0         255.0.0.0       U     0      0        0 oip2
100.64.0.0      0.0.0.0         255.255.255.0   U     0      0        0 ens34
192.168.11.0    0.0.0.0         255.255.255.0   U     0      0        0 ens33

アタッチまでのシーケンスをPcapしたファイルはこちらにアップしました。

6.補足1

PingのRTTについて補足します。
上記結果のように20~40msくらいで安定しているときもあれば、以下のように1秒を超えるときもあります。
このため、RTTが多少遅くてもよい場合は、以前の記事で紹介したOAISIMのUE&eNBを使用した方がよいかと思います。*12
なお、稼働時間については、L2 nFAPI環境でも1時間近く動作した実績はあります。

64 bytes from 10.10.0.254: icmp_seq=285 ttl=63 time=46.0 ms
64 bytes from 10.10.0.254: icmp_seq=286 ttl=63 time=39.8 ms
64 bytes from 10.10.0.254: icmp_seq=287 ttl=63 time=2590 ms
64 bytes from 10.10.0.254: icmp_seq=288 ttl=63 time=4147 ms
64 bytes from 10.10.0.254: icmp_seq=289 ttl=63 time=3130 ms
64 bytes from 10.10.0.254: icmp_seq=290 ttl=63 time=2154 ms
64 bytes from 10.10.0.254: icmp_seq=291 ttl=63 time=1195 ms
64 bytes from 10.10.0.254: icmp_seq=292 ttl=63 time=199 ms
64 bytes from 10.10.0.254: icmp_seq=293 ttl=63 time=13.0 ms
64 bytes from 10.10.0.254: icmp_seq=294 ttl=63 time=2592 ms
64 bytes from 10.10.0.254: icmp_seq=295 ttl=63 time=4167 ms


以上です。

7.最後に

以下のサイトを参考にさせて頂きました。
https://www.free5gc.org/installation
https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/l2-nfapi-simulator/l2-nfapi-simulator-w-S1-same-machine

今年は5G元年ということなので、年明け最初の記事は、5G関連の技術トピックにフォーカスしてみました。
これからどんどん発展していくと思うと、とてもワクワクします。

3Gや4G/LTEはほぼ完成した状態から勉強を始めたので追いつくのが大変でしたが、5Gは進化の過程に合わせてキャッチアップしていけたらいいなと考えています。

なお、今年の目標ではないですが、前回記事
metonymical.hatenablog.com
で紹介したk8sのPodやvmi上にて、L2 nFAPI や Free5GCを動作させられたら面白そうだなと考えています。
k8s上でモバイルコアNWを構築するにあたり、c-planeの足回りはDPDK*13やXDPで捌きながら、u-planeはSR-IOVで捌けるような構成ができると、さらに面白味が増すかなと思っています。

*1:AMF,SMF,UPF,HSS,PCRF

*2:というのも、仮想マシンでOAIを動作させるのに限界を感じており、COTSサーバで構築した方が良さそうなので。加えて、Lowlatency Kernelなども使いたいので。

*3:公式の手順通りだと、.networkの設定を行ってもIPアドレスがうまくアサインされなかったため、/etc/network/interfacesに直書きしています。

*4:/etc/network/interfacesに直書きしても、IPアドレスアサインされなかった場合に備えてインストールしておいても良いと思います。

*5:Free5GCはNextEPCがベースとなっているため、画面はほぼ一緒です。

*6:5つのプロセスが起動していると思います。

*7:公式サイトでは逆になっているように見受けられたので。

*8:実際は・・・と表示されます

*9:UEのアドレスがアサインされるインターフェース

*10:出力されるまでに10秒くらい掛かる場合がありました。

*11:非対称になるとPingが返って来ないと勘違いするため

*12:OAISIMの場合、概ね80~90msくらいで推移します。

*13:年末にk8s上でのDPDK動作検証を実施したのですが挫折しました。。。正確に言うと、OvSでもVPPでもDPDK自体は動作したのですが、外部NWにトラフィックを流すことができませんでした。

CentOS8 KubernetesのSR-IOV設定方法

Kubernetes(以下、k8s)のSR-IOV設定方法を記載します。
PodとVMI(Virtual Machine Instance)の両方でそれぞれSR-IOVを設定していきます。

1.構成

1-1.環境
1.Master
VMWare              : VMware(R) Workstation 15 Pro 15.5.1 build-15018445 

2.Worker
筐体                             : ProLiant DL360p Gen8
System ROM                       : P71 05/24/2019
NIC                              : Intel X520-SR2(82599ES), X540-AT2

3.Master&Worker共通
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 

Kubernetes                       : 1.17.0
Docker                           : 19.03.5
flannel                          : latest
KubeVirt                         : 0.24.0
Multus                           : latest
sriov-cni                        : latest
sriov-network-device-plugin      : latest
1-2.全体の構成

f:id:metonymical:20191215102800p:plain

1-3.全体の流れ
  1. 事前準備
  2. k8s Cluster & flannelの構築
  3. KubeVirtの構築
  4. SR-IOV関連のビルド
  5. SR-IOV関連の設定
  6. VMIとPodのデプロイ

1~3までは比較的多くのドキュメントが存在しますので、重要な箇所以外はある程度割愛します。*1

2.事前準備

2-1.諸々の準備
SELinuxの無効化 : Master&Worker*2
sed -i -e "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config

FW無効化 : Master&Worker
systemctl stop firewalld
systemctl disable firewalld

パススルー設定 : Master&Worker*3
sed -i -e "s/#options kvm_intel nested=1/options kvm_intel nested=1/g" /etc/modprobe.d/kvm.conf

swap無効化 : Master&Worker
vi /etc/fstab

#/dev/mapper/cl-swap     swap                    swap    defaults        0 0

hostsファイル設定 : Master&Worker*4
vi /etc/hosts

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4 c80g105.md.jp c80g105
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

192.168.11.105 c80g105 c80g105.md.jp
192.168.11.106 c80g106 c80g106.md.jp
2-2.HugePageとIOMMUの有効化 : Worker
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

vi /etc/fstab

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

hugepageの有効化
iommuの有効化
grubに設定反映
マウント

2-3.SR-IOVのVF設定 : Worker
vi /etc/rc.local

echo 8 > /sys/class/net/ens1f0/device/sriov_numvfs
echo 8 > /sys/class/net/ens1f1/device/sriov_numvfs
echo 8 > /sys/class/net/ens2f0/device/sriov_numvfs
echo 8 > /sys/class/net/ens2f1/device/sriov_numvfs
sleep 1
exit 0

chmod +x /etc/rc.d/rc.local

2枚のNICの各ポートにVFをそれぞれ8つづつ設定
実行権限付与

<補足>
過去記事では、Blacklistに追加していますが、今回は不要です。

2-4.vfio-pciの設定 : Worker
vi /etc/modprobe.d/vfio_pci.conf

options vfio_pci ids=8086:10ed

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を有効化するデバイス(NIC)のDeviceID設定
vfio-pciの永続化設定
vfio-pciのiommu利用の永続化

<補足(重要)>
DeviceIDはそれぞれ以下の通りとなります。

8086:10ed X520 VMI用 vfio-pci
8086:1515 X540 Pod用 ixgbevf

今回は2枚のNICでそれぞれVMI用とPod用として分けているため、vfio-pciを有効化するのは、X520のみとしています。*5
というのも、以下2点を確認したからです。*6

  • VMIでixgbevfを使用すると、kubectl get vmi上ではscheduledのまま固まります。flannelのIPはアサインされますがPingは通りません。*7
  • Podでvfio-pciNICを掴むことができませんでした。ip link showなどで確認しても追加されていません。

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

3.k8s Cluster & flannelの構築

3-1.Dockerのインストール : Master&Worker
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo && \
dnf -y install https://download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-1.2.10-3.2.el7.x86_64.rpm && \
dnf -y install docker-ce && \
systemctl start docker && \
systemctl enable docker
3-3.k8sのインストール : Master&Worker
Master
dnf -y install kubeadm kubectl

Worker
dnf -y install kubeadm

Master&Worker
systemctl start kubelet.service && \
systemctl enable kubelet.service
3-4.Dockerの設定 : Master&Worker
cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ]
}
EOF

mkdir -p /etc/systemd/system/docker.service.d

systemctl daemon-reload && \
systemctl restart docker
3-5.k8sClusterの構築 : Master
kubeadm init --apiserver-advertise-address=192.168.11.105 --pod-network-cidr=10.244.0.0/16

<出力例>
最後に以下の出力が表示されますので、赤文字部分をコピーしておいてください。
WorkerがMasterへJoinする際に使用します。

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.11.105:6443 --token 0gfh5j.vgu76alcycb2tc2e \
    --discovery-token-ca-cert-hash sha256:edcb1a3856838586a6ea7c99200daafa4fbb639e822838f4df81ce09d2faaac3 
3-6.k8s Cluster構築後の設定 : Master
コンフィグファイルのコピー
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

コマンド補完設定
echo "source <(kubectl completion bash)" >> ~/.bashrc
3-7.flannelのインストール : Master
mkdir /root/tmp && \
cd /root/tmp/ && \
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml && \
kubectl apply -f kube-flannel.yml

kubectl get nodes

<出力例>
以下のようにReadyとなるまで待ってください。

[root@c80g105 ~]# kubectl get nodes
NAME            STATUS   ROLES    AGE   VERSION
c80g105.md.jp   Ready    master   48m   v1.17.0
3-8.WorkerのJoin : Worker
kubeadm join 192.168.11.105:6443 --token 0gfh5j.vgu76alcycb2tc2e \
    --discovery-token-ca-cert-hash sha256:edcb1a3856838586a6ea7c99200daafa4fbb639e822838f4df81ce09d2faaac3

<出力例>
以下のようにWorkerもReadyとなるまで待ってください。

[root@c80g105 ~]# kubectl get nodes
NAME            STATUS   ROLES    AGE   VERSION
c80g105.md.jp   Ready    master   48m   v1.17.0
c80g106.md.jp   Ready    <none>   19s   v1.17.0

ここまでの準備が整ったらMaster&Workerをもう一度再起動してください。

4.KubeVirtの構築

4-1.KubeVirtインストール : Master
export RELEASE=v0.24.0
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-operator.yaml
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-cr.yaml

<出力例>
Podの状態が以下のようにすべてRunningになるまでには、2分程度掛かります。

kubectl get pods -n kubevirt

[root@c80g116 ~]# kubectl -n kubevirt get pods
NAME                               READY   STATUS    RESTARTS   AGE
virt-api-64f59f44dd-prkj4          1/1     Running   3          70m
virt-api-64f59f44dd-w9984          1/1     Running   3          71m
virt-controller-79cc7fb59d-6bz7d   1/1     Running   3          70m
virt-controller-79cc7fb59d-wt8g4   1/1     Running   3          70m
virt-handler-ljfqg                 1/1     Running   5          88m
virt-operator-54db765c7d-4xkq5     1/1     Running   3          70m
virt-operator-54db765c7d-5bgw4     1/1     Running   3          64m

全てRunningになったら、以下のコマンドを実行

kubectl -n kubevirt wait kv kubevirt --for condition=Available

<出力例>
以下の出力が表示されればOKです。

[root@c80g105 ~]# kubectl -n kubevirt wait kv kubevirt --for condition=Available
kubevirt.kubevirt.io/kubevirt condition met
4-2.virtctlのインストール : Master : オプション

必須ではありませんが、vmiを操作する際に便利です。*8

export RELEASE=v0.24.0
curl -L -o virtctl \
    https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/virtctl-${RELEASE}-linux-amd64

chmod +x virtctl
chown root:root virtctl
mv virtctl /usr/bin
4-3.virtctlのインストール : Master : オプション

必須ではありませんが、virtctlはKrewを使ってもインストールできます。*9

export RELEASE=v0.3.3

set -x; cd "$(mktemp -d)" &&
curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/download/v0.3.3/krew.{tar.gz,yaml}" &&
tar zxvf krew.tar.gz &&
KREW=./krew-"$(uname | tr '[:upper:]' '[:lower:]')_amd64" &&
"$KREW" install --manifest=krew.yaml --archive=krew.tar.gz &&
"$KREW" update

export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"

kubectl krew install virt

echo 'PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"' >> ~/.bashrc

5.SR-IOV関連のビルド

5-1.Golangのインストール : Master
dnf -y install epel-release golang
5-2.SR-IOV CNIのビルド : Master
git clone https://github.com/intel/sriov-cni.git && \
cd sriov-cni && \
make && \
cp build/sriov /opt/cni/bin

SRIOV CNIの実行ファイルをWorkerへコピー
scp /opt/cni/bin/sriov root@192.168.11.106:/opt/cni/bin/
5-3.SR-IOV Network Deviceのビルド : Master
cd ~/ && \
git clone https://github.com/intel/sriov-network-device-plugin.git && \
cd sriov-network-device-plugin && \
make

docker build -t nfvpe/sriov-device-plugin -f ./images/Dockerfile .

<出力例>

docker images

[root@c80g105 sriov-network-device-plugin]# docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
nfvpe/sriov-device-plugin            latest              a45310aae832        13 seconds ago      25.3MB
<none>                               <none>              b65d412dee59        19 seconds ago      905MB
k8s.gcr.io/kube-proxy                v1.17.0             7d54289267dc        6 days ago          116MB
k8s.gcr.io/kube-apiserver            v1.17.0             0cae8d5cc64c        6 days ago          171MB
k8s.gcr.io/kube-controller-manager   v1.17.0             5eb3b7486872        6 days ago          161MB
k8s.gcr.io/kube-scheduler            v1.17.0             78c190f736b1        6 days ago          94.4MB
golang                               alpine              69cf534c966a        8 days ago          359MB
k8s.gcr.io/coredns                   1.6.5               70f311871ae1        5 weeks ago         41.6MB
k8s.gcr.io/etcd                      3.4.3-0             303ce5db0e90        7 weeks ago         288MB
alpine                               latest              965ea09ff2eb        7 weeks ago         5.55MB
quay.io/coreos/flannel               v0.11.0-amd64       ff281650a721        10 months ago       52.6MB
k8s.gcr.io/pause                     3.1                 da86e6ba6ca1        24 months ago       742kB
5-4.SR-IOV Network Deviceのビルド : Worker
cd ~/ && \
git clone https://github.com/intel/sriov-network-device-plugin.git && \
cd sriov-network-device-plugin && \
docker build -t nfvpe/sriov-device-plugin -f ./images/Dockerfile .

<出力例>

docker images

[root@c80g106 sriov-network-device-plugin]# docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
nfvpe/sriov-device-plugin   latest              8c508ab37c1c        28 seconds ago      25.3MB
<none>                      <none>              571b3587b90d        34 seconds ago      855MB
k8s.gcr.io/kube-proxy       v1.17.0             7d54289267dc        6 days ago          116MB
golang                      alpine              69cf534c966a        8 days ago          359MB
alpine                      latest              965ea09ff2eb        7 weeks ago         5.55MB
quay.io/coreos/flannel      v0.11.0-amd64       ff281650a721        10 months ago       52.6MB
k8s.gcr.io/pause            3.1                 da86e6ba6ca1        24 months ago       742kB
kubevirt/virt-api           <none>              1a3697b92f09        49 years ago        293MB
kubevirt/virt-controller    <none>              ab9c8cce9c60        49 years ago        291MB
kubevirt/virt-handler       <none>              c5919b95ca31        49 years ago        313MB
kubevirt/virt-operator      <none>              45c6d543afb1        49 years ago        326MB

<補足>
この後の工程で、sriovdp-daemonset.yaml をデプロイするのですが、元から以下のように書かれているため、WorkerにおいてもDockerイメージをビルドしています。

      containers:
      - name: kube-sriovdp
        image: nfvpe/sriov-device-plugin
        imagePullPolicy: Never

6.SR-IOV関連の設定

以下のQuick Startに記載された順番に従い設定を行っていきます。
GitHub - k8snetworkplumbingwg/sriov-network-device-plugin: SRIOV network device plugin for Kubernetes

流れは以下の通りです。*10

  1. configMapの設定&デプロイ
  2. sriovdp-daemonsetのデプロイ
  3. Master:Multusのインストール
  4. sriov-crdの設定&デプロイ
  5. リソースの確認
6-1.ConfigMapの設定 : Master

ポイントは

"resourceName": "intel_sriov_netdevice1",
"resourceName": "intel_sriov_netdevice2",

として別けて記載している点です。
CRDやVMI&Podのyamlファイルにも一連の流れとして記載していくため、に別けて書いておきます。

既存ファイルを以下のように書き換えてください。

vi /root/sriov-network-device-plugin/deployments/configMap.yaml

[root@c80g105 ~]# vi /root/sriov-network-device-plugin/deployments/configMap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: sriovdp-config
  namespace: kube-system
data:
  config.json: |
    {
        "resourceList": [{
apiVersion: v1
kind: ConfigMap
metadata:
  name: sriovdp-config
  namespace: kube-system
data:
  config.json: |
    {
        "resourceList": [
            {
                "resourceName": "intel_sriov_netdevice1",
                "selectors": {
                    "vendors": ["8086"],
                    "devices": ["154c", "10ed"],
                    "drivers": ["i40evf", "vfio-pci"]
                }
            },
            {
                "resourceName": "intel_sriov_netdevice2",
                "selectors": {
                    "vendors": ["8086"],
                    "devices": ["154c", "1515"],
                    "drivers": ["i40evf", "ixgbevf"]
                }
            },
            {
                "resourceName": "intel_sriov_dpdk",
                "selectors": {
                    "vendors": ["8086"],
                    "devices": ["154c", "10ed"],
                    "drivers": ["vfio-pci"],
                    "pfNames": ["enp0s0f0","enp2s2f1"]
                }
            },
            {
                "resourceName": "mlnx_sriov_rdma",
                "isRdma": true,
                "selectors": {
                    "vendors": ["15b3"],
                    "devices": ["1018"],
                    "drivers": ["mlx5_ib"]
                }
            }
        ]
    }

kubectl create -f /root/sriov-network-device-plugin/deployments/configMap.yaml

<出力例>

kubectl get configmaps -n kube-system

[root@c80g105 ~]# kubectl get configmaps -n kube-system
NAME                                 DATA   AGE
coredns                              1      4h32m
extension-apiserver-authentication   6      4h33m
kube-flannel-cfg                     2      4h32m
kube-proxy                           2      4h32m
kubeadm-config                       2      4h32m
kubelet-config-1.17                  1      4h32m
multus-cni-config                    1      3h16m
sriovdp-config                       1      3h16m

<補足>
先ほど記載した表の通りに別けて書いてあることが確認できると思います。

8086:10ed X520 VMI用 vfio-pci
8086:1515 X540 Pod用 ixgbevf
6-2.SRIOVDP DaemonSetのデプロイ : Master

特に修正する箇所はありませんので、yamlファイルの中身を確認せず、以下のコマンドを入力しても構いません。

kubectl create -f /root/sriov-network-device-plugin/deployments/k8s-v1.16/sriovdp-daemonset.yaml
vi /root/sriov-network-device-plugin/deployments/k8s-v1.16/sriovdp-daemonset.yaml

[root@c80g105 ~]# vi /root/sriov-network-device-plugin/deployments/k8s-v1.16/sriovdp-daemonset.yaml

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sriov-device-plugin
  namespace: kube-system

---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-sriov-device-plugin-amd64
  namespace: kube-system
  labels:
    tier: node
    app: sriovdp
spec:
  selector:
    matchLabels:
      name: sriov-device-plugin
  template:
    metadata:
      labels:
        name: sriov-device-plugin
        tier: node
        app: sriovdp
    spec:
      hostNetwork: true
      hostPID: true
      nodeSelector:
        beta.kubernetes.io/arch: amd64
      tolerations:
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      serviceAccountName: sriov-device-plugin
      containers:
      - name: kube-sriovdp
        image: nfvpe/sriov-device-plugin
        imagePullPolicy: Never
        args:
        - --log-dir=sriovdp
        - --log-level=10
        securityContext:
          privileged: true
        volumeMounts:
        - name: devicesock
          mountPath: /var/lib/kubelet/
          readOnly: false
        - name: log
          mountPath: /var/log
        - name: config-volume
          mountPath: /etc/pcidp
      volumes:
        - name: devicesock
          hostPath:
            path: /var/lib/kubelet/
        - name: log
          hostPath:
            path: /var/log
        - name: config-volume
          configMap:
            name: sriovdp-config
            items:
            - key: config.json
              path: config.json

kubectl create -f /root/sriov-network-device-plugin/deployments/k8s-v1.16/sriovdp-daemonset.yaml

<出力例>*11

kubectl get pods -n kube-system

[root@c80g105 ~]# kubectl get pods -n kube-system
NAME                                    READY   STATUS    RESTARTS   AGE
coredns-6955765f44-cn2xp                1/1     Running   1          4h35m
coredns-6955765f44-z9dj6                1/1     Running   1          4h35m
etcd-c80g105.md.jp                      1/1     Running   1          4h35m
kube-apiserver-c80g105.md.jp            1/1     Running   1          4h35m
kube-controller-manager-c80g105.md.jp   1/1     Running   1          4h35m
kube-flannel-ds-amd64-7ltvr             1/1     Running   1          4h35m
kube-flannel-ds-amd64-rfr4f             1/1     Running   1          4h35m
kube-multus-ds-amd64-475wf              1/1     Running   0          3h19m
kube-multus-ds-amd64-9zz5d              1/1     Running   0          3h19m
kube-proxy-dmqvr                        1/1     Running   1          4h35m
kube-proxy-pwmx2                        1/1     Running   0          4h35m
kube-scheduler-c80g105.md.jp            1/1     Running   1          4h35m
kube-sriov-device-plugin-amd64-c2szn    1/1     Running   0          3h19m
kube-sriov-device-plugin-amd64-ttwj5    1/1     Running   0          3h19m
6-3.Multusのインストール : Master

ここも特に設定変更などは不要です。

cd /root && \
git clone https://github.com/intel/multus-cni.git && \
cd multus-cni && \
cat ./images/multus-daemonset.yml | kubectl apply -f -

<出力例>
デプロイ後、以下のような出力が表示されればOKです。

kubectl get pods --all-namespaces | grep -i multus

[root@c80g105 multus-cni]# kubectl get pods --all-namespaces | grep -i multus
kube-system   kube-multus-ds-amd64-7kc44              1/1     Running   0          51s
kube-system   kube-multus-ds-amd64-8ds8r              1/1     Running   0          51s
6-4.SRIOV CRDの設定 : Master
8086:10ed X520 VMI用 vfio-pci

上記VMI用のCRD(Custom Resource Definitions)の設定を行っていきます。
ポイントはannotationsのresourceNameがconfigMap.yamlと一致している点です。
加えて、この後に設定するVMI用yamlにsriov-net1を記載します。

vi /root/sriov-network-device-plugin/deployments/sriov-crd1.yaml

[root@c80g105 ~]# vi /root/sriov-network-device-plugin/deployments/sriov-crd1.yaml

apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: sriov-net1
  annotations:
    k8s.v1.cni.cncf.io/resourceName: intel.com/intel_sriov_netdevice1
spec:
  config: '{
  "type": "sriov",
  "cniVersion": "0.3.1",
  "name": "sriov300_1",
  "vlan": 300,
  "ipam": {
    "type": "host-local",
    "subnet": "192.168.30.0/24",
    "routes": [{
      "dst": "0.0.0.0/0"
    }],
    "gateway": "192.168.30.254"
  }
}'

kubectl create -f /root/sriov-network-device-plugin/deployments/sriov-crd1.yaml


続いて、Pod用のCRD設定を行っていきます。

8086:1515 X540 Pod用 ixgbevf
vi /root/sriov-network-device-plugin/deployments/sriov-crd2.yaml

[root@c80g105 ~]# vi /root/sriov-network-device-plugin/deployments/sriov-crd2.yaml

apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: sriov-net2
  annotations:
    k8s.v1.cni.cncf.io/resourceName: intel.com/intel_sriov_netdevice2
spec:
  config: '{
  "type": "sriov",
  "cniVersion": "0.3.1",
  "name": "sriov300_2",
  "vlan": 300,
  "ipam": {
    "type": "host-local",
    "subnet": "192.168.30.0/24",
    "routes": [{
      "dst": "0.0.0.0/0"
    }],
    "gateway": "192.168.30.254"
  }
}'

kubectl create -f /root/sriov-network-device-plugin/deployments/sriov-crd2.yaml

<出力例>
以下のように表示されればOKです。

[root@c80g105 ~]# kubectl get network-attachment-definitions
NAME         AGE
sriov-net1  3h29m
sriov-net2  3h29m
6-5.リソースの確認 : Master

SR-IOVのVFがWorkerのリソースとしてk8sに認識されていることを確認します。

dnf -y install jq
kubectl get node c80g106.md.jp -o json | jq '.status.allocatable'

<出力例>
以下のような出力が表示されればOKです。

[root@c80g105 ~]# kubectl get node c80g106.md.jp -o json | jq '.status.allocatable'
{
  "cpu": "40",
  "devices.kubevirt.io/kvm": "110",
  "devices.kubevirt.io/tun": "110",
  "devices.kubevirt.io/vhost-net": "110",
  "ephemeral-storage": "400024062519",
  "hugepages-1Gi": "16Gi",
  "intel.com/intel_sriov_dpdk": "0",
  "intel.com/intel_sriov_netdevice": "0",
  "intel.com/intel_sriov_netdevice1": "16",
  "intel.com/intel_sriov_netdevice2": "16",
  "intel.com/mlnx_sriov_rdma": "0",
  "memory": "313085984Ki",
  "pods": "110"
}

7.VMIとPodのデプロイ

VMI用として事前にqcow2イメージファイルを作成しておきます。*12
以下の過去記事などを参照の上、適当な仮想マシンを作成してください。
CentOS7 kickstartによるインストール - Metonymical Deflection
CentOS8 kickstartによるインストール - Metonymical Deflection

ここでは、CentOS7.7をインストールしたqcow2ファイルが、/root直下に既に存在する前提として話を進めます。

7-1.VMI用のイメージ登録 : Worker
イメージファイルのコピー
mkdir /root/docker
cd /root/docker
cp /root/c771.qcow2 ./

ビルド用のDockerfile作成
cat << END > Dockerfile
FROM scratch
ADD c771.qcow2 /disk/
END

イメージのビルド
docker build -t vmidisks/centos:7.7 .

<出力例>

[root@c80g106 docker]# docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
vmidisks/centos             7.7                 f5a39b56e448        21 seconds ago      1.64GB
nfvpe/sriov-device-plugin   latest              8c508ab37c1c        6 minutes ago       25.3MB
<none>                      <none>              571b3587b90d        7 minutes ago       855MB
k8s.gcr.io/kube-proxy       v1.17.0             7d54289267dc        6 days ago          116MB
golang                      alpine              69cf534c966a        8 days ago          359MB
nfvpe/multus                v3.4                7cf8e2d1b733        10 days ago         312MB
alpine                      latest              965ea09ff2eb        7 weeks ago         5.55MB
quay.io/coreos/flannel      v0.11.0-amd64       ff281650a721        10 months ago       52.6MB
k8s.gcr.io/pause            3.1                 da86e6ba6ca1        24 months ago       742kB
kubevirt/virt-handler       <none>              c5919b95ca31        49 years ago        313MB
kubevirt/virt-controller    <none>              ab9c8cce9c60        49 years ago        291MB
kubevirt/virt-api           <none>              1a3697b92f09        49 years ago        293MB
kubevirt/virt-operator      <none>              45c6d543afb1        49 years ago        326MB
7-2.VMIのデプロイ : Master
vi /root/sriov-network-device-plugin/deployments/c771.yaml

[root@c80g105 deployments]# vi /root/sriov-network-device-plugin/deployments/c771.yaml

apiVersion: kubevirt.io/v1alpha3
kind: VirtualMachineInstance
metadata:
  name: c771
spec:
  domain:
    resources:
      requests:
        memory: "2048Mi"
        cpu: "1"
    devices:
      disks:
      - name: containerdisk
        disk:
          bus: virtio
      interfaces:
      - name: pod
        bridge: {}
      - name: net1
        sriov: {}
      - name: net2
        sriov: {}
  volumes:
  - name: containerdisk
    containerDisk:
      image: vmidisks/centos:7.7
      imagePullPolicy: Never
  networks:
  - name: pod
    pod: {}
  - name: net1
    multus:
      networkName: sriov-net1
  - name: net2
    multus:
      networkName: sriov-net1


kubectl create-f /root/sriov-network-device-plugin/deployments/c771.yaml

<出力例>

kubectl get pods
kubectl get vmi

[root@c80g105 ~]# kubectl get pods
kubectl get vmi
NAME                       READY   STATUS    RESTARTS   AGE
virt-launcher-c771-5kdr2   2/2     Running   0          3h29m

[root@c80g105 ~]# kubectl get vmi
NAME   AGE     PHASE     IP               NODENAME
c771   3h29m   Running   10.244.1.14/24   c80g106.md.jp

<補足>
ポイントを5点ほど。

  1. multus: > networkName: に先ほど作成したCRDの名前sriov-net1を指定している点です。これにより、明示的にX520を使用する設定*13となっています。
  2. spec: > domain: > resources: には、requests:のみを記載している点です。ここでlimits: を追記すると起動後に高確率でOOM Killerが発動しVMIが瞬殺されます。*14
  3. kubectl get podsにて、READYが2/2になっています。これはVMIに加えて、virt-launcherが起動しているためです。
  4. kubectl get vmiにて、IPが10.244.1.14/24*15となっています。IPアドレス自体「10.244.1.14」は、PHASEがRunningになると、すぐに表示されます。しかし、私の環境ではプリフィックス「 /24 」が表示されるまでに90秒程度かかりました。また、プリフィックスが表示されなくてもログインできる場合があります。*16
  5. SR-IOVにてアサインされたNICが保持するIPアドレスを確認するためには、VMIへのログインが必要なので、virtctl console コマンドやsshなどでVMIにログインしてください。ログイン後、ip add showすると、以下のように見えると思います。eth1とeth2のMACアドレスがWorkerのVFのMACアドレスと一致していることを確認してください。加えて、外部NW機器にPingが通ることを確認できれば構築完了です。
[root@c771 ~]# ip add show
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0:  mtu 1450 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 8e:29:cb:fc:87:4a brd ff:ff:ff:ff:ff:ff
    inet 10.244.1.14/24 brd 10.244.1.255 scope global noprefixroute dynamic eth0
       valid_lft 86312966sec preferred_lft 86312966sec
    inet6 fe80::f6a2:e021:e183:9852/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: eth1:  mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 96:e5:96:36:1c:90 brd ff:ff:ff:ff:ff:ff
    inet 192.168.30.139/24 brd 192.168.30.255 scope global noprefixroute dynamic eth1
       valid_lft 2966sec preferred_lft 2966sec
    inet6 fe80::409f:ff:fe56:8741/64 scope link 
       valid_lft forever preferred_lft forever
4: eth2:  mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 9e:46:96:a2:4b:4e brd ff:ff:ff:ff:ff:ff
    inet 192.168.30.138/24 brd 192.168.30.255 scope global noprefixroute dynamic eth2
       valid_lft 2966sec preferred_lft 2966sec
    inet6 fe80::6c:5eff:fee0:56ca/64 scope link 
       valid_lft forever preferred_lft forever

Worker上では以下のように認識されます。

[root@c80g106 ~]# ip link show
  一部省略
6: ens1f0:  mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 90:e2:ba:0b:37:b8 brd ff:ff:ff:ff:ff:ff
    vf 0 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 1 MAC 96:e5:96:36:1c:90, vlan 300, spoof checking on, link-state auto, trust off, query_rss off
    vf 2 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 3 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 4 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 5 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 6 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 7 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
7: ens1f1:  mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 90:e2:ba:0b:37:b9 brd ff:ff:ff:ff:ff:ff
    vf 0 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 1 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 2 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 3 MAC 9e:46:96:a2:4b:4e, vlan 300, spoof checking on, link-state auto, trust off, query_rss off
    vf 4 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 5 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 6 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off
    vf 7 MAC 00:00:00:00:00:00, spoof checking on, link-state auto, trust off, query_rss off

7-3.Podのデプロイ
vi /root/sriov-network-device-plugin/deployments/pod2.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod2
  annotations:
    k8s.v1.cni.cncf.io/networks: sriov-net2
spec:
  containers:
  - name: pod2
    image: docker.io/centos/tools:latest
    command:
    - /sbin/init
    resources:
      requests:
        intel.com/intel_sriov_netdevice2: '1'
      limits:
        intel.com/intel_sriov_netdevice2: '1'

kubectl create -f /root/sriov-network-device-plugin/deployments/pod2.yaml

<出力例>

[root@c80g105 ~]# kubectl get pods -o wide
NAME                       READY   STATUS    RESTARTS   AGE   IP            NODE            NOMINATED NODE   READINESS GATES
pod2                       1/1     Running   1          11h   10.244.1.19   c80g106.md.jp              
virt-launcher-c771-cp2nk   2/2     Running   0          23m   10.244.1.27   c80g106.md.jp              

<補足>
ポイントを3点ほど。

  1. annotations: > k8s.v1.cni.cncf.io/networks: に先ほど作成したCRDの名前sriov-net2を指定している点です。これにより、明示的にX540を使用する設定*17となっています。
  2. resources: > requests: に intel.com/intel_sriov_netdevice2を指定している点です。また「'1'」はVFの数を示していますが、2や3と増やしても、VFは1つしかアサインされませんでした。*18
  3. SR-IOVにてアサインされたNICが保持するIPアドレスは、以下のコマンドで確認できます。net1のMACアドレスがWorkerのVFのMACアドレスと一致していることを確認してください。加えて、外部NW機器にPingが通ることを確認できれば構築完了です。
kubectl exec -it pod2 -- ip addr show

[root@c80g105 ~]# kubectl exec -it pod2 -- ip addr show
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
3: eth0@if31:  mtu 1450 qdisc noqueue state UP group default
    link/ether 0a:56:43:b8:ad:f5 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.1.19/24 scope global eth0
       valid_lft forever preferred_lft forever
24: net1:  mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 2e:f2:70:b7:e9:ca brd ff:ff:ff:ff:ff:ff
    inet 192.168.30.6/24 brd 192.168.30.255 scope global net1
       valid_lft forever preferred_lft forever

Worker上では以下のように認識されます。

[root@c80g106 ~]# ip link show
  一部省略
8: ens2f0:  mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether a0:36:9f:3e:70:d4 brd ff:ff:ff:ff:ff:ff
    vf 0 MAC 4e:bc:dc:62:a8:7e, spoof checking on, link-state auto, trust off, query_rss off
    vf 1 MAC ee:c9:15:96:2d:5d, spoof checking on, link-state auto, trust off, query_rss off
    vf 2 MAC c2:82:b1:0f:ca:2e, spoof checking on, link-state auto, trust off, query_rss off
    vf 3 MAC 86:e2:0a:d6:96:ff, spoof checking on, link-state auto, trust off, query_rss off
    vf 4 MAC 6e:5e:70:e8:da:48, spoof checking on, link-state auto, trust off, query_rss off
    vf 5 MAC f2:2f:55:d8:b3:40, spoof checking on, link-state auto, trust off, query_rss off
    vf 6 MAC ae:a8:e8:48:25:2c, spoof checking on, link-state auto, trust off, query_rss off
    vf 7 MAC da:d8:2a:7d:2f:5b, spoof checking on, link-state auto, trust off, query_rss off
9: ens2f1:  mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether a0:36:9f:3e:70:d6 brd ff:ff:ff:ff:ff:ff
    vf 0 MAC ba:73:6f:81:ea:31, spoof checking on, link-state auto, trust off, query_rss off
    vf 1 MAC 0a:24:c7:34:26:ec, spoof checking on, link-state auto, trust off, query_rss off
    vf 2 MAC d6:62:b0:f5:01:fc, spoof checking on, link-state auto, trust off, query_rss off
    vf 3 MAC a2:05:32:03:eb:d4, spoof checking on, link-state auto, trust off, query_rss off
    vf 4 MAC b2:46:0e:e4:93:90, spoof checking on, link-state auto, trust off, query_rss off
    vf 5 MAC 76:d6:8a:9f:1a:84, spoof checking on, link-state auto, trust off, query_rss off
    vf 6 MAC 2e:f2:70:b7:e9:ca, vlan 300, spoof checking on, link-state auto, trust off, query_rss off
    vf 7 MAC 8e:c0:94:c2:98:f9, spoof checking on, link-state auto, trust off, query_rss off
7-4.VMIとPodの差異について

以下にまとめます。

8086:10ed X520 VMI用 vfio-pci
8086:1515 X540 Pod用 ixgbevf

VMI用vfio-pciについて

  • worker上でip add showを打っても、vfio-pci上で動作しているため、blacklist登録時と同様にVFのNICは表示されません。このためip link showでMACアドレスを確認します。
  • VMIによってVFが掴まれていないとき、MACアドレスは00:00:00:00:00:00が正常です。
  • 今回の構成では、IPアドレスは外部NW機器*19にてDHCPサーバを立ててIPアドレスアサインしています。

Pod用ixgbevfについて

  • ixgbevfにて動作しているため、ip add showにてVFのNICが表示されます。
  • PodによってVFが掴まれているか否かに関わらず、任意のMACアドレスアサインされます。そのMACアドレスはそのままPodが継承します。
  • IPアドレスは(恐らく)MultusからDHCPのように自動でアサインされています。
  • アドレスレンジを指定する場合は以下のようにCRDに追記してください。
vi /root/sriov-network-device-plugin/deployments/sriov-crd2.yaml

[root@c80g105 ~]# vi /root/sriov-network-device-plugin/deployments/sriov-crd2.yaml

apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: sriov-net2
  annotations:
    k8s.v1.cni.cncf.io/resourceName: intel.com/intel_sriov_netdevice2
spec:
  config: '{
  "type": "sriov",
  "cniVersion": "0.3.1",
  "name": "sriov300_2",
  "vlan": 300,
  "ipam": {
    "type": "host-local",
    "subnet": "192.168.30.0/24",
    "rangeStart": "192.168.30.64",
    "rangeEnd": "192.168.30.127",
    "routes": [{
      "dst": "0.0.0.0/0"
    }],
    "gateway": "192.168.30.254"
  }
}'
7-5.VMIとPodのdescribe情報*20

VMIのdescribe
以下のコマンドで確認できます。実際の出力はここにアップしておきます。

kubectl describe pods virt-launcher-c771-cp2nk

Podのdescribe
以下のコマンドで確認できます。実際の出力はここにアップしておきます。

kubectl describe pods pod2

VMIのlog
VMIが正常起動しないときやログインできないときは、以下のログを確認してみてください。
膨大な量のログが出力されるので、ここにアップしておきます。

kubectl logs virt-launcher-c771-cp2nk compute

以上です。

8.最後に

以下のサイトを参考にさせて頂きました。
GitHub - k8snetworkplumbingwg/multus-cni
GitHub - k8snetworkplumbingwg/sriov-cni: DPDK & SR-IOV CNI plugin
GitHub - k8snetworkplumbingwg/sriov-network-device-plugin: SRIOV network device plugin for Kubernetes
https://kubevirt.io/user-guide/docs/latest/creating-virtual-machines/interfaces-and-networks.html
Kubernetes: Multus + SRIOV quickstart – Zenghui Shi

今回はSR-IOVでしたが、次回はOvS-DPDKなどにも挑戦できればと考えています。
f:id:metonymical:20191215111811p:plain

Kubernetesのネットワーク構成といった場合、flannelやCalicoの解説サイトがたくさんあったため、とても助かりました。
しかし、SR-IOVに関してVMIとPodの両方を解説しているサイトが皆無に等しかったため今回の記事を書きました。

今のネットワークのトレンドとしては、P4やeBPF+XDP、SRv6といった辺りかなと思っています。
このため、まだまだ私は遅れ気味だなと日々痛感しておりますが、これでようやく足元ぐらいに辿り着けていられればいいなぁ、と思っています。

また、クラウドネイティブといったキーワードが頻出していますが、実際に自分で手を動かしていじってみた結果、クラウドネイティブであることの意味とか意義とか優位性というモノが、理解できたような気がします。

今回、Podに加えて、KubeVirt上のVMIについても記載しましたが、私の個人的な感想として、一定の需要がありそうな気がしました。

というのも、
いきなり一足飛びでコンテナ化できない or コンテナ化するにはハードルが高いといったケースを鑑みるに、libvirtd上で仮想マシンを稼働させながらも、アーキテクチャk8sに(クラウドネイティブ化したシステムとして)統一したい、といった要件を満たせるかもしれないと思いました。

Migrate for Anthosなどは、まさしくこういった課題を解決するために提供され始めたのかな、という気がします。
Migrate for Anthos  |  Google Cloud

*1:おさらい程度に読み飛ばしてください。

*2:今回は無効化してしまいますが、本来はあまり良くないです。

*3:Masterは不要ですが念のため

*4:名前解決できるようにしておいてください。

*5:このため今回はNICを2枚挿しにしています。

*6:これに気付くまでに2週間程度掛かりました。。。

*7:kubectl get pods上ではrunningになりますが。

*8:virshのようにStart & StopやConsoleログインができます。

*9:私の環境では、tar.gzをDLするとハッシュ値が異なるというエラーで弾かれてしまったためインストールしていません。最近、KubeVirtがv0.24.0にアップデートされた影響かもしれません。

*10:Multusが3番目なのですが、先にインストールしても大丈夫かなと思っています。

*11:Master&Workerにて、それぞれsriov-device-pluginを起動させるため、5-3 & 5-4にて、Docker Imageのビルドをしています。

*12:Cloud Initからイメージファイルを落とした場合、証明書認証が必要となるため、公開鍵を仕込む必要がありますので、私は自前でqcow2ファイルを作っています。

*13:即ち、vfio-pciを使用する設定

*14:これに気付くのに1週間程度掛かりました。。

*15:flannelからアサインされるIP

*16:これの理由は未だによくわかっていません。。

*17:即ち、ixgbevfを使用する設定

*18:この辺りの仕様が正直よくわかっていません。。

*19:構成図右側のスイッチ

*20:何度か作り直しをしているためIPやMACなどが一部異なっていると思います。