확장 가능하고 탄력적인 애플리케이션 빌드

탄력적이고 확장 가능한 웹 애플리케이션 빌드는 모든 애플리케이션 아키텍처의 핵심입니다. 효과적으로 설계된 애플리케이션이라면 수요 증감에 따라 크기가 원활하게 조정되어야 할 뿐만 아니라 컴퓨팅 리소스가 1개 이상 손실되더라도 견딜 수 있을 만큼 충분히 탄력적이어야 합니다.

이 문서는 Compute Engine에 익숙한 시스템 운영 전문가를 대상으로 합니다. 이번 문서에서는 Google Cloud Platform(GCP)에서 모든 애플리케이션에 널리 적용되는 패턴과 사례를 사용해 확장 가능하고 탄력적인 애플리케이션 아키텍처를 빌드하는 방법에 대해서 알아보겠습니다. 또한 흔히 사용되는 Ruby on Rails 기반 애플리케이션인 Redmine이라는 오픈소스 프로젝트 관리 도구를 배포하는 예를 통해 이러한 원리가 실제 시나리오에서 어떻게 적용되는지 살펴보겠습니다. 이후 솔루션 예 배포 섹션에서 직접 애플리케이션을 배포하고 참조를 위해 모든 소스를 다운로드할 수 있습니다.

GCP를 사용하면 확장성과 탄력성이 우수한 웹 애플리케이션을 빌드하고 실행할 수 있습니다. 또한 수요에 따라 Compute Engine이나 자동 확장 처리 같은 서비스를 사용해 애플리케이션 리소스를 조정할 수도 있습니다. 그 밖에도 Compute Engine의 가격 책정 모델을 사용하면 초 단위로 비용을 지불하고 복잡한 용량 또는 예약 계획 없이 지속 사용 할인을 통해 자동으로 최적의 가격 혜택을 얻을 수 있습니다.

GCP에서 웹을 호스팅하는 옵션에 대한 일반적인 개요는 웹사이트 제공을 참조하세요.

확장성 및 탄력성 정의

샘플 애플리케이션 아키텍처를 설명하기 전에 이해를 돕기 위해 확장성탄력성이라는 용어를 정의하겠습니다.

확장성: 수요에 따른 용량 조정

확장 가능한 애플리케이션이라면 1명이 사용하든 1,000,000명이 사용하든 문제없이 작동하고 트래픽의 급증과 감소를 자동으로 원활하게 처리합니다. 확장 가능한 애플리케이션은 필요한 경우에만 가상 머신을 추가 및 제거함으로써 수요를 충족하는 데 필요한 리소스만을 사용합니다.

다음 다이어그램은 확장 가능한 애플리케이션이 수요 증가 및 감소에 응답하는 모습을 보여줍니다.

수요를 충족하기 위해 확장되는 리소스를 나타내는 차트

수요 변화에 따라 용량이 동적으로 조정되는 것을 확인할 수 있습니다. 설계에서 유연성이라고도 하는 이러한 구성을 사용하면 특정 시점에 애플리케이션에 필요한 컴퓨팅 리소스에만 비용을 지불할 수 있습니다.

탄력성: 예기치 못한 상황을 감내하도록 설계

가용성이 높은(탄력적인) 애플리케이션은 예상된 또는 예기치 못한 시스템의 구성요소 오류와 상관없이 계속 작동해야 합니다. 하나의 인스턴스가 오류를 일으키거나 전체 영역에서 문제가 발생하면 탄력적인 애플리케이션은 내결함성을 유지하여 지속적으로 작동하며 필요한 경우 자동으로 자체 복원합니다. 상태 저장 정보가 단일 인스턴스에 저장되지 않기 때문에 하나의 인스턴스 또는 전체 영역이 손실되어도 애플리케이션의 성능에는 영향을 미치지 않습니다.

완벽하게 탄력적인 애플리케이션을 사용하려면 소프트웨어 개발 수준과 애플리케이션 아키텍처 수준 모두에서 계획해야 합니다. 이 문서에서는 주로 애플리케이션 아키텍처 수준에 대해 살펴봅니다.

탄력적인 애플리케이션을 위한 애플리케이션 아키텍처를 설계하는 데 일반적으로 사용되는 요소는 다음과 같습니다.

  • 서버를 모니터링하고 요청을 가장 잘 처리할 수 있는 서버에 트래픽을 분산시키는 부하 분산기
  • 여러 리전에 가상 머신 호스팅
  • 강력한 스토리지 솔루션 구성

GCP: 유연성 및 비용 효율성

확장성과 탄력성을 지원하는 기존의 아키텍처는 리소스에 상당한 투자를 필요로 하는 경우가 많습니다. 온프레미스 솔루션을 사용할 경우 확장성을 실현하려면 최고 사용량을 처리할 수 있는 서버 용량에 과도한 비용을 소비하거나, 평균 수요만을 기반으로 구매하여 트래픽이 급증할 경우 애플리케이션 성능 또는 사용자 환경이 저하될 위험에 노출되는 선택지 중에서 결정해야 하는 경우가 많습니다. 하지만 탄력성을 구현하려면 서버 용량뿐만 아니라 지역 또한 중요한 고려사항입니다. 강한 폭풍이나 지진과 같은 물리적 사건의 영향을 줄이려면 다른 물리적 위치에서 서버를 운영하는 것을 고려해야 하는데, 이를 위해서는 비용이 많이 듭니다.

GCP는 아키텍처에 확장성과 탄력성을 추가하는 유연한 방법을 제공하는 대안인 클라우드 서비스 세트를 제공합니다. 또한 이러한 서비스는 고객이 직접 통제할 수 있는 가격 책정 구조를 통해 제공됩니다.

