预测:多云

Windows Azure 中基于性能的缩放

Joseph Fultz

下载代码示例

毫无疑问,云计算受到了众多关注,其实际应用正在成为各种技术平台和整个业内的推动力。云计算并非一个全新的或革命性的概念;实际上,它已经以共享托管和其他类似服务的形式存在了多年。而现如今,随着技术的进步以及人们在服务器和服务运行上积累的多年经验,使得云计算不单具有技术可行性,同时也使越来越多的消费者和提供商对其产生了兴趣。

云计算方面的发展将会超出 IT 行业的范畴,在公司中,从硬件和服务管理人员,到开发人员和架构师,乃至于负责审核预算和结算的高级管理人员,云计算将无所不在。现在是时候进行准备了。

本期专栏主要面向那些需要在工作中了解和利用云计算的开发人员和架构师。本文将针对如何完成特定任务提供一些指南,这包括有关架构的注意事项及其对成本和性能的影响。希望读者能提供对于本文所涉及主题的反馈,更重要的是,告诉我您特别关注云计算的哪些主题。

播云

对于云计算,人们最关注的优点之一是,应用程序所有者不必为基础结构的安装、配置或维护操心。实事求是的说,这的确吸引眼球。

但是我认为,更应当关注的是根据应用程序所有者的需求来扩大或缩小规模的能力,从而不以牺牲性能或浪费资源为代价来创造更高效的成本模型。根据我的经验,不论所讨论的平台如何,所有关于云计算的话题都会涉及到需求弹性问题。

在这一部分中,我将演示使用运行角色的性能计数器数据来实现特定 Web 角色实例数的自动增减过程的方法。为此,我将介绍 Windows Azure 功能中最具代表性的一部分,包括 Windows Azure Compute、Windows Azure Storage 和 REST 管理 API。

概念非常简单:根据阈值测试收集到的性能数据,然后相应地增减实例数。本文不会详细讨论诊断数据的收集,请读者自行了解或我将以后另外撰文。本文中,我将检查转储到 Windows Azure Storage 的表中的性能计数器数据,以及执行 REST 调用以更改配置中的实例数所需的代码和设置。此外,可供下载的代码示例中将包含一个进行 REST 管理调用的简单页面,用于根据用户输入强制更改实例数。该方案类似于图 1 所绘。

图 1 基于性能的缩放

项目设置

作为开始,我创建了包含一个工作者角色和一个 Web 角色的 Windows Azure 云服务项目。将该 Web 角色配置为从其发布性能计数器数据(特定为处理器时间百分比),并将数据每隔 20 秒推送到存储。使用代码在 WebRole::OnStart 方法内部实现这一操作,类似于以下所示:

var performanceConfiguration = 
  new PerformanceCounterConfiguration();
performanceConfiguration.CounterSpecifier = 
  @"\Processor(_Total)\% Processor Time";
performanceConfiguration.SampleRate = 
  System.TimeSpan.FromSeconds(1.0);
            
// Add the new performance counter to the configuration 
config.PerformanceCounters.DataSources.Add(
  performanceConfiguration);
config.PerformanceCounters.ScheduledTransferPeriod = 
  System.TimeSpan.FromSeconds(20.0);

此代码注册性能计数器,设置数据的收集时间间隔,然后将数据推送到存储。此处所用时间间隔值很适合于本例,但并不代表我会在生产系统中使用相同的值。在生产系统中,收集时间间隔将会长许多,因为此时需要考虑的是全天候操作。另外,为减少针对 Windows Azure 存储的事务处理数量,推送到存储的时间间隔也应延长。

接下来,我将创建自签名证书,将其用于进行 Azure REST 管理 API 调用。每个请求都必须经过验证,而证书则是完成验证的方法。我按照 TechNet 库的“在 IIS 7 中创建自签名的服务器证书”(technet.microsoft.com/library/cc753127(WS.10))一文中创建自签名证书的说明操作。我导出了一个 .cer 文件和一个 .pfx 文件。.cer 文件将用于对发送到管理 API 的请求签名,.pfx 文件将通过管理接口导入到计算角色中(请参见图 2)。

图 2 导入证书

稍后,我将回到此处并获取指纹以将其置入所创建的 Web 角色和工作者角色的设置中,使得这些角色可以访问证书存储和检索证书。

最后,要将其在 Windows Azure 中投入使用,还需要一个计算项目,以便发布两个角色以及一个将性能数据传输到的存储项目。准备好这些元素之后,可以进入正题了。

运行过忙还是过闲?

现在,我已经配置了 Web 角色并添加了代码用于发布性能计数器数据,下一步是提取该数据并将其与阈值进行比较。我将创建 TestPerfData 方法,该方法从表检索数据并对值进行测试。我编写了类似于下文的 LINQ 语句:

double AvgCPU = (
  from d in selectedData
  where d.CounterName == 
    @"\Processor(_Total)\% Processor Time"
  select d.CounterValue).Average();

通过比较平均利用率可以确定当前应用程序性能。如果实例运行过忙,则添加实例。如果运行过闲,则表明实例的运行不必要,浪费资源也就是浪费金钱,则可以减少实例数。

有关访问性能计数器表数据所需代码和设置的深入讨论,可参见我写的博客文章 blogs.msdn.com/b/joseph_fultz/archive/2010/06/30/querying-azure-perf-counter-data-with-linq.aspx。我使用了一个简单的 if-then-else 块来评估状态并确定所需的操作。相关内容将在创建更改运行服务配置所需的函数之后详细介绍。

使用 REST 管理 API

在完成 TestPerfData 方法之前,还有一些工作要做。此处需要一些方法来帮助找到指定角色的实例数、为该角色创建一个带有经过调整的实例数的新有效服务配置,并最终允许更新配置。

为此,我在项目中已添加一个类文件并创建了六个静态方法,如图 3 所示。

图 3 配置方法

方法 说明
GetDeploymentInfo 检索部署配置,包括编码服务配置。
GetServiceConfig 从部署信息检索和解码服务配置。
GetInstanceCount 提取指定角色的实例数。
ChangeInstanceCount 更新服务配置并返回完整 XML。
ChangeConfigFile 使用提供给函数的服务配置更新服务配置。
LookupCertificate 将包含指纹的环境设置传入并从证书存储检索证书。

与 REST 管理 API 交互的调用必须包含证书。为此,将证书添加到托管服务中,并将指纹添加到角色配置,用于在运行时提取证书。正确配置服务和角色之后,我使用以下代码从证书存储获取证书:

string Thumbprint = 
  RoleEnvironment.GetConfigurationSettingValue(
  ThumbprintSettingName);
X509Store certificateStore = 
  new X509Store(StoreName.My, StoreLocation.LocalMachine);
certificateStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = 
  certificateStore.Certificates.Find(
  X509FindType.FindByThumbprint, Thumbprint, false);

这是 LookUpCertificate 方法的主要代码,在用来与 REST API 交互的方法中调用。我将回顾 GetDeploymentInfo 函数作为如何构建调用的示例。对于本例,我在其中硬编码了一些访问 REST API 时所需的变量:

string x_ms_version = "2009-10-01";
string SubscriptionID = "[your subscription ID]";
string ServiceName = "[your service name]";
string DeploymentSlot = "Production";

此处需要使用正确的 URI 创建 HttpWebRequest,设置请求标头并将证书添加到其中。 这里,我构建了 URI 字符串并用它创建了新的 HttpWebRequest 对象:

string RequestUri = "https://management.core.windows.
net/" + 
  SubscriptionID + "/services/hostedservices/"+ 
  ServiceName + "/deploymentslots/" + DeploymentSlot;

HttpWebRequest RestRequest = 
  (HttpWebRequest)HttpWebRequest.Create(RequestUri);

为了使调用有效,必须在标头中包括版本。因此,我创建了名称/值集合,添加了版本密钥和数据,并将这些内容添加到请求标头集合中:

NameValueCollection RequestHeaders = 
  new NameValueCollection();
RequestHeaders.Add("x-ms-version", x_ms_version);
if (RequestHeaders != null) {
  RestRequest.Headers.Add(RequestHeaders);
}

准备这个特殊的请求的最后一步是将证书添加到请求中:

X509Certificate cert = LookupCertificate("RESTMgmtCert");
RestRequest.ClientCertificates.Add(cert);

最后,执行请求并读取响应:

RestResponse = RestRequest.GetResponse();
using (StreamReader RestResponseStream = new StreamReader(RestResponse.GetResponseStream(), true)) {
  ResponseBody = RestResponseStream.ReadToEnd();
  RestResponseStream.Close();
}

这是我在构建对 REST 管理 API 的请求时使用的通用模式。 GetServiceConfig 函数使用如下所示的 LINQ to XML 语句从部署配置中提取服务配置:

XElement DeploymentInfo = XElement.Parse(DeploymentInfoXML);
string EncodedServiceConfig = 
  (from element in DeploymentInfo.Elements()
where element.Name.LocalName.Trim().ToLower() == "configuration"
select (string) element.Value).Single();

在我的代码中,GetServiceConfig 的返回结果传递到 GetInstanceCount 和/或 ChangeInstance 计数函数,以提取信息或进行更新。ChangeInstance 函数提供的返回结果是更新后的服务配置,该配置传递给 ChangeConfigFile。接下来,通过构建与上一个请求(用于提取部署信息)类似的请求,ChangeConfigFile 将更新推送到服务,但其中存在以下重要区别:

  1. 将“/?comp=config”添加到了 URI 的末尾
  2. 使用动词 PUT 而非 GET
  3. 更新的配置作为请求正文进行传送

