Software-Defined Networks: A Systems Approach 日本語版+書き下ろし発売

この度、Larry PetersonとBruce Davie が中心となって書いているネットワーク関する書籍シリーズ “Systems Approach” の SDN 編 “Software-Defined Networks: A Systems Approach” という本の邦訳版「Software-Defined Networks ソフトウェア定義ネットワークの概念・設計・ユースケース」を出すことになり、その翻訳をやらせていただきました。6/22に発売開始予定です。

Software-Defined Networks: A Systems Approach

この翻訳本を出すことになったきっかけは本書の「訳者まえがき」のところでも書いたのですが、簡単に言うと、私の Nicira 時代の同僚で、VMware では私の直属の上司でもあった Bruce Davie から「”Software-Defined Networks: A Systems Approach” の日本語版を出したいので協力して欲しい」と相談されたのがキッカケです。原書を読んでみるととても興味深い内容だったので、ぜひ邦訳の手伝いをさせて欲しい、と返事をしました。ただ、私の知見だけでは不十分と思われたので、SDNやP4 に関して豊富な知識と経験を持たれている海老澤(通称 ebiken)さんにお声がけをし、ebiken さんがファブリックネットワークにも大変お詳しい小林さんに声をかけてくれました。お二人とも翻訳作業をご快諾いただき、3人で分担して翻訳作業にあたることになりました。

出版社は以前からお付き合いがあった翔泳社さんに相談をし、こちらもすぐに話がまとまり、出版に関して不慣れな我々を最後まで手厚くサポートしてくださいました。また、今回は単なる翻訳ではなく、日本オリジナルのコンテンツを加えたものにしたい、という意向もあったため、APRESIA Systems(株)、NTTネットワークイノベーションセンタ、東京大学中尾研究室の方々から新しい SDN のユースケースに関する情報を提供いただき、掲載いたしました。また、ヴイエムウェア(株)のRasmus Rusz Holtetに協力をしてもらって行った日本の SDN 市場に関するインタビュー記事も収録しています。

以下、本書の章立てになります。

第1章 SDNの概要と定義
第2章 ユースケース
第3章 基本アーキテクチャ
第4章 ベアメタルスイッチ
第5章 スイッチOS
第6章 ネットワークOS
第7章 リーフスパインファブリック
第8章 ネットワーク仮想化
第9章 アクセスネットワーク
第10章 SDNの未来
第11章 日本におけるSDN*
付録A [補足] ネットワークファブリック*
付録B [活用例] SDNとNFV*
付録C ハンズオンプログラミング — 演習問題
(*は日本語版オリジナルコンテンツ)

OpenFlow をきっかけに、世界的に SDN ブームが起こってから約10年が経過したわけですが、この10年間で SDN は大きく進化しました。本書がその理解への一助になれば、著者、訳者一同大変嬉しく思います。ご興味を持たれた方は、是非 Amazon でポチっとしていただければ幸いです(笑)

Autcraft: 自閉症を持つ子供のためのマインクラフト – Minecraft for kids with autism

昨日聞いた TED Radio Hour のエピソード “Work, Play, Rest – Part 2” の中の話がとても感動的だったのでご紹介をしようと思います。

紹介されていたのは “Autcraft” という自閉症を持った子供たち専用(大人ももちろんOK)のマインクラフト環境です。Stuart Duncanという人が 2013 年に立ち上げました。自閉症を患っている彼のご長男がマインクラフトに出会い、最初はとても楽しんでいたのですが、マインクラフトの中で馬鹿にされたりいじめられたりしたのが原因で、マインクラフトから徐々に遠ざかるようになってしまいまったそうです。Duncanさんは親として何かできないか、と考え、自閉症を持つ人のためマインクラフト環境を立ち上げたのが Autcraft です。虐めたり揶揄ったりしない、作っているものを壊したりしない、殺したり呪いをかけない、下品な行為をしない、などのルールを設け運用されています。2013 年に簡単な環境を作って公開したところ、2 日間で 750 通のメールを受け取る大反響だったそうで、現在でも1万人ほどの人々に利用されています。

対談の中で Duncan さんがこんなエピソードを話してくれました。Autcraft 立ち上げ当初からのユーザーで当時 12 歳だったある少年は、無事に高校、大学へ進学し、その間もAutcraft を楽しんでいたそうなのですが、大学卒業後は Autocraft から離れてしまったそうです。きっと忙しくなったのだろう、と Duncan さんは思っていたのですが、その彼から昨年のクリスマスにメールをもらいました。そして、彼からのメールにはこう書かれていました。「まだ Autcraft がきちんと動いていて多くの人に利用されていて嬉しいです。Autcraft が無かったら僕は高校にも行けてなかったと思うし、ましてや大学に進学して、やりたいクラブ活動に参加するなんてこともできなかったと思います。そして、今年長年の夢だった LEGO 社本社での採用が決まりました。ありがとうございます!」

ここで私は思わずウルっときてしまいました。技術の力をこんな風に使えたら素晴らしいですよね。興味がある方は是非このエピソードを聞いてみていただければと思います。Duncanさんのと話は 30:38 あたりから始まります。また、Duncanさんが以前に TED のステージで話された時の映像もあります。日本語字幕もありますすのでこちらも是非。

Photo by Nina PhotoLab on Unsplash

2021年総括

今年も無事にこれが書けることに感謝です。まさかコロナが2年続くとは思いもしませんでしたが、もうこれがノーマルになるような気もします。今年はオリンピックもありましたが、もう随分前な気がしますね。それでは、ここ数年やっている一年の振り返りをしてみたいと思います。

主な出来事

1月 clubhouse始める、でもすぐ飽きる
2月 特筆すべきことなし
3月 OCTO-GFI Conference(社内)、CNDOなどに登壇、長女結婚式
4月 次女が大学生に。G検定合格、食当たり?(原因不明)
5月 英会話レッスン再開、coursera開始、家族LINEMOへ乗換、ダニにやられる
6月 父永眠
7月 オリンピック開催、血尿出てビックリ
8月 初孫誕生、膀胱鏡検査悶絶、ワクチン1回目、たくろーさん永眠
9月 ワクチン2回目、56歳誕生日
10月 歯医者に通い始める、ONIC開催
11月 VMworld開催
12月 COSTCOで爆買

新たな取り組み

    • 1Password
    • Coursera
    • 英会話レッスン再開
    • LINEMO
    • メルカリ出品デビュー
    • おうちlab刷新

いろいろなサービスでパスワード漏洩のニュースが流れるたびに、サービスごとにパスワードを変えねば、と思いつつも、300 以上のサービスを使っているとなかなかそれも難しく、実行に移せていませんでした。が、今年ようやく重い腰を上げる決断をし、パスワード管理システムを使い始めることにしました。ブラウザのパスワード管理機能や Mac の Keychain を使う、という選択肢もありましたが、クロスブラウザ、クロスマシンで動くものを使いたかったので、1Password を使うことにしました。他にも LastPassBitwarden なども検討しましたが、まずは一つ使ってみようということで。結果、とても幸せになりました!

今年は Coursera というオンライン学習も始めてみました。手始めに University of Colorado Boulder 校の “The Little Stuff: Energy, Cells, and Genetics” というクラスを取ってみました。とても面白かったです。マウスに特定の匂いを嗅がせてから電気ショックなどのストレスを与えることを繰り返すと、匂いを嗅いだだけで怯えるようになる、というのはいわゆる「パブロフの犬」の実験でよく知られている条件反射なわけですが、なんとこのように訓練されたマウスの子孫にもそれが遺伝する、というのです。これにはびっくり! このように訓練されたマウスの遺伝子の塩基配列には変化が無いので、塩基配列以外のところで遺伝が起っているということになります。このような遺伝の仕組みを調べる学問は「エピジェネティクス(後成学)」と呼ばれ、近年注目を浴びているとのことでした。ふーむ、面白い!

おうちLabについては、機材は調達したものの、ここ数ヶ月忙しくて、残念ながら戯れる時間が取れませんでした。これについては,来年早いうちに記事を書きたいと思います。

Facebook Most Likes

3位 10/3 176, ONIC(お肉)打ち上げ(176 Likes)
2位 誕生日祝いのお礼(221 Likes)
1位 VMware 新オフィス@田町 (312 Likes)

コロナでオフィスを縮小もしくは完全に撤退する会社が多い中、VMwareは新しいオフィスを田町にオープンし、大幅な増床をしました。もったいないくらい素晴らしいオフィスです。このオフィスに負けないくらい素晴らしい仕事をしなければ、という気になりますね。

ONIC-2021
ONIC 2021 の打ち上げ
VMware Office at Tamachi
VMware の新オフィス@田町

映画

今年も出張がほとんどなかったので、映画も22作品と少なめです。(2019年は65作品、2020年は28作品)。ただ、今年は「鬼滅の刃」、「アルハンブラ宮殿の思い出」、「全裸監督2」、などのシリーズものも見たので、時間的には去年とさして変わっていないかもしれません。

今年見た映画のベスト3:

    1. コンフィデンスマンJP <ロマンス編>
    2. ANNA/アナ
    3. シティーハンター THE MOVIE史上最香のミッション

コンフィデンスマンJP、面白いですね。なかなか良くできています。ANNA/アナ、はどんでん返しに次ぐどんでん返しで、これも面白かったです。コンフィデンスマンJPといい、私、どんでん返しモノが好きなのかも。シティーハンターは言わずと知れた日本漫画の名作ですが、これはそれをフランスで実写化したもの。漫画の実写化というとガッカリすることが多いのですが、これはかなりいい感じです。原作に対するリスペクトが満ち溢れててとても良かったです。

Confidenceman JP
コンフィデンスマンJP <ロマンス編> (画像ソース: Amazon)
ANNA
ANNA / アナ(画像ソース: Amazon)
City Hunter
シティーハンター THE MOVIE史上最香のミッション(画像ソース: Amazon)

読書

今年は10冊ちょっと。まだまだ少ないですね。。うち、洋書が1冊。今年はディープラーニング(G検定)の勉強をしたので、技術系ではディープラーニング関係の本が多かったです。non-技術では「三体IIおよびIII」が圧倒的に面白かったです。新入社員の方に教えてもらった「動的平衡」も興味深い話でした。

Three Body Problem 2
三体II(画像ソース: Amazon)
Three Body Problem 3
三体III(画像ソース: Amazon)

Best YouTube Video

今年見た YouTube ビデオでベストだったのは、

でした。これは会社の同僚が教えてくれたもので、長年日本語言語学界隈で物議を醸してきた「象は鼻が長い」という文をどう解釈するか、ということについて話しているものです。この文、一見単純な日本語の文章に見えますが、さて、この文の主語は何だかわかりますか? 「象」なの? それとも「鼻」なの? あれれ、分からなくなってきましたよね(笑) この YouTube 動画はそれを解き明かしてくれます。

バイタル&健康

昨年は10 Kg以上の減量になりましたが、今年はそこからリバウンドしないことが目標でした。結果、1.6 Kg の体重増ということで、まあ、上出来だったのではないかと思います。体脂肪率もちょっと上がっちゃったのはご愛嬌。

Weight and Fat in 2021
体重と体脂肪率の推移 in 2021

今年の平均睡眠時間は5時間42分。去年より若干増えましたが、ほぼ同じペースです。

Sleep Meister Stat in 2021
Sleep Meisterでの統計 in 2021

NRC (Nike Run Club) でのランニングは引き続きやっています。今年は 1,200 km 以上走りました。「ランニングする前に読む本」という本を読み、スロージョギングをするようになったので、スピードは去年より落ちましたが、そのぶん距離を走るようになりました。通常、1 kmを 8 分くらいのペースで 5〜7 km、長いときは 10 kmくらい走っています。NTC (Nike Traning Club) でのワークアウトも続けています。

Nike Run Club in 2021
Nike Run Club in 2021

