[번역] Azure 앱서비스 구조를 깊게 살펴봅시다

원문: https://msdn.microsoft.com/en-us/magazine/mt793270

Azure 앱서비스는 웹, 모바일, API 어플리케이션을 개발할 때 최고의 PaaS가 되도록 만들었습니다. 간단한 마케팅 페이지부터 확장성이 중요한 상거래 솔루션까지 모두 소화할 수 있습니다.

앱서비스는 완전한 관리형(managed) 플랫폼으로, 쉽게 말해서 인프라를 직접 운영할 필요가 없다는 뜻입니다. OS 패치와 프레임워크는 모두 플랫폼에서 알아서 해주기 때문에 서버를 직접 관리하지 않아도 됩니다. 여러분이 만든 어플리케이션은 가상화된 서버에서 동작하며 확장할 최대한의 서버 인스턴스 수만 정해주면 됩니다. 어플리케이션이 더 많은 리소스를 필요로 할 때 플랫폼이 자동으로 인스턴스를 확장하고, 동시에 확장한 인스턴스의 부하 분산 처리까지 합니다.

저희 앱서비스 팀은 여러분이 굳이 자세한 내용을 몰라도 편하게 쓸 수 있도록 만들었지만 동작 원리를 깊게 알면 더 좋을 것입니다. 이 글에서는 앱서비스의 내부 구조를 다루고 몇 가지 시나리오에 알맞은 구성에 대해서도 살펴보겠습니다.

전세계적으로 지역 분산된 구조

클라우드 컴퓨팅은 빠르게 확장할 수 있어야 하고 한 없이 큰 용량을 가져야 합니다. 클라우드의 확장 개념을 우리가 모니터를 보는 모습에 빗대보겠습니다. 모니터에 그림 하나가 있다고 할 때, 이를 멀리서 보면 하나의 선명한 그림이지만 가까이서 보면 수많은 픽셀로 이루어져있습니다. 클라우드도 이처럼 많은 서버로 구성되면서 하나의 큰 그림을 만듭니다. 앱서비스 클러스터는 서버의 묶음을 ‘확장 단위(scale unit)’ 또는 ‘스탬프’라고 부르고, 이 확장 단위가 전세계 Azure 데이터센터에 빼곡히 있습니다.

앱서비스는 전세계에 Azure를 지원하는 지역이면 어디든 있습니다. Azure가 있는 모든 지역에는 사용자의 어플리케이션을 실행하는 앱서비스 확장 단위와 지역 제어 단위(regional control unit)가 있습니다. 평상시에는 제어 단위가 사용자에게 보이지 않으며 플랫폼의 일부로 취급됩니다. 제어단위에 문제가 있다면 그 때는 보이겠지만 평소엔 제어 단위가 있는지도 모를 것입니다. 제어 단위 중에는 모든 API 호출을 관리하는 게이트웨이 역할을 하는 제어 단위도 있습니다. 사용자가 새 어플리케이션 생성 명령을 포털에서 하든 명령줄인터페이스로 하든 Azure REST API로 하든 상관없이 Azure의 중앙제어부(management.azure.com)로 생성 요청을 보내는 역할입니다. Azure 리소스 관리자 (Azure Resource Manager; 이하 ARM. bit.ly/2i6UD07)는 사용자의 여러가지 리소스를 하나의 그룹으로 관리할 수 있도록 합니다. 여러분이 ARM에서 사용하는 API에 대해서 말하자면, ARM으로 리소스를 제어할 때 사용하는 API는 ARM이 직접 관리하는게 아닙니다. Azure는 서비스 별로 관리용 API가 있고, 여러분이 ARM에 요청하면 ARM이 각 서비스의 API를 호출하는 식입니다. 앱 서비스의 경우 ARM이 받는 모든 API 요청은 Geo-Master로 포워딩됩니다. Geo-Master는 전세계의 모든 확장 단위를 파악하고 있으므로 여러분이 새 웹사이트나 웹 작업 같은 앱 서비스 어플리케이션 생성을 요청하면 Geo-Master가 최적의 확장 단위를 찾은 후, 그 확장 단위에 생성 요청을 포워딩합니다. 확장 단위가 생성 요청을 받으면 생성할 앱을 실행할 공간과 인프라를 확보한 후(프로비저닝이라고 합니다) 앱 실행에 필요한 리소스를 할당합니다. 아래 그림 1은 Azure에서 새 앱을 만드는 과정입니다.

Global Distribution of App Service Scale Units
그림 1 전세계에 분산된 앱서비스 확장 단위

새 앱을 만드는 과정은 다음과 같습니다.

  1. 사용자가 새 웹사이트 생성을 요청합니다.
  2. ARM은 사용자가 리소스에 대한 접근과 작업 권한이 있는지 확인합니다. 이번 작업은 ‘생성하기’에 대한 권한입니다. 권한 확인이 끝나면 해당 요청을 앱서비스의 Geo-Master로 포워딩합니다.
  3. Geo-Master는 사용자 요청에 가장 알맞은 확장 단위를 찾아서 해당 요청을 포워딩합니다.
  4. 하나의 확장 단위에 새 어플리케이션을 만듭니다.
  5. Geo-Master는 요청에 대한 성공 여부를 리포트합니다.

앱서비스가 얼마나 많은 확장 단위로 이루어져있든 여러분의 어플리케이션은 대부분의 경우 단 하나의 앱서비스 확장 단위에서 동작합니다. Azure 트래픽관리자를 이용해서 여러 지역에서 앱이 실행되고 있다면 여러 개의 독립된 확장 단위에서 동작합니다. 확장 단위는 지역에 종속되어있기 때문입니다. 다시 말해, 여러분의 앱은 하나의 확장 단위에 종속되어있다고 볼 수 있습니다.

앱서비스 확장 단위란?

앱서비스 확장 단위(App Service scale unit)는 어플리케이션을 호스트하고 실행하는 여러 개의 서버를 하나로 묶은 단위입니다. 보통 한 단위는 1,000개 이상의 서버로 이루어져 있습니다. 서버를 클러스터로 구성하면 규모의 경제를 실현하고 인프라의 재사용성을 높일 수 있습니다. 앱서비스 확장 단위를 구성하는 시스템은 Azure 클라우드서비스(역자주: Cloud Service는 Azure 제품 이름 중 하나입니다) 배포로 이루어집니다. 지금은 Azure 클라우드서비스가 클래식 취급을 받지만 앱서비스를 처음 런칭했을 때는 2012년 6월이었습니다. 확장 단위를 만들고 제어하는 과정은 모두 자동화되어있습니다.

확장 단위의 부분 별 설명

확장 단위의 역할은 기본적으로 사용자의 어플리케이션을 호스팅하고 실행하는 것입니다. 어플리케이션은 윈도우 서버에서 구동되고 웹 작업자(Web Workers; 줄여서 ‘작업자’)는 구동 중인 어플리케이션을 참조합니다. 확장 단위 안에 있는 서버의 대부분은 작업자입니다. 확장 단위에는 작업자 외에 앱서비스의 여러 기능을 처리하는 지원 서버(support server)도 있습니다. 지원 서버마다 역할이 있고 각각의 지원서버는 여러 인스턴스에 배포되어 확장을 보다 수월하게 합니다.