GCP를 사용해 탄력적이고 확장 가능한 아키텍처 빌드

다음 표는 여러 GCP 서비스가 확장 가능하고 탄력적인 애플리케이션을 만들기 위해 필요한 주요 요구 사항 중 어느 것과 관련이 있는지 보여줍니다.

아키텍처 요구 사항 GCP 서비스
부하 분산 HTTP 부하 분산
서버 호스팅 Compute Engine, 리전 및 영역
서버 관리 인스턴스 템플릿, 관리형 인스턴스 그룹, 자동 확장 처리
데이터 저장 Cloud SQL, Cloud Storage

다음 다이어그램은 이러한 GCP 구성요소가 함께 작동하여 확장 가능하고 탄력적인 애플리케이션을 빌드하는 방법을 보여줍니다. 각 구성요소의 역할에 대해서는 다음 섹션에서 보다 자세히 설명하겠습니다.

확장 가능하고 탄력적인 애플리케이션을 나타내는 다이어그램

구성요소 개요

예시 애플리케이션 아키텍처의 각 구성요소는 애플리케이션의 확장성과 탄력성을 모두 보장하는 역할을 합니다. 이 섹션에서는 이러한 서비스를 간략하게 설명합니다. 이후 섹션에서는 이러한 서비스가 함께 작동하는 방법에 대해 설명합니다.

HTTP 부하 분산기

HTTP 부하 분산기는 고객이 애플리케이션에 액세스하는 데 사용하는 단일 공개 IP 주소를 노출합니다. 이 IP 주소는 DNS A 레코드(예: example.com) 또는 CNAME(예: www.example.comwww.example.com)에 연결할 수 있습니다. 수신되는 요청은 각 그룹의 용량에 따라 각 영역의 인스턴스 그룹에 분산됩니다. 영역 내에서 요청은 그룹 내의 인스턴스에 고르게 분산됩니다. HTTP 부하 분산기는 여러 리전에 걸쳐 트래픽의 균형을 조정할 수 있지만 이 솔루션 예에서는 여러 영역을 포함하는 단일 리전에서 트래픽을 사용합니다.

영역

영역은 리전 내에 있는 독립된 위치입니다. 영역은 같은 리전의 다른 영역과 높은 대역폭, 낮은 지연 시간의 네트워크 연결을 유지합니다. 한 리전의 여러 영역에 걸쳐 애플리케이션을 배포하는 것이 좋습니다.

인스턴스

인스턴스는 Google의 인프라에서 호스팅되는 가상 머신입니다. 이러한 인스턴스는 물리적 서버처럼 설치하고 구성할 수 있습니다. 배포 예에서는 시작 스크립트와 Chef를 사용하여 애플리케이션 서버의 인스턴스와 애플리케이션의 코드를 구성합니다.

인스턴스 템플릿

인스턴스 템플릿은 머신 유형, 이미지, 영역, 라벨, 기타 인스턴스 속성을 정의합니다. 이러한 인스턴스 템플릿을 사용해 관리형 인스턴스 그룹을 만들 수 있습니다.

관리형 인스턴스 그룹

관리형 인스턴스 그룹은 하나의 인스턴스 템플릿을 기반으로 하는 동종 인스턴스의 모음입니다. HTTP 부하 분산기는 관리형 인스턴스 그룹을 대상으로 그룹 내의 인스턴스 간에 작업을 분산시킬 수 있습니다. 관리형 인스턴스 그룹에는 해당 인스턴스 그룹 관리자 리소스가 있으며, 이 리소스는 그룹에서 인스턴스를 추가 및 제거하는 작업을 담당합니다.

자동 확장 처리

Compute Engine 자동 확장 처리는 트래픽, CPU 사용 또는 기타 신호에 대한 응답으로 그룹 관리자와 인터페이싱하여 관리형 인스턴스 그룹에서 Compute Engine 인스턴스를 추가 또는 제거합니다. 솔루션 예에서 자동 확장 처리는 HTTP 부하 분산기의 초당 요청 수(RPS) 측정항목에 응답합니다. 각 관리형 인스턴스 그룹이 자동으로 확장하게 만들려면 자동 확장 처리가 필요합니다.

Cloud SQL

Cloud SQL은 MySQL과 PostgreSQL을 모두 지원하는 완전 관리형 데이터베이스 서비스입니다. 복제, 암호화, 패치, 백업은 Google에서 관리합니다. Cloud SQL 인스턴스는 단일 영역에 배포되고 데이터는 자동으로 다른 영역에 복제됩니다. 이 예시에서 사용하는 Redmine 애플리케이션은 MySQL과 호환되며 Cloud SQL과 원활하게 작동합니다.

Cloud Storage

Cloud Storage를 사용하면 단순하고 확장 가능한 인터페이스에서 객체(일반적으로 파일)를 저장하고 검색할 수 있습니다. 이 솔루션에서 비공개 SSL 키를 확장 가능한 Compute Engine 인스턴스에 배포하는 데 Cloud Storage 버킷을 사용하며 또한 Redmine 애플리케이션에 업로드된 모든 파일을 저장하는 데에도 사용하므로 모든 인스턴스 디스크에 스테이트풀(stateful) 정보가 저장되지 않습니다.

탄력성

이번 아키텍처 예가 탄력성을 구현하려면 오류가 발생했거나 사용할 수 없게 된 인스턴스를 자동으로 대체할 수 있어야 합니다. 새 인스턴스가 온라인 상태가 되면 다음 작업을 수행해야 합니다.

  • 시스템 내 역할 이해
  • 자동으로 자체 구성
  • 모든 종속 항목 검색
  • 자동으로 요청 처리 시작

