ElastiFlowとKibanaを組み合わせてNetFlowのデータを分析してみる

目次

概要

今回は、ElastiFlow 環境を構築してネットワークのフロー情報の収集と可視化を行う環境を構築してみます。
OS は Ubuntu22.04 LTS です。

ElastiFlowとは

ElastiFlow とは、ネットワークのフロー情報を解析するためのツールの1つです。
OSS として提供されており、データの蓄積と解析を行う ElasticSearch 、データを可視化する WebUI を提供する Kibana を合わせて使用することでフロー情報の取得から解析・可視化を行うことができます。
この ElasticSearch に対して収集したデータを入れるツールとして以前は logstash が用いられていたようですが、今は ElastiFlow に内包された ElastiFlow Unified Flow Collector によってデータの収集と ElasticSearch 用データ形式への正規化が行われています。

準備

最初に、構築にあたって動作に必要なスペックを確認しておく必要があります。
フロー情報の分析などにそこそこの計算資源を消費しますので、公式サイトを参考にマシンスペックを決定します。

また、必要になる基本的なツールをインストールしておきます。

sudo apt install apt-transport-https unzip

Linux Kernelのチューニング

ElasticSearch はデフォルトで mmapfs ディレクトリを使用してデータを保存しますが、Linux のデフォルトの制限値が低すぎるためこの制限を 262114 に引き上げておく必要があります。

echo "vm.max_map_count=262144" | sudo tee /etc/sysctl.d/70-elasticsearch.conf > /dev/null

また、ネットワーク関係のパラメーターについても変更を行います。
デフォルトでは大量の UDP パケットの受信に最適化されておらずパケットがドロップされる可能性があるため、チューニングを行っておく必要があります。

echo -e "net.core.netdev_max_backlog=4096\nnet.core.rmem_default=262144\nnet.core.rmem_max=67108864\nnet.ipv4.udp_rmem_min=131072\nnet.ipv4.udp_mem=2097152 4194304 8388608" | sudo tee /etc/sysctl.d/60-net.conf > /dev/null

これらの設定を適用するためにOSを再起動します。

【補足】再起動せずに適用する(一時的な適用)
sudo sysctl -w vm.max_map_count=262144
sudo sysctl -w net.core.netdev_max_backlog=4096
sudo sysctl -w net.core.rmem_default=262144
sudo sysctl -w net.core.rmem_max=67108864
sudo sysctl -w net.ipv4.udp_rmem_min=131072
sudo sysctl -w net.ipv4.udp_mem='2097152 4194304 8388608'

ElasticSearchのインストール

最初に、ElasticSearch の PGP キーを追加します。

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -

続いて Elastic のリポジトリを追加します。

echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list > /dev/null

リポジトリの追加が出来たら ElasticSearch をインストールします。

sudo apt update
sudo apt install elasticsearch

ElasticSearchの設定

インストールが完了したら、ElasticSearch の設定を行います。

初めに、JVM のヒープサイズを設定します。
使用中に JVM のヒープサイズが動的に変更された場合にプロセスが一時停止する場合があるためです。
初期ヒープサイズと最大ヒープサイズを同じサイズに設定しておくことで回避することができます。
なお、今回は 12GB を割り当てていますが、システムのメモリ量の 1/3 の量に設定することが望ましいようですので、各自の環境に合わせて変更してください。

echo -e "-Xms12g\n-Xmx12g" | sudo tee /etc/elasticsearch/jvm.options.d/heap.options > /dev/null

続いて、systemd の設定ファイル内でシステム制限の引き上げを行います。

sudo mkdir /etc/systemd/system/elasticsearch.service.d
echo -e "[Service]\nLimitNOFILE=131072\nLimitNPROC=8192\nLimitMEMLOCK=infinity\nLimitFSIZE=infinity\nLimitAS=infinity" | sudo tee /etc/systemd/system/elasticsearch.service.d/elasticsearch.conf > /dev/null

ElasticSearch への接続に使用する証明書の生成を行います。
この処理を簡素にするために elasticsearch-certutil と呼ばれるツールが用意されていますのでこちらを利用します。
最初に CA を生成します。

sudo /usr/share/elasticsearch/bin/elasticsearch-certutil ca --pem

Please enter the desired output file [elastic-stack-ca.zip]: が表示されたらそのまま Enter を押下しデフォルトのまま進みます。
生成されたファイルは /usr/share/elasticsearch に配置されます。

生成されたファイルを解凍して移動します。

sudo mkdir /etc/elasticsearch/certs
sudo unzip /usr/share/elasticsearch/elastic-stack-ca.zip -d /etc/elasticsearch/certs

続いて、証明書を生成するのに必要な情報を記載したファイル /usr/share/elasticsearch/instances.yml 作成します。
このファイルに次の内容を書き込みます。 nameipdns の値は各自の環境に合わせて変更する必要があります。
なお、 dns についてはDNS名が無い場合は項目ごと設定しないでおくことができます。(下から2行分を削除)

instances:
  - name: "hostname" 
    ip: 
      - "192.168.1.2"
    dns: 
      - "hostname.dns.local"

先ほどの生成ツールを利用して証明書を生成します。

sudo /usr/share/elasticsearch/bin/elasticsearch-certutil cert --silent --in instances.yml --out certs.zip --pem --ca-cert /etc/elasticsearch/certs/ca/ca.crt --ca-key /etc/elasticsearch/certs/ca/ca.key

生成された ZIP ファイルを再度解凍しておきます。

sudo unzip /usr/share/elasticsearch/certs.zip -d /etc/elasticsearch/certs

設定ファイルを編集します。
設定ファイルは /etc/elasticsearch/elasticsearch.yml に存在します。
xpack.security.http.ssl.keyxpack.security.http.ssl.certificate にある証明書のパスは必ず各自の環境に合わせて変更しておく必要があります。
その他項目についても設定を変更する必要がある場合は調整してください。

cluster.name: elastiflow

path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch

bootstrap.memory_lock: true

network.host: 0.0.0.0
http.port: 9200

discovery.type: 'single-node'

indices.query.bool.max_clause_count: 8192
search.max_buckets: 250000

action.destructive_requires_name: 'true'

xpack.security.http.ssl.enabled: 'true'
xpack.security.http.ssl.verification_mode: 'none'
xpack.security.http.ssl.certificate_authorities: /etc/elasticsearch/certs/ca/ca.crt
xpack.security.http.ssl.key: /etc/elasticsearch/certs/hostname/myhohostnamest.key
xpack.security.http.ssl.certificate: /etc/elasticsearch/certs/hostname/hostname.crt

xpack.monitoring.enabled: 'true'
xpack.monitoring.collection.enabled: 'true'
xpack.monitoring.collection.interval: 30s

xpack.security.enabled: 'true'
xpack.security.audit.enabled: 'false'

設定ファイルの編集が完了したら ElasticSearch サービスを起動します。
なお、初期状態ではOS起動時に自動的に起動する設定にはなっていないため、合わせて設定しておきます。

sudo systemctl daemon-reload
sudo systemctl enable elasticsearch
sudo systemctl start elasticsearch

ElasticSearch の各種パスワードを設定します。

sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive

インタラクティブ形式で設定項目が出てきますので、それぞれ設定します。

ここまで完了したら、curl コマンドを利用して正しく動作しているか確認します。
<PASSWORD> は適宜変更してください。
下記のようなレスポンスが返ってくれば正しく動作しています。
レスポンスエラーになる場合は証明書のパス設定が正しいか、ElasticSearch が起動しているかなどを再度確認してみてください。

