편집

다음을 통해 공유


Java용 최신 웹앱 패턴

Azure App Service
Azure Front Door
Azure Cache for Redis

이 문서에서는 최신 웹앱 패턴을 구현하는 방법을 보여 줍니다. 최신 웹앱 패턴은 클라우드에서 웹앱을 현대화하고 서비스 지향 아키텍처를 도입하는 방법을 정의합니다. 최신 웹앱 패턴은 Azure Well-Architected Framework원칙에 부합하고 신뢰할 수 있는 웹앱 패턴을 기반으로 하는 규범적인 아키텍처, 코드 및 구성 지침을 제공합니다.

최신 웹앱 패턴을 사용하는 이유는 무엇인가요?

최신 웹앱 패턴은 웹 애플리케이션의 수요가 많은 영역을 최적화하는 데 도움이 됩니다. 이러한 영역을 분리하는 자세한 지침을 제공하여 비용 최적화를 위한 독립적인 스케일링을 지원합니다. 이 방법을 사용하면 중요한 구성 요소에 전용 리소스를 할당하여 전반적인 성능을 향상시킬 수 있습니다. 분리 가능한 서비스를 분리하면 앱의 한 부분의 속도 저하가 다른 사용자에게 영향을 주지 않도록 방지하고 개별 앱 구성 요소의 버전 관리 기능을 독립적으로 사용하도록 설정하여 안정성을 향상시킬 수 있습니다.

최신 웹앱 패턴을 구현하는 방법

이 문서에는 최신 웹앱 패턴을 구현하기 위한 아키텍처, 코드 및 구성 지침이 포함되어 있습니다. 다음 링크를 사용하여 필요한 지침으로 이동합니다.

  • 아키텍처 지침: 웹앱 구성 요소를 모듈화하고 적절한 PaaS(Platform as a Service) 솔루션을 선택하는 방법을 알아봅니다.
  • 코드 지침: 분리된 구성 요소를 최적화하는 네 가지 디자인 패턴을 구현합니다. 스트랭글러 무화과, 큐 기반 부하 평준화, 경쟁 소비자 및 상태 엔드포인트 모니터링 패턴.
  • 구성 지침: 분리된 구성 요소에 대한 인증, 권한 부여, 자동 크기 조정 및 컨테이너화를 구성합니다.

GitHub 로고 최신 웹앱 패턴의 참조 구현 (샘플 앱)이 있습니다. 최신 웹앱 구현의 끝 상태를 나타냅니다. 이 문서에서 설명하는 모든 코드, 아키텍처 및 구성 업데이트를 제공하는 프로덕션 수준의 웹앱입니다. 참조 구현을 배포하고 사용하여 최신 웹앱 패턴의 구현을 안내합니다.

아키텍처 지침

최신 웹앱 패턴은 신뢰할 수 있는 웹앱 패턴을 기반으로 합니다. 구현하려면 몇 가지 추가 아키텍처 구성 요소가 필요합니다. 메시지 큐, 컨테이너 플랫폼, 스토리지 서비스 및 컨테이너 레지스트리가 필요합니다(그림 1 참조).

최신 웹앱 패턴의 기준 아키텍처를 보여 주는 다이어그램그림 1. 최신 웹앱 패턴의 필수 아키텍처 요소입니다.

더 높은 SLO(서비스 수준 목표)를 위해 웹앱 아키텍처에 두 번째 지역을 추가할 수 있습니다. 비즈니스 필요에 따라 활성-활성 또는 활성-수동 구성을 지원하도록 트래픽을 두 번째 지역으로 라우팅하도록 부하 분산 장치를 구성합니다. 두 지역에는 연결하는 허브 가상 네트워크가 있는 지역을 제외하고 동일한 서비스가 필요합니다. 허브 및 스포크 네트워크 토폴로지 채택을 통해 네트워크 방화벽과 같은 리소스를 중앙 집중화하고 공유합니다. 허브 가상 네트워크를 통해 컨테이너 리포지토리에 액세스합니다. 가상 머신이 있는 경우 허브 가상 네트워크에 요새 호스트를 추가하여 안전하게 관리합니다(그림 2 참조).

두 번째 지역 및 허브 및 스포크 네트워크 토폴로지의 최신 웹앱 패턴 아키텍처를 보여 주는 다이어그램그림 2. 두 번째 지역 및 허브 및 스포크 네트워크 토폴로지의 최신 웹앱 패턴 아키텍처입니다.

아키텍처 분리

