JANOGのCode of Conduct

f:id:yuyarin:20191224033223g:plain

JANOG

こんにちは。JANOG(日本ネットワーク・オペレーターズ・グループ)というコミュニティで運営委員をやってるゆやりんです。

adventar.org

コミュニティ•カンファレンス運営 Advent Calendar 2019ということで、JANOGの運営について紹介しようと書いていたら、あまりにも長大なポエムが出来上がってしまったので、ほとんど捨てて要点を絞って最近話題のCode of Condactについて話したいと思います。

JANOGは7000名のネットワーク技術者のコミュニティ

日本ネットワーク・オペレーターズ・グループ(JANOG)はネットワーク運用者が集まる技術コミュニティです。インターネットはいろいろな組織や事業者のネットワークが相互接続することで形成されています。そのためそれを運用する技術者たちも繋がりを持ち、組織の壁を超えて技術的な情報を交換したり、共通の課題を解いていく必要があります。JANOGはそのためのコミュニティです。
1997年に北米のNANOG (North American NOG) に参加して刺激を受けた技術者たちが立ち上げ、今年で22年目になります。会員はメーリングリストの登録者数で数えており約7000名です。

活動のメインは年2回のMeeting

JANOGでは年2回ミーティングを開催しています。例年1月と7月に開催しており、最近では水曜の午後から金曜日までの2日半の開催になっています。
最近ではJANOG44ミーティングが神戸で開催され過去最高の1602名が参加しました。

f:id:yuyarin:20191224011527p:plain

JANOG44ミーティング in 神戸

参加者は増え続けてついに1600名に

f:id:yuyarin:20191224011538p:plain

JANOGミーティングの参加者数の推移

私が学生のときに初めて参加したJANOG26の時期は都内だと700名、地方だと300-400名程度の出席でした。それがここ5年くらいでどんどん増えてJANOG41で1000名超え、今回のJANOG44では1600名になりました。

JANOGのCode of Conduct をつくった

1600名も参加するミーティングともなると、色々な人が参加することが想定されます。JANOGでは2016年6月頃から検討をはじめて、2017年8月に「JANOG行動規範」として公開しました。

https://www.janog.gr.jp/doc/janog-comment/jc11.txt

JANOG行動規範


JANOGの価値は幅広い参加者の知見と興味、率直な意見交換に支えられています。
これはメーリングリストやミーティング、ワーキンググループなど、
JANOGがその目的を達成するための様々な活動の全てに共通しています。

それぞれの参加者は、異なる背景や状況、意見を持ちうることを認識し、
お互いに思いやり尊重しあいましょう。あらゆる嫌がらせや誹謗中傷、
反社会的行為は許容されません。この行動規範に反する行為や言動に対して、JANOG運営委員会は当該人物の退出や除名などの必要な措置を講ずることがあります。

共に楽しみ、共に課題を乗り越え、充実したコミュニティを作っていきましょう。

以上

なにか個別の問題が発生したので制定したという経緯ではなく、海外を中心に他のコミュニティでCoCを制定することが当たり前の流れになってきており、JANOGでもこれが必要になるだろうという判断を行いました。

ドラフトを策定して公開、次のJANOGミーティングで説明、意見募集。その次のJANOGミーティングのオープンマイクで採択という流れを取りました。そのため検討から約1年を要しています。

他のコミュニティのCoCを参考にした

CoCは同じく運営委員の金子さんや松崎さんが中心となって策定をしていただきました。策定にあたって他のコミュニティ、特にネットワーク系でアジア圏でカンファレンスを行っているAPNICやAPRICOTなどを参考にしており、APNICのCoCをベースに作成しました。

前向きなCoCを作ろう

なぜCoCが必要だと判断したのかは、松崎さんがJANOG39で開催してくれたJANOG行動規範BoF (Bird of feather, 同好会)の資料でも書かれている通り、

  • 僕たちがどういう価値観や倫理的指針を持って活動しているか明文化しておきたい
  • 嫌がらせ(ハラスメント)は許されない行為だと明確にしておきたい
  • この思いに賛同する人がどんどん参加して、このコミュニティが発展してくれると嬉しい

という3点が主な理由です。会員やミーティング参加者の行動を厳しく縛るような後ろ向きな規約を決めるのではなく、参加する人が安心して参加できるように前向きなCoCにしたいと作る際に方針を決めました。

JANOG39 :: [JANOGCOC] コミュニティの行動規範 - JANOGはどうする?

JANOGというコミュニティがどのような価値観を持っているかを表明するとともに、ハラスメントに対して許容しない、ハラスメントがあればアクションを取るということを明文化することで、そうした行為が行われた際にミーティング実行委員(スタッフ)のみなさんが自発的に行動できる指針を示しておくという効果ももあります。

CoCはコミュニティの価値を高めるために必要

「昔はそんなもの必要なかった」というご意見もいただきますが、我慢させられ泣き寝入りしてしまう人もたくさんいたのではないかと思います。

コミュニティ運営者がCoCを作ることで、そうしたハラスメントを受けた方をケアし、またハラスメントを抑止することで、コミュニティの価値や魅力を上げていくことに繋がります。それによりコミュニティが活性化して会員や参加者もその恩恵を受けることができるようになり、良い循環が生まれます。

検索するとCoCの雛形なども公開されていたりするので、まだCoCを制定していないコミュニティ運営者はぜひ制定してみてください。

NTTComを退職してNTTComに入社します

TL;DR

平成から令和への時代の変遷とともにNTTComを退職し、スペシャリスト社員としてNTTComに入社し、新たな人生を歩みます。

仕事としてはSDNのテックリードとしてNTTComのクラウドのSDN基盤のStabilityとScalabilityの向上に引き続きチャレンジしていきます。

一階級飛び級して課長相当のロールの仕事をして、終身雇用と年功序列労働組合の轍から外れる人生を歩むことになります。お給料もあがります。

これからは、短期的にはテックリード→アーキテクト/エンジニアリングマネージャー→CTOのような感じで、NTTComの中でのエンジニアのキャリアアップのロールモデルとして活躍し、道を切り開いていきたいと思います。

長期的にはNTTComに限らず、ネットワークのおもしろい案件を渡り歩いて請け負っていく、エンジニアやエンジニアリングマネージャーとして生きていこうかなと思います。おもしろい案件があればいつでもお声がけお待ちしております。

f:id:yuyarin:20181221093456j:plain

どしたん?

実は年明けから転職活動をしていました。理由としては

  • 知り合いから一緒に仕事をしたいと誘われていたおもしろい案件が複数あった
  • 年末のイベントで「エンジニアの給与が低い」という意見に対して社長が「自分の市場価値を考えろ」と煽ってきたので測りたくなった(最下部に補足追記あり 2019-05-01 18:30)
  • 転職活動を経験しておかないといざという時に失敗すると思ったので、全部落ちてもいいからとりあえず経験値を積もうと思った
  • 年末年始と社内で不信感の溜まる出来事が立て続けに起きた
  • 大きい仕事が一区切りした
  • ちょうど切りの良い年齢だったので、年末年始の考える時間で、自分の人生はこのままでいいのかと不安に思った
  • NTTの中でこのまま出世するにしても、他の会社を知らないまま偉くなりたくないなと思っていた

という要因がたまたま同じ時期に重なったというのが大きいです。

いくつかの会社から無事に内定をいただきまして、うち1社でネットワーク&ソフトウェアエンジニアとして最強のデータセンターネットワーク&クラウド基盤を構築しようと、転職する気満々で円満退職に向けて社内調整を行っていたのですが、いろいろなことがあって、結果としてNTTComに残ることにしました。内定を頂いた会社の皆様にはご迷惑をおかけして本当に申し訳ないです。

何があったん?

もともと社内でたくさんの先輩方から目をかけて頂いていたので、転職に際して強い引き止めがあることは予想していました。なので自分が何がやりたくて辞めるのか、なぜその会社に行くのかをちゃんと説明して、納得はしてもらえないだろうけど、理解はしていただいて、それから辞めようと思い、お世話になった方々に仁義を切って回っていました。

そこにこの記事ですよ。

style.nikkei.com

「おしかけラグビー」というキャッチーなフレーズが話題になってTwitterはてなのエンジニア界隈で大炎上したわけです。辞めるつもりの会社とはいえ愛着はあったし、今辞めたらラグビーボールを投げつけられたから辞めたと勘違いされかねないので、これはいかんとすぐに火消し記事を書いたわけです。

yuyarin.hatenablog.com

そしたらはてブでも反響があり、ねとらぼの記事にもなり、誤解はある程度解けて、徳力さんにもべた褒めされたわけです。

nlab.itmedia.co.jp

news.yahoo.co.jp

これらの記事は社内でも色んな人に見られて、感謝や好意的な反応、応援をいただきました。そして僕は内心「次の記事、退職エントリーなんやけどどないしょ…」って冷や汗をかきつづけていました。

こんな出来事がタイムリーに起きてしまった結果、結論から言うと、この記事で言及されているキャリアパスの「スペシャリスト社員」として、キャリアップしていくモデルケースになっていくことにしました。

どゆこと?

記事にも書かれている通り、スペシャリスト社員自体はもともと社内に密かにいたのですが、誰がスペシャリスト社員なのかも公にされていない状態でした。また、スペシャリスト社員自体が正社員の劣化版のような状態で始まっていました。しかしながら今は業界他社と競争力のある環境で人材を確保するための枠であり、多種多様なキャリアアップを実現するための制度となろうとしています。

仰々しくいいましたが平たく言うと、やっとIT系企業では普通の雇用が始まったということです。スタートラインに立っただけなんですが、この一歩はNTTのようなトラディショナルな日本の大企業にとっては大きな転換だと感じます。

そして私自身がこの制度で転身し、後輩のエンジニアたちの道標となるということが、NTTComを変えて優秀な若手エンジニアの環境や待遇を良くする方法を悩んでいた中で、非常に魅力的な選択肢として心に響いたので残る決心をしました。

その決心の土台として、今の仕事がチャレンジングで面白かったのと、今のチームが最高に良いチームだったことは非常に大きかったです。

なんで退職?

スペシャリスト社員になるために雇用替えを行うので、一旦退職という扱いになります。退職金もいただきます。そしてNTTの終身雇用&年功序列のシステムから外れます

