구조화되지 않은 Blob Storage(Azure를 사용하여 Real-World Cloud Apps 빌드)

작성자 : Rick Anderson, Tom Dykstra

수정 프로젝트 다운로드 또는 전자책 다운로드

Azure 전자책 을 사용하여 Real World Cloud Apps 빌드 는 Scott Guthrie가 개발한 프레젠테이션을 기반으로 합니다. 클라우드용 웹앱을 성공적으로 개발하는 데 도움이 될 수 있는 13개의 패턴과 사례를 설명합니다. 전자책에 대한 자세한 내용은 첫 번째 챕터를 참조하세요.

이전 챕터에서는 분할 체계를 살펴보고 수정 앱이 Azure Storage Blob 서비스 및 기타 작업 데이터를 Azure SQL Database에 저장하는 방법을 설명했습니다. 이 챕터에서는 Blob 서비스에 대해 자세히 알아보고 수정 프로젝트 코드에서 구현하는 방법을 보여 드립니다.

Blob Storage란?

Azure Storage Blob 서비스는 클라우드에 파일을 저장하는 방법을 제공합니다. Blob 서비스는 로컬 네트워크 파일 시스템에 파일을 저장하는 데 비해 여러 가지 이점이 있습니다.

  • 확장성이 뛰어나다. 단일 Storage 계정은 수백 테라바이트 단위를 저장할 수 있으며 여러 Storage 계정을 가질 수 있습니다. 가장 큰 Azure 고객 중 일부는 수백 페타바이트 단위를 저장합니다. Microsoft SkyDrive는 Blob Storage를 사용합니다.
  • 내구성이 있습니다. Blob 서비스에 저장하는 모든 파일이 자동으로 백업됩니다.
  • 고가용성을 제공합니다. 스토리지용 SLA는 선택한 지역 중복 옵션에 따라 99.9% 또는 99.99% 가동 시간을 약속합니다.
  • Azure의 PaaS(Platform-as-a-Service) 기능입니다. 즉, 파일을 저장하고 검색하고, 사용하는 실제 스토리지 양에 대해서만 비용을 지불하고, Azure는 서비스에 필요한 모든 VM 및 디스크 드라이브를 자동으로 설정 및 관리합니다.
  • REST API를 사용하거나 프로그래밍 언어 API를 사용하여 Blob 서비스에 액세스할 수 있습니다. SDK는 .NET, Java, Ruby 등에서 사용할 수 있습니다.
  • Blob 서비스에 파일을 저장하면 인터넷을 통해 파일을 쉽게 공개적으로 사용할 수 있습니다.
  • 권한이 부여된 사용자만 액세스할 수 있도록 Blob 서비스에서 파일을 보호하거나 제한된 기간 동안만 다른 사용자가 사용할 수 있도록 하는 임시 액세스 토큰을 제공할 수 있습니다.

Azure용 앱을 빌드할 때마다 온-프레미스 환경에서 이미지, 비디오, PDF, 스프레드시트 등 파일로 이동하는 많은 데이터를 저장하려고 합니다. -- Blob 서비스를 고려합니다.

스토리지 계정 만들기

Blob 서비스를 시작하려면 Azure에서 Storage 계정을 만듭니다. 포털에서 -- Data Services -- Storage -- 빠른 만들기를 클릭한 다음 URL 및 데이터 센터 위치를 입력합니다. 데이터 센터 위치는 웹앱과 동일해야 합니다.

스토리지 acct 만들기

콘텐츠를 저장할 주 지역을 선택하고 지역 복제 옵션을 선택하면 Azure는 국가/지역의 다른 부분에 있는 다른 데이터 센터에 모든 데이터의 복제본을 만듭니다. 예를 들어 미국 서부 데이터 센터를 선택하는 경우 파일을 저장할 때 미국 서부 데이터 센터로 전달되지만 백그라운드에서 Azure는 다른 미국 데이터 센터 중 하나에 복사합니다. 국가/지역의 한 부분에서 재해가 발생하면 데이터는 여전히 안전합니다.

