Azure Media Services アプリケーションの監視
このポストは、7 月 30 日に投稿した Monitoring Azure Media Services Application の翻訳です。
監視は、オンラインの生産性アプリケーションの重要なコンポーネントです。監視システムを適切に実装すれば、主要なイベントに関する警告を受け取り、適切に対応することができます。今回の記事では、Media Services アカウントを監視するために私が作成したサンプル コードをご紹介します。Media Services 上で構築されたアプリケーション用に独自のカスタム監視システムを計画するたたき台として、このサンプル コードをご活用ください。
サンプル コード
大まかに言うと、このサンプル コードは、サービスに対してあるコードを一定間隔で実行します。その後、処理の結果をログに記録し、別のコードによってログに失敗が含まれるかどうかを定期的に確認します。プログラムに定義した、障害の発生と見なす条件に達した場合は警告を出力します。これを踏まえて、コードは 2 つの Visual Studio プロジェクトに分けられています。1 つ目のプロジェクトでは、Media Services アセットを作成してから削除する API を一定間隔で呼び出し、結果を Azure 内の Table に記録します。2 つ目のプロジェクトでは、その Table の上位 5 件のエントリに失敗が含まれるかどうかを一定間隔で確認し、5 件すべてが失敗と記録されている場合に、警告を出力します。
1 つ目のプロジェクトの App.config は次のようになります。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<appSettings>
<add key="MediaServicesAccountName" value="<MediaAccountName>" />
<add key="MediaServicesAccountKey" value="<MediaAccountKey>" />
<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=<StorageAccountName>;AccountKey=<StorageAccountKey>" />
</appSettings>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
<MediaAccountName> と <MediaAccountKey> の部分は、監視対象の Media Services アカウントの名前とキーに置き換えてください。同様に、<StorageAccountName> と <StorageAccountKey> の部分は、データを記録するストレージ アカウントの名前とキーに置き換えます。
監視を実行するコードは次のようになります。先ほど述べたとおり、コードは無限ループで実行され、1 分おきに Media Services API を呼び出してアセットを作成してから削除します。API の呼び出しに失敗した場合は Table に失敗エントリを記録し、それ以外の場合は成功エントリを記録します。使用している関数については、コードの後で説明します。
using System;
using System.Linq;
using System.Configuration;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Table;
using Microsoft.WindowsAzure.MediaServices.Client;
namespace Monitoring
{
/// <summary>
///
/// </summary>
public class AssetLogEntity : TableEntity
{
public int Status { get; set; }
public string FailureData { get; set; }
}
/// <summary>
///
/// </summary>
class Program
{
// App.config ファイルから値を読み込む
private static readonly string _mediaServicesAccountName =
ConfigurationManager.AppSettings["MediaServicesAccountName"];
private static readonly string _mediaServicesAccountKey =
ConfigurationManager.AppSettings["MediaServicesAccountKey"];
private static readonly string _storageConnectionString =
ConfigurationManager.AppSettings["StorageConnectionString"];
private static CloudStorageAccount _cloudStorage = null;
private static CloudTableClient _tableClient = null;
private static CloudTable _monitoringTable = null;
// サービスのコンテキストのフィールド
private static CloudMediaContext _context = null;
private static MediaServicesCredentials _cachedCredentials = null;
/// <summary>
///
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
try
{
// Media Services の認証情報を静的クラス変数の形で作成しキャッシュする
_cachedCredentials = new MediaServicesCredentials(
_mediaServicesAccountName,
_mediaServicesAccountKey);
// キャッシュされた Media Services の認証情報を使用して CloudMediaContext を作成する
_context = new CloudMediaContext(_cachedCredentials);
_cloudStorage = CloudStorageAccount.Parse(_storageConnectionString);
_tableClient = _cloudStorage.CreateCloudTableClient();
_monitoringTable = _tableClient.GetTableReference("MonitoringData");
_monitoringTable.CreateIfNotExists();
Monitor();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
/// <summary>
///
/// </summary>
static void Monitor()
{
try
{
while (true)
{
try
{
Console.WriteLine("Starting Monitoring loop: " + DateTime.Now.ToString());
IAsset _asset = _context.Assets.Create("Monitoring Asset", AssetCreationOptions.None);
if (_asset == null)
{
LogMonitoringData(1, "Create Asset returned null");
}
else
{
_asset.Delete();
}
LogMonitoringData(0);
}
catch (Exception x)
{
Console.WriteLine(x.Message);
LogMonitoringData(1, x.Message);
}
Console.WriteLine("Going to sleep for a minute");
Console.WriteLine("");
Thread.Sleep(1000 * 60);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
/// <summary>
///
/// </summary>
static void LogMonitoringData(int status, string _failureData = "")
{
try
{
AssetLogEntity _assetLogEntity = new AssetLogEntity();
_assetLogEntity.PartitionKey = "Asset";
_assetLogEntity.RowKey = (DateTime.MaxValue.Ticks - DateTime.Now.Ticks).ToString("D12");
_assetLogEntity.Status = status;
_assetLogEntity.FailureData = _failureData;
TableOperation op = TableOperation.Insert(_assetLogEntity);
_monitoringTable.Execute(op);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
コードは比較的シンプルなものです。簡単にご説明します。
- AssetLogEntity – テーブル エンティティを定義します。上記のコードでは、成功した場合には Status に 0 を、失敗した場合には Status に 1 を記録します。Status が 1 の場合は、FailureData によって失敗メッセージが書き出されます。
- Main – Media Services アカウントのコンテキストを作成します。次に、MonitoringData テーブルがまだ存在しない場合には作成します。最後に Monitor 関数を呼び出します。
- Monitor – これが監視を実行するメインのコードです。この関数では While 文を使用した無限ループを実行します。1 分おきに Media Services アカウントのコンテキストを使用してアセットを作成してから削除します。LogMonitoringData 関数を呼び出して、API が成功した場合は成功エントリを記録し、それ以外の場合は失敗エントリを記録します。
- LogMonitoringData – 実際にデータを MonitoringData テーブルに書き込むタスクを実行します。最新のログ項目が上位に来るように、最大の DateTime 値から現在の DateTime を引いた Rowkey を使用します。これは、Azure Tables では項目が昇順に並べ替えられるためです。
2 つ目のプロジェクトの App.config は次のようになります。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<appSettings>
<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=<StorageAccountName>;AccountKey=<StorageAccountKey>" />
</appSettings>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
構成ファイルは 1 つ目のプロジェクトと似ていますが、Media Services アカウントの認証情報が含まれていない点が異なります (このプロジェクトでは MonitoringData テーブルのエントリのみを処理するため)。次のコードでは無限ループを実行し、失敗エントリが 5 件連続しているかどうかを 1 分おきに確認します。該当する場合は、警告を出力します。コードは次のようになります。その後でコード内の関数について簡単に説明します。
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Alerts
{
/// <summary>
///
/// </summary>
public class AssetLogEntity : TableEntity
{
public int Status { get; set; }
public string FailureData { get; set; }
}
class Program
{
private static readonly string _storageConnectionString =
ConfigurationManager.AppSettings["StorageConnectionString"];
private static CloudStorageAccount _cloudStorage = null;
private static CloudTableClient _tableClient = null;
private static CloudTable _monitoringTable = null;
/// <summary>
///
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
try
{
_cloudStorage = CloudStorageAccount.Parse(_storageConnectionString);
_tableClient = _cloudStorage.CreateCloudTableClient();
_monitoringTable = _tableClient.GetTableReference("MonitoringData");
_monitoringTable.CreateIfNotExists();
CheckforFailures();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
/// <summary>
///
/// </summary>
static void CheckforFailures()
{
while (true)
{
try
{
Console.WriteLine("Starting failure checking loop " + DateTime.Now.ToString());
TableQuery<AssetLogEntity> query = new TableQuery<AssetLogEntity>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "Asset"));
query.Take(5);
TableQuerySegment<AssetLogEntity> tqs = _monitoringTable.ExecuteQuerySegmented(query, null);
if ((tqs != null) && (tqs.Results != null))
{
if (tqs.Results.Count == 5)
{
bool _fireAlert = true;
for (int i = 0; i < tqs.Results.Count; i++)
{
if (tqs.Results[i].Status == 0)
{
_fireAlert = false;
break;
}
}
if (_fireAlert)
{
Console.WriteLine("More than 5 consecutive failures detected");
}
}
}
Console.WriteLine("Going to sleep for a minute");
Console.WriteLine("");
Thread.Sleep(1000 * 60);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
}
コードを簡単に説明します。
- AssetLogEntity – 1 つ目のプロジェクトと同様です。
- Main – MonitoringData テーブルへの参照を作成してから、CheckforFailures 関数を呼び出します。
- CheckforFailures – 無限ループを実行します。MonitoringData テーブルの上位 5 件のエントリを 1 分おきに取得し、5 件のエントリすべてが失敗であるかどうかを確認します。該当する場合は、画面に警告メッセージを出力します。
失敗する場合をシミュレーションするには、Azure 管理ポータルにアクセスし、Media Services アカウントに関連付けられたストレージ アカウントのキーを再生成します。その結果、Media Services アカウントから、アセットを作成するコンテナーのストレージ アカウントにアクセスできなくなり、1 つ目のプロジェクト内のアセットを作成する呼び出しに失敗します。5 分以上待ってから Azure 管理ポータルに再度アクセスし、[synchronize primary key] ボタンをクリックしてストレージ キーを同期します (以下のスクリーンショットを参照)。
数分後には Media Services アカウントが再び有効になります。次のスクリーンショットは、MonitoringData テーブルのエントリです。赤枠で囲まれているのは、失敗のエントリです。
次のスクリーンショットは、上記の 2 つのサンプル コード プロジェクトに関連付けられたコンソール ウィンドウです。両方のスクリーンショットを見ると、失敗が続いた時間がわかります。
考慮事項
最後に、このトピックに関する注意点を挙げます。
- 今回、MonitoringData テーブルの作成には、Media Services アカウントに関連付けられたストレージ アカウントを使用しませんでした。実際には、別のデータセンターのストレージ アカウントを使用しています。上記のサンプルでは、Media Services アカウント自体に関連した障害の状況のみをシミュレーションしましたが、ネットワークの問題またはメディア アプリケーションをホストしているデータセンターの Storage サービスの問題に関連した障害を捕捉する場合には、監視データを別の場所に書き込んだ方が良いでしょう。監視および警告を行うコードを実行する場所についても同じことが言えます。上記の 2 つのサンプル コードを Media Services アカウントがあるデータセンターの外部で実行することを推奨します。
- 監視の頻度と警告を出力する条件は慎重に決定してください。頻度が高すぎたり、多くの場合に該当する条件を設定したりすると、誤検知が多発します。
- 優れた設計の監視システムを構築することで、複数の Azure データセンターの複数のインスタンスと共に実行されている高可用性アプリケーションを開発する際に、フェールオーバーおよびフェールバックを自動化することができます。
- 上記のサンプルでは、Media Services アカウント自体を監視しています。Media Services 上で構築されたアプリケーションを監視する場合には、同様の原理を検討することをお勧めします。
- また、上記のサンプルと同様の原理を活用して、アプリケーションが利用する他の Azure サービスを監視することも可能です。