오류가 발생한 인스턴스를 자동으로 대체하기 위해 다음과 같이 다양한 Compute Engine 구성요소를 함께 사용할 수 있습니다.

시작 스크립트는 인스턴스가 부팅되거나 다시 시작될 때 실행됩니다. 이러한 스크립트를 사용해 소프트웨어 및 업데이트를 설치하거나, 서비스가 가상 머신에서 실행되도록 하거나, Chef, Puppet, Ansible, Salt 같은 구성 관리 도구를 설치할 수도 있습니다.

이 시나리오에서는 시작 스크립트를 사용하여 Chef Solo를 설치하여 인스턴스를 애플리케이션과 함께 작동하도록 구성합니다. 시작 스크립트와 Chef Solo를 사용하여 인스턴스를 자동으로 구성하는 방법을 알아보려면 이 항목 끝의 부록: 새 인스턴스 추가를 참조하세요.

시작 스크립트 이외에도 Compute Engine 인스턴스를 실행하기 전에 몇 가지 항목을 추가로 정의해야 합니다. 예를 들어 시스템 유형, 사용할 운영체제 이미지, 연결할 디스크를 지정해야 합니다. 이러한 옵션은 인스턴스 템플릿을 사용하여 정의합니다.

인스턴스 템플릿과 시작 스크립트를 함께 사용하여 Compute Engine 인스턴스를 실행하는 방법과 애플리케이션 아키텍처에서 특정 역할을 수행하기 위해 해당 인스턴스에서 소프트웨어를 구성하는 방법을 정의합니다.

시작 스크립트, 인스턴스 템플릿, 인스턴스가 함께 작동하는 방식을 나타내는 다이어그램

물론 인스턴스 템플릿은 템플릿일 뿐입니다. 이 템플릿이 작동하려면 새로운 Cloud Engine 인스턴스가 온라인 상태가 될 때 해당 템플릿을 적용하는 방법이 필요합니다. 이를 달성하기 위해 관리형 인스턴스 그룹을 만듭니다. 특정 시간에 실행할 인스턴스의 수와 해당 인스턴스에 적용할 인스턴스 템플릿을 결정합니다. 그러면 인스턴스 그룹 관리자는 필요에 따라 해당 인스턴스의 실행과 구성을 담당합니다.

다음 다이어그램은 이러한 구성요소가 함께 작동하는 방법을 보여줍니다.

  • 시작 스크립트
  • 인스턴스 템플릿
  • 인스턴스 그룹 관리자
  • 관리형 인스턴스 그룹
시작 스크립트, 인스턴스 템플릿, 인스턴스 그룹 관리자, 관리형 인스턴스 그룹이 함께 작동하는 방식을 나타내는 다이어그램

관리형 인스턴스 그룹과 해당 인스턴스 그룹 관리자는 영역별 또는 리전별 리소스일 수 있습니다. 인스턴스 템플릿은 모든 리전의 모든 영역에 위치한 여러 개의 관리형 인스턴스 그룹에 걸쳐 다시 사용할 수 있는 프로젝트 수준 리소스입니다. 하지만 인스턴스 템플릿에 영역별 리소스를 지정할 수 있으며, 이 경우 영역별 리소스가 위치한 영역으로 템플릿 사용이 제한됩니다.

이제 시작 스크립트, 인스턴스 템플릿, 관리형 인스턴스 그룹을 사용하여 비정상 인스턴스를 새로운 인스턴스로 교체하는 시스템을 구축했습니다. 다음 섹션에서는 비정상 인스턴스가 무엇인지 정의하고 이를 감지하는 한 방법을 알아봅니다.

상태 확인

이 시점에서 애플리케이션 예는 탄력성에 필요한 도구를 거의 모두 갖추었습니다. 하지만 한 가지 요소가 부족한데, 바로 비정상 인스턴스를 식별하여 이를 교체하는 방법이 필요합니다.

이 애플리케이션은 사용자가 HTTP 부하 분산기를 사용하여 적절한 정상 인스턴스에 연결할 수 있도록 설계되었습니다. 이 아키텍처를 사용하면 다음과 같은 두 가지 서비스를 통해 요청을 처리할 수 있는 인스턴스를 파악할 수 있습니다.

  • 상태 확인. HTTP 상태 확인은 각 인스턴스에 대해 상태 확인을 실행할 포트와 경로를 지정합니다. 정상 인스턴스에서 상태 확인은 200 OK 응답을 요구합니다.
  • 백엔드 서비스. 백엔드 서비스는 부하 분산기에서 트래픽을 수신하는 인스턴스 그룹을 1개 이상 정의합니다. 또한 인스턴스에서 노출되는 포트와 프로토콜(예: HTTP 포트 80)을 비롯해 인스턴스 그룹에 속한 인스턴스에 사용할 HTTP 상태 확인을 지정합니다.

다음 다이어그램은 애플리케이션 아키텍처를 나타내며 백엔드 서비스 및 HTTP 상태 확인이 부하 분산기 및 인스턴스 그룹과 어떻게 연관되는지 보여줍니다.

상태 확인 및 백엔드 서비스를 나타내는 다이어그램

Cloud SQL을 통한 데이터 탄력성

모든 애플리케이션 아키텍처의 세 가지 주요 영역은 네트워킹, 컴퓨팅, 스토리지입니다. 여기서 설명한 애플리케이션 아키텍처에서는 네트워킹 및 컴퓨팅 구성요소를 살펴봤지만 전체 요소를 다루려면 스토리지 구성요소 또한 살펴봐야 합니다.

