Contrail: Linux as an MPLS router

Contrail (http://www.opencontrail.org/) の日本語情報が少ない気がするので、ブログにまとめておく。

Contrail を一言でいうと、
 LinuxをMPLSルーター化するもの(GRE経由フルメッシュ)
で、主な用途としては、
 IPファブリックの下の何か(現実的にはOpenstackかKubernetes)、にセグメントの概念を提供する
だと思っている。

もう少し詳しく言うと、
元々サーバー数が増えてきて、IPファブリック (例としてはこちらを参考: https://www.janog.gr.jp/meeting/janog38/program/clos.html) を導入した際、ラック間で同じセグメントを共有することが出来ない、という制限が発生するようになった。
これは、アプリケーションによっては問題にならないものの、現実的には、'セグメントごとのアクセスポリシーを定めたい'、などの理由から、やはり同じ種類のサーバーは同じセグメントにまとめたい、という際に問題になる。

対応としては、VLANのような、セグメントを限定する情報を、IP上でencapsulateする方法が必要となる。

一つの方法としては、VXLANを使う方法がある。(VTEPは、スイッチ/サーバー、どちらもあり得る。)
スイッチの場合:
http://www.networkers.fi/blog/juniper-qfx-ip-fabric-and-vxlan-part-2/
サーバーの場合:
http://docs.openvswitch.org/en/latest/faq/vxlan/

もうひとつの方法として、MPLSを使った方法がある。こちらは、元々MPLSが使われていることが多い、SPネットワークでよく使われる。
https://techblog.yahoo.co.jp/infrastructure/evpn/

Contrailは後者の方法を採用したもので、kernelモジュール 'vrouter' を使い、出ていくトラフィックにMPLS情報を付与して、行き先のハイパーバイザに送る、という仕組みである。(行き先のハイパーバイザの情報は、コントローラー経由で各ノードに共有しておく)
構成図はこちら。
http://www.opencontrail.org/wp-content/uploads/2014/10/Figure01.png
http://www.opencontrail.org/opencontrail-architecture-documentation/

メリットとしては、デフォルトのopenstack ml2プラグインや、kubernetes と比べた場合はかなり明確で、
 l3で行き先を決めるので、ネットワークノード(スケールアウト不可)を持つ必要がない(ml2プラグインと比べて)
 マルチテナントが使用可能(kubernetesデフォルトと比べて)
となる。

kubernetes integrationの例はこちら。
https://www.mirantis.com/blog/kubernetes-openstack-multi-cloud-networking/

vlan+vxlan の構成に比べたメリットは、、vlan数の限界(4096)の影響を受けない、等はあげてもよいだろうか(環境によっては、アクセスポリシーの一括管理、等も挙げられるかもしれないが、execjson+applconnでも何とか出来ないことはない)

とはいえ、総じて、大規模なIPファブリックを組む環境であれば、一度検討してみてもよいのではなかろうか。

更に詳しく知りたい場合は、こちら
https://learningportal.juniper.net/juniper/user_activity_info.aspx?id=9687
https://learningportal.juniper.net/juniper/user_activity_info.aspx?id=9897

構成情報のJSON化

applconn を公開したので、そのまとめ。
https://github.com/aaabbb200909/applconn

applconn はexecjson の裏で使うロジックで、大雑把に言うと

  • 構成情報をJSON化する

ためのロジックとなっている。

execjson でロジックを書こうとすると、関連サーバーの構成情報を参照しないといけない部分が大量に出てくる。
※ 例えば、ユーザーがシステム名だけを指定してくるSRで処理を流す際に、あるシステムがどの ロードバランサー/アプリサーバー/DBサーバーで動いているかを調べる、など

これは、作業時に全サーバーの情報を確認しないと出てこないが、いくらfabricがあるとはいえ、毎回それをやるのも効率がよくないので、
事前にrsync+git で集めてきた情報を解析して、networkx (directed graph を使用, 直接、接続に関わらない部分は attibuteとして保管) に取り込み、結果をJSONとしてエクスポートするようにしている。
これによって、各筐体の構成情報が、プログラムから使える状態になる。

※ networkxについてはこちら
https://networkx.github.io/

rsync+git についてはこちら
http://aaabbb-200904.hatenablog.jp/entry/20120825/1345908313


合わせて、JSONをそのままelasticsearch に入れておくと、Kibanaからの検索も容易になる。
※ こちらも参照
http://qiita.com/sawanoboly/items/a8f9357f0f6044e7d7ff

また、networkx に入れている関係で、dfs_tree によって、特定のノードからつながっているノードを洗い出したり、
all_shortest_path によって 最短経路を洗い出したりすることも出来る。
前者は

の取得に使用出来る。
また、all_shortest_path は 例えば、

の取得に利用できる、ものと思われる。

また、networkx から graphviz 形式でエクスポート出来るので、上記の情報を可視化することも出来る。
※ 特に dfs_tree の情報


取得する情報としては、例えば以下のようなものが考えられる。

  • ロードバランサーの振り先 (ノード: クラスタIP, ロードバランサー, エッジ: クラスタIP -> ロードバランサー -> アプリサーバー)
  • アプリサーバーからのデータベース接続 (ノード: アプリサーバー, アプリケーション, エッジ: アプリケーションサーバー -> アプリケーション -> データベースサーバー)
  • ハイパーバイザ上のOS (ノード: ハイパーバイザ, エッジ: OS名 -> ハイパーバイザ)
  • ストレージのLUN (ノード: ストレージLUN名, ストレージ名, エッジ: ハイパーバイザ -> ストレージLUN名 -> ストレージ名)
  • IPセグメント (ノード: セグメント, ルーター名, エッジ: ルーター(コア) -> セグメント -> ルーター(下位))
  • LLDP情報 (ノード: スイッチ名, スイッチポート名, エッジ: スイッチ名(コア) -> ポート名(コアスイッチ) -> ポート名(アグリゲーション) -> スイッチ名(アグリゲーション) -> ... -> サーバー名)
  • ansible のfacts, puppet の facter, chef の ohai など


当初、これらを全てRDBに入れることを考えたのだが、テーブル設計でいい案が思いつかず、また、dfs_tree 結果を可視化したかったこともあり、結局全てJSONで保管する、で落ち着いている
※ テーブル名としては "サーバー" 等として、行に "ホスト名", "IP", .. 等を を入れていこうとしたのだが、OSが"Windows", "Linux" 等で取りたい量が変わるので、Null の列が増えてしまう。(サーバーのロールによっても変わる)
このため、 "Windows" テーブル, "Linux" テーブル 等を設けて 外部キーで管理、等を行おうとしたものの、結局 グラフ化と検索にしか使わないので、JSONでいいか、、となって今に至っている


達成したかったこととしては、こんなところだろうか。

なお、上記の仕組みは、商用製品だと、機能の一部として持っていることが多いが、(vSphere, Zabbix 等は管理対象の接続関係を可視化出来る) 例えば、

  • あるストレージが停止した場合に、影響を受けるアプリケーション (ストレージ -> ハイパーバイザ -> 仮想OS -> アプリサーバー/DBサーバー -> アプリケーション の順で洗う必要あり)

等の情報は、製品をまたぐために、容易には洗い出せない。

こういったギャップを埋めるために、(少なくとももうしばらくは、) applconn のような仕組みが、必要になってくるものと思われる。

JSON POSTで連携ジョブを実行

先ほどのコミットにより、execjson にJSONをPOSTすることでジョブ実行を開始できるようになった。
github.com


例えば、監視アラート後の自動ジョブによって、

  • サーバーの切り離し -> httpd の停止 -> httpd の起動 -> サーバーの組み入れ

等を実施したい状況で、

  • ロードバランサーの操作(haproxyctl operation=[up|down] servername=サーバー名)
  • httpd の停止・起動(httpdctl operation operation=[start|stop] servername=サーバー名)

は既にジョブとしてある場合、以下のようなJSONを記述して、POSTすればよい。
※ サーバー名はアラートから取得する

$ cat 20161005-071015.json
{
 xxx:
  ..
 joblist:
 [
  {
   "name": "haproxyctl",
   "operation": "down",
   "servername": "サーバー名",
  },
  {
   "name": "httpdctl",
   "operation": "stop",
   "servername": "サーバー名",
  },
  {
   "name": "httpdctl",
   "operation": "start",
   "servername": "サーバー名",
  }
  {
   "name": "haproxyctl",
   "operation": "up",
   "servername": "サーバー名",
  }
 ]
}
$ curl -F putjson=@20161005-071015.json localhost:8000/app1/postjson

こんな感じで、fabricベースのジョブをJSON API していくことが出来る。
※ ブラウザからも実行出来るので、定型作業と監視アラート対応のロジックは共用出来る。

定型作業のジョブが増えていくと便利になっていくのではなかろうか、、

execjsonに新規ジョブを追加する方法

execjson に新規ジョブを追加する際の対応についてまとめる。
例としてLinuxのパスワードリセットを自動化する場合の変更点をまとめる。

1. HTMLへの追加
execjsonでは、SRのインプットとしてHTMLを使用する。
このため、template/app1/index.html で以下の変更を加えている。

1-1. テンプレートdiv要素の追加
+<div id="passwdresetostemplate" style="display:none">
+<div class="operation">
+パスワードリセット(OS):<b class=passwdresetosstart>[+]</b><input id="" type="text" name="name" value="passwdresetos" hidden>
+サーバー:<input id="" type="text" name="server">
+ユーザー名:<input id="" type="text" name="username">
+</div>
+</div>

入力は 1つのdiv要素となっており、SRの名前として、"passwdresetos" を使用している。SR名は、ここ以外に何度か出てくる値となっており、execjson 内で一意になっている必要がある。
また、div要素の idは "SR名+template"となっている必要がある。

div要素の中では、input 要素で、入力項目を指定する。主に、text, select 等を使用しているが、それ以外も使用できると思う。
※ ここの各input項目のname は、fabric の引数名として、直接渡される。


1-2. ジョブ追加用の select/option への追加
 operation:<select id="sortofop" name="sortofop" onchange="addjob()">
...
+<option value="passwdresetos">パスワードリセット(OS)</option>
...
 </select>

HTML後半の select/option の中に、今回追加するジョブを加える。optionのvalue は、SR名と一致させる必要がある。


2. django view (app1/views.py) への追加

2-1. (オプション)複数 operation への対応
+operationswithseveralops=['mkdir','filetransfer','editcron','editat',
+ 'passwdresetos'
+]

パスワードリセットは複数オペレーションを持つ作業のため、SR名を operationswithseveralops のリストに追加している。
※ ジョブとオペレーションの違いについては後述、、

2-2. セッション取り込みロジックへの反映
def consumeoperationargs(jobname, duprp):
...
 + elif (jobname=="passwdresetos"):
 +  return consume(duprp, ["server", "username"])
...

execjson 内では、フォームとして上がってきた値をセッションDBに追加するため、各ジョブごとに該当するフォームの項目をargs(JSONのobj) として取り込んでいる。このため、django のview側でも、HTMLで指定した値を持つ必要がある。値は、consumeoperationargs 内の分岐として指定している。

3. batch/do.py への反映

+operationswithseveralops=['mkdir','filetransfer','editcron','editat',
+ 'passwdresetos'
+]

fabric にコマンドを渡す際、実行サーバーを指定する必要があるが、この部分は、batch/do.py の xxx_parse という、一連の関数で制御している。passwdresetos は、"server" で指定された値をそのまま使うので、generic_parse という関数を使用しており、こちらは operationswithseveralops に追加することで、対象となる。
※ そうでない場合、xxx_parse を別途作成する必要がある(ファイル転送などで個別のxxx_parse を使用)

4. batch/fabfileへの反映

+def passwdresetos(username):
+ sudo("faillog -u %(username)s -r")

実際に実施するコマンドをfabfileに反映する。(今回は faillog -u xxx -r を発行するのみ)

追加作業は以上となる。プログラミング経験がさほど無い、サーバー担当者でも書けるように作ったつもりだが、どうだろうか、、

SRの受け方は、Excelやワードで書く、メールの本文にフリーフォーマットで書く、専用のWeb画面を書く、等があるかと思うが、それぞれコマンドの自動発行がしづらい、入力チェックが出来ない、構築・導入に時間がかかる、等のデメリットがある。


execjson では、これらのいいとこ取りが出来ているのではないか、と思っている。

 

追記: ジョブとオペレーションの違いについて

execjsonの中では ジョブとオペレーション、というくくりがあるが、ジョブ、は一つのSR項目、に対応しており、オペレーションは、ジョブの中で指定される項目の一つ、という扱いになる。

基本的にはオペレーションとジョブは1対1だが、一部のジョブでは、ジョブとオペレーションが1対多にしたかったため、これを指定できる仕組みを入れている。
※ パスワードリセットでは、一つのジョブで、複数のユーザーのパスワードをリセット出来るようにしたかった。(複数のジョブを指定すれば済むことではあるが、、)

ITILから見た際のexecjsonの位置づけ

ITILで扱うITサービスのうち、もっとも容易に 自動化できる部分は、"リクエスト対応" の部分だが、この部分を自動化しようとすると、構成管理(構成情報の取得部分) も自動化する必要が出てくる。構成情報は、台帳+実機情報、で管理されているものなので、前項の記述で、この部分は自動化できる。
http://aaabbb-200904.hatenablog.jp/entry/2016/09/19/151822

逆にこれ以外の機能で、自動化出来そうな部分がないか、一応確認してみる。ITILv3 の各項目については、こちらを参照。
http://www.itmedia.co.jp/im/articles/0803/17/news126.html

1. サービス戦略
 財務管理
 需要管理
 サービスポートフォリオ管理
  -> どれも人間が決めなければいけない部分なので、自動化できる部分は無し

2. サービス設計
 サービスカタログ管理
 サービスレベル管理
 キャパシティ管理
 可用性管理
 ITサービス継続性管理
 情報セキュリティ管理
 サプライヤ管理
  -> キャパシティ管理(Ganglia), 情報セキュリティ管理(ansible) 等、データ取得/反映の部分については、からんできそう

3. サービス移行
 変更管理
 サービス資産、及び構成管理
 ナレッジ管理
 移行計画および支援
 リリース及びデプロイ管理
 サービスバリデーション及びテスト
 評価
  -> サービス資産及び構成管理(ansible), リリース及びデプロイ管理(ansible) 等、情報取得、モジュール反映、等で自動化出来そうな部分はあり

4. サービス運用
 イベント管理
 インシデント管理
 リクエスト対応
 アクセス管理
 問題管理
 サービスデスク
 技術管理
 アプリケーション管理
 ITオペレーション管理
 -> イベント管理(監視ツール+execjson), リクエスト対応(execjson) は、まるごと自動化出来そうなのと、アクセス管理(rsync+git )で棚卸の自動化が出来そう

5. 継続的サービス改善
 7ステップ改善
 サービス測定
 サービスレポート
 -> サービス測定、サービスレポートについて、サービス情報取得、レポート作成、については自動化出来る部分がありそう

こんなところだろうか。

実際のセキュリティ設定反映、デプロイ、等は変更管理や、リクエスト対応(定型対応の項目にあれば) を通じて行うことを考えると、リクエスト対応以外で自動化出来る部分は、構成情報/パフォーマンス情報、の取得部分、となるのではなかろうか。

※ サービス情報取得、については、変更管理成功率、等が入るのかと思うが、こちらはITIL管理ツール(OTRS ITSM等)に任せる形になる、、

もちろん、問題管理/変更管理の中で、自動化、が対策となることはあるかもしれないが、これは、一部のワークアラウンド(インシデント管理)、をリクエスト対応の項目に移す(アラート発生時にスクリプトを実行する、など) と考えると、上記で対応出来る。

なので、execjson の導入(構成情報取得自動化、が前提) を行うことで、ITサービスのうち自動化可能な部分の多くが自動化出来る、といってよいのではないかと思う。

台帳の扱いについて

execjson の話の続きとなる。
http://aaabbb-200904.hatenablog.jp/entry/20150430/1430398428

サービス要求(以下、SR)処理の自動化に関連して、必ず問題になってくるのが、
台帳更新をどうするか、だと思う。

通常、人間が実施する場合は、作業を実施した日付、内容、等を台帳(大抵はエクセル)に記述していくのだが、自動で処理する場合は、エクセルに書き込むわけにはいかない。
また、台帳全てをRDBで置き換える、という判断をした場合、自動処理内で実行する、SQLの開発が、それなりの量になる。
※ カスタム開発にするしかないので、自動処理自体の価格も高止まりになる、、

どうするか、なのだが、execjson では、処理が流れる際に、JSONをelasticsearch に投げるようにしている。

※ 書き方は大変いけてないが、、
os.popen('curl --silent --max-time 15 -XPUT http://%s/jobstates/jobstate/%s -d @%s.state > /dev/null' % (elasticsearchurl, docid, filename)
https://github.com/aaabbb200909/execjson/blob/master/web/mysite/batch/do.py

これによって、作業が発生した日付、実施内容が記録でき、かつ kibana 経由で検索出来るようになっている。

基本的には台帳の役割は、上記の 1. 作業内容の検索 が主となるのだが、
それ以外に、2. 設定状況の一覧表、 3. 新規払出の一次ソース、という役割もある。

2 については、基本的には管理対象の筐体の設定ファイル、と同じになるため、
 rsync+git
http://aaabbb-200904.hatenablog.jp/entry/20120825/1345908313
で、代用できる。
※ ただし、記述内容が複数のファイルにまたがる(vSphereのVM定義、など) ことがあるため、場合によっては、日次バッチ等で、エクセルに類似のHTMLを作る必要があるかもしれない

3. については、2で対応できないもの、
例えば、IPアドレスの払い出し(実機上に情報が無い) 、等がある。

2016/9/22 追記
これ以外に、ハードウェア目録、ハードウェア配置図、や、マクロが組み込まれており、エクセルが一次情報になっているケース、等もある


これに関しては、、他のソースから取り出すわけにはいかないので、
諦めてRDBを定義するしかない。
DjangoのO/Rマッパー, adminコンソール機能を使って、できる限り簡易に扱えるようにするくらいだろうか、、

上記、1,2,3 で台帳の役割は尽きていると思うのだが、どうだろうか。
※ 開発量を抑えるポイントとしては、3以外は開発工数無しで、置き換え可能なので、
 出来るだけ1,2 で処理を完結できるようにし、3に流れる分を減らすこと、となる。

ポストVLANのネットワークについて

最近諸事情で、 SDN/NFV の話について調べている。

現行構成の問題点について、まとまっている本がこちら。
https://www.amazon.co.jp/VMware-NSX-ebook/dp/B00UL14E3C/ref=tmm_kin_swatch_0?_encoding=UTF8&qid=1473494901&sr=8-1
いくつか上がっているが、パッと見てまずそうなのは、

  1. アクティブVLAN問題(安めのアクセススイッチだと同時に使えるVLANの数に限りがある(1000程度))
  2. MACアドレス上限問題(そもそもスイッチが覚えられるMACの数が限界)

あたりだろうか、、
※ サーバー数1000超えたあたりからどうにもならなくなりそう

対応としては、以下の構成になる。


VLANがVXLANに置きかわって、VLANタグの付与(実際にはVXLANでのラップ)を物理スイッチではなく、ハイパーバイザで肩代わりする形になっている。

ポイントとしては、以下。

  1. ハイパーバイザ間の接続にはVLANを用いない(用いてもよいけれど仮想スイッチの数だけトランク接続する、はしない)
  2. 仮想OSから発生した通信は、仮想スイッチのVXLAN(かGRE)でラップし、行き先のハイパーバイザに送る
  3. どうしても物理サーバーを仮想スイッチと同じセグメントに置きたくなった場合には、ToRスイッチのVXLAN機能を使って、仮想スイッチに直接送る

上記で、現行の仕組みと同じような構成が提供できそうに見える。
実際、パブリッククラウドの適用も広がっているので、サーバー数が1000台超える組織は少ないかもしれないが、状況によっては必要になりそうなので、覚えておこう
※ また、完全に新規ネットワークを組む場合は、最初から上記構成でいった方が、ハードウェア代が抑えられるものと思われる。

追記:
図の注意:

  • 上図の黒線スイッチはVLAN使用可能(値段高い、たくさん必要)
  • 下図の青線スイッチはVLAN不要(値段安い、たくさん必要)
  • 下図の青塗り潰しスイッチはVXLAN必要(高いが台数少ない)