構成情報データベースをgitで管理したいネットワーク運用者の憂鬱
TRANSCRIPT
© INTERNET MULTIFEED CO.
構成情報データベースをGitで管理したいネットワーク運⽤者の憂鬱
インターネットマルチフィード株式会社川上 雄也
2016/10/27 NetOpsCoding#4
© INTERNET MULTIFEED CO. 2
今⽇のお話n MRTGやNagiosなどのサーバ系のコンフィグをDBから⽣成するようにしましたn DBにはYAMLを使ってGitで差分管理するようにしましたn YAML+Gitで運⽤してみてRDBと⽐較して実際にどうだったか振り返りますn これから⾃動化を進めるにあたってのDB運⽤の課題を紹介します
© INTERNET MULTIFEED CO. 3
⾃⼰紹介
n お仕事p インターネット・エクスチェンジサービスJPNAPの運⽤⾃動化・⾼度化p 時期バックボーンの設計・検証p その他JPNAPの技術的なこと関すること全般
n 得意な⾔語:Rubyn 関連発表
p JANOG34「ネットワークエンジニアとソフトウェアエンジニアの狭間で」p InternetWeek2014「ネットワーク運⽤⾃動化のためのサービス・運⽤設計」
川上 雄也 (@yuyarin)インターネットマルチフィード株式会社 技術部JPNAP ネットワークエンジニア&ソフトウェアエンジニアJANOG運営委員・ShowNet NOCメンバー
© INTERNET MULTIFEED CO. 4
JPNAP (JaPan Network Access Point)n インターネット・エクスチェンジ・サービス
p L2スイッチのポートへを提供p お客様同⼠でBGPでピアを張ってもらい経路交換とトラフィック交換をしてもらうp 接続しているネットワーク(AS)は100ちょっとp ピークトラフィックは730Gbpsp 詳しくは: http://www.mfeed.ad.jp/service/jpnap.html
BGPルータ L2SW BGPルータ
BGP
経路交換とトラフィック交換
© INTERNET MULTIFEED CO. 5
JPNAPのネットワーク&システム構成
JPNAP Backbone
お客様 ルータ 光スイッチ
監視サーバ(Nagios)
グラフサーバ(MRTG)
ルートサーバL2SW(主系)
L2SW(副系)
JPNAP BGPルータ
アラートサーバ
OpsサーバMLサーバFlowサーバWebサーバ
© INTERNET MULTIFEED CO. 6
JPNAPで必要な設定作業n 作業内容
p プロビジョニングu モジュールの増設・撤去
p SO作業u 新規開通、増速、接続場所変更、VLAN追加など
p UNIに影響のないバックボーン作業(⼣⽅)u バックボーン増速や機器の追加などの作業
p UNIに影響のあるバックボーン作業(深夜)u ファームウェア更新など機器の再起動を伴う作業
n 作業対象p L2スイッチなどのネットワーク機器のコンフィグ(UNI側)p L2スイッチなどのネットワーク機器のコンフィグ(BB側)p MRTGやNagiosなどのサーバ系のコンフィグ(BB&UNI)
© INTERNET MULTIFEED CO. 7
これまでの運⽤
n ネットワーク機器の設定p ⼿順書をテキスト作成して承認をもらってその通りにコマンドを実⾏
n サーバ系の設定p わかってる⼈がテキストエディタでコンフィグを書いてRCS管理してscpでデプロイ
n 特にサーバ系は…p 不幸な⼈為ミスのオンパレードp 表記ゆれ、フォーマット違い、バラバラの命名が多発p 1件のSO作業に超絶時間がかかるp 精神が病む
全てが⼿作業!!!
© INTERNET MULTIFEED CO. 9
どのデータベースを使う?
▶ MySQL/MariaDB ▶ PostgreSQL ▶ SQLite
RDB
▶ CSV, TSV ▶ XML ▶ YAML ▶ JSON
テキスト
▶ MongoDB
NoSQL
▶ Excel
その他
© INTERNET MULTIFEED CO. 10
どうやってデータベースを選ぶか
n 運⽤を無視した「ツール・ファースト」は運⽤現場を崩壊させるだけ
n ⾒るべきポイントp データベースの変更⽅法・⼿順p ⽣成したコンフィグのデプロイ⽅法・⼿順p ⽣成するツールの使い⽅p ⽣成するツールを書くプログラミング⾔語p 使⽤したミドルウェアの運⽤
まずは⾃分たちの運⽤⽅法や⽂化に即した技術を選ぶ
© INTERNET MULTIFEED CO. 11
深夜のある⼤規模作業n スイッチのファームウェアのバージョンアップ
p ラインカードを⾼密度のものに変更するp そのためにUNIの収容ポートを変更しないといけない
n データベースの変更⼿順1. 顧客ポートから抜去するラインカードのポートを削除2. スイッチからラインカードを削除3. スイッチのファームウェアのバージョンを変更4. スイッチに新しいラインカードを登録5. 新しいラインカードのポートを顧客ポートに登録
© INTERNET MULTIFEED CO. 12
JPNAPのオペレーションの体制n オフィス作業者:2名(作業者+確認者)
p ネットワーク機器の設定を実施するu ⼿順書は予め作成しておき承認を得る必要がある
p 深夜作業の場合はサーバ系の設定も実施するu 通常はサーバ系の設定はサーバ担当で作業の前後のどこかで⾮同期に実施するが、
⼤規模深夜作業の場合は、作業中に何度も設定を⾏わないといけないからu そのためコンフィグは予め作成して正しいか確認しておく必要がある
n 現地作業者:各DCに2名(必要な場合)
© INTERNET MULTIFEED CO. 13
⼤規模作業時のサーバ系設定の要件n サーバ担当がいなくてもサーバ系の設定作業が実施できる
p どうしようもなく⼤変なときは専任でサーバ担当をつけるけど…
n サーバ系の設定作業と確認にはあまり時間がかけられないp ネットワーク機器の設定・確認だけでも結構な時間がかかる
n 各ステップのデータベース変更後に吐き出される予定のコンフィグは作業前⽇までに確認・承認しておきたいp ただし作っている間にも別の作業でデータベースは更新されるp コンフリクトが起きないようにしておくか、誰でもコンフリクトを解消できるように
しておく必要がある
© INTERNET MULTIFEED CO. 14
もしもデータベースがRDBだったら…n コンフィグの事前の作り込みと確認はどうやる…
p データベースをどこかにクローンしてきて、変更を加えて、コンフィグを吐き出す?
n DBにどうやって変更を加える?⼿順書には何が書かれる?p パターン1:SQL⽂を直接実⾏p パターン2:WebUIから操作するp パターン3:rails cでワンライナーを撃ちまくる
© INTERNET MULTIFEED CO. 15
もしもデータベースがRDBだったら…n パターン1:SQL⽂を直接実⾏する
p SELECT⽂で確認して、UPDATEやINSERTで更新するp SQLの⼿順書を作ってから構成情報に変更があって、コンフリクトに気づかずに
実⾏したら悲惨なことになるu コンフリクトに気づいたときに適切にマージできる判断⼒が必要になるu この場合は解決するための適切なSQL⽂を作ることができる必要がある
p 作業者がシステム設計、DB、SQLについて詳しくないと何かあったときに対応できないu 何が起きるのか理解できていないコマンドを実⾏させるの?という問題も
p てか、まじで⽣SQL叩くの?u モデルのバリデーションはすっとばすの?u 例えばモジュールを搭載したらインターフェイスのオブジェクトも作らないといけないので…
▼db9SELECT switch_modules.status FROM switch_modules JOIN switches ON switch_modules.switch_id = switch.id WHERE switch.hostname = 'ix-tky-sw1' AND switch_modules.slot_number = 1→ Empty であること
UPDATE (以下略)
© INTERNET MULTIFEED CO. 16
もしもデータベースがRDBだったら…n パターン2:WebUIを操作する
p コンフリクトの問題は発⽣するが、WebUIの操作ならなんとかできる気がするp WebUIの操作⼿順書は書くのが⾟いp WebUIの操作⼿順は作成者の意図した通りの操作が⾏われる保証がないp WebUIの操作は時間がかかるのでメンテナンス中にやってられないp そもそもWebUIの実装コストが⾼い
u scaffoldとかActive Adminの画⾯ではみんな満⾜してくれない…
▼トップ画⾯左上のメニューから「スイッチ⼀覧」を選択
▼スイッチ⼀覧画⾯スイッチ⼀覧から「ix-tky-sw1」を選択
▼スイッチ詳細画⾯ix-tyk-sw1のモジュール⼀覧のSlot 1の⾏の「状態」が「Empty」であることを確認同じ⾏の右端のカラムの「搭載」を押下
▼モジュール選択画⾯モジュール検索窓に「4x10G」と⼊⼒し、表⽰された⾏の「選択」ボタンを押下
▼スイッチ詳細画⾯ix-tyk-sw1のモジュール⼀覧のSlot 1の⾏の「種別」が「4x10G」であることを確認
© INTERNET MULTIFEED CO. 17
もしもデータベースがRDBだったら…n パターン3:rails cで打つワンライナーが書いてある
p rails cを起動して、Rubyのワンライナーで確認、更新するp モデルのバリデーションを通すだけパターン1よりまだマシp コントローラを呼ぶならもっとマシp コンフリクトしたときの解決はシステムとRoRに詳しくないと無理
▼db9
rails c→ rails consoleを起動
irb> slot = SwitchModule.includes(:switch).where("switch.hostname ='ix-tyk-sw1'").where("switch_module.slot_number = 1").firstirb> slot.state.name→ Emptyであること
※以下⻑すぎて書く気が起きなかった…
© INTERNET MULTIFEED CO. 19
JPNAPの選択n 要件
p 運⽤者が読み書きしやすいフォーマットであること(human readable)p 機械処理が簡単であること(machine friendly)p バージョン管理(差分管理とブランチ作成)ができること
n 利点p ファイルなので誰でも操作できてテキストエディタで編集もできるp ミドルウェアの管理が必要なくなるp 意味のある単位でコミットが作られるので後からトレースしやすいp ⼤規模作業⽤にブランチを切ってコンフィグを作り込んでおけるp ssh & git pullで簡単にデプロイできる
テキストベースのYAMLをデータベースにしてGitで管理
© INTERNET MULTIFEED CO. 20
⼤規模作業時のサーバ系コンフィグのワークフローn ⼀週間前くらい
p ⼤規模作業⽤にブランチを作成p 作業のステップごとにDBを書き換えてコミットを作成、Tag打ちするp 各ステップのDBの内容に基づいてコンフィグを⽣成し、レビューする
n メンテナンスの直前の⼣⽅p サーバ担当者が⼤規模作業⽤ブランチをrebaseする
u ブランチを作成してから加えられた変更をマージするu コンフリクトが発⽣したら適切にマージする
p これ以降はDBへの変更投⼊を禁⽌する
n 深夜のメンテナンス中p 作業担当者が各ステップごとにcherry-pickでDBへの変更を反映して、
コンフィグを⽣成し、各サーバにデプロイする
n メンテナンス後の平⽇⽇中p ブランチを削除する
© INTERNET MULTIFEED CO. 21
コンフィグの⽣成⼿順1. データベースであるYAMLに変更を加える2. rake コマンドでデータベースとテンプレートからコンフィグを⽣成3. rake diff コマンドで差分を確認する4. rake commit コマンドでcommitしてGitLabのリポジトリにpush5. rake deploy コマンドでサーバにデプロイ
※ ⼤規模作業時は⼿順1がgit cherry-pickになります
GitLab
push
監視サーバ(Nagios)
グラフサーバ(MRTG)YAML DB
ConfigGenerator
Config Templates
pull
⚙libjpnap
© INTERNET MULTIFEED CO. 22
⼤規模作業時のサーバ系コンフィグの⼿順n 変更作業がgitコマンド1発で完了n 実⾏する各コマンドの意味は数分のレクチャーで簡単に理解可能
▼ops:~/server-config-generator/database
git cherry-pick -n origin/20161027_netopscoding4_step01
rake
rake diff→差分を確認
rake commit M="Insert 4x10G module to slot 1"
rake deploy→ OKと表⽰されること→ システム側で確認作業を実施
© INTERNET MULTIFEED CO. 23
テキストベースDB&Git管理の評価n 運⽤実績
p 2014年3⽉に導⼊してから約2年半で約900コミットの作業を実施p YAMLとGitに関するトラブルは無し
n 良かった点p ブランチを切る運⽤は⾮常に良く機能したp 意味のある単位でコミットが作られ、差分がわかりやすいのでダブルチェックが楽
n 悪かった点p YAMLを読み込んで作ったRubyのオブジェクト間のリレーションシップが相互参照にな
る実装だったので…u ppしたときに全オブジェクトが出⼒されるし、pryのデバッグが地獄u Stack Traceが重すぎる(Rack AppだとCPU100%で固まる)
p git pullでのデプロイに結構時間がかかるu GitLabのパフォーマンスがボトルネックで並列化できない
p プログラムからDBを変更する必要がある次のレベルの⾃動化には適さないu オブジェクトを操作してYAMLに吐いてGit管理する?
© INTERNET MULTIFEED CO. 24
運⽤⾃動化の段階
▶ 単体の機能・スクリプトだけを実装すればOK
Phase 1 スクリプトに必要なパラメータを渡して⼈間が実⾏
▶ データベースとテンプレートを作っておけばOK▶ ただし機器やシステム側にコンフィグリロードの仕組みがないとダメ
Phase 2 データベースから全コンフィグを⽣成して機器にデプロイ
▶ ワークフロー、イベントハンドラ、メッセージキューイング、シリアライズ、ロックなどの⾼度な仕組みが必要
▶ データベースはシステムによりその都度⾃動で更新されていく
Phase 3 イベントドリブンで必要なコンフィグの差分を機器に投⼊
イマココ
© INTERNET MULTIFEED CO. 25
Phase 3でのテキストベースのDBn ファイルを読み込んで、オブジェクトにして、オブジェクトを操作して、また
ファイルに書き出すp コメントは全部消えます(まぁ仕⽅ないか)p YAMLだと⼈間の書いたコンフィグと吐き出されるコンフィグの差分が…
u その都度吐いて、吐かれたものをコミットすればなんとかu マージは⼈間がやる
n ⼈間の編集とシステムによる⾃動更新のコンフリクトp ⼈間がDBを直接編集することは避けられないp ⼈間が編集中はDBをロックしておく?
u その間お客さまが何か作業をしたくてもエラーになってしまう…(まぁそれはそれであり)u トランザクション機能は必要
p まぁでもRDBでもそれは起きるよなぁ
そこそこつらみがあるがなんとかなる?
© INTERNET MULTIFEED CO. 26
Phase 3でのPhase 2ベースのシステムの課題n データベースからコンフィグを全⽣成するタイミング
p イベントごとに意味のある処理が⾏われたら⽣成する?u デプロイに10分とかかかってると次のイベントがブロックされてしまう
p 定期的に⽣成する?u たまたまそのタイミングでデータベースが不完全な状態だったらどうしようu トランザクションを使ってAtomic性を保証しておく?
© INTERNET MULTIFEED CO. 28
Phase 3の⾃動化に向けてn データベースの実装
p Rails + PostgreSQLを利⽤するつもりn ネットワーク機器の操作スクリプト
p Telnet/sshのラッパーライブラリを各機器種別ごとに作成p 細かい単位のワークフローの実装p 従来のテキスト⼿順書のようにコードを記述できるDSLライブラリ
n 「ワークフロー、イベントハンドラ、メッセージキューイング、シリアライズ、ロックなどの⾼度な仕組み」p StackStormを有効活⽤p Webポータル操作をイベントとして、スクリプトを実⾏
© INTERNET MULTIFEED CO. 30
(参考) ツールの構成n Rakefile 各タスクを記述n lib/ 各システム⽤のコンフィグ⽣成のためのライブラリn libjpnap/ YAMLを読み込んでRubyオブジェクトを作るライブラリ(git管理)n database/ YAMLのデータベース(git管理)n template/ 各システム⽤のコンフィグ⽣成のためのテンプレート(git管理)n output/ 各システム⽤に⽣成されたコンフィグの置き場所(git管理)
© INTERNET MULTIFEED CO. 31
(参考) YAMLの例n 設計したモデルに基づいてYAMLを記述する
p switches.yml の例
ix-tky-sw1:hostname:ix-tky-sw1.mgmt.mfeed.ad.jpmgmt_ip_address:172.16.0.1vendor:brocadechassis:mlxe32firmware_version:10.0snmp_community:hogehogemodules:S1:8x10GS2:2x100Gix-tky-sw2:hostname:ix-tky-sw2.mgmt.mfeed.ad.jpmgmt_ip_address:172.16.0.2vendor:brocadechassis:mlxe16firmware_version:11.0modules:S1:8x10G
© INTERNET MULTIFEED CO. 32
(参考) YAMLのデータからオブジェクトを⽣成する例n YAMLを読み込んでRubyのオブジェクトにするライブラリ(libjpnap)
p YAMLのデータをコンストラクタに渡してインスタンスを作っていきリレーションシップはオブジェクトの参照として持たせる
p database.rb の例
classDatabasedefload_switchesdata=YAML.load('switches.yml')data.eachdo|hostname,switch_data|@switches[hostname]=Switch.new(switch_data)endendend
© INTERNET MULTIFEED CO. 33
(参考) YAMLのデータからオブジェクトを⽣成する例n YAMLを読み込んでRubyのオブジェクトにするライブラリ(libjpnap)
p YAMLのデータをコンストラクタに渡してインスタンスを作っていきリレーションシップはオブジェクトの参照として持たせる
p switch.rb の例
classSwitchattr_reader:hostname,:interfacesdefinitialize(data)@hostname=data['hostname']@mgmt_ip_address=data['mgmt_ip_address']@modules=data['modules']build_interfaces_from_modules...enddefinterface(interface_name)@interfaces[interface_name]endend
© INTERNET MULTIFEED CO. 34
(参考) コンフィグテンプレートの例n Rubyのオブジェクトを操作して必要なデータを作成し、ERBのテンプレートで
コンフィグを⽣成する(generator)p 各スイッチのインターフェイスのトラフィックを取るMRTGのコンフィグのテンプレー
トの例LogFormat:rrdtoolPathAdd:/usr/local/rrdtool/bin/EnableIPv6:noLogDir:/mrtg-logs/traffic/switch/<%=switch.hostname%>HtmlDir:/mrtg-images/traffic/switch/<%=switch.hostname%>ImageDir:/mrtg-images/traffic/switch/<%=switch.hostname%>NoMib2:YesSnmpOptions:timeout=>2,retries=>5RRDRowCount[_]:19200MaxBytes[_]:12500000000<%switch.interfaces.eachdo|if_index,interface|-%><%outfile="#{switch.hostname}_#{interface.code}"-%>Target[<%=outfile%>]:<%=interface.if_index%>:<%=switch.snmp_community%>@<%=switch.management_ip_address%>:::::2Title[<%=outfile%>]:<%=switch.hostname%><%=interface.name%><br/>ifIndex=<%=interface.if_index%>MaxBytes[<%=outfile%>]:<%=interface.speed.to_bytes%><%end-%>
© INTERNET MULTIFEED CO. 35
(参考) デプロイの⽅法n 対象になる全てのサーバに、全システムのコンフィグを含んだGitリポジトリを
クローンしておくn git pullすることでデプロイするn 必要なファイルをシンボリックリンクすることでアプリケーションに読み込ま
せる
© INTERNET MULTIFEED CO. 36
(参考) 実装の規模n コンフィグ⽣成ツール
p ライブラリu 12ファイル、18クラス、1,263⾏
p Rakefileu 35タスク、227⾏
p スクリプト(sh)u 16ファイル、505⾏
n データベースp 18ファイル、10,496⾏
n ライブラリ(libjpnap)p 85ファイル、98クラス、308メソッド、6,048⾏
n テンプレートp 9種類、81ファイル、2,248⾏
n コンフィグp 10種類、1,216ファイル、111,521⾏