ネットワークの自動化、何つかう? 〜自動化ツール紹介〜 #APC勉強会 #30(2回目) 参加メモ

ネットワークの自動化、何つかう?〜自動化ツール紹介〜 APC勉強会#30(2回目) - connpass に参加してきたのでいつものメモ。1回目のときはぼんやりしていたらあっという間に満席になってしまったので2回目で滑り込みです。

資料は1回目の時に公開されているのでそちらを参照してください。

ネットワークの自動化、何つかう?〜自動化ツール紹介〜

はじめに
  • NW機器もAPIに対応して、自動化フレンドリーなものが増えてきた
  • そういう機械でなくても自動化できるツールやライブラリを紹介
    • netmiko
    • napalm
    • ansible
    • salt
全体像
  • 業務フロー : 作業, 実際に config 流し込むところの自動化
  • ツール依存イメージ
    • 構成管理ツール : ansible, salt
    • python ライブラリ : napalm, netmiko
  • 機能概要
    • netmiko, napalm : ライブラリ。抽象化の仕方が違う。
      • netmiko...直接コマンドを指定, napalm...ひとつのメソッドで複数のベンダのコマンドに相当
    • ansible, salt : ツール。抽象化の仕方が違う。
      • saltはnapalmの中で使っているのでベンダをお意識しなくても使えるようになっている
      • ansibleはモジュールによるけど、コマンドを直接呼ぶタイプのモジュールが多い
netmiko

netmiko

  • シンプルで薄い抽象化ライブラリ。
  • login, モード移行, commit などを共通メソッドとして抽象化。
    • "conf term" とかは抽象化されてる
    • 表示・設定コマンドはコマンドを直接指定する
  • netmiko 対応機種
    • けっこうある

コード例

  • 状態表示
    • enable メソッドで抽象化されてる
    • send_command で直接コマンドを送る
      • コマンド実行結果がそのままとれる (特に parse とかしない)
  • 設定変更
    • 設定コマンドも直接指定
    • send_config_set でコマンド文字列(リスト)を渡して実行

まとめ

  • シンプルで薄い
  • teraterm マクロよりは楽かな
    • waitとかプロンプトとか気にしなくていいので設定に注力
    • 対応機種は限られるので注意
napalm

napalm

  • 多機能で厚い抽象化ライブラリ
    • 一部だけど表示コマンド・設定コマンドまで抽象化
  • 対応機種
    • netmikoよりはすくない
  • 対応メソッド
    • サイトにある…使いたい嬉々とやりたい操作の対応をチェックすべし。

コード例

  • 状態表示
    • get_interfaces メソッド→IOSの場合は"show interface" : 抽象化されてる
    • コマンド実行結果も parse されて構造化されたデータとして取得
    • 違うOSでも共通のデータ構造で取得できる
  • 設定変更
    • load_template メソッド。テンプレートを呼び出して、パラメータを渡して、設定実行。

まとめ

  • 多機能で厚い
    • 一部設定と表示に関しても抽象化…対応していないものについては直接コマンド指定して実行することもできる。
Ansible

ansible

  • シンプルな構成管理ツール
  • 抽象化というアプローチは特にとっていない。
    • ベンダごとにモジュールがあるのでそれを使い分ける
    • netconf 対応などは今後注目
  • 特徴
    • agentless
    • yaml で構成定義 (playbook)...なってほしい状態を定義する
      • プログラムを書くわけではない
  • 対応機種
    • v2.3, napalm よりちょっと多い

playbook例(cisco ios module)

  • 状態表示
    • コマンドを直接指定して実行
    • 特に parse されるわけじゃない
  • 設定変更
    • コマンドを直接指定して実行
    • "changed=1", 既に設定が入っていれば "changed=0"

まとめ

  • シンプル
    • インストールも pip で一発。必要なファイルも最低二つ。
salt

salt

  • 多機能な構成管理ツール
    • 中で napalm を使っている……抽象化するアプローチ
    • ベンダごとのモジュールも出てきているけど主要なのは napalm を使う方法
  • 概要
    • master/agent 型
    • yaml で構成定義
    • 高機能 : イベントドリブン, スケジューラ, WebAPI
    • v2016.11.0 で napalm 統合 (NW機器対応)