이 솔루션 예에서는 1세대 Cloud SQL 인스턴스를 사용하여 완전 관리형 MySQL 데이터베이스를 제공합니다. Cloud SQL을 사용하여 Google은 복제, 암호화, 패치 관리, 백업을 자동으로 관리합니다.

Cloud SQL 데이터베이스는 리전 전체에 존재하므로 데이터는 리전 내의 영역 간에 걸쳐 복제됩니다. 이 작업은 데이터가 업데이트될 때마다 이를 백업하는 것과 동일합니다. 드물지만 하나의 영역 전체에서 오류가 발생하는 경우 데이터는 보존됩니다.

Cloud SQL을 사용하면 다음 두 가지 복제 유형 중 하나를 선택할 수 있습니다.

  • 동기 복제. 동기 복제를 사용하면 업데이트가 클라이언트에 반환되기 전에 여러 영역에 걸쳐 복제됩니다. 이 방법은 심각한 문제가 발생했을 경우 안정성과 가용성을 보장하는 데 유용하지만 쓰기 속도가 느려집니다.
  • 비동기 복제. 비동기 복제는 쓰기가 로컬에 캐싱되면 데이터를 다른 위치에 복사하기 전에 쓰기를 확인하여 쓰기 처리량을 높입니다. 비동기 복제를 사용하면 복제가 완료될 때까지 기다릴 필요가 없으므로 더 빠르게 데이터베이스에 쓸 수 있습니다. 하지만 드문 경우 데이터베이스를 업데이트한 후 몇 초 만에 데이터 센터 시스템에 장애가 발생하면 최신 업데이트가 손실될 수 있습니다.

이 솔루션에서 사용하는 Redmine 애플리케이션은 작업 부하가 쓰기 집약적이지 않기 때문에 동기 복제를 사용합니다. 애플리케이션별로 쓰기 성능과 데이터 내구성 요구 사항에 따라 동기 복제와 비동기 복제 중 하나를 선택합니다.

확장성

이전 섹션에서는 애플리케이션 예에서 GCP를 사용하여 탄력적인 애플리케이션을 만드는 방법을 살펴보았습니다. 하지만 탄력성뿐만이 아니라 확장성을 실현하는 것 또한 중요합니다. 애플리케이션은 1명이 사용하든 1,000,000명이 사용하든 원활하게 작동해야 하며 사용자 수에 맞춰 리소스를 증가 또는 축소하여 비용 효율성을 유지해야 합니다.

애플리케이션의 리소스를 증가 또는 축소하려면 다음과 같은 요소가 필요합니다.

  • 서비스에서 인스턴스를 추가하거나 제거할 수 있는 수단. 또한 인스턴스를 추가 및 제거하는 시점을 결정하는 방법이 필요합니다. GCP의 자동 확장 처리가 이 문제를 해결합니다.
  • 스테이트풀(stateful) 데이터를 저장하는 수단. 인스턴스는 이동될 수 있으므로 이러한 인스턴스에 스테이트풀(stateful) 데이터를 저장하는 것은 바람직하지 않습니다. 애플리케이션 아키텍처는 관계형 데이터를 별도의 Cloud SQL 인스턴스에 저장하여 이 문제를 해결하지만 사용자가 업로드한 파일도 고려해야 합니다. Cloud Storage가 이 요구 사항을 충족합니다.

다음 섹션부터는 자동 확장 처리를 사용해 Redmine 애플리케이션을 실행하는 인프라의 확장 방법과 업로드된 파일에 Cloud Storage를 이용하는 방법에 대해서 알아보겠습니다.

자동 확장 처리를 통한 확장

애플리케이션 증가 또는 감소에 따라 필요한 리소스를 동적으로 조정해야 합니다. Compute Engine 자동 확장 처리를 사용하여 이 문제를 해결할 수 있습니다.

자동 확장 처리는 트래픽 또는 부하가 증가하면 리소스를 추가하여 추가 작업을 처리하고 트래픽 또는 부하가 감소하면 리소스를 제거하여 비용을 절감합니다. 자동 확장 처리는 사용자가 정의한 확장 규칙에 따라 이후에 사용자가 별도로 개입할 필요 없이 이 작업을 자동으로 수행합니다.

자동 확장 처리로 다음과 같은 두 가지 이점을 얻을 수 있습니다.

  1. 수요를 항상 충족할 수 있는 충분한 리소스가 있으므로 애플리케이션 사용자가 뛰어난 사용 환경을 경험할 수 있습니다.
  2. 수요가 지정된 임계값 이하로 감소할 경우 자동 확장 처리가 인스턴스를 제거하므로 비용을 보다 잘 제어할 수 있습니다.

자동 확장 처리는 CPU 사용량, 제공되는 용량 또는 Stackdriver Monitoring 측정항목에 따라 가상 머신의 수를 확장할 수 있습니다. 이 솔루션은 제공 용량 측정기준을 사용하여 인스턴스가 부하 분산기에서 수신하는 초당 요청 수(RPS)를 기반으로 Compute Engine 인스턴스를 추가하거나 제거합니다. 자세한 내용은 Compute Engine 자동 확장 처리를 통한 일괄 처리를 참조하세요.

초당 요청 수(RPS)

이전 섹션에서는 부하 분산기에서 트래픽을 수신할 인스턴스 그룹을 식별하는 단일 백엔드 서비스를 살펴보았습니다. 이 솔루션 예는 백엔드 서비스와 연결된 각 인스턴스 그룹에 balancingMode=RATE 또한 설정합니다. 이 속성은 부하 분산기가 maxRatePerInstance 속성에서 정의한 RPS(이 예에서는 100으로 설정함)를 기반으로 부하를 분산하도록 지시합니다. 이 구성은 부하 분산기가 각 인스턴스를 100RPS 이하로 유지함을 나타냅니다. 백엔드 서비스의 구성 속성에 대한 자세한 내용은
백엔드 서비스 문서를 참조하세요.

