Статьи

NuGet push … в Windows Azure

Глядя на то, как людям нравится развертывать свои приложения в облачной среде, большая фракция предпочитает использовать свою систему контроля версий в качестве источника для своего производственного развертывания. Хотя это интересно, я вижу много проблем: ваш исходный код может не запуститься сразу и, вероятно, должен быть скомпилирован. Вы не хотите поддерживать скомпилированные сборки в системе контроля версий, верно? Кроме того, возможно, существует какой-то процесс обеспечения качества, в котором развертывание может происходить только после утверждения. Почему бы не использовать контроль источника для того, для чего он нужен: контроль источника? А как насчет использования репозитория NuGet в качестве источника для нашего развертывания? Встречайте Windows Azure NuGetRole.

Отказ от ответственности / Предупреждение: это демонстрационный материал, и его, вероятно, нельзя использовать для реальных развертываний, не сделав его пуленепробиваемым!

Загрузите образец кода: NuGetRole.zip (262,22 КБ)

Как это использовать

Если вы скомпилируете исходный код ( загрузите ), у вас останется X шагов для запуска NuGetRole в Windows Azure:

  • Указание источника пакета для использования
  • Добавьте несколько пакетов в канал источника пакетов (который вы можете легко разместить на MyGet )
  • Развертывание в Windows Azure

Когда все эти шаги будут выполнены, NuGetRole загрузит все последние версии пакета из источника пакета, указанного в ServiceConfiguration.cscfg:

 <?xml version="1.0" encoding="utf-8"?>
 <ServiceConfiguration serviceName="NuGetRole.Azure" 
                       xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" 
                       osFamily="1" 
                       osVersion="*">
   <Role name="NuGetRole.Web">
     <Instances count="1" />
     <ConfigurationSettings>
       <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="UseDevelopmentStorage=true" />
       <Setting name="PackageSource" value="http://www.myget.org/F/nugetrole/" />
     </ConfigurationSettings>
   </Role>
</ServiceConfiguration>

Пакеты, которые вы публикуете, должны содержать только содержимое и / или папку lib . Другое содержимое пакета в настоящее время будет игнорироваться NuGetRole. Если вы хотите добавить к своей роли какой-либо веб-контент, например страницу по умолчанию, просто опубликуйте следующий пакет:

Обозреватель пакетов NuGet MyGet NuGet NuGetRole Azure

Просто нажмите и наблюдайте, как ваша ферма веб-ролей Windows Azure обновляет их содержимое. Или ваш сервер сборки отправляет пакет NuGet, содержащий ваше приложение, и сам обновляет ферму серверов. Все, что вам нравится.

Как это работает

Я сделал довольно пустой проект Windows Azure ( скачать ). В этом проекте существует одна веб-роль. Эта веб-роль состоит только из файла Web.config и класса WebRole.cs, который выглядит следующим образом:

public class WebRole : RoleEntryPoint
 {
     private bool _isSynchronizing;
     private PackageSynchronizer _packageSynchronizer = null;
 
     public override bool OnStart()
     {
         var localPath = Path.Combine(Environment.GetEnvironmentVariable("RdRoleRoot") + "\\approot");
 
         _packageSynchronizer = new PackageSynchronizer(
             new Uri(RoleEnvironment.GetConfigurationSettingValue("PackageSource")), localPath);
 
         _packageSynchronizer.SynchronizationStarted += sender => _isSynchronizing = true;
         _packageSynchronizer.SynchronizationCompleted += sender => _isSynchronizing = false;
 
         RoleEnvironment.StatusCheck += (sender, args) =>
                                         {
                                             if (_isSynchronizing)
                                             {
                                                 args.SetBusy();
                                             }
                                         };
 
         return base.OnStart();
     }
 
     public override void Run()
     {
         _packageSynchronizer.SynchronizeForever(TimeSpan.FromSeconds(30));
 
         base.Run();
     }
 }

The above code is essentially wiring some configuration values like the local web root and the NuGet package source to use to a second class in this project: the PackageSynchronizer. This class simply checks the specified NuGet package source every few minutes, checks for the latest package versions and if required, updates content and bin files.  Each synchronization run does the following:

public void SynchronizeOnce()
 {
     var packages = _packageRepository.GetPackages()
         .Where(p => p.IsLatestVersion == true).ToList();
 
     var touchedFiles = new List<string>();
 
     // Deploy new content
     foreach (var package in packages)
     {
         var packageHash = package.GetHash();
         var packageFiles = package.GetFiles();
         foreach (var packageFile in packageFiles)
         {
             // Keep filename
             var packageFileName = packageFile.Path.Replace("content\\", "").Replace("lib\\", "bin\\");
        
             // Mark file as touched
             touchedFiles.Add(packageFileName);
 
             // Do not overwrite content that has not been updated
             if (!_packageFileHash.ContainsKey(packageFileName) || _packageFileHash[packageFileName] != packageHash)
             {
                 _packageFileHash[packageFileName] = packageHash;
 
                 Deploy(packageFile.GetStream(), packageFileName);
             }
         }
 
         // Remove obsolete content
         var obsoleteFiles = _packageFileHash.Keys.Except(touchedFiles).ToList();
         foreach (var obsoletePath in obsoleteFiles)
         {
             _packageFileHash.Remove(obsoletePath);
             Undeploy(obsoletePath);
         }
     }
 }

Or in human language:

  • The specified NuGet package source is checked for packages
  • Every package marked “IsLatest” is being downloaded and deployed onto the machine
  • Files that have not been used in the current synchronization step are deleted

This is probably not a bullet-proof solution, but I wanted to show you how easy it is to use NuGet not only as a package manager inside Visual Studio, but also from your code: NuGet is not just a package manager but in essence a package management protocol. Which you can easily extend.

One thing to note: I also made the Windows Azure load balancer ignore the role that’s updating itself. This means a roie instance that is synchronizing its contents will never be available in the load balancing pool so no traffic is sent to the role instance during an update.