Saltの主な用語

  • Master : 指示を出す
  • Minion : いわゆる Agent, master の指示を受ける
    • Master-Minion 間は MQ
    • NW機器対応をどうするか…Proxy Minion
    • Master → Proxy Minion → NW機器 (ssh等)
  • Grains : ホスト名、シリアル番号、バージョン番号などの基本的な情報を保存
  • Pillars : 任意のデータを保存できる

対応機種

  • 基本的には napalm と同じ

設定がちょっとややこしい

  • バイスID → IPや認証情報は別ファイルで管理(proxy minion)

状態表示例

  • Grains から情報を引っ張ってくる
    • 内部で napalm キックして情報をとってくる
    • 構造化された情報がとれる

設定変更例

  • 定義ファイル実行
    • "changed=1"

デモ

  • 定義ファイルが更新されたら自動的に設定投入 : イベントドリブン
    • daemon で動いているのでファイル監視とかができる

まとめ

  • 多機能な構成管理ツール
各ツールの比較
  • ライブラリか、構成管理ツールか
  • 抽象化のアプローチ
  • メリット・デメリット
    • netmiko: 対応機種が多い。その分抽象化レベルは低い。
    • napalm: マルチベンダ環境の情報取得に強い…parseしてくれる。一方、設定系のメソッドがまだ少ない。足りないところは直接コマンド入力
    • ansible: シンプル! アプローチが違うので機種ごとにモジュールを使い分け。情報量が多い。開発が盛ん。
    • Salt: 高機能…イベントドリブンとかWebAPIとか。内部では napalm を使っているので、マルチベンダ情報取得に強いという特徴を。日本語情報は少ない。調べるときは公式ドキュメント頼み。

ツールつかいどころ選択

  • 管理したい危機に対応しているかどうか
  • コードを書くかどうか
    • コードにすると運用城南があるのであれば構成管理ツール系へ
  • シンプル or 高機能
    • 高機能 → 学習コストが高い
  • どの程度抽象化したいか
    • ターゲットが一つ二つであればそんなに抽象化気にしないのでは
  • 管理対象機器の種類・ベンダ数
    • 種類が多いほど、抽象化レベルが低いとベンダごとのコマンド使い分け・パターンが増える。
  • 既存で導入している構成管理ツールがあるならあわせる

まとめ

  • 特徴を把握して適材適所で
これから
  • 自動化を進めるにあたって: ツール・人・仕様
    • 人: 自動化するための設計・実装スキル、文化
    • ツール: 今回の話はここ
    • 仕様: api拡充、標準化
QA
  • dry-run/diff 表示
    • ansible, salt は dry-run できる。netmiko, napalm は自前で dry-run 動作を実装しないといけない。
  • ツールによる設定変更・管理のメリット
    • 設定要件→定義ファイルのexportなど、中間加工のしやすさ。デメリットとしてはyaml知らないといけないというのはある
  • excelからデータとれる?
    • 標準ではなさそう。やろうとおもえばやれる
  • commit の自動化って大丈夫?
    • 変なことすると途中で止まるとかはある。検証・dry-runしてチェックしたうえで実行とか、そのあとのテストなどでリスクヘッジしていく。アプリ開発の自動化とはこういった点で性質が異なる。
  • 表示コマンドの抽象化は必要か?
    • ケースバイケースではあるが、あると便利だと思う。自前で parse するのは大変。
  • 既存の自動化スクリプト+構成管理ツールでサーバとNWの一括管理というのがメリットがあるのでは
    • サーバ側を含めて、というのはメリットがあるだろう
  • 機器が対応していない場合のアプローチ?
    • paramiko (netmiko内部のssh module) ベースで自作, ansible module 自作、あたりか。
  • Ansible導入どうやってる?
    • 最初のハードル。インベントリファイルどう作るかとか。別なスクリプトかませないといけないかもしれない。最初の対応はちょっと泥臭い対応が必要になりそう。
  • 失敗時ロールバック?
    • 実装すれば全部可能。モジュールについては試していないが、junosのケースでは失敗時commitしないようになっているのでは?

所感

懇親会でも他の方と話をしたんですが、この辺のツールで気になるところはある程度共通だなあと。

  • エラー処理・例外処理
  • dry-run など実行前のチェックの可否