RPS에 맞춰 확장하려면 자동으로 확장할 각 인스턴스 그룹에 자동 확장 처리를 만들어야 합니다. 이번 예에서 인스턴스 그룹은 영역별 리소스이므로 각 영역에 자동 확장 처리를 만들어야 합니다.

자동 확장 처리가 애플리케이션 아키텍처에 어떻게 적용되는지 나타내는 다이어그램

각 자동 확장 처리에는 자동 확장 처리가 유지해야 하는 부하 분산기의 최대 제공 용량의 비율을 정의하는 utilizationTarget 속성이 포함되어 있습니다. 이 예시에서는 각 자동 확장 처리의 utilizationTarget을 각 인스턴스에 대해 백엔드 서비스의 최대치인 100RPS의 80%로 설정합니다. 그러면 RPS가 인스턴스별 최대치의 80%인 80RPS를 초과할 경우 자동 확장 처리가 이를 확장합니다. RPS가 이 임계값 아래로 떨어지면 자동 확장 처리가 이를 축소합니다.

자동 확장 처리가 인스턴스의 추가 또는 제거를 결정하는 원리를 나타내는 흐름 차트

각 자동 확장 처리 역시 자동 확장 처리가 위반할 수 없는 최소/최대 인스턴스 수를 정의합니다.

관리형 인스턴스 그룹만 자동 확장 기능을 제공합니다. 자세한 내용은 인스턴스 그룹인스턴스 그룹 자동 확장을 참조하세요.

파일 업로드 처리

Redmine 애플리케이션에는 사용자가 로그인하여 파일을 업로드하고 저장할 수 있는 기능이 포함되어 있습니다. Redmine과 여러 유사한 애플리케이션의 기본 동작은 이러한 파일을 로컬 디스크에 직접 저장하는 것입니다. 이 접근법은 잘 정의된 메커니즘이 있는 서버를 하나만 보유한 경우에는 괜찮은 방법입니다. 하지만 부하 분산기 이후에 자동 확장되는 Compute Engine 인스턴스가 다수 있다면 최적의 방법은 아닙니다. 사용자가 파일을 업로드했을 때 다음 요청이 파일이 저장된 시스템에 전달됨을 보장할 수 없으며 자동 확장 처리가 필요하지 않지만 파일이 들어 있는 인스턴스를 종료할 수도 있습니다.

더 나은 해결책은 자동으로 확장된 일련의 웹 서버에서 파일 업로드에 액세스하고 이를 저장하는 완벽한 중앙 집중식 위치를 제공하는 Cloud Storage를 사용하는 것입니다. 또한 Cloud Storage는 Amazon S3 클라이언트와 상호 운용 가능한 API를 제공하므로 별도의 수정 없이 Redmine S3 플러그인을 비롯한 기존의 S3용 애플리케이션 플러그인과 호환됩니다. 많은 타사 오픈소스 애플리케이션에는 Cloud Storage와 같은 객체 스토리지를 지원하는 플러그인이 포함되어 있습니다. 자체 애플리케이션을 빌드할 경우 Cloud Storage API를 직접 사용하여 파일 저장을 지원할 수 있습니다.

다음 다이어그램은 Redmine과 Cloud Storage를 사용해 파일을 업로드하고(파란색 화살표) 가져오는(녹색 화살표) 흐름을 나타낸 것입니다.

Redmine 애플리케이션을 통한 요청의 흐름을 나타내는 다이어그램

이 다이어그램에서 나타내는 프로세스는 다음과 같습니다.

  1. 사용자가 웹브라우저에서 파일을 게시합니다.
  2. 부하 분산기가 요청을 처리할 인스턴스를 선택합니다.
  3. 인스턴스가 파일을 Cloud Storage에 저장합니다.
  4. 인스턴스가 파일 메타데이터(예: 이름, 소유자, Cloud Storage 내 위치)를 Cloud SQL 데이터베이스에 저장합니다.
  5. 사용자가 파일을 요청하면 파일이 Cloud Storage에서 인스턴스로 스트리밍됩니다.
  6. 인스턴스가 부하 분산기를 통해 스트림을 전송합니다.
  7. 파일이 사용자에게 전송됩니다.

저장용량

Compute Engine 인스턴스에서 스테이트풀(stateful) 파일 업로드를 제거하고 동적 확장을 허용하는 것 이외에도 Cloud Storage는 내구성이 우수한 중복 스토리지를 제공하여 파일을 거의 무제한으로 업로드할 수 있습니다. 이 스토리지 솔루션을 사용하면 용량 계획에 대해 걱정하지 않고 사용하는 저장용량에 대해서만 비용을 지불할 수 있고 데이터가 여러 영역에 자동으로 중복 저장되므로 이 솔루션은 탄력성과 확장성이 우수하고 비용 효율적입니다.

비용

지금까지 이 문서에서 설명한 애플리케이션 아키텍처에서는 GCP를 사용하여 탄력적이고 확장 가능한 애플리케이션을 빌드하는 방법을 살펴보았습니다. 하지만 이것 외에도 애플리케이션을 빌드하는 데 고려할 사항이 있습니다. 가능한 한 비용 효율적인 방식으로 앱을 빌드해야 한다는 점입니다.