최신 웹앱 패턴을 구현하려면 기존 웹앱 아키텍처를 분리해야 합니다. 아키텍처를 분리하려면 모놀리식 애플리케이션을 각각 특정 기능 또는 기능을 담당하는 더 작고 독립적인 서비스로 분할해야 합니다. 이 프로세스에는 현재 웹앱을 평가하고, 아키텍처를 수정하고, 마지막으로 웹앱 코드를 컨테이너 플랫폼으로 추출해야 합니다. 목표는 분리되는 데 가장 도움이 되는 애플리케이션 서비스를 체계적으로 식별하고 추출하는 것입니다. 아키텍처를 분리하려면 다음 권장 사항을 따릅니다.

  • 서비스 경계 식별 도메인 기반 디자인 원칙을 적용하여 모놀리식 애플리케이션 내에서 바인딩된 컨텍스트를 식별합니다. 경계된 각 컨텍스트는 논리적 경계를 나타내며 별도의 서비스의 후보가 될 수 있습니다. 고유한 비즈니스 기능을 나타내고 종속성이 적은 서비스는 분리에 적합한 후보입니다.

  • 서비스 혜택을 평가합니다. 독립적인 크기 조정을 통해 가장 많은 이점을 제공하는 서비스에 집중합니다. 예를 들어 LOB 애플리케이션의 전자 메일 서비스 공급자와 같은 외부 종속성은 오류로부터 더 많은 격리가 필요할 수 있습니다. 자주 업데이트 또는 변경되는 서비스를 고려합니다. 이러한 서비스를 분리하면 독립적인 배포가 가능하며 애플리케이션의 다른 부분에 영향을 줄 수 있습니다.

  • 기술 타당성을 평가합니다. 현재 아키텍처를 검사하여 분리 프로세스에 영향을 줄 수 있는 기술 제약 조건 및 종속성을 식별합니다. 서비스에서 데이터를 관리하고 공유하는 방법을 계획합니다. 분리된 서비스는 자체 데이터를 관리하고 서비스 경계를 넘어 직접 데이터베이스 액세스를 최소화해야 합니다.

  • Azure 서비스를 배포합니다. 추출하려는 웹앱 서비스를 지원하는 데 필요한 Azure 서비스를 선택하고 배포합니다. 지침을 보려면 다음 Azure 서비스 선택 섹션을 사용합니다.

  • 웹앱 서비스를 분리합니다. 시스템의 다른 부분과 상호 작용하기 위해 새로 추출된 웹앱 서비스에 대한 명확한 인터페이스 및 API를 정의합니다. 일관성과 무결성을 보장하면서 각 서비스가 자체 데이터를 관리할 수 있도록 하는 데이터 관리 전략을 설계합니다. 이 추출 프로세스 중에 사용할 특정 구현 전략 및 디자인 패턴은 코드 지침 섹션을 참조하세요.

  • 분리된 서비스에 독립 스토리지를 사용합니다. 분리된 각 서비스에는 버전 관리 및 배포를 용이하게 하기 위해 자체 데이터 저장소가 있어야 합니다. 예를 들어 참조 구현은 웹앱에서 전자 메일 서비스를 분리하고 서비스가 데이터베이스에 액세스할 필요가 없습니다. 대신, 서비스는 Azure Service Bus 메시지를 통해 전자 메일 배달 상태를 웹앱에 다시 전달하고 웹앱은 해당 데이터베이스에 메모를 저장합니다.

  • 분리된 각 서비스에 대해 별도의 배포 파이프라인을 구현합니다. 별도의 배포 파이프라인을 사용하면 각 서비스를 자체적으로 업데이트할 수 있습니다. 회사 내의 다른 팀 또는 조직이 서로 다른 서비스를 소유하는 경우 별도의 배포 파이프라인을 사용하면 각 팀이 자체 배포를 제어할 수 있습니다. Jenkins, GitHub Actions 또는 Azure Pipelines와 같은 CI/CD(지속적인 통합 및 지속적인 업데이트) 도구를 사용하여 이러한 파이프라인을 설정합니다.

  • 보안 제어를 수정합니다. 방화벽 규칙 및 액세스 제어를 포함하여 새 아키텍처를 고려하도록 보안 컨트롤이 업데이트되었는지 확인합니다.

올바른 Azure 서비스 선택

아키텍처의 각 Azure 서비스에 대해 잘 설계된 프레임워크의 관련 Azure 서비스 가이드 를 참조하세요. 최신 웹앱 패턴의 경우 비동기 메시징을 지원하는 메시징 시스템, 컨테이너화를 지원하는 애플리케이션 플랫폼 및 컨테이너 이미지 리포지토리가 필요합니다.

  • 메시지 큐를 선택합니다. 메시지 큐는 서비스 지향 아키텍처의 중요한 부분입니다. 메시지 보낸 사람과 받는 사람을 분리하여 비동기 메시징을 사용하도록 설정합니다. 디자인 요구 사항을 지원하는 Azure 메시징 시스템을 선택하려면 Azure 메시징 서비스를 선택하는 방법에 대한 지침을 사용합니다. Azure에는 Azure Event Grid, Azure Event Hubs 및 Service Bus의 세 가지 메시징 서비스가 있습니다. Service Bus를 기본 선택으로 시작하고 Service Bus가 요구 사항을 충족하지 않는 경우 다른 두 옵션을 사용합니다.

    서비스 사용 사례
    Service Bus 엔터프라이즈 애플리케이션에서 고부가가치 메시지의 안정적이고 정렬된 트랜잭션 배달을 위해 Service Bus를 선택합니다.
    Event Grid 많은 수의 불연속 이벤트를 효율적으로 처리해야 하는 경우 Event Grid를 선택합니다. Event Grid는 대기 시간이 짧은 게시-구독 모델에서 많은 소규모 독립 이벤트(예: 리소스 상태 변경)를 구독자로 라우팅해야 하는 이벤트 기반 애플리케이션에 대해 확장할 수 있습니다.
    Event Hubs 원격 분석, 로그 또는 실시간 분석과 같이 처리량이 많은 대규모 데이터 수집을 위해 Event Hubs를 선택합니다. Event Hubs는 대량 데이터를 지속적으로 수집하고 처리해야 하는 스트리밍 시나리오에 최적화되어 있습니다.
  • 컨테이너 서비스를 구현합니다. 컨테이너화하려는 애플리케이션 부분의 경우 컨테이너를 지원하는 애플리케이션 플랫폼이 필요합니다. Azure 컨테이너 서비스 선택 지침을 사용하여 결정을 내릴 수 있습니다. Azure에는 Azure Container Apps, AKS(Azure Kubernetes Service) 및 Azure 앱 Service의 세 가지 주요 컨테이너 서비스가 있습니다. Container Apps를 기본 선택으로 시작하고 Container Apps가 요구 사항을 충족하지 않는 경우 다른 두 옵션을 사용합니다.

    서비스 사용 사례
    Container Apps 이벤트 기반 애플리케이션에서 컨테이너의 크기를 자동으로 조정하고 관리하는 서버리스 플랫폼이 필요한 경우 Container Apps를 선택합니다.
    AKS 확장, 네트워킹 및 보안을 위해 Kubernetes 구성 및 고급 기능에 대한 자세한 제어가 필요한 경우 AKS를 선택합니다.
    Web Apps for Container 가장 간단한 PaaS 환경을 위해 App Service의 컨테이너용 웹앱을 선택합니다.
  • 컨테이너 리포지토리를 구현합니다. 컨테이너 기반 컴퓨팅 서비스를 사용하는 경우 컨테이너 이미지를 저장할 리포지토리가 있어야 합니다. Docker Hub와 같은 공용 컨테이너 레지스트리 또는 Azure Container Registry와 같은 관리되는 레지스트리를 사용할 수 있습니다. Azure 지침의 컨테이너 레지스트리 소개를 사용하여 결정을 내릴 수 있습니다.

