금주의 Azure 질답 – 201604 첫째주

한 주에 쓸만한 Azure 질문을 공유하는 포스트입니다.

1. Azure Functions, Logic App, WebJobs 차이가 뭐에요?

원본링크: https://news.ycombinator.com/item?id=11398323

이번 Build 2016에서는 Azure Functions 라는 기능이 추가되었습니다.

Azure Functions 소개 영상 보기: https://channel9.msdn.com/Events/Build/2016/B858/

웹서버를 포함해서 보통의 서버가 하는 일이 ‘데이터를 받아서 함수를 실행하고 어디론가 보낸다’라는 것이 본질이고. 음..쓰다보니 컴퓨터라는게 그런거죠. 이런 함수 단위가 곧 클라우드에서 스케일업/아웃으로 하기 좋은 일이기도 하다보니 이에 대해 촘촘한 라인업을 구축하는 모습입니다.

물론 그래서 드는 의문은 기존 유사 기능과 무엇이 다르냐는 것이지요. Azure MVP들 사이에서도 이야기가 많이 오갔기에 정리해보고자 합니다.

  1. Azure Logic App
    이론상 ‘코드 한 줄 없이 실행’을 최우선 목표로 합니다. 그래서 미리 만들어진 것들이 갖추어져있고 대부분은 유명 API들입니다. Azure는 엔터프라이즈 고객이 많으므로 유명 API 중에서도 ERP관련 기능이 많습니다. 스타트업은 쓸 일이 딱히 없기도 합니다.

  2. Azure WebJobs
    가장 날 것의 함수 실행 기능입니다. 직접 만든 코드를 돌리고 가격은 WebApp에 구속되므로 WebApp의 자원을 재활용하겠다는 의지도 보입니다. 백그라운드 프로세싱 전용입니다.

  3. Azure Functions
    Azure Mobile Service는 Node.js로 브라우저에서 코딩을 하고 바로 확인하는 기능을 가지고 있습니다. Azure Functions는 그와 유사한 느낌을 줍니다.

브라우저에서 바로 코딩이다!
브라우저에서 바로 코딩이다!

현재 지원되는 언어는 Node.js와 C#이며 곧 F#을 비롯해서 여러 언어가 지원될거라고 하네요.

Azure Functions는 아래 그림처럼 Input/Output에 대해 어느정도 정리된 UI도 제공합니다.

딱 보면 뭐하는건지 대강 알겠습니다
딱 보면 뭐하는건지 대강 알겠습니다

제가 파악한 Azure Functions의 가장 큰 특징은 외부 접속이 가능하다는 겁니다. http를 endpoint로 노출할 수 있다는거죠.

다시 말해, HttpRequest를 trigger로 받는다는 의미고 이건 현재 여느 API 웹서버와 동일합니다. 즉, 간단한 기능은 이제 서버를 깔 필요도 없이, ASP.NET이나 dJango 웹프레임워크를 올릴 필요도 없이, 그냥 함수 내용만 쓰면 끝입니다.

이를 응용한 예시로 이미지파일 업로드가 바로 가능하다고 하는군요. 이제 웹서버에서는 이미지 POST를 받을 필요가 없을 것도 같습니다.

Azure Functions는 기술적으로는 Azure WebJobs 구현체 위에 올라가 있다고 하더군요. 그래서 기본적인 형태는 WebJobs의 대부분을 복사/붙이기로 가능하다고 합니다.

어쩌면 WebJobs는 이제 WebApp의 저렴한 비용이 목적이거나 남는 자원을 박박 긁어서 쓰겠다는 목적이 아닌 이상 딱히 쓸 일이 없을지도 모르겠습니다. (이거 나올 때부터 좀 애매하다 싶었어…)

Azure Functions에 대해 더 많은 내용은 아래 링크를 참고하세요.
https://azure.microsoft.com/en-us/blog/introducing-azure-functions/

YJ

금주의 Azure 질답 – 201602 둘째주

오늘은 Azure WebJob에 대한 내용을 공유합니다. 참고로 WebJob은 Azure WebApp 제품군 내에 있는 백그라운드 워커입니다.

저는 Azure WebJob을 테스트용으로만 쓰고 프로덕션에서는 사용하지 않는데 그럼에도 종종 다루는 이유는 (1) 아직 미숙할 뿐이지 나쁜게 아니라서 관심을 가지는 것은 좋기 때문이며, (2) Azure 팀에서 WebJob에 공을 많이 들이고 발전 속도도 빠르기 때문입니다.

