부록: 수정 샘플 애플리케이션(Azure를 사용하여 Real-World Cloud Apps 빌드)

작성자 : Rick Anderson, Tom Dykstra

수정 프로젝트 다운로드

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

Azure e-book을 사용하여 Real World Cloud Apps 빌드에 대한 이 부록에는 다운로드할 수 있는 Fix It 샘플 애플리케이션에 대한 추가 정보를 제공하는 다음 섹션이 포함되어 있습니다.

알려진 문제

수정 앱은 원래 이 전자책에 제시된 패턴 중 일부를 가능한 한 간단하게 설명하기 위해 개발되었습니다. 그러나 전자책은 실제 앱을 빌드하는 것에 관한 것이므로 릴리스된 소프트웨어에 대해 수행하는 것과 유사한 검토 및 테스트 프로세스에 수정 코드를 적용했습니다. 우리는 많은 문제를 발견했고, 실제 애플리케이션과 마찬가지로 일부는 수정했으며 일부는 이후 릴리스로 연기했습니다.

다음 목록에는 프로덕션 애플리케이션에서 해결해야 하는 문제가 포함되어 있지만, 한 가지 이유 또는 다른 이유로 Fix It 샘플 애플리케이션의 초기 릴리스에서 다루지 않기로 결정했습니다.

보안

  • 존재하지 않는 소유자에게 작업을 할당할 수 없는지 확인합니다.
  • 사용자가 만들거나 할당한 작업만 보고 수정할 수 있는지 확인합니다.
  • 로그인 페이지 및 인증 쿠키에 HTTPS를 사용합니다.
  • 인증 쿠키에 대한 시간 제한을 지정합니다.

입력 유효성 검사

일반적으로 프로덕션 앱은 수정 앱보다 더 많은 입력 유효성 검사를 수행합니다. 예를 들어 업로드에 허용되는 이미지 크기/이미지 파일 크기는 제한되어야 합니다.

관리자 기능

관리자는 기존 작업에 대한 소유권을 변경할 수 있어야 합니다. 예를 들어 작업 작성자는 회사를 떠날 수 있으며, 관리 액세스가 활성화되지 않는 한 작업을 유지할 권한이 있는 사람은 아무도 없을 수 있습니다.

큐 메시지 처리

Fix It 앱의 큐 메시지 처리는 최소한의 코드로 큐 중심 작업 패턴을 설명하기 위해 간단하게 설계되었습니다. 이 간단한 코드는 실제 프로덕션 애플리케이션에 적합하지 않습니다.

  • 이 코드는 각 큐 메시지가 한 번에 처리되도록 보장하지 않습니다. 큐에서 메시지를 받으면 다른 큐 수신기에 메시지가 표시되지 않는 시간 제한 기간이 있습니다. 메시지가 삭제되기 전에 시간 제한이 만료되면 메시지가 다시 표시됩니다. 따라서 작업자 역할 instance 메시지를 처리하는 데 오랜 시간이 걸리는 경우 이론적으로 동일한 메시지가 두 번 처리되어 데이터베이스에서 중복 작업이 발생할 수 있습니다. 이 문제에 대한 자세한 내용은 Azure Storage 큐 사용을 참조하세요.
  • 큐 폴링 논리는 메시지 검색을 일괄 처리하여 비용 효율적일 수 있습니다. CloudQueue.GetMessageAsync를 호출할 때마다 트랜잭션 비용이 발생합니다. 대신 단일 트랜잭션에서 여러 메시지를 가져오는 CloudQueue.GetMessagesAsync(복수 's')를 호출할 수 있습니다. Azure Storage 큐의 트랜잭션 비용은 매우 낮으므로 대부분의 시나리오에서 비용에 미치는 영향은 크지 않습니다.
  • 큐 메시지 처리 코드의 타이트한 루프는 CPU 선호도를 유발하므로 다중 코어 VM을 효율적으로 활용하지 못합니다. 더 나은 디자인은 작업 병렬 처리를 사용하여 여러 비동기 작업을 병렬로 실행합니다.
  • 큐 메시지 처리에는 기본적인 예외 처리만 있습니다. 예를 들어 코드는 포이즌 메시지를 처리하지 않습니다. (메시지 처리로 인해 예외가 발생하면 오류를 기록하고 메시지를 삭제해야 합니다. 또는 작업자 역할이 다시 처리하려고 하면 루프가 무기한으로 계속됩니다.)

SQL 쿼리가 바인딩되지 않음

현재 수정 코드는 인덱스 페이지에 대한 쿼리가 반환할 수 있는 행 수에 제한을 두지 않습니다. 데이터베이스에 대량의 작업을 입력하면 수신된 결과 목록의 크기로 인해 성능 문제가 발생할 수 있습니다. 솔루션은 페이징을 구현하는 것입니다. 예제는 ASP.NET MVC 애플리케이션에서 Entity Framework를 사용하여 정렬, 필터링 및 페이징을 참조하세요.

Fix It 앱은 FixItTask 엔터티 클래스를 사용하여 컨트롤러와 뷰 간에 정보를 전달합니다. 모범 사례는 보기 모델을 사용하는 것입니다. 도메인 모델(예: FixItTask 엔터티 클래스)은 데이터 지속성에 필요한 것을 중심으로 설계되었으며 뷰 모델은 데이터 프레젠테이션을 위해 설계될 수 있습니다.

수정 앱은 업로드된 이미지를 공용으로 저장하므로 URL을 찾은 사람은 누구나 이미지에 액세스할 수 있습니다. 이미지는 공용이 아닌 보안이 유지될 수 있습니다.

큐에 대한 PowerShell 자동화 스크립트 없음

샘플 PowerShell 자동화 스크립트는 Azure App Service Web Apps 완전히 실행되는 수정의 기본 버전에 대해서만 작성되었습니다. 큐 처리에 필요한 웹앱 및 클라우드 서비스 환경을 설정하고 배포하기 위한 스크립트를 제공하지 않았습니다.

사용자 입력의 HTML 코드에 대한 특수 처리

ASP.NET 악의적인 사용자가 사용자 입력 텍스트 상자에 스크립트를 입력하여 사이트 간 스크립팅 공격을 시도할 수 있는 여러 가지 방법을 자동으로 방지합니다. 또한 작업 제목 및 노트를 표시하는 데 사용되는 MVC DisplayFor 도우미는 브라우저에 보내는 값을 자동으로 HTML로 인코딩합니다. 그러나 프로덕션 앱에서는 추가 조치를 취할 수 있습니다. 자세한 내용은 ASP.NET 유효성 검사 요청을 참조하세요.

모범 사례

다음은 수정 앱의 원래 버전의 코드 검토 및 테스트에서 검색된 후 해결된 몇 가지 문제입니다. 일부는 원래 코더가 특정 모범 사례를 인식하지 못하여 발생했으며 일부는 코드가 신속하게 작성되었으며 릴리스된 소프트웨어를 위한 것이 아니었기 때문입니다. 웹앱을 개발하는 다른 사용자에게 도움이 될 수 있는 이 검토 및 테스트에서 배운 내용이 있는 경우 여기에 문제를 나열하고 있습니다.

데이터베이스 리포지토리 삭제

클래스는 FixItTaskRepository Entity Framework DbContext instance 삭제해야 합니다. 클래스에서 를 구현하여 IDisposable 이 작업을 수행했습니다 FixItTaskRepository .

public class FixItTaskRepository : IFixItTaskRepository, IDisposable
{
    private MyFixItContext db = new MyFixItContext();

    // other code not shown

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Free managed resources.
            if (db != null)
            {
                db.Dispose();
                db = null;
            }
        }
    }
}

AutoFac은 instance 자동으로 삭제 FixItTaskRepository 하므로 명시적으로 삭제할 필요가 없습니다.

또 다른 옵션은 에서 FixItTaskRepository멤버 변수를 DbContext 제거하고 대신 문 내에서 각 리포지토리 메서드 내에 로컬 DbContext 변수를 using 만드는 것입니다. 예를 들면 다음과 같습니다.

// Alternate way to dispose the DbContext
using (var db = new MyFixItContext())
{
    fixItTask = await db.FixItTasks.FindAsync(id);
}

DI와 같은 싱글톤 등록

클래스와 클래스의 PhotoService instance 하나만 필요하므로 이러한 클래스는 DependenciesConfig.cs에서 종속성 주입을 위한 단일 인스턴스로 등록되어야 합니다.Logger

builder.RegisterType<Logger>().As<ILogger>().SingleInstance();
builder.RegisterType<FixItTaskRepository>().As<IFixItTaskRepository>();
builder.RegisterType<PhotoService>().As<IPhotoService>().SingleInstance();

보안: 사용자에게 오류 세부 정보를 표시하지 않음

원래 수정 앱에는 일반 오류 페이지가 없었고 모든 예외가 UI에 버블 업되도록 하므로 데이터베이스 연결 오류와 같은 일부 예외로 인해 전체 스택 추적이 브라우저에 표시될 수 있습니다. 자세한 오류 정보는 악의적인 사용자의 공격을 용이하게 할 수 있습니다. 해결 방법은 예외 세부 정보를 기록하고 오류 세부 정보를 포함하지 않는 사용자에게 오류 페이지를 표시하는 것입니다. 수정 앱이 이미 로깅되고 있으며 오류 페이지를 표시하기 위해 Web.config 파일에 추가 <customErrors mode=On> 했습니다.

<system.web>
  <customErrors mode="On"/>
  <authentication mode="None" />
  <compilation debug="true" targetFramework="4.5" />
  <httpRuntime targetFramework="4.5" />
</system.web>

기본적으로 이렇게 하면 Views\Shared\Error.cshtml 이 오류에 대해 표시됩니다. Error.cshtml을 사용자 지정하거나 고유한 오류 페이지 보기를 만들고 특성을 추가할 defaultRedirect 수 있습니다. 특정 오류에 대해 다른 오류 페이지를 지정할 수도 있습니다.

보안: 작성자가 작업을 편집할 수 있도록 허용

대시보드 인덱스 페이지에는 로그온한 사용자가 만든 작업만 표시되지만 악의적인 사용자는 ID가 있는 URL을 다른 사용자의 작업에 만들 수 있습니다. 이 경우 404를 반환하는 코드를 DashboardController.cs 에 추가했습니다.

public async Task<ActionResult> Edit(int id)
{
    FixItTask fixittask = await fixItRepository.FindTaskByIdAsync(id);
    if (fixittask == null)
    {
        return HttpNotFound();
    }

    // Verify logged in user owns this FixIt task.
    if (User.Identity.Name != fixittask.Owner)
    {
       return HttpNotFound();
    }

    return View(fixittask);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(int id, [Bind(Include = "CreatedBy,Owner,Title,Notes,PhotoUrl,IsDone")]FormCollection form)
{
    FixItTask fixittask = await fixItRepository.FindTaskByIdAsync(id);

    // Verify logged in user owns this FixIt task.
    if (User.Identity.Name != fixittask.Owner)
    {
       return HttpNotFound();
    }

    if (TryUpdateModel(fixittask, form))
    {
        await fixItRepository.UpdateAsync(fixittask);
        return RedirectToAction("Index");
    }

    return View(fixittask);
}

예외를 삼키지 마세요.

원래 Fix It 앱은 SQL 쿼리에서 발생한 예외를 로깅한 후 null을 반환했습니다.

catch (Exception e)
{
    log.Error(e, "Error in FixItTaskRepository.FindTasksByOwnerAsync(userName={0})", userName);
    return null;
}

이렇게 하면 쿼리가 성공했지만 행을 반환하지 않은 것처럼 사용자에게 표시됩니다. 해결 방법은 catch 및 로깅 후 예외를 다시 throw하는 것입니다.

catch (Exception e)
{
    log.Error(e, "Error in FixItTaskRepository.FindTasksByCreatorAsync(creater={0})", creator);
    throw;
}

작업자 역할의 모든 예외 catch

작업자 역할의 처리되지 않은 예외로 인해 VM이 재활용되므로 try-catch 블록에서 수행하는 모든 작업을 래핑하고 모든 예외를 처리하려고 합니다.

엔터티 클래스에서 문자열 속성의 길이 지정

간단한 코드를 표시하기 위해 Fix It 앱의 원래 버전은 FixItTask 엔터티의 필드에 대한 길이를 지정하지 않았고 결과적으로 데이터베이스에서 varchar(max)로 정의되었습니다. 결과적으로 UI는 거의 모든 양의 입력을 허용합니다. 길이를 지정하면 웹 페이지의 사용자 입력과 데이터베이스의 열 크기에 모두 적용되는 제한이 설정됩니다.

public class FixItTask
{
    public int FixItTaskId  { get; set; }
    [StringLength(80)]
    public string CreatedBy { get; set; }
    [Required]
    [StringLength(80)]
    public string Owner { get; set; }
    [Required]
    [StringLength(80)]
    public string Title { get; set; }
    [StringLength(1000)]
    public string Notes { get; set; }
    [StringLength(200)]
    public string PhotoUrl { get; set; }
    public bool IsDone      { get; set; }  
}

프라이빗 멤버가 변경되지 않을 때 읽기 전용으로 표시

예를 들어 클래스에서 DashboardControllerFixItTaskRepository instance 만들어지고 변경될 것으로 예상되지 않으므로 읽기 전용으로 정의했습니다.

public class DashboardController : Controller
    {
        private readonly IFixItTaskRepository fixItRepository = null;

목록을 사용합니다. 목록 대신 Any() Count() > 0

목록에 있는 하나 이상의 항목이 지정된 조건에 맞는지 여부만 중요하면 조건을 충족하는 항목이 반환되는 즉시 반환되는 반면 Count 메서드는 항상 모든 항목을 반복해야 하므로 Any 메서드를 사용합니다. 대시보드 Index.cshtml 파일에는 원래 다음 코드가 있었습니다.

@if (Model.Count() == 0) {
    <br />
    <div>You don't have anything currently assigned to you!!!</div>
}

이를 다음과 같이 변경했습니다.

@if (!Model.Any()) {
    <br />
    <div>You don't have anything currently assigned to you!!!</div>
}

MVC 도우미를 사용하여 MVC 뷰에서 URL 생성

홈페이지의 수정 만들기 단추의 경우 수정 앱은 앵커 요소를 하드 코딩했습니다.

<a href="/Tasks/Create" class="btn btn-primary btn-large">Create a New FixIt &raquo;</a>

이와 같은 보기/작업 링크의 경우 Url.Action HTML 도우미를 사용하는 것이 좋습니다. 예를 들면 다음과 같습니다.

@Url.Action("Create","Tasks")

작업자 역할에서 Thread.Sleep 대신 Task.Delay 사용

새 프로젝트 템플릿은 작업자 역할에 대한 샘플 코드를 배치 Thread.Sleep 하지만 스레드가 절전 모드로 설정되면 스레드 풀이 불필요한 추가 스레드를 생성할 수 있습니다. 대신 Task.Delay 를 사용하여 이를 방지할 수 있습니다.

while (true)
{
    try
    {
        await queueManager.ProcessMessagesAsync();
    }
    catch (Exception ex)
    {
        logger.Error(ex, "Exception in worker role Run loop.");
    }
    await Task.Delay(1000);
}

비동기 void 방지

비동기 메서드가 값을 반환할 필요가 없는 경우 가 아닌 void형식을 Task 반환합니다.

이 예제는 클래스에서 가져옵니다.FixItQueueManager

// Correct
public async Task SendMessageAsync(FixItTask fixIt) { ... }

// Incorrect
public async void SendMessageAsync(FixItTask fixIt) { ... }

최상위 이벤트 처리기에만 사용해야 async void 합니다. 메서드를 로 async void정의하는 경우 호출자는 메서드를 대기 하거나 메서드가 throw하는 예외를 catch할 수 없습니다. 자세한 내용은 비동기 프로그래밍의 모범 사례를 참조하세요.

취소 토큰을 사용하여 작업자 역할 루프에서 중단

일반적으로 작업자 역할의 Run 메서드에는 무한 루프가 포함됩니다. 작업자 역할이 중지되면 RoleEntryPoint.OnStop 메서드가 호출됩니다. 이 메서드를 사용하여 Run 메서드 내에서 수행 중인 작업을 취소하고 정상적으로 종료해야 합니다. 그렇지 않으면 작업 중간에 프로세스가 종료될 수 있습니다.

자동 MIME 스니핑 프로시저 옵트아웃

경우에 따라 인터넷 Explorer 웹 서버에서 지정한 형식과 다른 MIME 형식을 보고합니다. instance 경우 인터넷 Explorer HTTP 응답 헤더 Content-Type: text/plain과 함께 전달된 파일에서 HTML 콘텐츠를 찾으면 인터넷 Explorer 콘텐츠를 HTML로 렌더링해야 한다고 결정합니다. 아쉽게도 이 "MIME 스니핑"은 신뢰할 수 없는 콘텐츠를 호스팅하는 서버의 보안 문제로 이어질 수도 있습니다. 이 문제를 해결하기 위해 Internet Explorer 8은 MIME 형식 결정 코드를 여러 번 변경했으며 애플리케이션 개발자가 MIME 스니핑을 옵트아웃할 수 있도록 합니다. 다음 코드가 Web.config 파일에 추가되었습니다.

<system.webServer>
     <httpProtocol>
        <customHeaders>
           <add name="X-Content-Type-Options" value="nosniff"/>
        </customHeaders>
     </httpProtocol>
     <modules>
      <remove name="FormsAuthenticationModule" />
    </modules>
  </system.webServer>

번들링 및 축소 사용

Visual Studio에서 새 웹 프로젝트를 만들 때 JavaScript 파일의 묶음 및 축소는 기본적으로 사용하도록 설정되지 않습니다. BundleConfig.cs에 코드 줄을 추가했습니다.

// For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862
public static void RegisterBundles(BundleCollection bundles)
{
    bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                "~/Scripts/jquery-{version}.js"));
 
   // Code removed for brevity/
 
   BundleTable.EnableOptimizations = true;
}

인증 쿠키에 대한 만료 시간 제한 설정

기본적으로 인증 쿠키는 2주 내에 만료됩니다. 더 짧은 시간이 더 안전합니다. StartupAuth.cs에서 이 설정을 변경할 수 있습니다.

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login"),
    ExpireTimeSpan = System.TimeSpan.FromMinutes(20)
});

로컬 컴퓨터의 Visual Studio에서 앱을 실행하는 방법

수정 앱을 실행하는 방법에는 두 가지가 있습니다.

  • 새 작업을 SQL 데이터베이스에 직접 쓰는 기본 애플리케이션을 실행합니다.
  • 큐와 백 엔드 서비스를 사용하여 애플리케이션을 실행하여 작업을 만듭니다. 큐 패턴은 큐 중심 작업 패턴 챕터에 설명되어 있습니다.

기본 애플리케이션 실행

  1. Visual Studio 2017을 설치합니다.
  2. Visual Studio용 .NET용 Azure SDK를 설치합니다.
  3. MSDN 코드 갤러리에서 .zip 파일을 다운로드합니다.
  4. 파일 탐색기 .zip 파일을 마우스 오른쪽 단추로 클릭하고 속성을 클릭한 다음 속성 창 차단 해제를 클릭합니다.
  5. 파일의 압축을 풉니다.
  6. .sln 파일을 두 번 클릭하여 Visual Studio를 시작합니다.
  7. 도구 메뉴에서 NuGet 패키지 관리자, 패키지 관리자 콘솔을 차례로 클릭합니다.
  8. PMC(패키지 관리자 콘솔)에서 복원을 클릭합니다.
  9. Visual Studio를 끝냅니다.
  10. Azure Storage 에뮬레이터를 시작합니다.
  11. Visual Studio를 다시 시작하여 이전 단계에서 닫은 솔루션 파일을 엽니다.
  12. FixIt 프로젝트가 시작 프로젝트로 설정되어 있는지 확인한 다음 Ctrl+F5를 눌러 프로젝트를 실행합니다.

큐 처리로 애플리케이션 실행

  1. 기본 애플리케이션 실행에 대한 지침을 따라 브라우저를 닫고 Visual Studio를 닫습니다.

  2. 관리자 권한으로 Visual Studio를 시작합니다. (Azure Compute Emulator를 사용하며 관리자 권한이 필요합니다.)

  3. MyFixIt 프로젝트(웹 프로젝트)의 애플리케이션 Web.config 파일에서 값을 appSettings/UseQueues "true"로 변경합니다.

    <appSettings>
        <!-- Other settings not shown -->
        <add key="UseQueues" value="true"/>
    </appSettings>
    
  4. Azure Storage 에뮬레이터가 계속 실행되고 있지 않으면 다시 시작합니다.

  5. FixIt 웹 프로젝트와 MyFixItCloudService 프로젝트를 동시에 실행합니다.

    Visual Studio 사용:

    1. F5 키를 눌러 FixIt 프로젝트를 실행합니다.
    2. 솔루션 탐색기 MyFixItCloudService 프로젝트를 마우스 오른쪽 단추로 클릭한 다음 디버그>새 인스턴스 시작을 클릭합니다.

    웹용 Visual Studio 2013 Express 사용:

    1. 솔루션 탐색기 FixIt 솔루션을 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.

    2. 여러 시작 프로젝트를 선택합니다.

    3. MyFixIt 및 MyFixItCloudService 아래의 작업 드롭다운 목록에서 시작을 선택합니다.

    4. 확인을 클릭합니다.

    5. F5 키를 눌러 두 프로젝트를 실행합니다.

      MyFixItCloudService 프로젝트를 실행하면 Visual Studio에서 Azure Compute Emulator를 시작합니다. 방화벽 구성에 따라 방화벽을 통해 에뮬레이터를 허용해야 할 수 있습니다.

Windows PowerShell 스크립트를 사용하여 기본 앱을 Azure App Service Web Apps 배포하는 방법

모든 항목 자동화 패턴을 설명하기 위해 수정 앱에는 Azure에서 환경을 설정하고 프로젝트를 새 환경에 배포하는 스크립트가 제공됩니다. 다음 지침에서는 스크립트를 사용하는 방법을 설명합니다.

큐를 사용하지 않고 Azure에서 실행하려는 경우 큐를 사용하여 로컬로 실행하도록 변경한 경우 다음 지침을 진행하기 전에 UseQueues appSetting 값을 다시 false로 설정해야 합니다.

이러한 지침에서는 이미 수정 솔루션을 로컬로 다운로드하여 실행했으며 Azure 계정이 있거나 관리할 권한이 있는 Azure 구독이 있다고 가정합니다.

  1. Azure PowerShell 콘솔을 설치합니다. 자세한 내용은 Azure PowerShell 설치 및 구성법을 참조하세요.

    이 사용자 지정된 콘솔은 Azure 구독에서 작동하도록 구성됩니다. Azure 모듈은 Program Files 디렉터리에 설치되며 Azure PowerShell 콘솔을 사용할 때마다 자동으로 가져옵니다.

    Windows PowerShell ISE와 같은 다른 호스트 프로그램에서 작업하려면 Import-Module cmdlet을 사용하여 Azure 모듈을 가져오거나 Azure 모듈의 명령을 사용하여 모듈의 자동 가져오기를 트리거해야 합니다.

  2. 관리자 권한으로 실행 옵션을 사용하여 Azure PowerShell 시작합니다.

  3. Set-ExecutionPolicy cmdlet을 실행하여 Azure PowerShell 실행 정책을 로 RemoteSigned설정합니다. Y(예)를 입력하여 정책 변경을 완료합니다.

    PS C:\> Set-ExecutionPolicy RemoteSigned
    

    이 설정을 사용하면 디지털 서명되지 않은 로컬 스크립트를 실행할 수 있습니다. 실행 정책을 Unrestricted로 설정하면 나중에 차단 해제 단계가 필요하지 않지만 보안상의 이유로 권장되지 않습니다.

  4. cmdlet을 Add-AzureAccount 실행하여 계정에 대한 자격 증명으로 PowerShell을 설정합니다.

    PS C:\> Add-AzureAccount
    

    이러한 자격 증명은 일정 기간 후에 만료되며 cmdlet을 Add-AzureAccount 다시 실행해야 합니다. 이 전자책을 작성할 때 자격 증명이 만료되기 전의 시간 제한은 12시간입니다.

  5. 여러 구독이 있는 경우 Select-AzureSubscription cmdlet을 사용하여 테스트 환경을 만들려는 구독을 지정합니다.

  6. Import-AzurePublishSettingsFile cmdlet을 사용하여 동일한 Azure 구독에 Get-AzurePublishSettingsFile 대한 관리 인증서를 가져옵니다. 이러한 cmdlet 중 첫 번째 cmdlet은 인증서 파일을 다운로드하고, 두 번째 cmdlet에서는 인증서 파일을 가져오기 위해 해당 파일의 위치를 지정합니다. > [! 중요]

    다운로드한 파일은 안전한 위치에 보관하거나, 완료되면 삭제합니다. Azure 서비스를 관리하는 데 사용할 수 있는 인증서가 포함되어 있기 때문입니다.

    PS C:\Users\username\Documents\Visual Studio 2013\Projects\MyFixIt\Automation> Get-AzurePublishSettingsFile
    PS C:\Users\username\Documents\Visual Studio 2013\Projects\MyFixIt\Automation> Import-AzurePublishSettingsFile "C:\Users
    \username\Downloads\Azure MSDN - Visual Studio Ultimate-12-14-2013-credentials.publishsettings"
    

    인증서는 SQL Database 서버에서 방화벽 규칙을 설정하기 위해 개발 머신의 IP 주소를 검색하는 REST API 호출에 사용됩니다.

  7. Set-Location cmdlet(별칭은 cd, chdirsl)을 실행하여 스크립트가 포함된 디렉터리로 이동합니다. (수정 솔루션 폴더의 Automation 폴더에 있습니다.) 디렉터리 이름에 공백이 포함되어 있는 경우 경로를 따옴표로 넣습니다. 예를 들어 디렉터리로 c:\Sample Apps\FixIt\Automation 이동하려면 다음 명령을 입력할 수 있습니다.

    PS C:\> cd "c:\Sample Apps\MyFixIt\Automation"
    
  8. Windows PowerShell 이러한 스크립트를 실행할 수 있도록 하려면 Unblock-File cmdlet을 사용합니다. (스크립트는 인터넷에서 다운로드되었기 때문에 차단됩니다.)

    경고

    보안 - 스크립트 또는 실행 파일에서 실행 Unblock-File 하기 전에 메모장에서 파일을 열고 명령을 검사한 다음 악성 코드가 포함되어 있지 않은지 확인합니다.

    예를 들어 다음 명령은 현재 디렉터리의 모든 스크립트에서 cmdlet을 실행 Unblock-File 합니다.

    PS C:\Sample Apps\FixIt\Automation> Unblock-File -Path .\*.ps1
    
  9. 기본(큐 처리 없음)에 대한 웹앱을 만들려면 앱 수정을 위해 환경 만들기 스크립트를 실행합니다.

    필수 Name 매개 변수는 데이터베이스의 이름을 지정하며 스크립트가 만드는 스토리지 계정에도 사용됩니다. 이름은 azurewebsites.net 도메인 내에서 전역적으로 고유해야 합니다. Fixit 또는 Test와 같이 고유하지 않은 이름을 지정하거나(예에서와 같이 fixitdemo) New-AzureWebsite cmdlet이 충돌을 보고하는 내부 오류와 함께 실패합니다. 스크립트는 웹앱, 스토리지 계정 및 데이터베이스에 대한 이름 요구 사항을 준수하기 위해 이름을 모든 소문자로 변환합니다.

    필수 SqlDatabasePassword 매개 변수는 SQL Database 대해 만들 관리자 계정의 암호를 지정합니다. 암호(& ;) <> 특수 XML 문자를 포함하지 마세요. 이는 Azure의 제한이 아니라 스크립트가 작성된 방식의 제한 사항입니다.

    예를 들어 "fixitdemo"라는 웹앱을 만들고 "Passw0rd1"의 SQL Server 관리자 암호를 사용하려는 경우 다음 명령을 입력할 수 있습니다.

    PS C:\Sample Apps\FixIt\Automation> .\New-AzureWebsiteEnv.ps1 -Name 
    fixitdemo <required params here>
    

    이름은 azurewebsites.net 도메인에서 고유해야 하며 암호 복잡성에 대한 SQL Database 요구 사항을 충족해야 합니다. (Passw0rd1 예제는 요구 사항을 충족합니다.)

    명령은 "."로 시작합니다. 스크립트의 악의적인 실행을 방지하려면 Windows PowerShell 스크립트를 실행할 때 스크립트 파일에 대한 정규화된 경로를 제공해야 합니다. 점을 사용하여 현재 디렉터리(".")를 나타내거나 다음과 같은 정규화된 경로를 제공할 수 있습니다.

    PS C:\Temp\FixIt\Automation> C:\Temp\FixIt\Automation\New-AzureWebsiteEnv.ps1 -Name fixitdemo -SqlDatabasePassword Pas$w0rd
    

    스크립트에 대한 자세한 내용은 cmdlet을 Get-Help 사용합니다.

    PS C:\Sample Apps\FixIt\Automation> Get-Help -Full .\New-AzureWebsiteEnv.ps1
    

    Get-Help cmdlet의 , , 및 Examples 매개 변수를 사용하여 Detailed반환되는 도움말을 필터링할 수 Parameters있습니다. Full

    스크립트가 실패하거나 "New-AzureWebsite: Set-AzureSubscription 호출하고 먼저 Select-AzureSubscription"과 같은 오류를 생성하는 경우 Azure PowerShell 구성을 완료하지 않았을 수 있습니다.

    스크립트가 완료되면 Azure 관리 포털을 사용하여 모든 항목 자동화 챕터에 표시된 대로 생성된 리소스를 볼 수 있습니다.

  10. FixIt 프로젝트를 새 Azure 환경에 배포하려면 AzureWebsite.ps1 스크립트를 사용합니다. 예를 들면 다음과 같습니다.

    PS C:\Sample Apps\FixIt\Automation> .\Publish-AzureWebsite.ps1 ..\MyFixIt\MyFixIt.csproj -Launch
    

    배포가 완료되면 Azure에서 실행 중인 수정으로 브라우저가 열립니다.

Windows PowerShell 스크립트 문제 해결

이러한 스크립트를 실행할 때 발생하는 가장 일반적인 오류는 권한과 관련이 있습니다. 및 Import-AzurePublishSettingsFileAdd-AzureAccount 성공했으며 동일한 Azure 구독에 사용했는지 확인합니다. 성공한 경우에도 Add-AzureAccount 다시 실행해야 할 수 있습니다. 에 Add-AzureAccount 의해 추가된 권한은 12시간 후에 만료됩니다.

개체 참조가 개체의 인스턴스로 설정되지 않았습니다.

스크립트가 "개체 참조가 개체의 instance 설정되지 않음"과 같은 오류를 반환하면 Windows PowerShell 처리할 개체를 찾을 수 없습니다(null 참조 예외임). cmdlet을 실행하고 Add-AzureAccount 스크립트를 다시 시도합니다.

New-AzureSqlDatabaseServer : Object reference not set to an instance of an object.
At C:\ps-test\azure-powershell-samples-master\WebSite\create-azure-sql.ps1:80 char:19
+ $databaseServer = New-AzureSqlDatabaseServer -AdministratorLogin $UserName -Admi ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [New-AzureSqlDatabaseServer], NullReferenceException
    + FullyQualifiedErrorId : 
Microsoft.WindowsAzure.Commands.SqlDatabase.Server.Cmdlet.NewAzureSqlDatabaseServer

InternalError: 서버에서 내부 오류가 발생했습니다.

cmdlet은 New-AzureWebsite 이름이 azurewebsites.net 도메인에서 고유하지 않은 경우 내부 오류를 반환합니다. 오류를 resolveNew-AzureWebsiteEnv.ps1의 Name 매개 변수에 있는 이름에 다른 값을 사용합니다.

New-AzureWebsite : InternalError: The server encountered an internal error. 
Please retry the request.
At line:1 char:1
+ New-AzureWebsite -Name fixitdemo
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          
: CloseError: (:) [New-AzureWebsite], Exception
+ FullyQualifiedErrorId : 
Microsoft.WindowsAzure.Commands.Websites.NewAzureWebsiteCommand

스크립트 다시 시작

"스크립트가 완료되었습니다" 메시지를 인쇄하기 전에 실패하여 New-AzureWebsiteEnv.ps1 스크립트를 다시 시작해야 하는 경우 스크립트가 중지되기 전에 만든 리소스를 삭제할 수 있습니다. 예를 들어 스크립트가 이미 ContosoFixItDemo 웹앱을 만들고 동일한 이름으로 스크립트를 다시 실행하면 이름이 사용 중이므로 스크립트가 실패합니다.

스크립트가 중지되기 전에 만든 리소스를 확인하려면 다음 cmdlet을 사용합니다.

  • Get-AzureWebsite
  • Get-AzureSqlDatabaseServer
  • Get-AzureSqlDatabase: 이 cmdlet을 실행하려면 데이터베이스 서버 이름을 로 파이프합니다 Get-AzureSqlDatabase. Get-AzureSqlDatabaseServer | Get-AzureSqlDatabase.

이러한 리소스를 삭제하려면 다음 명령을 사용합니다. 데이터베이스 서버를 삭제하면 서버와 연결된 데이터베이스가 자동으로 삭제됩니다.

  • Get-AzureWebsite -Name <WebsiteName> | Remove-AzureWebsite
  • Get-AzureSqlDatabase -Name <DatabaseName> -ServerName <DatabaseServerName> | Remove-SqlAzureDatabase
  • Get-AzureSqlDatabaseServer | Remove-AzureSqlDatabaseServer

Azure App Service Web Apps 및 Azure Cloud Service에 큐 처리를 사용하여 앱을 배포하는 방법

큐를 사용하도록 설정하려면 MyFixIt\Web.config 파일에서 다음을 변경합니다. 에서 appSettingsUseQueues 값을 "true"로 변경합니다.

<appSettings>
    <!-- Other settings not shown -->
    <add key="UseQueues" value="true"/>
</appSettings>

그런 다음 앞에서 설명한 대로 Azure App Service 웹앱에 MVC 애플리케이션 배포합니다.

다음으로, 새 Azure 클라우드 서비스를 만듭니다. 수정 앱에 포함된 스크립트는 클라우드 서비스를 만들거나 배포하지 않으므로 이를 위해 Azure Portal 사용해야 합니다. 포털에서 -- 컴퓨팅클라우드 서비스 -- 빠른 만들기를 클릭한 다음 URL 및 데이터 센터 위치를 입력합니다. 웹앱을 배포한 동일한 데이터 센터를 사용합니다.

새 Azure 클라우드 서비스 프로젝트를 만들기 위해 사용 가능한 선택 항목이 있는 Azure Cloud Service Portal 및 여러 탭을 보여 주는 다이어그램

클라우드 서비스를 배포하려면 구성 파일 중 일부를 업데이트해야 합니다.

MyFixIt.WorkerRole\app.config 에서 connectionStrings연결 문자열 값을 appdb SQL Database 대한 실제 연결 문자열 바꿉니다. 포털에서 연결 문자열 가져올 수 있습니다. 포털에서ADO .Net, ODBC, PHP 및 JDBC에 대한SQL Database - appdb - SQL Database 연결 문자열 보기를 클릭합니다. ADO.NET 연결 문자열 복사하고 값을 app.config 파일에 붙여넣습니다. "{your_password_here}"을(를) 데이터베이스 암호로 대체합니다. (스크립트를 사용하여 MVC 앱을 배포했다고 가정하면 스크립트 매개 변수에 SqlDatabasePassword 데이터베이스 암호를 지정했습니다.)

결과는 다음과 같습니다.

<add name="appdb" connectionString="Server=tcp:####.database.windows.net,1433;Database=appdb;User ID=####;Password=####;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;" providerName="System.Data.SqlClient" />

동일한 MyFixIt.WorkerRole\app.config 파일의 에서 appSettingsAzure Storage 계정에 대한 두 자리 표시자 값을 바꿉니다.

<appSettings>
  <add key="StorageAccountName" value="{StorageAccountName}" />
  <add key="StorageAccountAccessKey" value="{StorageAccountAccessKey}" />
</appSettings>

포털에서 액세스 키를 가져올 수 있습니다. 스토리지 계정을 관리하는 방법을 참조하세요.

MyFixItCloudService\ServiceConfiguration.Cloud.cscfg에서 Azure Storage 계정에 대해 동일한 두 자리 표시자 값을 바꿉니다.

<ConfigurationSettings>
    <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" 
             value="DefaultEndpointsProtocol=https;AccountName={StorageAccountName};AccountKey={StorageAccountAccessKey}" />
  </ConfigurationSettings>

이제 클라우드 서비스를 배포할 준비가 되었습니다. 솔루션 탐색에서 MyFixItCloudService 프로젝트를 마우스 오른쪽 단추로 클릭하고 게시를 선택합니다. 자세한 내용은 이 자습서의 2부에 있는 "Azure에 애플리케이션 배포"를 참조하세요.