そういやこれまたタイムリーに経団連会長が終身雇用のギブアップ宣言をされましたね。NTTの業務自体はなくなるとは思いませんが、経営する主体は変わることはあるんじゃないかなと思います。その時にいくらNTTとはいえ勤続年数が上がるに連れて給与が上がるシステムは全くもって期待できないと思います。

そしてスペシャリスト社員ということで職務型(いわゆるJob Description型)の雇用体系に変わります。なので今のポジションが無くなったら、どうなるかわかりません。

というわけで僕自身は一度終身雇用を捨てた身であって、転職先がたまたま同じ会社だっただけという認識です。なので常に転職市場に繰り出すことは考えています。

給料あがったの?

あがりました。他社とある程度の競争力のある水準まであげていただきましたし、職位としても非管理職の役付きになってすぐにもかかわらず、管理職である課長相当のロールにあげていただきました。次の職位にあがるためには3年とか一定期間を待って試験を受けないといけない中、実質一階級飛び級したことになります。これは実力さえ示せば勤続年数に関係なく上の役職相当の仕事ができるということで、トラディショナル・ジャパニーズ・カンパニーにおいては大きな変化だと感じています。逆に言えば育成の猶予があった3年をすっ飛ばして、その職位としての結果を求められる過酷な状況に自分を追い込んだということです。

また、スペシャリスト社員=給与アップというわけではありません。JDベースの仕組みですので、当たり前の話ですが、そのポジションの会社にとっての重要性や、その人に期待する役割などによって給料は決まります。

そのような結果、私より職位が高い人に比べて額面上は高い給料になっている場合もあります。NTTのような序列のある会社の中でこの状況は精神的に結構キツイです。でもどこかでこの逆転現象を起こさないと、自分より若い人たちの給料も上がらないと思ったので、自分がこの役目を買って出ることにしました。

終身雇用から外れたので退職金とかはありませんし、住宅補助や扶養手当のような一部の福利厚生もありません。そしてなにより将来の保証がありません。その分を考えた時に本当に良かったのかどうかはまだわかりません。が、どうせ辞めるつもりやったんやからええやろと吹っ切りました。

あがったとは言っても id:kumagi よりは安いし、彼らはそこから上がっていくことを考えると、まだまだGAFAへの流出は防げない水準であると関係者には伝えてあります。僕が内定を頂いき転職しようとしていたところはGAFAではなく国内企業です。たまたまこういう特殊なきっかけがあったので私の流出は防げましたが、待遇や環境を総合的に考えると、実は国内企業への流出を防げる水準にも全然なっていないということは言っておいたほうが良いかもしれません。

何の仕事やんの?

そもそも今まで何の仕事をしてるのかあまり公にしてませんでしたが、エンタープライズ業界向けのクラウドサービスのSDN基盤を作ってました。ひとまずはこれまで通りの仕事内容になるのですが、テックリードの肩書も正式にもらえる予定なので、引き続きチームを引っ張ってSDN基盤のSREを実現してStabilityとScalabilityの向上にチャレンジしていきたいと思っています。対外活動も引き続きやっていきます。

Why NTTCom?

NTTComは国内でIaaS/クラウドの開発ができる事業者の中で最大規模だと思っています(ちなみにNTTドコモもやってるよ)。AWSGCPの開発をやりたいと思うと、どうしてもアメリカの本社に行かないといけないですよね、知らんけど。今のチームにJoinしたときに中身を知って「え?NTTComがこんなにすごいサービス作ってるの!?」ってびっくりしました。

自分たちで開発しているからこそ、障害が起こると自分が日常で使っている色々なシステムやサービスが止まってしまう怖さや責任感もありますし、だからこそ「NTTComのクラウドのネットワーク全部俺」って言える楽しさもあります(もちろん私だけじゃなくてみんなで作ってますが、そういう気持ちでいられるということです)。それを結構魅力に感じています。

まだまだ直さないといけないところはたくさんあるし、日本企業の古い文化や体質が残っているところもたくさんありますが、労働組合が強く労働者が守らていて休みも取りやすい超絶ホワイトな会社であるというNTTの従来型の良い面も保ちながら、オフィスも新しくなったし、リモートワークもできるし、フレックス勤務もできるし、副業もできるし、Job Description型の雇用体系もできるし、会社自体も時代に合わせてここ数年で劇的に改善しています

従来どおり適度な仕事で安定した人生を送りたい方にはもちろん、不安定な環境でもいいので自己実現したい尖ったエンジニアにも、両方の人たちにおすすめできる会社になってきています。 

f:id:yuyarin:20190430212407j:plain

※ 自席ですが狭く見えるのは写ってはいけないものが写らないように狭い範囲だけを撮っているからです。なおMacBook 1画面派でモニターはあんまり使ってませんので私はデュアルディスプレイにはしてません。ぬいぐるみは「おしかけぷよぷよ部」を始めようと思ったからです。

さいごに

この件についてはたくさんの関係者の方に動いていただきました。本当にありがとうございました。

ところでお客様の中に、ネットワークのすべての分野に精通していて、クラウドコンピューティングが好きで、データセンターネットワークを作るのが好きで、特にBGPとかEVPNとかVXLANとかが好きで、でもVRRPとかSTPとかARP/NDとかエッジ・ホスト側のネットワーク技術も好きで、tcpdumpでパケットレベルのデバッグをするのが好きで、C++Pythonの読み書きデバッグが好きで、Linuxカーネルやネットワークスタックが好きで、OpenStackが好きで、CassandraとかRabbitMQとか分散ミドルウェアが好きで、docker/k8sとかのコンテナ技術が好きで、英語をしゃべるのが好きなエンジニア、いらっしゃいましたらTwitterとかで @yuyarin までご連絡ください。

それでは令和時代もよろしくお願いいたします。

 追記

『「エンジニアの給与が低い」という意見に対して社長が「自分の市場価値を考えろ」と煽ってきた』という部分にネガティブな反応が見受けられたのですが、まずこれはこれは私個人に対して向けた言葉ではなく、意見を出した人個人に「君は市場価値がない」という意味で言ったわけでもありません。

社長は社会人として当たり前のことを指摘したに過ぎないと思っています。何の根拠もなく給料を上げろというのは筋が通っていませんし、NTTヨーロッパの人の話を聞いていても他社提示額を以って給与交渉をするようですので、そのような方法を以って話をしようという意図だったのだと思います。社長からすると、会社全体で見たときに市場価値より高い給料で雇われている人がたくさん見えるのだと思います。

そのようなわけで、私も反発して感情的に転職をしようと思ったわけではなく、なるほど確かにと理性的に納得して、それを確かめるために転職市場に繰り出そうと思った次第です。

追記2

有益情報をいただきました!

 

NTTCom社員は本当にラグビー部員におしかけられているのか?

TL;DR

そんな事実は無いし、技術職も希望をちゃんと伝えればちゃんと配属されると思うので内定者・就活中の学生の皆様は安心してね。

この記事は何?

style.nikkei.com

b.hatena.ne.jp

この記事がはてなブックマークTwitterのエンジニア界隈で大変な話題になっているけど、事実と違うことが書かれて炎上しているのが中のエンジニアとして大変悲しいですし、内定者や就活中の学生のみなさんが不安になってしまうと思ったので、ちゃんと解説するいわゆる火消し記事です。

この記事は勤務時間中に上司の許可や広報の許可を得ずに書いていますので、普通の会社なら怒られるかもしれませんが、うちの会社は多分大丈夫だと思います。

あくまで一社員とその周囲からの観測範囲をベースにして書いているので、見え方が違うこともあるかもしれませんが、そこはBGPの経路情報と同じでご容赦を。

炎上しているポイント

主に以下の2点で炎上していると認識しています。

ポイント1:技術職がどこに配属されるのかわからない

「技術職として採用する場合、『どの部署に配属されるかはわかりません』と言ってきました。ところが、それだと『じゃあ御社には行きません』といわれてしまうケースも多くて。採用の環境の変化を感じます。だからこそ、色々な経験ができるのが当社のよさだと伝えていかなければならないと思っています」

ポイント2:業務中にラグビー部が突入してきてボールを投げてくる

職場に突然、ラグビー部員がわーっと入っていって、社員にボールをパスしたり、体を動かしてもらったりするんです。

要約

  • 技術リクルーティングの枠組の中で採用された技術職は配属先はちゃんと希望されたところに行けるようになっています。
  • 「おしかけラグビー」はラグビー部員が広報する活動のこと。色々な企業や団体に招かれて行っているみたいですが、NTTコム社内で突然ラグビー部がラグビーボールを投げつけている事実はありません。

技術職の配属について

私が入社した2011年ごろは記事に書いてあることは確かに本当でした。正確には技術職という枠組みもなくて、一律「新入社員」という扱いでした。その中で意にそぐわない配属をされて涙を流した同期もいました。

しかしここ数年は技術リクルーティングという枠組みを作って採用活動を行っており、こちらの枠組みでは、配属先の部署はある程度コントロールされて「約束通り」になるように頑張っています。クラウドのベアメタルの開発、クラウドのSDNの開発、高速ルータの開発、OCNの開発、といった感じで、ネットワーク、クラウド、セキュリティ、アプリケーションなど複数の分野で技術の専門家を採用しています。

もともとの採用方針がいわゆる日本企業的な「コミュ力重視」の採用で、そういうのが苦手だけどとても優秀なエンジニアがNTTComに入社しなくなってしまうのを避けるためのパスです。大学によっては推薦の枠組みがあるので、その時はご相談ください。

少なくともこの採用パスに入ることができる実力を持った腕利きのエンジニアには「何処に配属されるのか分からない」みたいな状況は待っていません。

スペシャリスト採用』のような仕組みがあってもいいのかもしれませんが、現実には3年ぐらいたってから選べるようにする方がいいかなと思っています。

という部分については私も納得行かないので、技術リクルーティングで採用されたエンジニアは1年目からスペシャリスト採用になる選択肢を持てるように声を上げていきたいと思います。

ラグビー部がラグビーボールを投げてくる件について

今までのNTTComでの業務時間中にラグビー部からラグビーボールを投げられたことはありません。もしかして僕は社員じゃなかったのかと不安になりましたが、エンジニアのSlackでアンケートを取ったところ21名(のべ在籍年数128年)から即回答があり、業務時間中にラグビー部から突然ラグビーボールを投げられた経験は誰一人ありませんでした。

親切な方が調べてTwitterで発信してくださっています

 

そのようなわけでこの部分については中の人的には「そんなことやってへんやろー」っていう事実が歪んで伝わった程度の記事が、思いの外のキャッチーさで炎上してしまったという感じです(もしかしたらどっかで本当にやってるのかもしれないけど)。