다시 말해, WebApp은 이제 발전할거라곤 모니터링 보강과 프레임워크 버전 향상 말고는 딱히 없는데 WebJob은 MVP들의 의견도 활발히 받고 GitHub 이슈에도 즉각적으로 반응하고 있습니다.

WebJob은 뭐랄까…애증의 존재죠. 아 쫌만 더 좋으면 쓰겠는데 아직 약간 부족한 느낌이 드는. 그 부족한 느낌은, 컨셉이 아닌 모니터링과 관리에 대한 이슈입니다. 이게 도는지 안도는지 확신이 잘 안드는거죠.

1. Azure WebJob에서 지원되는 닷넷프레임워크 버전은 무엇인가요?

원문링크: http://stackoverflow.com/q/35371522/361100

저도 최신버전이 나오면 별 생각없이 업그레이드 하는 편입니다만, WebJob은 현재 4.6까지 지원됩니다. 최신 닷넷프레임워크인 4.6.1은 안됩니다.

문제는 배포 실패로 안뜨고 일단 실행은 되는데 에러메시지가

Job failed due to exit code -2146232576

위에처럼 나옵니다. 하지만 곧 4.6.1이 지원될 예정이랍니다.

2. Azure WebJob을 staging에서는 안돌리고 싶어요.

원문링크: http://stackoverflow.com/q/24438789/361100

WebApp은 여러개의 deployment slot을 가질 수 있고 이는 staging/production을 swap할 때 유용합니다. staging 상태에서는 돌지 않게 하려면

WEBJOBS_STOPPED = 1

위와같이 configuration을 넣어두면 됩니다.

추가로 이용할 수 있는 Configuration은 아래 링크에 있습니다.
https://github.com/projectkudu/kudu/wiki/Web-jobs#configuration-settings

3. WebJob이 중간에 멈춰요.

원문링크: http://stackoverflow.com/q/27939766/361100

Triggered-mode WebJob의 실행 허용시간은 기본적으로 60초입니다. trigger로 실행된 WebJob의 경우, 60초 후에 프로세스가 끝났는지를 보고 n초 후에 종료시킵니다. 그래야 다음에 올 트리거를 받을 수 있을테니까요.

조절할 수 있는 시간은 ‘끝났는지 확인하는 60초’를 바꾸는 것이 아니라 ‘안끝났을 때 기다리는 n초’입니다. 최대 얼마까지 가능한지는 문서에 나와있지 않지만 10분은 넘는 것으로 압니다.

Continuous-mode WebJob의 실행 허용시간은 딱히 제한은 없습니다. 무한루프로 구현되기 때문이지요. 다만 어떠한 이유로 중지시킬 때는 미리 알려주는 장치가 있습니다.

WebJob의 환경변수인 WEBJOBS_SHUTDOWN_FILE에 지정된 파일을 작성하는 것이지요. WebJob이 강제종료하기 전에 파일이 생성되고, 우리가 만든 무한루프 로직은 이 파일이 생성되었는지 FileWatcher를 통해 확인한 후 무한루프를 종료하는 방식으로 작성하면 보다 안정적인 WebJob을 구현할 수 있습니다.

Azure 개발팀에 있는 Amit Apple 님의 코드를 빌려오자면 아래와 같습니다.

public class Program
{
    private static bool _running = true;
    private static string _shutdownFile;

    private static void Main(string[] args)
    {
        // Get the shutdown file path from the environment
        _shutdownFile = Environment.GetEnvironmentVariable("WEBJOBS_SHUTDOWN_FILE");

        // Setup a file system watcher on that file's directory to know when the file is created
        var fileSystemWatcher = new FileSystemWatcher(Path.GetDirectoryName(_shutdownFile));
        fileSystemWatcher.Created += OnChanged;
        fileSystemWatcher.Changed += OnChanged;
        fileSystemWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.FileName | NotifyFilters.LastWrite;
        fileSystemWatcher.IncludeSubdirectories = false;
        fileSystemWatcher.EnableRaisingEvents = true;

        // Run as long as we didn't get a shutdown notification
        while (_running)
        {
            // Here is my actual work
            Console.WriteLine("Running and waiting " + DateTime.UtcNow);
            Thread.Sleep(1000);
        }

        Console.WriteLine("Stopped " + DateTime.UtcNow);
    }

