Azure Drive  

Udostępnij na: Facebook

Autor: Piotr Zieliński

Opublikowano: 2011-01-12

Wprowadzenie

Zwykłe (blokowe) bloby w Windows Azure są trudne do wdrożenia dla już istniejących aplikacji. Załóżmy, że napisaliśmy prostą galerie zdjęć w ASP.NET i wykorzystaliśmy do tego celu przestrzeń System.IO, np. do robienia kopii zapasowych czy zarządzania plikami na dysku. Jeśli jednak chcielibyśmy przechowywać zdjęcia w Azure Blobs, musielibyśmy zmodyfikować kod. Azure Drive pozwala na stworzenie wirtualnego dysku w chmurze. Do Drive odwołujemy się w aplikacji tak, jak do zwykłego dysku twardego dostępnego lokalnie – np. za pomocą klas dostępnych w przestrzeni System.IO.

Jak to działa?

Azure Drive oparty jest na pliku VHD (Virtual Hard Disk). Użytkownicy Virtual PC z pewnością rozpoznają ten format, ponieważ wykorzystywany jest jako wirtualny dysk. Podobnie jest w przypadku Azure – wszelkie operacje, które dokonywane są przez nas na wirtualnym dysku, będą tak naprawdę wykonywane na pliku VHD. Możemy nawet wysłać wcześniej utworzony plik VHD i podpiąć go do aplikacji Azure. Plik VHD zostanie załączony do konkretnego bloba (stronicowanego) znajdującego się w zdefiniowanym przez nas kontenerze.

Podpięcie Azure Drive

Przede wszystkim musimy dołączyć bibliotekę Microsoft.WindowsAzure.CloudDrive do projektu. Z menu kontekstowego Solution Explorer wybieramy zatem Add Reference i zaznaczamy stosowne biblioteki:

 

Drive wykorzystuje pamięć podręczną (cache), aby zoptymalizować operacje odczytu.  Najpierw jednak należy zdefiniować parametry cache. Można tego dokonać, edytując plik ServiceDefinition.csdef ręcznie lub wykorzystując IDE (właściwości roli, zakładka Local Storage):

Zawartość pliku ServiceDefinition.csdef po modyfikacji:

<ServiceDefinition name="CloudService1" xmlns="https://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">

  <WebRole name="WebRole1">

    <InputEndpoints>

      <InputEndpoint name="HttpIn" protocol="http" port="80" />

    </InputEndpoints>

    <ConfigurationSettings>

      <Setting name="DiagnosticsConnectionString" />

    </ConfigurationSettings>

    <LocalResources>

      <LocalStorage name="DriveCache" cleanOnRoleRecycle="false" sizeInMB="300" />

    </LocalResources>

  </WebRole>

</ServiceDefinition>

Następnie możemy zainicjalizować cache dla Drive:

LocalResource localCache = RoleEnvironment.GetLocalResource("DriveCache");

CloudDrive.InitializeCache(localCache.RootPath, localCache.MaximumSizeInMegabytes);

RootPath zawiera ścieżkę, w której będzie przechowywany cache – dlatego należy skorzystać z klasy LocalResoruce, ponieważ nie wiemy, gdzie na komputerze docelowym może znajdować się cache.

Kolejnym krokiem jest stworzenie kontenera dla dysków (nie zapominajmy, że Azure Drive jest podpięty pod klasycznego, stronicowanego bloba) oraz uzyskanie adresu URL dla bloba przechowującego plik VHD (czyli dysk):

blobClient.GetContainerReference("drives").CreateIfNotExist();

Uri driveUri=null;         

driveUri=blobClient.GetContainerReference("drives").GetPageBlobReference("drive.vhd").Uri;

Mamy już wszystko: kontener, bloba i pamięć podręczną. Wystarczy teraz stworzyć wirtualny dysk o podanej pojemności, a potem go zamontować:

CloudDrive cloudDrive = cloudStorageAccount.CreateCloudDrive(driveUri.ToString());

cloudDrive.Create(64);

string driveLetter=cloudDrive.Mount(25,DriveMountOptions.Force);

Metoda Mount zwraca literę dysku (np. „C:”). Pierwszy parametr to rozmiar pamięci podręcznej. Metoda InitializeCache określała całkowity rozmiar pamięci podręcznej dla wszystkich zamontowanych dysków. Natomiast w metodzie Mount określamy rozmiar tylko dla danego dysku.

I to już wszystko! Teraz możemy korzystać z przestrzeni nazw, tak jak w klasycznej aplikacji Windows. Na zakończenie zapiszmy więc jakiś tekst do pliku hello.txt:

System.IO.StreamWriter writer=null;

writer=new System.IO.StreamWriter(string.Format("{0}\\hello.txt",driveLetter));

writer.WriteLine("Witaj swiecie!");

writer.Close();

Wysyłanie istniejącego pliku VHD