综合讲述

准备好函数用于查找和更改服务配置,并做好其他准备工作(如设置计数器、配置存储的连接字符串设置以及安装证书)之后,可以开始实施 CPU 阈值测试。

Visual Studio 模板生成每 10 秒唤醒一次的工作者角色以执行代码。为了简化起见,我没有这么做,而是添加了每 5 分钟运行一次的单独计时器。在计时器中,使用一条简单的条件语句测试利用率高于还是低于 85%,我将创建两个 Web 角色实例。采用这种方法,可以确保实例数毫无疑问地会从初始的两个减少为一个。

在工作者角色内部,有一个声明和实例化计时器的 Run 方法。在计时器用时处理程序中添加了一个对之前创建的 TestPerfData 函数的调用。对于本例,我跳过了 greater-than 条件的实施,因为已知 CPU 利用率不会如此高。将 less-than 条件设置为低于 85%,因为我确信计数器平均值将低于这个数值。通过设置这些精心准备的条件,我可以通过 Web 管理控制台或通过 Visual Studio 中的服务器资源管理器查看变化。

在 less-than-85-percent 块中,我检查了实例计数,修改了服务配置并更新了运行服务配置,如图 4 中所示。

图 4 Less-Than-85-Percent 块

else if (AvgCPU < 85.0) {
  Trace.TraceInformation("in the AvgCPU < 25 test.");
  string deploymentInfo = 
    AzureRESTMgmtHelper.GetDeploymentInfo();
  string svcconfig = 
    AzureRESTMgmtHelper.GetServiceConfig(deploymentInfo);
  int InstanceCount = 
    System.Convert.ToInt32(
    AzureRESTMgmtHelper.GetInstanceCount(
    svcconfig, "WebRole1"));
  if (InstanceCount > 1) {
    InstanceCount--;
    string UpdatedSvcConfig = 
      AzureRESTMgmtHelper.ChangeInstanceCount(
      svcconfig, "WebRole1", InstanceCount.ToString());
    AzureRESTMgmtHelper.ChangeConfigFile(UpdatedSvcConfig);
  }
}

我需要确保在调低实例计数值之前对其进行检查,因为不希望该值为零,零不是有效配置,会导致故障。

运行示例

现在我已准备就绪,可以执行示例并演示 Windows Azure 中的弹性。我知道自己设计的代码总能 一次成功,因此我直接右键单击“云服务项目”并单击“发布”。此对话框提供用于配置凭据的选项,这一步我已经完成(请参见图 5)。

图 5 发布项目

单击“确定”,此时只需等待程序包的复制和部署。部署完成后,我切换到 Web 管理控制台,看到有两个 Web 角色和一个工作者角色在运行,如图 6 中所示。

图 6 两个 Web 角色和一个工作者角色

我等待计时器事件的触发,执行确定 CPU 平均利用率低于 85% 的代码,然后递减 WebRole1 实例数。出现此情况后,管理页将刷新,以反映对部署的更新。

由于我使用的是小型 VM,只将计数值更改了一,并且应用程序是轻型的(一个 .aspx 页面),更新无需多长时间,我将看到如图 7 所示的最终自动收缩部署。

图 7 现在只有一个 Web 角色和一个工作者角色

前瞻

关于示例,我希望在考虑实际实施的情况下分享一些最后的想法。有几个要点需要注意。

首先,测试是简单化且理想化的。在实际实施中,需要评估的内容远不止简单的 CPU 利用率,并且需要考虑进行收集的时间片。

此外,您需要评估使用 Windows Azure 存储的成本。根据解决方案,理想方法是仅在表中清理感兴趣的记录。您可以减少上载时间间隔以降低事务处理成本,或者可以将数据移动到 SQL Azure 以使成本最低。

您还需要考虑在更新期间发生的情况。直接更新将导致用户丢失连接。而将新实例在暂存中启动,然后切换到虚拟 IP 地址会好一些。但是,在任何一种情况下,您将面临会话和视图状态问题。更好的解决方案是进入无状态并在规模调整时禁用测试。

以上就是我在 Windows Azure 中的弹性实施。请下载代码示例并立即试用。

Joseph Fultz  是达拉斯 Microsoft 技术中心的架构师,协助企业客户和 ISV 设计和制作软件解决方案以满足商业和市场需求。他在 Tech•Ed 及类似的内部培训活动中做过讲座。

*衷心感谢以下技术专家对本文的审阅:*Suraj Puri