코드 지침

독립 서비스를 성공적으로 분리하고 추출하려면 스트랭글러 무화과 패턴, 큐 기반 부하 평준화 패턴, 경쟁 소비자 패턴, 상태 엔드포인트 모니터링 패턴 및 재시도 패턴과 같은 디자인 패턴으로 웹앱 코드를 업데이트해야 합니다.

최신 웹앱 패턴 아키텍처에서 디자인 패턴의 역할을 보여 주는 다이어그램그림 3. 디자인 패턴의 역할입니다.

  1. 스트랭글러 무화과 패턴: 스트랭글러 무화과 패턴은 모놀리식 애플리케이션에서 분리된 서비스로 기능을 증분 방식으로 마이그레이션합니다. 기본 웹앱에서 이 패턴을 구현하여 엔드포인트를 기반으로 트래픽을 전달하여 기능을 독립 서비스로 점진적으로 마이그레이션합니다.

  2. 큐 기반 부하 평준화 패턴: 큐 기반 부하 평준화 패턴은 큐를 버퍼로 사용하여 생산자와 소비자 간의 메시지 흐름을 관리합니다. 큐를 사용하여 메시지 흐름을 비동기적으로 관리하도록 분리된 서비스의 생산자 부분에 이 패턴을 구현합니다.

  3. 경쟁 소비자 패턴: 경쟁 소비자 패턴을 사용하면 분리된 서비스의 여러 인스턴스가 동일한 메시지 큐에서 독립적으로 읽고 메시지를 처리하기 위해 경쟁할 수 있습니다. 분리된 서비스에서 이 패턴을 구현하여 여러 인스턴스에 작업을 분산합니다.

  4. 상태 엔드포인트 모니터링 패턴: 상태 엔드포인트 모니터링 패턴은 웹앱의 여러 부분의 상태 및 상태를 모니터링하기 위한 엔드포인트를 노출합니다. (4a) 기본 웹앱에서 이 패턴을 구현합니다. (4b) 또한 분리된 서비스에서 구현하여 엔드포인트의 상태를 추적합니다.

  5. 다시 시도 패턴: 다시 시도 패턴은 간헐적으로 실패할 수 있는 작업을 다시 시도하여 일시적인 오류를 처리합니다. (5a) 메시지 큐 및 프라이빗 엔드포인트에 대한 호출과 같이 주 웹앱의 다른 Azure 서비스에 대한 모든 아웃바운드 호출에서 이 패턴을 구현합니다. (5b) 또한 프라이빗 엔드포인트에 대한 호출에서 일시적인 오류를 처리하도록 분리된 서비스에서 이 패턴을 구현합니다.

각 디자인 패턴은 잘 설계된 프레임워크의 하나 이상의 핵심 요소에 부합하는 이점을 제공합니다(다음 표 참조).

디자인 패턴 구현 위치 안정성(RE) 보안(SE) CO(비용 최적화) 운영 우수성(OE) PE(성능 효율성) 잘 설계된 프레임워크 원칙 지원
스트랭글러 그림 패턴 기본 웹앱 RE:08
CO:07
CO:08
OE:06
OE:11
큐 기반 부하 평준화 패턴 분리된 서비스 생산자 RE:06
RE:07
CO:12
PE:05
경쟁 소비자 패턴 분리된 서비스 RE:05
RE:07
CO:05
CO:07
PE:05
PE:07
상태 엔드포인트 모니터링 패턴 주 웹앱 및 분리된 서비스 RE:07
RE:10
OE:07
PE:05
다시 시도 패턴 주 웹앱 및 분리된 서비스 RE:07

스트랭글러 무화과 패턴 구현

스트랭글러 무화과 패턴을 사용하여 모놀리식 코드베이스에서 새로운 독립 서비스로 기능을 점진적으로 마이그레이션합니다. 기존 모놀리식 코드 베이스에서 새 서비스를 추출하고 웹앱의 중요한 부분을 천천히 현대화합니다. 스트랭글러 무화과 패턴을 구현하려면 다음 권장 사항을 따릅니다.

  • 라우팅 계층을 설정합니다. 모놀리식 웹앱 코드 베이스에서 엔드포인트를 기반으로 트래픽을 안내하는 라우팅 계층을 구현합니다. 필요에 따라 사용자 지정 라우팅 논리를 사용하여 트래픽을 전달하기 위한 특정 비즈니스 규칙을 처리합니다. 예를 들어 모놀리식 앱에 엔드포인트가 있고 해당 기능을 분리된 서비스로 이동한 경우 /users 라우팅 계층은 모든 요청을 새 서비스로 /users 전달합니다.

  • 기능 롤아웃을 관리합니다. 기능 플래그단계적 롤아웃을 구현하여 분리된 서비스를 점진적으로 롤아웃 합니다. 기존 모놀리식 앱 라우팅은 분리된 서비스가 수신하는 요청 수를 제어해야 합니다. 약간의 요청으로 시작하고 안정성과 성능에 대한 확신을 얻으면서 시간이 지남에 따라 사용량을 증가합니다.

    예를 들어 참조 구현은 이메일 배달 기능을 독립 실행형 서비스로 추출하며, Contoso 지원 가이드가 포함된 전자 메일을 보내기 위한 요청의 상당 부분을 처리하도록 점진적으로 도입될 수 있습니다. 새 서비스가 안정성과 성능을 증명함에 따라 결국 모놀리식에서 전체 전자 메일 책임 집합을 인수하여 전환을 완료할 수 있습니다.

  • 외관 서비스를 사용합니다(필요한 경우). 외관 서비스는 단일 요청이 여러 서비스와 상호 작용해야 하거나 클라이언트에서 기본 시스템의 복잡성을 숨기려는 경우에 유용합니다. 그러나 분리된 서비스에 공용 API가 없는 경우 외관 서비스가 필요하지 않을 수 있습니다.

    모놀리식 웹앱 코드 베이스에서 요청을 적절한 백 엔드(모놀리식 또는 마이크로 서비스)로 라우팅하는 외관 서비스를 구현합니다. 분리된 새 서비스에서는 새 서비스가 외관을 통해 액세스할 때 요청을 독립적으로 처리할 수 있는지 확인합니다.