Azure는 지리적 정치적 경계를 넘어 데이터를 복제하지 않습니다. 기본 위치가 미국에 있는 경우 파일은 미국 내의 다른 지역에만 복제됩니다. 기본 위치가 오스트레일리아인 경우 파일은 오스트레일리아의 다른 데이터 센터에만 복제됩니다.

물론 앞에서 보았듯이 스크립트에서 명령을 실행하여 Storage 계정을 만들 수도 있습니다. 스토리지 계정을 만드는 Windows PowerShell 명령은 다음과 같습니다.

# Create a new storage account
New-AzureStorageAccount -StorageAccountName $Name -Location $Location -Verbose

Storage 계정이 있으면 Blob 서비스에 파일 저장을 즉시 시작할 수 있습니다.

수정 앱에서 Blob Storage 사용

수정 앱을 사용하면 사진을 업로드할 수 있습니다.

수정 작업 만들기

FixIt 만들기를 클릭하면 애플리케이션이 지정된 이미지 파일을 업로드하고 Blob 서비스에 저장합니다.

Blob 컨테이너 설정

Blob 서비스에 파일을 저장하려면 파일을 저장할 컨테이너 가 필요합니다. Blob 서비스 컨테이너는 파일 시스템 폴더에 해당합니다. 모든 항목 자동화 챕터에서 검토한 환경 만들기 스크립트는 Storage 계정을 만들지만 컨테이너를 만들지는 않습니다. 따라서 클래스의 CreateAndConfigure 메서드 PhotoService 는 컨테이너가 아직 없는 경우 컨테이너를 만드는 것입니다. 이 메서드는 Global.asaxApplication_Start 메서드에서 호출됩니다.

public async void CreateAndConfigureAsync()
{
    try
    {
        CloudStorageAccount storageAccount = StorageUtils.StorageAccount;

        // Create a blob client and retrieve reference to images container
        CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
        CloudBlobContainer container = blobClient.GetContainerReference("images");

        // Create the "images" container if it doesn't already exist.
        if (await container.CreateIfNotExistsAsync())
        {
            // Enable public access on the newly created "images" container
            await container.SetPermissionsAsync(
                new BlobContainerPermissions
                {
                    PublicAccess =
                        BlobContainerPublicAccessType.Blob
                });

            log.Information("Successfully created Blob Storage Images Container and made it public");
        }
    }
    catch (Exception ex)
    {
        log.Error(ex, "Failure to Create or Configure images container in Blob Storage Service");
    }
}

스토리지 계정 이름 및 액세스 키는 Web.config 파일의 컬렉션에 appSettingsStorageUtils.StorageAccount 저장되며 메서드의 코드는 이러한 값을 사용하여 연결 문자열을 빌드하고 연결을 설정합니다.

string account = CloudConfigurationManager.GetSetting("StorageAccountName");
string key = CloudConfigurationManager.GetSetting("StorageAccountAccessKey");
string connectionString = String.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}", account, key);
return CloudStorageAccount.Parse(connectionString);

그런 다음, 메서드는 CreateAndConfigureAsync Blob 서비스를 나타내는 개체와 Blob 서비스에서 "images"라는 컨테이너(폴더)를 나타내는 개체를 만듭니다.

CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("images");

새 스토리지 계정에 대해 앱을 처음 실행할 때 true인 "images"라는 컨테이너가 아직 없는 경우 코드는 컨테이너를 만들고 권한을 설정하여 공개합니다. (기본적으로 새 Blob 컨테이너는 프라이빗이며 스토리지 계정에 액세스할 수 있는 권한이 있는 사용자만 액세스할 수 있습니다.)

if (await container.CreateIfNotExistsAsync())
{
    // Enable public access on the newly created "images" container
    await container.SetPermissionsAsync(
        new BlobContainerPermissions
        {
            PublicAccess =
                BlobContainerPublicAccessType.Blob
        });

    log.Information("Successfully created Blob Storage Images Container and made it public");
}

Blob Storage에 업로드된 사진 저장