이 섹션에서는 이 문서에서 설명한 애플리케이션 아키텍처가 탄력성과 확장성이 우수할 뿐만 아니라 비용 효율성이 높은 이유를 살펴보겠습니다. 우선 애플리케이션의 사용량과 그 빈도에 대해 몇 가지 일반적인 사항을 가정한 다음 이를 기본 비용 예상치로 변환하겠습니다. 이러한 가정은 어디까지나 가정일 뿐이므로 필요에 따라 이 수치를 조정하여 자신의 애플리케이션의 예상 사용량에 더욱 가까운 예상 비용을 산정하세요.

컴퓨팅

애플리케이션 아키텍처의 주요 우려 사항은 서버를 계속 실행하기 위해 필요한 비용입니다. 이 비용 분석은 다음과 같은 사항을 가정합니다.

측정항목
월 평균 페이지 조회 수 20,000,000
월 평균 HTTP 요청 수 120,000,000
사용량이 많은(90% 이상) 시간 월요일에서 금요일, 오전 7:00부터 오후 6:00
페이지 조회당 데이터 전송량 100KB
월별 사용량이 많은 시간 220
사용량이 많은 시간 중 요청 비율 초당 요청 수(RPS) 127건
사용량이 많지 않은 시간 중 요청 비율 초당 요청 수(RPS) 6건

이러한 가정을 기반으로 사용량이 많은 시간(매월 월요일에서 금요일, 오전 7:00에서 오후 6:00까지) 중 애플리케이션이 수신하는 페이지 조회 수를 파악할 수 있습니다.

120,000,000(월별 요청 수) * 90%(사용량이 많은 시간 중 발생) = 108,000,000(r월별 사용량이 많은 시간의 요청 수)

평균적으로 한 달에는 근무일이 22일 정도입니다. 각 근무일 중 사용량이 많은 시간이 11시간일 경우, 매월 사용량이 많은 242시간을 처리하는 데 충분한 컴퓨팅 리소스를 제공해야 합니다.

그 다음으로는 이러한 유형의 트래픽을 처리할 수 있는 Compute Engine 인스턴스의 유형을 파악해야 합니다. 이 애플리케이션 아키텍처는 기본 부하 테스트에 gatling.io를 사용하여 테스트를 거쳤습니다. 이 테스트의 결과에 따라 n1-standard-1 유형의 Compute Engine 인스턴스 4개로 충분할 것으로 판단되었습니다.

사용량이 많은 시간이 아닌 경우 이 솔루션에서는 최소 두 개의 n1-standard-1 인스턴스가 실행됩니다.

이러한 인스턴스를 실행하는 데 드는 비용을 확인하려면 GCP 가격 계산기에서 최신 예상 비용을 확인하세요. 여기에서 해당 인스턴스가 두 경우 모두에서 자동으로 지속 사용 할인의 대상임을 알 수 있습니다.

부하 분산 및 데이터 전송

이 애플리케이션은 사용자가 연결하는 공개 IP 주소인 단일 전달 규칙을 사용하여 부하 분산기를 프로비저닝했습니다. 이 전달 규칙은 시간 단위로 비용이 청구됩니다.

데이터 전송 예상치의 경우 최악의 시나리오를 먼저 고려하세요. 부하 분산기 비용은 부하 분산기에서 처리한 수신 데이터에 대해 청구되고, 부하 분산기에서 송신한 트래픽은 일반 송신 요금이 적용됩니다. HTTP 요청 120,000,000건의 99.5%가 Redmine 프로젝트 페이지를 로딩하는 사용자라고 가정하겠습니다. 페이지 로드는 HTTP GET 요청 1건으로 계산되고, 다른 애셋(CSS, 이미지, jQuery)을 로드하는 데 HTTP GET 요청이 추가로 5건 발생합니다. 따라서 전체 페이지를 로드하면 HTTP 요청 6건이 발생합니다. 이를 계산한 결과는 다음과 같습니다.

  • 월별 전체 페이지 로드 약 20,000,000건
  • 페이지당 처리되는 인그레스 데이터 약 10KB 및 데이터 전송 약 450KB
  • 월별 부하 분산기가 처리하는 총 데이터 약 214GB 및 이그레스 트래픽 약 9,091GB

HTTP 요청 20,000,000건 중 나머지 0.5%는 평균 크기(약 0.5MB)의 파일을 업로드하는 HTTP POST 요청으로 매월 추가로 데이터를 처리하는 데 500GB가 사용됩니다.

이 GCP 가격 계산기 예상치는 이 시나리오에서 부하 분산기가 처리하는 714GB 데이터 전송과 이그레스 데이터 9,091GB 이상에 대한 예측 비용을 보여줍니다.

이러한 데이터 전송 예상치는 캐싱이나 콘텐츠 전송 네트워크(CDN)의 이점 없이 Compute Engine 인스턴스와 부하 분산기를 통한 각 요청에서 정적 애셋을 포함한 모든 콘텐츠를 제공하므로 최악의 시나리오를 가정합니다. 매월 2,000만 건의 페이지 로드를 기반으로 하는 이 솔루션에서 각 페이지 로드별 페이로드 약 450KB 중 333KB가 jQuery 로드에 필요합니다. Google 호스팅 라이브러리에서 jQuery를 로드하도록 애플리케이션의 한 줄을 업데이트하면 데이터 전송 비용을 73% 줄일 수 있습니다.

이 업데이트된 가격 예상치는 Google 호스팅 라이브러리로 전환하여 절감한 데이터 전송량을 보여줍니다.

스토리지