何かしらのカタチでコマンドを投げる、というところだけだとやっぱり実際動かしていシステムで使うのはちょっとねー、という話になる。あと、エラー処理とか例外処理とか、運用している側からすると、やりたいことそのものではないというのもあるかな。エラー処理とか対応手順とかはあるんだけど、CLIベースの機材に対してそれを自動化するための仕掛けそのものを作るのってハードル(手間的な)が高いんだよね…。ツールにあるエラーハンドリングの仕掛けの上でやりたいこと(やりたいエラー処理)に集中できるようにならないかな、と言う話とかね。

D3.js v4 の Hierarchical Edge Bundling を理解する(3)

Sample Code

サンプルコードはこれ

こういう図ができる。


データの準備

leaf 間のリンクデータを追加します。

    var link_data = [
        {"source" : "Azura", "target" : "Enoch"},
        {"source" : "Enoch", "target" : "Abel"},
        {"source" : "Enoch", "target" : "Noam"},
        {"source" : "Azura", "target" : "Noam"},
        {"source" : "Abel", "target" : "Enos"},
        {"source" : "Abel", "target" : "Noam"},
        {"source" : "Noam", "target" : "Enos"},
        {"source" : "Enos", "target" : "Cain"}
    ];

変わらないところ

cluster layout を円周上に描画するところは 前回 と同じです。leaf 間のリンクだけ上書きしてます。path の書き分けのために class 指定して CSS で描き分けてるところくらいかな。

変わるところ

まず、leaf 間リンク情報を Node オブジェクトに変換します。d3.map() については d3/d3-collection: Handy data structures for elements keyed by string. を参照。leaf 一覧 (nodes.leaves()) を name attribute で引っ張れるようにしています。

    var nodes_name_map = d3.map(nodes.leaves(), function(d) { return d.data.name; });

leaf 間リンクのデータは name だけ持っているので、それをキーに Node オブジェクトを取ってきます。

    var interleaf_links = link_data.map(function(d) {
        return {
            "source" : nodes_name_map.get(d.source),
            "target" : nodes_name_map.get(d.target)
        };
    });
    console.log("interleaf_links");
    console.log(interleaf_links);


leaf 間 path の描画

d3.radialLine() はそのまま使います。ここで重要なのは node.path(target) です。

# node.path(target) <>

Returns the shortest path through the hierarchy from this node to the specified target node. The path starts at this node, ascends to the least common ancestor of this node and the target node, and then descends to the target node. This is particularly useful for hierarchical edge bundling.

GitHub - d3/d3-hierarchy: 2D layout algorithms for visualizing hierarchical data.

path は node と target の間に共通の parent がある場合に、そこを経由した曲線を作成してくれます。……というのもあって上の図はあえて cluster layout tree の上に上書きしてみました。この機能、d3.js v3 では d3.layout.bundle() が提供していたようです。

    // path
    svg.selectAll("path.interleaf")
        .data(interleaf_links)
        .enter()
        .append("path")
        .attr("class", "interleaf")
        .attr("d", function(d) {
            return line(d.source.path(d.target));
        });

Hierarchical Edge Bundling

leaf のみにして parent node を描画しないようにすればできあがりです。

コードはこれ。

--- d3-hierarchical-edge-bundling.js    2017-06-03 23:56:00.309974700 +0900
+++ d3-hierarchical-edge-bundling2.js   2017-06-04 00:26:54.340704000 +0900
@@ -51,9 +51,6 @@
     var node_size = 20;
     var cluster = d3.cluster().size([360, radius]);
     var nodes = cluster(root_node);