Częstym scenariuszem wykorzystania Azure Drive jest przygotowanie pliku VHD na lokalnym komputerze, a następnie wdrożenie go na serwer Azure. W pierwszej kolejności stwórzmy jakieś pliki, które posłużą nam jako wirtualny dysk. Następnie musimy wygenerować plik VHD – istnieje wiele narzędzi do tego celu. Jeśli mamy jednak system Windows 7 lub Windows 2008, nie musimy kłopotać się z instalacją dodatkowej aplikacji – system wspiera generowanie VHD:

  1. uruchamiamy diskmgmt.msc (Start -> Uruchom -> diskmgmt.msc),

  2. z menu głównego wybieramy Action -> Create VHD,

  3. określamy ścieżkę docelową, maksymalny rozmiar i klikamy OK.

  4. zanim wykorzystamy dysk, musimy go zainicjować – klikamy w Initialize Disk i zaznaczamy MBR w Partition Style:

  5. następnie klikamy na dysku i z menu kontekstowego wybieramy New Simple Volume. W uruchomionym kreatorze większość opcji można pozostawić bez zmian:

  6. po zakończeniu formatowania uruchomi się okienko AutoPlay, w którym klikamy Open folder to view files:

W tej chwili możemy wypełniać dysk VHD tak jak każdy inny dysk w systemie Windows – przeciągając po prostu pliki. Po stworzeniu odpowiedniej struktury na dysku, należy powrócić do diskmgmt.msc i z menu kontekstowego wybrać Detach VHD:

Po odłączeniu dysku mamy wygenerowany i wypełniony danymi plik VHD. Możemy teraz przejść do umieszczenia go w Azure. Należy jednak pamiętać, że VHD musi zostać umieszczone jako Page Blob, a nie Block Blob. Zaczynamy od stworzenia bloba:

CloudPageBlob blob = blobStorage.GetPageBlobReference(uri);

blob.Properties.ContentType = "binary/octet-stream";

blob.Create(blobSize);

Możemy teraz przejść do implementacji funkcji odpowiedzialnej za wysyłanie danych (kod bazuje na Stéphane’s Page Blob Upload tool):

int singlePageSize = 1024 * 1024;

int numPages = (int)(file.Length / singlePageSize);

byte[] page = new byte[singlePageSize];

using (MemoryStream memoryStream = new MemoryStream(page))

{

    for (int i = 0; i < numPages; i++)

    {

        file.Read(page, 0, singlePageSize);

        memoryStream.Seek(0, SeekOrigin.Begin);

        blob.WritePages(memoryStream, i * singlePageSize);

    }

    if (blobSize > numPages * pageSize)

    {

        int remain = (int)(blobSize - (numPages * pageSize));

        page = new byte[remain];           

        using (MemoryStream remainMemoryStream = new MemoryStream(page))

        {

            file.Read(page, 0, remain);

            remainMemoryStream.Seek(0, SeekOrigin.Begin);

            blob.WritePages(remainMemoryStream, numPages * singlePageSize);

        }

    }

}

Po wywołaniu powyższego kodu będziemy mieli stworzonego bloba zawierającego dysk VHD.

Różnice między Development Fabric a Azure Fabric

Środowisko symulacyjne DevFabric obsługuje Azure Drive w sposób inny niż realne środowisko. Pliki VHD nie są dołączane, tak jak to zostało pokazane w Azure Fabric. Wywołanie metody CloudDrive.Create() powoduje  tak naprawdę utworzenie odpowiedniego folderu na dysku. Azure Drive w DevFabric nie stanowi blobów stronicowanych z podpiętymi VHD. Z tego  wynika, że nie można korzystać ze standardowych metod przeznaczonych do operowania na blobach. Ponadto nie jest możliwe wykorzystanie dysków umieszczonych w prawdziwym środowisku z poziomu aplikacji zahostowanej w DevFabric. Azure Drive w DevFabric nie będzie widoczny w listingach blobów, ponieważ nie jest on wewnętrznie przechowany jako blob.

Zakończenie

Wsparcie dla wirtualnych dysków znacząco upraszcza migrację rozwiązań opartych na klasycznych rozwiązaniach wykorzystujących przestrzeń System.IO. Ponadto programiści mogą stworzyć lokalnie dysk w postaci pliku VHD, a następnie wysłać go na serwer – dzięki temu zachowują całą strukturę katalogów i nie muszą jej drugi raz tworzyć w Azure (należy oczywiście pamiętać o tym, że blob przechowujący dysk jest stronicowany i tworzy się go za pomocą metody WritePages).


          

Piotr Zieliński

Absolwent informatyki o specjalizacji inżynieria oprogramowania Uniwersytetu Zielonogórskiego. Posiada szereg certyfikatów z technologii Microsoft (MCP, MCTS, MCPD). W 2011 roku wyróżniony nagrodą MVP w kategorii Visual C#. Aktualnie pracuje w General Electric pisząc oprogramowanie wykorzystywane w monitorowaniu transformatorów . Platformę .NET zna od wersji 1.1 – wcześniej wykorzystywał głównie MFC oraz C++ Builder. Interesuje się wieloma technologiami m.in. ASP.NET MVC, WPF, PRISM, WCF, WCF Data Services, WWF, Azure, Silverlight, WCF RIA Services, XNA, Entity Framework, nHibernate. Oprócz czystych technologii zajmuje się również wzorcami projektowymi, bezpieczeństwem aplikacji webowych i testowaniem oprogramowania od strony programisty. W wolnych chwilach prowadzi blog o .NET i tzw. patterns & practices.