PowerShellからMongoDBにアクセスする。

# PowerShellでMongoDBにアクセス

Windows2016TP4上でMongoDBを動かし、タスクスケジューラに設定したPowerShellからデータを書き込みます。

|ソフト等|バージョン|備考|
|:------|:--------|:---|
|Windows Server 2016|Technical Preview 4(Build 10586)|2012R2のHyper-V上で動作|
|PowerShell|5.0|2016TP4標準のまま|
|MongoDB|3.2.1|Windows 64-bit 2008 R2+ legacy|
|MongoDB .NET Driver|1.11.0||

## MongoDBのインストール。

インストーラは好きに[mongoDB公式](https://www.mongodb.org/downloads#production)からダウンロードすれば良いですが、デフォルトの「Windows 64-bit 2008 R2+」のmsi版は私の環境では問題があったので、「Windows 64-bit 2008 R2+ legacy」のzip版で以後は進めています。
（ここでの問題は、mongod.exeを叩くと動くものの、--installでmongodbサービスが登録されないというもの。手動で登録できたかもしれないけど、面倒なのでやめました。あと、フォルダが「C:\MongoDB\Server\3.2\bin」みたいに深くなってるのも嫌だった。）

![download.png](https://qiita-image-store.s3.amazonaws.com/0/111541/60288dac-90bf-bc41-714e-a474bd8cf875.png)

ダウンロードしたzipを解凍し、C:\MongoDBとした。
また、C:\MongoDB\binと同じ位置に、dbとlogの各フォルダを作成。
C:\MongoDB\mongod.cfgを作成し、以下の内容を記述。

```text:mondod.cfg
systemLog:
    destination: file
    path: C:\MongoDB\log\mongod.log
storage:
    dbPath: C:\MongoDB\db
```
以下のコマンドを、管理者のコマンドプロンプトで実行。

> cd \MongoDB\bin
> mongod.exe --config "C:\MongoDB\mongod.cfg" --install
> net start mongodb

とりあえず、mongo.exeでつながることを確認した。

## PowerShell用にドライバ準備。

PowerShellからMongoDBにつなぐには、[MongoDB .NET Driver](http://mongodb.github.io/mongo-csharp-driver/)を使うのが定番ですが、最新のv2.x系は一部のメソッドが変更されておりそのまま使えないなど色々めんどくさいです。なので私は、[v1.11.0](https://github.com/mongodb/mongo-csharp-driver/releases/tag/v1.11.0)を使います。
ダウンロードして解凍し、C:\MongoDB\CSharpDriver-1.11.0に配置しました。

## PowerShellで、プロセッサ使用率などをMongoDBに登録。

次の情報をカウンターから取得し、1分毎にMongoDBに登録するPowerShellを作成した。

* 日時
* コンピュータ名
* プロセッサ使用率（合計）
* ネットワーク使用量（最大）
* ディスク使用量（合計）

C:\MongoDB\logging.ps1に以下の内容で保存。

```ps1:logging.ps1
##### プロセッサ使用率などを、１分毎にMongoDBに登録 #####

# ドライバ読み込み
$DRIVER_PATH = "C:\MongoDB\CSharpDriver-1.11.0";
Add-Type -Path "$DRIVER_PATH\MongoDB.Bson.dll";
Add-Type -Path "$DRIVER_PATH\MongoDB.Driver.dll";

# MongoDBの、logsデータベースの、osコレクションに接続
$client = New-Object MongoDB.Driver.MongoClient("mongodb://S2016TP4");
$server = $client.GetServer();
$db = $server.GetDatabase("logs");
$coll = $db.GetCollection("os");

# 前回登録済みの日時を退避しておく変数
$old = "";

# 以下、無限ループ
while(1) {

    # 現在の日時を、分まで文字列化
    $now = Get-Date -UFormat "%Y%m%d %R";

    if($old -eq($now)) {
        ##### 登録済みの日時と同じなら、50ミリ秒待つ
        Start-Sleep -Milliseconds 50

    } else {
        ##### 登録済みの日時と異なるなら、以下を実行

        # 登録用ドキュメントを作成
        [MongoDB.Bson.BsonDocument]$d = @{

            # MongoDBのドキュメントID
            "_id"= [MongoDB.Bson.ObjectId]::GenerateNewId();

            # コンピュータ名を環境変数から取得
            "ComputerName" = $ENV:COMPUTERNAME;

            # 時刻を標準時に変換して設定
            "TimeStamp" = (Get-Date).ToUniversalTime();

            # プロセッサ使用率（合計）を設定
            "Processor_per" = (Get-counter -Counter "\Processor(_Total)\% Processor Time").CounterSamples.CookedValue;

            # ネットワーク使用量（最大）を設定
            "Network_byte" = ((Get-counter -Counter "\Network Interface(*)\Bytes Total/sec").CounterSamples.CookedValue | Measure-Object -Maximum).Maximum;

            # ディスク使用量（合計）を設定
            "Disk_byte" = (Get-counter -Counter "\LogicalDisk(_Total)\Disk Bytes/sec").CounterSamples.CookedValue;

        };
        # コレクションにドキュメントを登録
        $coll.save($d);

        # 登録済み日時を退避
        $old = $now;
    }
}
```

そんな難しいことはしていないので、ソース読めば分かると思いますが、Get-Counterについてだけ補足しておきます。
-Counterで書いてる呪文は、\ **オブジェクト名** ( **インスタンス名** )\ **カウンター名** となっています。
これらの値は、パフォーマンスモニターでパラメータを選択して追加すると下の図のように表示されますので、監視したい情報に応じて好きに変更できます。

![preformanceMoniter1.png](https://qiita-image-store.s3.amazonaws.com/0/111541/5a3b008b-c1d0-71b6-a782-1909fd16ec5b.png)

![preformanceMoniter2.png](https://qiita-image-store.s3.amazonaws.com/0/111541/c46e9b65-83d2-5600-32ef-ba6294df8bc5.png)

ただし、Network Interfaceは_Totalがありませんので上の図のようにネットワークごとの値しか取得できません。また、システムとしてみた場合、複数のネットワークの負荷を合計するのはあまり意味がないので、Measure-Object -Maximumを使って全インスタンスの中から最大値だけ取得しています。

## タスクスケジューラから起動するよう設定

タスクスケジューラから、powershell.exeを起動するよう登録し、「引数の追加」に次のように記述します。

> -command "c:\MongoDB\logging.ps1"

![task1.png](https://qiita-image-store.s3.amazonaws.com/0/111541/1900a2a2-74aa-e2b4-2329-6d6a9c52b0e4.png)

また、登録後にプロパティを開き、「ユーザーがログオンしているかどうかにかかわらず実行する」とか「最上位の特権で実行する」とか、運用に応じて設定しておきます。

![task2.png](https://qiita-image-store.s3.amazonaws.com/0/111541/dfc64fd7-fa76-3b61-ec04-59f6994e962d.png)

## 登録するドキュメントが一定時間で自動的に消えるように設定

このままだと永久にデータが増え続けてしまうので、一定期間経過後は自動的に消えるように設定しておきます。
最終的には1週間とか任意の期間にしたいですが、設定が有効か確認するため、まずは3分(180秒)にしておきます。

> mongo.exe
> use logs
> db.os.ensureIndex({"TimeStamp":1}, {expireAfterSeconds:180});

ここでは、osコレクションのTimeStamp項目にインデックスを張り、有効な時間を設定しています。
なお、MongoDBは標準時しか扱えないので、Date型は自前で.ToUniversalTime()とかしておかないと、これの動作もおかしなことになるので注意。

## 実行結果

登録したタスクを実行し、しばらくすると次のように過去3分だけデータが溜まっているのが確認できました。

![robomongo.png](https://qiita-image-store.s3.amazonaws.com/0/111541/bdde02b6-da64-b0f7-ec49-275f2d77bc87.png)

ここではデータの表示にRobomongoを使用しています。またテストのため、同じPowerShellを同時に別のマシンでも実行しています。

あとは、expireAfterSecondsを任意の期間(例えば、1日＝86400秒)に変更しておけばOKです。