TeamCity에서 Azure Worker Role Deploy하기

파워셸을 처음 써보느라 거의 5시간 걸려서 해냈네요. 프로그래밍의 재미란 역시 이런것….은 개뿔 옳지 않습니다.

이전 글 TeamCity에서 Azure WebSites Deploy하기에 이은 이번엔 Azure Cloud Worker Role로 Deploy하는 법을 알아봅시다.

Azure Worker Role은 백그라운드 작업에 유용한 것으로, 단점은 지극히 느린 Deploy 시간입니다. 대략 짧게는 4분, 길게는 10분 가량의 시간이 소요되는데, 이렇게 오래 걸리는 이유는 수많은 과정을 거치기 때문이랍니다. 그러니 Visual Studio만 쳐다볼게 아니라 이런건 빌드서버에 맡겨두고 아이돌 음악 두세곡이 흐를 때 즈음 알림을 받는게 더 고상한 삶입니다.

빌드서버란 보통 이런 일을 합니다
빌드서버란 보통 이런 입장입니다

참고로 이 글은 srkirkland의 포스트를 참고했으며, 최근 주요 변경점을 반영했고, 삽질 오류 상황을 덧붙여서 업데이트한 글입니다.

필자가 이미 충분히 인생을 허비했으니 이 글을 따라하는 분들은 영생을 얻길 바랍니다.

Azure Worker Role을 Deploy하기 위해서는 총 3개의 빌드스텝이 필요합니다.
이 글은 TeamCity에서 배포하는 방법에 대한 가이드이며, 다음의 과정을 거칩니다.

  1. 로컬에서 배포테스트
  2. 소스콘트롤에서 Pull / Nuget Restore
  3. MSBuild
  4. 인증파일(.publishsettings) 설정
  5. 파워셸 스크립트
  6. 에러체크
  7. 트리거 설정 (옵션)

사실 빌드서버라는게 하는 일이 뻔합니다. 소스받아 컴파일하고 압축된 결과를 업로드하고 초기구동 스크립트를 실행하는거죠.

1. 로컬에서 배포테스트

로컬 컴퓨터에서 돌리는 Visual Studio의 클라우드 프로젝트에 우클릭하여 Publish를 클릭합니다. 많은 셋팅은 이에 기반하므로 먼저 성공하는지 점검해봅니다. 특히 Worker Role은 소스에 문제가 있는 경우 무한대기에 빠지는 경우도 있으니 일단 가장 기본 소스로 배포 자체가 성공하는지를 점검하는 것이 중요합니다.

2. 소스콘트롤에서 Pull / Nuget Restore

이전 글의 과정 1번, 2번과 동일합니다. 그대로 따라서 첫번째 Build Step인 Nuget Restore까지 만들어주세요.

3. MSBuild

먼저 주의할 점은, 빌드 서버에 동일 버전의 Azure SDK, Azure Cmdlet이 설치되어있어야 합니다. 설치는 Platform Installer를 이용하시면 편합니다.
빌드스텝 하나를 만들고 다음과 같이 설정해주세요.

  • Runner Type: MSBuild
  • Bulid File path: 솔루션 파일
  • MSBuild version: Microsoft Build Tools 2013
  • MSBuild ToolsVersion: 12.0
  • Run platform: x86 (다를 수 있음)
  • Targets: Publish (중요)
  • Command line parameters: /p:Configuration=teamcity_worker

마지막 줄에 /p:Configuration=teamcity_worker란? 이것은 아래 그림과 같이 Start 버튼 옆에 Solution Configuration이라는 콤보박스에 있는 것입니다. 저처럼 한 솔루션에 백그라운드와 상관없는 프로젝트가 있는 경우 괜히 빌드 에러가 날 수 있으므로 Dependency를 확인하고 꼭 필요한 것들만 컴파일 하도록 체크해줍니다.

캡처3

4. 인증파일(.publishsettings) 설정

이게 재미난데, 빌드서버에서 Microsoft Azure PowerShell을 실행하고 아래와 같이 Get-AzurePublishSettingsFile이라고 입력하면, 뜬금없이 브라우저가 뜨더니 로그인을 하라고 합니다. 로그인을 하면 Azure의 구독(subscription)을 선택하게 되고 .publishsettings 파일을 다운받게 해줍니다. 콘솔공포증인 저는 처음에 콘솔입력에 쫄았지만 다행이었습니다.

캡처5

이 파일을 “C:\teamcity\”에 파일명을 짧게 고쳐서 넣습니다. 보안이 걱정되면 보안이 좀 더 좋은 어딘가에 넣어놓으세요. 참고로 빌드서버는 외부접속이 안되도록 하는 경우가 대부분입니다.

5. 파워셸 스크립트

파워셸 스크립트로 Azure에 업로드를 합니다.
우린 지금까지 Nuget restore, MSBuild 빌드스텝을 만들었고, 이제 세번째가 되는 새 빌드스텝을 만들어서 다음의 값을 입력합니다.

  • Runner Type: Powershell
  • Powershell run mode: Any / Bitness: x86 (이건 만든 프로젝트에 따라 맞추세요)
  • Error Output: error (중요)
  • Script: Source Code
  • Script source: 아래 코드를 복사붙이기 한 후 […] 부분을 바꿔주세요. gist에도 올려놨습니다.
  • Script execution mode: Put script into PowerShell stdin with “-Command -” arguments