프론트엔드

프론트엔드는 Layer-7 부하분산 장치입니다. 프록시와 같은 개념이며, 들어오는 HTTP 요청을 여러 어플리케이션과 어플리케이션에 속한 작업자에 분배합니다. 현재 앱서비스는 단순한 라운드로빈(Round-robin) 알고리즘으로 구현되어 있습니다. (역자주: 라운드로빈은 새가 한 번 날개짓 할 때마다 오르락내리락하는 것처럼 1-2-3-1-2-3…순서로 하나씩 돌아가며 수행하는 방식입니다)

웹 작업자 (Web Workers)

작업자는 앱서비스 확장 단위의 핵심입니다. 앱 그 자체를 구동합니다.

앱 서비스에서는 어플리케이션을 어떻게 실행할지 선택할 수 있습니다. 예를 들어, 공유(shared) 서버 또는 단독(dedicated) 서버를 ‘앱 서비스 계획(App Service Plan)’에서 선택할 수 있습니다. 앱 서비스 계획은 서버 할당, 기능, 용량 등을 정의한 묶음입니다. 공유 작업자는 여러 고객들의 어플리케이션을 호스팅하고 단독 작업자는 단 하나의 고객을 위한 어플리케이션을 호스팅합니다. 단독 작업자가 올라가 있는 단독 서버는 유형과 크기를 선택할 수 있습니다. 서버 사이즈가 클 수록 더 많은 CPU와 메모리 지원을 사용할 수 있습니다. 앱 서비스 계획은 어플리케이션을 실행하기 위해 서버를 미리 할당해놓습니다.

앱 서비스 확장 단위는 미리 프로비전한 작업자 풀을 가지고 있어서 그림 2의 첫번째 그림처럼 여러분의 어플리케이션을 받아들일 준비를 하고 있습니다. 여러분이 앱 서비스 계획에서 서버 두 대를 사용하겠다고 지정하면 그림 2의 두번째 그림처럼 앱 서비스는 두 대의 서버를 할당합니다. 그 다음, 그림 2의 세번째 그림처럼 앱서비스 계획에서 서버 두 대를 수평확장(scale-out)하겠다고 하면 작업자 풀에서 즉시 작업자를 할당합니다. 작업자를 미리 프로비전했고 워밍업까지 되어있으므로 여러분의 어플리케이션을 작업자가 배포받기만 하면 됩니다. 어플리케이션이 배포되면 작업자가 본격적으로 돌기 시작하고 프론트엔드는 트래픽을 받습니다. 이 모든 과정이 단 몇 초 내로 끝납니다.

Server Application Process in App Service Scale Unit
그림 2 앱 서비스 확장 단위에서 서버 어플리케이션을 실행하는 단계

그림 2의 네번째 그림은 여러 개의 앱서비스 계획을 각기 다른 색으로 표시하고 있습니다. 다른 색은 서로 다른 고객에 속한 앱 서비스 계획을 의미합니다.

파일 서버

앱은 콘텐츠를 보존할 저장소가 있어야 합니다. 콘텐츠는 HTML, .js, 이미지, 코드 등 어플리케이션을 구동하는데 필요한 파일을 말합니다. 파일 서버는 Azure 저장소 blob을 마운트하고 작업자에게 네트워크 드라이브로 보이도록 합니다. 작업자는 네트워크 드라이브를 로컬 드라이브로 맵핑합니다. 이렇게 하면 작업자가 취급하는 어플리케이션에서는 로컬드라이브를 다루는 것과 똑같아집니다. 어플리케이션에서 이루어지는 모든 파일 읽기/쓰기 작업은 파일서버를 거칩니다.

API 콘트롤러

API 콘트롤러는 Geo-Master가 앱 서비스를 조작할 때 사용됩니다. Geo-Master가 모든 확장 단위를 알고 있지만 여러분의 어플리케이션을 직접 관리하는 작업은 API 콘트롤러로 이루어집니다. 다시 말해, Geo-Master는 해당 확장 단위의 API 콘트롤러를 대행(delegate)할 뿐입니다. 예를 들어, Geo-Master에서 앱을 생성하라는 API 요청을 받으면 이 요청을 한 확장 단위의 API 콘트롤러로 넘기고 API 콘트롤러는 이 작업에 필요한 모든 단계를 처리합니다. Azure 포털에서 여러분이 만든 앱 서비스에 ‘재설정(Reset)’ 버튼을 누르면 API 콘트롤러가 그 앱에 할당된 모든 웹 작업자에 알림을 보내서 앱을 재시작합니다.

게시자 (Publishers)

Azure 앱 서비스는 FTP 접속을 지원하며 FTP로 앱 콘텐츠에 접근할 수 있습니다. 앞에서 언급했듯 앱 콘텐츠는 Azure 저장소 Blob에 저장되어있고 파일 서버는 이를 로컬 드라이브로 매핑하고 있는데, 게시자는 FTP 기능을 외부에 노출해서 앱 콘텐츠에 접근할 수 있게 합니다. 여러분은 게시자가 열어주는 FTP로 앱 콘텐츠 뿐만 아니라 로그도 접근할 수 있습니다.

앱 서비스에 앱을 배포하는데는 FTP 외에 여러가지 방법이 있습니다. 대표적으로 Visual Studio에서 Web Deploy를 이용하는 방법이 있고, 그 외에 Visual Studio 릴리즈 매니저의 지속 배포 기능이나 GitHub을 이용할 수도 있습니다.

SQL Azure

앱 서비스 확장 단위는 Azure SQL 데이터베이스에 메타데이터를 저장합니다. 어플리케이션 별로 저장 영역이 있고 그 안에는 어플리케이션에 대한 실행 정보도 저장되어 있습니다.

데이터 역할 (Data Role)

모든 역할에는 데이터베이스에 저장해야하는 데이터가 있기 마련입니다. 예를 들어, 웹 작업자는 앱을 런칭할 때 사이트 설정 정보가 있어야 하고 프론트엔드는 어떤 서버가 앱을 실행하고 있는지 알아야 HTTP 요청을 앱에 정확하게 전달할 수 있습니다. API 콘트롤러 또한 사용자가 명령한 정보를 읽고 데이터베이스에 저장하고 있습니다. 여기서 데이터 역할은, 확장 단위마다 있는 SQL 데이터베이스와 여러 역할 사이의 캐시(cache)입니다. 다른 여러 역할에서의 데이터 레이어(SQL 데이터베이스)를 추상화해서 확장성을 높이고 성능을 끌어올립니다. 뿐만 아니라, 소프트웨어 개발과 유지보수도 더 단순해집니다.

더 확실하게 알기 위한 사례

