Dispatch Serverless Framework

Dispatch は Function as a Service (FaaS) のためのフレームワークで、VMware が中心となってオープンソースで開発が進められています。

一般的に FaaS は大きく分けるとホスティングされたサービスとして提供されるもの(e.g. AWS Lambda、Azure Functions、Google Cloud Function、など)と、自らインストールして使うもの(Apache OpenWhisk、OpenFaaS、Fission、Kubreless、Riff、など)がに分けられますが、この分類でいくと Dispatch は後者に分類されるものになります。ただし、Dispatch はいわゆる FaaS 機能の中核となる部分(エンジン)を提供しているわけでありません。OpenFaaS、Kubeless、Riff など、現在  FaaS エンジンには多くの選択肢があります。このような状況の中で Dispatch がこれらの FaaS エンジンと似たような機能を提供してもあまり価値がありません。まだ、どの FaaS エンジンが今後主流になって行きそうかも分からない状況なので、特定のエンジンを前提にするのもあまり筋がよろしくなさそう、ということで、Dispatch は FaaS エンジン部分を pluggable にして、その周辺機能を実装する、という戦略を取っています。Dispatch が「FaaS フレームワーク」と呼ばれる所以です。

現在、Dispatch には2つのバージョン(ブランチ)があります。一つは Dispatch-Solo と呼ばれるもの、もう一つは Dispatch-Knative と呼ばれるものです。

Knative というのは Google Cloud Next 2018 で発表されたプロジェクトで、Google、Pivotal、RedHat、IBM などが開発に関わっています。その狙いは Dispatch に近く、FaaS / Serverless に対して、Build / Serving / Events といった基本的な building block を提供しよう、というものです。Dispatch は Knative が発表された際に、Knative と競合路線を取るのではなく、Knative と協調していく、という判断をしました。つまり、Knative が提供する基本機能(Build、Serving、Events)は Knative のものを利用し、マルチテナンシーなど、Knative にはない Dispatch ならではの機能を Dispatch として提供していこう、というわけです。この Knative  をベースにした Dispatch が Dispatch-Knativeで、商用環境で使われることを想定したフル機能版の Dispatch の開発を目指しています。Dispatch-Knative はプラットフォームとして Kubernetes を利用しており、現在盛んに開発が行われています。

Dispatch-Knative アーキテクチャ

一方、Dispatch-Solo は、Dispatch-Knative の持つ様々な依存関係を極力最小化して、出来るだけ手軽に使えるものを提供することを目的に作られています。Dispatch-Solo はプラットフォームに Kubernetes を必要とせず、Docker だけで動くようになっています。また Photon OS をベースとした OVA としてパッケージされていますので、手軽に ESXi、VMware Workstation、VMware Fusion 上に展開して使うことができます。Dispatch-Solo は Dispatch-Knative とほぼ同様な機能を提供していますが、スケールアウト、マルチテナンシー、サービス連携の機能がない等、一部機能に制限があります。プロダクション利用ではなく、お手軽に利用できることに注力をしているバージョンです。

Dispatch-Solo アーキテクチャ

Dispatch は以下のような機能を持っています。

  • Kubernetes 上で動作(Dispatch-Knative のみ)
  • Pluggable FaaS Engine (OpenFaaS, Riff, Kubeless)
  • Runtime (Pyhton3, Nodejs, Java, Powershell, Clojure)
  • Let’s Encrypt サポート
  • OpenTracing 対応
  • 複数 IDP (Google, Auth0, vIDM, GitHub) のサポート(Dispatch-Knativeのみ)
  • Organization に基づいたマルチテナンシー(Dispatch-Knative のみ)
  • RBAC 機能
  • Cloud Events 対応
  • 拡張可能なイベントドライバ
  • Open Service Broker 統合(Dispatch-Knative のみ)

Dispatch によって作られた Function は大きく分けて2つの方法で呼び出されます。一つは Function に API の Endpoint を設けて、それを明示的(同期的)に呼び出す方法です。Dispatch には Kong というオープンソースの API Gateway が組み込まれており、これを利用して API Endpoint を Function に紐づけることができます。もう一つの Function の呼び出し方法はイベントドライバによる呼び出しです。イベントドライバがサポートしているイベントと Dispatch によって作られた Function を binding することにより、非同期的に発生するイベントによって Function を呼び出すことができます。

現在 Dispatch がサポートしているイベントドライバは以下の通りです。

  • CloudEvent
  • vCenter
  • AWS
  • EventGrid
  • Cron

VMware が開発を主導していることもあり、vCenter のイベントドライバがサポートされているのが特徴的です。これにより例えば ESXi 上の仮想マシンの Power ON/OFF などをトリガーにして Dispatch に登録された Function を実行しオペレーションの自動化を実現する、といったことが可能になります。

今回はお手軽に利用できる Dispatch-Solo を使って、簡単に動作をみてみることにしましょう。

Dispatch-Solo の最新版の OVA は以下の方法で入手することができます。

$ curl -OL http://vmware-dispatch.s3-website-us-west-2.amazonaws.com/ova/dispatch-latest-solo.ova

次にこの OVA ファイルをデプロイします。今回は VMware Fusion を使うことにします。アプリケーションおよびネットワークの設定を聞かれますが、root パスワードだけ設定すれば大丈夫です。

Dispatch のバイナリをダウンロードしてインストールします。現在 Mac 用と Linux 用のバイナリが用意されています(残念ながら Windows 用のバイナリは今のところ用意されていません)。

