Azure SignalR Service의 복원력 및 재해 복구

복원력 및 재해 복구는 온라인 시스템에 공통적으로 필요합니다. Azure SignalR Service는 이미 99.9% 가용성을 제공하지만 여전히 지역 서비스입니다. 지역 전체에 중단이 발생하는 경우 서비스 인스턴스는 항상 한 지역에서 실행되기 때문에 다른 지역으로 장애 조치(failover)되지 않습니다.

지역 재해 복구의 경우 다음 두 가지 방식을 권장합니다.

  • 지역 복제를 사용하도록 설정합니다(쉬운 방법). 이 기능은 자동으로 지역 장애 조치(failover)를 처리합니다. 사용하도록 설정되면 Azure SignalR 인스턴스가 하나만 있고 코드 변경 내용이 적용되지 않습니다. 자세한 내용은 지역 복제를 확인합니다.
  • 서비스 SDK에서 여러 엔드포인트를 활용합니다. 당사의 서비스 SDK는 여러 SignalR 서비스 인스턴스를 지원하고 일부 인스턴스를 사용할 수 없는 경우 자동으로 다른 인스턴스로 전환합니다. 이 기능을 사용하면 재해 발생 시 복구할 수 있지만 올바른 시스템 토폴로지를 직접 설정해야 합니다. 이 문서에서 그 방법을 알아봅니다.

SignalR Service에 대한 고가용성 아키텍처

SignalR 서비스에 대한 지역 간 복원성을 보장하려면 서로 다른 지역에 여러 서비스 인스턴스를 설정해야 합니다. 그러면 하나의 지역이 다운되는 경우 다른 지역을 백업으로 사용할 수 있습니다. 앱 서버가 여러 서비스 인스턴스에 연결되면 기본 역할과 보조 역할의 두 가지 역할이 있습니다. 기본은 온라인 트래픽 수신을 담당하는 인스턴스이고, 보조는 완벽하게 작동하는 대체 인스턴스 역할을 합니다. SDK 구현에서 협상은 기본 엔드포인트만 반환하므로 클라이언트는 일반적인 경우에만 기본 엔드포인트에 연결됩니다. 그러나 주 인스턴스가 다운되면 클라이언트가 계속 연결할 수 있도록 협상이 보조 엔드포인트를 반환합니다. 기본 인스턴스 및 앱 서버는 일반 서버 연결을 통해 연결되지만 보조 인스턴스 및 앱 서버는 취약한 연결이라는 특수한 유형의 연결을 통해 연결됩니다. 약한 연결의 한 가지 특징은 다른 지역의 보조 인스턴스 위치로 인해 클라이언트 연결 라우팅을 수락할 수 없다는 것입니다. 클라이언트를 다른 지역으로 라우팅하는 것은 최적의 선택이 아닙니다(대기 시간 증가).

여러 앱 서버에 연결하는 경우 하나의 서비스 인스턴스가 여러 다른 역할을 가질 수 있습니다. 지역 간 시나리오에 대한 한 가지 일반적인 설정은 SignalR Service 인스턴스와 앱 서버의 쌍을 두 개(또는 이상) 가지는 것입니다. 앱 서버와 SignalR Service의 각 쌍은 동일한 지역에 위치하며 SignalR Service는 앱 서버와 기본 역할로 연결됩니다. 앱 서버와 SignalR Service의 각 쌍은 서로 연결되지만, SignalR은 다른 지역의 서버에 연결할 때 보조가 됩니다.

이 토폴로지를 통해 모든 앱 서버와 SignalR Service 인스턴스가 서로 연결되므로 한 서버의 메시지를 모든 클라이언트에 계속 배달할 수 있습니다. 그러나 클라이언트가 연결되면 최적의 네트워크 대기 시간을 달성하기 위해 동일한 지역의 앱 서버로 라우팅됩니다.

다음 다이어그램은 이러한 토폴로지를 보여 줍니다.

다이어그램은 각각 앱 서버 및 SignalR Service가 있는 두 개의 지역을 보여 줍니다. 여기에서 각 서버는 해당 지역의 SignalR Service와 기본으로 연결되며, 다른 지역의 서비스와 보조로 연결됩니다.

여러 SignalR Service 인스턴스 구성

앱 서버와 Azure Functions에서 모두 여러 개의 SignalR Service 인스턴스를 지원합니다.