まぁ、大丈夫です、ラグビーボールは飛んできません。

f:id:yuyarin:20190328113747p:plain

この記事で本当に伝わってほしいこと

外資IT系の友人たちからは「やっと10年前だな」という言葉を頂いていますが、これは本当にそのとおりです。私の見方としては「やっと"まとも"な改革ができる人が人事部長になれる時代がやってきた」のです。

山本恭子さんは数々の抵抗勢力に負けずに改革を推進して、社員が働きやすい会社を作ってきています。彼女は我々エンジニアの希望の星でもあります。なのでラグビーの件で「こんな人がトップだから」と言われるのはとても悲しいですし、より制度がよくなるように助言やサポートはしていきたいです(この記事も一つの形です)。

在宅勤務やフレックスなど働きやすい仕組みも整ってきましたし、能力型の「スペシャリスト社員制度」も始まっていますので、腕に覚えがあればGAFAに匹敵するぐらいの給与もでるかもしれません、知らんけど。制度などの面では本当に働きやすく、ここ1年で劇的に良い職場になってきました

記事でも言及されている通り、課長が勤務管理で忙しすぎるとか、まだまだ改善しないといけない面もたくさんありますが、それを人事部はちゃんと認識していて、なんとかしようと頑張っています。出さなくても良いネガティブな内容を敢えて出しているというのは「これから変えていくという有言実行のメッセージ」だと捉えています。

ところでお前誰?

NTTComでエンタープライズ向けのクラウドサービスのSDN基盤を開発してるよ。ネットワーク機器の検証もするし自分たちでコードも書くよ。開発環境もMacBook ProでSlack、G Suite、Confluence、Jira、GitHubだよ。今はスケーラビリティを上げつつもSDNのSREを実現して安定したネットワークが作れるようにチームで頑張ってるよ。

その前はインターネットエクスチェンジのJPNAPでネットワークエンジニアとソフトウェアエンジニアの両方をやってたよ。ガリガリソフトウェアを書いてネットワーク運用の自動化とかやってたよ。バックボーンの設計とかもやってたよ。

ネットワークとソフトウェアの両方ができるいい会社だよ。

まとめ

他の会社の炎上記事もこんな感じなのかなぁって思うと、話半分に読むのが良いのかなと思いました。

宣伝

NTTの人たちがやっているエンジニアリング的なお話に興味がある方はどうぞ

fukabori.fm

FAQ

映像があるんだけど?

www.youtube.com

この件については詳しくは知りませんが、場所はNTTCom社内ではなく浦安市役所だと思います。さすがにそこにいきなりおしかけるのは不法侵入になりますので、先方から招聘いただいて実施したイベントではないかと思います。その場合は、社内でいきなりラグビーボールをぶん投げてくるのとは事情が全く異なるかと思います。

取材場所はラグビーチームのシャイニングアークスの拠点であるアークス浦安パークだと思います。そのご縁で浦安市役所に伺ったのかと思います。

ちなみに浦安市にNTTComの事業部のオフィスはありません。

各部門の紹介 | NTTコミュニケーションズ 企業情報

たった21人の調査?

大手町プレイスっていう新築のビルにオフィスを移転しました!1フロア300-400人で仕切りもない大部屋なのでオフィスにラグビー部員が突入してきたらすぐわかりますよ。そういう状態で少なくともサービス開発・エンジニア系のフロアでそういった事象は21人の目で観測されなかったということです。ご指摘の通りSlackにいなさそうな部署の人のフロアで突入があったら、わかりません。営業系のフロアとか、社長室とか。

https://developer.ntt.com/sites/default/files/blog_images/MVIMG_20181218_182326.jpg

NTT Com Tech Night #1を開催しました! (レポート) | NTT Communications Developer Portal

火消し業者?

そうそう、NTTComでは副業もOKになりました。副業ってことにしてお金貰えばよかった…!

Telnetでネットワーク機器を操作するときのBCIP - Ruby編

f:id:yuyarin:20161224171645p:plain:w480

こんにちは、ゆやりんです。今年のNetOpsCoding Advent Calendarもいよいよ最終日になりました。たくさんの役に立つ&頭のおかしい投稿ありがとうございます。お仕事が忙しかったので最終日を選びました。オオトリにふさわしい記事はなんだろうかと考えていましたが、やっぱりTelnetについて書こうと思います!!

はじめに

ネットワーク機器を操作する手段としてNETCONFやREST APIなどが登場する中、ネットワークの要件や制約からそういった機能を使うことができず、未だにTelnetSSHでコンフィグを行わないといけない場合ということがあります。え?どの機器かって?昨日のtaijijijiくんの記事を読んでみて下さい。で、そういった状況でどのようにこのTelnetを利用したクライアントを実装するとよいのか、私のRubyでの実装の最善策(Best Current Implementational Practices)を紹介したいと思います。

大変申し訳無いことにコードを全部公開できないのですが、なるべくエッセンシャルな部分は共有できればと思います。いずれプロジェクトの全貌も公開できればなぁあと思います。

アーキテクチャ

Telnetクライアント周りの全体像はこんな感じなります。

f:id:yuyarin:20161224174349p:plain:w480

Telnetクライアントの実装としては王道のNet::Telnetを使って、プロキシとしてrancidのxloginを使うのがベストでした。

それをラップする機器ごとのTelnetクライアントを作り、コマンドを投げたら必要なレスポンスだけが返ってくるようにします。

その機能を使って、各機器のCLI相当の機能を提供するのが「機器固有レイヤーのClient」です。例えばインターフェイスにデスクリプションを設定するなどの具体的なメソッドを提供います。

その上位アプリケーションとして「ビジネスロジックレイヤーのClient」が存在します。これら2つの違いは、同じインターフェイスにデスクリプションを設定するというタスクについて、機器固有レイヤーのクライアントがルータのインターフェイス名を取るのに対し、ビジネスロジックレイヤーのClientはお客様の回線IDなどを引数に取ることです。ビジネスロジックレイヤーのClientでは与えられたIDから機器固有のIDに変換する役割を持ちます。また、複数の機器がある時に共通のインターフェイスを提供することでさらに上位のアプリケーションに対して抽象化された機能を提供します。例えばLAGを作るという機能は、CiscoではPort-Channelを作ることですし、Juniperではaeインターフェイスを作ることですし、Brocade(Ironware)ではLAGオブジェクトを作ることで、それぞれ手順が異なります。これらの差分を吸収します。

ここで作られたクライアントはビジネスロジックレイヤーのワークフローの中から呼び出されます。

Net::Telnet + xlogin

チームメイトの goto_infamouse 君が検証をしてくれているので、あわせてそちらの記事もお読み下さい。

Net::Telnet + xlogin の特徴

Telnet経由で機器を操作する方法として他にpty + IO#expectや、ruby_expectなども検討しましたが、実装と比較を行った結果Net::Telnet + xloginが一番扱いやすいと判断しました。

rancidに付属するxloginはexpectのスクリプト群で、Cisco IOS用のcloginやFoundary用のfloginなど、数十種類の機器に対するログインスクリプトが用意されています。一からexpectのスクリプトを書くよりは、これらを利用したほうが効率が良いです。

使っている機器はFoundaryやCiscoそのものではないので、スクリプトはfloginやcloginを改造してhogeloginを機器ごとに作って別リポジトリで管理しています。

xloginの良いところはコードにパスワードを書く必要が無いところです。実行者の ~/.cloginrc に書く必要があるので、パスワードを誤ってGitリポジトリに突っ込んでしまうみたいなミスも起きません。

Net::Telnet + xlogin の異常系の処理

商用ネットワーク環境で動かすシステムを作るときに一番重要になるのが異常系の処理です。想定される色々なエラーに対してNet::Telnetの動作を検証しました。たとえば、xloginスクリプトが無い、パーミッションが違う、異常終了する、ホスト名が引けない、機器へのIP接続性がない、ACLで接続拒否される、パスワードが間違っている、などなど、色々なパターンです。だいたいは Errno::EPIPENet::ReadTimeout で処理できますが、以下の3パターンは、正しく処理しないとルータのvtyを専有し続けるという点で、気をつけなければならないエラーでした。

間違ったenableパスワード

.cloginrc に記載されているパスワードが間違っていた場合、xloginのタイムアウト秒後に Net::ReadTimeout の例外が発生しますが、正しく処理しないとxloginの親プロセスがinitの子プロセスに移って、ルータのvtyを専有し続けます。 Net::ReadTimeout を補足した時は即座に xlogin のプロセスを kill する必要があります。

ネットワークの切断でtelnetセッションが切れる

途中でネットワーク機器への接続が切れる場合です。さきほどのケースと同様に Net::ReadTimeout が発生しますが、 xlogin を kill してもネットワーク接続性が無いため、ルータの vty は専有されたままになり、ルータ側の telnet-timeout を待つことになります。接続性が切れてもすぐに回復するような場合や、ACLやHWが原因でそのセッションだけ停止される場合に問題になります。今のところ対処方法はありません。

Matchで指定したプロンプトが来なかった

Net::Telnetではプロンプトのマッチングを行うことができます。期待したプロンプトが来なかった場合に「間違ったenableパスワード」の場合と同様の動作が発生します。

A::Telnet

A::Telnet の役割

Net::Telnetのラッパーを各機器ごとに作っています。ルータAのライブラリをA::Telnetとしましょう。このラッパーの主な目的は「コマンドを投入して、表示結果を得る」という機能を上位アプリケーションに提供することです。更に挙げれば次のような機能を提供しています

  • Net::Telnetの例外を適切に処理する
  • ログイン関連の機器固有のエラーを処理する
  • コマンドに対する出力だけを返す
  • コマンド実行後のプロンプトの判定を可能にする
  • 機器の操作ログの保存

A::Telnet のコマンド処理

例えば show clock というコマンドを実行すると時刻が表示されて、次のプロンプトが出る表示が出るような場合、Net::Telnetでcmdメソッドを実行すると次のような値が帰ってきます。

puts telnet.cmd('show clock')
=> "show clock\n22:02:20.947 JST Sat Dec 24 2016\n\e]1hostname\ahostname"

入力したコマンドそのものがエコーされ、実際に欲しい表示が出力され、その次のプロンプト(しかもターミナルのタイトルを変更するエスケープシーケンスが入っていることがある)が含まれる3行の文字列が返ってきます。これらを処理して本来欲しいコマンドの出力結果だけを上位アプリケーションに返します。

