Microsoft於日前釋出了Windows Azure Tools 1.2.......
管理你的Azure Roles - With Service Management API
文/黃忠成
關於Azure Service Management API
Azure已經試營運,據官方訊息,台灣地區於今年7月後將可加入試營運的行列,未來台灣地區的用戶,將可以透過試用或購買時數來建構自己的Azure服務。
對於Microsoft來說,Azure是一個極耗成本的嘗試,除了建構雲端的硬體設備外,軟體的開發耗費之巨,也是可以想像的。
除了改造Visual Studio來支援Azure開發外,Azure也是少數提供豐富的服務管理機制的雲端實作體,Azure提供了以REST為基礎的Service Management API,
透過這組API,開發者可以程式化部署、Scaling Out、Scaling Up和監測Azure服務的狀態,這能做什麼?很簡單,你可以寫程式把自己的Azure Project自動Deployment到Azure上,
不用透過Azure Portal!也可以寫程式監測Azure上的Role狀態,諸如Deploying、Running、Ready、Busy皆可獲知,透過Diagnostics資訊,你甚至可以在Azure服務用量巔峰時,
自動進行Scaling Out,獲取雲端服務獨有的無限擴充優勢。
想像一下,當你使用傳統ISP來Hosting服務時,在用量顛峰時,除了做璧上觀外,頂多也只能夠等待看服務人員能不能發個好心接電話,然後多加一台Server來分擔流量,
這種情況在Azure將不復存在,透過Service Management API/Diagnostics等技術,你可以自己寫一個程式,動態的、透過流量及用戶量來添加、或是遞減服務所使用的
Instance(機器/VM)數量。
關於Windows Azure SDK/Tools 1.2
Microsoft於日前釋出了Windows Azure Tools 1.2,讀者們可由以下網址取得:
除了原本就有的Azure Project Template/Tools外,Microsoft這次更將Azure Monitor機制整合到了Server Explorer中,這意味著,
你將可以在Visual Studio 2010中直接以Server Explorer來觀測Azure服務的執行狀態。
圖1
在Windows Azure Storage上按滑鼠右鍵,將可添加你想觀測的Storage Account。
圖2
圖3
完成後,即可在Server Explorer上看到該Storage Account的狀態。
圖4
也可看到Table Storage中Table的資料。
圖4-1
透過簡單的Filter機制,可以查詢Table Storage中的資料。
圖4-2
關於Filter語法,可參照以下網址:
|
也可以查看或以Blob Name來查詢Blob Storage內的資料。
圖4-3
於Blob上按右鍵可以將其內容開啟或是儲存。
圖4-4
圖4-5是開啟Blob中圖形的截圖。
圖4-5
不過相較於我們常用的Azure Storage Explorer工具,這個工具僅提供觀測,並無法對Storage Account中的Blobs/Tables/Queue做變動
的動作,簡單說就是唯讀。
Azure Storage Explorer 可於下列網址取得:
http://azurestorageexplorer.codeplex.com/
|
在Windows Azure Compute上按滑鼠右鍵,則可以添加對Azure Account的監測。
圖5
圖6
首次執行時,你必須在【Windows Azure Accounts】上按滑鼠右鍵來鍵入Azure Account資訊。
圖7
圖8
看到這個畫面時,如果玩過Azure Service Management API的人,一定會露出會心的一笑,以前要玩Azure Service Management API,得先弄出一個
Certificate file(憑證檔案)來,這通常是透過IIS 7或是其它工具來產生,Windows Azure Tools 1.2直接把這動作整合了,你只要選【Create】即可(幹得好...)。
圖9
圖10
鍵入關於此certificate的易記名稱(你可自訂),按下OK後,Azure Tools即為你產生一個certificate。
圖11
接著按下畫面上的【Copy the full path】連結,這個certificate檔案的路徑就會被複製到剪貼簿中,然後再按下下方的【Developer Portal】
連結,開啟Azure Portal網站,點選正確的帳號後進入圖12的畫面。
圖12
接著點選Account頁籤,進入圖13的畫面。
圖13
OK,現在請點下畫面中的【Manage My API Certificates】連結,準備將certificate檔案上傳到Azure Portal上,這可以開啟存取Service Management API的權限。
圖14
那certificate檔案在那?之前點【Copy the full path】時,這個檔案的完整路徑已經在剪貼簿中了,現在只要按下【瀏覽】按紐,於檔名部份按下貼上即可。
圖15
圖16
按下開啟舊檔後回到先前的畫面,再按下【Upload】按紐,即可將此certificate檔案上傳到Azure Portal上。
圖17
一切無誤的話,你將可於下方看到此certificate。
圖18
最後請再點選上方的Account頁籤,將畫面中最下方的【Subscription ID】複製下來。
圖19
貼到Azure Tools的畫面下方。
圖20
最後給他取個名字吧。
圖21
按下OK後,即完成了此Azure Account的添加動作,現在你應可看到圖22的畫面。
圖22
當然,如果你的Azure Account中沒有建立任何Service的話,那麼這邊就是空的,你可嘗試到Azure Portal中建立新的Service,這裡就會有如圖22的畫面了。
於此可選擇是要顯示Staging|Production的狀態,此處我們選擇Production,按下OK後,可於Server Explorer中看到圖23畫面。
圖23
當然,這還是唯讀的,你無法於此刪除或建立某一Azure Hosting Service。
Deployment
Azure Tools 1.2將Deployment(部署)機制也加入了,現在不用透過Azure Portal,只要在Azure Project上按下【Publish】後,
即可將Azure Project部署到Azure上了,請先建立一個專案。
圖24
眼尖的你應該已經發現,Azure已經支援.NET Framework 4了,按下OK後添加一個Web Role,然後於專案上按右鍵
進行Publish部署動作。
圖25
選擇使用那一個certificate。
圖26
然後Publish Cloud Service會列出此certificate所繫結的Azure Account下所有的Service及Storage Service供選擇。
圖27
圖28
選定後按下OK,即進入部署階段,你可於下方看到部署過程。
圖29
部署後,Azure Tools即啟動你的Azure Service,也就是說直接就Run了,不用再去Azure Portal上按【Run】。
圖30
OK,現在換我犯嘀咕了,為何我選了certificate後,Azure Tools可以直接列出所有的Services及Storage account?即使我把Server Explorer
中關於Azure的東西都刪除,它還是可以列出?如果我沒猜錯的話,Azure Tools已經把Subscription ID及certificate、Azure Account資訊存下來了,
所以它才能直接列出,否則不管是取得certificate、List Hosted Services,都需要certificate及subscription id,沒這兩個資訊是不可能做到這些動作的。
或許你也會犯嘀咕,為何還要透過Azure Portal來上傳certificate file?Service Management API不是也有新增certificate file的API嗎?直接來不就好了?
很簡單,Service Management API所有動作都建立在certificate上,沒有certificate前,一切免談。
註:呃,上面這段是我自己犯嘀咕......讀者們可以略過,哈。
|
註:當你Deployment的Slot已經有東西存在時,Azure Tools會詢問是要刪除先前的Deployment。
圖31
看來目前尚未支援Upgrade。
|
IntelliTrace
當Azure Project部署到雲端後,你就無法再對其做Debug動作,Azure Tools 1.2為了減輕除錯的負擔,提供了IntelliTrace機制,這個機制會將Azure Project的執行過程
記錄下來,並提供一個報表給開發者,在這個報表中將包含該Azure Project執行所在電腦、例外、及Thread List等資訊,對於開發者而言,最重要的就是例外資訊了。
要使用IntelliTrace for Azure,你得先下載一個Hotfix:
安裝後即可於Publish窗中勾選Enable IntelliTrace。
圖32
完成部署後,即可於Server Explorer中取得IntelliTrace Log。
圖33
點選後,Azure Tools即開始下載IntelliTrace Log。
圖34
完成後,即顯示報表。
圖35
我們故意在程式中放入一個例外,看看IntelliTrace能不能在例外發生時記錄並顯示。
圖36
圖37
IntelliTrace Log資訊是存在你所提供的Storage Account中,當點選View IntelliTrace Log時,Azure Tools才由Storage Account內的Blob中取回資料。
當然,IntelliTrace能做到的不只如此,透過IntelliTrace Log,我們可以對Azure上的應用程式除錯,是的!你沒聽錯,是除錯(Debug)!不過這個除錯動作是模擬的,
IntelliTrace會把應用程式執行期間的資訊存在於Log中,透過這個Log,Visual Studio 2010可以將整個執行時期進行有限度的還原,讓開發者可以在程式未執行時,
進行模擬除錯。
請開啟IntelliTrace Log Summary,找到發生例外的地方,按下下方的Start Debuging按紐。
圖37-1
接著會來到圖37-2的畫面。
圖37-2
唔,有點熟悉是吧?你可以用F10、F11來進行單步除錯,但請注意,這是有限度的模擬而已。
看起來好像很有用的機制是吧?讓我們再用另一個例子,透過Exception Log,我們發現了有某段程式碼丟出了IO Exception。
圖37-3
單看這個Exception,實在很難知道問題發生在那!沒關係,透過IntelliTrace,只要於此按下Start Debugging,就會到達例外的發生地。
圖37-4
接著你可以按下【Ctrl+Shift+F11】、或是按下左方的向上按紐,會發現到除錯點往後退。
圖37-5
當退到函式的最開端時,你可以像除錯一般應用程式般,以F10、F11來進行單步,有趣的是,當使用F11時,Visual Studio 2010允許你Step Into至呼叫函式。
圖37-6
於圖37-6處按下F11,會跳到InitializeBlobStorage函式內部,繼續進行除錯。
圖37-7
OK,這樣看來,IntelliTrace確實可以減輕我們除錯Azure上應用程式的負擔。
Deployment Package via Service Management API
如果你曾經玩過Azure Service Management API的話,那麼Azure Tools 1.2所提供的功能中,除了IntelliTrace外,其實都不會對你造成震憾,
因為這些都是Service Management API本來就提供的東西。
註:Azure Service Management API說明可由下列網址取得,幾乎所有的Method都可以在Microsoft.Samples.WindowsAzure.ServiceManagement.dll中找到對應。
|
在Azure Tools 1.2之前,Service Management API僅提供REST API介面,並未正式提供Managed Code版本(OK,我說非正式,Managed Code
在Azure Tools 1.2中,這個非正式的Service Management API 之 Managed Code版本,出現在了Azure Tools 1.2的目錄中。
圖38
這一來,不用去Codeplex下載,我們直接就可以用這個Managed Code的Service Management API了。
現在開始建立第一個Service Management API的應用例子,我們一次到位,直接做one-click部署的機制,首先請建立一個Console Project,
接著加入Microsoft.Samples.WindowsAzure.ServiceManagement.dll、Microsoft.WindowsAzure.ServiceRuntime.dll、
Microsoft.WindowsAzure.StorageClient.dll、System.ServiceModel、System.Runtime.Serialization.dll為Reference。
圖39
記得Target Framework 要選.NET Framework 4。
圖40
要使用Service Management API,我們得先有一個certificate才行,Azure Tools 1.2其實已經幫我們產生了一個,拿來用即可,
然後透過Service Management API Managed Library來部署。
程式 1
Program.cs
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure;
using Microsoft.Samples.WindowsAzure.ServiceManagement;
using System.ServiceModel;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Security.Permissions;
using System.Security.Principal;
namespace ConsoleApplication1
{
class Program
{
private const string subscriberID = "<your subscriptioin id>";
private static void InitializeBlobStorage()
{
CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
{
configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
RoleEnvironment.Changed += (anotherSender, arg) =>
{
if (arg.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
.Any((change) => (change.ConfigurationSettingName == configName)))
{
if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
{
RoleEnvironment.RequestRecycle();
}
}
};
});
}
private static CloudStorageAccount InitizlieBlob()
{
CloudStorageAccount storageAccount = new CloudStorageAccount(
new StorageCredentialsAccountAndKey("code6421storage1", "<your storage access key"), true);
CloudBlobClient client = storageAccount.CreateCloudBlobClient();
// Get and create the container
CloudBlobContainer container = client.GetContainerReference("files");
container.CreateIfNotExist();
// Setup the permissions on the container to be public
var permissions = new BlobContainerPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
container.SetPermissions(permissions);
return storageAccount;
}
private static void UploadDeploymentPackageToBlob(IServiceManagement serviceManagement)
{
InitializeBlobStorage();
InitizlieBlob();
CloudStorageAccount storageAccount = InitizlieBlob();
CloudBlobClient client = storageAccount.CreateCloudBlobClient();
// Get and create the container
CloudBlobContainer container = client.GetContainerReference("files");
CloudBlob pkgBlob = container.GetBlobReference("DemoDiagnostics.cspkg");
client.WriteBlockSizeInBytes = 512 * 1024;
pkgBlob.UploadFile(@"c:\temp1\DemoDiagnostics.cspkg");
string reqId = null;
using (OperationContextScope scope = new OperationContextScope(
serviceManagement as IClientChannel))
{
serviceManagement.ListHostedServices(subscriberID);
serviceManagement.CreateOrUpdateDeployment(subscriberID, "code6421", "production",
new CreateDeploymentInput()
{
PackageUrl = pkgBlob.Uri,
Label = Convert.ToBase64String(Encoding.UTF8.GetBytes("Deployment 15")),
Configuration = Convert.ToBase64String(Encoding.UTF8.GetBytes(
String.Join("", File.ReadAllLines(@"c:\temp1\ServiceConfiguration.cscfg")))),
Name = "TestRSSWeb" //the name must be Service Label
});
object propertyValue;
if (OperationContext.Current.IncomingMessageProperties.TryGetValue(
"httpResponse", out propertyValue))
{
System.ServiceModel.Channels.HttpResponseMessageProperty response =
(System.ServiceModel.Channels.HttpResponseMessageProperty)propertyValue;
reqId = response.Headers["x-ms-request-id"];
}
}
while (true)
{
string status = serviceManagement.GetOperationStatus(subscriberID, reqId).Status;
Console.WriteLine("Processing...:" + status);
if (status == "Succeeded")
break;
System.Threading.Thread.Sleep(10000);
}
RunDelopment(serviceManagement);
}
private static void RunDelopment(IServiceManagement serviceManagement)
{
string reqId = string.Empty;
object propertyValue;
using (OperationContextScope scope = new OperationContextScope(
serviceManagement as IClientChannel))
{
serviceManagement.UpdateDeploymentStatus(subscriberID, "code6421", "TestRSSWeb",
new UpdateDeploymentStatusInput() {
Status = "Running" });
if (OperationContext.Current.IncomingMessageProperties.TryGetValue(
"httpResponse", out propertyValue))
{
System.ServiceModel.Channels.HttpResponseMessageProperty response =
(System.ServiceModel.Channels.HttpResponseMessageProperty)propertyValue;
reqId = response.Headers["x-ms-request-id"];
}
}
while (true)
{
string status = serviceManagement.GetOperationStatus(subscriberID, reqId).Status;
Console.WriteLine("Running...:" + status);
if (status == "Succeeded")
break;
System.Threading.Thread.Sleep(10000);
}
while (true)
{
Deployment deps = serviceManagement.GetDeploymentBySlot(subscriberID,
"code6421", "production");
bool isRunning = true;
foreach (var role in deps.RoleInstanceList)
{
Console.WriteLine(string.Format("Role {0} : {1}",
role.RoleName, role.InstanceStatus));
if (!role.InstanceStatus.Equals("Ready"))
isRunning = false;
}
if (isRunning)
break;
System.Threading.Thread.Sleep(10000);
}
}
private static X509Certificate2 GetX509Certificate2(String subjectName)
{
X509Certificate2 x509Certificate2 = null;
X509Store store = new X509Store("My", StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection x509Certificate2Collection =
store.Certificates.Find(X509FindType.FindBySubjectName, subjectName, false);
x509Certificate2 = x509Certificate2Collection[0];
}
finally
{
store.Close();
}
return x509Certificate2;
}
static void Main(string[] args)
{
var serviceManagment =
ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint",
GetX509Certificate2("Windows Azure Tools"));
UploadDeploymentPackageToBlob(serviceManagment);
Console.Read();
}
}
}
|
執行前還需修改app.config為下列版本:
<?xmlversion="1.0"?>
<configuration>
<system.serviceModel>
<bindings>
<webHttpBinding>
<bindingname="WindowsAzureServiceManagement_WebHttpBinding"closeTimeout="00:01:00"openTimeout="00:01:00"receiveTimeout="00:10:00"sendTimeout="00:01:00">
<readerQuotasmaxStringContentLength="1048576"maxBytesPerRead="131072"/>
<securitymode="Transport">
<transportclientCredentialType="Certificate"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<client>
<endpointname="WindowsAzureEndPoint"address="https://management.core.windows.net"binding="webHttpBinding"bindingConfiguration="WindowsAzureServiceManagement_WebHttpBinding"contract="Microsoft.Samples.WindowsAzure.ServiceManagement.IServiceManagement"/>
</client>
</system.serviceModel>
</configuration>
|
編譯後即可執行。
圖41
這個程式會先將Azure Package 上傳至Blob,然後透過Service Management API來部署,部署的動作是非同步的,所以此處我們使用Tracking API
來查詢部署情況,在部署完成後,此程式會再次透過Service Management API來啟動此Service,接著再透過Service Management API來查詢啟動的狀態。
這個程式告訴了我們Azure Tools 1.2玩的把戲,其實背後大多是Service Management API的功勞。
Dynamic Changing Role Instances(Scaling Out)
選用雲端技術中一個很重要的理由,那就是可以無限的延展,一台機器不夠用,可以快速延展至兩台,兩台不夠用,可以快速的延展至三台或四台。
Azure支援兩種延展(Scaling)策略,一是Scaling Up,意思是透過升級機器上的CPU數量來進行延展,二是Scaling Out,意指啟用其它同CPU數的機器,
讓原本只有一台機器的服務,晉升為使用兩台機器。
Scaling Up需要重新部署應用程式至Azure,所以使用上較為麻煩,Scaling Out則不需要,只要透過Service Management API即可動態的增減某個Role
的機器數量。
Scaling Out需要原來的Service Configuration檔案,此例將原來的Service Configuration放一份在temp1目錄下。
Program.cs
|
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;
using Microsoft.Samples.WindowsAzure.ServiceManagement;
using System.ServiceModel;
using System.Security.Cryptography.X509Certificates;
namespace ScalingOutSamples
{
public partial class Form1 : Form
{
internal static string _subscriberID = "<your subscription id>;
public Form1()
{
InitializeComponent();
}
private void ChangeInstaceCount(IServiceManagement serviceManagement)
{
string reqId = string.Empty;
object propertyValue;
using (OperationContextScope scope =
new OperationContextScope(serviceManagement as IClientChannel))
{
XDocument doc = XDocument.Load(@"c:\temp1\ServiceConfiguration.cscfg");
var result = (from s1 in doc.Descendants(XName.Get("Instances", "http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration"))
where
s1.Parent.Attribute("name").Value == "WebRole1"
select s1).FirstOrDefault();
result.Attribute("count").Value = textBox1.Text;
string finalContent = doc.ToString();
serviceManagement.ChangeConfigurationBySlot(Form1._subscriberID,
"code6421", "Production",
new ChangeConfigurationInput()
{
Configuration =
Convert.ToBase64String(Encoding.UTF8.GetBytes(finalContent))
});
if (OperationContext.Current.IncomingMessageProperties.TryGetValue("httpResponse",
out propertyValue))
{
System.ServiceModel.Channels.HttpResponseMessageProperty response =
(System.ServiceModel.Channels.HttpResponseMessageProperty)propertyValue;
reqId = response.Headers["x-ms-request-id"];
}
}
}
private static X509Certificate2 GetX509Certificate2(String subjectName)
{
X509Certificate2 x509Certificate2 = null;
X509Store store = new X509Store("My", StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection x509Certificate2Collection =
store.Certificates.Find(X509FindType.FindBySubjectName, subjectName, false);
x509Certificate2 = x509Certificate2Collection[0];
}
finally
{
store.Close();
}
return x509Certificate2;
}
private void button1_Click(object sender, EventArgs e)
{
var serviceManagment = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint",
GetX509Certificate2("Windows Azure Tools"));
ChangeInstaceCount(serviceManagment);
}
}
}
|
App.config
|
<?xmlversion="1.0"?>
<configuration>
<system.serviceModel>
<bindings>
<webHttpBinding>
<bindingname="WindowsAzureServiceManagement_WebHttpBinding"closeTimeout="00:01:00"openTimeout="00:01:00"receiveTimeout="00:10:00"sendTimeout="00:01:00" >
<readerQuotasmaxStringContentLength="1048576"maxBytesPerRead="131072"/>
<securitymode="Transport">
<transportclientCredentialType="Certificate"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<client>
<endpointname="WindowsAzureEndPoint"address="https://management.core.windows.net"binding="webHttpBinding"bindingConfiguration="WindowsAzureServiceManagement_WebHttpBinding"contract="Microsoft.Samples.WindowsAzure.ServiceManagement.IServiceManagement"/>
</client>
</system.serviceModel>
</configuration>
|
此例為Windows Form專案,Reference了Microsoft.Samples.WindowsAzure.ServiceManagement.dll、System.ServiceMode、
System.RunTime.Serialization。
當改變了TextBox上的數字並按下OK後,你會發現該Service正在啟動其它的Instances,而原來的Instance仍處於可用狀態。
圖42
圖43
當我們可以動態的改變Role所使用的機器數量時,Scaling Out便成了Azure的利器,你可以選擇使用日期(例如周一、五使用2台,六日使用4台),
或是透過Diagnostics來監測CPU用量來動態調整使用的機器數量。