큐 기반 부하 평준화 패턴 구현

즉시 응답이 필요하지 않은 작업을 비동기적으로 처리하도록 분리된 서비스의 생산자 부분에 큐 기반 부하 평준화 패턴을 구현합니다. 이 패턴은 큐를 사용하여 워크로드 배포를 관리하여 전반적인 시스템 응답성 및 확장성을 향상시킵니다. 분리된 서비스가 일관된 속도로 요청을 처리할 수 있습니다. 이 패턴을 효과적으로 구현하려면 다음 권장 사항을 따릅니다.

  • 차단 해제 메시지 큐를 사용합니다. 큐에 메시지를 보내는 프로세스가 분리된 서비스가 큐의 메시지를 처리할 때까지 기다리는 동안 다른 프로세스를 차단하지 않는지 확인합니다. 프로세스에 분리된 서비스 작업의 결과가 필요한 경우 대기 중인 작업이 완료될 때까지 기다리는 동안 상황을 처리하는 다른 방법을 구현합니다. 예를 들어 Spring Boot에서 클래스를 StreamBridge 사용하여 호출 스레드를 차단하지 않고 메시지를 큐에 비동기적으로 게시할 수 있습니다(예제 코드 참조).

    private final StreamBridge streamBridge;
    
    public SupportGuideQueueSender(StreamBridge streamBridge) {
        this.streamBridge = streamBridge;
    }
    
    // Asynchronously publish a message without blocking the calling thread
    @Override
    public void send(String to, String guideUrl, Long requestId) {
        EmailRequest emailRequest = EmailRequest.newBuilder()
                .setRequestId(requestId)
                .setEmailAddress(to)
                .setUrlToManual(guideUrl)
                .build();
    
        log.info("EmailRequest: {}", emailRequest);
    
        var message = emailRequest.toByteArray();
        streamBridge.send(EMAIL_REQUEST_QUEUE, message);
    
        log.info("Message sent to the queue");
    }
    

    이 Java 예제에서는 메시지를 비동기적으로 보내는 데 사용합니다 StreamBridge . 이 방법을 사용하면 주 애플리케이션이 응답성을 유지하고 다른 작업을 동시에 처리할 수 있으며 분리된 서비스는 큐에 대기 중인 요청을 관리 가능한 속도로 처리합니다.

  • 메시지 다시 시도 및 제거를 구현합니다. 성공적으로 처리할 수 없는 큐에 대기 중인 메시지의 처리를 다시 시도하는 메커니즘을 구현합니다. 오류가 지속되면 큐에서 이러한 메시지를 제거해야 합니다. 예를 들어 Service Bus에는 기본 제공 재시도 및 배달 못 한 편지 큐 기능이 있습니다.

  • idempotent 메시지 처리를 구성합니다. 큐에서 메시지를 처리하는 논리는 메시지를 두 번 이상 처리할 수 있는 경우를 처리하기 위해 idempotent여야 합니다. Spring Boot에서는 중복 처리를 방지하기 위해 고유한 메시지 식별자를 사용 @StreamListener 하거나 @KafkaListener 사용할 수 있습니다. 또는 Spring Cloud Stream을 사용하여 기능적 접근 방식으로 작동하도록 비즈니스 프로세스를 구성할 수 있습니다. 여기서 consume 메서드는 반복적으로 실행될 때 동일한 결과를 생성하는 방식으로 정의됩니다. 메시지가 사용되는 방식의 동작을 관리하는 설정의 추가 목록은 Service Bus를 사용하여 Spring Cloud Stream을 참조하세요.

  • 환경의 변경 내용을 관리합니다. 비동기 처리로 인해 작업이 즉시 완료되지 않을 수 있습니다. 올바른 기대치를 설정하고 혼동을 피하기 위해 작업이 여전히 처리되는 경우 사용자에게 알려야 합니다. 시각적 신호 또는 메시지를 사용하여 작업이 진행 중임을 나타냅니다. 전자 메일 또는 푸시 알림과 같은 작업이 완료되면 사용자에게 알림을 받을 수 있는 옵션을 제공합니다.

경쟁 소비자 패턴 구현

분리된 서비스에서 경쟁 소비자 패턴을 구현하여 메시지 큐에서 들어오는 작업을 관리합니다. 이 패턴에는 분리된 서비스의 여러 인스턴스에 태스크를 분산하는 작업이 포함됩니다. 이러한 서비스는 큐에서 메시지를 처리하여 부하 분산을 강화하고 동시 요청을 처리하는 시스템 용량을 향상시킵니다. 경쟁 소비자 패턴은 다음과 같은 경우에 효과적입니다.

  • 메시지 처리 시퀀스는 중요하지 않습니다.
  • 큐는 잘못된 형식의 메시지의 영향을 받지 않습니다.
  • 처리 작업은 idempotent이므로 초기 애플리케이션을 넘어 결과를 변경하지 않고도 여러 번 적용할 수 있습니다.

