Разработка адаптера для роли виртуальной машины в Windows Azure

Обновлено: Март 2011 г.

[Поддержка функции «Роль виртуальной машины» в Windows Azure прекращается 15 мая 2013 г. После этой даты развертывания этой роли будут удалены. Для продолжения работы с существующими приложениями можно использовать виртуальные машины Windows Azure. Дополнительные сведения об использовании виртуальных машин для приложения см. на веб-странице Moving from VM Role to Windows Azure Virtual Machines (Переход от использования функции «Роль виртуальной машины» к использованию виртуальных машин Windows Azure).

Можно написать адаптер в виде службы Windows, который запускается автоматически при старте ОС и использует API Microsoft.WindowsAzure.ServiceRuntime для работы со сведениями из среды выполнения Windows Azure. Возможно, потребуется написать службу Windows, если для текущей роли экземпляра ВМ или других экземпляров ролей в службе требуются сведения о сетевом адресе, если требуется вести запись в локальный ресурс хранения, если во время выполнения необходимо считывать параметры конфигурации службы или реагировать на их изменения.

В этом разделе показано, как создать адаптер, демонстрирующий службы, поддерживающие роль виртуальной машины. Пример в данном разделе при запуске ОС монтирует диск Windows Azure, а затем настраивает IIS для записи файлов HTTP-журнала на диск.

Чтобы создать адаптер, необходимо выполнить следующие действия.

  1. Создание контейнера хранилища

  2. Создание проекта адаптера

  3. Добавление возможности отслеживания изменений в конфигурации экземпляра роли

  4. Задание поведения при запуске адаптера

  5. Задание поведения при остановке адаптера

  6. Создание пакетов установщика для адаптера

  7. Создание проекта установки для адаптера

  8. Добавление события после построения, чтобы исправить несоответствия сборки

  9. Добавление параметров конфигурации в модель облачной службы

  10. Установка адаптера

Адаптер использует хранилище больших двоичных объектов для хранения данных журнала на диске Windows Azure. Необходимо иметь доступ к учетной записи хранения в Windows Azure и создать контейнер, используемый адаптером. Используйте любое удобное средство, чтобы создать контейнер в хранилище Windows Azure с подходящим именем. Имя контейнера будет использовано при настройке модели облачной службы.

Visual Studio 2010 предоставляет шаблон для создания службы Windows. С помощью этого шаблона можно создать адаптер для экземпляров ролей виртуальной машины.

  1. Откройте Visual Studio 2010, в меню Файл выберите пункт Создать, затем Проект.

  2. На панели Установленные шаблоны в разделе Visual C# щелкните Windows, а затем на центральной панели щелкните Служба Windows.

  3. Введите имя решения и проект адаптера, затем нажмите кнопку ОК.

  4. В обозревателе решений щелкните правой кнопкой мыши решение, щелкните Диспетчер конфигурации, установите для проекта флажок Любой ЦП, затем нажмите кнопку Закрыть.

  5. В обозревателе решений задайте файлу Service1.cs то имя, которое используется для адаптера. В примерах в этом разделе используется заполнитель AdapterName, но можно указать другой.

  6. Щелкните проект правой кнопкой мыши, нажмите на странице приложения Свойства, убедитесь, что для параметра Целевая платформа выбран вариант .NET Framework 3.5.

  7. На странице публикации (в свойствах проекта) щелкните Необходимые компоненты, а затем убедитесь, что не выбран вариант .NET Framework 4 (клиентский профиль).

  8. В обозревателе решений добавьте ссылки на следующие сборки:

    • Microsoft.WindowsAzure.ServiceRuntime.dll

      noteПримечание
      Необходимо убедиться, что свойство этой сборки Копировать локально имеет значение False.

    • Microsoft.WindowsAzure.CloudDrive.dll

    • Microsoft.WindowsAzure.StorageClient.dll

    • Microsoft.Web.Administration.dll

  9. В обозревателе решений щелкните правой кнопкой мыши файл AdapterName.cs и выберите пункт Просмотр кода.

  10. Добавьте следующие инструкции using:

    
    using Microsoft.WindowsAzure.ServiceRuntime;
    using Microsoft.WindowsAzure.StorageClient;
    using Microsoft.WindowsAzure;
    using Microsoft.Web.Administration;
    using System.IO;
    using System.Threading;
    
  11. В обозревателе решений щелкните правой кнопкой мыши файл AdapterName.cs и выберите пункт Просмотр в конструкторе.

  12. На панели Свойства задайте следующие свойства:

    • ServiceName — можно задать имя, под которым адаптер будет идентифицироваться в системе.

    • (Имя) — можно задать имя для объекта адаптера, которое будет использоваться в коде.

    • CanShutdown — необходимо указать для этого свойства значение True, чтобы адаптер получал уведомление, если система завершает работу.

Файл Program.cs содержит следующий код:


static void Main()
{
   ServiceBase[] ServicesToRun;
   ServicesToRun = new ServiceBase[] 
   { 
      new ServiceName() 
   };  
   ServiceBase.Run(ServicesToRun);
}

Можно задать операции, которые будут выполнены при изменении конфигурации экземпляра роли. Например, можно сменить расположение диска, на который записываются данные журнала, изменив конфигурацию экземпляра роли. Для этого используйте события RoleEnvironment.

  1. В обозревателе решений щелкните правой кнопкой мыши файл AdapterName.cs и выберите пункт Просмотр кода.

  2. Измените конструктор, чтобы задать события изменения конфигурации. В следующем примере кода показаны определения событий, добавленные в конструктор.

    
    public AdapterName()
    {
       InitializeComponent();
       RoleEnvironment.Changed += RoleEnvironmentChanged;
       RoleEnvironment.StatusCheck += RoleEnvironmentStatusCheck;
    }
    
  3. Добавьте метод RoleEnvironmentChanged, который вызывается при возникновении события Changed. В следующем примере кода показан метод RoleEnvironmentChanged, который подключает новый диск Windows Azure и настраивает IIS для записи файлов журнала на новый диск:

    
    private CloudDrive currentDrive;
     
    private void RoleEnvironmentChanged(object sender, RoleEnvironmentChangedEventArgs e)
    {
       if(!e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>().Any(
          c => c.ConfigurationSettingName == "AdapterName.BlobPath"))
          return;
    
       try
       {
          // perform a rolling drive change
          var oldDrive = this.currentDrive;
          var newDrive = MountDrive();
          try
          {
             ConfigureWebServer(newDrive.LocalPath);
          }
          catch (Exception)
          {
             UnmountDrive(newDrive);
             throw;
          }
          this.currentDrive = newDrive;
          UnmountDrive(oldDrive);
       }
       catch (Exception ex)
       {
          this.EventLog.WriteEntry(ex.ToString(), EventLogEntryType.Error);
          throw;
       }
    }
    

    AdapterName — это имя, которое было указано для проекта адаптера.

  4. Добавьте методы, которые подключает и отключают диск Windows Azure, также добавьте метод для настройки расположения файла журнала IIS. В следующем примере кода показан метод MountDrive.

    
    private CloudDrive MountDrive()
    {
       // create or mount an instance-specific drive
       var credentials = GetStorageCredentials();
       var driveUri = GetDriveUri();
       var drive = new CloudDrive(driveUri, credentials);
    
       try
       {
          drive.Create(1024);
       }
       catch (Exception ex)
       {
          if (ex.Message != "ERROR_BLOB_ALREADY_EXISTS") throw;
       }
    
       // mount the drive
       string mountPoint = drive.Mount(1024, 
          DriveMountOptions.FixFileSystemErrors | DriveMountOptions.Force);
       this.EventLog.WriteEntry(string.Format("{0} mounted at {1}", drive.Uri, mountPoint));
       return drive;
    }
    
    private Uri GetDriveUri()
    {
       return new Uri(string.Format(
       RoleEnvironment.GetConfigurationSettingValue("AdapterName.BlobPath"),
          RoleEnvironment.CurrentRoleInstance.Id));
    }
    
    private StorageCredentials GetStorageCredentials()
    {
       return new StorageCredentialsAccountAndKey(
       RoleEnvironment.GetConfigurationSettingValue("AdapterName.AccountName"),
       RoleEnvironment.GetConfigurationSettingValue("AdapterName.AccountKey"));
    }
    

    Дополнительные сведения об использовании CloudDrive API см. в разделе CloudDrive. AdapterName.BlobPath, AdapterName.AccountName и AdapterName.AccountKey определены в файле определения облачной службы и настраиваются в файле конфигурации службы. Дополнительные сведения о задании этих параметров см. в разделе Добавление параметров конфигурации в модель облачной службы.

    В следующем примере кода показан метод UnmountDrive.

    
    private void UnmountDrive(CloudDrive drive)
    {
       drive.Unmount();
       this.EventLog.WriteEntry(string.Format("{0} unmounted", drive.Uri));
    }
    
    

    В следующем примере кода показан метод ConfigureWebServer.

    
    private void ConfigureWebServer(string drivePath)
    {
       using (var config = new ServerManager())
       {
          var logdir = Path.Combine(drivePath, @"inetpub\logs\LogFiles");
          config.SiteDefaults.LogFile.Directory = logdir;
    
          config.CommitChanges();
    
          this.EventLog.WriteEntry(string.Format("IIS log location set to '{0}'", logdir));
       }
    }
    
    
  5. Добавьте метод RoleEnvironmentStatusCheck, который используется для определения состояния экземпляра роли. Объект statusCheckWaitHandle используется в качестве сигнала для сообщения подсистеме балансировки нагрузки, занят экземпляр роли или свободен. Вызов метода Set объекта сигнализирует, что изменение конфигурации завершено. В следующем примере кода показан метод RoleEnvironmentStatusCheck.

    
    private readonly EventWaitHandle statusCheckWaitHandle = new ManualResetEvent(false);
    private volatile bool busy = true;
    
    private void RoleEnvironmentStatusCheck(object sender, RoleInstanceStatusCheckEventArgs e)
    {
       if (this.busy)
       {
          e.SetBusy();
       }
       statusCheckWaitHandle.Set();
    }
    
    

В этом примере адаптер при запуске выполняет следующие операции.

  • Инициализация кэша диска

  • Подключение диска

  • Настройка IIS для использования подключенного диска

  1. В обозревателе решений щелкните правой кнопкой мыши файл AdapterName.cs и выберите пункт Просмотр кода. Найдите метод OnStart, который был автоматически переопределен при создании проекта, и добавьте код, который проверит запуск экземпляра роли, выполнит операции адаптера в отдельном потоке, а затем отправит сигнал о завершении операций с помощью объекта statusCheckWaitHandle.

    
    protected override void OnStart(string[] args)
    {
       /// Windows Azure waits for auto-start services to be fully started 
       /// before sending any traffic.  Service Control Manager (SCM) does
       /// not impose a time limit on startup; the service need only 
       /// request additional time.
    
       if (!RoleEnvironment.IsAvailable) return;
          
       var startThread = new Thread(OnStartInternal);
       startThread.Start();
    
       // wait until a status check has occurred, so that Windows Azure 
       // knows we are working on something.
       WaitForHandle(statusCheckWaitHandle);
    }
    

    В следующем примере кода показан метод OnStartInternal, в котором подключается диск Windows Azure и изменяется настройка IIS для записи данных журнала на новый диск.

    
    private void OnStartInternal()
    {
       try
       {
          // initialize the drive cache
          string cachePath = RoleEnvironment.GetLocalResource("Data").RootPath;
          CloudDrive.InitializeCache(cachePath, 4096);
          this.EventLog.WriteEntry("initialization succeeded");
    
          // mount the current drive
          this.currentDrive = MountDrive();
    
          // configure IIS
          ConfigureWebServer(this.currentDrive.LocalPath);
    
          this.busy = false;
       }
       catch (Exception ex)
       {
          this.EventLog.WriteEntry(ex.ToString(), EventLogEntryType.Error);
          throw;
       }
    }
    

    В следующем примере кода показан метод WaitForHandle, который ожидает проверки состояния экземпляра роли.

    
    private const int ThreadPollTimeInMilliseconds = 1000;
    
    private void WaitForHandle(WaitHandle handle)
    {
       while (!handle.WaitOne(ThreadPollTimeInMilliseconds))
       {
          this.RequestAdditionalTime(ThreadPollTimeInMilliseconds * 2);
       }
    }
    

В этом примере адаптер при остановке заново настраивает расположение файла журнала и отсоединяет диск.

  1. В обозревателе решений щелкните правой кнопкой мыши файл AdapterName.cs и выберите пункт Просмотр кода. Найдите метод OnStop, который автоматически был переопределен после создания проекта, и добавьте код, который повторно задаст расположение файла журнала и отсоединит диск.

    
    protected override void OnStop()
    {
       OnStopInternal();
    }
    
  2. В следующем примере кода показан метод OnShutdown.

    
    protected override void OnShutdown()
    {
       /// Windows Azure stops sending traffic before shutting down.
       /// Note that some requests may still be executing.
       OnStopInternal();
    }
    
  3. Добавьте метод OnStopInternal, в котором будут выполнены описанные операции.

    
    private void OnStopInternal()
    {
       try
       {
          ConfigureWebServer(@"%SystemDrive%");
    
          if (this.currentDrive != null)
          {
             UnmountDrive(this.currentDrive);
             this.currentDrive = null;
          }
       }
       catch (Exception ex)
       {
          this.EventLog.WriteEntry(ex.ToString(), EventLogEntryType.Error);
          throw;
       }
    }
    
    

При установке службы Windows нужно выполнить некоторые действия. Это может сделать класс Installer. Visual Studio может создать эти установщики специально для службы Windows и добавить их в проект.

  1. В обозревателе решений щелкните правой кнопкой мыши файл AdapterName.cs и выберите пункт Просмотр в конструкторе.

  2. Щелкните фон конструктора для выбора самого адаптера, а не его элементов.

  3. Щелкните правой кнопкой мыши и выберите пункт Добавить установщик.

    По умолчанию к проекту будет добавлен класс компонента с двумя установщиками. Компонент называется ProjectInstaller, внутри него есть установщики для адаптера и соответствующего процесса.

  4. В окне конструктора для ProjectInstaller выберите serviceInstaller1.

  5. В окне Свойства введите для свойства ServiceName имя адаптера.

  6. Присвойте свойству StartType значение Авто.

  7. В конструкторе щелкните serviceProcessInstaller1. Присвойте свойству Учетная запись значение LocalService. Благодаря этому адаптер будет устанавливаться и запускаться для учетной записи локальной службы.

  8. Постройте проект.

Проект установки устанавливает скомпилированные файлы проекта и запускает установщиков, необходимых для запуска адаптера. Для создания полного проекта установки необходимо добавить выходные данные проекта к проекту установки, а затем добавить пользовательское действие по установке программы.

  1. В обозревателе решений щелкните правой кнопкой мыши решение, затем выберите Добавить, затем Новый проект.

  2. На панели Установленные шаблоны разверните узел Другие типы проектов, разверните Проекты установки и развертывания и выберите Установщик Visual Studio.

  3. На центральной панели щелкните Проект установки.

  4. Введите имя для проекта установки и нажмите кнопку ОК.

  5. Разверните Обнаруженные зависимости, дважды щелкните Microsoft .NET Framework, а затем на панели Свойства убедитесь, что значение для Версия задано как .NET Framework 3.5.

  6. Щелкните правой кнопкой мыши каждую из трех сборок в разделе Обнаруженные зависимости и выберите Исключить.

    • Microsoft.Web.Administration.dll

    • Microsoft.WindowsAzure.ServiceRuntime.dll

    • msshrtmi.dll

    • mswacdmi.dll

Теперь добавьте пользовательское действие по установке файла программы адаптера.

  1. В обозревателе решений щелкните проект установки правой кнопкой мыши, выберите Просмотр, затем Пользовательские действия.

  2. В редакторе Пользовательские действия щелкните правой кнопкой мыши узел Пользовательские действия и выберите Добавить пользовательское действие.

  3. Дважды щелкните Папку приложения в списке, чтобы открыть ее, и выберите Добавить вывод.

  4. В окне Добавить группу вывода проекта выберите Вывод основной информации, выберите (Активно) для конфигурации и нажмите кнопку ОК.

Поскольку Visual Studio вставляет 32-битные сборки в MSI-файл, а Windows Azure требуется 64-битные, необходимо выполнить скрипт, чтобы исправить несогласованные сборки. Для исправления сборок можно запустить JavaScript-файл в качестве события построения.

  1. Сохраните следующий код в файл FixMSI.js в корневой папке проекта установки:

    
    // workaround for "BadImageFormatException" issue - see http://msdn.microsoft.com/ru-ru/library/kz0ke5xt.aspx
    
    var msiOpenDatabaseModeTransact = 1;
    var msiViewModifyInsert = 1
    var msiViewModifyUpdate = 2
    var msiViewModifyAssign = 3
    var msiViewModifyReplace = 4
    var msiViewModifyDelete = 6
    
    var filespec = WScript.Arguments(0);
    var frameworkpath = WScript.Arguments(1);
    var installer = WScript.CreateObject("WindowsInstaller.Installer");
    var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
    
    WScript.Echo("Updating file '" + filespec + "' to use a 64-bit custom action...");
    
    Update64Bit();
    
    database.Commit();
    database = null;
    installer = null;
    
    function Update64Bit() {
        var sql;
        var view;
        var record;
        sql = "SELECT * FROM Binary WHERE `Name`='InstallUtil'";
        view = database.OpenView(sql);
        view.Execute();
        record = view.Fetch();
        if (record != null) {
            var dataCol = 2;
            record.SetStream(dataCol, frameworkpath + "\\InstallUtilLib.dll");
            view.Modify(msiViewModifyUpdate, record);
        }
        record = null;
        view.close();
        view = null;
    }
    
  2. В обозревателе решений выберите созданный ранее проект установки, а затем на панели Свойства добавьте следующую команду в свойство PostBuildEvent.

    
    cd $(ProjectDir) 
    CScript //NoLogo FixMSI.js "$(BuiltOutputPath)" "%SystemRoot%\Microsoft.NET\Framework64\v2.0.50727"
    
  3. В обозревателе решений щелкните правой кнопкой мыши на проекте установки и выберите Построить.

Чтобы адаптер мог связываться с Windows Azure, необходимо определить определенные параметры в модели облачных служб.

  1. Откройте файл ServiceDefinition.csdef для роли виртуальной машины

  2. Добавьте следующие параметры к элементу ConfigurationSettings:

    
    <Setting name="AdapterName.BlobPath" />
    <Setting name="AdapterName.AccountName" />
    <Setting name="AdapterName.AccountKey" />
    

    AdapterName — имя проекта адаптера.

  3. Добавьте следующий параметр в элемент LocalResources.

    
    <LocalStorage name="Data" />
    

    Дополнительные сведения об элементах, которые можно использовать в модели службы, см. в разделе VirtualMachineRole Schema.

  4. Сохраните файл.

  1. Откройте файл ServiceConfiguration.cscfg для роли виртуальной машины.

  2. Добавьте следующие параметры конфигурации в файл:

    
    <Setting name="AdapterName.BlobPath" value="http://StorageAccountName.blob.core.windows.net/ContainerName/{0}.vhd" />
    <Setting name="AdapterName.AccountName" value="StorageAccountName" />
    <Setting name="AdapterName.AccountKey" value="StorageAccountKey" />
    

    AdapterName — имя проекта адаптера. StorageAccountName — имя учетной записи хранения. StorageAccountKey — первичный ключ учетной записи хранения. ContainerName — ранее созданный контейнер хранилища. Дополнительные сведения о конфигурации модели службы см. в разделе Windows Azure Service Configuration Schema. Дополнительные сведения о задании и настройке параметров см. в разделе Создание и развертывание модели службы роли виртуальной машины.

  3. Сохраните файл.

Теперь адаптер может быть установлен на виртуальном жестком диске, который загружается в Windows Azure.

После создания кода, определения и настройки параметров в модели службы можно установить адаптер и развернуть пакет модели службы. В случае роли ВМ адаптер устанавливается на виртуальной машине после установки компонента интеграции Windows Azure.

Чтобы адаптер работал правильно, должны быть включены несколько функций операционной системы. Выполните следующую команду для образа, который будет передан в Windows Azure:

DISM /Online /Enable-Feature /FeatureName:NetFx3 /FeatureName:IIS-WebServerRole /FeatureName:IIS-WebServer /FeatureName:IIS-CommonHttpFeatures /FeatureName:IIS-HttpErrors /FeatureName:IIS-ApplicationDevelopment /FeatureName:IIS-HealthAndDiagnostics /FeatureName:IIS-HttpLogging /FeatureName:IIS-RequestMonitor /FeatureName:IIS-Security /FeatureName:IIS-RequestFiltering /FeatureName:IIS-Performance /FeatureName:IIS-WebServerManagementTools /FeatureName:IIS-StaticContent /FeatureName:IIS-DefaultDocument /FeatureName:IIS-DirectoryBrowsing /FeatureName:IIS-HttpCompressionStatic /FeatureName:IIS-ManagementConsole

Чтобы установить адаптер, перейдите в папку, содержащую MSI-файл, а затем дважды щелкните его. Этот файл находится в папке Debug проекта установки, созданного ранее.

См. также

Добавления сообщества

Показ: