지리적 중복성을 사용하여 고가용성 애플리케이션 설계

Azure Storage와 같은 클라우드 기반 인프라는 데이터 및 애플리케이션 호스팅을 위한 고가용성 및 내구성 플랫폼을 제공합니다. 클라우드 기반 애플리케이션 개발자는 사용자에게 이러한 이점을 극대화하기 위해 이 플랫폼을 활용하는 방법을 신중하게 고려해야 합니다. Azure Storage는 지역적 중단 중에도 고가용성을 보장하기 위해 지역 중복성 옵션을 제공합니다. 지역 중복 복제를 위해 구성된 스토리지 계정은 주 지역에서 동기적으로 복제된 다음 수백 마일 떨어진 보조 지역에 비동기적으로 복제됩니다.

Azure Storage는 지역 중복 복제를 위해 GRS(지역 중복 스토리지)GZRS(지역 영역 중복 스토리지)의 두 가지 옵션을 제공합니다. Azure Storage 지역 중복 옵션을 사용하려면 스토리지 계정이 RA-GRS(읽기 액세스 지역 중복 스토리지) 또는 RA-GZRS(읽기 액세스 지역 영역 중복 스토리지)에 대해 구성되어 있는지 확인합니다. 그렇지 않은 경우 스토리지 계정 복제 형식을 변경하는 방법에 대해 자세히 알아볼 수 있습니다.

이 문서에서는 제한된 용량에도 불구하고 주 지역에 심각한 중단이 발생한 경우에도 계속 작동할 애플리케이션을 설계하는 방법을 보여 줍니다. 주 지역을 사용할 수 없게 되면 애플리케이션은 주 지역이 다시 응답할 때까지 보조 지역에 대해 읽기 작업을 수행하도록 원활하게 전환할 수 있습니다.

애플리케이션 디자인 고려 사항

주 지역에서 읽는 것을 방해하는 문제가 있는 경우 보조 지역에서 읽어 일시적인 오류 또는 심각한 중단을 처리하도록 애플리케이션을 설계할 수 있습니다. 기본 지역을 다시 사용할 수 있게 되면 애플리케이션은 기본 지역에서 읽기로 되돌릴 수 있습니다.

RA-GRS 또는 RA-GZRS를 사용하여 가용성과 복원력을 위해 애플리케이션을 설계할 때 다음 주요 고려 사항을 염두에 두세요.

  • 주 지역에 저장한 데이터의 읽기 전용 복사본은 보조 지역에 비동기식으로 복제됩니다. 이 비동기 복제는 보조 지역의 읽기 전용 복사본이 주 지역의 데이터와 최종적으로 일치한다는 것을 의미합니다. 스토리지 서비스는 보조 지역의 위치를 결정합니다.

  • Azure Storage 클라이언트 라이브러리를 사용하여 주 지역 엔드포인트에 대한 읽기 및 업데이트 요청을 수행할 수 있습니다. 주 지역을 사용할 수 없는 경우 읽기 요청을 보조 지역으로 자동 리디렉션할 수 있습니다. 원하는 경우 주 지역을 사용할 수 있는 경우에도 읽기 요청을 보조 지역으로 직접 보내도록 앱을 구성할 수도 있습니다.

  • 주 지역을 사용할 수 없게 되면 계정 장애 조치를 시작할 수 있습니다. 보조 지역으로 장애 조치를 수행하면 주 지역을 가리키는 DNS 항목이 보조 지역을 가리키도록 변경됩니다. 장애 조치가 완료되면 GRS 및 RA-GRS 계정에 대한 쓰기 권한이 복원됩니다. 자세한 내용은 재해 복구 및 저장소 계정 장애 조치(failover)를 참조하세요.

최종 일관성 데이터 작업

제안된 솔루션에서는 잠재적인 부실 데이터를 호출하는 애플리케이션에 반환할 수 있다고 가정합니다. 보조 영역에 있는 데이터가 결국 일관적이기 때문에 보조 지역에 대한 업데이트가 복제를 완료하기 전에 기본 지역에 액세스할 수 없게 될 수 있습니다.

예를 들어 고객이 업데이트를 성공적으로 제출하지만 업데이트를 보조 지역으로 전파하기 전에 기본 지역이 실패했다고 가정하겠습니다. 고객이 데이터를 다시 읽어오도록 요청하면 업데이트된 데이터 대신 보조 지역의 부실 데이터를 수신합니다. 애플리케이션을 설계할 때 이 동작이 허용되는지 여부를 결정해야 합니다. 그렇다면 사용자에게 알리는 방법도 고려해야 합니다.

이 문서 뒷부분에서 최종 일관성 있는 데이터 처리마지막 동기화 시간 속성을 확인하여 기본 지역과 보조 지역의 데이터 간의 불일치를 평가하는 방법에 대해 자세히 알아볼 것입니다.

서비스를 개별적으로 또는 모두 함께 처리

가능성은 거의 없지만 한 서비스(BLOB, 큐, 테이블 또는 파일)를 사용할 수 없게 되는 반면 다른 서비스는 완전히 작동할 수 있습니다. 각 서비스에 대한 다시 시도를 개별적으로 처리하거나 모든 스토리지 서비스에 대한 다시 시도를 일반적으로 함께 처리할 수 있습니다.

예를 들어 애플리케이션에 큐와 Blob을 사용하는 경우 각 서비스에 대해 다시 시도 가능한 오류를 처리하기 위해 별도의 코드를 배치하도록 결정할 수 있습니다. 그렇게 하면 Blob service 오류는 Blob을 처리하는 애플리케이션 부분에만 영향을 미치므로 큐가 정상적으로 계속 실행되도록 합니다. 그러나 모든 스토리지 서비스 다시 시도를 함께 처리하기로 결정한 경우 두 서비스 중 하나가 다시 시도 가능한 오류를 반환하면 Blob 및 큐 서비스 모두에 대한 요청이 영향을 받습니다.

궁극적으로 이 결정은 애플리케이션의 복잡성에 따라 달라집니다. 다시 시도의 영향을 제한하기 위해 서비스별로 실패를 처리하는 것을 선호할 수 있습니다. 또는 주 지역의 스토리지 서비스에 문제가 검색되면 모든 스토리지 서비스에 대한 읽기 요청을 보조 지역으로 리디렉션하도록 결정할 수 있습니다.

읽기 전용 모드에서 애플리케이션 실행

주 지역의 중단에 효과적으로 대비하려면 애플리케이션이 실패한 읽기 요청과 실패한 업데이트 요청을 모두 처리할 수 있어야 합니다. 기본 지역이 실패하면 읽기 요청을 보조 지역으로 리디렉션할 수 있습니다. 그러나 보조 지역의 복제된 데이터는 읽기 전용이므로 업데이트 요청을 리디렉션할 수 없습니다. 이러한 이유로 읽기 전용 모드에서 실행할 수 있도록 애플리케이션을 설계해야 합니다.

예를 들어 Azure Storage에 업데이트 요청을 제출하기 전에 확인되는 플래그를 설정할 수 있습니다. 업데이트 요청이 들어오면 요청을 건너뛰고 사용자에게 적절한 응답을 반환할 수 있습니다. 문제가 해결될 때까지 특정 기능을 완전히 사용하지 않도록 설정하고 해당 기능을 일시적으로 사용할 수 없음을 사용자에게 알릴 수도 있습니다.

각 서비스의 오류를 개별적으로 처리하기로 결정하면 읽기 전용 모드에서 애플리케이션을 실행하는 기능도 서비스별로 처리해야 합니다. 예를 들어 각 서비스에 대해 읽기 전용 플래그를 설정할 수 있습니다. 그런 다음 필요에 따라 코드에서 플래그를 사용하거나 사용하지 않도록 설정할 수 있습니다.

읽기 전용 모드에서 애플리케이션을 실행할 수 있으면 주요 애플리케이션 업그레이드 중에 제한된 기능을 보장할 수도 있습니다. 애플리케이션을 트리거하여 읽기 전용 모드에서 실행하고 보조 데이터 센터를 가리켜서, 업그레이드를 진행하는 동안 주 지역의 데이터에 아무도 액세스하지 않도록 할 수 있습니다.

읽기 전용 모드에서 실행하는 경우 업데이트 처리

읽기 전용 모드에서 실행하는 경우 업데이트 요청을 처리하는 방법이 많이 있습니다. 이 섹션에서는 고려해야 할 몇 가지 일반적인 패턴에 중점을 둡니다.

  • 사용자에게 응답하고 업데이트 요청이 현재 처리되고 있지 않음을 알릴 수 있습니다. 예를 들어 연락처 관리 시스템을 사용하면 사용자가 연락처 정보에 액세스할 수 있지만 업데이트할 수는 없습니다.

  • 업데이트를 다른 지역의 큐에 넣을 수 있습니다. 이 경우 보류 중인 업데이트 요청을 다른 지역에 있는 큐에 써 놓은 다음 기본 데이터 센터가 다시 온라인이 되면 요청을 처리합니다. 이 시나리오에서는 업데이트 요청이 이후 처리를 위해 대기 중임을 사용자에게 알려야 합니다.

  • 다른 지역의 스토리지 계정에 업데이트를 작성할 수 있습니다. 주 지역이 다시 온라인 상태가 되면 데이터 구조에 따라 이러한 업데이트를 주 데이터에 병합할 수 있습니다. 예를 들어 이름에 날짜/시간 스탬프가 있는 별도의 파일을 만드는 경우 파일을 주 지역에 다시 복사할 수 있습니다. 이 솔루션은 로깅 및 IoT 데이터와 같은 워크로드에 적용할 수 있습니다.

다시 시도 처리

클라우드에서 실행되는 서비스와 통신하는 애플리케이션은 발생할 수 있는 계획되지 않은 이벤트 및 오류에 민감해야 합니다. 이러한 결함은 일시적이거나 지속적일 수 있으며 일시적인 연결 끊김에서 자연 재해로 인한 심각한 중단에 이르기까지 다양합니다. 가용성을 최대화하고 전반적인 애플리케이션 안정성을 개선하려면 적절한 다시 시도 처리로 클라우드 애플리케이션을 설계하는 것이 중요합니다.

읽기 요청

주 지역을 사용할 수 없게 되면 읽기 요청을 보조 스토리지로 리디렉션할 수 있습니다. 앞서 언급했듯이 애플리케이션이 잠재적으로 부실 데이터를 읽을 수 있어야 합니다. Azure Storage 클라이언트 라이브러리는 다시 시도를 처리하고 읽기 요청을 보조 지역으로 리디렉션하는 옵션을 제공합니다.

이 예에서 Blob Storage에 대한 다시 시도 처리는 BlobClientOptions 클래스에서 구성되며 이러한 구성 옵션을 사용하여 만드는 BlobServiceClient 개체에 적용됩니다. 이 구성은 주 지역의 읽기 요청 다시 시도가 보조 지역으로 리디렉션되는 주 지역 다음 보조 지역 방법입니다. 이 방법은 주 지역의 오류가 일시적일 것으로 예상되는 경우에 가장 적합합니다.

string accountName = "<YOURSTORAGEACCOUNTNAME>";
Uri primaryAccountUri = new Uri($"https://{accountName}.blob.core.windows.net/");
Uri secondaryAccountUri = new Uri($"https://{accountName}-secondary.blob.core.windows.net/");

// Provide the client configuration options for connecting to Azure Blob storage
BlobClientOptions blobClientOptions = new BlobClientOptions()
{
    Retry = {
        // The delay between retry attempts for a fixed approach or the delay
        // on which to base calculations for a backoff-based approach
        Delay = TimeSpan.FromSeconds(2),

        // The maximum number of retry attempts before giving up
        MaxRetries = 5,

        // The approach to use for calculating retry delays
        Mode = RetryMode.Exponential,

        // The maximum permissible delay between retry attempts
        MaxDelay = TimeSpan.FromSeconds(10)
    },

    // If the GeoRedundantSecondaryUri property is set, the secondary Uri will be used for 
    // GET or HEAD requests during retries.
    // If the status of the response from the secondary Uri is a 404, then subsequent retries
    // for the request will not use the secondary Uri again, as this indicates that the resource 
    // may not have propagated there yet.
    // Otherwise, subsequent retries will alternate back and forth between primary and secondary Uri.
    GeoRedundantSecondaryUri = secondaryAccountUri
};

// Create a BlobServiceClient object using the configuration options above
BlobServiceClient blobServiceClient = new BlobServiceClient(primaryAccountUri, new DefaultAzureCredential(), blobClientOptions);

주 지역을 장기간 사용할 수 없다고 판단되면 모든 읽기 요청이 보조 지역을 가리키도록 구성할 수 있습니다. 이 구성은 보조 전용 방법입니다. 앞에서 설명한 것처럼 이 시간 동안 업데이트 요청을 처리하는 전략과 읽기 요청만 처리 중임을 사용자에게 알리는 방법이 필요합니다. 이 예에서는 보조 지역 엔드포인트를 사용하는 BlobServiceClient의 새 인스턴스를 만듭니다.

string accountName = "<YOURSTORAGEACCOUNTNAME>";
Uri primaryAccountUri = new Uri($"https://{accountName}.blob.core.windows.net/");
Uri secondaryAccountUri = new Uri($"https://{accountName}-secondary.blob.core.windows.net/");

// Create a BlobServiceClient object pointed at the secondary Uri
// Use blobServiceClientSecondary only when issuing read requests, as secondary storage is read-only
BlobServiceClient blobServiceClientSecondary = new BlobServiceClient(secondaryAccountUri, new DefaultAzureCredential(), blobClientOptions);

읽기 전용 모드와 보조 전용 요청으로 전환할 시기를 아는 것은 회로 차단기 패턴이라는 아키텍처 디자인 패턴의 일부이며, 이에 대해서는 이후 섹션에서 설명합니다.

업데이트 요청

업데이트 요청은 읽기 전용인 보조 스토리지로 리디렉션할 수 없습니다. 앞에서 설명한 것처럼 애플리케이션은 주 지역을 사용할 수 없을 때 업데이트 요청을 처리할 수 있어야 합니다.

회로 차단기 패턴은 업데이트 요청에도 적용될 수 있습니다. 업데이트 요청 오류를 처리하기 위해 코드에서 임계값(예: 연속 10회 실패)을 설정하고 주 지역에 대한 요청 실패 횟수를 추적할 수 있습니다. 임계값이 충족되면 애플리케이션을 읽기 전용 모드로 전환하여 주 지역에 대한 업데이트 요청이 더 이상 실행되지 않도록 할 수 있습니다.

회로 차단기 패턴을 구현하는 방법

복구하는 데 다양한 시간이 걸릴 수 있는 오류 처리는 회로 차단기 패턴이라는 아키텍처 디자인 패턴의 일부입니다. 이 패턴을 적절하게 구현하면 애플리케이션이 실패할 가능성이 있는 작업을 반복적으로 실행하는 것을 방지할 수 있으므로 애플리케이션 안정성과 복원력이 개선됩니다.

회로 차단기 패턴의 한 측면은 기본 엔드포인트에 지속적인 문제가 있는 경우 식별하는 것입니다. 이 결정을 내리기 위해 클라이언트에서 다시 시도 가능한 오류가 발생하는 빈도를 모니터링할 수 있습니다. 각 시나리오가 다르기 때문에 보조 엔드포인트로 전환하고 읽기 전용 모드에서 애플리케이션을 실행하기로 결정하는 데 사용할 적절한 임계값을 결정해야 합니다.

예를 들어 주 지역에서 10번의 연속 실패가 있는 경우 전환을 수행하도록 결정할 수 있습니다. 코드의 실패 횟수를 유지하여 이를 추적할 수 있습니다. 임계값에 도달하기 전에 성공하면 개수를 다시 0으로 설정합니다. 개수가 임계값에 도달하면 읽기 요청에 보조 지역을 사용하도록 애플리케이션을 전환합니다.

대체 방법으로 애플리케이션에 사용자 지정 모니터링 구성 요소를 구현하기로 결정할 수 있습니다. 이 구성 요소는 상태를 확인하기 위해 간단한 읽기 요청(예: 작은 blob 읽기)으로 기본 스토리지 엔드포인트를 계속 ping할 수 있습니다. 이 방법은 약간의 리소스를 차지하지만 상당한 양은 아닙니다. 임계값에 도달하는 문제가 발견되면 보조 전용 읽기 요청 및 읽기 전용 모드로 전환합니다. 이 시나리오의 경우 기본 스토리지 엔드포인트에 다시 ping이 성공하면 주 지역으로 다시 전환하고 업데이트를 계속 허용할 수 있습니다.

전환 시점을 결정하는 데 사용되는 오류 임계값은 애플리케이션 내 서비스마다 다를 수 있으므로 구성 가능한 매개 변수로 만드는 것을 고려해야 합니다.

또 다른 고려 사항은 애플리케이션의 여러 인스턴스를 처리하는 방법과 각 인스턴스에서 재시도 가능한 오류를 감지하는 경우 어떻게 할지 입니다. 예를 들어 동일한 애플리케이션이 로드된 VM을 20개 실행하는 경우가 있습니다. 각 인스턴스를 별도로 처리할까요? 한 인스턴스에 문제가 발생하기 시작하면 해당 인스턴스에 대한 응답을 제한하려고 하나요? 아니면 한 인스턴스에 문제가 있을 때 모든 인스턴스가 동일한 방식으로 응답하기를 원하나요? 인스턴스를 개별적으로 처리하는 것은 인스턴스 간에 응답을 조정하는 것보다 훨씬 간단하지만 방법은 애플리케이션 아키텍처에 따라 다릅니다.

결과적으로 일치하는 데이터 처리

지역 중복 스토리지는 주 지역에서 보조 지역으로 트랜잭션을 복제하는 방식으로 작동합니다. 복제 프로세스는 보조 지역의 데이터가 결과적으로 일치하도록 보장합니다. 즉, 주 지역의 모든 트랜잭션이 결국 보조 지역에 표시되지만 표시되기까지 지연이 있을 수 있습니다. 또한 트랜잭션이 주 지역에 원래 적용된 것과 동일한 순서로 보조 지역에 도착한다는 보장도 없습니다. 트랜잭션이 보조 지역에 순서가 바뀌어 도착하면 서비스가 이를 처리할 때까지 보조 지역의 데이터가 불일치 상태인 것으로 생각할 수 있습니다.

Azure Table Storage에 대한 다음 예에서는 직원의 세부 정보를 업데이트하여 직원을 관리자 역할의 멤버로 만들 때 어떤 일이 발생할 수 있는지 보여 줍니다. 이 예제의 경우 직원 엔터티를 업데이트하고 총 관리자 수로 관리자 역할 엔터티를 업데이트해야 합니다. 업데이트가 보조 지역에서 어떻게 다른 순서로 적용되는지 살펴보겠습니다.

Time 트랜잭션 복제 마지막 동기화 시간 결과
T0 트랜잭션 A:
주 지역에
직원 엔터티 삽입
트랜잭션 A가 주 지역에 삽입됐지만
아직 복제되지 않음
T1 트랜잭션 A가
보조 지역에
복제됨
T1 트랜잭션 A가 보조 지역에 복제됨.
마지막 동기화 시간이 업데이트됨.
T2 트랜잭션 B:
엽데이트
직원 엔터티
업데이트
T1 트랜잭션 B가 주 지역에 기록됐지만
아직 복제되지 않음
T3 트랜잭션 C:
업데이트
administrator
엔터티
T1 트랜잭션 C가 주 지역에 기록됐지만
아직 복제되지 않음
T4 트랜잭션 C가
보조 지역에
복제됨
T1 트랜잭션 C가 보조 지역에 복제됨.
트랜잭션 B가 아직 복제되지 않아서
트랜잭션 B는 아직 복제되지 않았습니다.
T5 보조 지역의
엔터티 읽기
T1 트랜잭션 B가 아직 복제되지
않아서 직원 엔터티에 대해
부실한 값을 얻음 C가 복제되었기 때문에
관리자 역할 엔터티에 대해
새 값을 얻음 트랜잭션 B가 복제되지 않았기
때문에 마지막 동기화 시간이
아직 업데이트되지 않음. 엔터티 날짜/시간이 마지막
동기화 시간보다 나중이기 때문에
관리자 역할 엔터티가 불일치
상태라고 볼 수 있음
T6 트랜잭션 B가
보조 지역에
복제됨
T6 T6 – C까지 모든 트랜잭션이
복제됨. 마지막 동기화
시간이 업데이트됨

이 예제에서는 T5 지점에서 클라이언트가 보조 지역을 읽는 것으로 전환한다고 가정합니다. 지금은 관리자 역할 엔터티를 성공적으로 읽을 수 있지만 이 엔터티에는 관리자 수에 대한 값이 포함되어 있고 이 값은 현재 보조 지역에 관리자로 표시되어 있는 직원 엔터티의 수와 일치하지 않습니다. 정보가 일치하지 않을 위험과 함께 클라이언트가 이 값을 표시할 수 있습니다. 아니면 클라이언트에서 업데이트 순서가 바뀌어 진행되었으므로 관리자 역할이 잠재적으로 불일치 상태인지를 파악하려고 시도한 다음 사용자에게 이 사실을 알릴 수 있습니다.

스토리지 계정에 잠재적으로 일치하지 않는 데이터가 있는지 확인하기 위해 클라이언트는 마지막 동기화 시간 속성 값을 확인할 수 있습니다. 마지막 동기화 시간을 통해 보조 지역의 데이터가 마지막으로 일치했던 시간과 이 시간 전에 서비스가 모든 트랜잭션을 적용한 시간을 알 수 있습니다. 위의 예제에는 서비스가 보조 지역에 직원 엔터티를 삽입한 후 마지막 동기화 시간이 T1로 설정되어 있습니다. 이 시간은 서비스가 보조 지역에서 직원 엔터티를 업데이트할 때까지 T1에 유지되었다가 T6dm로 설정됩니다. 클라이언트가 T5에서 엔터티를 읽을 때 마지막 동기화 시간을 검색하는 경우 엔터티의 타임스탬프와 비교할 수 있습니다. 엔터티의 타임스탬프가 마지막 동기화 시간보다 늦은 경우 엔터티는 잠재적으로 일치하지 않는 상태이며 적절한 작업을 취할 수 있습니다. 이 필드를 사용하려면 주 지역에 대한 마지막 업데이트가 완료된 시간을 알아야 합니다.

마지막 동기화 시간을 확인하는 방법을 알아보려면 스토리지 계정의 마지막 동기화 시간 속성 확인을 참조하세요.

테스팅

애플리케이션에 재시도 가능한 오류가 발생하는 경우 예상대로 작동하는지를 테스트하는 것이 중요합니다. 예를 들어 애플리케이션이 문제를 검색하면 보조 지역으로 전환한 다음 주 지역을 다시 사용할 수 있게 되면 다시 전환하는지 테스트해야 합니다. 이 동작을 적절하게 테스트하려면 다시 시도 가능한 오류를 시뮬레이션하고 발생 빈도를 제어하는 방법이 필요합니다.

한 가지 옵션은 Fiddler를 사용하여 스크립트에서 HTTP 응답을 가로채고 수정하는 것입니다. 이 스크립트는 기본 엔드포인트에서 오는 응답을 식별하고 HTTP 상태 코드를 Storage 클라이언트 라이브러리에서 재시도 가능한 오류로 인식하는 코드로 변경할 수 있습니다. 이 코드 조각은 employeedata 테이블에 대한 읽기 요청에 대한 응답을 가로채서 502 상태를 반환하는 Fiddler 스크립트의 간단한 예를 보여줍니다.

static function OnBeforeResponse(oSession: Session) {
    ...
    if ((oSession.hostname == "\[YOURSTORAGEACCOUNTNAME\].table.core.windows.net")
      && (oSession.PathAndQuery.StartsWith("/employeedata?$filter"))) {
        oSession.responseCode = 502;
    }
}

시나리오를 더 실감나게 시뮬레이션하려면 보다 광범위한 요청을 가로채서 그 중 일부에 대한 responseCode만 변경하도록 이 예제를 확장할 수 있습니다. Fiddler 스크립트 사용자 지정에 대한 자세한 내용은 Fiddler 설명서에서 Modifying a Request or Response(요청 또는 응답 수정)를 참조하세요.

애플리케이션을 읽기 전용으로 전환하기 위해 구성 가능한 임계값을 설정한 경우 프로덕션이 아닌 트랜잭션 볼륨으로 동작을 테스트하는 것이 더 쉬울 것입니다.


다음 단계

주 엔드포인트와 보조 엔드포인트 간에 전환하는 방법을 보여 주는 전체 샘플은 RA-GRS 스토리지와 함께 회로 차단기 패턴을 사용하는 Azure 샘플을 참조하세요.