この時に次のプロンプトを得ることができるので、保存しておくとプロンプトの判定に利用することができます。Telnet経由の操作では正しい階層に入ったかどうかプロンプトで確認する必要があります。例えば global レベルと interface レベルの両方で実行可能なコマンドがあったときに、interfaceレベルでコマンドを実行しようとしたのに interface GigabitEthernet 1/1 が失敗して global レベルで投入してしまった、というようなことが起こりうるからです(そういうCLI設計もやめてほしいですが)。

あるネットワークに対する実装は次のようになっています。

    def execute_cmd(opt)
      result = @connection.cmd(opt) # @connectionはNet::Telnetのインスタンスです
      response = result.split($/)
      # 先頭行は入力したコマンドなので除外
      command = response.shift
      # 最終行はコマンド入力後のプロンプトなので結果から除外
      next_prompt = response.pop
      _log(response.join($/))
      @current_prompt = next_prompt
      return response.join($/)
    end

A::Telnet の接続開始部分

Net::Telnetでxloginを使った時のやっかいな部分は、デバッグが非常にやりづらいということです。何が原因でどこで死んでいるのかぱっと見分からないからです。さらにプロセスが残りる続けたりルータのvtyを食ったままになっていたりと大変でした。接続を開始するメソッドはこんな感じです。Proxy オプションに xlogin を IO.popen で作ったオブジェクトを渡してあげることで、ログイン処理をrancidに丸投げすることができます。プロセス終了時や異常終了時に xlogin を kill することに備えて、pidを保存しておきます。

    def open_connection
      proxy_command = "#{@xlogin} #{@hostname}"
      prompt = /^telnet@#{@hostname}(>|.*#)/
      prompt_enable = /^telnet@#{@hostname}#/
      timeout = @timeout
      wait_time = 0

      begin
        @proxy = IO.popen(proxy_command, 'r+')
        @proxy_pid = @proxy.pid
        _log("proxy opened #{proxy_command} with pid #{@proxy_pid}")
        @connection = Net::Telnet.new({
          'Prompt'     => prompt,
          'Timeout'    => timeout,
          'Waittime'   => wait_time,
          'Binmode'    => false,
          'Telnetmode' => false,
          'Proxy'      => @proxy,
        })
        _log("created Net::Telnet instance")

        r = execute_waitfor(prompt_enable)
        # 1. proxyコマンドが異常な挙動を起こしている場合
        unless r
          error_message = "unexpected behavior of proxy command"
          _log(error_message)
          raise Errno::EPIPE, error_message
        end
        # 1. Error: Couldn't login
        #   a. ホスト名が解決できない場合(Name or service not known)
        #   b. ネットワーク接続性はあるがホストに接続できない場合(No route to host)
        # 2. Error: Connection Refused
        #   a. telnetのACLでdenyされている(Connection refused)
        # 3. Error: Connection closed
        #   a. ログインパスワードが間違っている
        if r =~ /Error:/
          raise A::LoginError, r
        end
        # 2回waitforするとうまくいくというおまじない
        r = execute_waitfor(prompt_enable)

        _log("login completed")
        _log(@current_prompt)

        cmd("terminal length 0")

      # Errno::ENOENT
      #   proxyコマンドが存在しない場合
      rescue Errno::ENOENT => e
        raise e
      # Errno::EACCES
      #   proxyコマンドの実行権限がない場合
      rescue Errno::EACCES => e
        raise e
      # Errno::EPIPE
      #   proxyコマンドとのパイプが確立できない場合
      #   接続エラーなのでコネクションを閉じる必要が無い
      rescue Errno::EPIPE => e
        raise e
      # Net::ReadTimeout
      #   enableパスワードの不一致
      #   ネットワーク接続性の喪失
      # X::LoginError
      #   17行上で投げてる
      #
      # 接続した状態でのexpectのエラーなのでコネクションを閉じる
      rescue => e
        close_connection
        raise e
      end
   end

このメソッドはpriavteで、コンストラクタから呼ばれます。

「2回waitforするとうまくいくというおまじない」という部分に苦労を感じ取って頂けるとうれしいです。。。

A::Telnet のコマンド実行部分

publicメソッドとして外部に出しているのがこのcmdメソッドです。コマンドを受け取って、その表示結果だけを返す機能を提供しています。当初はここでプロンプト判定も行っていましたが、current_promptメソッドを提供して、判定は上位アプリケーションに任せる設計に変更しました。

    def cmd(command)
      result = ''
      begin
        @current_command = command
        _log("#{@current_prompt}#{command}")
        result = execute_cmd(command)
      rescue Errno::EPIPE => e
        raise e
      rescue => e
        close_connection
        raise e
      end

      return result
    end

A::Telnet の接続終了部分

接続終了時には SIGHUP で xlogin のプロセスを殺します。先にexitなどのコマンドでtelnetセッションから抜けている場合には xlogin のプロセスが終了しているためはkillを行おうとしても IOError が発生します。この例外はここで無視します。

    def close_connection
      return unless @connection
      _log("closing connection...")
      begin
        unless @proxy_pid==0
          _log("sending SIGHUP to proxy with pid #{@proxy_pid}...")
          # telnetなのでSIGHUPで適切に接続を終了してくれることを期待する
          Process.kill(:HUP, @proxy_pid)
        end
      # Errno::ESRCH
      #   プロセスが存在しない場合
      rescue Errno::ESRCH => e
        _log("process with pid #{@proxy_pid} not found")
        # NOP
      end
      begin
        @connection.close
      # IOError
      #   @connectionがcloseされている場合
      #   @proxyをkillしているので必ず発生するはず
      rescue IOError => e
        # NOP
      end
      _log("connection closed")
      @connection = nil
      @proxy_pid = 0
    end

いまや安全にこのクライアントを使って機器操作できているのはチームメイトの goto_infamouse 君がNet::Telnetの動作検証をしっかりやってくれたおかげです。

機器固有レイヤの A::Client

機器固有レイヤの A::Client の役割

このA::Clientクラスは機器固有の処理をする部分で一番実装が膨らむ箇所です。具体的にはCLIコマンドに相当するメソッドを提供して、その結果を構造化されたデータとして返します。

例えば Cisco IOSshow interface summary コマンドを例にすると、次のような利用を想定しています。

c = CiscoIOS::Client.new(hostname, options)
c.show_interface_summary
=> [{
  'Interface' => 'FastEthernet0',
  'Up' => true,
  'IHQ' => 0,
  ...
},{
  'Interface' => 'FastEthernet1',
  'Up' => false,
  'IHQ' => 0,
  ...
},
...
]

また、各種コマンドを実行したときのエラーハンドルやプロンプトのチェックもこのクラスで実施します。

機器固有レイヤの A::Client の構成

こんな感じの仕組みになっています。

module A
  class Client

    include ::A::CliParser

    def initialize(hostname, options = {})
      @hostname = hostname
      @telnet = start_client(hostname, options)
    end

    def close
      stop_client
    end

    private

    def start_client(hostname, options)
      A::Telnet.new(hostname, options)
    end

    def stop_client
      @telnet.close
    end

    def exec_cmd(command)
      @telnet.cmd(command)
    end

    public

    def show_interface(interface_names)
      interface_names = validate_interface_names(interface_names)
      res = exec_cmd("show interface #{interface_names}")
      parse_show_interface(res)
    end
  end

  module CliParser
    def parse_show_interface(res)
      # CLIの結果をパースして構造化して返す
    end
  end
end

CLIのパーサはテストをしやすくするためにモジュールとして切り出してあります。start_clientやexec_cmdなどもメソッドとして切り出しているのは、テストのときにモックのメソッドに差し替えられるようにするためです。

機器固有レイヤの A::Client のCLIパーサのテスト

このクラスでは愚直にCLIコマンドに相当するメソッドとそのパーサを実装するだけなので、それほど悩む点はありませんが、ひとつあるとするならばCLIパーサのテストです。

CLIの表示結果から、得られるHashオブジェクトが一意に定まるはずなので、テストケースとして入力をテキストで、出力をYAMLで用意しておきます。

# spec/fixtures/ios/15.0/show_interface_summary/mixed-interfaces.txt

 *: interface is up
 IHQ: pkts in input hold queue     IQD: pkts dropped from input queue
 OHQ: pkts in output hold queue    OQD: pkts dropped from output queue
 RXBS: rx rate (bits/sec)          RXPS: rx rate (pkts/sec)
 TXBS: tx rate (bits/sec)          TXPS: tx rate (pkts/sec)
 TRTL: throttle count

  Interface                   IHQ       IQD       OHQ       OQD      RXBS      RXPS      TXBS      TXPS      TRTL
-----------------------------------------------------------------------------------------------------------------
* FastEthernet0                 0         0         0       130    194000        50    195000        52         0
  FastEthernet1                 0         0         0         0         0         0         0         0         0
# spec/fixtures/ios/15.0/show_interface_summary/mixed-interfaces.yml
- Interface: "FastEthernet0"
  Up: true
  IHQ: 0
  ...
- Interface: "FastEthernet1"
  Up: false
  IHQ: 0
  ...

テストはRSpecを使って次のように書いています。

# spec/ios/cli_parser_spec.rb 
def path_of_example(version, command, case_name)
  command = command.tr(' ', '_')
  "spec/fixtures/ios/#{version}/#{command}/#{case_name}"
end

def load_input(version, command, case_name)
  File.read("#{path_of_example(version, command, case_name)}.txt")
end

def load_expected_output(version, command, case_name)
  YAML.load_file("#{path_of_example(version, command, case_name)}.yml")
end

def get_method_name(command)
  command = command.tr(' ', '_').tr('-', '_')
  "parse_#{command}"
end

def exec_parse(parser, version, command, case_name)
  input = load_input(version, command, case_name)
  method_name = get_method_name(command)
  parser.method(method_name).call(input)
end

RSpec.shared_examples "interface mixed-interfaces" do
  it "parses normal output" do
    case_name = 'mixed-interfaces'
    expected = load_expected_output(version, command, case_name)
    expect(exec_parse(parser, version, command, case_name)).to eq(expected)
  end
end

RSpec.shared_examples "interface error-invalid-slot-number" do
  it "raises error for invalid slot number" do
    case_number = 'error-invalid-slot-number'
    expect{ exec_parse(parser, version, command, case_number) }.to raise_error(A::InvalidInputError, 'Invalid input -> 100/1')
  end