여기까지 여러분은 Azure 앱 서비스가 어떻게 구성되어있는지 알 수 있었습니다. 이제 이를 바탕으로 앱 서비스 팀이 정리한 팁을 살펴보겠습니다. 지금부터 나올 내용은 앱 서비스 엔지니어링 팀이 사용자들과 함께 일을 진행해보면서 얻은 지식입니다.

밀집도 조절하기

대부분의 사용자는 하나의 앱서비스 계획에 10개 이하로 적은 수의 어플리케이션을 실행합니다. 아주 많은 어플리케이션을 실행하는 사용자도 있는데, 이 때 서버의 연산 능력을 넘지 않도록 조심해야 합니다.

예를 들어가며 어플리케이션의 구성과 계산 리소스(CPU/메모리/트래픽의 조합)와의 관계에 대해 살펴봅시다. 웹앱 2개와 모바일 백엔드 앱 1개가 앱 서비스 계획에 있다고 가정합시다. 이 때 앱서비스 계획은 서버 2대로 설정했습니다.

기본적으로 앱 서비스 계획에 있는 어플리케이션은 그 앱 서비스 계획이 제공하는 서버 리소스 전체를 사용합니다. 앱 서비스 계획이 단 하나의 서버만 실행하고 있다면 그저 단일 서버에서 모든 어플리케이션이 구동하고 있다고 이해하면 됩니다.

앱 서비스 계획에 계산 리소스가 여러 개일 때는 더 깊은 이해가 필요합니다. 하나의 앱 서비스 계획에 10개의 계산 리소스가 있다면 어플리케이션은 10개의 계산 리소스마다 실행됩니다. 다시 말해, 하나의 앱 서비스 계획에서 50개의 앱을 서버 10개로 설정했다면 첫 서버에 50개, 두번째 서버도 50개 등등 10개의 서버 모두 50개씩 실행하고 있는 것입니다.

앱 서비스 계획에서 여러 어플리케이션을 실행하다가 그 중 한 어플리케이션에 HTTP 요청이 많아져서 더 많은 계산 리소스가 필요할 때가 있습니다. 이 때 어플리케이션을 실행할 서버 수만 늘리면 해결될거라 생각해서 서버를 한 대에서 여러 대로 확장하면 별 효과를 보지 못할 수 있습니다. 왜냐하면 서버 수는 늘어나지만 그 안에 다른 어플리케이션이 차지하는 CPU/메모리 또한 서버 수만큼 확장되기 때문입니다.

이보다는 각 어플리케이션의 사용량과 트래픽을 고려해서 적은 자원이 필요한 앱끼리 묶고 높은 사용량을 보이는 앱끼리 묶어서 앱 서비스 계획을 분리하는 편이 낫습니다. 50개의 어플리케이션이 실행 중이라면 아래와 같이 계산 리소스를 구분할 수 있습니다:

  • 낮은 사용량의 40개 어플리케이션은 단일 앱 서비스 계획에 둡니다.
  • 중간 정도 사용량을 보이는 5개는 두번째 앱 서비스 계획을 만들고 단일 서버로 설정합니다.
  • 사용량이 높은 5개 어플리케이션은 각각 별도의 앱 서비스 계획에 둡니다. 그리고 자동 크기 조정 기능을 설정하고 최저는 1대, 최고는 각 사용량에 맞춰서 설정합니다.

위와 같이 하면 50개의 어플리케이션을 7개의 계산 리소스에 담은 기본적인 설정이 됩니다. 이제 사용량이 높은 5개의 어플리케이션은 필요에 따라 독립적으로 확장할 수 있습니다.

앱 단위로 확장하기

많은 어플리케이션을 앱 서비스 계획에서 효율적으로 관리하는 방법으로는 밀집도 조절 외에 앱 단위로 확장하는 방법도 있습니다. 자세한 내용은 이 링크(bit.ly/2iQUm1S)에서 볼 수 있습니다. 앱 단위로 확장하면 어플리케이션이 실행되는 서버 수의 최대치를 조절할 수 있을 뿐만 아니라 어플리케이션 단위로도 조절할 수 있습니다. 어플리케이션 단위로 조절하면 사용 가능한 모든 서버에서 실행되는 것이 아닌, 직접 설정한 최대한의 서버 수 내에서 실행할 수 있게 됩니다.

앞서 예를 든 50개의 앱을 앱 단위 확장 방법으로 설정하면 모두 하나의 앱 서비스 계획에 담고 각각의 앱을 다음과 같이 설정할 수 있습니다:

  • 낮은 사용량의 40개 어플리케이션은 최대 1개의 서버에서 실행하도록 각각 설정합니다.
  • 중간 정도 사용량의 5개 어플리케이션은 각각 최대 2개의 서버에서 실행하도록 설정합니다.
  • 나머지 높은 사용량을 가진 5개의 어플리케이션은 최대 10개의 서버에서 실행하도록 설정합니다.

위와 같이 하면 앱 서비스 계획은 최소 5개의 서버(높은 사용량에 최소치를 맞추어)로 시작할 수 있습니다. 그 후 CPU/메모리 사용량에 따라 자동 크기 조정 규칙을 만들면 됩니다.

이렇게 설정하면 Azure 앱 서비스는 어플리케이션이 필요한 컴퓨터 자원을 자동으로 할당합니다. 그리고 앱 서비스는 각 어플리케이션에 설정된 최대한의 작업자 숫자에 맞춰 어플리케이션 인스턴스 수를 제한할 것입니다. 결론적으로, 앱 서비스 계획에 작업자 숫자를 늘린다고 50개 앱이 모든 곳에서 실행되지 않게 됩니다.

요약하면, 앱 단위 확장은 앱 서비스 계획 위에서 도는 어플리케이션을 모든 서버로 흘러넘치지 않게 해줍니다. 그래서 모든 어플리케이션이 모든 계산 리소스에서 돌지 않도록 합니다.

어플리케이션 슬롯

앱 서비스에는 배포 슬롯이라는 것이 있습니다. 자세한 내용은 다음 링크(bit.ly/2iJzv3f)에서 볼 수 있습니다. 배포 슬롯은 프로덕션으로 동작 중인 어플리케이션과는 구분된 별개의 어플리케이션을 ‘슬롯’이라는 개념으로 만든 것입니다. 새로 만든 어플리케이션은 프로덕션으로 교체하기 전에 테스트용으로 활용할 수 있습니다.

어플리케이션 슬롯은 앱 서비스에서 가장 많이 쓰이는 기능입니다. 각각의 어플리케이션 슬롯은 사실상 완전히 독립된 어플리케이션입니다. 즉, 커스텀 도메인, 별개의 SSL 인증서, 별개의 어플리케이션 설정값 등 모든 요소가 독립적입니다. 나아가 앱 서비스 계획조차 프로덕션 슬롯과는 별개로 다룰 수 있다는 뜻이기도 합니다.

기본적으로 각 어플리케이션 슬롯은 동일한 앱 서비스 계획 안에 만들어집니다. 낮은 사용량의 어플리케이션은 리소스 사용량이 낮으므로 같은 앱 서비스 계획 안에 만들어도 괜찮습니다.