おかげさまで今年も大きな病気を患うことはありませんでした。4月に原因不明のムカムカと下痢を経験をしたくらいです。ただ、今年は何回か血尿を経験しました。いずれも走った直後の尿だけだったので、病気の可能性は低いと思いつつも、念の為に病院で調べてもらうことにしました。そしらた徹底的に調べてくれて(詳しくは「膀胱鏡検査」を参照)、結論としては問題ないだろう、ということになりました(ふー)。膀胱鏡検査はもう二度とやりたくないですw

ファイザーのワクチンを2度接種しました。副反応は全くと言っていいほど何も無く、なんだか肩透かしに合った感じでした。来年ブースターが打てるようになったらすぐに打とうと思います。Vaccination Certificate

仕事

    • VMware Japan Field Innovation Program 開始
    • 澤円さんとの対談

今年は Field Innovation Program という新しい試みの立ち上げに注力しました。その名の通りで、フィールドからイノベーションを加速させることを目的にしています。現在7つほどイニシアチブが立ち上がっており、参加してくれているメンバーが積極的に活動をしてくれています。

また、澤円さんと行った対談もとても良い経験でした。テーマは「マルチプラットフォームやクラウドネイティブ時代を生き抜くエンジニアのキャリアとは」。さすがに澤さん、お話が大変面白い。1時間ほどの対談でしたが、もっとお話をしていたかったです。その時の話が記事になっていますので、ご興味ある方は読んでみてください。

Fireside Chat with Sawa-san
澤円さんとの対談

プライベート

今年はプライベートでもいくつか大きな出来事がありました。

6月に父が亡くなりました。88歳でした。いまは、2年ほど前に先に天国に行った母と楽しく過ごしていることでしょう。

3月末に長女の結婚式を行いました。ちょうどコロナの合間のタイミングで、無事にできたのはラッキーでした。また、4月からは次女が大学生となり、北陸での生活が始まりました。そして、8月に長男夫妻のところに女の子が生まれ、初孫となりました(名実ともにおじいちゃんとなりましたw)

Wedding
長女の結婚式
The First Grand Daughter
初孫誕生

そして同じ月に、長年大変親しくさせていただいていた歌仲間の「たくろーさん」がご逝去されました。あまりに突然のことで心の整理がつかず、涙が出なくなるくらい泣きました。今頃は天国で大好きだった “You Don’t Have To Be A Star” を思い切り歌われていることでしょう。沢山の楽しいい思い出をありがとうございました。

買ってよかったもの

今年買ってよかったなと思ったものは、

Sticker
画像ソース: Amazon

です。実家への投げ込み広告防止の為に買ったのですが、それなりの効果はあったようです。350円投資の効果は十分にあります(笑)

Blog

今年はこの記事を含めて外向け3本、社内向け2本、英語のものはなし、という少々寂しい結果となってしまいました。外向けの技術に関するものも1本だけ。それも「このままだと今年のBlogは膀胱鏡検査話だけになってしまう!」という危機感から、慌てて書いた一本という体たらく。来年はもっと頑張りたいと思います。

コミュニティへの貢献

相変わらず VMware DevOps MeetupOpen Networking Conference (ONIC) は継続してやっています。また、Cloud Native Days Spring 2021 OnlineOpen Policy Agent Rego Knowledge Sharing Meetup などでもお話をさせていただきました。

オープンソースへの貢献は今年も殆どできず、簡単な pull req をいくつか送っただけでした。

今年前半、COVID-19によりインドが大変厳しい状況に陥ったため、2021 India COVID-19 Recovery Fund に寄付を行いました。また日本ユニセフにもわずかながら寄付をさせていただきました。

また、オープンソースというわけではないですが、今年の後半取り組んだプロジェクトがあります。来年前半にはご報告できるのではないかと思います。

フライト

今年は国内、国際ともに一度も飛行機には乗らずじまいでした。物理的に移動をしなくても概ね支障なく仕事ができるのが分かった一方、やっぱり物理的に移動する重要さも痛感しました一年でした。来年は少し移動できるようになるといいなー、と思います。

今年新規開拓した肉屋

外出が減ったせいで、新しい開拓は少なめです。

もちろんこれしか肉を食べに行かなかったわけではありませんよ。リピートした店もそこそこあります^^

2022年の抱負