경쟁 소비자 패턴을 구현하려면 다음 권장 사항을 따릅니다.

  • 동시 메시지를 처리합니다. 큐에서 메시지를 수신할 때 시스템 디자인과 일치하도록 동시성을 구성하여 시스템이 예측 가능한 크기를 조정하는지 확인합니다. 부하 테스트 결과는 처리할 적절한 수의 동시 메시지를 결정하는 데 도움이 되며, 시스템에서 수행하는 방법을 측정하기 위해 하나씩 시작할 수 있습니다.

  • 프리페치를 사용하지 않도록 설정합니다. 소비자가 준비가 된 경우에만 메시지를 가져오도록 메시지 프리페치를 사용하지 않도록 설정합니다.

  • 신뢰할 수 있는 메시지 처리 모드를 사용합니다. 피킹록(또는 해당 항목)과 같은 신뢰할 수 있는 처리 모드를 사용하여 처리에 실패한 메시지를 자동으로 다시 시도합니다. 이 모드는 삭제 우선 메서드에 대한 안정성을 향상시킵니다. 한 작업자가 메시지를 처리하지 못하는 경우 메시지가 여러 번 처리되더라도 다른 작업자가 오류 없이 처리할 수 있어야 합니다.

  • 오류 처리를 구현합니다. 형식이 잘못되었거나 처리할 수 없는 메시지를 별도의 배달 못 한 편지 큐로 라우팅합니다. 이 디자인은 반복적인 처리를 방지합니다. 예를 들어 메시지 처리 중에 예외를 catch하고 문제가 있는 메시지를 별도의 큐로 이동할 수 있습니다. Service Bus의 경우 지정된 수의 배달 시도 또는 애플리케이션의 명시적 거부 후 메시지가 배달 못 한 큐로 이동됩니다.

  • 순서가 다른 메시지를 처리합니다. 순서가 벗어난 메시지를 처리하도록 소비자를 디자인합니다. 여러 병렬 소비자는 메시지를 순서대로 처리할 수 있음을 의미합니다.

  • 큐 길이에 따라 크기 조정합니다. 큐에서 메시지를 사용하는 서비스는 큐 길이에 따라 자동 크기 조정되어야 합니다. 크기 조정 기반 자동 크기 조정을 사용하면 들어오는 메시지의 급증을 효율적으로 처리할 수 있습니다.

  • 메시지-회신 큐를 사용합니다. 시스템에서 메시지 후 처리에 대한 알림이 필요한 경우 전용 회신 또는 응답 큐를 설정합니다. 이 설정은 운영 메시징을 알림 프로세스와 나눕니다.

  • 상태 비지정 서비스를 사용합니다. 상태 비정상 서비스를 사용하여 큐에서 요청을 처리하는 것이 좋습니다. 이를 통해 쉽게 크기를 조정하고 리소스를 효율적으로 사용할 수 있습니다.

  • 로깅을 구성합니다. 메시지 처리 워크플로 내에서 로깅 및 특정 예외 처리를 통합합니다. serialization 오류를 캡처하고 이러한 문제가 있는 메시지를 배달 못 한 편지 메커니즘으로 보내는 데 집중합니다. 이러한 로그는 문제 해결을 위한 유용한 인사이트를 제공합니다.

예를 들어 참조 구현은 Container Apps에서 실행되는 상태 비 상태 비료 서비스에서 경쟁 소비자 패턴을 사용하여 Service Bus 큐에서 전자 메일 배달 요청을 처리합니다.

프로세서는 메시지 처리 세부 정보를 기록하여 문제 해결 및 모니터링을 지원합니다. 역직렬화 오류를 캡처하고 프로세스를 디버깅할 때 필요한 인사이트를 제공합니다. 서비스는 컨테이너 수준에서 확장되므로 큐 길이에 따라 메시지 급증을 효율적으로 처리할 수 있습니다(다음 코드 참조).

@Configuration
public class EmailProcessor {

    private static final Logger log = LoggerFactory.getLogger(EmailProcessor.class);

    @Bean
    Function<byte[], byte[]> consume() {
        return message -> {

            log.info("New message received");

            try {
                EmailRequest emailRequest = EmailRequest.parseFrom(message);
                log.info("EmailRequest: {}", emailRequest);

                EmailResponse emailResponse = EmailResponse.newBuilder()
                        .setEmailAddress(emailRequest.getEmailAddress())
                        .setUrlToManual(emailRequest.getUrlToManual())
                        .setRequestId(emailRequest.getRequestId())
                        .setMessage("Email sent to " + emailRequest.getEmailAddress() + " with URL to manual " + emailRequest.getUrlToManual())
                        .setStatus(Status.SUCCESS)
                        .build();

                return emailResponse.toByteArray();

            } catch (InvalidProtocolBufferException e) {
                throw new RuntimeException("Error parsing email request message", e);
            }
        };
    }
}

상태 엔드포인트 모니터링 패턴 구현