end


RSpec.describe A::CliParser do

  let(:test_class) { Struct.new(:parser) { include A::CliParser } }
  let(:parser) { test_class.new }

  context "with IOS Version 12.0" do
    let(:version) { '12.0' }

    describe "parse show interface summary" do
      let(:command) { 'show interface summary' }
      include_examples "interface mixed-interfaces"
      include_examples "interface error-invalid-slot-number"
      ...
    end
    describe "parse show ip ospf" do
    ...
  end

  context "with IOS Version 15.0" do
    let(:version) { '15.0' }
    ...
  end

  ...
end

工夫したポイントとしては、バージョンが複数あり、コマンドが複数あり、ケースが複数あるので、それぞれに識別子をつけてそれを指定すればテストができるようにしたところです。ここでいうケースは「インターフェイス名に無効な値を指定した場合」とか「10Gの正しく設定されたインターフェイスの場合」とかです。

まず、CLIParserをモジュールとして分離しているので、let(:test_class) { Struct.new(:parser) { include A::CliParser } }let(:parser) { test_class.new } でパーサの機能だけを持った試験用のクラスを作成します。

contextlet(:version) { '12.0' } を使ってOSのバージョンを指定し、describelet(:command) { 'show interface summary' } で対象のコマンドを指定します。

ケースは RSpec.shared_examples を使ってバージョンやコマンドを通して共通化しておきます。これを include_examples case_name で呼び出します。

バージョン、コマンド、ケース名の3つがわかると、入力ファイルのパス、出力ファイルのパス、CLIパーサのメソッド名が自動的にわかるように命名規則を定めています。

shared_example の中では、入力のテキストを読み込み、パーサに食わせて、出力のYAMLと比較する、ということをやっています。

このようにテストを書いておけば、少なくとも既知のコマンド出力に対しての動作を保証できるのでとても安心です。

ビジネスロジックレイヤのClient

ビジネスロジックレイヤでは次のことを行うつもりです。というのもまだしっかりと実装ができていなくて、この設計でいいのかわからないからです。

たとえばVLANを変更するといった処理をするときに、機器Aではインターフェイスをshutdownする必要があるが、機器Bでは必要ない、といった場合に、これらのワークフローの違いをどう扱えばいいのかという課題に直面します。上位のワークフローで扱うのか、このクラスでワークフローを隠蔽したメソッドを提供するのか、難しいです。単にやるだけならこのレイヤーで吸収してしまえばいいのですが、例えば「Web画面にshutdownされることの警告を出したい」となってしまうと困ります(実際にはshutdownされないのに警告を出して「shutdownされる可能性があります」みたいに言う手もありますが)。

このあたりに関しては試行錯誤している最中なので、もう少し知見が溜まってきたら記事を書いてみたいですし、もしも経験のある方がいらっしゃれば記事を書いてほしいです。

さいごに

今年のクリスマスイブはTelnetの記事を書いて過ごしました。

Merry Christmas and Happy NetOpsCoding!!

保育園を増やすか移民を受け入れるかどっちがいい?