(Mac/Linux共通)
$ export LATEST=$(curl -s https://api.github.com/repos/vmware/dispatch/releases/latest | jq -r .name)
(Macの場合)
$ curl -OL https://github.com/vmware/dispatch/releases/download/$LATEST/dispatch-darwin$ chmod +x dispatch-darwin$ mv dispatch-darwin /usr/local/bin/dispatch
(Linuxの場合)
$ curl -OL https://github.com/vmware/dispatch/releases/download/$LATEST/dispatch-linux$ chmod +x dispatch-linux$ mv dispatch-linux /usr/local/bin/dispatch

インストールした Dispatch-Solo の仮想マシンに振られた IP アドレスを確認し、それを DISPATCH_HOST という環境変数に設定して Dispatch が使用する設定ファイルを作っておきます。

$ export DISPATCH_HOST=10.156.250.95
$ cat << EOF > $HOME/.dispatch/config.json
{
"current": "solo",
"contexts": {
"solo": {
"host": "${DISPATCH_HOST}",
"port": 8080,
"scheme": "http",
"organization": "dispatch",
"cookie": "cookie",
"insecure": true,
"api-https-port": 443
}
}
}
EOF

これで Dispatch を使う準備が一通り整いました。ここから Dispatch を使って Function を作っていきます。まず、Funciton を実行するベースとなるイメージを作ります。各ランタイム(言語)ごとにイメージが用意されていますが、”create seed-image” コマンドを使うと、Node.js、Python 3、Java、Powershell 用のイメージを一発で作ることができるので便利です。

$ dispatch create seed-images
Created BaseImage: nodejs-base
Created BaseImage: python3-base
Created BaseImage: powershell-base
Created BaseImage: java-base
Created Image: nodejs
Created Image: python3
Created Image: powershell
Created Image: java
$ dispatch get images
     NAME    | URL |    BASEIMAGE    |  STATUS  |         CREATED DATE         
--------------------------------------------------------------------------
  java       |     | java-base       | CREATING | Sat Dec  8 16:16:24 JST 2018 
  nodejs     |     | nodejs-base     | CREATING | Sat Dec  8 16:16:24 JST 2018 
  powershell |     | powershell-base | CREATING | Sat Dec  8 16:16:24 JST 2018 
  python3    |     | python3-base    | CREATING | Sat Dec  8 16:16:24 JST 2018 

“create seed-image” コマンド実行直後は “get image” コマンド結果の STATUS の欄が “CREATING” になっていますが、しばらく待つと全てのランタイムの STATUS が “READY” になります。

$ dispatch get images
     NAME    |                         URL                          |    BASEIMAGE    | STATUS |         CREATED DATE         
-------------------------------------------------------------------------------------------------------------------------
  java       | dispatch/dd3c73a4-2cf7-40db-8944-6c0d11f9157b:latest | java-base       | READY  | Sat Dec  8 16:16:24 JST 2018 
  nodejs     | dispatch/bbd15fbc-cfef-43d6-9d56-105c359fdedd:latest | nodejs-base     | READY  | Sat Dec  8 16:16:24 JST 2018 
  powershell | dispatch/1efa88c4-55cc-4bf8-a6d9-0a80c084cc89:latest | powershell-base | READY  | Sat Dec  8 16:16:24 JST 2018 
  python3    | dispatch/bcd7a157-117e-47c3-a276-3012d07b1848:latest | python3-base    | READY  | Sat Dec  8 16:16:24 JST 2018 

次に実際に実行される Function を作っていきます。今回は Python を使って簡単な関数を作ってみましょう。

$ cat << EOF > hello.py
def handle(ctx, payload):
    name = "Noone"
    place = "Nowhere"
    if payload:
        name = payload.get("name", name)
        place = payload.get("place", place)
    return {"myField": "Hello, %s from %s" % (name, place)}
EOF

handle() という関数がいわゆるハンドラ関数で、関数のエントリポイントとなります。この handle() という関数は2つの引数を取ります。”ctx” は Context の意で、ホスト側から渡される情報(例えば Secret など)が入ってきます。一方、”payload” の方は HTTP(S) のリクエストで渡されれてくる情報が入ってきます。今回の例では HTTP POST されてくる “name” というアトリビュートと “place” というアトリビュートを payload  から取り出しています。

次に “create function” コマンドで今しがた作った関数を Dispatch に登録します。今回はその関数に “hello-world” という名前を付けることにします。

$ dispatch create function hello-world --image python3 ./hello.py
Created function: hello-world

“get function” コマンドで関数が作られたのを確認することができます(STATUS の欄が READY になれば OK です)。

$ dispatch get function
     NAME     |                       FUNCTIONIMAGE                       | STATUS |         CREATED DATE         
--------------------------------------------------------------------------------------------------------------
  hello-world | dispatch/func-02625403-7cf4-477f-aca8-793a9cc2d55c:latest | READY  | Sat Dec  8 16:18:18 JST 2018 

では、この関数を実際に呼び出してみましょう。それには “exec” コマンドを使います。HTTP のパラメータは “–input” に続く文字列で指定できます。

$ dispatch exec hello-world --input '{"name": "Jon", "place": "Winterfell"}' --wait
{
    "blocking": true,
    "executedTime": 1544253540926528854,
    "faasId": "02625403-7cf4-477f-aca8-793a9cc2d55c",
    "finishedTime": 1544253540930869980,
    "functionId": "4a550524-85da-4d28-9d5f-47b35a425cd8",
    "functionName": "hello-world",
    "input": {
        "name": "Jon",
        "place": "Winterfell"
    },
    "logs": {
        "stderr": null,
        "stdout": [
            "Serving on http://0.0.0.0:9000"
        ]
    },
    "name": "440256c2-7710-4ea1-bc20-5231c2bd4363",
    "output": {
        "myField": "Hello, Jon from Winterfell"
    },
    "reason": null,
    "secrets": [],
    "services": null,
    "status": "READY",
    "tags": []
}

“–wait” というパラメータはこの関数の呼び出しを同期的に行う、という意味です。”–wait” を付けないと、関数は実行されますが、”exec” コマンドはその関数の終了を待たずに終了しますので、結果は別途確認する必要があります。今回は “–wait” を付けて実行していますので、返された JSON の中の “output” に期待された結果が返ってきているのが確認できます。

次に、この関数に API Endpoint を設定してみましょう。API Endpoint の設定には “create api” コマンドを使用します。

$ dispatch create api api-hello hello-world --method POST --path /hello
Created api: api-hello
shindom-a01:Downloads shindom$ dispatch get api api-hello
    NAME    |  FUNCTION   | PROTOCOL  | METHOD | DOMAIN |                   PATH                   |  AUTH  | STATUS | ENABLED 
--------------------------------------------------------------------------------------------------------------------------------
  api-hello | hello-world | http      | POST   |        | http://10.156.250.95:8081/dispatch/hello | public | READY  | true    
            |             | https     |        |        | https://10.156.250.95/dispatch/hello     |        |        |         
--------------------------------------------------------------------------------------------------------------------------------

では、この API Endpoint を呼び出してみましょう。

$ curl http://10.156.250.95:8081/dispatch/hello -H "Content-Type: application/json" -d '{"name": "Motonori", "place": "Tokyo"}'
{"myField":"Hello, Motonori from Tokyo"}

さて、ここまでは関数を CLI もしくは API Endpoint から同期的に関数を呼び出す例をみてきましたが、今度はイベントドライバによる非同期的な関数の呼び出しを試してみましょう。

Dispatch でサポートされているいくつかのイベントドライバの中から、今回は最もシンプルな Cron Event Driver を使ってみましょう。Cron Event Driver はその名の通りで、定期的に関数を呼び出してくれるイベントドライバで、UNIX の cron と同じような書式で起動するタイミングを指定をすることができます。

Dispatch のイベントドライバを使うためには3つのステップを踏む必要があります。1つ目は Event Driver Type の設定です。今回はdispatchframework にある Cron Event Driver に “cron” という名前を付けてやります。

$ dispatch create eventdrivertype cron dispatchframework/dispatch-events-cron:0.0.1
Created event driver type: cron

次に行うのは Event Driver の登録です。先ほど作った “cron” という Event Driver Type のドライバを作り “every-10-seconds” という名前を付け、イベントドライバに cron 書式(e.g. “0/10 * * * *”)で実行されるべきタイミングを指定します。

$ dispatch create eventdriver cron --name every-10-seconds --set cron="0/10 * * * *"
Created event driver: every-10-seconds

最後にイベントに対する subscription の設定を行います。具体的には Funciotn(今回の例で hello-world)とイベントドライバが生成するイベント(今回の例では cron.trigger)の紐付け( binding) 設定を行うことになります。

$ dispatch create subscription hello-world --name cron-sub --event-type cron.trigger
created subscription: cron-sub

これで設定は終わりです。この設定を行うと、10秒ごとに hello-world という Function が呼ばれることになり、その結果は “get runs” コマンドで確認をすることができます。

$ dispatch get runs
                   ID                  |  FUNCTION   | STATUS |               STARTED               |              FINISHED               
-------------------------------------------------------------------------------------------------------------------------------------
  5f9a79e0-1612-4169-90e8-e28e1f26640c | hello-world | READY  | 2018-12-08T16:35:30.003557189+09:00 | 2018-12-08T16:35:30.006887242+09:00 
  55fc2066-bd53-4e04-b0b6-bc1bb7940833 | hello-world | READY  | 2018-12-08T16:35:20.003363373+09:00 | 2018-12-08T16:35:20.007476442+09:00 
  19878a0d-312d-44ca-94b7-dc4999caf433 | hello-world | READY  | 2018-12-08T16:35:11.482092927+09:00 | 2018-12-08T16:35:11.485768711+09:00 
  14d2f6dc-2aa6-4bef-b9c4-a1cd6db6fe1f | hello-world | READY  | 2018-12-08T16:35:00.003163345+09:00 | 2018-12-08T16:35:00.007290792+09:00 
  d55328b0-53a5-48b0-b828-0ddff6648cd9 | hello-world | READY  | 2018-12-08T16:34:50.002078415+09:00 | 2018-12-08T16:34:50.006185049+09:00 
  3bc0d86b-1d73-4a94-bd7b-142f6d4743ae | hello-world | READY  | 2018-12-08T16:34:41.469205488+09:00 | 2018-12-08T16:34:41.472881958+09:00 

いかがでしたでしょうか? Dispatch はまだまだ若いプロジェクトで、活発に開発が行われています。みなさん、是非 Dispatch を使ってみてフィードバックをしていただだけると助かります。また、Dispatch はまだまだ把握しやすい規模のプロジェクトですので、開発にも参加しやすいと思います。色々な形で皆で Dispatch を盛り上げていけると嬉しいです!

VMware is the Dialtone for Kubernetes / 20代の人には分からないだろうけどw

最近たびたび “Dialtone” という言葉を耳にすることがあります。例えば今年(2018年)のVMworld U.S. Day 1 の General Session で、CEO の Pat Gelsinger が “VMware is the Daltone for Kubernetes” というフレーズを使っていました。最近の若者には馴染みがないかもしれませんが、この “Dialtone”、いわゆる電話の受話器をあげた時にするあの「ツーー」という音のことです。Pat も壇上で「20代の人には分からないかもしれないけど・・」とジョークを飛ばしてしましたね。

私は思い切り “元祖 Dialtone” 世代な人間ですが、上記のような新しい使われ方をした “Dialtone” という言葉を聞くたびに、何となく意味が分かるような分からないようなで、眠れない日々を送っていました(嘘)。このままではいかん、ってことで、この Dialtone ってのは一体どういう意味なんだ、と上司(Bruce Davie)に聞いてみたところ明快な説明をしてくれました。最近の Dialtone という言葉は、「世界中どこにいても普遍的(ubiquitous)に存在していて、誰しもがそれの意味を理解していて使うことができる」ということを比喩的に表しているんだそうです。確かに昔の電話の「ツーー」という音も、ほぼグローバルで共通で皆がその意味を理解できていたと思います。別の Dialtone の例としては、例えば HTTP is the dialtone for the Internet なんて言い方もできるそうです(HTTP はほぼインターネット上の共通言語なので)。

なるほど、これですっきり眠れるようになりました!(笑)

画像クレジット:unsplash-logoPaweł Czerwiński

2017年総括

今まではFacebookに書いていた「今年の総括」ですが、せっかくなのでこちらに書いて置こうかと思います(あんまりBlog記事書けてないので、記事数稼ぎですw)

2017年も残すところあと数時間となりました。今年も簡単に一年を振り返ってみたいと思います。

主な出来事

1月 スタバで相武紗季に遭遇。
2月 特筆すべきことなし。
3月 ギターを新調。Ascend時代の友人と20年ぶりの再会。
4月 車を小型化。
5月 CiscoによるViptela買収発表。
6月 INTEROP、本棚大幅増設。
7月 我が家に蟻侵入、たくろーさんライブ、素晴らしかった。
8月 買収完了。Cisco GSXに参加、デカイ。
9月 今年も沢山のHBDメッセージ、ありがとうございました!
10月 ONIC Japan、大盛況でした。姪っ子の結婚式。シンガポール出張。
11月 EBC & Sales Kickoff。
12月 パラオへ。

2017年の新たな取り組み

  • 六本木で働く^^
  • Enriched Air 取得

2017年 Most “Like!” On Facebook

  • Ciscoロゴの前でとった写真(237件)。そんなに違和感があるかしら?ww

2017年に読んだ本

  • 3〜4冊? ひどい状態。

フライト

  • JAL 8回、23,779 FLOY ONポイント(去年 23回、50,245 FLY ONポイント)
  • ANA 8回、 11,816 ポイント(去年 6回、10,674 ポイント)

今年は出張が少なかったため、去年と比べるとかなり減った。

2017年に開拓した肉屋

  • USHIHACHI 武蔵小杉店
  • 鉄板焼き 貴真
  • うしごろ 銀座店
  • トラジ葉菜 上大岡店
  • Dish Dash Grill Milpitas店
  • Strip Steak Honolulu店
  • Jan Su Jan Milpitas店

こちらも去年と比べると激減。忙しかったからなー。

2017年に見た映画

  • 約30本。
  • 洋画ベスト「きっと、星のせいじゃない(原題:The Fault in Our Starts)」。
  • 邦画ベストは該当なし。

出張が減ったぶん見た本数も減ったが、Amazon Videoで少し補った。

2018年目標

  • 2017年にやり残したこと
  • 新たしいことへの挑戦(いろいろ)
  • 英語頑張ろう!

こんなところです。また来年もよろしくお願いいたします。

NATは意外と難しい

以下の議論はNATでもNAPTでも成り立つので、どちらの場合でも単にNATと書くことにします。

ViptelaのIPsecの認証方法はauthentication-typeというパラメータで指定することができます。例えばこんな感じです。

security
 ipsec
  authentication-type ah-sha1-hmac sha1-hmac ah-no-id
 !
!

ah-sha1-hmac、sha1-hmacはほぼ自明だと思いますが、ah-no-idという見慣れない設定は何のためにあるのでしょう?

一般的にはNATが途中にあるとAHは機能しません。NATが書き換えるフィールドがAHで守られているからです。しかし、Viptelaのアーキテクチャでは、仮にNATが途中にあっても変換前と後のIPアドレス、ポート番号をシステムが把握しているため、NAT環境であってもAHを使用することができるようになっています。ただし、NATで書き換えられるのがIPアドレスとポート番号だけであれば良いのですが、一部のNATの中にはIPヘッダのIdentificationフィールド(以下、IDフィールド)を書き換えるものがあります。ah-no-idというパラメータはこのようなNATデバイスがある場合に使用するもので、AHによる検証をする際にIDフィールドを無視する、という意味になります。

世の中のNATにIDフィールドを書き換えるNATがいるのか最初は疑っていたのですが、実際このようなNAT実装は存在するようで、少なくともApple AirMac ExtremeやAirMac Expressはそのように動くようです。実際、手元にあったAirMac Expressで試してみました。
[shell]$ sudo hping3 -c 1 -N 12345 -S -s 11111 -p 22222 8.8.8.8[/shell]
のようにパケットを出してみて、NATされる前と後のパケットを比較してみました(ちなみに-SオプションをつけてSYNフラグを付けているのは、NATの実装の多くはTCPのステートをみていて、SYNパケットがくる前にパケットが来てもトランスレーションせずに廃棄するからです)。

NAT変換前(AirPort Express):
[shell]06:21:06.670952 IP (tos 0x0, ttl 64, id 12345, offset 0, flags [none], proto TCP (6), length 40)
10.0.1.3.11111 > 8.8.8.8.22222: Flags [S], cksum 0xa76d (correct), seq 868543788, win 512, length 0[/shell]
NAT変換後(AirPort Express):
[shell]15:20:52.361257 IP (tos 0x0, ttl 63, id 14428, offset 0, flags [none], proto TCP (6), length 40)
10.156.250.80.44210 > 8.8.8.8.22222: Flags [S], cksum 0x2c38 (correct), seq 868543788, win 512, length 0[/shell]
このように確かにIDフィールドが12345から14428に変換されています。

比較のためにVyOSのNATを使って同じように試験をしてみましょう。

NAT変換前(VyOS):
[shell]08:37:57.157399 IP (tos 0x0, ttl 64, id 12345, offset 0, flags [none], proto TCP (6), length 40)
10.200.1.11.11111 > 8.8.8.8.22222: Flags [S], cksum 0x5623 (correct), seq 7492057, win 512, length 0[/shell]
NAT変換前(VyOS):
[shell]08:37:57.157410 IP (tos 0x0, ttl 63, id 12345, offset 0, flags [none], proto TCP (6), length 40)
221.245.168.210.11111 > 8.8.8.8.22222: Flags [S], cksum 0xdb2d (correct), seq 7492057, win 512, length 0[/shell]
こちらはNAT変換前も変換後もIDフィールドは12345のままです。

今まで私はIDフィールドはデータグラムがフラグメントされた際のリアセンブルに使用されるためのものなので、途中の機器で書き換えられるべきではない、と考えていました。したがって、このようにIDフィールドまで書き換えるNATはかなり特殊で、いわゆるキワモノ(変態)であると思っていたのですが、先日 RFC6864 “Updated Specification of the IPv4 ID Field” を見つけて読んでみると、この考えが変わりました。

このRFCではIPデータグラムをAtomicなものとNon-Atomicなものに分類して考えています。

Atomicなデータグラム
まだフラグメントされておらず、その後もフラグメントされることのない(フラグメントが禁じられている)データグラム
Non-Atomicなデータグラム
すでにフラグメントされているか、今後フラグメントされる可能性のあるデータグラム

歴史的にはIDフィールドはリアセンブル以外の目的(例えば重複検知、など)での使い方も考えられていたものの、このRFCでは改めてIDフィールドはリアセンブル以外の目的で使うことを禁止することを明確化し、それに伴いAtomicなデータグラムに対してはIDフィールドに関する要求条件を緩めています(AtomicなデータグラムにIDフィールドは不必要なため)。しかしながら、Non-Atomicなデータグラムに対しては依然RFC791で規定されている「MDL (Max Datagram Lifetime、典型的には2分)の間は同じIDを使い廻してはいけない」という要求をしています。

これはNATのようないわゆるミドルボックスにはかなりややこしい問題です。本RFCでも、

NATs/ASMs/rewriters present a particularly challenging situation for
fragmentation. Because they overwrite portions of the reassembly
tuple in both directions, they can destroy tuple uniqueness and
result in a reassembly hazard. Whenever IPv4 source address,
destination address, or protocol fields are modified, a
NAT/ASM/rewriter needs to ensure that the ID field is generated
appropriately, rather than simply copied from the incoming datagram.

Specifically:
>> Address-sharing or rewriting devices MUST ensure that the IPv4 ID
field of datagrams whose addresses or protocols are translated
comply with these requirements as if the datagram were sourced by
that device.

(拙訳)NAT/ASM (Address-Sharing Mechanism)/rewriterはフラグメンテーションに困難をもたらす。両方向においてリアセンブルするためのタプルの一部を書き換えるため、タプルのユニーク性を担保することができなくなり、リアセンブルができなくなるからである。IPv4送信元、送信先アドレスまたはプロトコルフィールドが書き換えられる場合は、NAT/ASM/rewriterは単に元のデータグラムのIDフィールドをコピーするのではなく、IDフィールドが正しく生成されることを保障しなければならない。

具体的には、Address-sharingまたはrewriterデバイスがIPv4データグラムのアドレスやプロトコル番号を書き換えた場合は、IDフィールドが、あたかもデータグラムがそのデバイスから出たかのごとく、これらの要求条件を満たすようにIDフィールドを使わなければいけない。

と書かれています。確かによく考えると、送信元、送信先アドレス、プロトコルのタプルでのIDフィールドのユニーク性を担保するためには、アドレスを書き換えるNATデバイスは、元のパケットのIDフィールドを単純にコピーするべきではなく、あたかも自分が出したパケットかのようにユニーク性を担保したIDを新たに払い出す必要があります。経験的には多くのNATデバイスはVyOS (多くのLinuxベースのもの同様でしょう)のように動くように思いますが、Apple AirMac Extreme/Expressの動作の方が正しい(というか素性がよろしい)ということになります。

Appleさん、今まで変態扱いしてごめんなさい。

Catch-22

本日、iPhoneのバッテリーがなくなってしまい、ソフトバンクショップに駆け込んだ。モバイル・バッテリーは持っていたものの、Lightningケーブルを忘れてしまったので充電することができなかったのだ。ショップで充電はしてくれたのだが、その間仕事ができないので、ソフトバンクショップで繋がる無線LANを探すも、残念ながら無料で繋がるものはなし。0001softbankというSSIDは繋がるものの、ソフトバンクのデバイスでないと使えないので、Mac/PCはダメ。

一方、SWS1dayというSSIDはソフトバンクのWi-Fiスポットで、ソフトバンク以外の端末でも一日467円で使えるようだ。

しかたない、何事も経験かと思い、これを使おうと試みてみたが、サインアップの過程でメールでPINが送られてくる模様。ネット接続できないからネット接続サービスに申し込もうとしているのに、申し込みにネット接続がいる。詰んだ orz

こんな顛末をオフラインで書いている間に充電されたiPhoneが戻ってきました。ちゃんちゃんw

ビッグエコー・ビジネスプランを試してみた

ヴィプテラ・ジャパンにはオフィスがない。最近は電源を取れるカフェも多くなってきたので、カフェを仕事場がわりに使うことが多いのだが、電話会議をしたり、二人で共同作業をしたり、という時にはどうしてもカフェでは制約が多い。そこで、先日ビッグエコーとNTTコムがタイアップして始めた「ビジネスプラン」というのを利用してみた。

夜は賑わうカラオケボックスも、昼間の時間帯はどこも利用率が低い状況だ。そのような時間帯の利用率をどのようにあげるかはカラオケボックス業界の大きな課題だが、ビジネスユースに使おうというのはなかなか面白い試みだと思う。

平日19時まで、60分600円、もしくはフリータイムで1,500円を選択できる。この料金には1ソフトドリンクが含まれているので、かなりリーズナブルな値段設定と言えると思う。ビジネスプランはこれに加えて、電源タップ、HDMIケーブル(両側HDMIのケーブルに加え、片側ミニHDMIなケーブルも用意されているようだ)、ホワイトボードなどを借りることができ、店舗によっては無線LANが使えるところもある。使う部屋に依存するとは思うが、私が試した時は無線LANを使って8.8.8.8にpingした際のRTTは概ね30ms〜50ms程度であった。素晴らしいとも言えないが、普通に使うなら困ることはないだろう。

ふだんはカラオケの映像が流れている画面にPCの画面が映し出されているのはかなりシュールな絵図ではある。

BigEcho Business Plan
ビッグエコーのビジネスプランを使ってみた

少々困ったのはTVへのHDMIケーブルの接続。カラオケボックスのTVは壁に据え付けられていることが多いので、TVの背面や側面を自由に見ることができない。HDMIをどこに挿したらいいのか手探りで探す必要があった。

ビジネスプランで借りて、仕事に行き詰まったら気晴らしに歌えるのかって? どうやらそれは店舗によるようだ。特に制約なしという店舗とビジネスプランでは歌えないという店舗があった(歌えないと言われた店舗もかなり近くでガンガン歌っていたので、ビジネスプランと通常利用の部屋をきっちり分けている(例えばフロアで分ける、など)はしてない模様。したがってビジネスプランで使っているにもかかわらず、周りからは絶叫ソングが聴こえてくることもあるが、これが気にならないのであればビッグエコーのビジネスプランはかなりお得で快適な環境を与えてくれると思う。

このビジネスプラン、まだ使える店舗は東京と神奈川の一部の店舗に限られているが、なかなか快適な環境なのでノマドワーカーの方は一度試されてみてはいかが?

OVN (Open Virtual Network) の紹介

OVN(Open Virtual Network)は、L2、L3、ACLなどの仮想ネットワーキング機能を提供するオープンソースソフトウェアです。OVNは、仮想スイッチとして非常に広く使われているOpen vSwitch(OVS)と連携して動作します。開発もOVSと同じコミュニティで行われていおり、OVSのサブプロジェクト的な位置付けになっています。OVSと同様、ソースコードは完全にオープン、議論はパブリックなメーリングリスト及びIRCを使って進められています。VMware、RedHatなどが中心となって開発を進めていますが、誰でも開発に参加することができます。

現在OVNがターゲットにしているプラットフォームはLinux系ハイパーバイザ(KVM、Xen)やコンテナなどです。DPDKにも対応します。今のところESXiのサポートの予定はありません。なお、OVSはHyper-Vに移植されていますので、OVNのHyper-Vサポートも将来的にはあるかもしれません。

OVN自体はCMS(Cloud Management System)中立で、OpenStack、DockerやMesosなどとの連携を視野に入れています。その中でも特にOpenStackとの連携はOVNにとって大きな意味を持ち、OpenStackとのより良いインテグレーションはOVN開発の大きな動機の一つになっています。

OVNの目指すところはOVSと同様で、ハイパーバイザ1000台規模の大規模環境をサポートし、かつ、商用利用に耐える品質を持ったものを提供する、というものです。

以下に、OVNの基本アーキテクチャを以下に示します。

OVN Architecture
OVNアーキテクチャ

OVNには主に二つのコンポーネント(プロセス)があります。一つはovn-northd、もう一つはovn-controllerです。ovn-northdはその名前からも分かる通り、CMSに対して “northbound” (北向き)なインターフェースを提供しています。現状、ovn-northdはOVNのデプロイメントに1つだけ存在しますが、将来的にはこの部分は冗長化、スケールアウト化していく予定です。

OVNのアーキテクチャでとてもユニークなのは、CMSとのインテーグレーションポイントがデータベース(DB)である、という点です。この部分にRESTfulなAPIを期待される方も多いかと思いますが、現状ovn-northdはCMSとDBを使って連携をするようになっています。

ovn-northdは2つのデータベースを使います。一つは “Northbound DB” で、こちらは仮想ネットワークの「あるべき」状態(desired state)を保持します。具体的には論理スイッチ、論理ポート、論理ルータ、論理ルータポート、ACLなどに関する情報が保持されます。ここには一切物理的な情報は含まれません。ovn-northdから見てnorth側にいる実体(典型的にはCMS)はこのNorhbound DBにデータを書き込むことでovn-northdと連携します。ovn-northdが扱うもう一つのDBは “Southbound DB” で、こちらは物理、論理、バインディングなどのランタイム情報を保持します。このDBにはChassis、Datapath Binding、Encapsulation、Port Binding、Logical Flowといった情報が含まれます。ovn-northdの主な役割は、Northbound DBをSouthbound DBに変換し、Logical Flowを生成することです。

OVNを構成するもう一つのプロセスはovn-controllerというもので、こちらは各ホストにインストールされる分散されたローカル コントローラです。ovn-controllerはSouthbound DBから情報を読み取り、しかるべき設定を各ホスト上のOVS(ovsdb-server, ovs-vswitchd)に対して行います。また、ホストやVIFが追加された場合には、それらの情報をSouthbound DBへ書き込みます。Logical Flow情報をPhysical Flow情報に変換してOVSに設定を投入するのもovn-controllerの役目です。

ちなみに現在、OVN用のDBとしてOVSDBを使っています。本質的にはDBは何でも良いのですが、OVSにはOVSDBがすでに用意されていて、開発者側もその特性をよく理解しているので、手っ取り早くOVSDBを使っています。

OVNが提供する機能はL2、L3、ACL (Security Group)の3つです。

OVNのL2機能は論理スイッチ機能を提供します。具体的には、各ホスト間でL2 over L3なオーバーレーネットワークを自動的に作成してくれます。OVNがデフォルトで使うカプセル化技術はGeneveです。メタデータのサポート、マルチパスとの親和性、ハードウェアアクセラレーションの必要性を考慮すると、Geneveが最も妥当な選択でしょう。ただし、Geneveのハードウェアアクセラレーションに対応したNICはまださほど多くはないので、Geneveに対応していないNICで高い性能が必要な場合はSTTを使うこともできます。また、通常HW-VTEPはGeneveやSTTには対応していないので、HW-VTEPと通信する場合はVXLANを使います。

OVNのL3の機能はいわゆる分散論理ルーティング機能を提供します。OVNのL3機能はどこか特定のホストで動くわけではなく、すべてのホストが自律的にL3機能を実行します。OVNが現在サポートしているL3構成はとてもシンプルなもので、ルータの下に接続されている論理スイッチ間のルーティング及びDefault Gatewayへのルーティングに限られます。今のところ、Default Gateway以外のStatic Routeを設定することはできません。シンプルですが、OpenStack NeutronのL3基本機能を実現するのには十分です。NATは近々サポートされる予定です。

従来、OpenStack NeutronのOVS Pluginでは、Linux Bridge上のtapインターフェース(vnet)にiptablesを使ってACLをかけることによってSecurity Groupを実現していました。OVSのBridgeとLinux Bridgeが混在しているので、いささか複雑なアーキテクチャだったと言えます。

under-the-hood-scenario-1-ovs-compute
従来のOpenStack Neutron OVS pluginのアーキテクチャ(http://docs.ocselected.org/openstack-manuals/kilo/networking-guide/content/figures/6/a/a/common/figures/under-the-hood-scenario-1-ovs-compute.png より引用)

しかしOVS 2.4からOVSがconntrackと連携できるようになったので、iptablesに頼ることなくOVSでステートフルなACLをネイティブに実現できるようになりました。OVNはこのOVSとconntrackの連携機能を使ってACLを実現しています。

conntrack連携機能はOVSの機能なので、OVNなしでも利用はできますが、OVNからはconntrackを意識することなく、論理的なACLを設定するだけでそれがOVSのconntrack機能に自動的にコンパイルされて設定されますので大変便利です。

次回以降で、L2、L3、Security Groupの機能をもう少し深堀りしていこうと思います。

NetFlow on Open vSwitch

Open vSwitch (OVS)はかなり以前(2009年頃から)NetFlowをサポートしています。OVSでNetFlowをenableするには以下のようなコマンドを使います:

[shell]# ovs-vsctl — set Bridge br0 netflow=@nf — –id=@nf create NetFlow targets=\”10.127.1.67\”[/shell]

また、NetFlowをdisableにするには次のようにすれば良いです。

[shell]# ovs-vsctl — clear Bridge br0 netflow[/shell]

NetFlowには幾つかのバージョンがあります。その中でV5とV9が最もよく使われいるバージョンだと思います。OVSがサポートしているのはNetFlow V5のみです。NetFlow V9は現時点ではサポートしていません(OVSはNetFlow V9の直接の後継にあたるIPFIXをすでにサポートしているので、OVSがNetFlow V9をサポートするようになることはまずないと思われます)。

以下に、NetFlow V5パケットのヘッダーとフローレコード(1 NetFlowパケットに最大30個まで入ります)を示します。

NetFlow V5 header format NetFlow V5 header format

NetFlow V5 flow record format NetFlow V5 flow record format

NetFlow V5はIPv6のフローレコードを扱うことができません。IPv6トラフィックをモニターしたい場合は、sFlowかIPFIXを使ってください。

一般的なルータ/スイッチでのNetFlow実装と比較して、OVSのNetFlowの実装にはいくつかユニークな点がありますので、それらについて以下で述べます。

多くのNetFlow対応ルータ/スイッチではいわゆる「サンプリング」をサポートしてます。サンプリングとは、全パケットを処理するのではなく、一部だけを処理対象とするものです(サンプリング手法はいくつかありますが、このBlogの趣旨からはずれるのでここでは詳しくは述べません)。一方、OVSのNetFlowはサンプリングを行いません。OVSでサンプリングを使ったフロー処理をしたい場合はsFlowかIPFIXを使う必要があります。

OVSのNetFlowがサンプリングをしない、という点とも若干関連しますが、NetFlowフローレコード中にある「バイト数(dOctets)」と「パケット数(dPkts)」が32bitのフィールドなので、巨大なフロー(elephantフローなどと呼ばれることもあります)があると、これらのフィールドは比較的容易に溢れてしまいます。OVS内部では64bit値でバイト数、パケット数を数えていますので、これらの数が32bitでは収まらないフローがあった場合には、1つのフローの情報を複数のフローレコードに分けてエクスポートするようになっており、できるかぎり正確な値をコレクターに伝えるように努力をしています。

典型的なルータ/スイッチのNetFlow設定では、グローバルな設定(例えばエクスポート先の指定、など)に加え、インターフェース毎にNetFlow処理のenable/disalbe設定がある場合が多いと思います。一方、OVSではインターフェース毎の設定ではなく、ブリッジ毎にNetFlowの設定を行う形になっています。

多くのルータ/スイッチベースのNeflowエクスポータは、NetFlowパケットのソースIPアドレスを明示的に設定することができます(この際loopbackアドレスを使うのが一般的でしょう)。一方、OVSにはこのような設定はありません。OVSのNetFlowパケットのソースIPアドレスはホストOSのIPスタックによって決定さます。通常、このアドレスはパケットが送出されるインターフェースに紐付いているIPアドレスになります。NetFlow V5にはsFlowの「agent address」のような概念がないため、コレクターはNetFlowパケットのソースIPアドレスでエクスポータを区別するのが一般的です。OVSではNetFlowパケットのソースIPアドレスを明示的に設定することができませんので、なにかしらの理由でNetFlowパケットを送出するインターフェースが変わった場合は、NetFlowパケットのソースIPアドレスも変わる可能性があることに留意をしておきましょう。

ドキュメントにははっきりとは書かれていませんが、OVSはNetFlowエクスポータを複数指定するすることができるようになっています。これによりコレクタの冗長構成を実現することができます。設定は以下のとおりです:

[shell]# ovs-vsctl — set Bridge br0 netflow=@nf — –id=@nf create NetFlow targets=\[\”10.127.1.67:2055\”,\”10.127.1.68:2055\”\][/shell]

通常フローベースのネットワーク管理を行う場合、フローレコードに含まれるインターフェースのIn/Outの番号がとても重要な意味を持ちます。なぜなら、このインターフェース番号の情報を使って、興味関心のあるトラフィックのフィルタをすることが多いからです。商用コレクターの多くは洗練されたフィルタ機能を持っていることが多いです。ルータ/スイッチのNetFlowエクスポータの場合は、このインターフェース番号にはSNMPのIfIndexが使われます。一方、OVSのNetFlowではOpenFlowのポート番号が使われます。この番号はovs-ofctlコマンドで確認することができます。

[shell]# ovs-ofctl show br0
OFPT_FEATURES_REPLY (xid=0x2): dpid:0000000c29eed295
n_tables:254, n_buffers:256
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
1(eth1): addr:00:0c:29:ee:d2:95
config: 0
state: 0
current: 1GB-FD COPPER AUTO_NEG
advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD 1GB-FD COPPER AUTO_NEG
supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD 1GB-FD COPPER AUTO_NEG
speed: 1000 Mbps now, 1000 Mbps max
LOCAL(br0): addr:00:0c:29:ee:d2:95
config: 0
state: 0
speed: 0 Mbps now, 0 Mbps max
OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0[/shell]

この例ではeth1のOpenFlowポート番号が1であることを示しています。幾つかのインターフェース番号は特定の意味を持っています。ホスト自身が持つインターフェース(上の例で”LOCAL”と示されているインターフェース)のインターフェース番号は65534になります。また、パケットがブロードキャスト/マルチキャストの場合にはOutのインターフェース番号は65535になります。一般的なルータ/スイッチではこれはどちらの場合もインターフェース番号は0になることが多いと思います。

あれ、最近OVSにはIfIndexが追加されたんじゃ、と思われる方もおられるかもしれません。ご指摘の通りで、最近OVSの”Interface”テーブルにIfIndexが追加されています。ではNetFlowでもこのIfIndexを使うべきではと考えるのはごもっともですが、実はそんなに簡単な話ではありません。例えば、OVSによって作られるトンネルインターフェース(GRE, VXLAN, STTなど)はIfIndexを持っていませんので、単純にNetFlowのフローレコードでIfIndexを使おうとすると、これらのインターフェースに流れるトラフィックの情報を伝えられなくなってしまいます。

NetFlow V5のヘッダには「Engine ID」と「Engine Type」というフィールドがあります。デフォルトでこれらのフィールドがどのような値になるかはデータパスの種類によって異なります。OVSがnetdevを使ったユーザスペースで動いている場合、Engine ID、Engine Typeはおのおのデータパス名のハッシュから得られた値の上位8bit、下位8bitになります。一方、Linux Netlinkを使ったkernelデータパスの場合は、Engine ID、Engine TypeともにデータパスのIfIndexが使われます。OVSのデータパスのIfIndexは以下のコマンドで確認することができます:

[shell]# cat /proc/sys/net/ipv4/conf/ovs-system[/shell]

もちろんこららのデフォルト値は明示的に設定することも可能です。例えば以下のお通り:

[shell]# ovs-vsctl set Bridge br0 netflow=@nf — –id=@nf create NetFlow targets=\”10.127.1.67:2055\” engine_id=10 engine_type=20[/shell]

上のコマンドは最初にNetFlowの設定を行うのと同時にEngine TypeとEngine IDを設定をする場合の例ですが、NetFlowの設定をしたあとに、関連パラメータを変更をすることも可能です。

[shell]# ovs-vsctl clear NetFlow br0 engine_id=10 engine_type=20[/shell]

典型的なEngine TypeとEngine IDのユースケースは、物理的には一つだが論理的には複数あるエクスポータを区別するというものだと思います。Cisco 6500のケースなどがいい例です。Cisco 6500はMSFCとPFCがそれぞれ独立したNetFlowエンジンを持っているので、これらを区別したい場合にEngine Type、Engine IDを使う場合があります。OVSの場合はには、複数のブリッジから出力されるNetFlowフロレコードを区別したいケースなどで使用することができます。先に述べた通り、OVSでは、NetFlowパケットのソースIPアドレスはホストの標準的なIPスタックにによって決められます(そしてこれは、必ずしもNetFlowがenableにされたブリッジインターフェスのIPアドレスと同じものにはなりません)。したがって、NetFlowパケットのソースIPアドレスを使ってどのブリッジからエクスポートされたフローレコードなのかを特定することができません。しかし、Engine TypeやEngine IDにそれぞれ個別な値を設定することによって、どのブリッジからのフローレコードなのかを識別することができるようになります。とはいうものの、Engine Type/Engine IDをみて論理的なエクスポータを識別することができるコレクターは私の知る限りそう多くはないと思います。

Engine IDに関してはもう一つの使い道があります。既に述べたように、OVSはNetFlowフローレコードのIn/Outのインターフェース番号としてOpenFlowのポート番号を使用します。OpenFlowのポート番号はブリッジごとにユニークな値です。したがって、複数のブリッジでは同じOpenFlowポート番号が使われる可能性があります。このままではコレクターはそれらのインターフェースを区別することができません。このような場合にはadd_to_interfaceという設定をtrueにしてやることで解決することができます。

[shell]# ovs-vsctl set Bridge br0 netflow=@nf — –id=@nf create NetFlow targets=\”10.127.1.67:2055\” add_to_interface=true[/shell]

このパラメータがtrueの場合、In/Outのインターフェース番号の上位7bitがEngine IDの下位7bitで置き換えられるようになります。ブリッジごとに異なるEngine IDを振るようにすれば、ブリッジ間のインターフェース番号の衝突を避けることができるようになります。

一般的なルータ/スイッチベースのNetFlowエクスポータと同様、OVSのNetFlowにもactiveおよびinactiveなタイムアウトの概念があります。activeタイムアウトは以下のようなコマンドで設定できます(秒で指定)。

[shell]# ovs-vsctl set Bridge br0 netflow=@nf — –id=@nf create NetFlow targets=\”10.127.1.67:2055\” active_timeout=60[/shell]

明示的に設定されたない場合はactiveタイムアウトは600秒になります。また、activeタイムアウトに-1が指定された場合はactiveタイムアウトはしないようになります。

OVSのNetFlowにもinactiveタイムアウト機構が備わっていますが、明示的にこれを設定することはできません。OVSが管理しているフローの情報がinactiveタイムアウトでdatapathから削除された際にNetFlowのフローレコードもエクスポートされるようになっています。このinactiveタイムアウトは動的であり、さまざまな要素(OVSのバージョン、CPUやメモリの使用状況、など)によって決まります。このタイムアウト時間は最近のOVSでは通常1〜2秒と短めになっています。これは一般的なルータ/スイッチベースのNetFlowエクスポータのデフォルトのinactiveタイムアウト時間(通常15秒)と比べるとかなり短い時間となっています。

ICMPのフローがエクスポートされる際の扱いですが、OVSは一般的なルータ/スイッチと同様な動きをします。すなわち、フローレコードのソースポート番号に “ICMP Type * 256 + Code” の値を入れ、デスティネーションポート番号には0を入れる、というものです。

フローレコード中のNextHop、ソース/デスティネーションAS番号およびnetmaskは0になります。これはOVSが「スイッチ」であることを考えると妥当な動きと言えます。

ここまでで述べてきたように、OVSでNetFlowを使うにあたってはいくつかのポイントについて留意をしておく必要があります。sFlowやIPFIXと比べた時のNetFlowの強みの一つは、NetFlowをサポートしたオープンソースや商用のコレクタが豊富にあることだと思います。どのようなコレクタを選択してもまず間違いなくNetFlow V5はサポートされているはずです。ぜひ、OVSのNetFlow機能を試してみてください。きっとあなたのネットワークにいままでにない可視性を提供してくれることと思います。

ネットワーク仮想化とNSXに関する本が出ます

いままでなにか形になったものを残したいとずっと思っていましたが、少しだけ目標が叶いました。このたび、ヴイエムウェアの同僚たち数名と「詳解VMware NSX ネットワーク仮想化の基礎と応用」という書籍を出す事になりました。

詳解VMware NSX
詳解VMware NSX

翻訳本ではなく完全書き下ろしです。500ページを越えるボリュームなので、ちょっとお値段も張ってしまい申し訳ありませんが、もしご興味があれば買ってみてください。

章立ては以下のようになっています。

  • Chapter 01 技術背景と定義
  • Chapter 02 標準化とメリット
  • Chapter 03 既存ネットワークの課題
  • Chapter 04 ネットワーク仮想化のAPI
  • Chapter 05 NSXの技術解説
  • Chapter 06 OpenStackとNeutron
  • Chapter 07 SDDCとNSX

この中で私は「Chapter 05 NSXの技術解説」の前半部分といくつかのコラムを書かせていただいています。いままでも本の執筆や雑誌等への寄稿はそれなりにやっていましたが、名前が陽に出る形のものは今回がはじめてで、ちょっと感慨深いものがあります。

この本を出したいと思ったきっかけは、われわれの日々の活動の中にありました。ネットワークの仮想化というのはまだ新しい概念で、世の中で十分に浸透しているものではありません。したがってお客様の中にはネットワーク仮想化について十分に理解されていなかったり、場合によっては勘違いされていたりということがままありました。しかし、このような状況も無理はありません。いままでほとんど情報が世に出ていなかったのですから。そこで、われわれがネットワーク仮想化とNSXに関する本を書いて、このような状況を変えていかねば、と思った次第です。

この本を出すのは簡単ではありませんでした。構想からほぼ1年。途中頓挫しそうになったこともありましたが、なんとかやり遂げる事ができました。また、世界に先駆けて日本でこのような本を出せたのも意義深いことであると思っています。これもひとえに他の著者さん(水本さん、田中さん、横井さん、高田さん、小椋さん)が日々の業務の傍ら頑張ってくれたおかげです。特に、執筆者としてだけではなく、本プロジェクトをリードもしてくれた田中さんの、ともするとさぼりがちな筆者一同への激励と “ケツ叩き”(笑)のおかげで、なんとかvForum Tokyo 2014というヴイエムウェア(株)のイベントまでに出版をするという目標が達せられました。また、我々の不慣れな文章の編集に連日の徹夜でおつき合いくださった株式会社Heculaの丸山弘詩様と株式会社インプレスジャパン畑中二四様にも感謝をしたいと思います。

本を出すのが我々のゴールではありません。この本によってみなさんのネットワークの仮想化についての理解が少しでも深まり、NSXをより身近なものに感じていただけるようになれば幸いです。

Geneve on Open vSwitch

先日のBlogでGeneveという新しいEncapsulation方式について紹介をしましたが、さっそくOpen vSwitch(OVS)に実装されmasterにマージされましたので試してみました。今回はGeneveを検証する事が目的ですので、KVMなどは使わずに、単純に異なるホストにある2つのOVS BridgeをGeneveトンネルで繋いでみることにします。VMware FusionでUbuntu 14.04を2つ(host-1、host-2)動かし、それぞれgithubから取ってきた最新のOVSをインストールしました。各仮想マシンにはNATによるEthernetインターフェースを1つ設定し、DHCPでアドレスをとる事にします。以下の例ではそれぞれのホストで192.168.203.151と192.168.203.149というIPアドレスがDHCPで取れた場合の例です。Bridgeを2つ(br0、br1)作成し、br0を物理ネットワークと接続するブリッジとし、br1の間にGeneveのトンネルを張る事にします。

Geneve Test with Open vSwitch Geneve Test with Open vSwitch

host-1, host-2でのOVSの設定は以下の通りです。

[shell]mshindo@host-1:~$ sudo ovs-vsctl add-br br0
mshindo@host-1:~$ sudo ovs-vsctl add-br br1
mshindo@host-1:~$ sudo ovs-vsctl add-port bra eth0
mshindo@host-1:~$ sudo ifconfig eth0 0
mshindo@host-1:~$ sudo dhclient br0
mshindo@host-1:~$ sudo ifconfig br1 10.0.0.1 netmask 255.255.255.0
mshindo@host-1:~$ sudo ovs-vsctl add-port br1 geneve1 — set interface geneve1 type=geneve options:remote_ip=192.168.203.149[/shell]

[shell]mshindo@host-2:~$ sudo ovs-vsctl add-br br0
mshindo@host-2:~$ sudo ovs-vsctl add-br br1
mshindo@host-2:~$ sudo ovs-vsctl add-port bra eth0
mshindo@host-2:~$ sudo ifconfig eth0 0
mshindo@host-2:~$ sudo dhclient br0
mshindo@host-2:~$ sudo ifconfig br1 10.0.0.2 netmask 255.255.255.0
mshindo@host-2:~$ sudo ovs-vsctl add-port br1 geneve1 — set interface geneve1 type=geneve options:remote_ip=192.168.203.151[/shell]

このような設定をすれば、host-1とhost-2のbr1間のpingが通るようになり、物理ネットワーク側にパケットが出る際にはGenveでトンネルされます。
[shell]mshindo@host-1:~$ ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.759 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.486 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.514 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.544 ms
64 bytes from 10.0.0.2: icmp_seq=5 ttl=64 time=0.527 ms
^C
— 10.0.0.2 ping statistics —
5 packets transmitted, 5 received, 0% packet loss, time 3998ms
rtt min/avg/max/mdev = 0.486/0.566/0.759/0.098 ms
mshindo@host-1:~$ [/shell]

Wiresharkでパケットを見てみましょう。Wiresharkにも2014/06/16にGeneveのdissectorが追加されていますので、最新のWiresharkをビルドすればGeneveパケットの中身を見る事ができます。

Geneve Frame by Wireshark
Geneve Frame by Wireshark

Geneveが使うポート番号は6081/udpです(このポート番号は2014/03/27にIANAに登録されています)。また、今回のように単純にOVSのBridge同士を繋ぐだけであれば特にVNIは必要はありませんので、明示的にVNIの値を指定していません。その場合はVNIの値は0になります。VNIの値を明示的に設定したい場合は、

[shell]mshindo@host-1:~$ sudo ovs-vsctl add-port br1 geneve1 — set interface geneve1 type=geneve options:remote_ip=192.168.203.149 options:key=5000[/shell]

というようにoptionsでkeyというパラメータで指定すればOKです。その際のパケットは以下のようになります(10進数の5000は16進数では0x1388)。

Geneve Frame with VNI 5000 by Wireshark
Geneve Frame with VNI 5000 by Wireshark

GeneveはEthernetフレームだけではなく、他のタイプのフレームもトンネルできるようになっています。そのためにProtocol Typeというフィールドが用意されています。今回の例ではEthernetフレームをトンネルしていますので、Protocol Typeの値はTransparent Ethernet Bridgingを示す0x6558が指定されています。

現時点の実装では、Geneveの特徴であるGeneve Optionsを指定する事はまだできません。追ってサポートが加えられると思います。

Geneveの真価が発揮されるのは、Geneveのフレームフォーマットを理解してTSOをすることができるNICが出現した場合です。そのようなNICは現時点ではまだ世の中には出てないですが、少なくともソフトウェア的(OVS的)にはGeneve Readyになりました。現在はまだmasterブランチにしかGeneveのコードが入っていなかったのでgithubの最新masterブランチから取ったコードをビルドして試しましたが、OVSのバージョン2.2がリリースされれば、今回試したコードも含まれてくることになるので、簡単に試す事ができるようになるはずです。