新監視ツール、出来ました!!

ども、uozy☆です。


前回・前々回とバックアップに関する記事を書きましたが、今回は新しく作った監視ツールについて書いてみたいと思います。

tech.sanwasystem.com

そもそも、なんで今さら作ったのか?

弊社のメインプロダクトは「NT-golf」(ゴルフ場向けの基幹システム)です。当然監視の仕組みもあるのですが、若干不足する部分があったり通知がしょぼかったりと・・・わがままなuozy☆には不満のある内容でした。 そこで、今後のシステムの進化にもついていけるうように新しい仕組みを使って構築してしまおうと思ったのです。 監視ツール(OSSも含めて)を使えばいいんじゃないの?って話になると思いますが、以下のような理由から作ってしまう事にしました。

  • そんなにコストはかけられない
  • 別に多機能じゃなくていい
  • 自分達でメンテして好きに拡張したい
  • メール通知とかうざい、slackに通知して欲しい
  • クライアントの環境が異なりすぎて全てに対応するのがむずかしい
  • エージェントを入れなきゃいけないようなツールはイヤ。ただ配置するだけがいい

構成

今回構築したツールのざっくり構成図です。

f:id:uozy:20150826214800p:plain

と、意外と盛り沢山な内容になっています。それでは、図の番号に沿って解説していきたいと思います。


①運用環境(ゴルフ場)

運用環境はWindowsServer+SQLServerのクラサバ環境となっています。ここではPowerShellを使いジョブの実行とサーバー情報の収集をします。DB関係のジョブもSQLServerAgentをあえて使わずに、SQL文をInvoke-sqlcmdコマンドレットで実行するようにしています。展開の速さ(psファイルと設定ファイルを置けばいいだけ)と、環境の差異を埋めるようにしています。実行結果はローカルにログファイルを吐く(通信出来なった場合を考慮)と共に、fluentdに送信するようにしています。ちなみにジョブのキックはタスクスケジューラで行います。

  • DB関係
$Query = "DBCC CHECKDB('" + $DBName +"') ;select @@error as ret"
$ret = Invoke-Sqlcmd -ServerInstance $SV -Username $UserID -Password $Password -Database $DBName -Query $Query
if ($ret.ret -eq 0) {$ret1 = "正常"}else{"異常"}

#上記のように結果を返さないようなコマンドも、@@errorを取る事でエラーを拾う事が出来ます。
  • ファイルバックアップ
$CopySourse  = "D:\コピー元"
$CopyDest = "E:\コピー先"

#バックアップ用ディレクトリ作成
$nowstring = Get-Date -Format "yyyyMMdd_HHmmss"
New-Item -Path $CopyDest -Name $nowstring -ItemType directory

#コピーする(サブディレクトリも含めて)
Copy-Item -Container $CopySourse -Destination (Join-Path $CopyDest $nowstring) -Exclude '*.txt' -Recurse -Force
  • イベントログの取得
Get-EventLog -LogName Application -EntryType Error -After "2015/08/01" -Source "MSSQLSERVER" -ErrorAction SilentlyContinue

#-LogName   : Application System など
#-EntryType : Error、Information、FailureAudit、SuccessAudit、Warning
#-Source    : ソース名 ※APCSmartUPSログ→APCPBEAgentなど
#この辺の設定値はイベントログ画面を見ればすぐにわかると思います。
  • ディスク容量
$Disks = Get-WmiObject Win32_LogicalDisk | Where-Object { $_.DriveType -eq 3 }
# DriveTypeは↓
# Unknown:0 No Root Directory:1 Removable Disk:2 Local Disk:3
# Network Drive:4 Compact Disc:5
# RAM Disk:6

foreach ($Disk in $Disks)
{
    #ドライブ名
    $DriveName = $Disk.DeviceID
    #サイズ
    $Size = [long]$Disk.Size
    #空き容量
    $Free = [long]$Disk.FreeSpace
    #使用量
    $UsedSize = [long]$Disk.Size - [long]$Disk.FreeSpace
    #使用率
    $DriveUsesPer = (($UsedSize/$Size)*100)
}
  • fluentdへの送信
#送信する内容を定義
$PostParams = @{JobName = "DailyBackUpJob"
                Message = "正常終了しました"
                time = Get-Date -Format s;}
#fluentdに送信
Invoke-RestMethod -Uri $fluentdURL  -Method POST -Body ([System.Text.Encoding]::UTF8.GetBytes(($PostParams | convertto-json))) -ContentType 'application/json; charset=utf-8'