あんまりプライベートなこととか政治的な意見は公に書きたくないんだけど、 id:gomi-box から待機児童ゼロチャレンジバトン(#taikijidou0challenge)なるものが回ってきたので書きます。

gomi-box.hatenablog.com

時間が無い人のためのまとめ

タイトルはちょっと飛躍してますが本質は結局はそういうことなんじゃないのかなーと思う。

最初にまとめておくとこの記事にはこういう話が書いてある:

  • 待機児童を減らすことはあくまで手段で、目的は日本の人口を増やすことだよ
  • 日本の人口を増やそうと思うと3人は産まないとだめだよ
  • 働きたいから保育園に預けるってのはワガママだよ
  • でも今の世論や政策のようにワガママを聞いてもらえない状況だと、みんな子供は多くても1人で産むの止めちゃうよ
  • 日本政府がやるべきことは国民個人がワガママ=自己利益の最大化に走ろうとした時に日本の人口を増やせるような方向へ誘導する政策を実行することだよ
  • それができないと年金は破綻するし、経済を維持するために移民を受け入れないといけなくなるよ
  • ちなみに移民も子供を産んで保育園に預けようとするから、日本人は更に保育園に預けられなくなって子供を産む数が減るよ
  • みんなそれでも平気?

そもそも日本の人口を増やさなくていいとか移民大歓迎って人にとっては、この記事は価値がありませんので、ここで読むのを止めていただくことを推奨する。

ここから国に対する要求として次の3点を主張する。

  • 子供を3人産める環境を作れ
  • 育児と仕事の選択をさせるな、両立させろ
  • 保育を親ではなく国の義務・責任にしろ

ここでは「保育園」を0歳から小学校入学までの保育施設をひっくるめた意味で使いたいので幼稚園の延長保育や認定こども園なども含めたいと思う。

最低3人産まないと日本の人口は増えない

女性1人当たり2.08人産まないと日本の人口は維持できないらしい。結婚しない女性もいるだとうし、子供を産まない夫婦もいるので、産む気のある夫婦では3人は少なくとも産まないと日本の人口を維持できないし増やすこともできない思う。なので3人産む前提で話を進めたいと思う。

子供が1人である程度活動できるようになるのを小学校1年生だとすると、1人産んだ時につきっきりで子供の面倒を見ないといけなくなるのは0歳から6歳頃までの6年間になる。3人産むとなると最低でも8年はかかる。法的な育児休業は1歳6ヶ月までだし企業規定の育児休業も3歳までのところが多く、年子で3人とか無理なので実際は間隔を空けて10年近くかかることになる。

また共働きしないと家計が成り立たない世帯は保育園に預けざるを得ないので、彼らの話はしない。共働きしなくても家計は成り立つけど働きたいから預けたいという世帯を対象にする。

それから、3歳までは親が育てたほうがいい等の育児論や教育論もここでは扱わない。

男女平等と自己実現を尊重する価値観

昔は男は外で稼ぎ女は家の面倒を見ろという価値観だったかもしれないけど、現代は違う。男女平等が叫ばれ女性の社会進出が叫ばれ、それが実現できてきているように思う。

男女関係なく個人の自己実現を尊重する風潮が少なくとも若い世代ではあるんじゃないかなと思う。いわゆるやりたいことをやってなりたい自分になるってやつだ。

僕も妻には働いて欲しいというか、自分のやりたいことをやってほしいと思う。だから育児の負担は半分ずつにしたいと思っている。

なのでここでは男が女がどっちがという話はしない。

女性の権利等の話はちょうど今日素晴らしい記事ホッテントリ入りしていましたので、そちらを読むことをお勧めします。

働き盛りに仕事を長期間休まないといけないのは辛い

3人の子供を育てるとなると、2人で負担を分担しても5年、片方だけが負担するなら10年を乳幼児の育児に専念することになる。

5年とか10年の休業というのは20歳で就職して60歳で定年したときに就労できる40年の間のそれぞれ12%、25%に相当する。

しかもただの5年、10年ではない。大半の人は20代後半から30代の一番仕事がノってきて頑張らないといけない期間に休業することになる。復帰する頃には30代後半や40代に突入している可能性が高い。

その期間働けないということはこの年功序列の日本社会においては残念ながら出世に大きく影響がある。出世を望まなくとも10年のハンデは非常に大きい。

転職をしようと思ってもただでさえ雇用の流動性の低い日本でブランクを抱えて40歳前後で転職というのもなかなか能力と経験がずば抜けていないと難しい話だ。

だから活躍したいのに保育園に預けられなくて日本死ねって言いたくなる気持ちは痛いほどよく分かる。

働きたいから子供を保育園にあずけるのは確かにワガママです

そうした自己実現のために子供を保育園に預けたいという願望は確かにワガママだと言える。それは間違いない。

ただ、そういうワガママな人たちに国のせいにすんなとかワガママだとか自己責任だとか努力が足りないとか地方に引っ越せとか言っても何の意味もない。むしろ逆効果だ。子供を産んだのに日本の社会はなんて酷い仕打ちをするんだと思ってしまう。

そもそも共働きしないと行きていけない人たちしか保育園に預けてはいけないなんていう決まりはない。会見で化粧している余裕があるとかブランド物の抱っこ紐だとかいう批判は筋違いも甚だしいので取り合う必要はないだろう。

保育園問題に対する批判は出産を萎縮する

子供を産んでも自己責任といわれ何かを要求するとワガママだと叩かれる風潮が出来上がってしまうと、子供を産みたいと思う人は減ってしまう。

今の御時世結婚は損だから独身でいようとか子供を産んだら自分たちの時間や使えるお金が少なくなるから産まないでおこうみたいな主張が少なくなく聞かれる状況なのだ。

その上更に、子供を産んだうえで自分の生きたいように生きようとすると社会から叩かれるような空気を膨らませるとますます少子化が加速する。

レベル0の勇者をパーティーに加えて稼いだゴールドを勇者の装備につぎ込みスライム狩りを始めるのだ。2人で海外旅行に行くのもおしゃれなレストランにいくのも無理どころか外食すら厳しい。親達の自己実現など程遠くなってしまう。

個人の最適解は子供を産まないことになる

そうなってくると彼らの個人としての最適解としては子供を産まないということになる。

これは結婚しない・子供を産まないのがお得という風潮にも既に現れている。

子供のために家賃の安い郊外に引っ越して長時間かけて通勤し、趣味も我慢して節制に励む。そんなことするぐらいだったら子供なんてイラネって考える人達は今後ますます増えてくるんじゃないかなと思う。

この発想は特に自己実現欲の強うそうな高所得者層やインテリ層に多いんじゃないかなと思う。そうだとすると後は言わずもがな。

こんな風にもし個人が自己実現のために局所最適化を行うこと自体が嫌なのであれば「1億総活躍」みたいなことは言わないでおいて欲しい。

産んだとしても1人で終わり

もう一度言うが、こういう人たちに「ワガママ言うな、死ね」といっても何の意味もないのだ。「じゃあもう子供産むのやめよー」ってなるだけなのだ。

1人産んで保育園に入れられず、それに文句を言ったら袋叩きに遭う、こんな仕打ちなんだったら2人目を産みたいと思えるか、というより1人目の育児で精一杯で現実的に産めなくなるよね。そんな苦渋を舐めた人たちが2人目を産もうと思わないよね。

それに1人目を仮に保育園に入れられたとしても2人目も保育園に入れられる保証はないのだから(優先度は上がる)、保育園に1人目を預けられたとしても不安は拭えない。

自分たちの遺伝子を残したいという目的であれば子供は1人で十分なのだ。

国がやるべきことは「囚人のジレンマ」の解消

個々が自分の利益を最大化しようとした結果、系全体にとって望ましくない結果になることをゲーム理論の用語で「囚人のジレンマ」と言う。

今のままでは個人が自己実現のために人生を最適化すると子供を産まない選択肢をとるようになり少子化が進行する。これは国家にとって望ましい結果ではないと思う。

そうならないように、1億総活躍の自己実現を求めて国民が個人の人生の最適化に走る時に、国家として利益を最大化できるような仕組みを作ることが政治家の仕事であると思う。保育園を作らない理由をたらたら述べるだけの人はいらない。

もし出生率が増えることが国家として望ましい結果なのであれば、子供を産めば産むほどお得になる制度を整備しないといけない。例えば環境問題のために「エコカー減税」でやったように。出生で言うなら政策ではないが一企業であるソフトバンクがやっているように。

僕は保育園問題の専門家ではないので、保育士の給料がどうとか騒音問題がどうとか独身税の是非とか不妊の人への配慮といった個々の戦術について提案・議論するだけの知識を持ち合わせていないので、そのへんの議論は専門家にお任せするが、日本国家の戦略としてこの方向性は間違っていないと思う。

保育園が増やせないなら移民を受け入れることになる

3人産まないといけないのに産む気のある人が1人で産むのを止めてしまう社会になると当然人口は減少の一途をたどる。そうすると経済を支える生産年齢層がむちゃくちゃ減る

そうすると年金システムが破綻するのは容易に想像がつくだろう。

そうならないよう生産年齢層を増やすためには外国から人を招かざるを得ない。つまり移民の受け入れだ。

日本の風土や文化を鑑みると、移民受け入れは合わないと思う。受け入れたとしても慣習や文化の違いから様々な場所で軋轢を生み、日本人のストレスはマッハだろう。すでにそのようなことが起きていることは度々マスメディアで取り上げられていると思う。今後それが加速すると、今は関係ないと思っている人も巻き込まれることになるだろう。

保育はもはや福祉ではない

生存のためには必須ではないが最低限の幸福の実現のために行われるのが福祉であるならば、もはや保育は福祉だと思ってはいけない。シラク三原則のブコメでも指摘があるように国家の義務として保育を扱い、家族構成や両親の生き方や経済状況に関係なく産まれてくる全ての子供を育て上げる仕組みを作れないのであればこの国に未来はない。

保育園問題は20年先の種まきだ

当たり前の話だが生まれたての赤ん坊が成人するまで20年かかる。

つまりこの保育園問題は20年先への投資であることを理解して欲しい。

逆に言うならば、問題が生じてから対応しようとしても効果が現れるまでに20年かかるということだ。問題に気づいた時には既に手遅れになっている可能性が高い。

そうならないためにも今このタイミングで何かしらの対策は講じなければならない。

もしかしたらもう遅いのかもしれないけどね。

誰が声を上げるのか?

で、この問題に誰が声を上げるのだろうか?

保育園に受かった親たちは通知が届くまでは皆同じ気持で元ネタの記事を読んでいたに違いないと思う。1つ歯車が狂っていたら自分も日本死ねという側になっていたのだ。

でも保育園に受かったら受かったで今度はそっちに集中しなきゃいけなくなる。彼らとしては政治活動をする余裕があるなら子供にリソースをつぎ込みたいのだ。送り迎えがあるから早く起きて家をでなければならない。デモする余裕があるなら少しでも睡眠時間に充てて体力の回復に努めたいというのが本音だろう。

そうなると声を上げるのは落ちてしまった親たちになる。彼らも3歳児クラスで保育園に預けられるようになるかもしれないし、小学校に入るようになったら過去の話になってしまう(今度は学童問題が待っているんだけどね)。

すでに指摘があるように継続してこの保育園問題を政治に訴える人はいないのだ。

保育園問題は国家の生存戦略の問題です

保育園に預けられる預けられないとかで困ってる人が声を上げるべき問題では無いんです。

国家として次の世代をどう繋いでいくかという問題だと思うんです。

家族構成や両親の生き方や経済状況に関係なく、産まれてくる全ての子供を育て上げられる仕組みを国家として作り上げて、全ての夫婦が安心して子供を産めるようにして下さい。

政治家の皆様、そこんとこよろしく。

FAQ

Q. 家から働けばよくね?

A. 子供の面倒見ながら仕事とか無理

Q. 保育園じゃなくて親に預ければよくね?

A. 自己実現のために親の自己実現を犠牲にするとか鬼畜。子育て終わったと思ったら孫育てとか地獄っしょ。

あとは思いついたら足していく

コンテンツプロバイダこそがIPv6に対応しておいた方がいい1つの理由

要約:NATによるIPv4アドレス枯渇対策には限界があり、Webサービスのパフォーマンスの低下を引き起こす可能性があるので、自衛のためにもIPv6に対応しておいたほうが良い

コンテンツプロバイダからするとIPv6に対応してもコストがかかるだけで売上は上がらず、対応する意味のないもので、私もそのように感じていたのですが、色々なカンファレンスネットワークでCGNやIPv4-IPv6移行・共存技術の検証を行っていた結果、ISP側でできる努力の限界が見えてきて、コンテンツプロバイダがさっさとIPv6に対応したほうがインターネット的にもコンテンツプロバイダ自身も幸せになるんじゃないかなと、いちネットワークエンジニア&ソフトウェアエンジニアとして感じたのでこの記事を書きました。

IPv4アドレスの枯渇

IPv4アドレスは枯渇しました。2015年12月時点で各RIR(地域インターネットレジストリ)の/8のアドレス保有数は次のとおりです(IPv4枯渇時計より)。

地域 RIR 残り/8数
アフリカ AfriNIC 2.14
アジア・パシフィック APNIC 0.64
北米・中米 ARIN 0
南米 LACNIC 0.4
欧州・中東 RIPE NCC 1.01

日本を管轄しているAPNICでも既に最後の/8に手を付けており、IPv4アドレスの通常割り振りは終了しています。現在では最大でも/22(1024アドレス)の割り振りしか行われません。

NATによるIPv4アドレス枯渇対策の仕組み

おもに一般家庭向けISPの視点で話を進めます。

NAT(正確にはNAPTですがこの記事ではNATとします)を行うことでIPv4アドレスを節約することができます。通常のISPのユーザだと宅内ではプライベートIPv4アドレスを利用しており、ルータで割り当てられた1つのグローバルIPv4アドレスにNATすることでインターネットへアクセスすることができます。

通常は通信ホストの識別を32-bitのIPv4アドレスだけで行うのですが、NATではこれに加えて16-bitのTCP/UDPのポート番号を使用することで、同じIPv4アドレスを持っていても通信ホストの識別を行うことを可能にしています。これにより通信の識別に使えるbit数はAddress 32bit + Port 16bit の 48bit になります。この考え方はA+Pと呼ばれます。

f:id:yuyarin:20151211002201p:plain:w480

しかしながらアドレス枯渇により各家庭にそれぞれグローバルIPv4アドレスを割り当てることも難しくなってきました。そこで登場するのがCGN(NAT444), DS-Lite, MAP-EなどのIPv4枯渇対策技術です。

これらの技術の詳細は私の過去の講演資料をご覧頂くとして、共通する基本的なコンセプトは 複数のご家庭をまとめて1つのIPv4アドレスにNATする」 ということです。つまり同じグローバルIPv4アドレスを複数の家庭で共有することになります(ここでは「家庭」を加入者=subscriberの意味で使用しています)。特にCGN(Career Grade NAT)では各家庭のルータでNATした後に、ISPでNATをする多段NATになります。

f:id:yuyarin:20151211002204p:plain:w480

これらの技術は全てNATを利用しています。つまり 各家庭の識別子にポート番号を利用します。この時ISPは各家庭に何ポート割り当てるかを設計する必要があります。仮に256ポートとしましょう。すると1つのグローバルIPv4アドレスに対して65536/256 = 256の家庭を収容することができます。このようにすることでISPはアドレスの利用効率を256倍にすることができます。

これ以降の文脈ではNATは主にISPによって提供され複数の家庭を1つのIPv4グローバルアドレスに収容するNAT(狭義のCGN, DS-Lite, MAP-E, NAT64等の技術のNAT機能)を指すことにします。

NATの2つのパラーメータにまつわる問題

ポート割当数

さて、ここで問題になるのはISPは1家庭あたり何ポートを割り当てるべきかということです。総務省のレポートVol.1, Vol.2によると端末当たり平均50ポート、最大500ポートぐらいのポート消費があるため、設計値としては1000ポートを推奨と書かれています。この推奨値に従って1家庭あたり1024ポートを割り当てた場合、アドレスの利用効率は65536/1024 = 64倍になります。この値は大きいでしょうか?小さいでしょうか?

また、家庭という単位も問題になります。一人暮らしの家庭とビッグダディみたいな方の家庭では同じ1回線でも接続する端末の数が異なります。端末が多いご家庭では1024ポートの割当でも足りないかもしれません。

ポートが足りなくなるとどのようになるかというと、少し古い資料ですがNTT Comの宮川さんの発表資料Google Mapsが穴あきになってしまう例が示されています。

ポート割当数を多くすれば通信不能になるセッションを減らすことはできますし、ISPとしても通信の劣化に繋がるようなことはしたくありませんから、なるべくポート数を多く割り当てたいです。しかし、もともとアドレスが足りなくてこのようなことをやっているので、あまり大きな値を設定することはできませんし、アドレスが足りなくなってくるとやむを得ずポート割当数を減らす事になるかもしれません。そうなった時には 空いているポートがなくて通信ができないセッションが発生するかもしれません。

まとめると、 ポート割当数を多くすればアドレスの利用効率が落ちてアドレス枯渇が早くなり、少なくすれば通信できないセッションの発生確率が上がるというトレードオフが存在します。

タイムアウト

Skypeや対戦ゲームなどのP2P通信を可能にするためには通信が終わった後も一定時間NATのセッションテーブルを持ち続けなければなりません。これをどれぐらいの時間保持するのかがタイムアウト値です。

タイムアウト値を大きくすればポートの利用効率が悪くなり、早くポート割当数の上限に達してしまい、通信できないセッションが発生する可能性が高くなります。短くすればポートを効率よく使うことができますが、P2P通信に失敗する可能性がありますし、その他にも次のような弊害があります。

Keepaliveをしているアプリケーションが利用できなくなる:制御用のセッションを張りっぱなしにして一定の間隔でKeepaliveを送るようなアプリがあります。例えばGoogle Hangoutは15分間隔でKeepaliveを送ります。このため、TCPタイムアウトが15分以下になっているとこのアプリケーションは利用できないということになります。これを回避するためには十分長いタイムアウト値を設定する必要があります。他にもAndroidiPhoneなどモバイル端末のKeepaliveもあります。一部のNAT機器にはポート番号ごとにタイムアウト値を設定できるものもありますが、全てのISPが世の中に存在する全てのアプリケーションに対してカスタマイズされた十分なタイムアウト値を設定することは難しいでしょう。

NAT機器でのポートの再利用周期とサーバ側のタイムアウト値の関係:NAT機器で空いているポートをどのように使用していくかは実装依存です。TCPのFINが来ていないポートの再利用禁止期間はRFC6888で2分間と決められていますが、FINが来て一度開放されたポートがすぐに再利用されてしまって、しかも同じ宛先サーバの同じポートに通信しようとした場合、 サーバ側のソケットがまだ閉じていなくて通信できない ということが起きる可能性があります。ISP側とサーバ側とのタイムアウト設定値の相性問題ですので、これを回避するためには世の中の全てのサーバに対して十分だと思われる時間ポートの再利用を行わないようにする必要があります。しかしあるISPが全てのコンテンツのサーバに対して問題が起きない十分なタイムアウト値を設定することは不可能ですし、またコンテンツプロバイダもNATを使用する全てのISPに対して正常に動作するサービスを提供することも難しいでしょう。

NATの限界

全てのアプリケーションを快適に動作させるにはNATのタイムアウト値を長く設定する必要があります。しかしそうするとポートの消費効率が悪くなるため、1家庭当たりたくさんのポート数を割り当てる必要が出てきます。たくさんのポート数を割り当てると1グローバルIPv4アドレスに収容できる家庭の数が減ってきます。

逆に言うと収容する端末が増えてきたにもかかわらず、IPv4アドレスがこれ以上入手不可能になった場合には、ISPは1家庭あたりのポート割当数を減らしたりタイムアウト値を短くしたりする必要に迫られるわけです。その分サービス品質は低下しますが、IPv4を生かすことに迫られている間は他に選択肢はありません。

この他にも、NATを通した時に特定のWebサイトに対してうまく通信ができずパフォーマンスに問題が出るような事例が見つかっていますが、未だ原因を解明できていません。特に1画面に多量に画像などを表示するサイトでは問題が出る可能性が高いです。

このような状況が発生したに我々が提供するWebサービスは快適に利用できるでしょうか?

もしそのようなISPのユーザに対しても 自分のWebサービスだけに対しては高いパフォーマンスでアクセスできるようにしたいのであれば、IPv4でアクセスさせることを避けるべきです。

IPv6に対応する

ここまで読んでいただいた皆様には なぜGoogleFacebookApple、それにCDN事業者がIPv6対応に本気を出しているのかがお分かりいただけるかと思います。

我々が提供しているWebサービスで画面の読み込みが遅くなったりできなくなったりして、ユーザから遅くて使えないサービスだと思われるのが問題になったり、そのパフォーマンス低下が損益に直結したりするような場合であれば、 ISPのNATに頼るのではなくコンテンツプロバイダ側で根本的な対策が必要になるでしょう。その 自衛方法の1つがIPv6対応になるでしょう。

特に中国、インド、東南アジアなどの地域でサービス提供を目指すコンテンツプロバイダにはIPv4の枯渇は死活問題になります。JANOG35で発表された人口あたりのIPv4アドレス数を考えると日本が一人あたり1.5アドレス以上あるのに対して、中国では0.24、インドでは0.02です。こういった国々に対してサービスを提供したい場合にはこのIPv4アドレス枯渇問題に対して真剣に取り組む必要があります。これらの国々ではIPv6のインターネットが主流になる可能性が十分にあります。

AppleはNAT64環境下で動作しないアプリはAppStoreで承認しないという方針を発表しました。これ自体はAppleがMVNOをIPv6+NAT64で提供しようと考えているからだと推測されますが、これに対応するだけでは不十分です。なぜならNAT64もIPv4へのNATを行う技術ですので、これまで述べてきた問題が同様に発生します。今年10月に総務省から発表された携帯キャリア3社の「モバイルネットワークのIPv6推進に向けた取り組み」も、おそらくこのIPv4 NATの限界問題を認識してのことではないかと思います。

IPv6に一気に対応するコストを払うのはコンテンツプロバイダにとって多大な負担になるので、今のうちから徐々にIPv6に対応することをオススメいたします。単純にフロントエンドのサーバにIPv6の到達性を持たせればいいわけではないですし、自前でネットワークを持っている規模のコンテンツプロバイダであればネットワーク機器もIPv6に対応したものにする必要があり、その検証から導入に時間もコストもかかります。もしあなたが今からサービスを立ち上げるのであればIPv6に対応したクラウドで、IPv4に依存しないアプリケーション設計を行うことをオススメいたします。

別の方向のアプローチとしてHTTP2に対応するという方法もあります。HTTP2では1つのTCPセッションの上で複数のHTTPセッションを通信させる機能が提供されています。Google, FacebookはSPDY/HTTP2に対応しています。HTTP2では1サイトに対して基本的には1ポートしか使わないのでポート数を節約することができ、NATの負荷を軽減することができます。ただし他のWebサービスが多数のポートを使った結果ポート枯渇が発生して、自分のWebサービスHTTP2のセッション自体が張れないという可能性は残ります。

まとめ

しばらくの間はNATによりIPv4インターネットは生き続けますが、NATにも限界があります。その限界に達した時に我々のWebサービスのパフォーマンスが低下しているように見えるユーザが現れ、サービスが重いと思われるリスクがあります。これに対してIPv6に対応することは効果的な対策になるでしょう。問題が起きる前に早いうちからIPv6に対応するためのコストを積み立てておいたほうが良いででしょう。

以上がコンテンツプロバイダこそがIPv6に対応しておいたほうがいい理由です。

運用自動化のためにネットワーク機器に求めるもの

f:id:yuyarin:20151130235731p:plain

12月1日のNetOpsCoding Advent Calender 2015の記事です。

NetOpsCodingは、ネットワークインフラにおける運用を自動化した事例や システム開発の知見、有用なツールなどを共有する勉強会です。2015年10月30日に第一回目の勉強会「NetOpsCoding #1」を開催しました。

運用自動化のためにネットワーク機器に求めるもの

ネットワーク運用を自動化しなければならないということは、もはや言うまでもないことだと思います。自分たちのネットワークサービスをソフトウェア制御で提供しようと思った場合に大きな壁になるのが、ネットワーク機器を外部のソフトウェアから制御する方法です。

ネットワーク機器の制御には大きく分けて次のような項目があるかと思います。

  • 情報取得 (show系コマンド)
  • 設定投入 (conf t)
  • アラート (syslog, SNMP trap)

これらの制御を外部のソフトウェアから行おうと思った場合に、どういう機能がネットワーク機器にあれば嬉しいのかを、ネットワーク運用者かつ自動化ソフトウェア開発者の立場から書いてみたいと思います。

運用自動化を目指すネットワーク運用者の目的は綺麗なコードを書くことではありません。一番大事なものはお客さまや自分たちのコンテンツのトラフィックです。そのため自動化にあたっては多少コードが汚くなったとしても 安全なソフトウェアを書くことが第一 です。

ちなみにここで言うネットワーク機器というのはCisco, Juniper, Brocade, Huawei, Alcatelなどのキャリア・DC向けのルータやスイッチを中心に考えていますが、あまり標準化が進んでいないPXCやWDMなどの機器も念頭に置いています。

全機能にアクセスできる統一的手段

APIとかNETCONFやSNMPとか対応していても、サービスに必要な全ての機能にアクセスできなければ意味がありません。 結局はtelnet/sshしてCLIから操作する羽目になりNet::Telnet最強説の暗黒面に足を踏み出すことになります。

例えば、インターフェイスの設定はNETCONFでできるけど、リンクアグリゲーションの設定はNETCONFだとできない。インターフェイスのUp/DownはSNMPで取得できるけど、LACP BlockedのステートはSNMPで取得できない。こういった状況にある場合は結局のところtelnet/sshしなくてはいけなくなります。

1つの種類の機器に対して複数の手段での操作を行う必要があると、それだけ検証の工数が倍増していくだけでなく、バグを踏むリスクが増えるので非常に辛いです。例えばSNMPでこのMIBを叩くとrebootするとかいうバグを見たことがある方は多いかと思います。逆に言えばメーカー間や機種間で共通したプロトコルやアクセス手段は必要ありません。実装が違うためどのみち検証工数はかかりますし、バグはそれぞれ存在するからです。

独自APIでも何でも構いません、その機器が提供している全ての機能に同じ方法でアクセスできる手段を提供してください。

さもなければ我々はtelnet/sshの闇から抜け出すことはできないでしょう。

コマンドの実行結果をJSON形式で必ず返す

残念ながらtelnetCLIからコマンドを実行する必要が出てきた場合に、次に困ることが実行結果の解釈です。ただしこの項目に関してはAPIも含めて全ての制御方式に当てはまることです。

show系コマンド

show系のコマンドを打った時にその結果をパースして必要な箇所を抜き出す必要があります。そしてこの表示フォーマットは人間には読みやすいですが、機械処理しようとすると非常に厄介なフォーマットになっている場合がほとんどです。また、バージョンによって表示フォーマットが変わっている場合、バージョンごとに応じたパーサを用意する必要があります。さらに、特殊な条件で表示結果が変わる場合、例えば「この機能を有効にしていたら表示にこの行が追加される」みたいなケースで、知らずに機能を有効化してしまってパーサが死ぬみたいなことが起きると悲惨です。

人間用の表示フォーマットと機械処理用のJSONフォーマットの2つの表示フォーマットに対応してください。

JUNOSでは show ... | display xmlXML形式で出力できるため非常にありがたいです。

config系コマンド

conf tしてからコンフィグ系のコマンドを実行した時、コマンドが成功た場合は結果は表示されず、コマンドが失敗した場合は何か文章が表示されることが多いかと思います。表示があったら失敗、なかったら成功、と判定しようとした時に厄介なのがwarningです。たとえば「rebootが必要です」みたいな警告が表示されるコマンドがあります。この場合設定の投入は成功していますが、表示はあります。こうしたものをいちいち場合分けして判断して対応する労力は大変です。

成功・失敗などのコマンド実行結果や、付随するwarningなどが構造化されたデータとして必ず返ってくるようにしてください。

階層移動系コマンド

Cisco系のCLIで設定する場合はinterface ...など階層を移動するようなコマンドを打つ必要があります。自動設定の時に怖いのが、例えば「誤ったインターフェイスに入ってshutdownしてしまう」といった階層移動ミスです。

GigabitEthernet 1/10 に入るつもりが、コマンドを流しこんだ際に最後の 0 がバッファ溢れで落ちて奇跡的に GigabitEthernet 1/1 に入ってしまって、そこでshutdownを打ってしまうと大惨事です。

1行程度のコマンドであればこのようなことは起きないとは思いますが、大量のコマンドを一度に流し込んだ場合にバッファが溢れる機器は実際にあるため、実装する側としては流し込まんだコマンドでバッファ溢れが起きていないか確認する必要があります。

階層移動のコマンドを打った場合も必ずその結果に移動先の階層の情報を含めて返して、対象の階層に確実に移動できたかどうかを確認できるようにしてください。

本当はJUNOSのset文のように階層はあるけれども階層に入らずに設定が行えるような仕組みになっていると嬉しいです。ターミナルのプロンプトに入ったインターフェイス名が表示される機器だとまだマシです。

トランザクション

先ほどの話は設定の際も安全性のために1つ1つコマンドを入れて結果を確認するという話でした。でもそれをやると時間がかかってしまうので、できれば設定をひとかたまりで投入できると嬉しいです。

このように複数の設定を同時に設定しようとした場合に怖いのが、途中のコマンドで失敗する場合です。失敗したコマンドの前に投入したコマンドが残っていても大丈夫な場合もありますが、できれば消しておきたいです。

ひとかたまりの設定を投入した時に途中で失敗したら、そもそも設定を投入する前の状態にロールバックできるような、トランザクションの機能を実装してください。

JUNOSやIOS-XRのcommitの機能がこれにあたります。IOSIOSライクな機器ではできません。

おかしなコンフィグを通さずにエラーで返す

なるべくそのようなことは起きないように設計・実装するのですが、例えば「存在しないaccess-listを設定する」といったことが起きてしまった時に大惨事にならないようになっていると嬉しいです。この例の場合、機器の挙動は「全てpermit」「全て暗黙のdeny」などメーカによって異なりますが、自動設定の観点からはエラーを返すという挙動になっていると嬉しいです。

グラマーが正しくてもセマンティクスがおかしい設定が投入された時にはエラーを返すようにしてください。

コンフィグをテキストで渡してload

ネットワーク運用自動化の初期の段階として、データベースとテンプレートを用いてコンフィグを全文生成するという段階があります。このコンフィグ全文から1つ前の全文との差分を作成し、適切に機器に投入する機能を実装することは非常に難しいです。このような場合にコンフィグ全文を送って今の状態との差分をネットワーク機器側で適切に抽出して適用してくれると嬉しいです。

コンフィグ全文をファイルで送ってreloadすることで差分を適切に適用する機能を実装してください。

JUNOSではload overrideでできます。

またコンフィグの投入方式として、コマンドをscpなどでファイル転送して、ファイルから読み込みできるようにすると、telnetでのバッファ溢れなどの悲劇が防げるので嬉しいです。

投入するコマンドをファイルで送ってloadすることで設定投入できる機能を実装してください。

JUNOSではload setでできます。

デスクリプションの変更が他の設定に影響を与えないようにする

自動化を可能にするためにdescriptionの命名規則を制定して、descriptionを書き直す作業が発生することがあります。たとえばLAGのインターフェイス名にdescriptionが含まれているような機器の場合、descriptionを変更するためには一度LAGを解体する必要がある、つまりトラフィック影響があるわけです。本来description変更はトラフィック影響が無い作業であるべきですので、そのようになっていると嬉しいです。もしくはそのようになっていたとしても、名前部分のreplaceが効いたり、atomicにLAGの解体・構築ができてトラフィックに影響がないような仕組みになっていれば十分です。

description変更がトラフィックに影響を与えないようにコンフィグを設計してください。

すべてのものに一意の型番をつける

モジュール型のネットワーク機器のラインカードモジュールやファブリックモジュール、ひいてはファンモジュールや電源モジュールなど、オブジェクトとして存在するものに対しては一意の型番を付与してください。メーカ側でそれらが決まっている場合、非常に楽になります。また、それらの型番と同じ文字列がshow系のコマンドで表示されるようにしてください。ただ、型番に括弧()を入れたりするのはやめてください…

ネットワーク機器を構成するすべてのものに一意の型番を付けてください。

ある機器のあるモジュールではシャーシサイズによって別のモジュールになっているにもかかわらず、機器の内部では同じ型番で扱われていたりします。こうなるとデータベース上でのモジュールの識別子を、メーカが決めた型番や機器内部で扱われている文字列とは別のものにする必要が発生します。こういったモジュールが1つ存在するだけで、全てのモジュールに対して「識別用の型番」と「設定用の型番」の2つの型番を管理する必要が生じます。これは避けたいです。

違うものに同じ型番を絶対につけないでください。

機械処理可能で十分な情報が含まれたプッシュ型通知

Link Down/UpやBGP Up/Downなどのイベントに自動で対応したり、集計・蓄積しようとしようと思うと、プッシュ型通知の機能が必要になります。この機能はsyslogやSNMP Trapとして存在します。

ネットワーク運用自動化の際にこれらのプッシュ型通知機能に求められるのは次の要件です。

  • データが構造化されていること
  • 対象を一意に特定するための情報が含まれていること

データが構造化された通知

syslogは人間に読みやすい文字列です。フォーマットがメーカでバラバラですし、同じ機器でもバージョンやログの種別によってフォーマットがバラバラになっています。syslogから情報を得るためには正規表現でマッチングすることが多いかと思います。この時に正規表現で上手くマッチングできないようなログがあると非常に厄介になります。JSONなどのデータ構造で送ることができれば機械処理は非常にやりやすくなります。

特定の宛先にはJSONフォーマットで送ることができるような機能があると嬉しいです。

Dec 01 00:00:00 INFO router01 GigabitEthernet0/1 Link Down (remote fault)

といったsyslogは人間には見やすいかもしれませんが、機械処理するのは面倒です。次のようなJSONフォーマットで出力できると非常に楽です。

{"hostname":"router01","hostaddr":"10.20.30.40","datetime":"2015-12-01 00:00:00 +0900","priorty":"info","event":"Link Down","detail":{"ifIndex":"1","ifDescr":"GigabitEthernet0/1","description":"AS65500","reason":"Remote Fault"}}

これはあくまでも例で、共通部分に関しては色々なメーカの機器でスキーマを揃えたほうが良いので、標準化してみたいと思います。

通知をJSONフォーマットで送ることができるような機能を実装してください。そして全てのアラートのスキーマを公開してください

SNMP Trapは構造化された通知です。MIBによりスキーマが定義されています。しかしバイナリフォーマットでありスキーマの解釈にはMIBファイルの読み込みが必要なので、スクリプトから扱うには敷居が高く、結局snmptrapdを使ってsyslogに変換して扱う状況が見られます。私はSNMP Trapをまだ上手く扱えていないので、SNMP Trapの可能性に言及することはできませんが、上手く使えば良い通知になるのではないでしょうか。

WDMなどの製品ではsyslogには対応しておらず、SNMP Trapのみに対応しているという場合が多いです。

対象を一意に特定するための情報が含まれた通知

アラートを使って何かを行いたいと思うと、その対象を一意に特定する情報が必要です。この時に単にID的な要素を含むよりかは、ユーザが決めることができるdescriptionなどの要素が含まれていると非常に運用が楽になります。

さきほどのsyslogの例だと

Dec 01 00:00:00 INFO router01 GigabitEthernet0/1 (AS65500) Link Down (remote fault)

このようにdescriptionが入っていると、desciptionを使った分類や、イベントのトリガーが可能になります。そうでなければインターフェイス名やifIndex値からdescriptionなどを逆引きする仕組みを実装する必要に迫られます。例えばインターフェイスが変わったとしても、AS65500のためにアラートを蓄積するシステムの構築が容易になります。この例だとGigabitEthernet0/1で対象を一意に特定できますが、ifIndexのほうが一意性は高い場合もあります。また、コンフィグを行う際に使用するインターフェイス名と、ifDescrの内容が違う場合もあります。この場合syslogに記載されている内容が役に立たない場合もあります。SNMP TrapでifIndexしか含まれていないものがあり、それを人間が識別可能な文字列に直すために16進数演算を含む複雑な処理をようするようなものもあります。

通知には対象が容易に特定可能になる情報を含めてください。通知には1.一意のID、2.人間にとって可読性の高い識別子、3.ユーザ定義のdescriptionを含めさせてください

ちなみにUDPだとパケット長問題やパケロス問題が出てくるので、TCPにせざるを得ないかなぁと感じています。そういう点からも TCPでsyslogを送ることができる という機能もあると嬉しいです。できないネットワーク機器はそれなりに多いです。

API Firstな設計

なんだかんだいって、REST APIが欲しいです。でも人間のオペレーションにはネットワーク機器のCLIは最高です。Linuxサーバで?を押してがっかりしたことが何度合ったか。WDM機器に多く見られるNMSもネイティブGUIやWebGUIで良く出来ているものがあります。しかしそれらのCLIGUIインターフェイスは全てAPIのラッパーになっていて、そのAPIはユーザに対しても開かれている方が嬉しいです。そうすることで運用者が独自に運用に必要なシステムを作ることが可能になります。

全ての機能をAPIとして実装して、そのラッパーとしてCLIGUIを提供してください。そしてそのAPIは運用者が利用できるようにしてください

まとめ

かなり長くなってしまいましたが、ネットワーク運用者視点で自動化のためにどのような機能があったら嬉しいかをまとめてみました。途中特定のメーカの賛美に思われる部分もありましたが、先進的な機能を実装しているので、やはり紹介しないわけにはいきません。

この記事が、少しでもネットワーク機器を開発されるメーカの皆様の助けになり、自動化しやすいネットワーク機器がたくさん世に送られることを願います。