각 지역에서 SignalR Service 및 앱 서버/Azure Functions를 만든 후에 모든 SignalR Service 인스턴스에 연결되도록 앱 서버/Azure Functions를 구성할 수 있습니다.

구성을 통해

Azure:SignalR:ConnectionString이라는 구성 항목에서 환경 변수/애플리케이션 설정/web.cofig로 SignalR Service 연결 문자열을 설정하는 방법을 미리 알고 있어야 합니다. 여러 엔드포인트가 있는 경우 여러 구성 항목에서 각각 다음 형식으로 설정할 수 있습니다.

Azure:SignalR:ConnectionString:<name>:<role>

ConnectionString에서 <name>은 엔드포인트의 이름이고 <role>은 해당 역할(기본 또는 보조)입니다. 이름은 선택 사항이지만 여러 엔드포인트 간의 라우팅 동작을 추가로 사용자 지정하려는 경우 유용합니다.

코드를 통해

연결 문자열을 다른 곳에 저장하려는 경우 AddAzureSignalR()(ASP.NET Core에서) 또는 MapAzureSignalR()(ASP.NET에서)을 호출할 때 코드에서 이를 읽어 매개 변수로 사용할 수 있습니다.

다음은 샘플 코드입니다.

ASP.NET Core:

services.AddSignalR()
        .AddAzureSignalR(options => options.Endpoints = new ServiceEndpoint[]
        {
            new ServiceEndpoint("<connection_string1>", EndpointType.Primary, "region1"),
            new ServiceEndpoint("<connection_string2>", EndpointType.Secondary, "region2"),
        });

ASP.NET:

app.MapAzureSignalR(GetType().FullName, hub,  options => options.Endpoints = new ServiceEndpoint[]
    {
        new ServiceEndpoint("<connection_string1>", EndpointType.Primary, "region1"),
        new ServiceEndpoint("<connection_string2>", EndpointType.Secondary, "region2"),
    };

기본 또는 보조 인스턴스를 여러 개 구성할 수 있습니다. 기본 및/또는 보조 인스턴스가 여러 개인 경우 협상은 다음 순서로 엔드포인트를 반환합니다.

  1. 온라인 상태인 기본 인스턴스가 하나 이상이면 임의의 기본 온라인 인스턴스를 반환합니다.
  2. 모든 기본 인스턴스가 다운되면 임의의 보조 온라인 인스턴스를 반환합니다.

Azure Functions SignalR 바인딩의 경우

여러 SignalR Service 인스턴스를 사용하도록 설정하려면 다음을 수행해야 합니다.

  1. Persistent 전송 형식을 사용합니다.

    기본 전송 형식은 Transient 모드입니다. Azure의 local.settings.json 파일이나 애플리케이션 설정에 다음 항목을 추가해야 합니다.

    {
        "AzureSignalRServiceTransportType":"Persistent"
    }
    

    참고 항목

    Transient 모드에서 Persistent 모드로 전환할 때, Transient 모드에서는 허브 메서드의 인수를 직렬화하는 데 Newtonsoft.Json 라이브러리가 사용되지만, Persistent 모드에서는 기본적으로 System.Text.Json 라이브러리가 사용되므로 JSON serialization 동작이 변경될 수 있습니다. System.Text.JsonNewtonsoft.Json과 기본 동작에 있어 몇 가지 주요 차이점이 있습니다. Persistent 모드에서 Newtonsoft.Json을 사용하려면 local.settings.json 파일에 "Azure:SignalR:HubProtocol":"NewtonsoftJson" 구성 항목을 추가하거나 Azure Portal에 Azure__SignalR__HubProtocol=NewtonsoftJson을 추가할 수 있습니다.

  2. 구성에서 여러 SignalR Service 엔드포인트 항목을 구성합니다.

    ServiceEndpoint 개체를 사용하여 SignalR Service 인스턴스를 나타냅니다. <EndpointName><EndpointType>을 항목 키에 포함하고, 연결 문자열을 항목 값에 포함하여 서비스 엔드포인트를 정의할 수 있습니다. 키 형식은 다음과 같습니다.

    Azure:SignalR:Endpoints:<EndpointName>:<EndpointType>
    

    <EndpointType>은 선택 사항이며 기본값은 primary입니다. 아래의 샘플을 확인합니다.

    {
        "Azure:SignalR:Endpoints:EastUs":"<ConnectionString>",
    
        "Azure:SignalR:Endpoints:EastUs2:Secondary":"<ConnectionString>",
    
        "Azure:SignalR:Endpoints:WestUs:Primary":"<ConnectionString>"
    }
    

    참고 항목

    • Azure Portal의 App Service에서 Azure SignalR 엔드포인트를 구성할 때 키에서 ":"을 이중 밑줄인 "__"로 바꿔야 합니다. 자세한 내용은 환경 변수를 참조하세요.

    • {ConnectionStringSetting} 키로 구성된 연결 문자열(기본값은 "AzureSignalRConnectionString")도 이름이 비어 있는 기본 서비스 엔드포인트로 인식됩니다. 하지만 이러한 구성 스타일은 여러 엔드포인트에는 권장되지 않습니다.

관리 SDK

구성에서 여러 엔드포인트 추가

SignalR Service 연결 문자열에 대해 키 Azure:SignalR:Endpoints를 사용하여 구성합니다. 키는 Azure:SignalR:Endpoints:{Name}:{EndpointType} 형식이어야 하며, 여기서 NameEndpointTypeServiceEndpoint 개체의 속성이며 코드에서 액세스할 수 있습니다.

다음 dotnet 명령을 사용하여 여러 인스턴스 연결 문자열을 추가할 수 있습니다.

dotnet user-secrets set Azure:SignalR:Endpoints:east-region-a <ConnectionString1>
dotnet user-secrets set Azure:SignalR:Endpoints:east-region-b:primary <ConnectionString2>
dotnet user-secrets set Azure:SignalR:Endpoints:backup:secondary <ConnectionString3>

코드에서 여러 엔드포인트 추가

ServiceEndpoint 클래스는 Azure SignalR Service 엔드포인트의 속성을 설명합니다. Azure SignalR Management SDK를 사용하면 다음을 통해 여러 인스턴스 엔드포인트를 구성할 수 있습니다.

var serviceManager = new ServiceManagerBuilder()
                    .WithOptions(option =>
                    {
                        options.Endpoints = new ServiceEndpoint[]
                        {
                            // Note: this is just a demonstration of how to set options.Endpoints
                            // Having ConnectionStrings explicitly set inside the code is not encouraged
                            // You can fetch it from a safe place such as Azure KeyVault
                            new ServiceEndpoint("<ConnectionString0>"),
                            new ServiceEndpoint("<ConnectionString1>", type: EndpointType.Primary, name: "east-region-a"),
                            new ServiceEndpoint("<ConnectionString2>", type: EndpointType.Primary, name: "east-region-b"),
                            new ServiceEndpoint("<ConnectionString3>", type: EndpointType.Secondary, name: "backup"),
                        };
                    })
                    .BuildServiceManager();

장애 조치(Failover) 시퀀스와 모범 사례

이제 올바른 시스템 토폴로지 설정이 완료되었습니다. 하나의 SignalR 서비스 인스턴스가 다운될 때마다 온라인 트래픽이 다른 인스턴스로 라우팅됩니다. 기본 인스턴스가 다운되는 경우 다음과 같은 상황이 발생합니다(그리고 얼마 후 복구됨).

  1. 기본 서비스 인스턴스가 작동 중지되어 이 인스턴스의 모든 서버 연결이 끊어졌습니다.
  2. 이 인스턴스에 연결된 모든 서버는 이를 오프라인으로 표시하고 협상을 통해 이 엔드포인트 반환을 중지하고 보조 엔드포인트 반환을 시작합니다.
  3. 이 인스턴스의 모든 클라이언트 연결도 닫히고 클라이언트가 다시 연결됩니다. 이제 앱 서버가 보조 엔드포인트를 반환하므로 클라이언트는 보조 인스턴스에 연결됩니다.
  4. 이제 보조 인스턴스가 모든 온라인 트래픽을 사용하게 됩니다. 보조가 모든 앱 서버에 연결되므로 서버에서 클라이언트로 가는 모든 메시지는 계속 배달될 수 있습니다. 하지만 클라이언트에서 서버로 가는 메시지는 동일한 지역에 있는 앱 서버로만 라우팅됩니다.
  5. 기본 인스턴스가 복구되어 다시 온라인 상태가 되면 앱 서버에서 이에 대한 연결을 다시 설정하고 온라인으로 표시합니다. 이제 협상이 기본 엔드포인트를 다시 반환하므로 새 클라이언트가 기본 엔드포인트에 다시 연결됩니다. 그러나 기존 클라이언트는 연결이 끊어지지 않으며 연결이 끊어질 때까지 보조 클라이언트로 계속 라우팅됩니다.

아래 다이어그램은 SignalR Service에서 장애 조치가 수행되는 방법을 보여줍니다.

그림 1 장애 조치(failover) 전 장애 조치(failover) 전

그림 2 장애 조치(failover) 후 장애 조치(failover) 후

그림 3 기본 복구 후 짧은 시간 기본 복구 후 짧은 시간

정상 사례에서 볼 수 있듯이 기본 앱 서버 및 SignalR Service만 온라인 트래픽을 가집니다(파란색). 장애 조치(failover) 후 보조 앱 서버 및 SignalR Service도 활성화됩니다. 기본 SignalR Service가 다시 온라인 상태가 된 후 새 클라이언트가 기본 SignalR 에 연결됩니다. 하지만 기존 클라이언트는 두 인스턴스 모두에 트래픽이 있으므로 계속 보조에 연결됩니다. 모든 기존 클라이언트 연결이 끊긴 후, 시스템은 정상으로 돌아갑니다(그림 1).

지역 간 고가용성 아키텍처를 구현하기 위한 두 가지 주요 패턴이 있습니다.

  1. 첫 번째는 모든 온라인 트래픽을 사용하는 앱 서버와 SignalR Service의 쌍을 갖고, 백업으로 다른 쌍을 확보하는 것입니다(활성/수동이라고 함, 그림 1에서 설명됨).
  2. 다른 하나는 앱 서버와 SignalR Service 인스턴스의 쌍이 두 개(또는 이상)인 것입니다. 각각 온라인 트래픽의 일부를 사용하며 다른 쌍에 대한 백업 역할을 합니다(활성/활성이라고 함, 그림 3과 유사).

SignalR Service는 두 패턴을 모두 지원하며 주요 차이점은 앱 서버를 구현하는 방법입니다. 앱 서버가 활성/수동인 경우 SignalR 서비스도 활성/수동입니다(기본 앱 서버는 기본 SignalR 서비스 인스턴스만 반환하므로). 앱 서버가 활성/활성인 경우 SignalR 서비스도 활성/활성입니다(모든 앱 서버가 자체 기본 SignalR 인스턴스를 반환하므로 모두 트래픽을 가져올 수 있으므로).

사용하도록 선택한 패턴이 무엇인지 확인하세요. 각 SignalR 서비스 인스턴스를 기본으로 앱 서버에 연결해야 합니다.

또한 SignalR 연결의 특성상(긴 연결임) 재해 및 장애 조치(failover)가 발생하는 경우 클라이언트에서 연결이 끊깁니다. 최종 고객에게 명료하도록 그러한 경우를 클라이언트 쪽에서 처리해야 합니다. 예를 들어 연결을 닫은 후 다시 연결을 수행합니다.

장애 조치(failover)를 테스트하는 방법

장애 조치(failover)를 트리거하려면 다음 단계를 따릅니다.

  1. 포털의 기본 리소스에 대한 네트워킹 탭에서 공용 네트워크 액세스를 사용하지 않도록 설정합니다. 리소스에 개인 네트워크가 사용하도록 설정된 경우 액세스 제어 규칙을 사용하여 모든 트래픽을 거부합니다.
  2. 기본 리소스를 다시 시작합니다.

다음 단계

이 문서에서는 SignalR Service에 대한 복원력을 달성하기 위해 애플리케이션을 구성하는 방법을 알아보았습니다. SignalR Service의 서버/클라이언트 연결 및 연결 라우팅에 대해 자세히 알아보려면 이 문서에서 SignalR Service 내부 기능에 대해 읽어보세요.

여러 인스턴스를 함께 사용하여 대규모 연결을 처리하는 분할과 같은 스케일링 시나리오의 경우 여러 인스턴스를 스케일링하는 방법을 참조하세요.

여러 SignalR Service 인스턴스를 사용하여 Azure Functions를 구성하는 방법에 대한 자세한 내용은 Azure Functions에서 여러 Azure SignalR Service 인스턴스 지원을 참조하세요.