$ curl -XGET -k "https://elastic:<PASSWORD>@127.0.0.1:9200"
{
  "name" : "elastiflow",
  "cluster_name" : "elastiflow",
  "cluster_uuid" : "askEKLTJgskl4wNKL",
  "version" : {
    "number" : "7.17.6",
    "build_flavor" : "default",
    "build_type" : "deb",
    "build_hash" : "f65e9d338dc1d07b642e14a27f338990148ee5b6",
    "build_date" : "2022-08-23T11:08:48.893373482Z",
    "build_snapshot" : false,
    "lucene_version" : "8.11.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

Kibanaのインストール

続いて Kibana をインストールしていきます。

sudo apt update
sudo apt install -y kibana

先ほど作成した ElasticSearch の証明書をコピーします。

sudo cp -r /etc/elasticsearch/certs /etc/kibana

Kibana の設定ファイルを編集します。
設定ファイルは /etc/kibana/kibana.yml に存在します。
環境に合わせて変更する必要があるのは次の箇所です。

  • server.publicBaseUrl
  • server.ssl.key
  • server.ssl.certificate
  • elasticsearch.hosts
    • localhost では証明書エラーで接続できませんので必ずIPで設定します。
  • elasticsearch.password
  • elasticsearch.ssl.key
  • elasticsearch.ssl.certificate
  • xpack.encryptedSavedObjects.encryptionKey
    • 変更しなくても動作しますがランダム文字列に変えておくとよいでしょう。
telemetry.enabled: false
telemetry.optIn: false
newsfeed.enabled: false

server.host: '0.0.0.0'
server.port: 5601
server.maxPayload: 8388608
server.publicBaseUrl: 'https://192.168.2.1:5601'

server.ssl.enabled: true
server.ssl.certificateAuthorities: /etc/kibana/certs/ca/ca.crt
server.ssl.key: /etc/kibana/certs/myhost/myhost.key
server.ssl.certificate: /etc/kibana/certs/myhost/myhost.crt

elasticsearch.hosts: ['https://192.168.2.1:9200']
elasticsearch.username: 'kibana_system'
elasticsearch.password: 'PASSWORD'
elasticsearch.ssl.certificateAuthorities: /etc/kibana/certs/ca/ca.crt
elasticsearch.ssl.key: /etc/kibana/certs/myhost/myhost.key
elasticsearch.ssl.certificate: /etc/kibana/certs/myhost/myhost.crt
elasticsearch.ssl.verificationMode: 'certificate'

elasticsearch.requestTimeout: 132000
elasticsearch.shardTimeout: 120000

kibana.autocompleteTimeout: 2000
kibana.autocompleteTerminateAfter: 500000

monitoring.enabled: true
monitoring.kibana.collection.enabled: true
monitoring.kibana.collection.interval: 30000

monitoring.ui.enabled: true
monitoring.ui.min_interval_seconds: 20

xpack.maps.showMapVisualizationTypes: true

xpack.security.enabled: true
xpack.security.audit.enabled: false

xpack.encryptedSavedObjects.encryptionKey: 'THIS_IS_A_RANDOM_STRINGS'

Kibana を起動します。
こちらも ElasticSearch と同様に初期状態ではOS起動時に自動的に起動する設定にはなっていないので合わせて設定しておきます。

sudo systemctl daemon-reload
sudo systemctl enable kibana
sudo systemctl start kibana

サービスが正常に起動しているか確認します。

sudo systemctl status kibana

正常に起動できていれば、ブラウザで https://<IP>:5601 にアクセスするとログイン画面が表示されます。
証明書エラーが表示されますが、自己証明書を使用しているためです。自己責任で続行することができます。

ElastiFlow Unified Flow Collectorのインストール

続いて、NetFlow のデータを利用するために Elastic Unified Flow Collector をインストールします。

wget https://elastiflow-packages.s3.amazonaws.com/flow-collector/flow-collector_5.6.0_linux_amd64.deb
sudo apt install ./flow-collector_5.6.0_linux_amd64.deb

ElastiFlow Unified Flow Collectorの設定

コレクターの設定をしていきます。

まずは、証明書をコピーして利用できるようにします。

sudo mkdir /etc/elastiflow/ca
sudo cp /etc/elasticsearch/certs/ca/ca.crt /etc/elastiflow/ca

続いて、せっかくなので無償で使用できる Basic ライセンスを取得しておきます。
こちらのページからリクエストすることができます。
ライセンスをリクエストすると30分程度でライセンス情報が記載されたメールが届きます。

ライセンスを取得出来たら、必要な設定をしていきます。
設定ファイルは /etc/systemd/system/flowcoll.service.d/flowcoll.conf に存在しています。
必要な項目をコメントアウトするなどして編集します。

ライセンス関連

EF_FLOW_LICENSED_UNITS はライセンス形態により異なりますが今回は BASIC ライセンスですので 1 を設定します。

Environment="EF_FLOW_ACCOUNT_ID=<メールに記載のあるACCOUNT_ID>"
Environment="EF_FLOW_LICENSE_KEY=<メールに記載のあるLICENSE_KEY>"
Environment="EF_FLOW_LICENSED_UNITS=1"
ネットワーク関連

本来は 0.0.0.0 で良いはずなのですが、私の環境ではその設定では IPv6 でしか Listen してくれなかったので明示的に IPv4 インターフェースのアドレスを指定しておきます。

Environment="EF_FLOW_SERVER_UDP_IP=192.168.1.2"
ElasticSearch関連

下記項目は環境に合わせて変更します。

  • EF_FLOW_OUTPUT_ELASTICSEARCH_ADDRESSES
  • EF_FLOW_OUTPUT_ELASTICSEARCH_PASSWORD
Environment="EF_FLOW_OUTPUT_ELASTICSEARCH_ENABLE=true"
Environment="EF_FLOW_OUTPUT_ELASTICSEARCH_ECS_ENABLE=true"
Environment="EF_FLOW_OUTPUT_ELASTICSEARCH_TIMESTAMP_SOURCE=end"
Environment="EF_FLOW_OUTPUT_ELASTICSEARCH_INDEX_TEMPLATE_SHARDS=1"
Environment="EF_FLOW_OUTPUT_ELASTICSEARCH_INDEX_TEMPLATE_REPLICAS=0"
Environment="EF_FLOW_OUTPUT_ELASTICSEARCH_INDEX_PERIOD=rollover"
Environment="EF_FLOW_OUTPUT_ELASTICSEARCH_ADDRESSES=192.168.1.2:9200"
Environment="EF_FLOW_OUTPUT_ELASTICSEARCH_USERNAME=elastic"
Environment="EF_FLOW_OUTPUT_ELASTICSEARCH_PASSWORD=changeme"

なお、 EF_FLOW_OUTPUT_ELASTICSEARCH_TIMESTAMP_SOURCE の設定について詳細が公式ドキュメントに記載されていますので、必要な場合は変更します。

証明書関連

ElastiFlow に使用する証明書の設定を行います。

Environment="EF_FLOW_OUTPUT_ELASTICSEARCH_TLS_ENABLE=true"
Environment="EF_FLOW_OUTPUT_ELASTICSEARCH_TLS_CA_CERT_FILEPATH=/etc/elastiflow/ca/ca.crt"

設定が完了したら ElastiFlow Unifed Flow Collector を起動します。
こちらも初期状態では OS 起動時に起動する設定にはなっていないので、合わせて設定しておきます。

sudo systemctl daemon-reload
sudo systemctl enable flowcoll
sudo systemctl start flowcoll

サービスが正常に起動しているか確認します。

sudo systemctl status flowcoll

GEOIPとASNのデータベース設定

別途データベースをダウンロードすることで GeoIP の情報と ASN(Autonomous System Number) の情報を利用することができます。
最新のデータベースはこちらから取得することができます。

cd /etc/elastiflow/maxmind
wget https://github.com/P3TERX/GeoLite.mmdb/releases/download/2022.09.16/GeoLite2-ASN.mmdb
wget https://github.com/P3TERX/GeoLite.mmdb/releases/download/2022.09.16/GeoLite2-City.mmdb
wget https://github.com/P3TERX/GeoLite.mmdb/releases/download/2022.09.16/GeoLite2-Country.mmdb

続いて、このデータベースを利用するように ElastiFlow の設定ファイルである /etc/systemd/system/flowcoll.service.d/flowcoll.conf を編集します。

Environment="EF_FLOW_DECODER_ENRICH_MAXMIND_ASN_ENABLE=true"
Environment="EF_FLOW_DECODER_ENRICH_MAXMIND_ASN_PATH=maxmind/GeoLite2-ASN.mmdb"
Environment="EF_FLOW_DECODER_ENRICH_MAXMIND_GEOIP_ENABLE=true"
Environment="EF_FLOW_DECODER_ENRICH_MAXMIND_GEOIP_PATH=maxmind/GeoLite2-City.mmdb"
Environment="EF_FLOW_DECODER_ENRICH_MAXMIND_GEOIP_LANG=en"

設定が完了したら反映のため ElastiFlow サービスを再起動しておきます。
なお、適用には daemon-reload を実施する必要があります。

sudo systemctl daemon-reload
sudo systemctl restart flowcoll

Kibanaの設定

Kibana の設定をしていきます。
まずは ElastiFlow 用の Kibana Object をインポートします。
インポートに必要なオブジェクトはここから取得することができます。
今回、ElasticSearch は Version7.17.6、Schema としてデフォルトの CODEX を使用しているので、 kibana-7.14.x-codex-light.ndjson を使用します。
事前にダウンロードしておきます。

まず、ブラウザで https://<IP>:5601 にアクセスしてログインします。
デフォルトのユーザー名は elastic でパスワードは先ほど設定したものを使用します。

ログイン出来たら左側のメニューから Stack Management から Saved Objects を開きます。
画面右上の import から import を押すとファイル選択画面が出てくるので、先ほどダウンロードしたファイルを選択し Import を押します。

しばらくするとインポートされたオブジェクトが表示されます。

NetFlowでフロー情報を流してみる

ここまで設定できれば ElastiFlow の環境構築は一通り完了です。
ルーターに NetFlow の設定をしてフロー情報を流して動作を確認しましょう。
ElastiFlow Unified Flow Collector のデフォルトの NetFlow ポートは 9995 です。

今回は Ubiquiti Networks 社のルーターである EdgeRouter に設定を入れて動作を確認しました。
各自の環境に合わせて設定方法は調べてください。

EdgeRouter への NetFlow 設定の例

set system flow-accounting disable-memory-table
set system flow-accounting ingress-capture pre-dnat
set system flow-accounting interface eth0
set system flow-accounting netflow enable-egress engine-id 1
set system flow-accounting netflow engine-id 0
set system flow-accounting netflow server 192.168.2.1 port 9995
set system flow-accounting netflow timeout expiry-interval 60
set system flow-accounting netflow timeout flow-generic 60
set system flow-accounting netflow timeout icmp 60
set system flow-accounting netflow timeout max-active-life 60
set system flow-accounting netflow timeout tcp-fin 10
set system flow-accounting netflow timeout tcp-generic 60
set system flow-accounting netflow timeout tcp-rst 10
set system flow-accounting netflow timeout udp 60
set system flow-accounting netflow version 9
set system flow-accounting syslog-facility daemon

Dashboardを見てみる

ここまで正しく設定できていれば、Kibana 上でフロー情報が見えるようになるはずです。
いくつかダッシュボード画面を紹介します。

Overview

フロー情報の概要を確認することができます。

TOP-N

通信量順に送信元・宛先の情報を確認することができます。

Flows

フロー情報を Sankey Diagram 形式で確認することができます。

Geo IP

送信元・宛先の GeoIP 情報を確認することができます。

AS Traffic

送信元・宛先の ASN 情報を確認することができます。

まとめ

今回は、ElastiFlow 環境を構築してネットワークのフロー情報の可視化環境を構築しました。
こういったフロー情報を可視化するツールが OSS で無償で利用できるのはとても便利です。
アクセス元の国や ASN を確認できることで、サーバーの利用状況などを確認することができます。

おうちで始めるNVMe over TCP入門

目次

概要

前回、NVMe over RDMA 環境を構築し、簡単な動作確認を行いました。
今回は、前回の概要でも触れたとおりNVMe over TCP を試してみます。

NVMe over TCP はその名の通り、NVMe プロトコルを TCP プロトコル上に流すことでリモートでの利用を可能にしたものです。
NVMe over RDMA (RoCEv2) と異なり、一般的な TCP プロトコルを使用しているため専用のスイッチなどを必要としないメリットがあります。
しかしながら、メモリ上のデータを直接扱う RDMA の恩恵を受けられないため、プロトコルスタックの処理によるCPU負荷の上昇やIOに対する遅延の増大、スループットの低下などのデメリットも存在します。

今回はそんな NVME over TCP 環境を構築し、動作確認を行ってみます。

環境

今回の検証に使用した環境です。
マシンは2台用意し、それぞれをターゲット(Server)側、Initiator(Client)側としています。

ターゲット側

詳細
Machine HPE ProLiant DL380 Gen10
CPU Intel Xeon Silver 4208 x2
Memory DDR4 ECC RDIMM 128GB (16GB x8 2400MHz)
NIC Mellanox ConnectX-4 100Gb(Ethernet) MCX415A-CCAT
NVMe Intel DC P4600 1.6TB U.2 x2
OS Ubuntu Server 22.04 LTS

Initiator側

詳細
Machine Fujitsu Primergy RX2540 M4
CPU Intel Xeon Gold 6148 x1
Memory DDR4 ECC RDIMM 128GB (32GB x4 2666MHz)
NIC Mellanox ConnectX-4 100Gb(Ethernet) MCX415A-CCAT
OS Ubuntu Server 22.04 LTS

準備

今回も、前回と同様に SPDK を用いて環境構築を行います。
SPDK をビルドし nvmf_tgt が実行可能な状態までは前回と同様の手順です。

ネットワーク設定

この段階でネットワーク設定を行っておきます。
手順については省略します。
なお、今回は次のような構成にしています。
なお、リンクスピードは100Gbps、MTUは9000です。

ターゲット側 10.0.0.1/24 ---------- 10.0.0.2/24 Initiator側

nvme-cliのインストール

ターゲット側とInitiator側の両方に nvme-cli をインストールしておきます。

# apt install nvme-cli

ターゲット側の設定

nvmf_tgt を起動した状態で設定を入れていきます。
前回と異なるのは、 nvmf_create_transportnvmf_subsystem_add_listener 設定において TCP を指定している部分です。

[email protected]:~/spdk# ./build/bin/nvmf_tgt
[email protected]:/usr/src/spdk# ./scripts/rpc.py nvmf_create_transport -t TCP -u 16384 -m 8 -c 8192
[email protected]:/usr/src/spdk# scripts/rpc.py bdev_nvme_attach_controller -b NVMe0 -a 0000:39:00.0 -t pcie
NVMe0n1
[email protected]:/usr/src/spdk# scripts/rpc.py bdev_nvme_get_controllers
[
  {
    "name": "NVMe0",
    "ctrlrs": [
      {
        "state": "enabled",
        "trid": {
          "trtype": "PCIe",
          "traddr": "0000:39:00.0"
        },
        "cntlid": 0,
        "host": {
          "nqn": "nqn.2014-08.org.nvmexpress:uuid:d4549310-b740-4edc-bed0-95dbcbec1178",
          "addr": "",
          "svcid": ""
        }
      }
    ]
  }
]
[email protected]:/usr/src/spdk# scripts/rpc.py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001 -d SPDK_Controller1
[email protected]:/usr/src/spdk# scripts/rpc.py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 NVMe0n1
[email protected]:/usr/src/spdk# scripts/rpc.py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t tcp -a 10.0.0.1 -s 4420

なお、詳細な設定内容や複数ディスクへの対応方法などは前回の記事で解説しています。

Initiator側の設定

NVMe over TCP を利用するには nvme-tcp モジュールが必要となり、対応したカーネルに更新する必要があります。
今回は、 linux-modules-extra-5.15.0-1004-gke を利用します。

[email protected]:# apt install linux-modules-extra-5.15.0-1004-gke
[email protected]:# reboot
[email protected]:# mobprobe nvme_tcp

無事に nvme-tcp モジュールを有効化出来たら、nvme discover を実行して検出できるか確認します。

[email protected]:~# nvme discover -t tcp -a 10.0.0.1 -s 4420

Discovery Log Number of Records 1, Generation counter 1
=====Discovery Log Entry 0======
trtype:  tcp
adrfam:  ipv4
subtype: nvme subsystem
treq:    not required
portid:  0
trsvcid: 4420
subnqn:  nqn.2016-06.io.spdk:cnode1
traddr:  10.0.0.1
sectype: none

接続し、認識しているか確認します。

[email protected]:~# nvme connect -t tcp -n "nqn.2016-06.io.spdk:cnode1" -a 10.0.0.1 -s 4420
[email protected]:~# nvme list
Node                  SN                   Model                                    Namespace Usage                      Format           FW Rev
--------------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
/dev/nvme0n1          SPDK00000000000001   SPDK_Controller1                         1           1.60  TB /   1.60  TB    512   B +  0 B   22.09

問題なく認識していることが確認できたので、 fio で簡単にベンチマークしていきます。

fioを用いてベンチマーク

Initiator 側から NVMe に IO 負荷をかけてベンチマークを実施します。
ベンチマークには fio を用います。

[email protected]:~# apt install fio

シーケンシャルリードでベンチマークをまわしてみます。

[email protected]:~# fio --name=seqread --rw=read --filename=/dev/nvme0n1 --direct=1 --ioengine=libaio --bs=32m --numjobs=2 --size=10G --group_reporting
seqread: (g=0): rw=read, bs=(R) 32.0MiB-32.0MiB, (W) 32.0MiB-32.0MiB, (T) 32.0MiB-32.0MiB, ioengine=libaio, iodepth=1
...
fio-3.28
Starting 2 processes
Jobs: 2 (f=2): [R(2)][100.0%][r=3171MiB/s][r=99 IOPS][eta 00m:00s]
seqread: (groupid=0, jobs=2): err= 0: pid=2327: Fri Jul  1 13:32:00 2022
  read: IOPS=98, BW=3167MiB/s (3321MB/s)(20.0GiB/6466msec)
    slat (usec): min=6780, max=27344, avg=12248.84, stdev=2025.29
    clat (usec): min=5220, max=12290, avg=7910.04, stdev=1526.57
     lat (usec): min=15432, max=35796, avg=20160.18, stdev=1641.21
    clat percentiles (usec):
     |  1.00th=[ 5342],  5.00th=[ 5538], 10.00th=[ 5735], 20.00th=[ 6128],
     | 30.00th=[ 6718], 40.00th=[ 7504], 50.00th=[ 8291], 60.00th=[ 8717],
     | 70.00th=[ 8979], 80.00th=[ 9241], 90.00th=[ 9634], 95.00th=[10028],
     | 99.00th=[10552], 99.50th=[10814], 99.90th=[12256], 99.95th=[12256],
     | 99.99th=[12256]
   bw (  MiB/s): min= 3008, max= 3328, per=100.00%, avg=3173.33, stdev=42.02, samples=24
   iops        : min=   94, max=  104, avg=99.17, stdev= 1.31, samples=24
  lat (msec)   : 10=94.06%, 20=5.94%
  cpu          : usr=0.28%, sys=7.61%, ctx=11290, majf=0, minf=16410
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=640,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
   READ: bw=3167MiB/s (3321MB/s), 3167MiB/s-3167MiB/s (3321MB/s-3321MB/s), io=20.0GiB (21.5GB), run=6466-6466msec

Disk stats (read/write):
  nvme0n1: ios=0/0, merge=0/0, ticks=0/0, in_queue=0, util=0.00%

3.3GB/s となっており、これは使用したNVMeの公称値とほぼ同様です。

続いて、シーケンシャルライトを試します。

[email protected]:~# fio --name=seqwrite --rw=write --filename=/dev/nvme0n1 --direct=1 --ioengine=libaio --bs=32m --numjobs=2 --size=10G --group_reporting
seqwrite: (g=0): rw=write, bs=(R) 32.0MiB-32.0MiB, (W) 32.0MiB-32.0MiB, (T) 32.0MiB-32.0MiB, ioengine=libaio, iodepth=1
...
fio-3.28
Starting 2 processes
Jobs: 2 (f=2): [W(2)][100.0%][w=1280MiB/s][w=40 IOPS][eta 00m:00s]
seqwrite: (groupid=0, jobs=2): err= 0: pid=2372: Fri Jul  1 13:32:35 2022
  write: IOPS=38, BW=1248MiB/s (1308MB/s)(20.0GiB/16414msec); 0 zone resets
    slat (usec): min=7971, max=51897, avg=27101.64, stdev=4912.11
    clat (usec): min=10756, max=46502, avg=24141.04, stdev=5077.53
     lat (usec): min=25093, max=79939, avg=51243.83, stdev=6243.81
    clat percentiles (usec):
     |  1.00th=[14484],  5.00th=[16909], 10.00th=[18482], 20.00th=[20055],
     | 30.00th=[21103], 40.00th=[22676], 50.00th=[23725], 60.00th=[24773],
     | 70.00th=[25822], 80.00th=[27395], 90.00th=[31065], 95.00th=[33817],
     | 99.00th=[39584], 99.50th=[40633], 99.90th=[46400], 99.95th=[46400],
     | 99.99th=[46400]
   bw (  MiB/s): min=  960, max= 1408, per=99.86%, avg=1246.00, stdev=60.59, samples=64
   iops        : min=   30, max=   44, avg=38.94, stdev= 1.89, samples=64
  lat (msec)   : 20=19.69%, 50=80.31%
  cpu          : usr=3.62%, sys=4.22%, ctx=11075, majf=0, minf=19
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,640,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
  WRITE: bw=1248MiB/s (1308MB/s), 1248MiB/s-1248MiB/s (1308MB/s-1308MB/s), io=20.0GiB (21.5GB), run=16414-16414msec

Disk stats (read/write):
  nvme0n1: ios=0/0, merge=0/0, ticks=0/0, in_queue=0, util=0.00%

こちらも 1.3GB/s となっており公称値と同等です。

まとめ

今回は、前回作成した環境と SPDK を利用して、NVMe over TCP 環境を構築し、簡単なパフォーマンス測定を行いました。
シーケンシャルリード/ライトにおいてはスループット面では特に問題はないようです。
次回、NVMe over RDMA と NVMe over TCP の詳細なパフォーマンス測定を行う予定です。

NVMeとConnectX-4で始めるNVMe over RDMA (RoCEv2)入門

目次

概要

近年、NVMe over Fabric (=NVMeoF) という言葉をよく聞くようになりました。
この Fabric 自体は NVMe のプロトコルをネットワーク上に流すことでリモートホスト上からターゲットホストの NVMe Disk に直接IO操作を行うものです。
同じような目的を持ったプロトコルとしては iSCSI などがあげられます。
しかし、SCSI 自体が近年目覚ましく進化したフラッシュストレージ (=SSD) の性能に追いつけていないのが現状です。
この問題に対処するため、より効率的なプロトコルとして新たに NVMe プロトコルが策定されました。
NVMe over Fabric は効率的な NVMe プロトコルを利用することにより iSCSI よりも高速にIOを裁くことができるメリットがあり、注目されています。

NVMe over Fabric は通信に使用するプロトコルとして Infiniband や Ethernet 、さらには FibreChannel を選択することができます。
加えて、RDMA(=Remote Direct Memory Access) などの技術を用いることでより高速・低遅延を実現しています。

RDMA 自体は Infiniband の技術です。
しかし、RDMA を Ethernet 上で利用するための技術として、RoCE (RDMA over Converged Ethernet) があります。
この RoCE は v1 と v2 が存在し、RoCEv1 は Link Layer のプロトコルであるため異なるネットワークセグメント間において接続することができませんでした。
しかし RoCEv2 では Internet Layer のプロトコルとなったため、ルーティングが可能となり、異なるネットワークセグメント間においても使用することができるようになりました。

まとめると、低遅延でかつより高速にリモートホスト上の NVMe にアクセスするための技術として、NVMe over RDMA があり、これを Ethernet 上で利用するために RoCEv2を用いる必要があるということです。

また、RoCE 自体は途中経路上のスイッチにも設定が必要です。
このため、RDMA を用いずに TCP で直接接続するプロトコルとして、NVMe over TCP が存在します。
しかし、Mellanox (NVIDIA) の ConnectX-4 以降においてはスイッチの設定を不要とする Zero Touch RoCE が使用でき、より簡単に環境構築が可能となりました。

今回はそんな ConnectX-4 を用いながら、実際に NVMe over RDMA (RoCEv2) な環境を構築し、軽くパフォーマンスなどを見ていきます。
(NVMe over TCPについては後日検証します。)

環境

今回の検証に使用した環境です。
マシンは2台用意し、それぞれをターゲット(Server)側、Initiator(Client)側としています。

ターゲット側

詳細
Machine HPE ProLiant DL380 Gen10
CPU Intel Xeon Silver 4208 x2
Memory DDR4 ECC RDIMM 128GB (16GB x8 2400MHz)
NIC Mellanox ConnectX-4 100Gb(Ethernet) MCX415A-CCAT
NVMe Intel DC P4600 1.6TB U.2 x2
OS Ubuntu Server 22.04 LTS

Initiator側

詳細
Machine Fujitsu Primergy RX2540 M4
CPU Intel Xeon Gold 6148 x1
Memory DDR4 ECC RDIMM 128GB (32GB x4 2666MHz)
NIC Mellanox ConnectX-4 100Gb(Ethernet) MCX415A-CCAT
OS Ubuntu Server 22.04 LTS

Mellanoxドライバのインストール

事前に、RDMAに対応した Mellanox ドライバである OFED をインストールしておく必要があります。
ドライバは公式サイトからダウンロードできます。

# tar -xzvf MLNX_OFED_LINUX-5.6-2.0.9.0-ubuntu22.04-x86_64.tgz
# cd MLNX_OFED_LINUX-5.6-2.0.9.0-ubuntu22.04-x86_64
# ./mlnxofedinstall --with-nvmf
# /etc/init.d/openibd restart

インストール完了後、念のために initramfs を更新しておきます。
しなかった場合、この後に行うカーネルモジュールの読み込みでエラーになる場合があります。

# update-initramfs -u
# reboot

RoCEv2サポートの確認

念のため、公式サイトを参考にNICがRoCEv2に対応していることを確認しておきます。
ネットワークインタフェース名を確認しておきます。

# cat /sys/class/infiniband/mlx5_0/ports/1/gid_attrs/ndevs/0
ens5np0
# cat /sys/class/infiniband/mlx5_0/ports/1/gid_attrs/ndevs/1
ens5np0

今回は 0 と 1 が該当するものでした。
続いて、確認した情報をもとにサポートしているかどうかを確認します。

# cat /sys/class/infiniband/mlx5_0/ports/1/gid_attrs/types/0
IB/RoCE v1
# cat /sys/class/infiniband/mlx5_0/ports/1/gid_attrs/types/1
RoCE v2

RoCE v2をサポートしていることが確認できました。

カーネルモジュールのロード

ターゲット側、Initiator側の両方でカーネルモジュールを読み込んでおきます。

# modprobe mlx5_core
# lsmod | grep mlx
mlx5_ib               434176  0
ib_uverbs             135168  6 rdma_ucm,mlx5_ib
ib_core               417792  8 rdma_cm,ib_ipoib,iw_cm,ib_umad,rdma_ucm,ib_uverbs,mlx5_ib,ib_cm
mlx5_core            1888256  1 mlx5_ib
mlxdevm               172032  1 mlx5_core
mlxfw                  32768  1 mlx5_core
psample                20480  1 mlx5_core
tls                   106496  2 bonding,mlx5_core
mlx_compat             69632  11 rdma_cm,ib_ipoib,mlxdevm,iw_cm,ib_umad,ib_core,rdma_ucm,ib_uverbs,mlx5_ib,ib_cm,mlx5_core
pci_hyperv_intf        16384  1 mlx5_core
# modprobe nvmet
# modprobe nvmet-rdma
# modprobe nvme-rdma
# lsmod | grep nvme
nvme_rdma              40960  0
nvme_fabrics           24576  1 nvme_rdma
nvmet_rdma             57344  0
nvmet                 135168  1 nvmet_rdma
rdma_cm               122880  3 nvme_rdma,nvmet_rdma,rdma_ucm
ib_core               417792  10 rdma_cm,ib_ipoib,nvme_rdma,nvmet_rdma,iw_cm,ib_umad,rdma_ucm,ib_uverbs,mlx5_ib,ib_cm
nvme                   49152  2 nvmet,nvmet_rdma
nvme_core             122880  4 nvmet,nvme,nvme_rdma,nvme_fabrics
mlx_compat             69632  17 rdma_cm,ib_ipoib,mlxdevm,nvmet,nvme,nvme_rdma,nvmet_rdma,iw_cm,nvme_core,nvme_fabrics,ib_umad,ib_core,rdma_ucm,ib_uverbs,mlx5_ib,ib_cm,mlx5_core

ネットワーク設定

この段階でネットワーク設定を行っておきます。
手順については省略します。
なお、今回は次のような構成にしています。

ターゲット側 10.0.0.1/24 ---------- 10.0.0.2/24 Initiator側

nvme-cliのインストール

ターゲット側とInitiator側の両方に nvme-cli をインストールしておきます。

# apt install nvme-cli

ターゲット側の設定

今回は、ターゲット側のソフトウェアとして SPDK (=Storage Performance Development Kit) を用います。
git から clone してビルドします。

[email protected]:~# git clone https://github.com/spdk/spdk
[email protected]:~# cd spdk/
[email protected]:~/spdk# git submodule update --init
[email protected]:~/spdk# scripts/pkgdep.sh --rdma
[email protected]:~/spdk# ./configure --with-rdma --enable-debug
[email protected]:~/spdk# make

テストを実施しておきます。
All unit tests passed の表示を確認できればOKです。

[email protected]:~/spdk# ./test/unit/unittest.sh
<省略>
=====================
All unit tests passed
=====================
WARN: lcov not installed or SPDK built without coverage!
WARN: neither valgrind nor ASAN is enabled!

テストをパスしたことが確認出来たら、NVMeドライバの変更を行います。
変更には、SPDK 内の setup.sh を用います。
先に現在の状態を確認します。

[email protected]:~/spdk# scripts/setup.sh status
Hugepages
node     hugesize     free /  total
node0   1048576kB        0 /      0
node0      2048kB        0 /      0
node1   1048576kB        0 /      0
node1      2048kB        0 /      0

Type     BDF             Vendor Device NUMA    Driver           Device     Block devices
I/OAT    0000:00:04.0    8086   2021   0       ioatdma          -          -
I/OAT    0000:00:04.1    8086   2021   0       ioatdma          -          -
I/OAT    0000:00:04.2    8086   2021   0       ioatdma          -          -
I/OAT    0000:00:04.3    8086   2021   0       ioatdma          -          -
I/OAT    0000:00:04.4    8086   2021   0       ioatdma          -          -
I/OAT    0000:00:04.5    8086   2021   0       ioatdma          -          -
I/OAT    0000:00:04.6    8086   2021   0       ioatdma          -          -
I/OAT    0000:00:04.7    8086   2021   0       ioatdma          -          -
NVMe     0000:39:00.0    8086   0a54   0       nvme             nvme0      nvme0n1
NVMe     0000:3a:00.0    8086   0a54   0       nvme             nvme1      nvme1n1
I/OAT    0000:80:04.0    8086   2021   1       ioatdma          -          -
I/OAT    0000:80:04.1    8086   2021   1       ioatdma          -          -
I/OAT    0000:80:04.2    8086   2021   1       ioatdma          -          -
I/OAT    0000:80:04.3    8086   2021   1       ioatdma          -          -
I/OAT    0000:80:04.4    8086   2021   1       ioatdma          -          -
I/OAT    0000:80:04.5    8086   2021   1       ioatdma          -          -
I/OAT    0000:80:04.6    8086   2021   1       ioatdma          -          -
I/OAT    0000:80:04.7    8086   2021   1       ioatdma          -          -

現在はカーネル上の ioatdma や nvme で制御が行われていることがわかります。
続いて、念のため NVMe をフォーマットして初期化しておきます。
検証環境では、以前ブロックデバイスとして使用していたディスクを使用したためか、この後の操作で Active devices: [email protected], so not binding PCI dev といったような表示となりドライバの変更を行うことが出来ませんでした。
フォーマットは次のコマンドで実施できます。
(使用するすべてのNVMeに対して実施しました。)

[email protected]:~# nvme format /dev/nvme0n1

先ほどの setup.sh をオプションなしで実行することで uio_pci_generic に変更します。

[email protected]:~/spdk# scripts/setup.sh
0000:39:00.0 (8086 0a54): nvme -> uio_pci_generic
0000:3a:00.0 (8086 0a54): nvme -> uio_pci_generic
0000:00:04.7 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.6 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.5 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.4 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.3 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.2 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.1 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.0 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.7 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.6 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.5 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.4 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.3 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.2 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.1 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.0 (8086 2021): ioatdma -> uio_pci_generic

変更が行われたか確認しておきます。
uio_pci_generic になっていればOKです。

[email protected]:~/spdk# scripts/setup.sh status
Hugepages
node     hugesize     free /  total
node0   1048576kB        0 /      0
node0      2048kB     1024 /   1024
node1   1048576kB        0 /      0
node1      2048kB        0 /      0

Type     BDF             Vendor Device NUMA    Driver           Device     Block devices
I/OAT    0000:00:04.0    8086   2021   0       uio_pci_generic  -          -
I/OAT    0000:00:04.1    8086   2021   0       uio_pci_generic  -          -
I/OAT    0000:00:04.2    8086   2021   0       uio_pci_generic  -          -
I/OAT    0000:00:04.3    8086   2021   0       uio_pci_generic  -          -
I/OAT    0000:00:04.4    8086   2021   0       uio_pci_generic  -          -
I/OAT    0000:00:04.5    8086   2021   0       uio_pci_generic  -          -
I/OAT    0000:00:04.6    8086   2021   0       uio_pci_generic  -          -
I/OAT    0000:00:04.7    8086   2021   0       uio_pci_generic  -          -
NVMe     0000:39:00.0    8086   0a54   0       uio_pci_generic  -          -
NVMe     0000:3a:00.0    8086   0a54   0       uio_pci_generic  -          -
I/OAT    0000:80:04.0    8086   2021   1       uio_pci_generic  -          -
I/OAT    0000:80:04.1    8086   2021   1       uio_pci_generic  -          -
I/OAT    0000:80:04.2    8086   2021   1       uio_pci_generic  -          -
I/OAT    0000:80:04.3    8086   2021   1       uio_pci_generic  -          -
I/OAT    0000:80:04.4    8086   2021   1       uio_pci_generic  -          -
I/OAT    0000:80:04.5    8086   2021   1       uio_pci_generic  -          -
I/OAT    0000:80:04.6    8086   2021   1       uio_pci_generic  -          -
I/OAT    0000:80:04.7    8086   2021   1       uio_pci_generic  -          -

続いて、ターゲットプログラムを起動します。
なお、この後の作業では Shell が最低2つ必要になりますので、事前に用意しておくことをおすすめします。

[email protected]:~/spdk# ./build/bin/nvmf_tgt

起動したら、もう一方の Shell で設定を投入していきます。
まず初めに、RDMA の設定を投入します。
次のコマンドは、I/O ユニットサイズを 8192byte 、最大 I/O ユニットサイズを 131072 、カプセル内のデータサイズを 8192byte としています。

[email protected]:~/spdk# scripts/rpc.py nvmf_create_transport -t RDMA -u 8192 -i 131072 -c 8192

続いて、ブロックデバイスの設定を行います。
SPDK では Malloc を使用して RAM ディスクを作成して試すこともできます。
ここでは、両方の手順について記述しておきます。

Malloc を使用する場合

[email protected]:~/spdk# scripts/rpc.py bdev_malloc_create -b Malloc0 512 512
Malloc0
[email protected]:~/spdk# scripts/rpc.py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001 -d SPDK_Controller1
[email protected]:~/spdk# scripts/rpc.py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 Malloc0
[email protected]:~/spdk# scripts/rpc.py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t rdma -a 10.0.0.1 -s 4420

NVMe を使用する場合
bdev_nvme_attach_controller 実施時の -a オプションはPCIeのIDを指定します。
このIDは setup.sh status で確認でき、今回の例では 0000:39:00.0 または 0000:3a:00.0 です。

[email protected]:~/spdk# scripts/rpc.py bdev_nvme_attach_controller -b NVMe0 -a 0000:39:00.0 -t pcie
[email protected]:~/spdk# scripts/rpc.py bdev_nvme_get_controllers
[
  {
    "name": "nvme0",
    "ctrlrs": [
      {
        "state": "enabled",
        "trid": {
          "trtype": "PCIe",
          "traddr": "39:00.0"
        },
        "cntlid": 0,
        "host": {
          "nqn": "nqn.2014-08.org.nvmexpress:uuid:e00e3bdd-93c6-4f3b-83be-0f9965b91e97",
          "addr": "",
          "svcid": ""
        }
      }
    ]
  }
]
[email protected]:~/spdk# scripts/rpc.py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001 -d SPDK_Controller1
<ここで名前を確認しておく>
[email protected]:~/spdk# scripts/rpc.py bdev_get_bdevs | grep "name"
<確認した名前を使用>
[email protected]:~/spdk# scripts/rpc.py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 NVMe0n1
[email protected]:~/spdk# scripts/rpc.py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t rdma -a 10.0.0.1 -s 4420

双方において、listen するアドレスとして 10.0.0.1 を使用し、ポート番号は 4420 を使用していますが、それぞれを環境に合わせて変更してください。
(なお、本来であればポート番号はRoCEv2用にIANAで予約された 4791 を使用するべきだと思います。)

ここからの手順は NVMe を使用した場合の例です。手順は同様ですが、表示が若干異なる場合があります。

ここまでの設定は nvmf_tgt を停止すると初期化されてしまうため、エクスポートしておきます。

[email protected]:~/spdk# scripts/rpc.py save_config > setting.json

エクスポートした設定は nvmf_tgt を起動する際に読み込ませることができます。

[email protected]:~/spdk# ./build/bin/nvmf_tgt -c setting.json

Initiator側の設定

Initiator 側で Discover を実施して検出できるか確認します。

[email protected]:~# nvme discover -t rdma -a 10.0.0.1 -s 4420

Discovery Log Number of Records 1, Generation counter 1
=====Discovery Log Entry 0======
trtype:  rdma
adrfam:  ipv4
subtype: nvme subsystem
treq:    not required
portid:  0
trsvcid: 4420
subnqn:  nqn.2016-06.io.spdk:cnode1
traddr:  10.0.0.1
rdma_prtype: not specified
rdma_qptype: connected
rdma_cms:    rdma-cm
rdma_pkey: 0x0000

無事検出出来たので、接続します。

[email protected]:~# nvme connect -t rdma -n "nqn.2016-06.io.spdk:cnode1" -a 10.0.0.1 -s 4420

認識しているか確認します。

[email protected]:~# nvme list
Node                  SN                   Model                                    Namespace Usage                      Format           FW Rev
--------------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
/dev/nvme0n1          SPDK00000000000001   SPDK_Controller1                         1           1.60  TB /   1.60  TB    512   B +  0 B   22.09

無事認識していることが確認できました。

fioを用いてベンチマーク

Initiator 側から NVMe に IO 負荷をかけてベンチマークを実施します。
ベンチマークには fio を用います。

[email protected]:~# apt install fio

シーケンシャルリードでベンチマークをまわしてみます。

[email protected]:~# fio --name=seqread --rw=read --filename=/dev/nvme0n1 --direct=1 --ioengine=libaio --bs=32m --numjobs=2 --size=10G --group_reporting
seqread: (g=0): rw=read, bs=(R) 32.0MiB-32.0MiB, (W) 32.0MiB-32.0MiB, (T) 32.0MiB-32.0MiB, ioengine=libaio, iodepth=1
...
fio-3.28
Starting 2 processes
Jobs: 2 (f=2): [R(2)][100.0%][r=3200MiB/s][r=100 IOPS][eta 00m:00s]
seqread: (groupid=0, jobs=2): err= 0: pid=3997: Sun Jun 26 07:40:59 2022
  read: IOPS=100, BW=3222MiB/s (3378MB/s)(20.0GiB/6357msec)
    slat (usec): min=7776, max=17896, avg=10373.59, stdev=1942.05
    clat (usec): min=4888, max=11913, avg=9478.44, stdev=1901.61
     lat (usec): min=17439, max=22960, avg=19853.26, stdev=319.36
    clat percentiles (usec):
     |  1.00th=[ 5342],  5.00th=[ 6456], 10.00th=[ 7635], 20.00th=[ 7898],
     | 30.00th=[ 8029], 40.00th=[ 8160], 50.00th=[ 8586], 60.00th=[11076],
     | 70.00th=[11338], 80.00th=[11469], 90.00th=[11600], 95.00th=[11731],
     | 99.00th=[11863], 99.50th=[11863], 99.90th=[11863], 99.95th=[11863],
     | 99.99th=[11863]
   bw (  MiB/s): min= 3200, max= 3328, per=99.99%, avg=3221.33, stdev=24.36, samples=24
   iops        : min=  100, max=  104, avg=100.67, stdev= 0.76, samples=24
  lat (msec)   : 10=53.75%, 20=46.25%
  cpu          : usr=0.19%, sys=14.35%, ctx=10189, majf=0, minf=16408
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=640,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
   READ: bw=3222MiB/s (3378MB/s), 3222MiB/s-3222MiB/s (3378MB/s-3378MB/s), io=20.0GiB (21.5GB), run=6357-6357msec

Disk stats (read/write):
  nvme0n1: ios=0/0, merge=0/0, ticks=0/0, in_queue=0, util=0.00%

3.3GB/s となっており、これは使用したNVMeの公称値とほぼ同様です。

続いて、シーケンシャルライトを試します。

[email protected]:~# fio --name=seqwrite --rw=write --filename=/dev/nvme0n1 --direct=1 --ioengine=libaio --bs=32m --numjobs=2 --size=10G --group_reporting
seqwrite: (g=0): rw=write, bs=(R) 32.0MiB-32.0MiB, (W) 32.0MiB-32.0MiB, (T) 32.0MiB-32.0MiB, ioengine=libaio, iodepth=1
...
fio-3.28
Starting 2 processes
Jobs: 2 (f=2): [W(2)][100.0%][w=1345MiB/s][w=42 IOPS][eta 00m:00s]
seqwrite: (groupid=0, jobs=2): err= 0: pid=4369: Sun Jun 26 08:39:02 2022
  write: IOPS=40, BW=1303MiB/s (1366MB/s)(20.0GiB/15720msec); 0 zone resets
    slat (usec): min=10077, max=40126, avg=24915.36, stdev=5671.09
    clat (usec): min=10707, max=43728, avg=24162.18, stdev=5742.35
     lat (usec): min=30520, max=65408, avg=49078.53, stdev=3970.34
    clat percentiles (usec):
     |  1.00th=[12125],  5.00th=[13960], 10.00th=[16909], 20.00th=[19268],
     | 30.00th=[20579], 40.00th=[22414], 50.00th=[23987], 60.00th=[26084],
     | 70.00th=[27919], 80.00th=[29754], 90.00th=[31327], 95.00th=[32637],
     | 99.00th=[35914], 99.50th=[37487], 99.90th=[43779], 99.95th=[43779],
     | 99.99th=[43779]
   bw (  MiB/s): min= 1152, max= 1408, per=99.99%, avg=1302.71, stdev=33.82, samples=62
   iops        : min=   36, max=   44, avg=40.71, stdev= 1.06, samples=62
  lat (msec)   : 20=26.56%, 50=73.44%
  cpu          : usr=3.30%, sys=3.68%, ctx=10582, majf=0, minf=19
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,640,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
  WRITE: bw=1303MiB/s (1366MB/s), 1303MiB/s-1303MiB/s (1366MB/s-1366MB/s), io=20.0GiB (21.5GB), run=15720-15720msec

Disk stats (read/write):
  nvme0n1: ios=0/0, merge=0/0, ticks=0/0, in_queue=0, util=0.00%

こちらも 1.3GB/s となっており公称値と同等です。

EthernetSwitchを挟む

NVMeをもう1台分設定したうえで、経路上に Ethernet Switch を挟んでみます。
使用する Switch は Azure/SONiC を搭載したホワイトボックススイッチです。

ブロックデバイスの設定と同様の手順をターゲット側に実施し、NVMeの設定を追加しておきます。
追加後の Initiator 側からの Discover 結果は次のようになります。

[email protected]:~# nvme discover -t rdma -a 10.0.0.1 -s 4420

Discovery Log Number of Records 2, Generation counter 2
=====Discovery Log Entry 0======
trtype:  rdma
adrfam:  ipv4
subtype: nvme subsystem
treq:    not required
portid:  0
trsvcid: 4420
subnqn:  nqn.2016-06.io.spdk:cnode1
traddr:  10.0.0.1
rdma_prtype: not specified
rdma_qptype: connected
rdma_cms:    rdma-cm
rdma_pkey: 0x0000
=====Discovery Log Entry 1======
trtype:  rdma
adrfam:  ipv4
subtype: nvme subsystem
treq:    not required
portid:  0
trsvcid: 4420
subnqn:  nqn.2016-06.io.spdk:cnode2
traddr:  10.0.0.1
rdma_prtype: not specified
rdma_qptype: connected
rdma_cms:    rdma-cm
rdma_pkey: 0x0000

2つの NVMe を認識していることがわかります。
本来であれば Switch 側に QoS 等の設定が必要なはずですが、今回使用した ConnectX-4 の Zero Touch RoCEv2 によって Switch 側の設定なしに機能しているようです。

複数のInitiatorからIO負荷をかける

複数の Intiator でそれぞれ1台ずつ NVMe を接続した状態で同時に IO 負荷をかけてみます。
結果は次のようになりました。

Initiator 1 : Run status group 0 (all jobs):
   READ: bw=3221MiB/s (3377MB/s), 3221MiB/s-3221MiB/s (3377MB/s-3377MB/s), io=200GiB (215GB), run=63589-63589msec
Initiator 2 : Run status group 0 (all jobs):
   READ: bw=3223MiB/s (3380MB/s), 3223MiB/s-3223MiB/s (3380MB/s-3380MB/s), io=200GiB (215GB), run=63538-63538msec

それぞれのディスクで 3.3GB/s を確認できます。
この程度では特に問題なく動作するようです。
この時、 Switch 側のトラフィック量は次のようになっていました。

[email protected]:~$ show interfaces counters
      IFACE    STATE       RX_OK        RX_BPS    RX_UTIL    RX_ERR    RX_DRP    RX_OVR       TX_OK        TX_BPS    TX_UTIL    TX_ERR    TX_DRP    TX_OVR
-----------  -------  ----------  ------------  ---------  --------  --------  --------  ----------  ------------  ---------  --------  --------  --------
  Ethernet0        U  51,631,461  6616.61 MB/s     52.93%         0         2         0   4,696,866    13.35 MB/s      0.11%         0         0         0
  Ethernet4        D           0      0.00 B/s      0.00%         0         0         0           0      0.00 B/s      0.00%         0         0         0
  Ethernet8        D           0      0.00 B/s      0.00%         0         0         0           0      0.00 B/s      0.00%         0         0         0
 Ethernet12        D           0      0.00 B/s      0.00%         0         0         0           0      0.00 B/s      0.00%         0         0         0
 Ethernet16        U   2,788,576  6529.03 KB/s      0.05%         0         0         0  31,156,784  3302.67 MB/s     26.42%         0         0         0
 Ethernet20        D           0      0.00 B/s      0.00%         0         0         0           0      0.00 B/s      0.00%         0         0         0
 Ethernet24        D           0      0.00 B/s      0.00%         0         0         0           0      0.00 B/s      0.00%         0         0         0
 Ethernet28        D           0      0.00 B/s      0.00%         0         0         0           0      0.00 B/s      0.00%         0         0         0
 Ethernet32        U   1,907,040  6825.01 KB/s      0.05%         0         0         0  20,460,390  3315.02 MB/s     26.52%         0         0         0
 Ethernet36        D           0      0.00 B/s      0.00%         0         0         0           0      0.00 B/s      0.00%         0         0         0
 Ethernet40        D           0      0.00 B/s      0.00%         0         0         0           0      0.00 B/s      0.00%         0         0         0
 Ethernet44        D           0      0.00 B/s      0.00%         0         0         0           0      0.00 B/s      0.00%         0         0         0

Ethernet0 がターゲット側のインタフェースであり、 6.6GB/s を観測しており、先ほどのベンチマーク結果と一致しています。

まとめ

今回は、SPDK を利用して NVMe over RDMA ターゲットを構築するとともに、Initiator から接続を行って検証を行うとともに、簡単なパフォーマンス測定を行いました。
検証に使用した環境では十分に性能が発揮できているようです。
後日、NVMe over TCP の検証も行う予定なので、その際にベンチマーク結果などを確認できればと思います。

100GbEを試す Mellanox ConnectX-4 + 100G-CWDM4

目次

背景

前回はお借りした機材を利用して100GbEをテストしました。
今回は、手元に Mellanox ConnectX-4 があったので、こちらを再度簡単にテストしてみることにします。

検証機材

今回は、NICとケーブルを前回と異なるものでテストします。
(ちなみに前回はお借りした機材でしたが今回はすべて私物です。)

詳細
Machine Fujitsu Primergy CX2550 M2
CPU Intel Xeon E5-2690v4 x2
Memory DDR4 ECC RDIMM 128GB
NIC Mellanox ConnectX-4 100Gb(Ethernet) MCX415A-CCAT
Cable Intel QSFP28 100G-CWDM4 module (SPTSBP2CLCKS)
OS Ubuntu Server 20.04.4 LTS

Mellanox ConnectX-4 MCX415A-CCAT

Intel QSFP28 100G-CWDM4 module

準備

初めに、ドライバをインストールします。
手順は前回と同様です。
Mellanoxのドライバは公式サイトからダウンロードできます。

IPアドレスについて、前回と同様に 10.0.0.1/2410.0.0.2/24 を使用し、以下のように直結することにします。

10.0.0.1/24 ---------- 10.0.0.2/24

設定はnetplanを用いて行いました。

2つのNIC間は100G-CWDM4モジュールとシングルモードファイバー(duplex)を用いて接続しました。

リンクアップを確認

ethtool コマンドを用いて、100Gbpsでリンクアップしているか確認します。

$ ethtool ens11np0
Settings for ens11np0:
        Supported ports: [ FIBRE ]
        Supported link modes:   1000baseKX/Full
                                10000baseKR/Full
                                40000baseKR4/Full
                                40000baseCR4/Full
                                40000baseSR4/Full
                                40000baseLR4/Full
                                25000baseCR/Full
                                25000baseKR/Full
                                25000baseSR/Full
                                50000baseCR2/Full
                                50000baseKR2/Full
                                100000baseKR4/Full
                                100000baseSR4/Full
                                100000baseCR4/Full
                                100000baseLR4_ER4/Full
        Supported pause frame use: Symmetric
        Supports auto-negotiation: Yes
        Supported FEC modes: None BaseR RS
        Advertised link modes:  1000baseKX/Full
                                10000baseKR/Full
                                40000baseKR4/Full
                                40000baseCR4/Full
                                40000baseSR4/Full
                                40000baseLR4/Full
                                25000baseCR/Full
                                25000baseKR/Full
                                25000baseSR/Full
                                50000baseCR2/Full
                                50000baseKR2/Full
                                100000baseKR4/Full
                                100000baseSR4/Full
                                100000baseCR4/Full
                                100000baseLR4_ER4/Full
        Advertised pause frame use: Symmetric
        Advertised auto-negotiation: Yes
        Advertised FEC modes: RS
        Link partner advertised link modes:  Not reported
        Link partner advertised pause frame use: No
        Link partner advertised auto-negotiation: Yes
        Link partner advertised FEC modes: Not reported
        Speed: 100000Mb/s
        Duplex: Full
        Port: FIBRE
        PHYAD: 0
        Transceiver: internal
        Auto-negotiation: on
Cannot get wake-on-lan settings: Operation not permitted
        Current message level: 0x00000004 (4)
                               link
        Link detected: yes

Speed: 100000Mb/s の表示が確認でき、100Gbpsでリンクアップしていることが確認できます。

計測

計測ツールは iperf (iperf2) です。
iperf3 での計測はこの結果から今回は省略します。
今回は最初からMTU9000で計測を行います。

計測結果です。

$ iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size:  128 KByte (default)
------------------------------------------------------------
[ ID] Interval       Transfer     Bandwidth
[  4] local 10.0.0.2 port 5001 connected with 10.0.0.1 port 51694
[  9] local 10.0.0.2 port 5001 connected with 10.0.0.1 port 51700
[  5] local 10.0.0.2 port 5001 connected with 10.0.0.1 port 51696
[  7] local 10.0.0.2 port 5001 connected with 10.0.0.1 port 51698
[  4]  0.0-10.0 sec  28.0 GBytes  24.1 Gbits/sec
[  9]  0.0-10.0 sec  27.8 GBytes  23.9 Gbits/sec
[  5]  0.0-10.0 sec  27.0 GBytes  23.2 Gbits/sec
[  7]  0.0-10.0 sec  28.3 GBytes  24.2 Gbits/sec
[SUM]  0.0-10.0 sec   111 GBytes  95.3 Gbits/sec

95.3Gbits/set と、おおよそ100Gbpsの速度が出ていることが確認できました。

まとめ

今回は、Mellanox ConnectX-4 NICとIntel製の100G-CWDM4モジュールを用いて100GbEのテストを行いました。
当初は100G-SR4モジュールを用いる予定でしたが、ファイバーのコストが高いため、ファイバーのコストを安く抑えられる上にモジュール単体のコストが安い100G-CWDM4モジュールを用いてテストを実施しました。

結果として、しっかり100GbEにふさわしい速度が出ていることが確認できました。
しかしながら、100G NICともなるとそれなりに発熱します。
今回検証に使用したマシンはアイドル時でも部屋のカーテンがなびくほどのファンを搭載していますが、QSFP28モジュールやNICはギリギリ触れないぐらいの温度となっていました。
一般的なPCで使用する場合には冷却方法を慎重に検討する必要がありそうです。

自宅ラック勢が語るおうちクラウド運用経験談

この記事は、CyberAgent 22 新卒 Advent Calendar 2021の19日目の記事です。

目次

概要

本記事では、自宅でサーバーやネットワーク機器を運用する筆者が、普段の運用をどのように行っているかを紹介します。
また、後半では運用していく中で発生したトラブル経験などを紹介します。

サーバーとは

サーバーと言われてどのようなものを想像するでしょうか。
一般的に、自宅サーバーなどと言われると自宅に中古のデスクトップPCなどを用意してサーバーとして運用するような形を想像する方が多いと思います。
しかし、コンピューターにはサーバー用に設計されたものが存在します。
これらはデータセンターなどで運用され、サイズ等が規格化されておりラックサーバーなどと呼ばれています。
また、これを搭載するための専用のラックが存在しており、ユニット数(U)でいくつかの種類があります。このラックを使用することで、上方向へ積み上げてサーバーを設置することが可能になります。
ラックサーバー自体は当然、一般家庭に設置することを想定したデバイスではありませんから、静音性より冷却性能の方が重要です。そのため、基本的に静音性とは無縁です。

(サーバーラックに搭載されたラックサーバーの例)

自宅サーバーを運用するメリット

では、デスクトップPCなどを使用する場合を含め自宅でサーバーを運用するメリットは何でしょうか。
まず一つ目は、学習機会を得られるということです。
AWSやGCPといったパブリッククラウドを利用するのと違い、自宅でサーバーを運用するには、物理レイヤーからすべて自分で設計や構築を行う必要があります。
パブリッククラウドが普及した近年では、手間が掛かる事から嫌厭されがちですが、学習することに重点を置けば、より多くの学習機会が得られるというメリットがあると言えます。
その中では、ネットワークに関する知識のほか、ハードウェアに関する知識も必要となります。そして、こういったレイヤーの知識は経験も重要です。この経験は、実際に運用していく中で原因切り分けなどを実施することで身に付く部分が多い分野でもあります。

もう一つは、シビアにコストを気にする必要が無くなるということです。
パブリッククラウドでは、インスタンスの起動時間や通信量による課金でコストが掛かります。例えば、パッケージを再インストールしたい場合などに、追加でコストが発生するわけです。(ローカルにキャッシュされている場合を除く)
つまり、何かを試すたびにコストが少しずつ掛かっていきます。
しかし、自宅サーバーであれば初期投資こそ掛かるものの、継続的なランニングコストは基本的には電気代のみです。
少なくとも通信量による従量課金は気にする必要はなくなるでしょう。また、24時間運用を前提としていれば、何か検証したい項目が出てきたとしてもスペックさえ余裕があれば自由に検証することができます。
ある意味定額で使えますから、従量課金を気にする必要がなくなります。

そして、パブリッククラウドでは手が出せないよなスペックのサーバーを使用することが出来るというメリットもあります。
月数万~数十万円するような構成のサーバーを、自宅サーバーであれば自由に、それも複数台使うことが出来ます。
また、Proxmoxなどを導入する事によって、簡単に仮想化基盤を構築することが出来ます。

なぜラックサーバーなのか

大きな理由の一つは、管理面です。
サーバー用に設計されたマシンでは、BMCと呼ばれるICチップが提供する管理機能が充実しています。この管理機能は、サーバー上で動作するOS等に関係なく、ネットワーク経由で電源の操作やコンソール画面の確認に加えてメモリやCPUなどのステータスやログなども確認することができます。
このような管理機能が存在することで、ネットワーク経由での管理がしやすくなります。

もう一つは、24時間365日動作し続けることが前提となっているハードウェアのため、耐久性が非常に高いことです。
加えて、ECCメモリと呼ばれるエラー訂正機能のあるメモリを使用しているため、長時間の安定した動作が期待できます。

機材選定と設計

サーバーを運用するうえで、機材の選定と設計は重要です。
筆者の環境では、機材選定時に下記の項目を重視しています。

  • 消費電力
  • 冷却ファンが発する騒音が許容範囲内か
  • CPUやメモリなどの性能
  • 拡張性や搭載可能なストレージの種類(サーバーの場合)
  • 使用可能なプロトコルやL2,L3でのパケット処理性能(ネットワーク機器の場合)

自宅でサーバーを運用する際に気を付けなければならないことは、消費電力や冷却ファンが発する騒音です。
高スペックなサーバーを運用する際は消費電力を気にしておく必要があります。
また、私のようにラックサーバーを使用する場合、多くのラックサーバーは掃除機レベルの騒音を出すため、自身の生活や近隣への影響がないか注意をしておく必要があります。

また、サーバーを使用する目的に応じてCPUやメモリなど、必要な性能を決めておく必要があります。
私の場合は基本的に仮想化基盤として使用することがほとんどです。そのため、ある程度の計算資源を持つサーバーを複数台用意して運用しています。

加えて、拡張性も重要です。
例えば、10GbE以上の高速なネットワークを構築する場合、それらの通信に必要なNIC(=ネットワークインターフェースカード)を搭載可能か確認しておく必要があります。
また、SASやU.2といったSATA以外の接続規格を持つストレージ製品を使用する場合、それらが接続可能であるか調査しておく必要があります。

(100GbEを検証したときの記事

ネットワーク機器では、使用可能なプロトコルやL2, L3でのパケット処理性能を調査しておく必要があります。
比較的安価なネットワーク機器の場合、OSPFは使用可能だがBGPは使用できないといったように、使用可能なプロトコルに制限があるケースがあります。
また、L3でのルーティング性能に限界がある場合もあります。このあたりについて、事前に調査し目的を果たせるデバイスであるかを確認したうえで入手しています。

(40GbEスイッチを検証したときの記事

構築と運用

ラックサーバーはとても重く20kg ~ 40kgの重量がある製品が多いです。
そのため、床面の耐荷重やダメージについて考慮しておく必要があります。また、重量物ですので設置の際にも注意が必要です。
私の場合、構造用合板を敷くことで荷重分散と床面へのダメージ軽減を図っています。

また、運用面においても定期的なメンテナンスは重要です。
特に物理レベルでは、埃の除去やケーブル等に過度な力が掛かっていないかなどをチェックし、事故や障害を未然に防ぎます。

現在のおうちクラウド

筆者の現在の自宅環境では、下記のような構成を運用しています。

  • サーバー8台
  • 合計352vCPU
  • 合計1.3TBメモリ
  • Enterprise SSD 20TB~

これらのサーバーはLACPによって冗長化された10GbE以上のネットワークで接続されています。
また、OpenStackをベースとする仮想化基盤として使用しており、ストレージとしてSSDを採用することでVMを快適に使用できるようにしています。
また、これだけの計算資源を持つ環境ですので、高スペックなVMを沢山建てることが出来ています。

トラブル&事件集

筆者が自宅サーバーを運用する中で経験したトラブルや事件について、抜粋して一部紹介します。

サーバー発煙事件

運用中に突然サーバーが停止し、電源の再投入を実施したところメモリのVRMと思われる部分が焼損した事件です。
半導体チップの焼ける匂いが部屋に充満した事件で、改めて安全管理について検討するきっかけとなった事件でした。
当時はヒヤヒヤしました。今だから笑って話せる事件です。

過去のまとめ

ファームウェアにバグはつきもの

プログラムは、規模が大きくなればなるほどバグが発生するリスクが高まります。ファームウェアのバグによる影響を受けたケースは何件かありますが、特に厄介だったのが特定のパケットの一部だけ破損する問題でした。
この問題は、PPP(PPPoE)パケットのうち、上りパケットだけが破損するというもので、ファームウェアをダウングレードすることで解決しました。
しかし、パケットの一部だけが破損するという厄介な状況であったため、当初はそもそもパケットが破損しているという事にたどり着くのに時間がかかったうえ、設定等を疑った為解決に時間を要した問題でした。(当時該当するバグの情報がなかった)

リンクが上がらない

SFPコネクタを使用したネットワーク接続では、対向機器との相性問題に遭遇することがしばしばあります。
特にDAC(=Direct Attach Cable)では問題が顕著に発生します。
こういった、規格上は接続可能であるのに実際にはうまく使用できないケースは、物理層では多々発生します。

パケットロスト事件

この問題は、特定の経路を通るパケットが40%程度ロストするといった問題でした。
原因は、光ファイバーでの光の受光強度が高すぎたため、物理レベルでデータが壊れることでした。
当初はMTUなどの設定を疑っていたのですが、各パラーメーターを見ているうちにFCS(=Frame Check Sequence)エラーが多発していることに気付き、光モジュールの組み合わせを変更することで解決しましたが、物理レベルでのパケット破損を初めて経験した出来事でした。

(SFP+ 光モジュール)

さいごに

筆者が自宅サーバーを運用する中で感じたメリットを踏まえて、ラックサーバーを導入した理由や運用について紹介しました。
思ったより文字ばかりになってしまったなと思っているところですが、どこにあるか分からない需要にマッチしていれば幸いです。
最後までお読みいただきありがとうございました。

EdgeRouter製品におけるIPv6トンネル接続のスループット

目次

概要

本記事では、Ubiquiti Networks社が提供するEdgeRouter製品でIPv6トンネル接続を行った場合のスループットについてまとめます。

検証機器

次のリストに示すデバイスでテストを行いました。

  • EdgeRouter-X
  • EdgeRouter-Lite
  • EdgeRouter-8
  • EdgeRouter-4

検証環境

検証に使用するトンネル接続には、IPIP6及びIP6GREを使用し、スループットの計測はiperf3を使用して計測を行いました。
構成はローカルでP2Pで接続されたIPv6ネットワーク上に検証機器で示したデバイスを接続しています。
また、対向のデバイスは一般的なラックマウントサーバにVyOSをインストールして使用し、計測上のボトルネックとならないことを確認しています。

なお、都合上、検証機器上にiperf3サーバを立てる形で計測しています。そのため、多少の負荷がルータにかかる状態である点に注意してください。

計測結果

Model IPIP6 IP6GRE 備考:CPU
EdgeRouter-X 136Mbps 138Mbps MediaTek MT7621AT (880 MHz, 2 cores)
EdgeRouter-Lite 85Mbps 84Mbps Cavium CN5020 (500 MHz, 2 cores)
EdgeRouter-8 243Mbps 246Mbps Cavium CN6120 (880 MHz, 2 cores)
EdgeRouter-4 912Mbps 914Mbps Cavium CN7130 (1 GHz, 4 cores)

まとめ

計測結果より、搭載されているCPUの世代やクロックがスループットに大きく影響していることが読み取れます。
加えて、EdgeRouter-4はファンレスで消費電力もEdgeRouter-8と比べると各段と少なく、とにかく性能が欲しい場合には選択肢の1つとなるでしょう。
もちろん、国内でも容易に入手可能で安価なEdgeRouter-Xでもそれなりのスループットが確保できると言えるため、選択肢として十分有力でしょう。

参考になれば幸いです。

NTT西日本 フレッツ光でNGN網内のRTT計測結果

目次

概要

フレッツ光を契約し、フレッツ・v6オプションを申し込むことで利用できるNGN網内での通信について、RTT(ラウンドトリップタイム)を計測したので、その結果をまとめます。

計測環境

  • エリア:NTTフレッツ西日本管内
  • 計測拠点:
    • 京都府京都市内A(以下KYT-Aと表記)
    • 京都府京都市内B(以下KYT-Bと表記)
    • 京都府京都市内C(以下KYT-Cと表記)
    • 大阪府南部(以下OSKと表記)
    • 島根県松江市(以下SMNと表記)

なお、拠点「KYT-A」及び拠点「KYT-B」については、地理的状況から同じ局舎へ収容されているものと推定しています。
加えて、諸事情により全拠点間を網羅的にテストはしていません。

計測方法

  • フレッツ・v6オプションによるIPv6アドレスを収容しているルータ機器間で、ICMPを用いて計測
  • 特に明記がない場合はすべてIPv6のみで通信
  • ICMPでのテストは各10回実行(-cオプションで10を指定)

京都府内での計測結果

まず、地理的に同じ局舎へ収容されていると推定される拠点「KYT-A」と拠点「KYT-B」間での計測結果が次のようになります。

$ sudo ping6 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX -c 10
PING 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX(240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX) 56 data bytes
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=1 ttl=61 time=2.35 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=2 ttl=61 time=2.23 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=3 ttl=61 time=2.80 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=4 ttl=61 time=2.38 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=5 ttl=61 time=2.39 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=6 ttl=61 time=2.42 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=7 ttl=61 time=2.97 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=8 ttl=61 time=2.83 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=9 ttl=61 time=3.02 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=10 ttl=61 time=2.76 ms

--- 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9014ms
rtt min/avg/max/mdev = 2.236/2.619/3.029/0.277 ms

平均値が 2.619ms となっています。

続いて、拠点「KYT-A」と拠点「KYT-C」間での計測結果です。

$ sudo ping6 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX -c 10
PING 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX(2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX) 56 data bytes
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=1 ttl=61 time=2.76 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=2 ttl=61 time=2.40 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=3 ttl=61 time=2.74 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=4 ttl=61 time=2.79 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=5 ttl=61 time=2.69 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=6 ttl=61 time=2.18 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=7 ttl=61 time=2.73 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=8 ttl=61 time=2.78 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=9 ttl=61 time=2.20 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=10 ttl=61 time=2.81 ms

--- 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9014ms
rtt min/avg/max/mdev = 2.188/2.612/2.813/0.240 ms

平均値が 2.612ms となっています。

最後に、拠点「KYT-B」と拠点「KYT-C」間での計測結果です。

$ sudo ping6 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX -c 10
PING 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX(240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX) 56 data bytes
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=1 ttl=61 time=2.65 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=2 ttl=61 time=2.65 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=3 ttl=61 time=2.79 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=4 ttl=61 time=3.24 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=5 ttl=61 time=2.77 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=6 ttl=61 time=3.18 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=7 ttl=61 time=2.66 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=8 ttl=61 time=2.79 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=9 ttl=61 time=3.21 ms
64 bytes from 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=10 ttl=61 time=2.68 ms
--- 240b:250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9011ms
rtt min/avg/max/mdev = 2.654/2.866/3.245/0.241 ms

平均値が 2.866ms となっています。

京都大阪間での計測結果

拠点「KYT-A」と拠点「OSK」間での計測結果です。
大阪側の計測拠点は「大阪府南部地域」にあります。京都市内-大阪市内間のおおよそ2倍の物理的距離があります。

$ sudo ping6 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX -c 10
PING 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX(2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX) 56 data bytes
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=1 ttl=58 time=6.20 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=2 ttl=58 time=6.10 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=3 ttl=58 time=6.07 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=4 ttl=58 time=5.71 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=5 ttl=58 time=6.37 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=6 ttl=58 time=6.28 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=7 ttl=58 time=6.33 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=8 ttl=58 time=6.28 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=9 ttl=58 time=6.27 ms
64 bytes from 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=10 ttl=58 time=6.30 ms

--- 2001:a250:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9012ms
rtt min/avg/max/mdev = 5.713/6.193/6.377/0.201 ms

平均値が 6.193ms となっています。

京都島根間での計測結果

拠点「KYT-A」と拠点「SMN」間での計測結果です。

$ sudo ping6 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX -c 10
PING 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX(2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX) 56 data bytes
64 bytes from 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=1 ttl=57 time=18.0 ms
64 bytes from 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=2 ttl=57 time=18.1 ms
64 bytes from 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=3 ttl=57 time=18.1 ms
64 bytes from 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=4 ttl=57 time=18.1 ms
64 bytes from 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=5 ttl=57 time=18.3 ms
64 bytes from 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=6 ttl=57 time=18.3 ms
64 bytes from 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=7 ttl=57 time=18.3 ms
64 bytes from 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=8 ttl=57 time=17.7 ms
64 bytes from 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=9 ttl=57 time=17.8 ms
64 bytes from 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX: icmp_seq=10 ttl=57 time=17.8 ms

--- 2409:253:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9012ms
rtt min/avg/max/mdev = 17.774/18.092/18.331/0.218 ms

平均値が 18.092ms となっています。

まとめ

NTT西日本がフレッツ光サービス内で提供するNGN網でのRTTの計測を行いました。
京都市内での計測データでは、2.5~3.0ms程度となっています。
これらの3拠点は地理的に非常に近いため、このような数値が出るのは納得できます。
なお、拠点「KYT-A」と拠点「KYT-B」間でISPより割当されるIPv4アドレスを用いて行ったテストでは、平均値は10.638msとなっており、ISP間でトランジットされていると考えれば至極妥当な結果となっていました。

また、京都大阪間がおよそ6ms程度である事が分かりました。
大阪側の拠点は大阪市内ではなく大阪府南部地域にあります。物理的距離は大阪市内までの距離のおよそ2倍程度あります。
この2拠点間でのIPv4でのテスト結果の平均値は7.751msとなっており、NGN網内での優位性をさほど感じられませんでした。
理由として、この2拠点は共に同じISPを利用してIPv4アドレスの割当を受けており、外部とのトランジット無しでISP内のみで通信が行われていると考えられます。

加えて、京都島根間では18ms以上かかる事が分かりました。

これらの結果の平均値を、物理的な直線距離とあわせてグラフにしたものが以下になります。
実際には、網内で通過する機器数やその機器の伝送遅延等が異なると考えられ、また、サンプル数が非常に少ない為あてになるとは言えません。
(青点が各計測結果、青破線は近似直線を示しています。)

ある程度距離に対して比例の関係にあるように見えますが、やはりサンプル数が少ないため参考程度にとどめておくべきでしょう。

個人的な感想としては、光で通信しているにしてはかなり遅いという印象を受けました。
NGN網内で様々な機器を通過し、その伝送遅延が大きいためでしょうか。

今後も、計測可能な対象があれば計測してみたいと思います。

EdgeRouterでIPIP6やIP6GREを設定する

目次

概要

本記事では、Ubiquiti Networks社が提供しているEdgeRouterにおける、IPIP6トンネルやIP6GREトンネルの設定について、例を示します。
なお、EdgeRouterで使用されるEdgeOSのバージョンは v2.0.9-hotfix.1 を使用したものです。

IPIP6

IPIP6はIPv4 over IPv6となります。
IPIP6の設定例は次のようになります。

set interfaces ipv6-tunnel v6tun0 address 10.0.0.0/31
set interfaces ipv6-tunnel v6tun0 encapsulation ipip6
set interfaces ipv6-tunnel v6tun0 local-ip 'fd00::1'
set interfaces ipv6-tunnel v6tun0 remote-ip 'fd00::2'

上記の設定項目については、次のようになります。
address v6tun0が持つトンネル内の終端IPアドレスを指定します。
encapsulation トンネル方式を指定します。この例ではipip6を使用します。
local-ip トンネル接続に使用する自IPv6アドレスを指定します。
remote-ip トンネル接続に使用する対抗側のIPv6アドレスを指定します。

上記設定を施したのち、 show interfaces ipv6-tunnel を実行した結果を以下に示します。

ipv6-tunnel v6tun0 {
    address 10.0.0.0/31
    encapsulation ipip6
    local-ip fd00::1
    remote-ip fd00::2
}

IP6GRE

IP6GREの設定例は次のようになります。

set interfaces ipv6-tunnel v6tun0 address 10.0.0.0/31
set interfaces ipv6-tunnel v6tun0 encapsulation ip6gre
set interfaces ipv6-tunnel v6tun0 local-ip 'fd00::1'
set interfaces ipv6-tunnel v6tun0 remote-ip 'fd00::2'

上記の設定項目については、次のようになります。
address v6tun0が持つトンネル内の終端IPアドレスを指定します。
encapsulation トンネル方式を指定します。この例ではip6greを使用します。
local-ip トンネル接続に使用する自IPv6アドレスを指定します。
remote-ip トンネル接続に使用する対抗側のIPv6アドレスを指定します。

上記設定を施したのち、 show interfaces ipv6-tunnel を実行した結果を以下に示します。

ipv6-tunnel v6tun0 {
    address 10.0.0.0/31
    encapsulation ip6gre
    local-ip fd00::1
    remote-ip fd00::2
}

VyOSとEdgeRouterにおけるBGPの設定と挙動の差異

目次

概要

本記事では、VyOSとEdgeRouterそれぞれにおいてBGPを設定する場合の設定項目の差異と、挙動の違いについてまとめます。
隅々まで調査したわけではなく、利用する中で判明した点をまとめたものですので、完璧ではないことを予めご理解ください。

VyOS

VyOSとは、オープンソースで提供されるソフトウェアルータです。
Debianをベースとして開発されており、ルーティングなどのネットワークの基本的な機能から、各種VPNプロトコルやトンネリングプロトコルなどを扱うことが出来ます。
vyos.io

EdgeRouter

EdgeRouterは、Ubiquiti Networks社が発売しているネットワーク機器のブランドです。
低価格でありながら高機能であり、またライセンス料などが発生しないという特徴を持つ製品です。
中身はVyOSベースとなっているようなので、基本的にVyOSのドキュメントを参考にすることが出来ます。

本記事では、EdgeOS v2.0.9-hotfix.1 を例に取り上げていきます。

BGP設定周りのコマンド体系

VyOSでは1.2系列からBGP設定周りのコマンド体系が一部変更となっています。
例えば、広報するネットワークを明記する場合、VyOS 1.2系以降では次のように記述します。

set protocols bgp address-family ipv4-unicast network 192.168.1.0/24

しかし、VyOS 1.1.8以前、またはEdgeRouterにおいては、address-family の下に ipv4-unicast という設定項目は存在しません。
したがって、VyOS 1.1.8以前、またはEdgeRouterにおいては次のように記述する必要があります。

set protocols bgp 65001 network 192.168.1.0/24

network項目で設定したネットワークの広報動作の違い

上記で例を示した network で指定した広報したいネットワークの広報動作についても、VyOSとEdgeRouterで挙動の違いがみられます。
VyOSの場合、こちらのページに次のような記述があります。

By default, the BGP prefix is advertised even if it’s not present in the routing table. This behaviour differs from the implementation of some vendors.

これは、この network で設定したネットワークについては、そのネットワークがルーティングテーブル上に存在しない場合でも広報を行うことを示していると解釈できます。
しかしながら、EdgeRouter環境においてはこの挙動に違いがみられます。
例として、先ほどのネットワークの設定を、先に述べたコマンド体系の違いを考慮した上で次のような設定をEdgeRouterへ流し込んだとします。

protocols {
    bgp 65001 {
        neighbor 10.0.0.2 {
            remote-as 65002
        }
        network 192.168.1.0/24 {
        }
    }
}

上記設定の場合、VyOSのドキュメントを信用する限り、192.168.1.0/24というネットワークがルーティングテーブル上に存在しなくてもremote-asであるAS65002に対してこのネットワークが広報されることになります。
ここで、上記設定を流し込んだEdgeRouter上でルーティングテーブルを確認してみると以下のようになっていたとします。

$ show ip route
Codes: K - kernel, C - connected, S - static, R - RIP, B - BGP
       O - OSPF, IA - OSPF inter area
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       > - selected route, * - FIB route, p - stale info

IP Route Table for VRF "default"
K    *> 0.0.0.0/0 [20/0] via XXX.XXX.XXX.XXX, eth0, 07:09:35

すなわち、192.168.1.0/24というネットワークはルーティングテーブル上に存在しないという事です。
この状態で、自ASが広報しているネットワークを確認してみると、次のようになります。

$ show ip bgp neighbors 10.0.0.2 advertised-routes

結果を書き忘れたわけではありません。広報しているネットワークが存在しない場合、説明書きすら表示されません。
ということで、ルーティングテーブル上に存在しなくても広報されるはずの192.168.1.0/24というネットワークは、EdgeRouter環境においては広報されない、という事になります。
この点、VyOSと挙動が異なるようですので注意が必要です。

実際には、ルーティングテーブル上に無いネットワークを広報することは危険であると考えられます。したがって、VyOSのドキュメントに記載があるように、他ベンダーやEdgeRouterにおいてこのような実装がなされていること自体は不思議ではありません。
(多くの場合、redistributeなどの指定を行うと思います。)
また、VyOSのドキュメントには、次の設定を行うことで、ルーティングテーブル上に存在しないネットワークを広報しなくなるという旨の記載があります。

set protocols bgp parameters network-import-check

もう少し調査してみると、EdgeRouterには上記の設定項目は存在しないことが分かります。これは、デフォルトでこのチェックが行われていることを暗示していると言えます。
また、次のような設定項目が存在することを確認できます。

set protocols bgp 4220101001 parameters disable-network-import-check

コマンド名から推察するに、デフォルトで有効になっている「ルーティングテーブル上に存在するかどうかのチェック」を無効化するコマンドであると考えられます。
しかし、EdgeRouterを展開するUbiquiti Networksのコミュニティサイトのこのページには、次のような記述があります。

The list below shows other config settings that are no longer supported.
~~中略~~
protocols bgp <> parameters disable-network-import-check

つまり、EdgeOS v1.8.0以降では上記設定はサポートされなくなっています。
実際、上記設定を EdgeOS v2.0.9-hotfix.1 に流し込んでみても、挙動は変化しませんでした。

では、対象のネットワークがルーティングテーブル上に存在する場合は正しく広報が行われるのか。
試しに、次のような設定をしてみます。

set protocols static route 192.168.1.0/24 blackhole

上記は、192.168.1.0/24を宛先無しで静的ルート登録を行うものです。
先ほどと同様にルーティングテーブルを確認してみます。

$ show ip route
Codes: K - kernel, C - connected, S - static, R - RIP, B - BGP
       O - OSPF, IA - OSPF inter area
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       > - selected route, * - FIB route, p - stale info

IP Route Table for VRF "default"
K    *> 0.0.0.0/0 [20/0] via XXX.XXX.XXX.XXX, eth0, 07:09:35
S    *> 192.168.1.0/24 [1/0] is a summary, Null

これで、ルーティングテーブルに192.168.1.0/24というネットワークが登録されました。(多少無理矢理ですが…)
少し待ってから、先ほどと同様に自ASが広報しているネットワークについて確認してみます。

$ show ip bgp neighbors 10.0.0.2 advertised-routes
BGP table version is 45, local router ID is XXX.XXX.XXX.XXX
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal
Origin codes: i - IGP, e - EGP, ? - incomplete

    Network          Next Hop            Metric    LocPrf       Weight Path
*>  192.168.1.0/24   10.0.0.1                      100          32768    i

Total number of prefixes 1

ルーティングテーブルに登録された192.168.1.0/24というネットワークが広報されていることが確認できます。

ということで、EdgeRouterではルーティングテーブル上に存在しないネットワークは、たとえ指定したとしても広報されず、この動作はVyOSとは異なる というお話でした。

まとめ

EdgeRouterでBGPを扱うサンプルは割と散見されるのですが、「とりあえずBGPを動かしてみた」といった感じで実際の挙動に触れていないものが多かったので書いてみました。
VyOS 1.2系以降と、VyOS 1.1.8系以前やEdgeRouterでは、BGP周りのコマンド体系に違いがあります。
VyOSの情報を参考とする場合、注意が必要です。
また、BGP設定内の network で設定した挙動について、VyOSとEdgeRouterで違いがあることも確認できました。
実際、設定中にこの問題に直面したので、実験等でこの設定をされる際には注意が必要です。(ハマります)

kolla-ansibleを使ってOpenStack環境を構築してみる all-in-one編

目次

2021/01/09 インスタンスにパスワードを設定する箇所の誤りを修正しました。

kolla-ansibleとは

公式ページ

OpenStack 環境のデプロイメントツールです。
更に、完全にカスタマイズ可能であることも大きな特徴です。
加えて、CentOS や Ubuntu などの多くの Linux ディストリビューションに対応しているのも大きな特徴と言えます。
なお、デプロイには Ansible が利用されます。

他にも devstack や microstack など数多くのデプロイメントツールが存在しますが、これらは開発環境向けで、カスタマイズが困難であったり、再起動したら壊れてしまうものなど、扱いにくいのが現状です。
また、 kolla-ansible と同様に Ansible を利用して OpenStack 環境の構築を行う openstack-ansible なども存在しますが、こちらも筆者環境での検証では再起動したら壊れてしまうものでした。

そこで、今回は kolla-ansible を利用してみることにしました。
なお、 kolla-ansible ではすべてのコンポーネントを1つのサーバ上で動作させる all-in-one 構成と、複数のサーバをクラスタリングして利用する multinode 構成を選択することができます。
今回は all-in-one 構成を構築してみます。
multinode 構成については、次回以降取り扱う予定です。

インストール準備

kolla-ansible では CentOS や Ubuntu などを利用することができますが、本記事では Ubuntu 20.04 LTS を利用しています。

事前に python3 などを用意しておく必要がありますので、必要なパッケージとともに導入します。

$ sudo apt update
$ sudo apt install python3-dev libffi-dev gcc libssl-dev
$ sudo apt install python3-pip

インストールされた pip3 が最新バージョンであるか確認します。

$ sudo pip3 install -U pip

続いて、 Ansible をインストールします。

$ sudo apt install ansible

kolla 及び kolla-ansible のインストールを行います。
なお、ここでは apt でインストールする方法と、 github から落としてくる方法の2つが選択できますが、2020/12/29時点では apt からインストールするとUbuntu 20.04 LTSは動作対象外との旨が表示され先に進むことができなくなりますので、ここでは github から落としてきて利用します。

$ git clone https://github.com/openstack/kolla
$ git clone https://github.com/openstack/kolla-ansible
$ sudo pip3 install ./kolla
$ sudo pip3 install ./kolla-ansible

必要なディレクトリを作成し、所有権の設定を行います。

$ sudo mkdir -p /etc/kolla
$ sudo chown $USER:$USER /etc/kolla

先程 github から落としたファイルのうち、必要なファイルを作成したディレクトリへコピーします。

$ cp -r kolla-ansible/etc/kolla/* /etc/kolla
$ cp kolla-ansible/ansible/inventory/* .

ここで、 /etc/ansible/ansible.cfg[defaults] に以下を追記して Ansible の設定を変更します。

host_key_checking=False
pipelining=True
forks=100

ここで、 Ansible の設定とインベントリの構成が正しいかチェックしておきます。

$ ansible -i all-in-one all -m ping

OpenStack の各サービスのパスワード類を生成します。

$ cd ~/kolla-ansible/tools
$ ./generate_passwords.py

/etc/kolla/globals.yml を編集して kolla の設定を行います。
設定はページKolla globals.yml を参考にしてください。
なお、cinder などの有効化設定もこちらで行います。
必要に応じて設定をしてください。

kolla_base_distro: "ubuntu" #ホストOSのディストリビューション
kolla_install_type: "binary" #パッケージの取得方法
kolla_internal_vip_address: "192.168.122.206" #管理IFのアドレス
network_interface: "enp1s0" #管理ネットワークのIF
neutron_external_interface: "enp6s0" #OpenStack Neutron ネットワークのIF

デプロイを行います。

$ cd ~/kolla-ansible/tools
$ ./kolla-ansible -i ../../all-in-one bootstrap-servers
$ ./kolla-ansible -i ../../all-in-one prechecks
$ ./kolla-ansible -i ../../all-in-one deploy

ここまで完了すると、 kolla_internal_vip_address で設定したIPアドレスにアクセスすると OpenStack のダッシュボードが表示されます。
しかし、外部へアクセスするためのネットワークなどが設定されていないため、それらの設定を行います。
kolla-ansible/tools/init-runonce をエディタで開き、下記を追記します。
CIDRRANGE , GATEWAY などを適時変更します。

ENABLE_EXT_NET=${ENABLE_EXT_NET:-1}
EXT_NET_CIDR=${EXT_NET_CIDR:-'192.168.122.0/24'}
EXT_NET_RANGE=${EXT_NET_RANGE:-'start=192.168.122.15,end=192.168.122.45'}
EXT_NET_GATEWAY=${EXT_NET_GATEWAY:-'192.168.122.1'}

cloud-init を利用してインスタンスのパスワードを設定する場合は以下の変更を行います。
~/kolla-ansible/ansible/roles/nova/templates/nova.conf.j2

[libvirt]
inject_password = True
inject_partition = -1

この変更は、 /etc/kolla/nova-compute/nova.conf に反映されますされると記述があるのですが、こちらの環境では反映を確認できませんでした。

~/kolla-ansible/ansible/roles/horizon/templates/local_settings.j2

OPENSTACK_HYPERVISOR_FEATURES = {
    'can_set_mount_point': False,
    'can_set_password': True,  //FalseをTrueに変更
    'requires_keypair': False,
    'enable_quotas': True
}

この変更は、 /etc/kolla/horizon/local_settings に反映されます。

最後に、変更した設定を OpenStack に適用します。
その前に、 python3-openstackclient をインストールしておきます。

$ sudo apt install python3-openstackclient

続いて、認証情報の入ったスクリプトファイルを生成します。

$ cd kolla-ansible/tools
$ ./kolla-ansible post-deploy
$ . /etc/kolla/admin-openrc.sh

変更した設定を適用します。

$ kolla-ansible/tools/init-runonce
$ cd kolla-ansible/tools
$ ./kolla-ansible -i ../../all-in-one reconfigure

以上が完了すると、変更した設定が適用され、ネットワークなどがいくつか作成されているはずです。
なお、 Horizon のパスワードは以下のようにして表示することができます。

$ grep keystone_admin_password /etc/kolla/passwords.yml

以上、簡単にではありますが、参考になりましたら幸いです。

About

インフラエンジニア
主に作業ログ

About Me

Archives