주 앱 코드에서 상태 엔드포인트 모니터링 패턴을 구현하고 분리된 서비스 코드를 구현하여 애플리케이션 엔드포인트의 상태를 추적합니다. AKS 또는 Container Apps와 같은 오케스트레이터는 이러한 엔드포인트를 폴링하여 서비스 상태를 확인하고 비정상 인스턴스를 다시 시작할 수 있습니다. Spring Boot는 데이터베이스, 메시지 브로커 및 스토리지 시스템과 같은 주요 종속성에 대한 상태 검사 엔드포인트를 노출할 수 있는 Spring Boot Actuator를 사용하여 상태 검사에 대한 기본 제공 지원을 제공합니다. 상태 엔드포인트 모니터링 패턴을 구현하려면 다음 권장 사항을 따릅니다.

  • 상태 검사를 구현합니다. Spring Boot Actuator를 사용하여 상태 검사 엔드포인트를 제공합니다. Spring Boot Actuator는 기본 제공 상태 표시기와 다양한 종속성에 대한 사용자 지정 검사를 포함하는 엔드포인트 /actuator/health 를 노출합니다. 상태 엔드포인트를 사용하도록 설정하려면 사용자 pom.xml 또는 build.gradle 파일에 종속성을 추가 spring-boot-starter-actuator 합니다.

    <!-- Add Spring Boot Actuator dependency -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    

    참조 구현에 application.properties 표시된 대로 상태 엔드포인트를 구성합니다. txt management.endpoints.web.exposure.include=metrics,health,info,retry,retryevents

  • 종속성 유효성을 검사합니다. Spring Boot Actuator에는 데이터베이스, 메시지 브로커(RabbitMQ 또는 Kafka) 및 스토리지 서비스와 같은 다양한 종속성에 대한 상태 표시기가 포함되어 있습니다. Azure Blob Storage 또는 Service Bus와 같은 Azure 서비스의 가용성을 확인하려면 이러한 서비스에 대한 상태 지표를 제공하는 Azure Spring Apps 또는 Micrometer 통합과 같은 커뮤니티 플러그 인을 사용합니다. 사용자 지정 검사가 필요한 경우 사용자 지정 HealthIndicator 빈을 만들어 구현할 수 있습니다.

    import org.springframework.boot.actuate.health.Health;
    import org.springframework.boot.actuate.health.HealthIndicator;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CustomAzureServiceBusHealthIndicator implements HealthIndicator {
        @Override
        public Health health() {
            // Implement your health check logic here (e.g., ping Service Bus)
            boolean isServiceBusHealthy = checkServiceBusHealth();
            return isServiceBusHealthy ? Health.up().build() : Health.down().build();
        }
    
        private boolean checkServiceBusHealth() {
            // Implement health check logic (pinging or connecting to the service)
            return true; // Placeholder, implement actual logic
        }
    }
    
  • Azure 리소스를 구성합니다. 앱의 상태 검사 URL을 사용하여 활동성 및 준비 상태를 확인하도록 Azure 리소스를 구성합니다. 예를 들어 Terraform을 사용하여 상태 검사 URL을 사용하여 Container Apps에 배포된 앱의 활동성 및 준비 상태를 확인할 수 있습니다. 자세한 내용은 Container Apps의 상태 프로브를 참조 하세요.

재시도 패턴 구현

재시도 패턴을 사용하면 애플리케이션이 일시적인 오류로부터 복구할 수 있습니다. 재시도 패턴은 신뢰할 수 있는 웹앱 패턴의 핵심이므로 웹앱은 이미 다시 시도 패턴을 사용해야 합니다. 웹앱에서 추출한 분리된 서비스에서 발급한 메시징 시스템 및 요청에 재시도 패턴을 적용합니다. 다시 시도 패턴을 구현하려면 다음 권장 사항을 따릅니다.

  • 다시 시도 옵션을 구성합니다. 메시지 큐와 통합할 때 적절한 재시도 설정을 사용하여 큐와의 상호 작용을 담당하는 클라이언트를 구성해야 합니다. 최대 재시도 횟수, 재시도 사이의 지연 및 최대 지연과 같은 매개 변수를 지정합니다.

  • 지수 백오프를 사용합니다. 재시도를 위한 지수 백오프 전략을 구현합니다. 즉, 각 재시도 사이의 시간을 기하급수적으로 늘려 실패율이 높은 기간 동안 시스템의 부하를 줄이는 데 도움이 됩니다.

  • SDK 재시도 기능을 사용합니다. Service Bus 또는 Blob Storage와 같은 특수한 SDK가 있는 서비스의 경우 기본 제공 재시도 메커니즘을 사용합니다. 기본 제공 재시도 메커니즘은 서비스의 일반적인 사용 사례에 맞게 최적화되어 있으며, 필요한 구성을 줄여 재시도를 보다 효과적으로 처리할 수 있습니다.

  • HTTP 클라이언트에 대한 표준 복원력 라이브러리를 채택합니다. HTTP 클라이언트의 경우 Spring의 RestTemplate 또는 WebClient와 함께 Resilience4*를 사용하여 HTTP 통신에서 재시도를 처리할 수 있습니다. Spring의 RestTemplate을 Resilience4j의 재시도 논리로 래핑하여 일시적인 HTTP 오류를 효과적으로 처리할 수 있습니다.

  • 메시지 잠금을 처리합니다. 메시지 기반 시스템의 경우 데이터 손실 없이 다시 시도(예: 사용 가능한 경우 "피킹 잠금" 모드 사용)를 지원하는 메시지 처리 전략을 구현합니다. 실패한 메시지가 효과적으로 다시 시도되고 반복된 실패 후 배달 못 한 편지 큐로 이동되었는지 확인합니다.

구성 지침

다음 섹션에서는 구성 업데이트 구현에 대한 지침을 제공합니다. 각 섹션은 잘 설계된 프레임워크의 하나 이상의 기둥에 맞춥니다.

구성 안정성(RE) 보안(SE) CO(비용 최적화) 운영 우수성(OE) PE(성능 효율성) 잘 설계된 프레임워크 원칙 지원
인증 및 권한 부여 구성 SE:05
OE:10
독립적인 자동 크기 조정 구현 RE:06
CO:12
PE:05
서비스 배포 컨테이너화 CO:13
PE:09
PE:03

인증 및 권한 부여 구성

