성능 효율성 검사 목록
Microsoft Azure Well-Architected Framework의 핵심 요소 중 하나인 성능 효율성은 워크로드를 효율적으로 사용자가 요구한 사항에 맞게 확장할 수 있는 기능입니다. 이 검사 목록을 사용하여 성능 효율성 관점에서 애플리케이션 아키텍처를 검토합니다.
애플리케이션 설계
크기 조정을 고려한 디자인. 크기 조정을 사용하면 애플리케이션이 사용하는 역할, 큐 및 기타 서비스의 수를 늘리고 줄이면서 가변 부하에 대응할 수 있습니다. 애플리케이션은 크기 조정을 염두에 두고 설계해야 합니다.
애플리케이션 및 사용하는 서비스는 상태 비저장 상태여야 하므로 요청을 모든 instance 라우팅할 수 있으며 특정 인스턴스를 추가하거나 제거해도 사용자에게 부정적인 영향을 주지 않습니다. 구성, 자동 검색 또는 부하 분산을 구현하여 서비스가 추가되거나 제거되면 애플리케이션에서 필요한 라우팅을 수행할 수 있습니다.
예를 들어 웹 애플리케이션은 라운드 로빈 방식으로 큐 집합을 사용하여 작업자 역할에서 실행되는 백그라운드 서비스로 요청을 라우팅할 수 있습니다. 웹 애플리케이션은 요청을 성공적으로 라우팅하고 애플리케이션의 부하를 분산하기 위해 큐 수의 변경 내용을 검색할 수 있어야 합니다.
Azure NAT Gateway를 사용하여 인터넷에 대한 아웃바운드 연결을 확장할 수 있습니다. Azure NAT Gateway는 아웃바운드 트래픽을 인터넷에 연결하는 확장 가능하고 안정적이며 안전한 방법을 제공하며 SNAT 고갈로 인한 연결 오류를 방지하는 데 도움이 됩니다.
워크로드 분할. 프로세스의 각 부분을 불연속 및 분해되도록 디자인합니다. 각 부분의 크기를 최소화하고 우려 사항 분리 및 단일 책임 원칙에 대한 일반적인 규칙을 따릅니다. 이러한 사례를 통해 구성 요소 파트를 배포하여 역할 또는 데이터베이스 서버와 같은 각 컴퓨팅 단위의 사용을 최대화할 수 있습니다.
워크로드를 분할하면 특정 리소스의 인스턴스를 추가하여 애플리케이션의 크기를 쉽게 조정할 수 있습니다. 복합 도메인의 경우 마이크로 서비스 아키텍처를 도입하는 방안을 고려해 봅니다.
단위로 크기 조정. 성장을 수용하기 위해 더 많은 리소스를 계획합니다. 각 리소스에 대해 크기 조정 상한을 알고 이러한 한도를 초과하려면 분할 또는 분해를 사용합니다. 또한 단위로 크기를 조정하면 전체 시스템의 다른 부분에서 리소스 제한으로 인한 부정적인 영향을 덜 쉽게 수행할 수 있습니다.
스케일 아웃 작업을 더 쉽게 하려면 잘 정의된 리소스 집합에서 시스템의 배율 단위를 결정합니다. 예를 들어 x 개수의 웹 및 작업자 역할을 추가하려면 추가된 워크로드를 처리하기 위해 y 추가 큐 및 z 스토리지 계정이 필요할 수 있습니다. 따라서 배율 단위는 x 웹 및 작업자 역할, y 큐 및 z 스토리지 계정으로 구성될 수 있습니다.
하나 이상의 배율 단위를 추가하여 쉽게 확장할 수 있도록 애플리케이션을 디자인합니다. 배포 스탬프 패턴을 사용하여 배율 단위를 배포하는 것이 좋습니다.
플랫폼 자동 크기 조정을 활용합니다. Azure와 같은 호스팅 플랫폼이 자동 크기 조정을 지원하는 경우 기본 제공 메커니즘이 요구 사항을 충족할 수 없는 경우 사용자 지정 또는 타사 메커니즘을 사용하는 것이 좋습니다. 가능한 경우 크기 조정 규칙을 예약하여 시작 지연 없이 리소스를 사용할 수 있도록 합니다. 적절한 경우 예기치 않은 수요 변화에 대처하기 위해 규칙에 반응형 자동 크기 조정을 추가합니다. 자세한 내용은 자동 크기 조정 지침을 참조하세요.
참고
자동 크기 조정 작업을 사용하고 이전 Azure 클래식 배포 모델의 규칙에 사용자 지정 카운터를 추가할 수 있습니다. 자세한 내용은 클래식 배포 모델에 대한 VM 제품군 vCPU 할당량 증가를 참조하세요.
클라이언트 선호도 방지. 가능한 경우 애플리케이션에 선호도가 필요하지 않은지 확인합니다. 그런 다음 요청을 모든 instance 라우팅할 수 있으며 인스턴스 수는 관련이 없습니다. 또한 각 사용자에 대한 상태 정보를 저장, 검색 및 유지 관리하는 오버헤드를 방지합니다.
CPU 집약적 및 I/O 집약적 작업을 백그라운드 작업으로 오프로드. 서비스에 대한 요청이 많은 리소스를 실행하거나 사용하는 데 시간이 오래 걸릴 것으로 예상되는 경우 이 요청에 대한 처리를 별도의 작업으로 오프로드합니다. 호스팅 플랫폼에 따라 작업자 역할 또는 백그라운드 작업을 사용하여 이러한 작업을 실행합니다. 이 전략을 사용하면 서비스가 계속 요청을 수신하고 응답을 유지할 수 있습니다. 자세한 내용은 백그라운드 작업 지침을 참조하세요.
백그라운드 작업에 대한 워크로드 분산. 백그라운드 작업이 많거나 작업에 상당한 시간 또는 리소스가 필요한 경우 작업자 역할 또는 백그라운드 작업과 같은 여러 컴퓨팅 단위로 작업을 분산합니다. 가능한 한 가지 솔루션은 경쟁 소비자 패턴을 참조하세요.
공유 없음 아키텍처로 이동합니다. 공유 없음 아키텍처는 공유 서비스 또는 스토리지와 같은 단일 경합 지점이 없는 독립적이고 자급자족 노드를 사용합니다. 이론적으로 이러한 시스템은 거의 무제한적으로 확장될 수 있습니다.
완전히 공유되지 않는 접근 방식은 대부분의 애플리케이션에서 실용적이지 않지만 더 나은 성능을 위해 디자인할 수 있는 기회를 제공할 수 있습니다. 예를 들어 서버 쪽 세션 상태, 클라이언트 선호도 및 데이터 분할 사용을 피하는 것은 비공유 아키텍처로 이동하는 좋은 예입니다.
데이터 관리
데이터 분할 사용. 데이터를 여러 데이터베이스 및 데이터베이스 서버로 나눕니다. Azure SQL 데이터베이스 탄력적 풀 및 Azure Table Storage와 같이 투명하게 분할을 제공하는 데이터 스토리지 서비스를 사용하도록 애플리케이션을 디자인할 수 있습니다. 이 방법은 성능을 최대화하고 더 쉽게 크기를 조정하는 데 도움이 될 수 있습니다. 분할의 이점에는 더 나은 쿼리 성능 및 가용성, 더 간단한 확장성 및 더 쉬운 관리가 포함됩니다.
다양한 분할 기술에는 수평, 수직 및 기능이 포함 됩니다. 이러한 기술을 결합하여 최대의 이점을 얻을 수 있습니다. 다른 데이터 저장소 형식을 다른 데이터 형식과 일치시키고 특정 데이터 형식에 최적화된 데이터 저장소 형식을 선택합니다. 예를 들어 관계형 데이터베이스 대신 테이블 스토리지, 문서 데이터베이스 또는 열 패밀리 데이터 저장소를 사용할 수 있습니다. 자세한 내용은 데이터 분할 지침을 참조하세요.
결과적 일관성을 위한 디자인. 결과적 일관성은 여러 저장소에 분할된 관련 데이터를 동기화하는 데 필요한 시간을 줄이거나 제거하여 확장성을 향상시킵니다. 비용은 데이터를 읽을 때 항상 일관되지는 않으며 일부 쓰기 작업으로 인해 충돌이 발생할 수 있다는 것입니다. 결과적 일관성은 동일한 데이터의 읽기 빈도는 높은 반면, 쓰기 빈도는 낮은 경우에 이상적입니다. 자세한 내용은 데이터 일관성 입문을 참조하세요.
구성 요소와 서비스 간의 수다스러운 상호 작용 감소. 애플리케이션이 서비스를 여러 개 호출해야 하는 상호 작용을 디자인하지 않고 각각 소량의 데이터를 반환합니다. 대신 모든 데이터를 반환할 수 있는 단일 호출을 사용합니다. 예를 들어 데이터베이스의 저장 프로시저를 사용하여 복잡한 논리를 캡슐화하고 왕복 횟수를 줄이며 리소스 잠금을 방지할 수 있습니다.
가능한 경우, 대기 시간이 긴 서비스 또는 구성 요소를 호출하는 경우 여러 관련 작업을 단일 요청으로 결합합니다. 이 방법을 사용하면 성능을 보다 쉽게 모니터링하고 복잡한 작업을 최적화할 수 있습니다.
큐를 사용하여 고속 데이터 쓰기에 대한 부하 평준화. 수요 급증으로 인해 서비스가 과부하가 발생하고 오류가 증가할 수 있습니다. 큐 기반 부하 평준화 패턴은 이 상황을 방지하는 데 도움이 될 수 있습니다. 이 패턴은 큐를 작업과 호출하는 서비스 간의 버퍼로 사용합니다. 큐는 간헐적인 부하를 원활하게 처리할 수 있으며, 그렇지 않으면 서비스가 실패하거나 작업 시간이 초과될 수 있습니다.
데이터 저장소에 대한 부하 최소화. 데이터 저장소는 비용이 많이 드는 리소스이며 처리 병목 상태인 경우가 많으며 규모 확장이 쉽지 않습니다. 데이터 저장소보다 애플리케이션을 스케일 아웃하는 것이 더 쉽기 때문에 애플리케이션 내에서 계산 집약적인 처리를 최대한 많이 시도해야 합니다.
가능한 경우 데이터 저장소에서 XML 문서 또는 JSON 개체 처리와 같은 논리를 제거하고 애플리케이션 내에서 처리를 수행합니다. 예를 들어 스토리지에 대한 불투명 문자열이 아닌 데이터베이스에 XML을 전달하지 마세요. 애플리케이션 계층 내에서 XML을 직렬화하거나 역직렬화하고 데이터 저장소에 네이티브 양식으로 전달합니다.
검색되는 데이터 양 최소화. 열을 지정하고 조건을 사용하여 행을 선택하는 방식으로 필요한 데이터만 검색합니다. 테이블 값 매개 변수 및 적절한 격리 수준을 사용합니다. 불필요하게 데이터를 검색하지 않도록 엔터티 태그와 같은 메커니즘을 사용합니다.
적극적으로 캐싱 사용. 가급적 캐싱을 사용하여 데이터를 생성하거나 전달하는 리소스 및 서비스에 대한 부하를 줄입니다. 캐싱은 상대적으로 정적이거나 가져오기 위해 상당한 처리가 필요한 데이터에 가장 적합합니다. 적절한 경우 데이터 액세스 및 사용자 인터페이스 생성을 포함하여 애플리케이션의 각 계층에서 캐싱이 발생해야 합니다. 자세한 내용은 캐싱 지침을 참조하세요.
데이터 증가 및 보존 처리. 애플리케이션에서 저장된 데이터 양은 시간이 지남에 따라 증가합니다. 이렇게 증가하면 스토리지 비용과 데이터 액세스 대기 시간이 증가하여 애플리케이션 처리량 및 성능에 영향을 줍니다. 가능하면 더 이상 액세스하지 않는 이전 데이터 중 일부를 주기적으로 보관합니다. 또는 액세스 대기 시간이 더 길더라도 거의 액세스하지 않는 데이터를 더 비용 효율적인 장기 스토리지로 이동합니다.
효율적인 이진 형식을 사용하여 DTO(데이터 전송 개체)를 최적화합니다. DTO는 애플리케이션 계층 간에 여러 번 전달됩니다. 크기를 최소화하면 리소스 및 네트워크에 대한 부하가 줄어듭니다. 데이터를 사용하는 각 위치에서 필요한 형식으로 변환하는 오버헤드와 절감액의 균형을 맞습니다. 구성 요소를 쉽게 재사용하도록 하려면 최대 상호 운용성이 있는 형식을 채택합니다.
캐시 제어 설정. 처리 부하를 최소화하려면 가능한 경우 출력 캐싱 또는 조각 캐싱을 사용하도록 애플리케이션을 디자인하고 구성합니다.
클라이언트 쪽 캐싱 사용. 웹 애플리케이션은 캐시할 수 있는 콘텐츠에서 캐시 설정을 사용하도록 설정해야 하며, 기본적으로 사용하지 않도록 설정되는 경우가 많습니다. 프록시 서버 및 클라이언트에서 콘텐츠를 캐시할 수 있게 적절한 캐시 제어 헤더를 제공하도록 서버를 구성합니다.
Azure Blob Storage 및 Azure Content Delivery Network를 사용하여 애플리케이션의 부하를 줄입니다. 이미지, 리소스, 스크립트, 스타일 시트 등 정적이거나 비교적 정적인 공용 콘텐츠를 Blob Storage에 저장하는 것이 좋습니다. 이 방법은 각 요청에 대해 이 콘텐츠를 동적으로 생성하는 부하에서 애플리케이션을 완화합니다.
또한 Content Delivery Network를 사용하여 이 콘텐츠를 캐시하고 클라이언트에 배달하는 것이 좋습니다. Content Delivery Network를 사용하면 콘텐츠가 Content Delivery Network 캐시를 포함하는 지리적으로 가장 가까운 데이터 센터에서 배달되기 때문에 클라이언트의 성능이 향상됩니다. 자세한 내용은 콘텐츠 전송 네트워크 사용 모범 사례를 참조하세요.
SQL 쿼리 및 인덱스 최적화 및 튜닝. 일부 T-SQL 문 또는 구문은 저장 프로시저에서 코드를 최적화하여 성능에 부정적인 영향을 줄 수 있습니다. 예를 들어 datetime 리터럴 값과 비교하기 전에 datetime 형식을 varchar로 변환하지 마세요. 대신 date/time 비교 함수를 사용합니다.
적절한 인덱스가 없으면 쿼리 실행 속도가 느려질 수 있습니다. 개체/관계형 매핑 프레임워크를 사용하는 경우 작동 방식과 데이터 액세스 계층의 성능에 미치는 영향을 이해합니다. 자세한 내용은 쿼리 튜닝을 참조하세요.
데이터를 비정규화합니다. 데이터 정규화는 중복 및 불일치를 방지하는 데 도움이 됩니다. 그러나 데이터 정규화는 성능에 영향을 줄 수 있는 오버헤드를 부과합니다. 오버헤드에는 여러 인덱스 유지 관리, 참조 무결성 확인, 작은 데이터 청크에 대한 여러 액세스 수행, 데이터를 다시 조립하기 위한 테이블 조인 등이 포함됩니다. 일부 추가된 스토리지 볼륨 및 중복이 데이터 저장소의 부하를 줄이는 데 허용되는지 여부를 고려합니다.
또한 일반적으로 크기 조정이 더 쉬운 애플리케이션 자체를 사용하여 참조 무결성 관리와 같은 작업을 인수하여 데이터 저장소의 부하를 줄일 수 있는지도 고려합니다. 자세한 내용은 데이터 분할 지침을 참조하세요.
구현
성능 안티패턴을 방지합니다. 애플리케이션이 압박을 받을 때 성능 문제를 일으키는 일반적인 사례는 클라우드 애플리케이션에 대한 성능 안티패턴 을 검토합니다.
비동기 호출 사용. 가능하면 비동기 코드를 사용하여 I/O 또는 네트워크 대역폭으로 제한되거나 눈에 띄는 대기 시간이 있는 리소스 또는 서비스에 액세스합니다. 비동기 호출은 호출 스레드를 잠그지 않습니다.
리소스 잠금을 방지하고, 대신 낙관적 접근 방식 사용. 대기 시간이 눈에 띄는 스토리지 또는 기타 서비스와 같은 리소스에 대한 액세스를 잠그는 것이 성능 저하의 주요 원인입니다. 항상 낙관적 접근 방식을 사용하여 스토리지에 쓰기와 같은 동시 작업을 관리합니다. 스토리지 계층 기능을 사용하여 충돌을 관리합니다. 분산 애플리케이션에서 데이터는 최종적으로만 일관될 수 있습니다.
대기 시간이 높고 대역폭이 낮은 네트워크를 통해 압축성이 뛰어난 데이터 압축. 대부분의 웹 애플리케이션에서 클라이언트 요청에 대한 HTTP 응답은 애플리케이션에서 생성되고 네트워크를 통해 전달되는 가장 큰 양의 데이터입니다. HTTP 압축은 특히 정적 콘텐츠의 경우 이 볼륨을 줄일 수 있습니다.
동적 콘텐츠를 압축하면 네트워크에서 비용과 부하를 줄일 수 있지만 서버에 약간 더 높은 부하가 적용됩니다. 보다 일반화된 환경에서 데이터 압축은 전송되는 데이터의 양을 줄이고 전송 시간과 비용을 최소화할 수 있지만 압축 및 압축 해제 프로세스에는 오버헤드가 발생합니다.
참고
압축은 성능이 입증할 수 있는 경우에만 사용해야 합니다. JSON 또는 이진 인코딩과 같은 다른 serialization 메서드는 페이로드 크기를 줄이고 성능에 미치는 영향을 줄일 수 있지만 XML은 이를 증가시킬 가능성이 높습니다.
연결 및 리소스 사용 시간 최소화. 사용해야 하는 시간 동안만 연결 및 리소스를 유지 관리합니다. 가능한 한 늦게 연결을 열고 가능한 한 빨리 연결 풀로 반환할 수 있습니다. 또한 리소스를 최대한 늦게 획득하고 최대한 빨리 삭제합니다.
필요한 연결 수 최소화. 서비스 연결에는 리소스가 사용됩니다. 필요한 서비스 연결 수를 제한하고 가능하면 기존 연결이 다시 사용되도록 합니다. 예를 들어 인증 후 적절한 경우 가장을 사용하여 코드를 특정 ID로 실행합니다. 가장은 연결을 다시 사용하여 연결 풀을 최대한 활용하는 데 도움이 됩니다.
참고
서비스별 지침을 따르는 경우 일부 서비스에 대한 API는 자동으로 연결을 다시 사용합니다. 애플리케이션에서 사용하는 각 서비스에 대해 연결을 다시 사용할 수 있는 조건을 이해합니다.
요청을 일괄 처리로 보내 네트워크 사용 최적화. 예를 들어 큐에 액세스할 때 메시지를 일괄 처리로 보내고 읽고 스토리지 또는 캐시에 액세스할 때 일괄 처리로 여러 읽기 또는 쓰기를 수행합니다. 일괄 처리는 네트워크 전체의 호출 수를 줄임으로써 서비스 및 데이터 저장소의 효율성을 극대화하는 데 도움이 될 수 있습니다.
서버 쪽 세션 상태를 저장하기 위한 요구 사항을 방지합니다. 서버 쪽 세션 상태 관리에는 일반적으로 클라이언트 선호도가 필요하거나 각 요청을 동일한 서버 instance 라우팅하여 시스템의 크기 조정 기능에 영향을 줍니다. 가능한 경우 사용하는 서버와 관련하여 클라이언트를 상태 비 상태 비지정으로 디자인합니다. 그러나 애플리케이션에서 세션 상태를 유지해야 하는 경우에는 중요한 데이터 또는 대량의 클라이언트별 데이터를 애플리케이션의 모든 인스턴스에서 액세스할 수 있는 분산된 서버 쪽 캐시에 저장하세요.
Table Storage 스키마 최적화. Table Storage와 같은 모든 쿼리에서 테이블 및 열 이름을 전달하고 처리해야 하는 테이블 저장소를 사용하는 경우 더 짧은 이름을 사용하여 오버헤드를 줄이는 것이 좋습니다. 그러나 지나치게 압축된 이름을 사용하여 가독성 또는 관리 용이성을 희생하지 마세요.
배포 중 또는 애플리케이션 시작 시 리소스 종속성 만들기. 리소스의 존재를 테스트한 다음 리소스가 없는 경우 리소스를 만드는 메서드에 대한 반복 호출을 방지합니다. Azure Storage 클라이언트 라이브러리의 및
CloudQueue.CreateIfNotExists
와 같은CloudTable.CreateIfNotExists
메서드는 이 패턴을 따릅니다. 이러한 메서드는 스토리지 테이블 또는 스토리지 큐에 대한 각 액세스 전에 호출되는 경우 상당한 오버헤드를 부과할 수 있습니다.대신 애플리케이션이 배포되거나 처음 시작될 때 필요한 리소스를 만듭니다. 웹 또는 작업자 역할에 대한 시작 코드의 각 리소스에 대해 에 대한 단일 호출
CreateIfNotExists
이 허용됩니다. 그러나 코드가 존재하지 않는 리소스에 액세스하려고 하면 발생할 수 있는 예외를 처리해야 합니다. 이러한 경우는 예외를 기록하고 리소스가 누락되었음을 작업자에게 경고해야 합니다.경우에 따라 예외 처리 코드의 일부로 누락된 리소스를 만드는 것이 적절할 수 있습니다. 리소스가 존재하지 않는 경우 프로그래밍 오류, 철자가 틀린 리소스 이름 또는 기타 인프라 수준 문제를 나타낼 수 있으므로 이 방법을 주의해서 사용합니다.
경량 프레임워크 사용. 리소스 사용, 실행 시간 및 애플리케이션의 전체 부하를 최소화하려면 사용할 API 및 프레임워크를 신중히 선택해야 합니다. 예를 들어 웹 API를 사용하여 서비스 요청을 처리하면 애플리케이션 공간을 줄이고 실행 속도를 높일 수 있습니다. 그러나 웹 API는 Windows Communication Foundation의 추가 기능이 필요한 고급 시나리오에는 적합하지 않을 수 있습니다.
서비스 계정 수를 최소화합니다. 예를 들어 특정 계정을 사용하여 연결을 제한하거나 더 적은 수의 연결이 유지 관리될 때 더 나은 성능을 제공하는 리소스 또는 서비스에 액세스합니다. 이 방법은 데이터베이스와 같은 서비스에 일반적이지만 원래 사용자의 가장으로 인해 정확한 감사에 영향을 줄 수 있습니다.
성능 프로파일링 및 부하 테스트 수행 배포 중, 테스트 루틴 과정, 최종 릴리스 전에 수행하여 애플리케이션이 정상적으로 실행되고 필요에 따라 확장되도록 합니다. 이 테스트는 프로덕션 플랫폼과 동일한 유형의 하드웨어에서 수행해야 합니다. 프로덕션에서와 동일한 형식 및 수량의 데이터 및 사용자 로드를 사용합니다. 자세한 내용은 클라우드 서비스의 성능 테스트를 참조하세요.