まず去年掲げた2021年の抱負を振り返ってみます。

    • 筋肉つける(未達
    • 1,000キロ走る(達成
    • 読書20冊(技術書10冊、non技術書10冊)(未達
    • コードの貢献(未達
    • 月1料理を月2料理くらいに(未達
    • お家ネットワークの10G化(未達

ひどい、ひどすぎる。ま、全部自分の怠惰が原因なわけですが。。。

というわけで2022年の抱負は(できるかぎり定量的に)、

    • 筋肉つける(筋肉量50Kgを目指す)
    • 読書20冊(技術書10冊、non技術書10冊)
    • Blog 4本(できれば英語でも)
    • 1 Project にコードの貢献
    • TOEIC 950、IELTS Academic 7.5
    • ハーフマラソンに参加
    • せめて月1料理を

を上げたいと思います。来年の振り返りでまた惨敗となる可能性も大きいですが、目標は高く持つべし!とうことで。自らを追い込むタイプ。

それでは、皆さんまた来年もよろしくお願いいたします。

おうち Lab と GitDNSOps

本記事は、VMware DevOps Meetup #10 でお話した内容に修正加筆をしたものです。

我が家の「おうち Lab」は、他の方の「逸般の誤家庭」に比べるとかなりおとなしめです。

ちょっと変わったところというと、インターネット回線が2本引いてあることくらいでしょうか。なぜ2本引いているのかは、我が家のネットワークの歴史的背景によるものなので、まずちょっとだけその話を。

私が住んでいるのは築20年ほど経ったマンションです。マンション購入時に光回線を引くことを条件にしたのですが、当時はまだ光によるインターネットサービスは一般的ではなかったので、PRI 回線を引き込んでほぼ休止扱いにしてありました。その後、マンションにインターネットサービスが2つ提供されることになり、一つはケーブル TV 系の VDSL サービスで、もう一つは UCOM(現アルテリア・ネットワークス)によるもので、宅内までイーサを引き込むものでした。当然、私は後者を選択。帯域的なメリットもありましたが、それよりも当時 UCOM のインターネットサービスは /29 のグローバルアドレスをくれるという太っ腹ネットワークだった、いうのが UCOM を選択した大きな理由でした。この UCOM のネットワークは非常に品質の高いネットワークサービスを提供してくれていてとても満足しています(詳しくは「自宅のインターネット回線品質をVeloCloudで測ってみた」を参照)

ただ、UCOM のネットワーク帯域は 100Mbps で今となっては少々寂しいですし、IPv6 のサービスも提供されていません。また、いっとき私は SD-WAN のスタートアップ (Viptela) に勤めていたので、SD-WAN のテストをするために複数回線が欲しかった、ということもあり、複数ネットワークを引き込むことにしました。前に引いてあった光回線を活用して FLETS 光を利用し、更に NURO を加え3回線にした後、現在は FLETS を解約し、UCOM と NURO の2本構成に縮退しています。

その他は至って普通の Lab 環境です。Intel NUC や自作サーバで vSphere を利用して仮想環境を用意していろいろなものを検証しています。外向きに出しているサービスは個人用 blog サーバと、DNS くらいです。自分が保有しているいくつかのドメイン用の権威 primary サーバとして coreDNS、secondary サーバとして NSD を GCP 上で動かしています。以前 secondary DNS は実家の Raspberry Pi 上で動かしていたのですが、今年実家に誰もいなくなりネットワークも廃止したため、引っ越しを迫られ、引越し先をいろいろと検討しました。もちろん自宅環境に移すこともできたのですが(幸いグローバルアドレスもありますし)、同じ failure domain に primary と secondary を置くのは避けたかったので、別の方法を模索しました。いくつかのドメインは「お名前.com」で取得したもので、お名前.com はそこで取ったドメインに関しては無料で secondary DNS のサービスをしているので、これが使えたらベストだったのですが、あいにく私がメインで使っているドメイン “shin.do” はお名前.comで取ったものではなかったので、この方法は見送りとなりました。色々検討した結果、最終的には GCP 上のフリーインスタンス (e2-micoro) を使って secondary DNS (NSD) を動かすことにしました。無料のはずなのですが、毎月110円ほど費用が発生しているのが謎ですが、今のところ深く気にしないことにしています(笑)また、Lab ネットワークのための DNS cache サーバ兼簡易権威 DNS サーバとして unbound を使っています。

前置きが長くなりましたが、今回はこの外向きの coreDNS で GitOps をしてみる、というのをやってみました。GitOps とはなんぞや、という話は ここここ などを参照ください。要は Git リポジトリ内容を唯一の真 (Single Source of Truth) として扱って宣言的にオペレーションを廻してやろう、という考え方/方法、です。Git を使うので、バージョン・コントロールしやすく、アクセス・コントロールや監査が容易、というメリットなどを享受することができます。

ただ、私の管理している DNS サーバは基本「ワンオペ」なので、アクセス・コントールや監査のしやすさはあまり関係ありません。気軽にロールバックできたりするのは少しだけありがたいですが、そこまで大きな魅力でもありません。それでも DNS に GitOps を適用しようと思うのは、半分ノリと、DNS の変更を git push だけ済ませられるというちょっとした心地よさからでしょうか。。GitOps を DNS に適用するので、これを私は “GitDNSOps” と呼んでいます。はい、勝手に私がそう呼んでいるだけで、一般的に受け入れられている言葉ではないです。でも、流行ったら少し嬉しいので、GitDNSOps、GitDNSOps、GitDNSOps、と3回叫んでおくことにします。今回、Kubernetes 環境は microk8s を使って構築し、LoadBalancer には MetalLB を使いました。この選択に特段深い意味はないので、どっかのタイミングで Tanzu Community Edition に置き換えてもいいかな、と思っています。また、GitOps には ArgoCD を使いましたが、flux などを使っても全然構いません。

coreDNS の設定は Kubernetes の ConfigMap で食わせています。特に変わった設定はありませんが、Corefile で ”reload 10s” と指定している部分だけ説明だけ説明をしておこうと思います。この設定をすると coreDNS は指定された時間(この例だと10秒)で再度設定を読み直してくれます。この設定をしないと、git push して ConfigMap が書き換わっても、coreDNS はそれに気づいてくれないので、DNSのリソースレコードが更新されません。というわけで、若干エレガントさに欠けますが、reload 設定をして設定を読み直すようにしています。

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
data:
  Corefile: |
    shin.do:5353 {
        file /etc/coredns/shin.do.zone
        reload 10s
    }
  shin.do.zone: |
    $TTL  3600
    @ IN  SOA ns1.shin.do.  root.shin.do. (
            2021081404 3600 900 3600000 3600 )  
         IN  NS  ns1.shin.do.
         IN  NS  ns2.shin.do.
         IN  MX  10 ASPMX.L.GOOGLE.COM.
    ns1  IN  A 221.245.168.210
    ns2  IN  A 35.247.124.32
    blog IN  A 221.245.168.211

それではみなさん、GitDNSOps で気持ち良い DNS 生活を!

(Photo by Roman Synkevych on Unsplash )

膀胱鏡検査

本日、病院に行って下腹部のCT検査と膀胱鏡検査をしてきました。

事の始まりは7月末に二度ほど血尿が出たことです。血尿というと人によってはトマトジュースのようなのが出ることがあるらしいですが、私のはそんなに酷いものではなく、薄い茶褐色という感じでした。二度とも軽いランニング直後のトイレで一度だけ出たもので、それ以降は(少なくとも目に見えるような形では)血尿が出ることはありませんでした。運動後に血尿が出るのはさほど珍しいことではないらしく、私のもおそらくランニングに起因していたのだろうと思ったのですが、念の為病院に行ってみることにしました。

近くのクリニックで診てもらった限りでは問題なし。ただ、きちんと調べるためには設備のある病院に行ってください、ということだったので、紹介状を書いてもらって港南台にある南部病院へ行くことにしました。まずは尿検査、血液検査、下腹部のX線検査をしてもらい、特に問題は見つかりませんでしたが、より詳細を調べるためにCT検査と膀胱鏡検査をしましょう、ということになり、本日再度南部病院に行ってきたのでした。

CT検査は今まで何度かやってますし、勝手は分かっています。しかし胱鏡検査は今回初めての経験です。膀胱鏡検査については特に何も下調べをせずに臨みました。「どうやってカメラ入れるの? 入れるの一箇所しかないと思われるけど(汗)。痛いの? 麻酔はどうするの? 注射? それも怖いゾ。揉み込む? 漬物ちゃいますがな」、など、漠然とした不安をを抱えなら検査を待つこと小一時間。看護師さんに「麻酔はしますよ〜」と言われ、ちょっとだけ安心したところで診察室に呼ばれました。まず、ズボンとパンツを脱いで診察台に上がるように言われます。電動で股がグイーンと開くようになっています。下半身なにも着けずこの状態でいるだけでもかなりの羞恥プレーです。看護婦さんに軽く「先」を消毒され、真打ちの先生登場。「じゃ、行きますよ〜」と言われ、やおら差し込まれました。ひー、ぎゃー、麻酔はどこいったの〜。強烈です。更に押し込まれ激痛が走ります。「ここが一番きついところですからね〜、頑張って!」と先生。いや、先生、これ以上があったらもう耐えられないです、私。「力入れないで下さいね〜」と言われるも、こんなん力入れないなんて無理ですがな。これを平静心で受けられるやつがいるとでも!? などと思いながら、悶絶すること5分少々。「はーい、これで終わりです」という一言で、すーっと抜かれ、無事こちらの世界に戻ってきましたw

麻酔は、麻酔はどうしたの?という感じですが、そんな弱音を吐くところを看護師さんに見せるなんて、っていう男のプライドが邪魔をして結局聞けずじまい。カメラを入れるときに潤滑ゼリーのようなものは使ってたと思うので、その中に麻酔成分が入ってたのかも知れません。でも、そんないきなり入れらたたら、麻酔効いている暇もありませんがなw

結局、膀胱鏡検査でも血尿につながるようなはっきりとした原因は見つけられずでした。ただ、膀胱の中に赤い斑点のような箇所が数箇所見つかり、そこから出血してたのかも、という見解でした。先生曰く「この斑点、見たところ悪性のものには見えないけど、これが大きくなるようだと心配だから、何ヶ月か後にもう一回調べましょっか。」って、えー、またこれやるんかい!(絶句)

Photo by Diana Polekhina on Unsplash

2020年総括

ここ数年恒例でやっている、今年一年の振り返りと来年への抱負を書いておこうと思います。

主な出来事

1月 極寒ソウル出張、JANOG45、関谷先生お祝い
2月 コロナ前滑り込みセッション、アカデミー賞を米国TVで観賞、NURO開通
3月 在宅生活開始、IELTS受験
4月 NTC開始、完全在宅
5月 完全在宅、会社のミュージックビデオ作成に参加
6月 Google Cloud イベント基調講演
7月 大学時代の友達から嬉しい連絡
8月 NRC開始、夜中に漏電ブレーカー落ちる
9月 誕生日メッセージ約100件
10月 ONICオンライン開催
11月 iPhoone 12ゲト、VMworld Japan開催
12月 会社PCリニューアル, Apple Watchゲト

新たな取り組み

    • 年賀状廃止
    • IELTS受験
    • 月一料理
    • SwiftUI
    • Nike Training Center (NTC)
    • Friends シーズン1制覇
    • Zoom飲み会
    • Nike Run Center (NRC)
    • Rakutenに魂を売る
    • Appleにも魂を売る

Facebook Most Likes

    • 3位、カブトムシ訪問(118件)
    • 2位、Google Cloudイベント登壇(142件)
    • 1位、年賀状やめます(175件)

何よりも「年賀状やめます」が一番だったのは興味深いですw

映画

今年はコロナの関係で海外出張がほとんどなかったため、見た映画の数も例年よりはかなり少なかったです(2019年は65本、2020年は28本)。洋邦を問わずベスト3は

    1. パラサイト
    2. ジョジョ・ラビット
    3. TENET
パラサイト 半地下の家族(画像ソース:Amazon)
ジョジョ・ラビット(画像ソース:Amazon)
TENET(画像ソース:Amazon)

番外編として「コンテイジョン」はかなり衝撃的でしたね。まるで予言のようでした。

バイタル

睡眠は平均5時間36分。去年より20分ほど短くなってました。Work From Homeの影響かもしれません。

Sleep Meister 2020
Sleep Meisterでの2020年統計

一方、体重は大きく変わりました。3月からほぼ完全リモートワークとなり、家から一歩も出ない日が続くようになりました。特に非常事態宣言の出ていた4月、5月はそれぞれ1日の平均歩数が638歩、368歩という酷いありさま。それもそのはず、この2ヶ月間は家から一歩も出ませんでした。これは流石にいかんだろう、ということで、4月から Nike Training Center (NTC) を使って1日1度15分程度の軽いワークアウトをすることにしました。これが案外楽しくて、ほぼ毎日欠かさずやることとなり、今日がちょうど300回目のワークアウトでした。

Nike Training Center300回目

それに伴い体重も良い感じで減りましたが、ワークアウトだけでは限界を感じ、8月からは Nike Running Club (NRC) を使って家の周りを走ることにしました。8月からの5ヶ月間で440Km走りました。毎日3〜4Km程度を7分/kmくらいのペースで軽く走っているだけなので、ガチで走る人から見たら早歩き程度だと思いますが、それでもそれなりに効果はありさらに体重は減少。結局今年一年で体重は10.2Kg減、体脂肪率も7.5ポイント下がりました。この習慣は来年も続けていきたいと思います。

Weight and Fat Scale
体重と体脂肪率の推移

読書

つまみ読みしたものはそこそこあれど、ちゃんと読んだのは6冊。相変わらず少な過ぎですね。ただ6冊中5冊は純粋な技術書ではなかったので、そこは去年と比べると改善したポイント。友人から勧められた西和彦さんの「反省記」は懐かしい話満載で面白かったです。

仕事

今年はGoogle CloudさんのGCVEサービスのローンチイベントで基調講演をさせていただいたのが大きなイベントでした。また、JANOG、Cloud Operators Day、ONICなどでも登壇をさせていただきました。去年まではvFORUMと呼ばれていた弊社のイベントですが、今年からは VMworldと名前が変わり、そこで弊社のイノベーションに対する取り組みについてお話をさせていただきました。やらせていただいたセッションはライブのデモも多かったので、いろいろハラハラする場面もありましたが、なんとか大きな放送事故は起こさずやり切ることができました(笑)

今年の新たに取り組んだものとしてはSpatial Computingがあります。VR/AR/MRなどの技術の総称です。Oculusを手に入れていろいろ試してみていますが、大きな可能性を感じる分野です。今後も継続して取り組んでいきたいです。

また、仕事とは直接関係ありませんが、弊社のDiversity & Inclusionチームと協力して、障がいを持たれた方々が作られたクッキーやコーヒーを社員の皆さんにお届けする、というプロジェクトにも取り組みました。このような機会をくれた Sweet Heart Project さんに感謝です。いろいろと乗り越えなければいけない壁がありかなり大変でしたが、なんとかクリスマスプレゼントとして社員の皆さんにお届けすることができました。来年以降も何かしらの形でやっていきたいと思います。

Sweet Heart Projectからのクッキーとコーヒー詰め合わせ

プライベート

プライベートでの大きな変化は、長男長女が相次いで結婚したことですね。長男は、沖縄でのコロナの状況が悪化する直前に滑り込みで結婚式をあげることができました。長女の結婚式は来年予定。旅立つのはあっという間ですね。

結婚式2020@沖縄

残念ながら今年はライブ活動はほとんどできませんでした。コロナ直前にバンド仲間でセッションをやったのと(セッションは初体験で楽しかったです!)、社内で各国から有志メンバーを募り音楽ビデオクリップを作るというのもやりました。昨今、音楽のテクノロジーも進んでいて、離れていても創作活動が出来ちゃうのにびっくりしました。

また、生活上一つ変化がありました。メインのクレジットカードを楽天にして、生活は可能な限りこちらのカードを使うようにしたことです。楽天はやっぱりポイントがザクザク溜まりますね〜。生活のかなりの部分を一社に握られている気がしてちょっと心配ですが、賢く使っていきたいと思います。

iPhone 12 にしたのも大きな変化でした。去年は「iPhone みたいな高価なスマホはいらん。Google Pixel 5にする!」と息巻いていたのですが、いざ両機種出てみるとなぜか iPhone 12 Pro Maxを買ってました(笑) 価格以外に iPhone 12 に不満はないですが、iPhoneの怖いところはAppleのエコシステムにズブズブと引き込まれてしまうところですね。先述したように、ワークアウトやランニングをするようになるとスマートウォッチが欲しくなります。長い間静観していたApple Watchにとうとう手を出してしました。そうなるとイヤホンも欲しくなります。ちょうど使っていたBluetoothのイヤホンが壊れたこともあり、耳垂れうどんと揶揄されるAirPods Proもポチッとしてしました。AppleからARグラスが出たらきっと買っちゃうんだろうなー。

普段からよく集まっているメンバーとZoom飲み会も結構やりました。リモート飲み会だと気軽に集まれるようになって、むしろ以前より密なコミュニケーションが取れるようになったのはとても良かったと思います。

Blog

今年は外向け4本、社内向け2本。英語のもはなし。Stay Homeで書く時間はある程度あったはずなのに、なんともお恥ずかしい限りです。ただ、「宣言的ネットワーキングとインクリメンタル処理」については渾身の一本だったかと思います。ご興味のある方はご一読いただけると幸いです。

コミュニティへの貢献

こちらも毎年言っているものの、なかなか実現できていません。今年はほんの少しだけAntreaで使っているIPFIXライブラリにcontributionしましたが、途中で仕事が忙しくなってしまい、それ以降はできなくなってしまいました。ま、仕事が、とかは単なる言い訳で、要は自分のやる気と実力が十分でないって事ですね。来年また頑張ろう!

フライト

今年は海外出張が2回、国内出張・旅行が2回、という、かつてない少ない回数となりました。というわけで、去年までのマイル修行の恩恵は今年はほとんど受ける事ができませんでした。来年はもう少し外に出られるようになるといいなー。

今年新規に開拓した肉屋

こちらも今年はほとんどなし。

    • 牛和鹿 六本木店
    • ジャッキーステーキハウス 沖縄
    • ヤンバルミート 沖縄
    • 焼肉やまと、越谷(20年ぶりの再訪)

2021年の抱負

    • 筋肉つける
    • 1,000キロ走る
    • 読書20冊(技術書10冊、non技術書10冊)
    • コードの貢献
    • 月1料理を月2料理くらいに
    • お家ネットワークの10G化

皆さん、また来年もよろしくお願いいたします!

Photo by Kelly Sikkema on Unsplash

宣言的ネットワーキングとインクリメンタル処理

はじめに

2020年1月に札幌で行われたJANOG45で、Cisco Systemsの河野美也さんと共同で「宣言的(Declarative)ネットワーキング」というセッションをやらせていただきました。その時の資料はこちらです。残念ながら30分と時間が限られていたため、十分にお伝えすることができなかった部分もあるため、こちらに補足記事としてまとめておくことにしました。

そもそも宣言的(Declarative)って何?

最近「宣言的」や「Declarative」という言葉をよく耳にするようになっていると思います。実は、宣言的/Declarativeという言葉はそう新しいものではなく、実はかなり前から使われている言葉です。プログラミング言語の世界では関数や述語論理に基づいたプログラミング言語などが従来型の命令的プログラミング言語との対比として「宣言的プログラミング言語」と呼ばれていました。比較的最近では、Ansibleなどの構成管理ツールやJenkinsなどのCI/CDツールにも宣言的な側面があると言われていますし、FlutterやSwiftUIなどのU/Iフレームワークも「宣言的U/I」などと呼ばれ注目されています。このように「宣言的」という言葉はさまざまな文脈でいろいろな意味で使われており、決定的な定義があるわけではありません。宣言的という言葉が持つ多面性については河野さんがこちらの記事で言及してくださっていますので是非ご一読ください。

しかし、最近「宣言的」という言葉をよく聞くようになったのはなんと言ってもKubernetesの影響でしょう。Kubernetesが宣言的であると言われるのは、Kubernetesでは各種リソースの望まれる状態(Desired State)をManifestとして定義する事ができて、現在の状態(Current State)とDesired Stateの間にズレがある場合は、コントローラがそれを検知し自動的にそのズレを修復するように動くようになっているからです。「どのようにしてそのズレを解消するかは問わない。ともかくDesiredな状態にしてくれ!」と指示をするので、Kubernetesは宣言的であると考えらています。Desired Stateと現在のStateを常に比較し、差異があればそれをなくそうとする動作はReconciliation Loopと呼ばれており、各コントローラに実装されています。Kubernetesに自己修復(Self-Healing)性があると言われるのは、Kubernetesの各種コントローラにこのようなReconciliation Loopが実装されているためです。

宣言的(Declarative)ネットワーキング

JANOG45のセッション後に幾つかのフィードバックをもらいました。その中の一つは以下のようなものでした。

非常に妥当なご意見だと思いますし、他にも同じように感じられた方も多かったようです。なぜ今日のネットワークはKubernetes的な宣言性を持ち得ていないのか、どのようにしたらそれを実現できるのか、という点に興味を抱いている人は多いんですね。これは大変興味深い話であり、私自身も大きな関心を持っています。

個人的には、今日のネットワークがKubernetes的な宣言性を持てていない理由の一つとして「Desired Stateを定義するのが難しく、Reconciliationするのも難しい」という点があるのではないかと思っています。KubernetesのDeploymentであれば、どのようなコンテナがいくつ動いているかを把握したり、アプリケーションが応答するかをlivenessProbeなどで監視して、Desired Stateとのズレがあったら基本的に再度コンテナを立ち上げ直す事でReconciliationを図ります。では、ネットワークのDesired Stateって何でしょう? おそらく「ホストAからホストBにパケットが届く事」あるいはL4まで考えれば「ホストA上のサービスCからホストB上のサービスDへパケットが届く事」ということになるのではないかと思います。このようなDesired Stateを検出するのはなかなか困難です。コンテナは一次元的な要素なので比較的シンプルですが、ネットワークはsourceとdestinationがある二次元的な要素ですので、N^2問題に直面します。ホストがN台あったとすると、全ての到達性を確認するためにはN*Nのprobeをしなければいけません。Network Policy(ACL)も含めてネットワークのDesired Stateを考えると複雑性はN*Nでは済まず、サービス数Mも含めて考えてN*M*N*Mでの到達性を把握する必要があります。パケットの到達性はsourceに依存するので、このようなprobeをどこからしたら良いのか、という点も悩ましいです。また、仮にこのようなProbeできてネットワークのDesired Stateと現在のStateにズレをうまく検出できたとしても、それをどのようにReconcileするかも難しいです。KubernetesのDeployment/Podの場合は、単純にコンテナを作り直す、という戦略を取っているわけですが、ネットワークの場合は、単純にネットワークを実現しているコンテナを再起動しても解決しない場合が多いと思います。ネットワークのエンドポイントに問題はなく、途中の経路に何か問題があって疎通性が阻害されている場合には、いくらエンドポイントを再起動してもおそらく問題は解決しないでしょう。

このように、現在のネットワークにKubernetes的な宣言的特性や自己修復性を持たせるのは簡単なことではないように思います(少しドメインを限定すればネットワークにも宣言性を持たせることは可能という考え方もあります[3])。ただ、上記のような宣言的ネットワーキング / Declarative Networkingの困難さに立ち向かおうとしている人はきっといるはずです。非常に有望な「研究課題」と言えるでしょう。

もう一つの宣言的ネットワーキングの側面

上記のようなネットワークのKubernetes的な宣言性は非常に興味深いトピックではありますが、私がJANOG45でお話をさせていただいたのは、これとはちょっとまた別の観点からの宣言的ネットワーキングの話で、SDNコントローラに宣言的なエンジンを用いることによっていかに堅牢で効率的なコントローラを実現できるか、という点でした。

ネットワークは非常に複雑な分散システムです。設定している最中に障害が起こり得ます。一般的にネットワーク設定は冪等ではありません(例えばポートを作った後にどこか別のノードで設定が失敗し、再度最初から同じ設定を入れようと思っても、すでにポートが作成済であればエラーになってしまうでしょう)。したがって、設定の途中で障害が起こったら単純に最初からやり直すだけではダメで、綺麗にロールバックしてやらなければいけません。ロールバックしている最中にまた別の障害が起こる、など多重障害が起こる可能性もあります。このようにさまざまなFailureシナリオにきちんと対応するするのは簡単ではありません。堅牢なSDNコントローラを作るのは非常に難しいことなのです。

Niciraの創業者の一人であるMatin Casadoから聞いた話なのですが、「Niciraでは幾つかのSDNコントローラを試作したが、上記のようなネットワークの複雑性からどれもなかなかうまくいかなかった。最終的に最も堅牢に動いたのはDatalogをベースにした宣言的なエンジンを使ったコントローラだった」とのことでした。この話を聞いて以来、私はずーっと宣言的なSDNコントローラに興味を抱いていました。

たまたまCiscoの河野さんも宣言的ネットワークに興味関心をお持ちである、ということを知り、それでは共同でJANOGでセッションをやってみましょう、ということになった次第です。河野さんからは宣言的ネットワークに関するさまざまな観点と考察を、私の方からは宣言的なSDNコントローラエンジンの適用事例の一つとして、OVNのコントローラにおけるDatalog技術の適用のお話をさせていただきました。OVN の概要とアーキテクチャに関してはこちらこちらを参考にしてください。

SDN コントローラとテーブル更新

一般的にSDNコントローラは多数のテーブルを扱います。その中でもフローテーブルは非常に巨大かつ動的に更新される必要があるものです。VMやコンテナは別のホストに移動する可能性があるので、それらのイベントに追従する形でフローテーブルを動的に更新してやらなければいけません。これを大規模な環境でやるのは思いの外大変です。

仮に2台のハイパーバイザ(Host1とHost2)と仮想マシンが3台(VM1、VM2、VM3)があり、VM1とVM2がHost1上で動いていて、VM3がHost2上で動いていたとしましょう。

SDNコントローラはそのような状況を表す以下のようなテーブルを持っているはずです。

Controller:

VM IP Host Port
VM1 10.10.10.101 Host 1 2
VM2 10.10.10.102 Host 1 3
VM3 10.10.10.103 Host 2 2

SDNコントローラはこのテーブルから各ハイパーバイザ(Host1、Host2)のフローテーブルを以下のように設定することになります。

Host1:

Match Action
dst=10.10.10.101 output:2 (local port)
dst=10.10.10.102 output:3 (local port)
dst=10.10.10.103 output:1 (tunnel port)

Host2:

Match Action
dst=10.10.10.101 output:1 (tunnel port)
dst=10.10.10.102 output:1 (tunnel port)
dst=10.10.10.103 output:2 (local port)

このようなフローテーブルの設定を行うには、概ね以下のようなコードをコントローラに実装すれば良いでしょう。

for h in Hosts:
  for vm in VM:
    if vm.host == h:
      h.add_flow(match="dst={vm.ip}", action="output:{vm.port}")
    else
      h.add_flow(match="dst={vm.ip}", action="output:{tunport}")

ここで、仮にVM2がHost1からHost2に移動(vMotion/Live Migration)したとしましょう。

コントローラはこのようなVMの移動を把握することができますので、自分が持っているテーブルを以下のように更新します。

Controller:

VM IP Host Port
VM1 10.10.10.101 Host 1 2
VM2 10.10.10.102 Host 2 3
VM3 10.10.10.103 Host 2 2

当然この変更に合わせてハイパーバイザ上のフローテーブルも更新しなければなりません。一番簡単な方法は、上にあげたロジックを再度実行することです。こうすれば適切なフローテーブルが新たに各ハイパーバイザに設定されるはずです。

Host1:

Match Action
dst=10.10.10.101 output:2 (local port)
dst=10.10.10.102 output:1 (tunnel port)
dst=10.10.10.103 output:1 (tunnel port)

Host2:

Match Action
dst=10.10.10.101 output:1 (tunnel port)
dst=10.10.10.102 output:3 (local port)
dst=10.10.10.103 output:2 (local port)

しかしこれはあまりに非効率です。テーブルが小さいうちはこのような実直な方法でも良いかもしれませんが、テーブルサイズが大きくなってきた場合、この方法は明らかにCPUやネットワーク帯域の無駄になります。大規模環境では設定にかかる時間も無視できないほど大きくなってしまうでしょう。もう少し良い方法はないでしょうか?

誰もがすぐに思いつくのはおそらく「変更があったところだけ更新をする」という方法でしょう。いわゆるインクリメンタルなアップデート処理をすれば良いわけです。アルゴリズム的にはこんな感じになるでしょう。

for h in Hosts:
  if old_vm.host == h:
    h.del_flow(...)
  else
    h.del_flow(...)
  if new_vm.host == h:
    h.add_flow(match="dst={new_vm.ip}", action="output:{new_vm.port}")
  else 
    h.add_flow(match="dst={newvm.ip}", action="output:{tunport}")

一見簡単そうな変更に見えるかもしれませんが、実は結構面倒な処理になります。このようなインクリメンタル処理のアルゴリズムはテーブル毎に個別に実装してやらなければいけません。OVNコントローラには数十個のテーブルがありますし、NSX-Tでは百を超えるテーブルがありますので、すべてのテーブルにこのようなインクリメンタル処理のアルゴリズムを個別に実装していくのはそう簡単な作業ではありません。また、テーブル間には依存関係がある場合があるので、一つの変更に対して、整合性を保つように複数のテーブルを注意深く更新していかなければなりません。設定の途中でエラーになった際のリカバリー処理なども考えると、テーブルごとにインクリメンタルな処理を実装する方式はかなり難度が高いことで、場合によってはコントローラの堅牢性を失いかねません。

宣言的コントローラエンジンとDatalog

OVNは大規模環境でも堅牢に動くProcution ReadyなSDNコントローラを目指していますので、まさに上記のような課題に直面することになります。

OVNではovn-northdとovn-controllerという2種類のコントローラが動いています。前者がネットワーク全体を管理するコントローラ、後者が各ハイパーバイザで動いているローカルのコントローラです。現在、ovn-northdコントローラにDatalogを使った宣言的な仕組みを適用する試みが行われています。

Datalogは一階述語論理に基づいた宣言的なプログラミング言語です。文法的にはPrologに似ていますが、Prologとはいくつかの点で異なり、演繹的データベースの問い合わせに使われることが多い DSL (Domain Specific Language) の一つです。

例えば、

Parent(Anakin, Luke).
Parent(Anakin, Leia).
Sibling(c1, c2) :- Parent(p, c1), Parent(p, c2), c1 != c2.

というDatalogのプログラムからは

Sibling(Luke, Leia).
Sibling(Leia, Luke).

という結果が導出されます。”:-” を含んだ行は「ルール」と呼ばれ、”:-” の右側の項が全て成り立つ時に”:-” の左側が成り立つ、という意味になります。”:-” を含まない行は「事実(Fact)」と呼ばれます。上記のDatalogプログラムをざっくりと日本語にすると、

Lukeの親はAnakinである(事実)。Leiaの親はAnakinである(事実)。c1とc2が共通の親を持っており、c1とc2が異なれば、c1とc2は兄弟である(ルール)。

ということになり、そしてそこから導かれる結果は、

LukeはLeiaと兄弟である。LeiaはLukeと兄弟である。

となります。

Datalogを使うと、先に紹介した手続き的なフローテーブルの書き換え処理を宣言的に記述する事ができます。例えば、

Host(1),
Host(2);
VM("VM1", "10.10.10.101", 1, 2),
VM("VM2", "10.10.10.102", 1, 3),
VM("VM3", "10.10.10.103", 2, 2);
Flow(h, "dst=${ip}", "output:${p}") :- Host(h), VM(_, ip, h, p).
Flow(h, "dst=${ip}", "output:1") :- Host(h), VM(_, ip, other, _), other != h.

というルールとFactから

Flow(1, dst=10.10.10.101, output:2).
Flow(1, dst=10.10.10.102, output:3).
Flow(1, dst=10.10.10.103, output:1).
Flow(2, dst=10.10.10.101, output:1).
Flow(2, dst=10.10.10.102, output:1).
Flow(2, dst=10.10.10.103, output:2).

という結果を自動的に演繹的に導出することができます。

また、VM2がHost 1からHost 2のポート3に移動したとすると、

VM("VM2", "10.10.10.102", 2, 3),

のようにFactをアップデートをすれば、それを反映した正しい結果、

Flow(1, dst=10.10.10.101, output:2).
Flow(1, dst=10.10.10.102, output:1).
Flow(1, dst=10.10.10.103, output:1).
Flow(2, dst=10.10.10.101, output:1).
Flow(2, dst=10.10.10.102, output:3).
Flow(2, dst=10.10.10.103, output:2).

という結果を得ることができます。このように、Datalogを使うとフローテーブルがどのようなルールに則っているべきかを宣言的に記述するだけで良くなり、SDNコントローラを実装する人間はどのようにハイパーバイザのフローテーブルを設定すべきか、そのロジックを考えなくて済むようになります。これはとてもありがたいことです。

Differential Datalog

Datalogを使うと、フローテーブルなど、SDNコントローラが扱わなければいけない各種テーブルの更新処理を宣言的に記述することができるようになります。手続き的に書くよりはるかにメンテナンス性の良い記述をすることができるようになるので、コントローラの堅牢性の向上に大きく貢献すると考えられます。

ただし、Datalogを使うだけでは、先ほど見たような「インクリメンタルな更新処理をいかに効率よく行うか」という問題を解決することにはなりません。そこで登場するのがDifferential Datalogです(DDlogと呼ばれることもあります)。

Differential DatalogはDifferential Dataflowという仕組みの上に作られていますので、まずDifferential Dataflowについて簡単に説明をしておきましょう。Differential Datalowはいわゆるビックデータ処理システムの一つと考えられますが、インクリメンタルな更新をとても効率よく処理することができるのが特徴です。

例えばTwitterのツイートにつけられているハッシュタグ間の相関関係を調べるために、ハッシュタグをノード、mentionをそれらのノードを繋ぐ辺として見立て、ハッシュタグの「グラフ」を作ることを考えてみましょう。いわゆるグラフから結合成分(Connected Component)を抽出する問題です。現在、Twitterで1日につぶやかれるツイートの数はおよそ5億個です。このような大量のデータからグラフの結合成分を抽出する処理はビッグデータ処理システムの代表格の一つであるMapReduceの得意分野の一つです。ただ、Twitterのように常に新しいツイートが行われる(すなわち入力が随時変化していく)ようなケースはMapReduceはあまり得意ではありません。入力が変化するたびに初めから再度計算をし直さなければいけないからです。一方、Differential Dataflowは入力の変化に応じてアウトプットを効率よく計算することができるため、入力の変化に応じた結果をリアルタイムに得ることができます。

Differential Datalogは、Differential Dataflowをバックエンドに用いたDatalog処理系で、VMware Research Groupで開発されました。Differential Dataflowをベースにしているので、優れたインクリメンタル更新処理性能を持ちつつ、Datalogによる宣言的なプログラミングが可能です。ユーザはどのようにインクリメンタル処理が行われるのかを意識する必要はなく、基本的にDatalogでルールやFactを記述すれば、入力の変化に追従して素早く出力を計算することができます。

Datalogの宣言性とDifferential Dataflowの優れたインクリメンタル更新処理性能を兼ね備えているDifferential Datalogは、SDNコントローラにおけ各種テーブルの更新処理にとても向いている仕組みです。

実際にDifferential Datalogの差分更新がどれくらい高速なのか、簡単な実験をしてみましょう。

仮に100台のハイパーバイザー(Host0〜99)上に10,000台のVM(VM0〜9999)があり、VM0がHost0からHost1に移動した際のフローテーブルを計算するとします。まずは、フローテーブルのあるべき姿を定義するルールをDifferential Datalogで宣言的に定義します。

$ cat flowtable.dl
input relation Host(id: bigint)
input relation VM(vm: string, ip: string, host: bigint, port: bigint)
output relation Flow(host: bigint, matches: string, action: string)
Flow(h, "dst=${ip}", "output:${p}") :- Host(h), VM(_, ip, h, p).
Flow(h, "dst=${ip}", "output:1") :- Host(h), VM(_, ip, other, _), other != h.

このルールを使用するためには、これのコードをDifferential Datalogのコンパイラ(ddlog)にかけます。そうすると、このルールが埋め込まれたRustのコードが生成されます。

$ ddlog -i flowtable.dl -L ~/differential-datalog/lib/

ddlogによって生成されたRustコードをDifferential DataflowのライブラリとともにRustコンパイラでビルドすると、Differential Datalogで記述されたルールエンジンを備えた静的ライブラリとCLIツールが生成されます。

$ (cd flowtable_ddlog/ && cargo build --release)
Finished release [optimized] target(s) in 0.49s
$ ls -l flowtable_ddlog/target/release/
total 59100
drwxrwxr-x 34 shindom shindom 4096 Mar 17 09:23 build
drwxrwxr-x 2 shindom shindom 57344 Mar 17 09:36 deps
drwxrwxr-x 2 shindom shindom 4096 Mar 17 09:23 examples
-rwxrwxr-x 2 shindom shindom 15984984 Mar 17 09:36 flowtable_cli
-rw-rw-r-- 1 shindom shindom 2325 Mar 17 09:36 flowtable_cli.d
drwxrwxr-x 10 shindom shindom 4096 Mar 17 09:35 incremental
-rw-rw-r-- 2 shindom shindom 42098680 Mar 17 09:35 libflowtable_ddlog.a
-rw-rw-r-- 1 shindom shindom 2260 Apr 12 07:14 libflowtable_ddlog.d
-rw-rw-r-- 1 shindom shindom 219 Mar 17 09:23 libflowtable_ddlog.la
-rw-rw-r-- 2 shindom shindom 2350122 Mar 17 09:35 libflowtable_ddlog.rlib

今回の例だと、flowtable.dlに記述されたルールエンジンを持つライブラリlibflowtable_ddlog.a、libflowtable_ddlog.rlibと、CLIコマンドのflowtable_cliが生成されます。

本来であれば、このlibflowtable_ddlog.(a|rlib)を使ったコードをRust、C/C++、Java、Goなどで書くべきなのですが、今回は生成されたCLIツールを使って処理性能を簡易的に調べてみることにします。

100台のホスト上に10,000台の仮想マシンが動いている初期状態の作成と、そのうちの一台の仮想マシン(VM0)がホスト0からホスト1に移動をDifferential Datalogで書くと以下のようになります。これで、VMが一台ホストを移動した際のフローテーブルの更新を模擬することができます。また、かかった時間を調べるために、各処理の途中で時刻を表示するようにしてあります。

start;
insert Host(0);
insert Host(1);
      :
insert Host(98);
insert Host(99);
insert VM("VM0", "10.10.0.0", 0, 0);
insert VM("VM1", "10.10.0.1", 1, 1);
insert VM("VM2", "10.10.0.2", 2, 2);
      :
insert VM("VM9997", "10.10.99.97", 97, 9997);
insert VM("VM9998", "10.10.99.98", 98, 9998);
insert VM("VM9999", "10.10.99.99", 99, 9999);

echo initial setup started:;
timestamp;
commit;
echo initial setup finished:;
timestamp;
start;
delete VM("VM0", "10.10.0.0", 0, 0);
insert VM("VM0", "10.10.0.0", 1, 0);
echo update started:;
timestamp;
commit;
echo update finished:;
timestamp;

これをflowtable_cliに食わせてやると以下のような結果になります。

$ ./flowtable_ddlog/target/release/flowtable_cli --no-print < flowtable.dat
initial setup started:
Timestamp: 490458325506557
initial setup finished:
Timestamp: 490467825165438
update started:
Timestamp: 490467825243578
update finished:
Timestamp: 490467829061228

これを見て分かる通り、最初のフローテーブルの作成には約9.5秒かかっていますが、VMが移動した後のフローテーブルの更新には0.00381765秒しかかかっていません。フローテーブルの更新を実直に再計算するとフローテーブルの初期作成とほぼ同じ時間がかかると考えられるので、Differential Datalogのインクリメンタルアップデートの素晴らしい処理性能をご理解いただけるのではないかと思います。

Differential Datalog の OVN への適用

上で述べたように、現在OVNのovn-northdコントローラをDifferential Datalogを使って再実装する努力が行われています。この作業はOVNのGithubリポジトリのddlog-dev-v2というブランチで開発作業が進められています。

Differential Datalogを使ってテーブル処理を書く主なメリットは、

  • よりメンテナンス性の高いコードになる
  • インクリメンタルな更新による性能向上

の2点です。まず、メンテナンス性の高いコードになる、とう点をみてみましょう。以下は、OVNのmeterおよびmeter_bandテーブルを管理する部分のコードです(OVNはC言語で書かれています)。

struct band_entry {
    int64_t rate;
    int64_t burst_size;
    const char *action;
};

static int
band_cmp(const void *band1_, const void *band2_)
{
    const struct band_entry *band1p = band1_;
    const struct band_entry *band2p = band2_;

    if (band1p->rate != band2p->rate) {
        return band1p->rate > band2p->rate ? -1 : 1;
    } else if (band1p->burst_size != band2p->burst_size) {
        return band1p->burst_size > band2p->burst_size ? -1 : 1;
    } else {
        return strcmp(band1p->action, band2p->action);
    }
}

static bool
bands_need_update(const struct nbrec_meter *nb_meter,
                  const struct sbrec_meter *sb_meter)
{
    if (nb_meter->n_bands != sb_meter->n_bands) {
        return true;
    }

    /* A single band is the most common scenario, so speed up that
     * check. */
    if (nb_meter->n_bands == 1) {
        struct nbrec_meter_band *nb_band = nb_meter->bands[0];
        struct sbrec_meter_band *sb_band = sb_meter->bands[0];

        return !(nb_band->rate == sb_band->rate
                 && nb_band->burst_size == sb_band->burst_size
                 && !strcmp(sb_band->action, nb_band->action));
    }

    /* Place the Northbound entries in sorted order. */
    struct band_entry *nb_bands;
    nb_bands = xmalloc(sizeof *nb_bands * nb_meter->n_bands);
    for (size_t i = 0; i < nb_meter->n_bands; i++) {
        struct nbrec_meter_band *nb_band = nb_meter->bands[i];

        nb_bands[i].rate = nb_band->rate;
        nb_bands[i].burst_size = nb_band->burst_size;
        nb_bands[i].action = nb_band->action;
    }
    qsort(nb_bands, nb_meter->n_bands, sizeof *nb_bands, band_cmp);

    /* Place the Southbound entries in sorted order. */
    struct band_entry *sb_bands;
    sb_bands = xmalloc(sizeof *sb_bands * sb_meter->n_bands);
    for (size_t i = 0; i < sb_meter->n_bands; i++) {
        struct sbrec_meter_band *sb_band = sb_meter->bands[i];

        sb_bands[i].rate = sb_band->rate;
        sb_bands[i].burst_size = sb_band->burst_size;
        sb_bands[i].action = sb_band->action;
    }
    qsort(sb_bands, sb_meter->n_bands, sizeof *sb_bands, band_cmp);

    bool need_update = false;
    for (size_t i = 0; i < nb_meter->n_bands; i++) {
        if (nb_bands[i].rate != sb_bands[i].rate
            || nb_bands[i].burst_size != sb_bands[i].burst_size
            || strcmp(nb_bands[i].action, sb_bands[i].action)) {
            need_update = true;
            goto done;
        }
    }

done:
    free(nb_bands);
    free(sb_bands);

    return need_update;
}

/* Each entry in the Meter and Meter_Band tables in OVN_Northbound have
 * a corresponding entries in the Meter and Meter_Band tables in
 * OVN_Southbound.
 */
static void
sync_meters(struct northd_context *ctx)
{
    struct shash sb_meters = SHASH_INITIALIZER(&sb_meters);

    const struct sbrec_meter *sb_meter;
    SBREC_METER_FOR_EACH (sb_meter, ctx->ovnsb_idl) {
        shash_add(&sb_meters, sb_meter->name, sb_meter);
    }

    const struct nbrec_meter *nb_meter;
    NBREC_METER_FOR_EACH (nb_meter, ctx->ovnnb_idl) {
        bool new_sb_meter = false;

        sb_meter = shash_find_and_delete(&sb_meters, nb_meter->name);
        if (!sb_meter) {
            sb_meter = sbrec_meter_insert(ctx->ovnsb_txn);
            sbrec_meter_set_name(sb_meter, nb_meter->name);
            new_sb_meter = true;
        }

        if (new_sb_meter || bands_need_update(nb_meter, sb_meter)) {
            struct sbrec_meter_band **sb_bands;
            sb_bands = xcalloc(nb_meter->n_bands, sizeof *sb_bands);
            for (size_t i = 0; i < nb_meter->n_bands; i++) {
                const struct nbrec_meter_band *nb_band = nb_meter->bands[i];

                sb_bands[i] = sbrec_meter_band_insert(ctx->ovnsb_txn);

                sbrec_meter_band_set_action(sb_bands[i], nb_band->action);
                sbrec_meter_band_set_rate(sb_bands[i], nb_band->rate);
                sbrec_meter_band_set_burst_size(sb_bands[i],
                                                nb_band->burst_size);
            }
            sbrec_meter_set_bands(sb_meter, sb_bands, nb_meter->n_bands);
            free(sb_bands);
        }

        sbrec_meter_set_unit(sb_meter, nb_meter->unit);
    }

    struct shash_node *node, *next;
    SHASH_FOR_EACH_SAFE (node, next, &sb_meters) {
        sbrec_meter_delete(node->data);
        shash_delete(&sb_meters, node);
    }
    shash_destroy(&sb_meters);
}

かなりの量のコードで、それなりに複雑です。

一方、これと同じ処理をDifferential Datalogで記述すると、

/* Meter_Band table */
for (mb in nb.Meter_Band) {
    sb.Out_Meter_Band(.uuid_name = uuid2name(mb._uuid),
                      .action = mb.action,
                      .rate = mb.rate,
                      .burst_size = mb.burst_size)
}

/* Meter table */
for (meter in nb.Meter) {
    sb.Out_Meter(.name = meter.name,
                 .unit = meter.unit,
                 .bands = set_map_uuid2name(meter.bands))
}

のようになります。非常にシンプルに記述できますので、可読性に優れ、バグの混入の可能性も低くすることができると考えられます。これがDifferential Datalogを使う一つ目のメリットです。

Differential Datalogを適用することのもう一つのメリットは、インクリメンタルアップデートの性能向上です。eBayのHan Zhouが、Cで実装されたovn-northdを使った場合とDifferential Datalog版のovn-northdを使った時のスケールテストの結果を共有してくれています [1]。結果は以下の通りです。

Cバージョン 67分47秒
DDlogバージョン 7分39秒

Differential Datalogを使うことで、およそ10倍程度の性能向上があったことになります。残念ながらこのテストの詳しいテストシナリオははっきりしないのですが、OVNを使ったOpenStack環境でスケール試験を行なったようです。

また、RedHatのMark Michelsonらが別の性能評価をしています。Markが行ったテストは、1つの論理ルータの下に159個の論理スイッチを作り、それぞれの仮想スイッチに92個の論理ポートを作った上でACLを適用する、というものです [2]。その結果をグラフしたのが以下になります。

View post on imgur.com

 

このテストは約15,000論理ポートを順次作成していく、というシナリオなので、インクリメンタルな処理性能を測るのにあまり適したシナリオではありませんが(インクリメンタルな処理性能が最も顕著に現れるのは、すでにある大規模な環境に若干の更新をした場合です)、それでもCでの実装に比べるとDifferential Datalog (DDlog)の実装の方がかなりいい性能が得られていることが分かります。

今後に向けて

テクノロジーとしては非常に有望なDifferential Datalogですが、課題もあります。

Differential Datalogは従来の手続型言語とは大きく異なります。今までCで書いてきた人が「明日からDatalogで書いてね!」と言われてもパラダイムが全く異なりますのでなかなか難しいのではないかと思います。ツールチェーンも少々複雑です。上述の通り、Differential DatalogのコンパイラRustのコードを吐き出します。また、Differential Datalogのコンパイラ自身はHaskellで書かれています。OVNはCで書かれているので、Differential Datalog版のovn-northdの動きを調べる際には、CやDifferential DatalogだけではなくRustや場合によってはHaskellの知識も求められることになります。これらの言語およびそこで使われているツールチェーンを一通り学習するのはそれなりの時間を要するでしょう。また、Differential Datalogにはデバッグの機能も盛り込まれてはいるものの、皆さんが普段使い慣れているデバッガに比べるとまだまだプリミティブです。

ovn-northdのDifferential Datalog実装は現時点ではまだmasterとは別ブランチとなっており、masterブランチではCを使った開発が引き続き行われています。したがって、Cで書かれた新機能がmasterブランチに追加されたら、その機能をDifferential Datalogで実装して、追従していかなければなりません。これをタイムリーにやっていくのものなかなか大変です。完全にDifferential Datalogに移行ができればこのような悩みは無くなりますが、それにはもっともっとテストをしなければなりません。具体的にはCバージョンで生成されるテーブルとDifferential Datalogバージョンから生成されるテーブルがいかなる場合(障害時も含めて)で差異がない、という状態にならないとCバージョンからDifferential Datalog版に移行することはできません。これにはまだ時間がかかると思われます。また、現時点でDifferential Datalogが適用されているのはovn-northdだけです。理論的にはローカルコントローラであるovn-controllerの方にもDifferential Datalogを適用することができますが、まだ未対応です。

これらの課題がありつつも、Differential Datalogは定性的には素晴らしい特性を持っていますので、今後はOVNだけではなく、大規模なSDN環境における宣言的かつインクリメンタル処理を効率よく行う仕組みとして広く使われていくようになるのではないかと思います。NSX-TのコントローラはNiciraのコントローラと同様、Datalogベースの宣言的なエンジンを備えていますが、そこにDifferential Datalogによる差分更新機能を適用することも検討されているようです。また、Differential Datalogは非常に汎用的な仕組みですので、今後はSDNだけでなく広く分散システムに対する宣言的なアプローチとして使われていく可能性もあります。引き続き注視をしていきたいと思います。

参考リンク

[1] https://mail.openvswitch.org/pipermail/ovs-dev/2019-July/360604.html
[2] https://mail.openvswitch.org/pipermail/ovs-dev/2019-July/360889.html
[3] https://codeout.hatenablog.com/entry/2020/07/06/130233

Photo by Irvan Smith on Unsplash

自宅のインターネット回線品質をVeloCloudで測ってみた

こちらの記事で書いた通り、現在我が家には3つのインターネット回線が引かれている状態です。VeloCloudのEdgeは回線のlatency、jitter、packet lossを測って可視化してくれる機能を持っていますので、今回はこの機能を使って各回線の品質を調べてみました。

結果は以下の通りです(グラフをクリックすると拡大されます)。

ちなみに、”So-net” はNURO光、”NTT” はフレッツ光(OCN)、”ARTERIA Networks Corporation” はUCOMを表しています。VeloCloud EdgeにWAN回線を繋ぎこむと、WAN側のアドレスを認識して、そのアドレスから自動的にその回線の通信サービスロバイダ名をWANリンクのNameとして設定してくれるようになっていますので便利です(EdgeがNATの内側にあってもこの機能は動きます)。

フレッツ光(OCN)- Latency –

フレッツ光(OCN)- Jitter –

フレッツ光(OCN)- Packet Loss –

NURO光 – Latency –

NURO光 – Jitter –

NURO光 – Packet Loss –

UCOM – Latency –

UCOM – Jitter –

UCOM – Packet Loss –

これらの結果をみて分かる通り、フレッツは想像通りインターネットが混雑する時間帯(21:00から0:00くらい)はLatency、Jitter共に大きくなっており、Packet Lossも若干見られます。前回のテスト結果で見られたような通信速度の低下を裏付ける形になっています。一方、NUROとUCOMは全ての時間帯で安定しており、Jitter、Packet Lossもほとんどありません。なお、現時点ではVeloCloud EdgeはIPv6のサポートをしていないので、これらの測定結果はIPv4のものです。IPv6で測れればフレッツ(OCN)はもっと良好な結果になったと思われます。

それぞれのグラフの縦軸のスケールが異なるので、パッと見比較しにくいと思いますので、Latency、Jitter、Packet Lossについて全ての回線を重ね合わせたグラフ(Stackグラフ)も参考のため載せておきます。色が全て青系の色なのでちょっと分かりにくいですが・・・。

3回線重ね合わせ – Latency – 

3回線重ね合わせ – Jitter –

3回線重ね合わせ – Packet Loss –

Photo by Joe Neric on Unsplash

我が家にNUROがやってきた!

先日、自宅に無事にNUROの回線が開通しました。これで、UCOM、フレッツ光(OCN)に加えNURO光が使えるようになり、「トリプルホーム」という少々贅沢な環境となりました。なんでこんな(無駄に贅沢なw)ことになっているか不思議に思われるかもしれませんが、これには若干歴史的な経緯もありまして。。備忘録も兼ねてこれまでの経緯を記録すると共に、これらのインターネットサービスの現時点での快適度について調べてみました。

我が家のインターネット環境の歴史

今から約20年前の1999年、当時インターネットアクセスの手段としてはアナログ電話やISDNやが一般的だった中「家を買うなら光が欲しい」と思いまして(笑)、今のマンションを購入する際に「部屋まで光を引き込むこと」というのを入居の条件にしたのでした(新築マンションだったのでそのような交渉が可能でした)。ただ、当時はマンション施工業者も「ファイバーって何? PD?なんじゃそりゃ!」みたいな状態だったので話が全く噛み合わず埒があかなかったので、結局入居時に「INS1500 (PRI) を使います!」と宣言し、それでやっと光を通してもらえることになりました。というわけで、無事に入居時にファイバーが部屋まで引き込まれました。ただ、それをちゃんとPRI回線として使ったのは、2000年問題対策として自宅にMAX6000を置いて(当時、私はアセンドに勤務していました)、会社のリモートアクセス環境のバックアップ環境を作った時くらいで、それ以外の時は休止にしていました。ちなみにMaxを自宅に持ち込んでいた時にテストでMultilink PPPしてみたのですが、自宅で24Bが順に繋がっていくのを見るのはなかなか壮観でした(笑)。

2001年、NTTのフレッツ光のサービスが始まりましたが、すでに引き込んであったファイバーがあったおかげでBフレッツに入ることができました。私一人でBフレッツ回線を使っていたので、極めて快適なネットワーク環境でした。

2003年になって、うちのマンションにもにもようやくインターネットサービスが提供されることになりました。その際、二つの選択肢が提供されました。一つはケーブルTV系のインターネットサービス、もう一つはUCOMが提供するインターネットサービスでした。当時ケーブルTV系のサービスは宅内まではVDSLで引き込む形だったので、私は部屋まで100Mbps Ethernetが来るUCOMのサービスに入ることにしました。それに伴い、Bフレッツのほうは解約しました。UCOMの方が安かった、というのもあるのですが、なんと言ってもUCOMのサービスはグローバルアドレスを/29でくれることが大きな魅力でした!

UCOMのサービスは非常に高速かつ安定していて、しかもグローバルIPを/29でくれるという気前の良さだったのでとても満足しており、10年以上我が家のインターネット環境に変化はありませんでした。次の変化が訪れたのは2016年、私がSD-WANのスタートアップであるViptela(後にCisco Systemsが買収)に入社したタイミングでした。Viptelaは日本にオフィスがなかったので、自宅がオフィス兼ラボになっていました。SD-WANの検証やデモをするためには、複数回線ないと話にならないので、このタイミングでもう一本インターネット回線を部屋まで引くことにしました。検証を兼ねていたので、その時に最も広く使われてたであろうフレッツ光ネクストを選択しました。ファイバーは以前Bフレッツで使っていたものを使い回すことができました。IPv6のテストもしたかったので、プロバイダはOCNを選びました。これで、無事に自宅がデュアルホームな環境になりました(正確にはこの時LTEもWAN回線に使っていたので、すでにトリプル回線になっていたのですが、通信費を抑えるためにLTEは必要のない時は落としておくことが多かったので、常時トリプルホームというわけはなかったです)。

2018年、Ciscoを退社する際にViptelaのラボ機材も返却しましたが、なんとなく未練があってフレッツ光(OCN)は解約はせずにデュアルホームな環境を維持することにしました。ただ、デュアルホームと言っても別に高度なことをしていたわけではなく、普段の生活にはフレッツを使い、DNSやBlogのサーバをUCOM側の回線から外部に公開をする、という感じで使っていました。

この環境に特に不満はなかったのですが、今年になってマンションで利用できるインターネットサービスとして新たにNURO光が加わることなり、前から興味があったので話を聞いてみることにしました。一番気にしていたのはIPv6でしたが、こちらは普通に使える、ということでした。費用的にはフレッツ光よりもNUROの方がだいぶお得になります。気になる速度ですが、フレッツ光の回線速度は1Gbpsに対してNURO光は2Gbpsです。あくまでこれはリンク速度ですので、実効の速度がどれくらいになるかはやってみないとわかりません。とりあえず試してみるか、ということでNUROに申し込みを行いました。

で、先日無事にNUROの回線が開通した次第です。すでに入居時に引いた光ケーブルとUCOMのためのUTPケーブルが通っていたので、新たに光のケーブルを通すのに若干苦労しましたが、無事に工事をすることができました。

我が家のインターネット環境の快適度比較

さて、気になる速度ですが、今回はfast.com を使って調べてみました。fast.comははNetflixが提供しているもので、Netflixが快適に使えるネットワーク環境なのかどうかをユーザが判断できるようにするために提供されています。クライアントがデュアルスタックの際、IPv6で繋がればIPv6で、そうでなければIPv4で測ってくれるので便利です。他のブロードバンド速度測定サイトのように余計な広告が出てこないのもいいですね!

測定方法は3回測って一番結果が良かったものを採用することにしました。時間帯的にはインターネットがあまり混んでいないと思われる時間帯(朝の5:30頃)と混んでいると思われる時間帯(深夜0:00頃)で測りました。また、フレッツ光(OCN)とNUROに関してはIPv4、IPv6についてそれぞれ測っています。ちなみにフレッツ光(OCN)のIPv6はIPoEです。UCOMはIPv6が来ていないので、IPv4のみ測っています。結果は以下の通りです。

IPv4-フレッツ光(OCN)

フレッツ光(OCN) IPv4 早朝
フレッツ光(OCN) IPv4 深夜

IPv6-フレッツ光(OCN)

フレッツ光(OCN) IPv6 早朝
フレッツ光(OCN) IPv6 深夜

IPv4-NURO光

NURO IPv4 早朝
NURO IPv4 深夜

IPv6-NURO光

NURO IPv6 早朝
NURO IPv6 深夜

IPv4-UCOM

UCOM IPv4 早朝
UCOM IPv4 深夜

考察

これからみて分かる通り、フレッツのIPv4 (PPPoE)はよく言われているように、夜間等のインターネット混雑時はかなり遅くなります。IPoEのIPv6はこの影響を受けないので今のところ比較的安定して快適な環境が得られています。NUROはIPv4は混雑時には若干速度が低下するものの概ね良好、IPv6は終始快適な状況です。UCOMは100Mbpsのサービスなので、昼夜を問わず安定して90Mbps近く出ているのは素晴らしいと思います。

今後の予定

この結果からすると当初の目論見通り、フレッツ光を解約してNURO光にすることになるかなーと思います。UCOMは手放せないので、NUROとUCOMのデュアルホーム環境ですね。ただ、フレッツの解約はいつでもできるので、VeloCloud使ってもう少しこのトリプルホーム環境で遊んでみようかな。

Photo by Sincerely Media on Unsplash

Antrea – Yet Another CNI Plug-in for Kubernetes

Antreaとは

2019年11月にSan Diegoで行われたKubeConのタイミングで、Kubernetesのためのネットワークプラグインとして、新たにAntreaというプロジェクトが公開されました。CNI (Container Networking Interface) に準拠したプラグインで、現在はVMwareが中心となって開発をしていますが、オープンソースのプロジェクトですので、今後は徐々にコミュティーからのサポートが得られていくのではないかと思います。GitHubのリポジトリはここにあります。

“Antrea”という名前ですが、実はAntrea Netから来ています。Antreaはフィンランドに近いロシアの街で、世界最古の漁網が見つかった場所として知られており、その網は見つかった場所にちなみんで「Antrea Net」と呼ばれています。Kubernetesという名前は「操舵手」というギリシャ語から付けられたものですが、これに倣ってKubernetes関連のプロジェクトには船や海にちなんだ名前(例えばhelm/tillerなど)がつけられることが多くなっています。一方、CNIのプラグインであるCalicoやFlannelはどちらも繊維(ファブリック)に関係のある名前になっています(「ネットワーク」と「ファブリック」をかけている??)。この「海」と「ファブリック」の二つの要素を兼ね合わせた「漁網」であるAntrea Netに因んでAntreaという名前がつけられました。

Antreaの特徴は以下のような点になります。

  • Open vSwitchによるデータプレーン
  • Network Policyのサポート(iptablesからの脱却)
  • LinuxとWindowsサポート(予定)
  • 幅広いプラットフォームサポート
  • Octantとの連携
  • rspanやIPFIXのサポートなど(予定)

Antreaは仮想スイッチとして広く使われているOpen vSwitch (OVS)をデータパスに使用しているのがもっとも大きな特徴です。OVSをデータパスに使用することでいくつかのメリットが得られます。KubbernetesのNetwork PolicyやServiceのロードバランスを実現するために、他のCNIプラグインの多くはiptablesを使っています。iptablesは古くからある実績豊富な機能ではありますが、性能はあまりよろしくありません。iptabblesはルール数が多くなると、CPUに対する負荷、遅延、設定時間が非常に大きくなることが知られています。複雑なNetwork Policyや多数のServiceを使っているKubernetes環境では、iptablesベースのCNIでは期待した性能が得られないことが予想されます。一方、AntreaはOVSをデータプパスに使っているため、iptablesに起因する性能的問題を回避することができます。

OVSはLinux以外のOS(例えばWindows)もサポートしているので、多様な環境にまたがったKubernetesネットワーク環境を実現することができます。現時点のAntreaはLinuxのみのサポートですが、将来的にはWindowsもサポートする予定です。KubernetesはすでにWindowsをサポートしていますので、AntreaがWindowsをサポートするようになれば、LinuxとWindowsにまたがったネットワークを作って、そこに統一的なNetwork Policyを適用することができるようになります。また、OVSは仮想スイッチの機能だけではなく、他にもさまざまな機能(NetFlow/sFlow/IPFIXやRSPANなど)をサポートしています。将来的にはこれらの機能をAntreaから利用できるようにしていく予定です。

Antreaは幅広いプラットフォームで実行されれることを想定しています。オンプレの仮想環境はもちろん、ベアメタルや各種パブリッククラウドで実行することを想定して作られています。AntreaはKindで作ったKubernetesクラスタもサポートしていますので、簡易的にクラスタを用意してテストをすることもできて便利です。ただし、現在はLinuxの上のKindのみのサポートになります。MacOSのKindはまだサポートされてません。Kindで作ったクラスタでAntreaを動かす場合は、OVSのカーネルモジュールは使われず、OVSのユーザ空間のデータパスが使われます(ブリッジ作成時にdatapath_type=netdevが指定される)。

EncapsulationはデフォルトではVXLANが使われますが、GeneveやGRE、STTを使うこともできます。またこれらのEncapsulationとIPsecを組み合わせて使うこともできます。将来的には、Encapsulationをしないモードもサポートが予定されています。

VMwareはNSX-TのためのCNIプラグインとしてNCPを提供しています。それなのになぜ別のCNIプログインを開発しているのか疑問に思われる方もいるでしょう。NCPはすでに多くのプロダクション環境で使われており、実績面ではAntreaよりもはるかに成熟しています。また、NCPはiptablesに依存しないServiceのロードバランシングを既に実現しているので、スケーラビリティ的にも優れています。一方、Antreaはまだ出来立てのホヤホヤで商用の実績はありません。ただ、Antreaは100%オープンソースで手軽に始めることができるのは魅力です。幅広いプラットフォームやOSで利用できるようになる潜在的能力に魅力を感じる人も多いでしょう。Antreaが機能面、堅牢面においてすぐにNCPのレベルに達するとは考えにくいですが、コミュニティのサポートが得られればかなりのスピードで進化していく可能性を秘めていると思います。現時点でAntreaをプロダクション環境で使うことはお勧めしませんが、将来的には何かしらの形でNSX/NCPとの連携のようなこともできるようになるかもしれません。その頃にはAntreaも十分に成熟しているはずですので、ユーザにとって環境やユースケースによって使い分けられる選択肢が広がることは喜ばしいことでしょう。

アーキテクチャ

Antrea はKubernetesに特化したネットワークプラグインです。例えばCalicoはKubernetesだけでなくOpenStackなど他のオーケストレーションシステムをサポートしていますが、AntreaはKubernetesのみを対象としています。そのぶんAntreaはKubernetesの持つ仕組み(API、Tool、Operator)を最大限に利用するようになっています。

アーキテクチャーは以下通りです。

Antreaはいくつかのコンポーネントによって構成されます。Antrea Conrollerはいわゆるコントローラで、Deploymentとして作成さられます。Kubernetes APIを使って関連するオブジェクトの追加や削除を監視しています。一方、Antrea AgentはDaemonSetとして作られ、各ノードで動作します。Antrea agentはOpenFlowの設定をOVS に対して行います。antrea-cniはKubeletからのCNIコマンドをAntrea Agentに引き渡します。antctlコマンドはAntrea Controllerに対するCLIインターフェースを提供するもので、現在絶賛開発中です。

現時点ではkube-proxyはKubernetes標準のものを使用しています(したがって、Serviceのロードバランシングにはiptablesを使います)が、将来的にはこれはOVSベースのロードバランシングを使ったkube-proxyに置き換えられる予定です。Network Policyは既に完全にOVSベースで、iptablesは使われていません。なお、Antreaの最初のリリース(v0.1.0)では、OVSのOpenFlowテーブルを設定するのにovs-ofctlコマンドを使ってしましたが、現在はlibOpenflowライブラリを使うように変更されています。

OctantはKubernetes Clusterの状況を可視化してくれるダッシュボードです。AntreaはOctant用のプラグインを用意しており、Octantから簡単にAntreaの状況を確認することができます。

インストールと動作確認

今回はオンプレの環境でAntreaを動かしてみます。Kubernetes環境はESXiの上にCluster APIで2つのWorker Nodesを作った素のKubernetes環境を用意することにします。ESXi向けCluster API(CAPV; Cluster API Provider for vSphere) はCentOS 7、Ubuntu 18.04、Photon3用のOVAイメージを提供していますが、今回はUbuntu 18.04を使うことにします。AntreaはKubernetes 1.16以降を要求しますので、v1.16.3のOVAイメージを使用することにします。

標準的な方法でCluster APIでKubernetes環境を作ります。ノードが上がってきたら、各ノードでOVSのカーネルモジュールを入れます。Cluster APIで作られるノードは再構築される可能性があるため、本来ならばCluster APIで使うOVAテンプレートに予めOVSを組み込んでおくべきですが、今回はテストなのでそのステップは省略することにします。各ノードで 以下のコマンド

sudo apt update ; sudo apt -y install openvswitch-switch

を実行して、OVSをインストールします。次にカーネルモジュールが正しく読み込まれたか確認しましょう。

$ lsmod | grep openvswitch
openvswitch           131072  0
nsh                    16384  1 openvswitch
nf_nat_ipv6            16384  1 openvswitch
nf_defrag_ipv6         20480  3 nf_conntrack_ipv6,openvswitch,ip_vs
nf_nat_ipv4            16384  2 openvswitch,iptable_nat
nf_nat                 32768  5 nf_nat_masquerade_ipv4,nf_nat_ipv6,nf_nat_ipv4,xt_nat,openvswitch
nf_conntrack          131072  12 xt_conntrack,nf_nat_masquerade_ipv4,nf_conntrack_ipv6,nf_conntrack_ipv4,nf_nat,nf_nat_ipv6,ipt_MASQUERADE,nf_nat_ipv4,xt_nat,openvswitch,nf_conntrack_netlink,ip_vs
libcrc32c              16384  4 nf_conntrack,nf_nat,openvswitch,ip_vs

この時点ではKubernetesのノードは以下のような状態になっているはずです。

bash-3.2$ kubectl get nodes
NAME                                       STATUS     ROLES    AGE   VERSION
workload-cluster-1-controlplane-0          NotReady   master   30m   v1.16.3
workload-cluster-1-md-0-78469c8cf9-5r9bv   NotReady   <none>   21m   v1.16.3
workload-cluster-1-md-0-78469c8cf9-tlkn9   NotReady   <none>   21m   v1.16.3

STATUSが “Not Ready” になっていますが、これはCNIプラグインがまだ何もインストールされていないためで、期待された動きです。

Antreaのインストールは非常に簡単です。以下のコマンドを実行するだけで必要なコンポーネントが全てインストールされます。

kubectl apply -f https://raw.githubusercontent.com/vmware-tanzu/antrea/master/build/yamls/antrea.yml

ここではAntreaの最新のものを使うようにしていますが、Antreaの開発のペースは早いので、一時的に最新版ではうまく動作しないケースもあるかもしれません。その場合には、

kubectl apply -f https://github.com/vmware-tanzu/antrea/releases/download/<TAG>/antrea.yml

の<TAG>の部分を適当なものに置き換えてください(現時点で最新のリリース版タグは “v0.2.0” です)。

インストールが無事に終了するとkube-system Namespaceに以下のようなPodができて、全てRunningのSTATUSになっているはずです。

bash-3.2$ kubectl get pods -n kube-system
NAME                                                        READY   STATUS    RESTARTS   AGE
antrea-agent-dvpn2                                          2/2     Running   0          8m50s
antrea-agent-qqhpc                                          2/2     Running   0          8m50s
antrea-agent-tm28v                                          2/2     Running   0          8m50s
antrea-controller-6946cdc7f8-jnbxt                          1/1     Running   0          8m50s
coredns-5644d7b6d9-878x2                                    1/1     Running   0          46m
coredns-5644d7b6d9-pvj2j                                    1/1     Running   0          46m
etcd-workload-cluster-1-controlplane-0                      1/1     Running   0          46m
kube-apiserver-workload-cluster-1-controlplane-0            1/1     Running   0          46m
kube-controller-manager-workload-cluster-1-controlplane-0   1/1     Running   2          46m
kube-proxy-4km5k                                            1/1     Running   0          38m
kube-proxy-cgxcp                                            1/1     Running   0          38m
kube-proxy-h4css                                            1/1     Running   0          46m
kube-scheduler-workload-cluster-1-controlplane-0            1/1     Running   2          46m
vsphere-cloud-controller-manager-gfgbp                      1/1     Running   1          45m
vsphere-csi-controller-0                                    5/5     Running   0          45m
vsphere-csi-node-6mvpt                                      3/3     Running   0          7m4s
vsphere-csi-node-gwr67                                      3/3     Running   0          7m4s
vsphere-csi-node-hb6rk                                      3/3     Running   0          7m4s

DaemonSetも確認してみましょう。

bash-3.2$ kubectl get daemonset -n kube-system
NAME                               DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                     AGE
antrea-agent                       3         3         3       3            3           beta.kubernetes.io/os=linux       9m1s
kube-proxy                         3         3         3       3            3           beta.kubernetes.io/os=linux       47m
vsphere-cloud-controller-manager   1         1         1       1            1           node-role.kubernetes.io/master=   46m
vsphere-csi-node                   3         3         3       3            3           <none>                            46m

antrea-agentがDeamonSetとして実行されているのを確認することができます。

ConfigMapを確認してみましょう。

bash-3.2$ kubectl get configmap -n kube-system
NAME                                 DATA   AGE
antrea-config-tm7bht9mg6             3      10m
coredns                              1      48m
extension-apiserver-authentication   6      48m
kube-proxy                           2      48m
kubeadm-config                       2      48m
kubelet-config-1.16                  1      48m
vsphere-cloud-config                 1      47m
bash-3.2$ kubectl get configmap antrea-config-tm7bht9mg6 -n kube-system -o yaml
apiVersion: v1
data:
  antrea-agent.conf: |
    # Name of the OpenVSwitch bridge antrea-agent will create and use.
    # Make sure it doesn't conflict with your existing OpenVSwitch bridges.
    #ovsBridge: br-int
    # Datapath type to use for the OpenVSwitch bridge created by Antrea. Supported values are:
    # - system
    # - netdev
    # 'system' is the default value and corresponds to the kernel datapath. Use 'netdev' to run
    # OVS in userspace mode. Userspace mode requires the tun device driver to be available.
    #ovsDatapathType: system
    # Name of the interface antrea-agent will create and use for host <--> pod communication.
    # Make sure it doesn't conflict with your existing interfaces.
    #hostGateway: gw0
    # Encapsulation mode for communication between Pods across Nodes, supported values:
    # - vxlan (default)
    # - geneve
    # - gre
    # - stt
    #tunnelType: vxlan
    # Default MTU to use for the host gateway interface and the network interface of each Pod. If
    # omitted, antrea-agent will default this value to 1450 to accomodate for tunnel encapsulate
    # overhead.
    #defaultMTU: 1450
    # Whether or not to enable IPSec encryption of tunnel traffic.
    #enableIPSecTunnel: false
    # CIDR Range for services in cluster. It's required to support egress network policy, should
    # be set to the same value as the one specified by --service-cluster-ip-range for kube-apiserver.
    #serviceCIDR: 10.96.0.0/12
  antrea-cni.conf: |
    {
        "cniVersion":"0.3.0",
        "name": "antrea",
        "type": "antrea",
        "ipam": {
            "type": "host-local"
        }
    }
  antrea-controller.conf: ""
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"antrea-agent.conf":..<snipped>..}}
  creationTimestamp: "2020-01-06T06:57:51Z"
  labels:
    app: antrea
  name: antrea-config-tm7bht9mg6
  namespace: kube-system
  resourceVersion: "4819"
  selfLink: /api/v1/namespaces/kube-system/configmaps/antrea-config-tm7bht9mg6
  uid: e6115864-4179-471d-a382-a1bb4afdeca1

これをみて分かるように、ConfigMapでは、

  • Integration Bridge名(default: br-int)
  • DataPath タイプ(default: system)
  • ホストと通信するインターフェース名(default: gw0)
  • トンネル(Encapsulation)タイプ(default: vxlan)
  • MTU値(default: 1450)

などを設定することができます。

それでは何かDeploymentを作ってみましょう。今回はお手軽にnginxのDeploymentをreplicas=2で作って、port-forwardでPodにアクセスしてみましょう。

bash-3.2$ cat nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
bash-3.2$ kubectl apply -f nginx.yaml
deployment.apps/nginx-deployment created
bash-3.2$ kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-54f57cf6bf-fx4mz   1/1     Running   0          44s
nginx-deployment-54f57cf6bf-mg74x   1/1     Running   0          44s
bash-3.2$ kubectl port-forward nginx-deployment-54f57cf6bf-fx4mz 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

無事にnginxにアクセスするすることができました。

次にantrea-agentの内部がどう動いているのか見てみましょう。

bash-3.2$ kubectl exec -ti antrea-agent-dvpn2 -n kube-system /bin/bash
Defaulting container name to antrea-agent.
Use 'kubectl describe pod/antrea-agent-dvpn2 -n kube-system' to see all of the containers in this pod.
root@workload-cluster-1-md-0-78469c8cf9-tlkn9:/# ovs-vsctl show
2fa33701-4b83-4016-a070-08fc90921b2d
    Bridge br-int
        Port "coredns--f4e323"
            Interface "coredns--f4e323"
        Port "tun0"
            Interface "tun0"
                type: vxlan
                options: {key=flow, remote_ip=flow}
        Port "nginx-de-70ebff"
            Interface "nginx-de-70ebff"
        Port "vsphere--7be010"
            Interface "vsphere--7be010"
        Port "gw0"
            Interface "gw0"
                type: internal
    ovs_version: "2.11.1"

Pod用のインターフェースが作成されて、Integrationブリッジ(br-int)に接続されている様子が分かると思います。

最後にOctantとの連携を見てみましょう。

AntreaにはOctant用のプラグインが用意されていて、Octant側からAntreaの状況を確認することができるようになっています。AntreaのOctantプラグインはPodとして動かす方法とプロセスとして動かす方法と2つあります。今回はPodとして動かしてみます。

まず、必要なSecretを作ります。

kubectl create secret generic octant-kubeconfig --from-file=/Users/shindom/work/cluster-api/out/workload-cluster-1/kubeconfig -n kube-system

次にbuild/yamls/antrea-octant.ymlを使用したkubeconfigに合わせてファイル名を書き換えます。

# Change admin.conf to the name of kubeconfig file in your set up.
- name: KUBECONFIG
  value: "/kube/kubeconfig"

Antrea用のOctantプラグインはAntreaのレポジトリにソースがあるので自分でビルドすることもできますが、私のDockerhubにビルド済みのものを置いておきましたので、こちらをお使いいただくのが簡単です。build/yamls/antrea-octant.ymlを以下のように書き換えて、

containers:
 - name: antrea-octant
   image: mshindo/octant-antrea-ubuntu:latest

(アップデート2020/10/25アップデート:  AntreaのDockerhub Repositoryに適切なバージョンのAntrea用Octantプラグインが置かれるようになりましたので、上記の私のイメージは不要になりました。したがって、私のRepositoryからイメージも削除しいています)。

Podとしてデプロイします。

kubectl apply -f build/yamls/antrea-octant.yml

上記のyamlファイルはNodePortを作るようになっていますので、そこに接続するとOctantのUIにアクセスすることができます。

今後の予定

Windowsの対応、No Encapsulationモードの対応、OVSベースのService Load Balancing、RSPAN/IPFIXなどのDay 2 Operation機能への対応などは既に述べた通りですが、他にも

  • IPv6でのPod Network対応
  • DPDKやAF_XDPなどの高速データパス技術への対応

などがロードマップに上がっています。

本当はAntreaが採用しているデータパスのパイプラインについても説明したかったのですが、力尽きたので今回はここまでで(笑)。

Photo by Manuel Sardo on Unsplash