© 2011 Microsoft Corporation
モバイルアプリをAzure で作る - 【データを保存する】
Windows Azure に MySQL を構築しよう(Part2)
「Windows Azure に MySQL を構築しよう(Part1)」(以下、Part1)では、MySQL と phpMyadmin を Windows Azure 上で動かしました。今回は Part1 で作成したソリューションを利⽤し、MySQL のレプリケーション(マスタ・スレーブ) 環境を構築します。レプリケーションは Part1 で作成した Web ロール(WebRole1)をマスタ、今回追加するワーカーロール(WorkerRole1) をスレーブとします。
では、はじめましょう。Visual Studio Web Developer 2010 Express を起動し、Part1 で作成したソリューションを 開いてください。
■クラスライブラリプロジェクトの作成
WebRole1 プロジェクトの WebRole.cs で⾏っている Windows Azure ドライブのマウントや MySQL サーバーのイン ストールなどの処理をクラスライブラリに移動し、WorkerRole1 からでも使えるように少し変更します。 まずは、新しいクラスライブラリプロジェクト(AzureMySQLLibrary)をソリューションに追加し、以下のコンポーネ ントを参照設定に追加します。 ・System.ServiceModel ・System.ServiceProcess ・Microsoft.WindowsAzure.CloudDrive ・Microsoft.WindowsAzure.ServiceRuntime ・Microsoft.WindowsAzure.StorageClient 次に MySQLRoleManager クラスを作成し、コードを追加します。 [MySQLRoleManager.cs] using System; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.ServiceModel; using System.ServiceProcess; using System.Text; using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.ServiceRuntime; using Microsoft.WindowsAzure.StorageClient;
© 2011 Microsoft Corporation
namespace AzureMySQLLibrary {
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public class MySQLRoleManager
{
private CloudStorageAccount StorageAccount; private CloudDrive AzureDrive;
private string MySQLPath;
private MySQLRoleManager() {
}
public static MySQLRoleManager GeMySQLRoleManager() {
MySQLRoleManager manager = new MySQLRoleManager();
manager.Initialize();
return manager; }
public void Start() {
string currentRoleName = string.Empty; // Windows Azure ドライブのマウント
CloudBlobClient blobClient = StorageAccount.CreateCloudBlobClient();
// 【#1】ロール名を取得しコンテナ名に設定 currentRoleName = RoleEnvironment.CurrentRoleInstance.Role.Name.ToLower(); CloudBlobContainer blobContainer = blobClient.GetContainerReference(currentRoleName); AzureDrive = StorageAccount.CreateCloudDrive(blobContainer.GetPageBlobReference("disk.vhd").Uri.ToString( ));
© 2011 Microsoft Corporation
// 【#2】Hosts とファイアウォールの設定 StringBuilder hosts = new StringBuilder(); foreach (var r in RoleEnvironment.Roles) {
var role = RoleEnvironment.Roles[r.Key]; string roleName = role.Name;
string ipAddr =
role.Instances[0].InstanceEndpoints["MySQL"].IPEndpoint.Address.ToString();
// ファイアウォールの設定
string arg = "advfirewall firewall add rule name=\"mysql\" dir=in action=allow program=\"{0}mysql\\bin\\mysqld.exe\" enable=yes remoteip={1}/32";
ExecuteCommand("netsh", string.Format(arg, driveLetter, ipAddr));
// Host に書き込む内容を作成
hosts.AppendFormat("{0} {1}\r\n", ipAddr, roleName); }
string path = Environment.SystemDirectory + @"\drivers\etc\hosts"; File.AppendAllText(path, hosts.ToString());
// 【#3】Blob からひな形を取得して MySQL の設定ファイル書き換え CloudBlockBlob blockBlob =
blobContainer.GetBlockBlobReference("my.template.ini"); string templateText = blockBlob.DownloadText();
string outPath = Path.Combine(driveLetter, @"mysql\my.ini");
var parameters = new NameValueCollection(); parameters["driveLetter"] = driveLetter;
string contents = SubstituteParameters(parameters, templateText); File.WriteAllText(outPath, contents);
// MySQL サービスのインストール
MySQLPath = Path.Combine(driveLetter, @"mysql\bin\mysqld.exe"); ExecuteCommand(MySQLPath, "--install mysql");
// MySQL サービスの開始
using (ServiceController sc = new ServiceController("mysql")) {
© 2011 Microsoft Corporation
sc.Start(); }
}
public void Stop() {
// MySQL サービスを停止
using (ServiceController sc = new ServiceController("mysql")) {
if (sc.Status == ServiceControllerStatus.StartPending) sc.Stop();
}
// MySQL サービスのアンインストール
ExecuteCommand(MySQLPath, "--remove mysql");
// Windows Azure ドライブのアンマウント AzureDrive.Unmount();
}
// 初期化
private void Initialize() { // 構成設定パブリッシャの初期設定 CloudStorageAccount.SetConfigurationSettingPublisher( (configName, configSettingPublisher) => { var connectionString = RoleEnvironment.GetConfigurationSettingValue(configName); configSettingPublisher(connectionString); } ); StorageAccount = CloudStorageAccount.FromConfigurationSetting("StorageConnectionString"); } // パラメータを書き換える
private string SubstituteParameters(NameValueCollection parameters, string input) {
© 2011 Microsoft Corporation
string output = input;
foreach (var parameter in parameters.AllKeys) {
output = output.Replace("{" + parameter + "}", parameters[parameter]); }
return output; }
// 指定したファイルを実⾏
private void ExecuteCommand(String filePath, String arg) {
var psi = new ProcessStartInfo(); psi.FileName = filePath;
psi.Arguments = arg;
psi.RedirectStandardOutput = true; psi.UseShellExecute = false; psi.CreateNoWindow = true;
using (var proc = new Process()) { proc.StartInfo = psi; proc.Start(); proc.WaitForExit(30000); } } } } Part1 の WebRole.cs とほぼ同じ処理となっていますが、以下の 3 点を変更しています。 ・【#1】ロールによって異なる VHD ファイルを使⽤するため、コンテナ名を固定からロール名を使うように変更 ・【#2】ロール間で通信を⾏うために Hosts を作成し、ファイアウォールの設定を追加
・【#3】ロールによって異なる MySQL の設定ファイルを使⽤するため、my.ini のひな形となるテキストを Blob から取得
□Web ロール プロジェクトの修正
Web ロールプロジェクト(WebRole1)を修正します。まずは、エンドポイントの設定を追加し、他のロールから MySQL に接続できるようにします。
© 2011 Microsoft Corporation (エンドポートの追加-Web ロール) 名前 :MySQL 種類 :Internal プロトコル :tcp プライベート ポート:3306
次に、先ほど作成した MySQLManager クラスを使うように AzureMySQLLibrary を参照設定に追加し WebRole.cs の コードを修正します。 [WebRole.cs] using Microsoft.WindowsAzure.ServiceRuntime; using AzureMySQLLibrary; namespace WebRole1 {
public class WebRole : RoleEntryPoint {
private MySQLRoleManager Manager = null; public override bool OnStart()
{
Manager = MySQLRoleManager.GeMySQLRoleManager(); Manager.Start();
return base.OnStart(); }
public override void OnStop() { Manager.Stop(); base.OnStop(); } } }
© 2011 Microsoft Corporation □ワーカーロール プロジェクトの作成 新しいワーカーロールプロジェクト(WorkerRole1)を追加します。 最初に、エンドポイント、ロールのランタイムの権限を管理者へ昇格、ストレージの接続⽂字列、の 3 つを設定します。 1.エンドポイントの設定 (エンドポートの追加-ワーカーロール) 名前 :MySQL 種類 :Internal プロトコル :tcp プライベート ポート:3306 2. ロールのランタイムの権限を管理者へ昇格 [ServiceDefinition.csdef] <WorkerRole name="WorkerRole1"> ・・・・省略・・・ <Runtime executionContext="elevated" /> </WorkerRole>
© 2011 Microsoft Corporation 3. ストレージの接続⽂字列(名前:StorageConnectionString) (ストレージアカウント接続⽂字列) 次に AzureMySQLLibrary を参照設定に追加し、Web ロールと同様にロール・インスタンスの起動時と終了時にコード を追加します。WorkerRole1 はこれで完了です。 [WorkerRole.cs] using System.Diagnostics; using System.Net; using System.Threading; using Microsoft.WindowsAzure.ServiceRuntime; using AzureMySQLLibrary; namespace WorkerRole1 {
public class WorkerRole : RoleEntryPoint {
private MySQLRoleManager Manager = null;
public override void Run() {
© 2011 Microsoft Corporation
// これはワーカーの実装例です。実際のロジックに置き換えてください。 Trace.WriteLine("WorkerRole1 entry point called", "Information");
while (true) { Thread.Sleep(10000); Trace.WriteLine("Working", "Information"); } }
public override bool OnStart() { // 同時接続の最大数を設定します ServicePointManager.DefaultConnectionLimit = 12; Manager = MySQLRoleManager.GetMySQLManager(); Manager.Start(); return base.OnStart(); }
public override void OnStop() { Manager.Stop(); base.OnStop(); } } } □MySQL の準備
最初に WorkerRole1 で使⽤する Windows Azure ドライブの VHD ファイルを⽤意します。今回は Part1 で使⽤した VHD ファイルをコピーして使います。CloudXplorer を起動して、コンテナ(workerrole1)を新しく作成し、Part1 で作 成した webrole1 コンテナにある disk.vhd をコピーして入れます。
次に MySQL の設定で使⽤する my.template.ini を作成し Windows Azure Storage にアップロードします。 my.template.ini はマスタ⽤とスレーブ⽤を作成し各コンテナにアップロードします。
© 2011 Microsoft Corporation マスタ⽤ my.template.ini(webrole1 コンテナ) [mysqld] basedir={driveLetter}mysql datadir={driveLetter}mysql\data innodb_data_home_dir={driveLetter}mysql\data port=3306 innodb_flush_method=normal log-bin server-id=10 スレーブ⽤ my.template.ini(workerrole1 コンテナ) [mysqld] basedir={driveLetter}mysql datadir={driveLetter}mysql\data innodb_data_home_dir={driveLetter}mysql\data port=3306 innodb_flush_method=normal server-id=11 (webrole1 コンテナ)
最後に、WorkerRole1 で動いている MySQL も管理できるように phpMyAdmin の設定を⾏います。phpMyAdmin フ ォルダの直下にある config.sample.inc.php をコピーして、config.inc.php を作成します。config.inc.php の Servers configuration に管理するサーバーの情報(下記)を追加します。 $i++; /* Authentication type */ $cfg['Servers'][$i]['auth_type'] = 'cookie'; /* Server parameters */ $cfg['Servers'][$i]['host'] = 'WorkerRole1'; $cfg['Servers'][$i]['connect_type'] = 'tcp'; $cfg['Servers'][$i]['compress'] = false;
© 2011 Microsoft Corporation
/* Select mysqli if your server has it */ $cfg['Servers'][$i]['extension'] = 'mysql'; $cfg['Servers'][$i]['AllowNoPassword'] = false;
/* rajk - for blobstreaming */
$cfg['Servers'][$i]['bs_garbage_threshold'] = 50; $cfg['Servers'][$i]['bs_repository_threshold'] = '32M'; $cfg['Servers'][$i]['bs_temp_blob_timeout'] = 600; $cfg['Servers'][$i]['bs_temp_log_threshold'] = '32M'; これで準備完了です。ビルドして問題がなければデプロイします。デプロイが終わったらレプリケーションの設定を⾏ います。 □レプリケーションの設定 マスタとなる WebRole1 にリモートデスクトップで接続し、mysql コンソールアプリケーションを使いスレーブユーザ ーアカウント(repl)を作成します。
GRANT REPLICATION SLAVE ON *.* TO repl@'WorkerRole1' IDENTIFIED BY 'password';
次に、スレーブとなる WorkerRole1 にリモートデスクトップで接続し、同じく mysql コンソールアプリケーションを 使い、レプリケーションの設定を⾏って開始します。
CHANGE MASTER TO MASTER_HOST='WebRole1',MASTER_PORT=3306,MASTER_USER='repl', MASTER_PASSWORD='password';
SLAVE START;
※WorkerRole1 の MySQL が起動しない場合や Windows Azure ドライブのマウントが失敗する場合は、WebRole1 と同 じように、コピーではなくローカルで VHD ファイルを作成してアップロードし、再度デプロイしてみてください。
これでレプリケーションの設定は完了です。マスタが更新されるとスレーブにその更新内容が伝わり、同期されます。 最後に、WebRole1 の phpMyAdmin から WorkerRole1 の MySQL も管理できるようにユーザーを追加しておきます。
GRANT ALL PRIVILEGES ON *.* TO root@'WebRole1' IDENTIFIED BY "P@ssw0rd";
今回は、Part1 で作成した MySQL の実⾏環境を使った、MySQL のレプリケーションについて解説しました。 Windows Azure では IP アドレスを固定する機能はありませんし、標準では名前を使って特定のインスタンスを指定す ることもできません。このため、MySQL 以外の汎⽤的なアプリケーションやサービスを使うシナリオでも、Hosts やファ イアウォールの考え方は有⽤です。ぜひご活⽤ください。