如何以编程方式管理 Azure IaaS






4.75/5 (4投票s)
学习如何使用 C# 代码管理 Windows Azure 虚拟机 (IaaS)。
引言
本文档介绍了与 IaaS(基础设施即服务)相关的编程主题,这些主题以 Windows Azure 虚拟机(及其相关的虚拟磁盘和虚拟网络等资源)的形式提供。您知道 Windows Azure 最初是一个 PaaS 云平台,但考虑到一些需要对虚拟机进行完全控制的业务场景,Windows Azure 便转向提供 IaaS。
有时,出于以下一个或多个原因,我们需要通过代码管理 Windows Azure 虚拟机 (IaaS)
- 通过提供到 Windows Azure 虚拟机的突发连接器来处理超融合系统
- 提供消耗 Windows Azure 虚拟机的多租户系统
- 在您的本地或云服务上自动执行需要利用某些虚拟资源的过程
我们将使用 C# 代码实现以下基本操作
- 列出镜像
- 创建虚拟机
- 列出虚拟机
- 重启虚拟机
- 删除虚拟机
在开始实现上述操作之前,我们需要准备客户端和 Windows Azure 订阅,以便通过提供管理证书(x.509 v3 证书)进行正确通信。该证书允许客户端访问您的 Windows Azure 订阅中的资源,而使用 Windows Azure 服务管理 REST API 发出的请求则需要针对您提供给 Windows Azure 的证书进行身份验证。
有关设置管理证书的更多信息,请访问此处。要在其他客户端计算机上安装 .cer 文件,您需要 .pfx 文件;如果 .pfx 文件不存在,则需要将 .cer 文件导出为 .pfx 文件。
注意:您需要在计算机上安装 .net 4.5 才能尝试此代码。
使用代码
此处使用的基本 C# 类对象是用于访问 Azure IaaS 服务的 REST API 的客户端 HttpClient(提供发送 HTTP 请求和接收来自 URI 标识的资源的 HTTP 响应的基类)。此对象必须使用所需数据进行初始化,例如证书、标头和内容(如果需要)。
我还想在此提及,代码基于使用 异步编程 来调用 Azure,这可以提高性能,并使我们能够处理需要多个子调用才能完成某些操作的复杂调用。
以下代码解释了如何获取证书并使用所需的标头和内容等数据初始化 HttpClient 对象。
HttpClient GetHttpClient()
{
X509Store certificateStore = null;
X509Certificate2 certificate = null;
try
{
certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certificateStore.Open(OpenFlags.ReadOnly);
string thumbprint = ConfigurationManager.AppSettings["CertThumbprint"];
var certificates = certificateStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
if (certificates.Count > 0)
{
certificate = certificates[0];
}
}
finally
{
if (certificateStore != null) certificateStore.Close();
}
WebRequestHandler handler = new WebRequestHandler();
if (certificate!= null)
{
handler.ClientCertificates.Add(certificate);
HttpClient httpClient = new HttpClient(handler);
//And to set required headers lik x-ms-version
httpClient.DefaultRequestHeaders.Add("x-ms-version", "2012-03-01");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
return httpClient;
}
return null;
}
我们将 httpClient 对象作为引用对象,用于调用 Windows Azure REST API IaaS 服务。对于每个请求操作,我们需要定义:
- 请求 URI
- HTTP 方法
- 标头
- 内容正文
(1) 列出镜像
List<String> imageList = new List<String>();
//replace _subscriptionid with your WA subscription
String uri = String.Format("https://management.core.windows.net/{0}/services/images", _subscriptionid);
HttpClient http = GetHttpClient();
Stream responseStream = await http.GetStreamAsync(uri);
if (responseStream != null)
{
XDocument xml = XDocument.Load(responseStream);
var images = xml.Root.Descendants(ns + "OSImage").Where(i => i.Element(ns + "OS").Value == "Windows");
foreach (var image in images)
{
string img = image.Element(ns + "Name").Value;
imageList.Add(img);
}
}
有关 REST 调用(请求/响应)的更多信息,请访问此链接:http://msdn.microsoft.com/en-us/library/windowsazure/jj157191.aspx
(2) 创建虚拟机
创建虚拟机需要先创建服务和部署,因此,如果尚未创建托管服务和部署,则创建 VM 应分三个步骤完成:
- 创建托管服务,它是 Windows Azure 中服务部署的容器。一个订阅可以有零个或多个托管服务。
- 创建部署,即在 Windows Azure 上运行的服务。部署可以运行在暂存或生产部署环境中。它可以由其部署 ID 或其运行的部署环境进行管理。
- 创建虚拟机,此步骤需要前两个步骤的信息。
我建议在此处使用相同的名称来命名服务、部署和虚拟机,以便于从代码管理虚拟机。
注意:托管服务的名称在 Windows Azure 中必须是唯一的。该名称是 DNS 前缀名称,可用于访问托管服务。例如:http://ServiceName.cloudapp.net//
2.1 创建服务
请求 URI:
https://management.core.windows.net/<subscription-id>/services/hostedservices
HTTP 方法:
POST (HTTP 1.1)
标头:
x-ms-version: 2012-03-01
Content-Type: application/xml
正文:
有关请求正文(和其他信息)的更多详细信息,请访问 http://msdn.microsoft.com/en-us/library/windowsazure/gg441304.aspx
//The following method show how to create hosted service
async public Task<String> NewAzureCloudService(String ServiceName, String Location, String AffinityGroup, String subscriptionid)
{
String requestID = String.Empty;
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices", subscriptionid);
HttpClient http = GetHttpClient();
System.Text.ASCIIEncoding ae = new System.Text.ASCIIEncoding();
byte[] svcNameBytes = ae.GetBytes(ServiceName);
String locationEl = String.Empty;
String locationVal = String.Empty;
if (String.IsNullOrEmpty(Location) == false)
{
locationEl = "Location";
locationVal = Location;
}
else
{
locationEl = "AffinityGroup";
locationVal = AffinityGroup;
}
XElement srcTree = new XElement("CreateHostedService",
new XAttribute(XNamespace.Xmlns + "i", ns1),
new XElement("ServiceName", ServiceName),
new XElement("Label", Convert.ToBase64String(svcNameBytes)),
new XElement(locationEl, locationVal)
);
ApplyNamespace(srcTree, ns);
XDocument CSXML = new XDocument(srcTree);
HttpContent content = new StringContent(CSXML.ToString());
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml");
HttpResponseMessage responseMsg = await http.PostAsync(uri, content);
if (responseMsg != null)
{
requestID = responseMsg.Headers.GetValues("x-ms-request-id").FirstOrDefault();
}
return requestID;
}
//The following method show how to create hosted service deployment
async public Task<String> NewAzureVMDeployment(String ServiceName, String VMName, String VNETName, XDocument VMXML, XDocument DNSXML)
{
String requestID = String.Empty;
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments", _subscriptionid, ServiceName);
HttpClient http = GetHttpClient();
XElement srcTree = new XElement("Deployment",
new XAttribute(XNamespace.Xmlns + "i", ns1),
new XElement("Name", ServiceName),
new XElement("DeploymentSlot", "Production"),
new XElement("Label", ServiceName),
new XElement("RoleList", null)
);
if (String.IsNullOrEmpty(VNETName) == false)
{
srcTree.Add(new XElement("VirtualNetworkName", VNETName));
}
if(DNSXML != null)
{
srcTree.Add(new XElement("DNS", new XElement("DNSServers", DNSXML)));
}
XDocument deploymentXML = new XDocument(srcTree);
ApplyNamespace(srcTree, ns);
deploymentXML.Descendants(ns + "RoleList").FirstOrDefault().Add(VMXML.Root);
String fixedXML = deploymentXML.ToString().Replace(" xmlns=\"\"", "");
HttpContent content = new StringContent(fixedXML);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml");
HttpResponseMessage responseMsg = await http.PostAsync(uri, content);
if (responseMsg != null)
{
requestID = responseMsg.Headers.GetValues("x-ms-request-id").FirstOrDefault();
}
return requestID;
}
2.3 创建虚拟机
请求 URI:
https://management.core.windows.net/<subscription-id>/services/hostedservices/<cloudservice-name>/deployments/<deployment-name>/roles
<cloudservice-name> 和 <deployment-name> 是从前几步提供的输入。
HTTP 方法:
POST (HTTP 1.1)
标头:
x-ms-version: 2012-03-01
Content-Type: application/xml
正文:
有关请求正文(和其他信息)的更多详细信息,请访问 http://msdn.microsoft.com/en-us/library/windowsazure/jj157186.aspx
async public Task<String> NewAzureVM(String ServiceName, String VMName, XDocument VMXML)
{
String requestID = String.Empty;
String deployment = await GetAzureDeploymentName(ServiceName);
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments/{2}/roles", _subscriptionid, ServiceName, deployment);
HttpClient http = GetHttpClient();
HttpContent content = new StringContent(VMXML.ToString());
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml");
HttpResponseMessage responseMsg = await http.PostAsync(uri, content);
if (responseMsg != null)
{
requestID = responseMsg.Headers.GetValues("x-ms-request-id").FirstOrDefault();
}
return requestID;
}
(3) 列出虚拟机
要列出托管在 Windows Azure 订阅中的虚拟机,我们需要遍历所有托管服务以获取其托管的虚拟机。
为此,我们需要执行以下操作:
- 列出托管服务
- 列出每个托管服务的虚拟机
3.1 列出托管服务
请求 URI:
https://management.core.windows.net/<subscription-id>/services/hostedservices
HTTP 方法:
GET (HTTP 1.1)
标头:
x-ms-version: 2012-03-01
正文:
无。
有关此 HTTP 请求的更多信息,请访问此链接:http://msdn.microsoft.com/en-us/library/windowsazure/ee460781.aspx
async private Task<List<XDocument>> GetAzureServices(String subscriptionid)
{
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices ", subscriptionid);
List<XDocument> services = new List<XDocument>();
HttpClient http = GetHttpClient();
Stream responseStream = await http.GetStreamAsync(uri);
if (responseStream != null)
{
XDocument xml = XDocument.Load(responseStream);
var svcs = xml.Root.Descendants(ns + "HostedService");
foreach (XElement r in svcs)
{
XDocument vm = new XDocument(r);
services.Add(vm);
}
}
return services;
}
3.2 列出托管服务的虚拟机
请求 URI:
https://management.core.windows.net/<subscription-id>/services/hostedservices/<service-name>/deployments/<deployment-name>/roles/<role-name>
HTTP 方法:
GET (HTTP 1.1)
标头:
x-ms-version: 2012-03-01
正文:
无。
有关此 HTTP 请求的更多信息,请访问:http://msdn.microsoft.com/en-us/library/windowsazure/jj157193.aspx
async public Task<XDocument> GetAzureVM(String ServiceName, String VMName, String subscriptionid)
{
String deployment = await GetAzureDeploymentName(ServiceName);
XDocument vmXML = new XDocument();
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments/{2}/roles/{3}",
subscriptionid, ServiceName, deployment, VMName);
HttpClient http = GetHttpClient();
Stream responseStream = await http.GetStreamAsync(uri);
if (responseStream != null)
{
vmXML = XDocument.Load(responseStream);
}
return vmXML;
}
因此,可用于列出所有虚拟机的最终方法是:
async public Task<XDocument> GetAzureVMs()
{
List<XDocument> services = await GetAzureServices();
XDocument vms = new XDocument();
vms.Add(new XElement("VirtualMachines"));
ApplyNamespace(vms.Root, ns);
foreach (var svc in services)
{
string ServiceName = svc.Root.Element(ns + "ServiceName").Value;
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deploymentslots/{2}", _subscriptionid, ServiceName, "Production");
try
{
HttpClient http = GetHttpClient();
Stream responseStream = await http.GetStreamAsync(uri);
if (responseStream != null)
{
XDocument xml = XDocument.Load(responseStream);
var roles = xml.Root.Descendants(ns + "RoleInstance");
foreach (XElement r in roles)
{
XElement svcnameel = new XElement("ServiceName", ServiceName);
ApplyNamespace(svcnameel, ns);
r.Add(svcnameel); // not part of the roleinstance
vms.Root.Add(r);
}
}
}
catch (HttpRequestException http)
{
// no vms with cloud service
}
}
return vms;
}
(4) 重启虚拟机
请求 URI:
https://management.core.windows.net/<subscription-id>/services/hostedservices/<service-name>/deployments/<deployment-name>/roles/<role-name>/Operations
POST (HTTP 1.1)
Content-Type: application/xml
有关此 HTTP 请求的更多详细信息,请访问:http://msdn.microsoft.com/en-us/library/windowsazure/jj157197.aspx
async public Task<String> RebootVM(String ServiceName, String RoleName)
{
String requestID = String.Empty;
String deployment = await GetAzureDeploymentName(ServiceName);
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments/{2}/roleInstances/{3}/Operations",
_subscriptionid, ServiceName, deployment, RoleName);
HttpClient http = GetHttpClient();
XElement srcTree = new XElement("RestartRoleOperation",
new XAttribute(XNamespace.Xmlns + "i", ns1),
new XElement("OperationType", "RestartRoleOperation")
);
ApplyNamespace(srcTree, ns);
XDocument CSXML = new XDocument(srcTree);
HttpContent content = new StringContent(CSXML.ToString());
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml");
HttpResponseMessage responseMsg = await http.PostAsync(uri, content);
if (responseMsg != null)
{
requestID = responseMsg.Headers.GetValues("x-ms-request-id").FirstOrDefault();
}
return requestID;
}
(5) 删除虚拟机
您可以通过删除其部署来删除托管的虚拟机,但我建议也删除其托管服务,这样可以更轻松地从代码管理虚拟机。
5.1 删除部署
请求 URI:
https://management.core.windows.net/< subscription-id >/services/hostedservices/< service-name >/deployments/<Deployment-Name>
HTTP 方法:
DELETE (HTTP 1.1)
标头:
x-ms-version: 2012-03-01
正文:
无。
async public Task<HttpResponseMessage> DeleteDeployment( string deploymentName)
{
string xml = string.Empty;
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments/{2}", _subscriptionid, deploymentName, deploymentName);
HttpClient http = GetHttpClient();
HttpResponseMessage responseMessage = await http.DeleteAsync(uri);
return responseMessage;
}
5.2 删除托管服务
请求 URI:
https://management.core.windows.net/<subscription-id>/services/hostedservices/<service-name>
HTTP 方法:
DELETE (HTTP 1.1)
标头:
x-ms-version: 2012-03-01
正文:
无。
async public Task<HttpResponseMessage> DeleteService(string serviceName)
{
string xml = string.Empty;
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}", _subscriptionid, serviceName);
Log.Info("Windows Azure URI (http DELETE verb): " + uri, typeof(VMManager));
HttpClient http = GetHttpClient();
HttpResponseMessage responseMessage = await http.DeleteAsync(uri);
return responseMessage;
}
以下是可用于删除部署和服务的方法:
async public Task<string> DeleteVM(string vmName)
{
string responseString = string.Empty;
// as a convention here in this post, a unified name used for service, deployment and VM instance to make it easy to manage VMs
HttpClient http = GetHttpClient();
HttpResponseMessage responseMessage = await DeleteDeployment(vmName);
if (responseMessage != null)
{
string requestID = responseMessage.Headers.GetValues("x-ms-request-id").FirstOrDefault();
OperationResult result = await PollGetOperationStatus(requestID, 5, 120);
if (result.Status == OperationStatus.Succeeded)
{
responseString = result.Message;
HttpResponseMessage sResponseMessage = await DeleteService(vmName);
if (sResponseMessage != null)
{
OperationResult sResult = await PollGetOperationStatus(requestID, 5, 120);
responseString += sResult.Message;
}
}
else
{
responseString = result.Message;
}
}
return responseString;
}
参考文献
Windows Azure 服务管理 REST API 参考
使用 Async 和 Await 进行异步编程(C# 和 Visual Basic)