이미지 파일을 업로드하고 저장하기 위해 앱은 인터페이스와 클래스의 인터페이스 구현을 PhotoService 사용합니다IPhotoService. PhotoService.cs 파일에는 Blob 서비스와 통신하는 수정 앱의 모든 코드가 포함되어 있습니다.

다음 MVC 컨트롤러 메서드는 사용자가 FixIt 만들기를 클릭할 때 호출됩니다. 이 코드에서 는 photoService 클래스의 PhotoService instance 참조하고 fixittask 새 작업에 대한 데이터를 저장하는 엔터티 클래스의 FixItTask instance 참조합니다.

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "FixItTaskId,CreatedBy,Owner,Title,Notes,PhotoUrl,IsDone")]FixItTask fixittask, HttpPostedFileBase photo)
{
    if (ModelState.IsValid)
    {
        fixittask.CreatedBy = User.Identity.Name;
        fixittask.PhotoUrl = await photoService.UploadPhotoAsync(photo);
        await fixItRepository.CreateAsync(fixittask);
        return RedirectToAction("Success");
    }

    return View(fixittask);
}

클래스의 PhotoService 메서드는 UploadPhotoAsync 업로드된 파일을 Blob 서비스에 저장하고 새 Blob을 가리키는 URL을 반환합니다.

public async Task<string> UploadPhotoAsync(HttpPostedFileBase photoToUpload)
{            
    if (photoToUpload == null || photoToUpload.ContentLength == 0)
    {
        return null;
    }

    string fullPath = null;
    Stopwatch timespan = Stopwatch.StartNew();

    try
    {
        CloudStorageAccount storageAccount = StorageUtils.StorageAccount;

        // Create the blob client and reference the container
        CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
        CloudBlobContainer container = blobClient.GetContainerReference("images");

        // Create a unique name for the images we are about to upload
        string imageName = String.Format("task-photo-{0}{1}",
            Guid.NewGuid().ToString(),
            Path.GetExtension(photoToUpload.FileName));

        // Upload image to Blob Storage
        CloudBlockBlob blockBlob = container.GetBlockBlobReference(imageName);
        blockBlob.Properties.ContentType = photoToUpload.ContentType;
        await blockBlob.UploadFromStreamAsync(photoToUpload.InputStream);

        // Convert to be HTTP based URI (default storage path is HTTPS)
        var uriBuilder = new UriBuilder(blockBlob.Uri);
        uriBuilder.Scheme = "http";
        fullPath = uriBuilder.ToString();

        timespan.Stop();
        log.TraceApi("Blob Service", "PhotoService.UploadPhoto", timespan.Elapsed, "imagepath={0}", fullPath);
    }
    catch (Exception ex)
    {
        log.Error(ex, "Error upload photo blob to storage");
    }

    return fullPath;
}

메서드에서 CreateAndConfigure 와 같이 코드는 스토리지 계정에 연결하고 컨테이너가 이미 있다고 가정하는 경우를 제외하고 "images" Blob 컨테이너를 나타내는 개체를 만듭니다.

그런 다음, 새 GUID 값을 파일 확장자와 연결하여 업로드할 이미지에 대한 고유 식별자를 만듭니다.

string imageName = String.Format("task-photo-{0}{1}",
    Guid.NewGuid().ToString(),
    Path.GetExtension(photoToUpload.FileName));

그런 다음, 코드는 Blob 컨테이너 개체와 새 고유 식별자를 사용하여 Blob 개체를 만들고, 해당 개체의 파일 종류를 나타내는 특성을 설정한 다음, Blob 개체를 사용하여 Blob Storage에 파일을 저장합니다.

CloudBlockBlob blockBlob = container.GetBlockBlobReference(imageName);
blockBlob.Properties.ContentType = photoToUpload.ContentType;
blockBlob.UploadFromStream(photoToUpload.InputStream);

마지막으로 Blob을 참조하는 URL을 가져옵니다. 이 URL은 데이터베이스에 저장되며 수정 웹 페이지에서 업로드된 이미지를 표시하는 데 사용할 수 있습니다.

fullPath = String.Format("http://{0}{1}", blockBlob.Uri.DnsSafeHost, blockBlob.Uri.AbsolutePath);