파워셸 스크립트

$ErrorActionPreference = "Stop"
$subscription = "[Your Subscription Name]"
$service = "[Your Azure Service Name]"
$slot = "staging" #staging or production
$package = "[ProjectName]bin[BuildConfigName]app.publish[ProjectName].cspkg"
$configuration = "[ProjectName]bin[BuildConfigName]app.publishServiceConfiguration.Cloud.cscfg"
$timeStampFormat = "g"
$deploymentLabel = "ContinuousDeploy to $service v%build.number%"

Write-Output "Running Azure Imports"
Import-AzurePublishSettingsFile "C:TeamCity[Your PublishSettings Filename].publishsettings"

function Publish(){
 Write-Output "$(Get-Date -f $timeStampFormat) - Publising Azure Deployment..."
 $deployment = Get-AzureDeployment -ServiceName $service -Slot $slot -ErrorVariable a -ErrorAction silentlycontinue 

 if ($a[0] -ne $null) {
    Write-Output "$(Get-Date -f $timeStampFormat) - No deployment is detected. Creating a new deployment. "
 }

 if ($deployment.Name -ne $null) {
    #Update deployment inplace (usually faster, cheaper, won't destroy VIP)
    Write-Output "$(Get-Date -f $timeStampFormat) - Deployment exists in $service.  Upgrading deployment."
    UpgradeDeployment
 } else {
    CreateNewDeployment
 }
}

function CreateNewDeployment()
{
    write-progress -id 3 -activity "Creating New Deployment" -Status "In progress"
    Write-Output "$(Get-Date -f $timeStampFormat) - Creating New Deployment: In progress"

    $opstat = New-AzureDeployment -Slot $slot -Package $package -Configuration $configuration -label $deploymentLabel -ServiceName $service

    $completeDeployment = Get-AzureDeployment -ServiceName $service -Slot $slot
    $completeDeploymentID = $completeDeployment.deploymentid

    write-progress -id 3 -activity "Creating New Deployment" -completed -Status "Complete"
    Write-Output "$(Get-Date -f $timeStampFormat) - Creating New Deployment: Complete, Deployment ID: $completeDeploymentID"
}

function UpgradeDeployment()
{
    write-progress -id 3 -activity "Upgrading Deployment" -Status "In progress"
    Write-Output "$(Get-Date -f $timeStampFormat) - Upgrading Deployment: In progress"

    # perform Update-Deployment
    $setdeployment = Set-AzureDeployment -Upgrade -Slot $slot -Package $package -Configuration $configuration -label $deploymentLabel -ServiceName $service -Force

    $completeDeployment = Get-AzureDeployment -ServiceName $service -Slot $slot
    $completeDeploymentID = $completeDeployment.deploymentid

    write-progress -id 3 -activity "Upgrading Deployment" -completed -Status "Complete"
    Write-Output "$(Get-Date -f $timeStampFormat) - Upgrading Deployment: Complete, Deployment ID: $completeDeploymentID"
}

#Azure Publish Execution
try{
    Set-AzureSubscription -CurrentStorageAccount [Your Storage Account Name] -SubscriptionName $subscription
    Publish
}catch{
    throw #Rethrow to detect failed build status.
    Break
}

하단의 [Your Storage Account Name]까지 꼼꼼하게 바꿔주세요. 바꿀 값들은 로컬에서 Publish할 때 나오는 창의 Summary에 대부분의 내용이 있습니다.

여기까지 하고 일단 Run을 실행해서 잘 되는지 확인해봅니다.

6. 에러 체크

TeamCity는, PowerShell 스크립트를 실행할 때 오류가 나도 success라고 되는 문제가 있습니다. 하지만, 저는 그 문제를 스크립트 마지막에 throw를 넣는 처리를 했고, 이제 teamcity가 이것을 통해 error라고 인식하게 해야 합니다.

말은 복잡하지만 아래처럼 간단히 체크 하나만 해주면 됩니다.
캡처3

끝났습니다.

7. 보너스: 트리거

저는 이전에 올린 Azure WebSites와 같은 솔루션을 사용하고 있으므로, VCS에서 Worker 프로젝트만 수정해도 WebSites가 구워지고, WebSites만 수정해도 8분가량 걸리는 Worker가 수정되는 문제가 있었습니다.

이를 해결하기 위해서는 Build Trigger Rule을 설정해줍니다.
캡처3
Trigger Rules의 기본규칙은, +는 Include, -는 Exclude이며, 위 그림의 설정을 보건데, /BapulServer/BapulSpreadWorker, /BapulServer/BapulCloudApiService 라는 폴더의 하부에 변화가 생겼을 때만 빌드를 수행합니다. 반대는 -:/폴더경로 식으로 해주면 ‘그 폴더에 대한 변화는 무시한다’는 뜻이 되므로 적절히 조합해서 사용하시면 되겠습니다. 자세한 규칙은 공식문서를 참고하세요.

최종적으로는, 아래 그림과 같이 셋팅이 되어 있을겁니다.
캡처4

그럼 즐거운 하루 보내세요.
레알 끝!

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중