이 솔루션은 Redmine 애플리케이션을 통해 업로드된 모든 파일에 Cloud Storage를 사용합니다. 이전 섹션에서 설명한 것처럼 이 사용량의 약 0.5%는 파일 업로드에 사용되며, 각 파일의 크기는 평균 약 0.5MB입니다. 즉 매월 1,000,000건의 새로운 파일 업로드가 발생할 것으로 예상되므로 매월 500GB의 새로운 스토리지가 필요합니다. 또한 이 솔루션은 새 파일을 저장하기 위해 매월 1,000,000건의 HTTP PUT 작업을 가정하며, 이러한 작업은 A 클래스 작업으로 비용이 청구됩니다.

GCP 가격 계산기의 이 비용 예상치는 Cloud Storage를 사용할 경우의 예상 비용을 나타냅니다.

이 아키텍처는 Cloud SQL을 사용하여 애플리케이션의 모든 관계형 데이터를 저장합니다. 앞에서 설명한 측정항목 예를 기반으로 1,024MB RAM을 포함한 D2 데이터베이스 유형은 애플리케이션 작업 부하에 충분한 용량을 제공하며 24시간 연중무휴로 실행되어야 합니다. 이 데이터베이스는 사용량이 높을 것으로 예상되므로 계산기에서 I/O 작업에 높음 옵션을 선택하세요. 이 아키텍처 예는 100,000개의 문서를 삽입하여 테스트하였으며, 그 결과 50GB 디스크가 100,000,000개 이상의 문서를 지원하여 데이터베이스가 지정된 속도를 8년 이상 지원할 수 있는 것으로 판단되었습니다.

이 GCP 가격 계산기의 예상치는 아키텍처 비용 중 해당 부분의 예상 비용을 보여줍니다.

솔루션 예 배포

이번 솔루션에서 설명한 애플리케이션 예를 배포하려면 GitHub 저장소인 확장 가능하고 탄력적인 GCP 기반 애플리케이션을 방문하세요.

부록: 새 인스턴스 추가

탄력적이고 확장 가능한 애플리케이션 아키텍처를 구축하는 과정의 일환으로 새 인스턴스를 추가하는 방법을 결정해야 합니다. 구체적으로, 새 인스턴스가 온라인 상태가 되면 이를 자동으로 구성하는 방법을 결정해야 합니다.

이 섹션에서는 사용 가능한 옵션을 몇 가지 살펴보겠습니다.

소프트웨어 설치 부트스트랩화

사용자의 웹 요청을 처리하기 위해 각 인스턴스에는 기본 운영체제 위에 추가 소프트웨어와 구성 데이터를 설치해야 합니다. 구성 데이터에는 데이터베이스 연결 정보를 비롯해 파일이 저장되는 Cloud Storage 버킷의 이름이 포함됩니다. 이러한 구성요소를 레이어로 나타내면 각 인스턴스에서 실행할 전체 스택을 시각화할 수 있습니다.

인스턴스 내의 소프트웨어 스택을 나타내는 다이어그램

이 솔루션은 실행 시 인스턴스가 사용하는 Compute Engine 이미지를 지정하는 인스턴스 템플릿을 사용합니다. 특히 이 솔루션은 Canonical에서 개발 및 지원하는 Ubuntu 14.10 이미지를 사용합니다. 이 이미지는 기본 운영체제 이미지이므로 애플리케이션에 필요한 소프트웨어 또는 구성이 포함되지 않습니다.

새로운 인스턴스를 실행할 때 스택의 나머지 부분을 자동으로 설치하려면 Compute Engine 시작 스크립트Chef Solo를 함께 사용하여 실행 시점에 부트스트랩화할 수 있습니다. startup-script 메타데이터 속성 항목을 인스턴스 템플릿에 추가하여 시작 스크립트를 명시할 수 있습니다. 시작 스크립트는 인스턴스가 부팅될 때 실행됩니다.

이 시작 스크립트는 다음과 같은 작업을 수행합니다.

  1. Chef 클라이언트를 설치합니다.
  2. node.json이라는 이름의 특수한 Chef 파일을 다운로드합니다. 이 파일은 Chef가 인스턴스에 대해 실행할 구성을 지정합니다.
  3. Chef를 실행하여 세부 구성을 처리하도록 합니다.

전체 시작 스크립트는 다음과 같습니다.

#! /bin/bash

# Install Chef
curl -L https://www.opscode.com/chef/install.sh | bash

# Download node.json (runlist)
curl -L https://github.com/googlecloudplatform/... > /tmp/node.json

# Run Chef
chef-solo -j /tmp/node.json -r https://github.com/googlecloudplatform/...

애플리케이션 구성 제공

새 인스턴스가 부팅되고 시작 스크립트와 Chef를 통해 구성되었으면 요청을 처리하기 전에 확인해야 할 정보가 있습니다. 이번 예에서는 각 인스턴스마다 호스트 이름, 사용자 이름, 비밀번호 같은 데이터베이스 연결 정보를 비롯해 사용할 Cloud Storage 버킷 이름과 연결을 위한 사용자 인증 정보를 알아야 합니다.

각 Compute Engine 인스턴스에는 이와 관련하여 사용자가 정의할 수 있는 메타데이터 속성이 포함되어 있습니다. 앞서 특수한 startup-script 메타데이터 속성 추가에 대해 알아보았지만 임의의 키-값 쌍을 추가할 수도 있습니다. 여기서 인스턴스가 데이터베이스와 Cloud Storage 버킷에 연결하는 데 필요한 구성 데이터를 포함하도록 인스턴스 템플릿의 속성을 지정할 수 있습니다.

인스턴스 템플릿의 메타데이터는 GCP Console에서 다음과 같이 표시됩니다.