이 URL은 FixItTask 테이블의 열 중 하나로 데이터베이스에 저장됩니다.

public class FixItTask
{
    public int FixItTaskId  { get; set; }
    public string CreatedBy { get; set; }
    [Required]
    public string Owner     { get; set; }
    [Required]
    public string Title     { get; set; }
    public string Notes     { get; set; }
    public string PhotoUrl  { get; set; }
    public bool IsDone      { get; set; } 
}

데이터베이스의 URL과 Blob Storage의 이미지만 있는 Fix It 앱은 데이터베이스를 작고 확장 가능하며 저렴하게 유지하며, 스토리지가 저렴하고 테라바이트 또는 페타바이트 처리가 가능한 곳에 이미지가 저장됩니다. 하나의 스토리지 계정은 수백 테라바이트의 수정 사진을 저장할 수 있으며, 사용한 것에 대해서만 비용을 지불할 수 있습니다. 따라서 첫 번째 기가바이트에 대해 9센트를 지불하는 작은 비용을 시작하고 추가 기가바이트당 동전에 대한 이미지를 더 추가할 수 있습니다.

업로드된 파일 표시

수정 애플리케이션은 작업에 대한 세부 정보를 표시할 때 업로드된 이미지 파일을 표시합니다.

사진으로 작업 세부 정보 수정

이미지를 표시하기 위해 MVC 보기가 해야 할 일은 브라우저로 전송된 HTML의 값을 포함하는 PhotoUrl 것입니다. 웹 서버와 데이터베이스는 주기를 사용하여 이미지를 표시하지 않고 이미지 URL에 몇 바이트만 제공합니다. 다음 Razor 코드에서 는 Model 엔터티 클래스의 FixItTask instance 참조합니다.

<fieldset>
<legend>@Html.DisplayFor(model => model.Title)</legend>
<dl>
    <dt>Opened By</dt>
    <dd>@Html.DisplayFor(model => model.CreatedBy)</dd>
                <br />
    <dt>@Html.DisplayNameFor(model => model.Notes)</dt>
    <dd>@Html.DisplayFor(model => model.Notes)</dd>
                <br />
                @if(Model.PhotoUrl != null) {
        <dd><img src="@Model.PhotoUrl" title="@Model.Title" /></dd>
                }
</dl>
</fieldset>

표시되는 페이지의 HTML을 보면 다음과 같이 Blob Storage의 이미지를 직접 가리키는 URL이 표시됩니다.

<fieldset>
<legend>Brush the dog again</legend>
<dl>
    <dt>Opened By</dt>
    <dd>Tom</dd>
                <br />
    <dt>Notes</dt>
    <dd>Another dog brushing task</dd>
                <br />
    <dd>
<img src="http://storageaccountname.blob.core.windows.net/images/task-photo-312dd635-ba87-4542-8b15-767032c55f4e.jpg" 
           title="Brush the dog again" />
    </dd>
</dl>
</fieldset>

요약

수정 앱이 Blob 서비스에 이미지를 저장하는 방법과 SQL 데이터베이스의 이미지 URL만 저장하는 방법을 살펴보았습니다. Blob 서비스를 사용하면 SQL 데이터베이스가 다른 작업보다 훨씬 작게 유지되고, 거의 무제한으로 확장할 수 있으며, 많은 코드를 작성하지 않고도 수행할 수 있습니다.

스토리지 계정에 수백 테라바이트가 있을 수 있으며 스토리지 비용은 SQL Database 스토리지보다 훨씬 저렴하며, 매월 기가바이트당 약 3센트에서 시작하여 작은 트랜잭션 요금이 부과됩니다. 그리고 최대 용량에 대한 비용을 지불하는 것이 아니라 실제로 저장하는 양에 대해서만 지불하고 있으므로 앱의 크기를 조정할 준비가 되었지만 모든 추가 용량에 대한 비용을 지불하지는 않습니다.

다음 챕터에서는 오류를 정상적으로 처리할 수 있는 클라우드 앱을 만드는 것의 중요성에 대해 설명합니다.

리소스

자세한 내용은 다음 리소스를 참조하세요.