    private static void OnChanged(object sender, FileSystemEventArgs e)
    {
        if (e.FullPath.IndexOf(Path.GetFileName(_shutdownFile), StringComparison.OrdinalIgnoreCase) >= 0)
        {
            // Found the file mark this WebJob as finished
            _running = false;
        }
    }
}

더 자세한 내용은 WebJob을 개발하고 있는 분의 블로그를 확인해보세요.
http://blog.amitapple.com/post/2014/05/webjobs-graceful-shutdown/#.VNqThvmG8oc

추가로, 아래 링크에서 WebJob을 모니터링하는 방법에 대하여 알려줍니다.

http://jasonhaley.com/post/Monitor-the-Status-of-an-Azure-WebJob

4. Continuous WebJob 실행주기를 설정하고 싶어요.

원문링크: http://stackoverflow.com/q/29625813/361100

기본적으로 Continuous WebJob의 인터벌은 5초입니다. 이를 변경하는 방법이 나와있습니다.

사실 이것을 구현하기 위한 방법은 세가지가 있습니다.

  1. 제가 즐겨쓰는건데, Azure Scheduler를 이용해서 WebApp에서 GET을 불러주는겁니다. 네, WebJob을 쓰지 않는거죠.
  2. 전통적인 Thread.Sleep이나 Task.Delay를 쓰는 경우 (링크의 채택된 답변)
  3. WebJob Timer Extention을 이용하는 경우: http://stackoverflow.com/a/34963841/361100 사실 2번 방법인 Thread.Sleep은 기계어 실행을 중지하고 CPU 카운트만 돌리는 것이므로 의도한 지연 실행과는 다른 의미라고 할 수 있습니다. ‘결과가 같은거니 그냥 쓴다’는 셈이죠. 그러므로 WebJob을 위해 만들어진 이 방법을 추천합니다.

YJ

금주의 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에서 처리되는 진행률을 표시하는 샘플도 있는데, 저는 이것을 보고 ‘아 이렇게 구현할 수도 있구나’ 했습니다.

TeamCity에서 Azure WebJobs Deploy하기

Azure WebJobs는 Azure WebSites(이하 WebSites)를 이용한 백그라운드 워커입니다. WebSites의 설정에 종속적으로 구동되며 이로 인해 뚜렷한 장단점을 가집니다.

장점

  • 배포가 매우 빠르고 쉬움.
  • 다양한 언어(.net, .py, .sh 등)를 지원.

단점

  • 완벽히 신뢰하기엔 모호한 동작.
  • WebSites와의 자원 공유로 성능 저하.

추천하는 사용법은, 초기엔 가볍고 정확함이 크게 필요 없는 WebJobs로 빠르게 테스트하고, 중기에 보다 견고한 동작을 필요로 하면 WebRole로 이전하는 것입니다. 그리고 마지막에 로직이 완숙해지면(그럴 시기는 안오겠지만ㅋㅋ) 로그 등 상태확인 기능을 보다 충실히 넣고 WebJobs로 다시 빠른 배포의 장점을 얻는 것입니다.

이 글은 TeamCity CI(Continuous Integration)를 이용한 Azure WebJobs 배포 방법을 적었으며, 이전에 작성했던 TeamCity에서 Azure WebSites Deploy하기 글이 많은 도움이 되었습니다.

Azure WebJobs를 TeamCity로 배포하기 위해서는 다음의 과정을 거칩니다.

  1. Visual Studio에서 Azure 배포 프로필 생성
  2. Azure WebSites Deploy 비밀번호 가져오기
  3. 소스콘트롤에서 Pull
  4. Nuget Restore
  5. MSBuild 실행

1. Visual Studio에서 Azure 배포 프로필 생성

먼저 Visual Studio의 프로젝트에서 우클릭을 하여 GUI를 이용한 WebJobs로 배포를 합니다. 이를 통해 프로필이 생성될 것입니다.

Azure WebJob 배포는 이 메뉴를 누릅니다
Azure WebJob 배포는 이 메뉴를 누릅니다

위의 과정을 거쳐서 WebJobs를 성공적으로 배포하면, 프로젝트 아래 pubxml 파일이 생성됩니다.

이처럼 프로필 이름과 동일한 pubxml 파일이 만들어집니다
이처럼 프로필 이름과 동일한 pubxml 파일이 만들어집니다

2. Azure WebSites Deploy 비밀번호 가져오기

Azure 포털에 들어가서 배포한 WebJobs가 정상동작하는지 확인했으면 이제 TeamCity를 이용하여 자동화시켜도 될 것입니다.