하지만, 하나의 앱 서비스 계획에 담긴 모든 어플리케이션은 동일한 서버에서 실행되므로 프로덕션 어플리케이션과 같은 서버에 있게 됩니다. 프로덕션이 아닌 어플리케이션 슬롯에 부하테스트를 해도 프로덕션 어플리케이션까지 영향을 받게 되는 문제가 있습니다.

부하테스트를 할 때 프로덕션 슬롯에 주는 영향 없이 리소스 사용량을 점검하고 싶다면, 새 앱 서비스 계획을 만든 후 해당 슬롯을 그 계획으로 옮기면 됩니다. 다음과 같이 해보세요:

  • 테스트용 슬롯을 옮길 새 앱 서비스 계획을 만듭니다. 주의사항: 앱 서비스 계획은 프로덕션 슬롯이 있는 앱 서비스 계획과 같은 리소스 그룹, 같은 지역에 있어야 합니다.
  • 테스트용 슬롯을 위 단계에서 만든 앱 서비스 계획으로 옮깁니다. 이제 계산 리소스는 프로덕션 슬롯과 완전히 다릅니다.
  • 이제 테스트 슬롯에 마음껏 테스트를 하세요. 앱 서비스 계획이 다르므로 프로덕션 슬롯의 리소스에는 영향을 주지 않습니다.
  • 테스트를 마친 후 프로덕션 슬롯으로 교체하고 싶다면 테스트 했던 슬롯을 다시 프로덕션 슬롯이 있는 앱 서비스 계획으로 옮긴 후에 전환(swap)을 하세요.

무중단 프로덕션 배포

어떤 개발팀은 어플리케이션을 운영하면서 매일 업데이트를 배포하기도 합니다. 이 때, 여러분은 프로덕션에 바로 업데이트를 하고 싶진 않을 것입니다. 배포할 때 서비스 중단을 최소화할 수만 있다면 더욱 좋겠지요. 어플리케이션 슬롯을 잘 활용하면 충분히 가능합니다. ‘pre-production’이라는 슬롯을 하나 만든 후 가장 최근의 소스코드를 배포하고 프로덕션 설정과 동일하게 맞춥니다. 테스트를 충분히 했다면 전환(swap) 버튼을 눌러서 프로덕션 슬롯과 바꿉니다. 교체 작업은 어플리케이션을 재시작하지 않고 콘트롤러가 프론트엔드 부하분산장치(load balancer)에 알림을 줘서 트래픽을 최신 슬롯으로 리다이렉트하도록 합니다.

프로덕션의 트래픽을 받기 전에 워밍업을 해야 하는 어플리케이션도 있습니다. 예를 들면 캐시를 생성해야 하거나 .NET 어플리케이션의 경우 .NET 런타임이 JIT 처리를 해야하는 경우입니다. 프로덕션으로 전환하기 전에 미리 슬롯을 워밍업할 수 있습니다.

pre-production 슬롯으로 테스트와 워밍업을 동시에 하는 사용자도 있습니다. Visual Studio Release Manager 같은 지속 배포 도구를 사용하면 슬롯 전환 전에 pre-production 슬롯에 코드를 배포하자마자 테스트를 실행해서 문제가 없는지 확인한 후 워밍업 수행까지 한 흐름에 할 수 있습니다.

확장 단위의 네트워크 설정

앱 서비스의 확장 단위는 클라우드 서비스를 통해 배포됩니다. 이와 관련한 네트워크 설정과 특징을 이해하면 여러분의 앱이 네트워크에 어떤 영향을 주고받는지 더 깊게 이해할 수 있습니다.

확장 단위는 하나의 가상 IP (VIP)만 노출하고 있습니다. 한 확장 단위 안에 있는 모든 어플리케이션은 이 VIP를 통해 트래픽을 받습니다. 또한, 이 VIP는 앱 서비스 확장 단위가 배포된 클라우드 서비스를 나타낸 것이기도 합니다.