웹앱에 추가하는 새 Azure 서비스(워크로드 ID)에 대한 인증 및 권한 부여를 구성하려면 다음 권장 사항을 따릅니다.

  • 각 새 서비스에 대해 관리 ID를 사용합니다. 각 독립 서비스에는 고유한 ID가 있어야 하며 서비스 대 서비스 인증에 관리 ID를 사용해야 합니다. 관리 ID는 코드에서 자격 증명을 관리하고 자격 증명 누출 위험을 줄일 필요가 없습니다. 코드 또는 구성 파일에 연결 문자열 같은 중요한 정보를 배치하지 않도록 방지할 수 있습니다.

  • 각 새 서비스에 최소 권한을 부여합니다. 각 새 서비스 ID에 필요한 권한만 할당합니다. 예를 들어 ID가 컨테이너 레지스트리로 푸시하기만 하면 되는 경우 끌어오기 권한을 부여하지 마세요. 이러한 사용 권한을 정기적으로 검토하고 필요에 따라 조정합니다. 배포 및 애플리케이션과 같은 다양한 역할에 서로 다른 ID를 사용합니다. 이렇게 하면 하나의 ID가 손상된 경우 잠재적인 손상이 제한됩니다.

  • IaC(Infrastructure As Code)를 채택합니다. Bicep 또는 Terraform과 같은 유사한 IaC 도구를 사용하여 클라우드 리소스를 정의하고 관리합니다. IaC는 배포에서 보안 구성의 일관된 애플리케이션을 보장하고 인프라 설정을 버전 제어할 수 있도록 합니다.

사용자(사용자 ID)에 대한 인증 및 권한 부여를 구성하려면 다음 권장 사항을 따릅니다.

  • 사용자에게 최소 권한을 부여합니다. 서비스와 마찬가지로 사용자에게 작업을 수행하는 데 필요한 권한만 부여되었는지 확인합니다. 이러한 사용 권한을 정기적으로 검토하고 조정합니다.

  • 정기적인 보안 감사를 수행합니다. 보안 설정을 정기적으로 검토하고 감사합니다. 잘못된 구성 또는 불필요한 사용 권한을 찾아 즉시 수정합니다.

참조 구현은 IaC를 사용하여 각 ID에 추가된 서비스 및 특정 역할에 관리 ID를 할당합니다. Container Registry 푸시 및 풀에 대한 역할을 정의하여 배포에 대한 역할 및 권한 액세스를 정의합니다(다음 코드 참조).

resource "azurerm_role_assignment" "container_app_acr_pull" {
  principal_id         = var.aca_identity_principal_id
  role_definition_name = "AcrPull"
  scope                = azurerm_container_registry.acr.id
}

resource "azurerm_user_assigned_identity" "container_registry_user_assigned_identity" {
  name                = "ContainerRegistryUserAssignedIdentity"
  resource_group_name = var.resource_group
  location            = var.location
}

resource "azurerm_role_assignment" "container_registry_user_assigned_identity_acr_pull" {
  scope                = azurerm_container_registry.acr.id
  role_definition_name = "AcrPull"
  principal_id         = azurerm_user_assigned_identity.container_registry_user_assigned_identity.principal_id
}


# For demo purposes, allow current user access to the container registry
# Note: when running as a service principal, this is also needed
resource "azurerm_role_assignment" "acr_contributor_user_role_assignement" {
  scope                = azurerm_container_registry.acr.id
  role_definition_name = "Contributor"
  principal_id         = data.azuread_client_config.current.object_id
}

독립적인 자동 크기 조정 구성

최신 웹앱 패턴은 모놀리식 아키텍처를 분리하기 시작하고 서비스 분리를 도입합니다. 웹앱 아키텍처를 분리하는 경우 분리된 서비스를 독립적으로 확장할 수 있습니다. 전체 웹앱이 아닌 독립적인 웹앱 서비스를 지원하도록 Azure 서비스를 확장하면 요구 사항을 충족하는 동안 비용 크기 조정이 최적화됩니다. 컨테이너를 자동 크기 조정하려면 다음 권장 사항을 따릅니다.

  • 상태 비지정 서비스를 사용합니다. 서비스가 상태 비 상태인지 확인합니다. 웹앱에 In-process 세션 상태가 포함된 경우 Redis와 같은 분산 캐시 또는 SQL Server와 같은 데이터베이스로 외부화합니다.

  • 자동 크기 조정 규칙을 구성합니다. 서비스에 대한 가장 비용 효율적인 제어를 제공하는 자동 크기 조정 구성을 사용합니다. 컨테이너화된 서비스의 경우 Kubernetes KEDA(Event-Driven Autoscaler)와 같은 이벤트 기반 크기 조정은 종종 세분화된 제어를 제공하여 이벤트 메트릭에 따라 크기를 조정할 수 있도록 합니다. Container Apps 및 AKS는 KEDA를 지원합니다. App Service와 같이 KEDA를 지원하지 않는 서비스의 경우 플랫폼 자체에서 제공하는 자동 크기 조정 기능을 사용합니다. 이러한 기능에는 메트릭 기반 규칙 또는 HTTP 트래픽을 기반으로 하는 크기 조정이 포함되는 경우가 많습니다.

  • 최소 복제본을 구성합니다. 콜드 시작을 방지하려면 복제본을 하나 이상 유지하도록 자동 크기 조정 설정을 구성합니다. 콜드 시작은 중지된 상태에서 서비스를 초기화하는 경우이며, 종종 지연된 응답을 만듭니다. 비용 최소화가 우선 순위이고 콜드 시작 지연을 허용할 수 있는 경우 자동 크기 조정을 구성할 때 최소 복제본 수를 0으로 설정합니다.

  • 쿨다운 기간을 구성합니다. 적절한 쿨다운 기간을 적용하여 크기 조정 이벤트 간에 지연을 발생합니다. 목표는 임시 부하 급증에 의해 트리거되는 과도한 크기 조정 작업을 방지하는 것입니다.

  • 큐 기반 크기 조정을 구성합니다. 애플리케이션에서 Service Bus와 같은 메시지 큐를 사용하는 경우 요청 메시지가 있는 큐의 길이에 따라 크기를 조정하도록 자동 크기 조정 설정을 구성합니다. Scaler는 큐의 모든 N 메시지에 대해 하나의 서비스 복제본을 유지 관리하는 것을 목표로 합니다(반올림됨).