②fluentd (ログデータの転送)

ここでは各運用環境から送られてきたログデータをmongoDBに送信しています。将来的に拡張する場合はfluentdで転送先を増やしていけばいいと思います。今回はmongoDBに送信するのでtd-agent.confに以下の設定を追加します。

<match mongo.**>
 type mongo
 host XXX.XXX.XXX.XXX
 database [database_name]
 port 27017
 collection [collection_name]
 flush_interval 10s
</match>

インストール等fluentd→mongoDBについて、詳しくはこの記事を参考にしてください。 tech.sanwasystem.com

③mongoDB (ログデータの保存)

mongoDBについてはご存知の通りだと思うので、細かい説明はしません。フレキシブルでほんとうに便利ですね。今回のように、ただログデータを受けるだけ、みたいな場合には最適だと思います。ドキュメント類も充実しているので安心して使えます。 ちなみにuozy☆はGUIが大好きなのでmongoDBの操作にはRobomongoを使用しています。 f:id:uozy:20150827115817p:plain

ちなみにmongoDBの内部構造としてはこのようになっています。

Databases
 L Collections
    L Documents
       L Fields

④ConsoleApp (ログデータの解析)

さて、今度は各運用環境から集まったログデータのなかから異常ログデータを検索します。ここではConsoleApplication(C#)を作成しmongoDBへアクセスしデータを抽出します。こちらも運用環境同様にジョブのキックはタスクスケジューラで行います。

まずはドライバをNuGetしてください。

PM> Install-Package mongocsharpdriver

データを取得する時はこんな感じです。

using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;

//mongoDBの設定
MongoServerSettings settings = new MongoServerSettings();
settings.Server = new MongoServerAddress([HostNmae], [PortNo]);
MongoServer server = new MongoServer(settings);

//DBを取得
var database = server.GetDatabase([DBName]);

//find条件を作成
var query = Query.And(Query.EQ("StatusColum", BsonString.Create("Error")),
                      Query.GT("time", BsonValue.Create(DateTime.Now.AddDays(-1))));

//データを取得
var logs = database.GetCollection(MongoCollection).Find(query);

⑤slack (通知)

最後は④で抽出したエラーデータをslackに通知します。弊社では全社的にslackを使用していて、slack通知だとメール通知とは違い、スピード感のある対応が出来ると思っています。

tech.sanwasystem.com

さて、slack通知を見てみましょう。まずはJSON.NetをNuGetします。

PM> Install-Package Newtonsoft.Json

そして送信用のpayloadを準備します。

public class Payload
{
     //通知先チャネル
     [JsonProperty("channel")]
     public string Channel { get; set; }
     //通知ユーザー名 
     [JsonProperty("username")]
     public string Username { get; set; }
     //メッセージ本文
     [JsonProperty("text")]
     public string Text { get; set; }
     //アイコン
     [JsonProperty("icon_emoji")]
     public string Icon_emoji { get; set; }
}

送信♪

String uri = "http://XXXXXXXX";
Payload payload= new Payload();

//送信するデータを設定
payload.Channel= "channel_name";
payload.Username= "user_name";
payload.Text = "This is Message!!";
payload.Icon_emoji = ":grinning:";

//JSONに変換
string payloadJson = JsonConvert.SerializeObject(payload);

//送信
using (WebClient client = new WebClient())
{
     NameValueCollection data = new NameValueCollection();
     data["payload"] = payloadJson;
     var response = client.UploadValues(uri, "POST", data);
}

と、足早に説明しましたが、割と簡単に出来ました\(^o^)/

まとめ

だらだらと長くなってしまったので一旦まとめます。今回のツールで出来る事は以下の通り。各々でやってる事は非常に単純ですが、連携する事でいろいろな事が出来るようになっています。

  • DBメンテナンス(SQL実行)
  • フォルダのバックアップ
  • イベントログの取得
  • ディスク容量監視
  • 1日1回、上記4項目を運用環境で実行し結果を集約
  • 集約した結果に異常値が含まれている場合はslackで通知

今後について

せっかく作ったのでこれをベースに魔改造していろいろやっていきたいと思っています! salesforce連携してインシデント管理してみたり、取る情報をもっと増やしてvisualizeしてみたり、HWの故障の予測なんかも出来るかもしれません。まだまだやる事がいっぱいです!!

今回はここまで

バイバーイ \(^o^)/