인스턴스 템플릿의 커스텀 메타데이터 정보를 표시하는 Google Cloud Platform Console 스크린샷

Chef는 Ohai라는 도구를 사용하여 인스턴스의 메타데이터에서 이러한 구성 정보를 파싱하고 템플릿을 채워 애플리케이션에 필요한 구성 파일을 만듭니다. 데이터베이스 연결 정보를 포함하는 database.yaml 파일을 만들어 적절한 메타데이터 항목에 자동으로 액세스하는 템플릿은 다음과 같습니다.

production:
    adapter: mysql2
    database: <%= node['gce']['instance']['attributes']['dbname'] %>
    host:     <%= node['gce']['instance']['attributes']['dbhost'] %>
    username: <%= node['gce']['instance']['attributes']['dbuser'] %>
    password: <%= node['gce']['instance']['attributes']['dbpassword'] %>

또한 로컬 메타데이터 서비스를 사용하여 인스턴스 내에서 메타데이터 값에 액세스할 수도 있습니다. 여기서 curl을 사용하여 데이터베이스 비밀번호를 검색할 수 있습니다.

curl "http:/metadata.google.internal/computeMetadata/v1/instance/attributes/dbpassword" -H "Metadata-Flavor: Google"

성능 및 종속 항목 고려 사항

이 솔루션에서 사용하는 부트스트랩화 방법은 기본 운영체제 이미지로 시작하고, Chef를 사용하여 실행 시 모든 소프트웨어를 설치하고, 인스턴스 메타데이터를 사용하여 애플리케이션 구성 데이터를 제공합니다.

인스턴스 내의 소프트웨어 스택을 나타내는 다이어그램이 스택은 이미지로 번들된 소프트웨어, 실행 시 설치되는 소프트웨어, 실행 후 제공되는 소프트웨어를 보여줍니다.

이 접근법의 장점은 시스템 구성이 Chef 설명서에 명시되어 있다는 것입니다. 이 설명서를 버전 제어, 공유, 사용하면 Vagrant 또는 Docker를 사용하여 가상 머신을 로컬로 프로비저닝하여 테스트하거나, 자신의 데이터 센터에 또는 다른 클라우드 공급업체의 서버를 구성할 수 있습니다. 이미지 관리 또한 간단합니다. 이 예제 애플리케이션의 경우 애플리케이션이 사용하는 기본 OS 이미지 하나만 추적하면 됩니다.

단점 중 하나로 모든 소프트웨어를 다운로드 및 설치하고 일부 경우에는 컴파일이 필요하므로 실행에 시간이 오래 걸린다는 점을 고려해야 합니다. 또한 이 방법을 사용하여 추가되는 종속 항목 또한 고려해야 합니다. 이 예제에서 Chef는 apt, Rubygems, GitHub에서 몇 가지 패키지를 설치했습니다. 새 인스턴스를 실행할 때 이 저장소 중 하나라도 사용할 수 없을 경우 구성에서 오류가 발생합니다.

커스텀 이미지 및 부트스트랩화

Compute Engine을 사용하여 커스텀 이미지를 만들 수 있으므로 실행 시 모든 프로그램을 설치하는 것이 유일한 부트스트랩 방법은 아닙니다. 예를 들면 다음과 같은 작업을 수행할 수 있습니다.

  1. 기본 Ubuntu 14.10 이미지를 실행합니다.
  2. Redmine 애플리케이션을 제외한 모든 프로그램(Ruby, nginx 등)을 설치합니다.
  3. 결과에서 이미지를 만듭니다.
  4. 인스턴스 템플릿에서 이 이미지를 사용합니다.

이렇게 하면 새 인스턴스를 실행할 때 Redmine만 설치하면 됩니다. 부팅 시간이 단축되고, 외부 패키지 종속 항목을 크게 줄일 수 있습니다.

이미지에 Redmine을 제외한 모든 프로그램이 설치된 인스턴스 스택을 나타내는 다이어그램

커스텀 이미지 접근법을 더욱 발전시켜 모든 종속 항목, 애플리케이션 소스, 구성을 비롯한 모든 요소를 이미지화할 수 있습니다. 이렇게 하면 부팅 시간이 최고 수준으로 단축되고 외부 종속 항목이 제거되지만 애플리케이션 내에 단 하나라도 변경 사항이 발생할 경우 새 이미지를 만들고 인스턴스 템플릿을 업데이트해야 합니다.

모든 소프트웨어가 이미지로 번들된 인스턴스 스택을 나타내는 다이어그램

인스턴스를 부트스트랩화하는 접근법을 점진적인 방식으로 간주합니다. 실행 시 많은 요소를 구성하도록 하면 부팅에 시간이 오래 걸리지만 관리할 이미지 수가 적어집니다. 많은 구성을 커스텀 이미지로 만들면 부팅 시간이 짧아지고 종속 항목이 줄어들지만 더 많은 이미지를 관리해야 할 수 있습니다. 대부분의 고객의 경우 이 중간에서 적절한 결정을 내리는 것이 좋습니다. 사용자와 애플리케이션에 따라 적절한 방식을 선택하세요.

인스턴스에 소프트웨어를 설치하는 방식을 점진적으로 나타내는 다이어그램실행 후 모든 소프트웨어를 설치하는 방식에서 모든 소프트웨어가 이미지에 번들된 방식까지 보여줍니다.

다음 단계

  • GCP에서 웹을 호스팅하는 옵션에 대한 일반적인 개요는 웹사이트 제공을 참조하세요.
  • 다른 Google Cloud Platform 기능을 직접 사용해 보려면 가이드를 살펴보세요.
이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...