예를 들어 참조 구현은 Service Bus KEDA 스케일러를 사용하여 Service Bus 큐의 길이에 따라 컨테이너 앱의 크기를 자동으로 조정합니다. 명명된 service-bus-queue-length-rule크기 조정 규칙은 지정된 Service Bus 큐의 메시지 수에 따라 서비스 복제본 수를 조정합니다. 매개 변수는 messageCount 10으로 설정됩니다. 즉, scaler는 큐에 있는 10개 메시지마다 하나의 복제본을 추가합니다. 최대 복제본(max_replicas)은 10으로 설정되고, 재정의되지 않는 한 최소 복제본은 암시적으로 0입니다. 그러면 큐에 메시지가 없을 때 서비스가 0으로 축소될 수 있습니다. Service Bus 큐에 대한 연결 문자열 Azure에서 비밀로 안전하게 저장되며, Service azure-servicebus-connection-stringBus에 대한 스케일러를 인증하는 데 사용됩니다.

    max_replicas = 10
    min_replicas = 1

    custom_scale_rule {
      name             = "service-bus-queue-length-rule"
      custom_rule_type = "azure-servicebus"
      metadata = {
        messageCount = 10
        namespace    = var.servicebus_namespace
        queueName    = var.email_request_queue_name
      }
      authentication {
        secret_name       = "azure-servicebus-connection-string"
        trigger_parameter = "connection"
      }
    }

서비스 배포 컨테이너화

컨테이너화는 앱이 작동하기 위한 모든 종속성이 광범위한 호스트에 안정적으로 배포될 수 있는 경량 이미지로 캡슐화됨을 의미합니다. 배포를 컨테이너화하려면 다음 권장 사항을 따릅니다.

  • 도메인 경계를 식별합니다. 먼저 모놀리식 애플리케이션 내에서 도메인 경계를 식별합니다. 이렇게 하면 별도의 서비스로 추출할 수 있는 애플리케이션 부분을 결정하는 데 도움이 됩니다.

  • Docker 이미지를 만듭니다. Java 서비스에 대한 Docker 이미지를 만들 때 공식 OpenJDK 기본 이미지를 사용합니다. 이러한 이미지에는 Java를 실행하는 데 필요한 최소한의 패키지 집합만 포함되어 패키지 크기와 공격 노출 영역을 최소화합니다.

  • 다단계 Dockerfile을 사용합니다. 다단계 Dockerfiles를 사용하여 런타임 컨테이너 이미지와 빌드 시간 자산을 구분합니다. 프로덕션 이미지를 작고 안전하게 유지하는 데 도움이 됩니다. 미리 구성된 빌드 서버를 사용하고 jar 파일을 컨테이너 이미지에 복사할 수도 있습니다.

  • 비루트 사용자로 실행합니다. Java 컨테이너를 사용자 이름 또는 UID, $APP_UID를 통해 비루트 사용자로 실행하여 최소 권한 원칙에 맞춥니다. 손상된 컨테이너의 잠재적인 영향을 제한합니다.

  • 포트 8080에서 수신 대기합니다. 비루트 사용자로 실행하는 경우 포트 8080에서 수신 대기하도록 애플리케이션을 구성합니다. 이는 비루트 사용자에 대한 일반적인 규칙입니다.

  • 종속성을 캡슐화합니다. 앱이 작동하기 위한 모든 종속성이 Docker 컨테이너 이미지에 캡슐화되어 있는지 확인합니다. 캡슐화를 사용하면 다양한 호스트에 앱을 안정적으로 배포할 수 있습니다.

  • 올바른 기본 이미지를 선택합니다. 선택하는 기본 이미지는 배포 환경에 따라 달라집니다. 예를 들어 Container Apps에 배포하는 경우 Linux Docker 이미지를 사용해야 합니다.

참조 구현은 Java 애플리케이션을 컨테이너화하기 위한 Docker 빌드 프로세스를 보여 줍니다. 이 Dockerfile은 필요한 Java 런타임 환경을 제공하는 OpenJDK 기본 이미지()mcr.microsoft.com/openjdk/jdk:17-ubuntu와 함께 단일 단계 빌드를 사용합니다.

Dockerfile에는 다음 단계가 포함됩니다.

  1. 볼륨 선언: 임시 볼륨(/tmp)이 정의되어 컨테이너의 기본 파일 시스템과 분리된 임시 파일 스토리지를 허용합니다.
  2. 아티팩트 복사: 애플리케이션의 JAR 파일(email-processor.jar)이 모니터링을 위해 Application Insights 에이전트(applicationinsights-agent.jar)와 함께 컨테이너에 복사됩니다.
  3. 진입점 설정: 컨테이너는 런타임 중에 애플리케이션을 모니터링하는 데 사용하여 Application Insights 에이전트를 사용하도록 java -javaagent 설정된 애플리케이션을 실행하도록 구성됩니다.

이 Dockerfile은 Linux 기반 컨테이너를 지원하는 Container Apps와 같은 배포 환경에 적합한 런타임 종속성만 포함하여 이미지를 유지합니다.

# Use OpenJDK 17 base image on Ubuntu as the foundation
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu

# Define a volume to allow temporary files to be stored separately from the container's main file system
VOLUME /tmp

# Copy the packaged JAR file into the container
COPY target/email-processor.jar app.jar

# Copy the Application Insights agent for monitoring
COPY target/agent/applicationinsights-agent.jar applicationinsights-agent.jar

# Set the entry point to run the application with the Application Insights agent
ENTRYPOINT ["java", "-javaagent:applicationinsights-agent.jar", "-jar", "/app.jar"]

참조 구현 배포

Java용 최신 웹앱 패턴의 참조 구현을 배포합니다. 리포지토리의 개발 및 프로덕션 배포에 대한 지침이 있습니다. 배포 후에는 디자인 패턴을 시뮬레이션하고 관찰할 수 있습니다.

참조 구현의 아키텍처를 보여 주는 다이어그램그림 3. 참조 구현의 아키텍처입니다. 이 아키텍처의 Visio 파일을 다운로드합니다.