먼저 배포용 비밀번호를 Azure 포털에서 다운받습니다.

배포용 Credential 파일 다운로드
배포용 Credential 파일 다운로드

그 다음, 다운 받은 PublishSettings 파일을 열어서 userPWD=”etxffzgX4wML0RQ43R2KR87pemoySuJva7Cs”와 같은 부분이 있는데 따옴표 안의 내용을 복사합니다. 복사한 내용은 나중에 사용되니 잠시 메모장에 붙여넣기로 보관해주세요.

3. 소스콘트롤에서 Pull

TeamCity에서 Version Control Settings 메뉴를 이용하여 소스콘트롤에서 다운로드 받는 설정을 합니다. 이것은 어렵지 않으니 생략하겠습니다. 저희는 GitHub Private Repository를 이용합니다. 저희는 Visual Studio 2013을 이용하여 개발된 닷넷 프로젝트 하나를 굽게 할 것입니다.

4. NuGet Restore

닷넷 세상의 maven이라 할 수 있는 NuGet을 안쓰는 프로젝트가 거의 없죠? TeamCity는 NuGet 지원이 좋아서 NuGet Restore도 간편하고, 그 자체로 NuGet 서버 기능을 할 뿐 아니라 NuGet.org 서버에 Push하는 기능까지 이미 탑재하고 있습니다.

주의: 만일 TeamCity에 NuGet 설정이 되어있지 않다면 TeamCity 우상단에 Administration을 클릭한 후, Integration > NuGet > NuGet.exe 탭에서 “Fetch NuGet”을 설정해주세요.

Build Steps 메뉴에서 Build Step 하나를 추가합니다. 우리는 소스콘트롤에서 다운 받은 폴더의 솔루션파일(.sln)을 지정하고 nuget restore를 할 것입니다. 설정은 다음과 같이 해주세요.

  • Runner Type: NuGet Installer
  • Nuget.exe: Default
  • Path to Solution File: 우측 트리 아이콘을 눌러서 .sln 파일을 지정해주세요. 소스콘트롤 셋팅이 이미 되었다면 다운로드 받은 폴더 구조가 나올 것입니다.
  • Restore Mode: Restore
  • Update Mode: Update via packages.config file

5. MSBuild

이제 Build하고 그 결과를 WebJobs에 배포합니다. 새 Build Step을 생성하고 아래와 같이 지정해주세요.

  • Runner Type: MSBuild
  • Build file path: .csproj 경로 (.sln 아님)
  • MSBuild version: Microsoft Bulid Tools 2013
  • MSBuild ToolsVersion: 12.0
  • Command line parameters: 아래 소스 참고

아래는 읽기편하게 줄바꿈을 했지만 설정시 꼭 한 줄로 적어주세요.

/p:Configuration=Release 
/p:DeployOnBuild=True 
/p:PublishProfile="bapul-cube" 
/p:ProfileTransformWebConfigEnabled=False 
/p:AllowUntrustedCertificate=true 
/p:_DestinationType=AzureWebSite 
/p:Password=etxffzgX4wML0RQ43R2KR87pemoySuJva7Cs

여기까지 하면 빌드 후 바로 WebJobs에 배포하는데요, 위의 /p: 부분에서 몇가지만 풀어보자면 아래와 같습니다.

  • Configuration=Release : 이건 쉽죠. Release 설정으로 빌드하겠다는겁니다.
  • DeployOnBuild=True : 말그대로 빌드 후 바로 배포 스크립트를 실행하는겁니다.
  • PublishProfile=”bapul-cube”: pubxml파일, 즉 Azure WebJobs 배포 프로필 이름입니다.
  • Password: publishsettings 파일에서 복사한 내용을 붙여넣어주세요.

여기까지가 끝입니다.
이제 코딩하고 Git push만 하면 TeamCity가 자동으로 배포까지 해줄 것입니다 🙂

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

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

Azure WebJob

테크데이즈2014에서 발표한 발표자료 및 데모 소스입니다.

Azure WebSites의 부가기능인 Azure WebJobs를 사용하는 방법과 그 예시입니다. 데모는 YouTube 링크에서 썸네일 이미지를 가져오고, 썸네일을 합성한 후 blob storage에 저장합니다.

10660249_706337572754052_5073071700168196147_n

좋은 기회를 제공해주신 마이크로소프트의 많은 관계자 분들에게 감사의 마음을 드립니다.