앱 서비스 어플리케이션은 HTTP(80 포트)와 HTTPS(443 포트)로 오는 트래픽만 받습니다. 기본적으로 모든 어플리케이션은 HTTPS를 지원하는 azurewebsites.net 도메인이 있습니다. 뿐만 아니라 앱 서비스는 Server Name Indication(SNI)와 IP기반 Secure Socket Layer(SSL)를 모두 지원합니다. IP기반 SSL의 경우, 인바운드 트래픽에 대한 IP만 할당받으며, 이 IP가 클라우드 서비스 배포본과 엮여있습니다. 참고로 HTTPS로 요청받는 모든 SSL 연결은 프론트엔드에서 끝나고, 프론트엔드는 그 트래픽을 특정 어플리케이션이 있는 작업자로 포워딩합니다. (역자주: 프론트엔드에서 인증서기반 암호화/복호화를 처리하며, 프론트엔드와 작업자 사이는 일반 HTTP 통신입니다. 더 자세한 내용은 다음 글을 참고하세요. http://stackoverflow.com/a/43132372/361100)

공개 VIP

기본적으로 한 확장 단위의 모든 인바운드 HTTP 트래픽에는 1개의 공개 VIP를 사용합니다. 즉, 어떤 앱이든 하나의 VIP로 지정할 수 있습니다. 앱 서비스에 1개의 앱만 있다면, nslookup 명령을 실행해보세요. 아래는 그 결과 예입니다.

#1 PS C:\> nslookup awesomewebapp.azurewebsites.net
#2 Server:  UnKnown
#3 Address:  10.221.0.3
#4 Non-authoritative answer:
#5 Name: waws-prod-bay-001.cloudapp.net
#6 Address:  168.62.20.37
#7 Aliases: awesomewebapp.azurewebsites.net

한 줄씩 awesomewebapp.azurewebsites.net에 대한 결과를 살펴보겠습니다.

  • 줄 #1 nslookup에서 awseomwebapp.azurewebsites.net 을 쿼리한 결과입니다.
  • 줄 #5 어플리케이션 awseomwebapp을 호스팅하는 확장 단위의 도메인 이름을 볼 수 있습니다. cloudapp이라는 이름에서 알 수 있듯이 앱 서비스 확장 단위는 Azure 클라우드서비스에 배포되어있다는 것을 알 수 있습니다. WAWS는 Windows Azure Web Sites의 약자입니다. 과거에 Azure가 Windows Azure라고 불렸으며 앱 서비스는 Web Sites로 불렸던 적이 있습니다.
  • 줄 #6 확장 단위의 VIP를 볼 수 있습니다. 줄 #5에서 언급한 waws-prod-bay-001는 호스팅하는 모든 어플리케이션을 하나의 공개 VIP로 지정할 수 있습니다.
  • 줄 #7 같은 IP 주소에 매핑된 모든 도메인 별칭입니다.

아웃바운드 VIP

보통의 어플리케이션은 다른 Azure 서비스와 연결하거나 Azure가 아닌 외부 서비스와 통신할 때가 많습니다. 이 때 어플리케이션이 속해 있는 확장 단위를 넘어서 외부 네트워크로 연결을 해야 하는데 이를 아웃바운드 네트워크라고 합니다. Azure 서비스인 SQL 데이터베이스나 저장소에 연결하는 것도 아웃바운드 네트워크입니다. 아웃바운드 통신에는 최대 5개의 가상IP (VIP; Virtual IP)를 사용할 수 있습니다. 하나는 공용 VIP이며, 나머지 4개는 아웃바운드 전용입니다. 하나의 확장 단위에 있는 모든 어플리케이션은 이 5개의 IP를 사용하는데, 여러분이 어떤 VIP를 사용할지 선택할 수는 없습니다. 그러므로, 여러분이 접속할 서비스에 화이트리스트 IP를 등록하고 싶다면 5개를 모두 등록해야합니다. 여러분의 어플리케이션이 놓여있는 확장 단위의 IP 주소를 확인하려면 아래 그림 3과 같이 포털의 앱 서비스 속성에서 볼 수 있습니다.

App Service Application Outbound IP Address View in Azure Portal
그림 3 Azure 포털에서 앱 서비스 어플리케이션의 아웃바운드 IP 주소를 볼 수 있습니다

인바운드와 아웃바운드의 모든 IP를 단독으로 할당받으려면 이 링크(bit.ly/2hVRSlR)의 앱 서비스 환경(App Service Environment) 문서를 참고해주세요.

IP와 SNI SSL

앱 서비스는 IP기반 SSL 인증서를 지원합니다. IP-SSL을 사용하려면 HTTP 트래픽을 받기 위한 인바운드용 IP 주소를 지정받아야(dedicated IP) 합니다.

Azure에서 제공하는 지정 IP 주소와는 달리 앱 서비스의 IP-SSL은 여러분이 앱을 사용하고 있는 동안만 유효합니다. 다시 말해, IP 주소를 지정받았으되 고정적으로 가지고 있을 수는 없습니다. 여러분이 IP-SSL을 삭제하면 지정받았던 IP 주소를 잃고, 그 IP 주소는 다른 어플리케이션에 할당됩니다.

앱 서비스는 SNI SSL도 지원합니다. SNI SSL은 IP를 지정받을 필요가 없고 대부분의 브라우저에서 지원하므로 SNI SSL 사용을 권장합니다.

아웃바운드 네트워크에서의 포트 허용량과 그 한계

보통의 어플리케이션은 외부 네트워크에 아웃바운드로 연결할 일이 많습니다. 앞서 언급했듯 Azure 내부 서비스인 SQL 데이터베이스나 저장소에 접속하는 것 뿐만 아니라 HTTP/HTTPS API에 접속하기도 합니다. Bing 검색 API를 이용하거나 비즈니스 로직을 구현한 백엔드 API 어플리케이션에 접속하는 경우가 그 예입니다.

위의 경우는 모두 앱 서비스가 외부로 나가는 네트워크 소켓을 열고 아웃바운드 요청을 만드는 작업이며, Azure 네트워크 관점에서는 원격(remote) 접속입니다. 그러므로, 앱 서비스에서 원격 엔드포인트로 나가는 요청은 Azure 네트워킹 설정과 관련이 있으며,  네트워크주소변환(Network Address Translation; NAT) 테이블의 매핑작업으로 관리되고 있습니다.

하나의 앱 서비스 확장 단위에서 NAT 매핑을 새로 만드는 작업은 시간도 걸리지만 만들 수 있는 최대 NAT 매핑 갯수도 명확한 한계값이 있습니다. 그러므로, 앱 서비스의 아웃바운드 연결은 제약을 받을 수도 있고 더 이상 만들 수 없는 경우도 있습니다.

연결 수의 한계는 다음과 같습니다:

  • B1/S1/P1 인스턴스 당 1,920 연결
  • B2/S2/P2 인스턴스 당 3,968 연결
  • B3/S3/P3 인스턴스 당 8,064 연결
  • 앱 서비스 환경 당 최대 64K 연결

연결 관리를 잘 못하는 어플리케이션은 접속 제한 문제를 늘 겪습니다. 보통은 많은 부하를 받을 때 외부 접속도 많아지므로 이런 문제가 있는 어플리케이션은 부하가 많아질 시점에 원격 접속 실패 메시지도 그만큼 자주 나오곤 합니다. 접속 실패시 다음과 같은 메시지를 보게 됩니다: “액세스 권한에 의해 숨겨진 소켓에 액세스를 시도했습니다. aaa.bbb.ccc.ddd (An attempt was made to access a socket in a way forbidden by its access permissions aaa.bbb.ccc.ddd)”

이런 문제를 줄이기 위한 몇가지 방법이 있습니다:

  • ADO.NET/EF를 사용하는 .NET 어플리케이션은 데이터베이스 연결 풀링을 사용하세요.
  • php/MySql의 경우 지속적인 데이터베이스 접속(persistent database connections)을 사용하세요.
  • Node.js에서 아웃바운드 HTTP/HTTPS 요청을 할 때는 keep-alives를 설정해서 연결을 재사용하세요. 설정에 대한 자세한 내용은 다음 링크(bit.ly/2iGrcoo)를 참고하세요.
  • .NET 어플리케이션에서 아웃바운드 HTTP/HTTPS 요청을 할 때는 System.Net.Http.HttpClient 인스턴스를 이용해서 연결 풀을 재사용하거나 System.Net.HttpWebRequest 사용할 때 Keep-alive 연결로 설정하세요. 참고: System.Net.ServicePointManager.DefaultConnectionLimit 숫자를 늘리세요. 기본값은 하나의 엔드포인트마다 2개의 동시접속으로 제한되어 있습니다.

App Service 샌드박스에는 몇 가지 제약이 더 있습니다. 지금까지 언급했던 제약보다 저수준의 제약 사항이며 자세한 내용은 다음 링크(bit.ly/2hXJ6lL)를 참고하시기 바랍니다.

정리하기

Azure 앱 서비스는 웹, 모바일, API 어플리케이션에 알맞은 PaaS 입니다. 앱 서비스 내부는 유동적인 요소가 많지만 개발자가 어플리케이션 개발에만 집중할 수 있도록 추상화했습니다. 이제 여러분들이 전세계로 어플리케이션을 확장하는데 필요한 복잡한 고민은 앱 서비스가 처리해줄 것입니다.

우리가 꼽는 앱 서비스의 모범적인 사례는 대부분 어플리케이션 확장에 대한 내용입니다. 앱 서비스 계획 안에서 어플리케이션이 웹 작업자와 어떻게 매핑되는지 잘 이해할 수록 확장 규모를 최적화하는데 도움이 됩니다.

Azure와 Azure 앱 서비스는 우리가 클라우드-퍼스트를 제창한 이래 빠르게 발전하고 있습니다. 2017년에도 새로운 혁신은 계속 될 것입니다.

덧붙임: 확장 단위 내의 구성요소 관계에 대해

이 글을 읽으면 확장 단위의 각 요소가 매우 강한 의존관계에 있는 것처럼 보일 수 있습니다. 하지만 설계상 각 요소는 느슨하게 연결되어 있습니다. 웹 작업자 외에 다른 역할이 비정상적인 상태여도 HTTP 트래픽을 처리하고 있는 어플리케이션은 계속 HTTP 트래픽을 처리할 수 있습니다.

예를 들어, 게시자가 정상동작 하지 않으면 FTP 접속을 할 수는 없지만 어플리케이션의 HTTP 트래픽에는 영향이 없고 다른 배포 기능에도 영향이 없습니다. API 콘트롤러에 있는 버그로 새 어플리케이션을 만들 수는 없더라도 이미 확장 단위에 있는 어플리케이션은 문제없이 동작합니다.


Yochay Kiriaty Microsoft Azure 팀의 principal program manager 입니다. 앱 서비스 플랫폼에서 웹, 모바일, API, functions를 이끌고 있습니다. Kiriaty는 90년대 후반부터 웹 기술 분야에서 일을 했고 성능과 확장을 중요하게 생각합니다. 이메일: yochay@microsoft.com 트위터: @yochayk

Stefan Schackow Azure 앱 서비스 팀의 program manager 입니다. Azure 웹앱을 처음 클라우드 서비스로 선보일 때부터 일했습니다. 현재는 Azure 앱 서비스의 배포와 개발을 책임지는 program manager들을 이끌고 있으며, Microsoft의 온프레미스/하이브리드 제품군(Azure Pack 및 Azure Stack) 개발도 책임지고 있습니다. 이메일: stefsch@microsoft.com

이 문서의 리뷰를 맡아준 Eduardo Laureano와 Nir Mashkowski에게 감사드립니다.

이 문서를 번역한 김영재 교육서비스 바로풀기의 개발사 Bapul의 CTO로서 기술로 교육에 새로운 시각을 주기 위해 열심히 개발하고 있습니다.

금주의 Azure 질답 – 201601 마지막주

아니 벌써 1월이 끝났다니.
이런 기분으로 앞으로 11번 반복하면 1년이 지나겠죠.

WordPress.com이 개편 중에 있다보니 마크다운으로 작성하다가 다시 편집하면 html 태그로 바꿔놔서 긴 글을 쓰기 힘들군요. 저장 직전에 메모장에 복사했다가 수정할 때 다시 붙여넣고 있습니다.

1. Azure Search와 Elastic Search는 뭐가 달라요?

원문보기: social.msdn.microsoft.com/Forums/azure/…

Azure Search는 Search-as-a-Service를 표방하고 나온 Azure의 SaaS(Software-as-a-Service) 중 하나입니다. 그 기반으로는 실제로 Elastic Search(이하 ES)를 사용하고 있습니다.

ES를 한겹 더 감싸서 OData 형식으로 노출하고, 유저들에겐 관리 편의를 제공한다고 보시면 됩니다. 관리 편의 중에서는 샤딩 뿐만 아니라 액세스 관리 등 보안도 포함되지요.

검색을 클라우드로 하는 것은 뭐가 좋은가요? ‘클라우드적인 의사결정’에서 중요한 것이 있다면, 소프트웨어를 구현한 것과 이를 스케일업하는 것은 완전 다른 이야기라는 것입니다. Elastic Search도 소량만 운용하면서 ‘우린 ES로 검색기술을 쓴답니다’하는 것과 스케일업/아웃하면서 모든 것을 차질없이 운용하는 것은 다른 이야기가 되는 것이겠지요.

Azure Search Scalability
Azure Search Scalability. (출처: Channel9)

스케일이나 글로벌 대응을 고려하지 않으면 클라우드는 허세고 카페24같은 로컬호스팅 쓰는게 제일 싸고 좋습니다.

각설하고, Azure Search는 현재 성숙한 상태는 아닙니다. 그러므로 지원되는 장점보다 지원 안되는 단점이 큰 상태라고 생각합니다. ES에서 되지만 Azure Search에는 없는 것은 아래와 같습니다.

  • Index Schema는 미리 알려줘야 함: 자동으로 인덱싱을 하는 기능이 없습니다.
  • Array 형태는 index 안됨: 지원되는 형태에 없답니다.
  • Re-indexing 안됨: index schema 변경시 re-indexing은 안되고 다시 데이터를 올려줘야 합니다.
  • ES용 플러그인 안됨: Azure Search는 ES를 기반으로만 사용할 뿐이므로 현재 ES에 제공되는 플러그인이 지원되지 않습니다. 하지만 ES 플러그인들의 모방 프로젝트들이 계속 생겨나고 있습니다. 예를 들어, Fluent 형식으로 쿼리할 수 있게 하는 RedDog.Search 프로젝트가 있지요.
  • Form body로 보내는 복잡한 쿼리 안됨

단점을 보면 이것이 적합한 서비스는 (1) 데이터 구조가 단순하고 (2) SQL Azure, DocumentDB 등 Azure 기반으로 축적된 데이터에 검색기능을 넣기 위해 (3) 간단한 검색 요구사항 대비 강력한 기능을 가장 빨리 적용하고 싶을 때 유용하다고 판단할 수 있겠습니다.

Azure Search에 대한 맛보기로는 아래 동영상보다 친절한 것은 없어보이더군요.
https://channel9.msdn.com/Events/Microsoft-Azure/AzureConf-2014/Search-Like-a-Pro-with-Azure-Search?ocid=player

2. Azure WebApp, WebJob에서 그 호스트의 주소를 어떻게 알아요?

원문보기: http://stackoverflow.com/q/35088567/361100

WebApp은 몇 개의 환경변수를 미리 정의하고 있는데 그 중 WEBSITE_HOSTNAME를 사용하면 자신이 호스팅 된 WebApp 주소(*.azurewebsites.net)를 알 수 있습니다. 배포할 때 이용하면 주소를 하드코딩하지 않아도 되므로 좋습니다.

WebJobs는 자신이 실행되는 폴더 경로 등의 환경 변수도 있습니다.

3. Azure WebJob과 WebApp API는 어떻게 통신해요?

원문보기: http://stackoverflow.com/q/31114346/361100

이 질문은 꽤 많이 나옵니다. 그도 그럴 것이, WebApp이라는 것이 사실 WebJob이 한 몸처럼 보이는데 한 몸이 아니기 때문이지요.

WebApp에서 요청받은 쪽(프론트라고 부르겠습니다)이 WebJob을 직접 실행하거나 결과를 즉시 받아서 처리하는 방법은 없습니다. 그 이유는, WebJob은 요청을 받는 프론트와 구조적으로 별개로 동작하기 때문입니다. 즉, 요청받은 쪽에서 WebJob을 트리거하는 방법도 없습니다.

해결 방법 중 대표적인건 Azure Queue를 사용하는 것입니다. 프론트에서 Azure Storage Queue에 값을 넣으면 WebJob이 이를 Trigger로 받아서 처리하는 것이지요.

하지만 여기에도 함정이 있는데, WebJob에서 Azure Queue Trigger로 작성해도 실제로는 트리거가 아니라 polling을 몇 번 하다가 얻어걸리는(?) 것입니다. 그러므로 바로바로 된다고 말할 수 없습니다.

물론 컴퓨터 통신에서 trigger라고 하는 많은 것은 사실 촘촘한 polling이지만, WebJob은 게을러서 트리거 몇 번 해보다가 얻는게 없으면 polling 주기마저도 늦춥니다.

그러므로 시간이 민감한 백그라운드 작업이라면 배포가 느리더라도 WorkerRole을 사용하는 것이 좋습니다. 시간에 민감하지 않은 통계 등에는 WebJob의 가격이 저렴하니 괜찮은 선택입니다.

그 외에 WebJob에서 프론트를 호출하는 방법 중 SignalR을 이용하여 WebJob에서 처리되는 진행률을 표시하는 샘플도 있는데, 저는 이것을 보고 ‘아 이렇게 구현할 수도 있구나’ 했습니다.

ASP.NET에서 hang이 걸릴 때 처음 파악해야 할 점

얼마전 우리는 Azure의 ASP.NET 서버(WebRole, WebSites 모두)에서 어느 순간 엄청난 랙이 발생하는 것을 경험했습니다. 서비스가 올라간 Azure WebSites는 허구헌날 죽었습니다. 게다가 여러 APM, 감지툴을 써도 그 원인을 모호하게 알려줘서 판단하기 어려웠습니다.

뭐라고 툴에서 나왔냐면 _DynamicModule_Microsoft.Owin.Host.SystemWeb.OwinHttpContext에서 ExecuteRequestHandler의 OnPreExecuteRequest state에 문제가 있다는 것입니다.

그런데 PreExecuteRequest는 OWIN 처리의 아주 앞부분에 해당하는 것으로 사실 개발자 입장에서 딱히 커스텀할 여지가 없는 부분입니다. OWIN의 Startup.cs파일에 app.Use(…); 부분이지요. 실행 중인 VM에 프로파일러를 통해 스레드를 덤프해보기도 했지만 딱히 메모리 누수나 hot spot은 안보였습니다. 문제를 해결하는 동안은 여러 인스턴스를 돌리면서 ACK가 느려진다는 것이 감지되면 IIS를 리스타트 하는 방법으로 버텼습니다.

원인은, ORM(Object-relational mapper)에서 detach를 안한 콘트롤러가 하나 있어서 그곳에 많은 활동을 한 유저가 요청하면 그에 대한 모~든 연결된 정보를 다 가져오려고 했기 때문입니다. ORM은 DB스키마를 클래스 오브젝트로 매핑해준다는 기본 기능 뒤에서 입출력시 object graph를 DB스키마와 맞추는 attach, 데이터를 가져올 때 오브젝트에 매핑하고 변경 사항 트래킹을 끊는 detach 과정이 있습니다. (보다 자세히는 Context라는 개념도 있지만 논외로 합니다)

attach/detach를 개발자가 의도적으로 사용할 경우 attach를 실패하는건 주로 UPSERT 과정인지라 마치 SQL INSERT문을 잘못 쓰는 것처럼 에러가 나며, detach를 안하면 어디까지 가져와야 하는지 몰라서 가능한한 많은 연관정보를 가져오게 됩니다. (물론 ORM 제조사, 설정따라 다를 수 있습니다) 중요한건, 제가 겪은 detach에 대한 문제는 테스트 과정에서는 안나왔다는 것입니다. 왜냐하면 테스트용 DB는 그렇게 레코드가 많지 않았기 때문입니다.

저희의 경우, 네이버 지식인 같은 서비스를 가정할 때 6000개의 답변을 한 유저의 경우 6000개 답변+6000개 질문+수천개 다른 답변+각 질문당 2개 이상의 태그+이미지 한 장 이상+댓글+수백명의친구+그 친구들의 질문+그 친구들의 답변+그 모든 사람의 프로필….=수백만개의 레코드를 줄줄줄 다 긁어불러와서 메모리로 올리려고 했던 것이지요. 반면 활동량이 적은 (대부분의) 유저들이 문제가 있는 콘트롤러에 요청할 때는 그래봤자 수백건 뿐일테니 문제가 거의 일어나지 않아서 어느 조건에서 나는지도 파악하기에 모호했습니다. Azure WebSites가 허구헌날 죽었던 이유는 작은 크기로 램이 금새 가득차서 자체적으로 리사이클을 실행했기 때문입니다.

이런 문제 해결에 가장 먼저 해야 할 것은 LeanSentry 블로그 글의 도움을 받았는데, 현재 IIS의 request queue에 작업중인 요청 목록을 보는 것입니다. 이에 비슷한 글은 IIS 공식홈에서도 볼 수 있습니다.

아래는 문제가 발생할 당시의 스크린샷입니다.

캡처2

이 창을 보려면 서버의 IIS관리자를 열고 해당 어플리케이션을 좌측 트리에서 클릭한 후 Worker Process라는 아이콘을 클릭합니다. 접속한 IP (모자이크 처리), 요청한 콘트롤러 주소, 현재 처리중인 state, 끝으로 각 요청 별 소요시간을 보여줍니다.

역시, 스크린샷을 보는 것처럼 유사해보이는 요청 몇개가 어마어마한 Time Elapsed 값을 가지고 있습니다. 그리고 APM툴들이 말하는 문제 부분을 동일하게 보여주고 있습니다. APM은 여기 써있는 것을 전달해주는 것이었겠죠. 우리는 이것을 보자마자 해당 콘트롤러를 가서 문제를 쉽게 해결할 수 있었습니다. 참고로 Time Elapsed 단위가 밀리세컨드로 알고 있는데 새로고침(화면에 Show All 버튼)을 하면 보통은 아무리 길어도 개당 200ms이하로 잠깐씩 보였다 사라집니다.

이 문제 해결과정에서 의아한건, DB를 가져오는 ORM 코드는 WebApi Controller의 거의 마지막 지점에 있는데 State 표시는 그보다 훨씬 앞 지점에 있다는 것입니다. IIS의 매커니즘을 잘은 모르지만 이러한 IIS의 정보를 그대로 보고하는 APM 툴을 문제 해결에 맹신하면 안된다는 교훈을 줬습니다.

더불어 Azure 웹서비스 운용에 한가지 팁이 생겼는데, Azure WebRole을 메인으로 개발하다가 안정화가 되면 Azure WebSites로 이전해서 운용하는 것을 추천합니다. 이유는 WebRole은 배포가 20분 가량으로 매우 느리지만 디버깅하기 좋으며 WebSites는 경쾌하지만 개발자에게는 투명하지 않고 자유도도 낮기 때문입니다.

저희는 WebSites 2 instance + WebRole 2 instance를 Traffic Manager(RoundRobin 전략)로 엮어서 사용하고 있습니다. WebRole을 적어도 하나 이상은 써야 맘이 놓이는게, 실제로 성능이 더 안정적이고 더 빠른 리스폰스가 오는 것을 확인했기 때문입니다.

TeamCity에서 Azure WebSites Deploy하기

JetBrains TeamCity는 훌륭한 CI(Continuous Integration) 소프트웨어로, 자바기반으로 만들어져서 거의 모든 OS를 지원하며 무료 사용으로도 어느정도 풍부한 기능을 사용할 수 있다. 적어도 꼬꼬마 스타트업은 뒤집어쓸 정도로 여유롭다.

특히 Jenkins를 쓰는 회사들은 어지간해서는 TeamCity로 교체하길 추천한다. 그 이유는 Jenkins는 정말 눈물이 나고 손가락이 오그라들 정도로 민망하고 불편한 UI/UX를 가지고 있기 때문이다. 개발자도 예쁜거 좋아한다.

각설하고, 원래 Azure WebSites는 Git에서 바로 배포가 가능하다. 하지만 프로젝트가 복잡해지면 기본 기능만으론 부족해진다.

Azure WebSites를 TeamCity로 배포하기 위해서는 다음의 과정을 거치면 된다.

  1. 소스콘트롤에서 Pull
  2. Nuget Restore
  3. 빌드+배포 (빌드하자마자 바로 publish 가능하도록 되어 있다)
  4. Azure WebSites Deploy 비밀번호 적용

1. 소스콘트롤에서 Pull

TeamCity의 Project 하나를 만들면 좌측 메뉴 중 Version Control Settings 메뉴가 있다. master 브랜치로 하든 release 브랜치로하든, 이 글의 주제 밖이며 설정에 어려움이 딱히 없다.

2. Nuget Restore

Build Steps에서 하나를 추가한 후 아래와 같이 설정한다.

  • Runner Type: NuGet Installer
  • NuGet.exe: Default (2.8.3) 버전명은 다를 수 있다
  • Path To Solution File: 옆에 트리를 눌러서 .sln 파일을 지정한다.
  • Restore Mode: Restore

나머지는 딱히 할 것이 없다.

만일 Nuget 버전이 안나온다면 Teamcity에 Nuget설치가 안된 것이다. 걱정마시라. TeamCity의 NuGet 설정은 클릭 몇 번에 끝난다. 설정법은 우상단 Administration > Integration 섹션에 NuGet > Nuget.exe 탭의 Fetch Nuget 버튼만 누르면 버전별로 골라가져올 수 있다. TeamCity 자체가 NuGet서버로 사용될 수도 있고 빌드시에는 자신한테 있는 NuGet.exe를 이용해 패키지를 다운받아서 restore시 적용한다.

가히 멋지고 인텔리전트하고 판타스틱하다.

3. 빌드+배포

이에 대해서는 두가지 방법이 있다. (1) Solution Build를 하는 것, (2) MSBuild를 이용하는 것. 필자처럼 솔루션에 꼭 WebSites에 올릴 것만 있는게 아니라 다른 프로젝트(백그라운드작업 등)가 같이 있다면 MSBuild를 이용한다. (Solution Build만으로 문제없으면 그냥 Azure WebSites에서 제공하는 기본 배포 기능 이용해도 된다.)

  • Solution Build를 이용할 분들은 다음을 참고한다 => 링크
  • 보너스: Azure WebRole로 배포하길 원하면 다음을 참고한다 => 링크

MSBuild로 설정할 경우 다음과 같이 설정한다.

  • Runner Type: MSBuild
  • Build file path: 앞서 NuGet에서 지정한 것과 동일한 .sln 파일 지정.
  • MSBuild Version: Microsoft Build Tools 2013
  • MSBuild ToolsVersion: 12.0
  • Run platform: 12.0
  • Targets: Rebuild
  • Command line parameters
/p:Configuration=teamcity_server
/p:DeployOnBuild=true
/p:PublishProfile=teamcity
/p:ProfileTransformWebConfigEnabled=False
  • Reduce test failure feedback time: 체크하세요.

위의 /p: 중에 알아야할게 있다.

  • Configuration=teamcity_server: teamcity_server는 무엇인가? 이것은, 아래의 Configuration Manager의 설정 이름으로 여러 프로젝트 중 관련 없는 것을 무시하도록 설정한 이름이다. 보면 체크가 빠진 것들이 보일 것이다.

  • DeployOnBuild=true: 이것이 멋진 기능인데 Build하면 바로 Publish하는 것이다. 패키지 zip으로 만들고 업로드하고 명령어 날리는 것을 알아서 다 해준다.
  • PublishProfile=teamcity: 이게 어디있는 이름이냐면 아래 그림과 같은거다. 우리가 프로젝트에 우클릭하고 Publish 누르면 Publish Web이라는 창이 뜨고 여기서 Publish를 누르면 Azure WebSites에 올라가는데 그 설정 이름이다. 여기서는 teamcity라고 하나 만들었다.


이 창은 Publish라고 부르지만 기술적으로는 MSDeploy라고도 한다.

여기까지 하면 대략 TeamCity Build Steps 설정은 아래 그림과 같아진다.


세번째 행에 disable된 것은 무시하자. 실험하느라 만들었던거다.

이제 여기서 성급하게 우상단에 Run을 누르면 Authentication error가 발생한다. 이유는 Azure WebSites Deploy용 비밀번호를 안넣었기 때문이다. 이제 마지막 설정이 남았다.

Azure WebSites Deploy 비밀번호 적용

Azure 포탈에 접속해서 배포용 PublishSettings 파일을 받는다.


위 그림에 노란색 표시한 버튼이다.

받아서 메모장으로 열면 userPWD=”etxffzgX4wML0RQ43R2KR87pemoySuJva7Cs”와 같은 부분이 보일 것이다. 따옴표 안의 내용을 복사한다.

프로젝트 우클릭으로 Publish를 클릭해서 만들었던 teamcity.pubxml이라는 파일을 수정할거다. 더블클릭으로 열면 된다. 위치는 프로젝트>Properties>PublishProfiles>teamcity.pubxml 이다.

<PropertyGroup>
    ....
    <Password>etxffzgX4wML0RQ43R2KR87pemoySuJva7Cs</Password>
</PropertyGroup>

위와 같이 Password라는 태그를 만들어 넣는다.

됐다!

이제 TeamCity에 Run을 눌러서 Success가 되는지 확인해보자. 에러가 나면 여기에 물어보지 말고 직접 해결하자!

보너스

지금은 21세기이므로 git push를 하면 바로 굽는 설정을 하자. master branch로 push만하면 몇 분 후 알아서 구워주고, 결과는 아래 그림처럼 Visual Studio에서도 확인할 수 있다.
캡처4

방법은 간단하다. TeamCity에서 아래와 같이 Triggers 설정 하나만 해주면 된다.

VisualStudio에서 받아보는건, 우상단 자기 ID를 클릭하면 IDE플러그인을 받을 수 있는 메뉴가 뜨며, Notification Rules탭에서 IDE, Email 등으로 빌드 결과를 볼 수 있게 되어 있는데 IDE Notifier의 Add New Rule을 눌러서 원하는 프로젝트에 빌드 성공/실패에 대한 체크박스만으로 설정하면 된다.

레알 끝이다.