-    var parent2child_links = nodes.links();
-    console.log("parent2child_links");
-    console.log(parent2child_links);

     var nodes_name_map = d3.map(nodes.leaves(), function(d) { return d.data.name; });
     var interleaf_links = link_data.map(function(d) {
@@ -73,19 +70,6 @@
         .curve(d3.curveBundle.beta(0.85))
         .radius(function(d) { return d.y; })
         .angle(function(d) { return path_angle(d.x); });
-    svg.selectAll("path.parent2child")
-        .data(parent2child_links)
-        .enter()
-        .append("path")
-        .attr("class", "parent2child")
-        .attr("d", function(d) {
-                return line([
-                    d.source,
-                    { "x" : d.source.x, "y" : (d.source.y + d.target.y)/2 },
-                    { "x" : d.target.x, "y" : (d.source.y + d.target.y)/2 },
-                    d.target
-                ]);
-            });

     // path
     svg.selectAll("path.interleaf")
@@ -99,7 +83,7 @@

     // circle (overwrite path)
     svg.selectAll("circle")
-        .data(nodes.descendants())
+        .data(nodes.leaves())
         .enter()
         .append("circle")
         .attrs({
@@ -112,7 +96,7 @@

     // text
     svg.selectAll("text")
-        .data(nodes.descendants())
+        .data(nodes.leaves())
         .enter()
         .append("text")
         .attrs({

おわりに

3回に分けて、順を追いつつ Hierarchical Edge Bundling の話を見てきました。D3.js Gallery にあるのは凄くきれいで洗練されてるんだけど、何をやっているのか理解したいという目的で見るとちょっと難しいね。まああれは、D3.js でどんなことができるのか、"上" のイメージを見せるものだよね。本当はこの上で、CSSを組み合わせて動的にハイライトさせるとかそういう処理があったりするんですが、そのへんは発展課題で。最小限でやるとこんな感じじゃないかなあと思っていますが、それほど javascript とかやってるわけじゃないので、もうちょっといい方法もあるかもしれません。

D3.js v4 の Hierarchical Edge Bundling を理解する(2)

はじめに

前回 は cluster layout で階層データを描いていました。ここから Hierarchical Edge Bundling にいく……前にこれをちょっと変えます。上下方向(水平・垂直方向)に広げて描いていた階層を円周上にレイアウトしてみたいと思います。

参考

Sample Code

サンプルコードはこれ

こういう図ができる。

変わらないところ

データの取り扱いや cluster layout で各ノードの座標を決めるところまでは同様。というか、ノードや path の描画もほぼ同じ。

変わるところ

座標系を極座標に変換している、というところがちがいます。

    var radius = Math.min(width/2, height/2);
 
    var cluster = d3.cluster().size([360, radius]);

これは、各ノードの x 座標を角度(degree)、y 座標 (root nodeからの垂直方向の距離)を半径(原点からの距離)として設定しています。

path の描画

変更しているのは d3.line() ではなく d3.radialLine() にして極座標指定で線を描いているところ。

    var line = d3.radialLine()
        .curve(d3.curveBundle.beta(0.85))
        .radius(function(d) { return d.y; })
        .angle(function(d) { return path_angle(d.x); });
    svg.selectAll("path")
        .data(links)
        .enter()
        .append("path")
        .attr("d", function(d) {
                return line([
                    d.source,
                    { "x" : d.source.x, "y" : (d.source.y + d.target.y)/2 },
                    { "x" : d.target.x, "y" : (d.source.y + d.target.y)/2 },
                    d.target
                ]);
            });

function path_angle(x) {
    return rad(x);
}

とりあえず素直に前回作ったものを変換してみた。すると上の図のようになる…。root node ("Eve") の x 座標自体が角度として設定されるので、そのまま変換するとちょっと不格好な path になる。まあ、parent node 〜 child node の path にはもともと方向性があるので、そのまま変換すると偏りが出てしまうのだな。ここはもうちょっと工夫が必要。

ノードの描画

これも極座標を直交座標に変換している。90度 (Math.PI/2) ずらしているけど、これは d3.radialLine().angle() が -y (12時) のところを起点とするため。(参照 : d3/d3-shape: Graphical primitives for visualization, such as lines and areas.)

    // circle (overwrite path)
    svg.selectAll("circle")
        .data(nodes.descendants())
        .enter()
        .append("circle")
        .attrs({
            "cx" : node_x,
            "cy" : node_y,
            "r" : node_size/2
        })
        .append("title")
        .text(function(d) { return d.data.name; });

function node_angle(x) {
    return rad(x) - Math.PI/2;
}
function node_x(d) {
    return d.y * Math.cos(node_angle(d.x));
}
function node_y(d) {
    return d.y * Math.sin(node_angle(d.x));
}

描画上のトリック

    var svg = d3.select("body")
        .append("svg")
        .attrs({"width" : width,
                "height" : height})
        .append("g")
        .attr("transform", "translate(" + width/2 + "," + height/2 + ")scale(0.8,0.8)");

円形レイアウトにしたので、座標系の原点を SVG 領域の真ん中にシフトさせています(センタリング)。縮小するのは前回と同じではみだし防止。