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つの種類の機器に対して複数の手段での操作を行う必要があるとそれだけ検証の工数が倍増していくため非常に辛くなります。逆に言えば全てのルータメーカーで共通したプロトコルやアクセス手段は必要ありません。
独自APIでも何でも構いません、その機器が提供している全ての機能に同じ方法でアクセスできる手段を提供してください。
コマンドの実行結果をJSON形式で必ず返す
残念ながらtelnetでCLIからコマンドを実行する必要が出てきた場合に、次に困ることが実行結果の解釈です。ただしこの項目に関してはAPIも含めて全ての制御方式に当てはまることです。
show系コマンド
show系のコマンドを打った時にその結果をパースして必要な箇所を抜き出す必要があります。そしてこの表示フォーマットは人間には読みやすいですが、機械処理しようとすると非常に厄介なフォーマットになっている場合がホトんです。また、バージョンによって表示フォーマットが変わっている場合、バージョンごとに応じたパーサを用意する必要があります。さらに、特殊な条件で表示結果が変わる場合、例えば「この機能を有効にしていたら表示にこの行が追加される」みたいなケースで、知らずに機能を有効化してしまってパーサが死ぬみたいなことが起きると悲惨です。
人間用の表示フォーマットと機械処理用のJSONフォーマットの2つの表示フォーマットに対応してください。
JUNOSでは show ... | display xml
でXML形式で出力できるため非常にありがたいです。
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の機能がこれにあたります。IOSやIOSライクな機器ではできません。
おかしなコンフィグを通さずにエラーで返す
なるべくそのようなことは起きないように設計・実装するのですが、例えば「存在しない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系のコマンドで表示されるようにしてください。ただ、型番に括弧()を入れたりするのはやめてください…
ネットワーク機器を構成するすべてのものに一意の型番を付けてください。
ある機器のあるモジュールではシャーシサイズによって別のモジュールになっているにもかかわらず、機器の内部では同じ型番で扱われていたりします。こうなるとデータベース上でのモジュールの識別子を、メーカが決めた型番や機器内部で扱われている文字列とは別のものにする必要が発生します。これは避けたいです。
違うものに同じ型番を絶対につけないでください。
機械処理可能で十分な情報が含まれたプッシュ型通知
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にせざるを得ないかなぁと感じています。
API Firstな設計
なんだかんだいって、REST APIが欲しいです。でも人間のオペレーションにはネットワーク機器のCLIは最高です。Linuxサーバで?を押してがっかりしたことが何度合ったか。WDM機器に多く見られるNMSもネイティブGUIやWebGUIで良く出来ているものがあります。しかしそれらのCLIやGUIのインターフェイスは全てAPIのラッパーであり、APIはユーザに対しても開かれている方が嬉しいです。
全ての機能をAPIとして実装して、そのラッパーとしてCLIやGUIを提供してください。そしてそのAPIは運用者が利用できるようにしてください
まとめ
かなり長くなってしまいましたが、ネットワーク運用者視点で自動化のためにどのような機能があったら嬉しいかをまとめてみました。途中特定のメーカの賛美に思われる部分もありましたが、先進的な機能を実装しているので、やはり紹介しないわけには行きません。
この記事が、少しでもネットワーク機器を開発されるメーカの皆様の助けになり、自動化しやすいネットワーク機器がたくさん世に送られることを願います。