9장 Release Server

이재홍 http://www.pyrasis.com 2007.10.27 ~ 2008.04.20

목차

빌드 서버 설정

빌드가 끝난 결과물을 보관하고 배포할 때 릴리스 서버를 사용합니다. 릴리스 서버라고 해서 특별한 프로그램이 필요한 것은 아니고 웹, FTP, Subversion 등으로 파일을 배포하는 것입니다. 빌드 서버는 회사 내부에 존재하고 있을 것이고, 이 빌드 서버에서 빌드된 파일을 직접 고객에게 전달하는 일은 거의 드물 것입니다. 이 릴리스 서버의 주된 사용자는 제품 테스트 부서입니다. 즉 릴리스 서버의 역할은 개발 부서에서 테스트 부서로 파일을 전달하는 것입니다.

CruiseControl.NET의 buildpublisher 기능을 이용하여 빌드된 파일을 버전별로 보관할 수 있습니다. 빌드 서버와 릴리스 서버는 같은 컴퓨터 상에 설정될 수도 있고, 다른 컴퓨터에 나뉘어져서 설정될 수도 있습니다. 같은 컴퓨터상에 설정되어 있다면 buildpublisher 기능으로 파일을 복사하면 되고, 다른 컴퓨터상에 설정되어 있다해도, 공유 폴더와 네트워크 드라이브 설정을 이용하여 빌드된 파일을 릴리스 서버에 보관할 수 있습니다.

CruiseControl.NET에서 빌드된 결과물을 buildpublisher 기능으로 특정 디렉터리에 복사를 하도록 설정하겠습니다. 복사된 디렉터리의 파일을 웹, FTP 등을 통하여 배포하는 것입니다.

<buildpublisher>는 아래와 같이 사용합니다. <sourceDir>은 빌드 된 파일이 생성되는 디렉터리입니다. Visual Studio에서 Release 모드로 빌드했다면, 프로젝트 디렉터리 아래에 release 디렉터리가 만들어지고, 이 디렉터리에 빌드된 실행 파일과 심볼 파일이 있을 것입니다. <publishDir>은 빌드된 파일을 복사할 디렉터리입니다. 이번 예제에서는 C:\Releases 아래에 복사하겠습니다. 주의할 점은 프로젝트별로 구분을 해주어야 한다는 것입니다. 그래서 C:\Release\example이 됩니다. \fileserver\example와 같이 다른 컴퓨터에 있는 공유 폴더를 직접 지정해 줄 수도 있습니다. <useLabelSubDirectory>는 true로 설정합니다. true로 설정하면 C:\Release\example 디렉터리 아래에 각 버전별로 디렉터리를 자동으로 만들어 주고 그 아래에 파일들이 복사됩니다. false로 설정하면, C:\Release\example 아래에 빌드된 파일이 계속 덮어쓰기가 됩니다.

<buildpublisher>
    <sourceDir>C:\build\example\release</sourceDir>
    <publishDir>C:\Releases\example</publishDir>
    <useLabelSubDirectory>true</useLabelSubDirectory>
</buildpublisher>

C:\Program Files\CruiseControl.NET\server\ccnet.config

<cruisecontrol>
    <project name="example">
        <!-- example 프로젝트의 Web Dashboard 주소 -->
        <webURL>http://192.168.1.100:8000/ccnet/server/local/project/example/ViewProjectReport.aspx</webURL>

        <!-- 기본 레이블로 지정 -->
        <labeller type="defaultlabeller">
            <prefix>1.0.0.</prefix>
        </labeller>

        <!-- Subversion 저장소 주소 및 작업 디렉터리 설정 -->
        <sourcecontrol type="svn">
            <trunkUrl>http://192.168.1.100/svn/example/trunk</trunkUrl>
            <workingDirectory>c:\build\example</workingDirectory>
        </sourcecontrol>

        <!-- 매일 20:00에 자동 빌드 하도록 설정 -->
        <triggers>
            <scheduleTrigger time="20:00" buildCondition="ForceBuild" name="Scheduled" />
        </triggers>

        <tasks>
            <!-- 자동 버전 업데이트 스크립트 -->
            <exec>
                <executable>powershell.exe</executable>
                <baseDirectory>C:\tools</baseDirectory>
                <buildArgs>C:\tools\rcversion.ps1 "example" "C:\build\example\example\example.rc"</buildArgs>
            </exec>

            <!-- Visual Studio 2005 빌드 설정 -->
            <devenv>
                <solutionfile>C:\build\example\example.sln</solutionfile>
                <configuration>Release</configuration>
            </devenv>

            <!-- 수정 된 소스 코드를 원래대로 되돌림 -->
            <exec>
                <executable>powershell.exe</executable>
                <baseDirectory>C:\tools</baseDirectory>
                <buildArgs>C:\tools\revert.ps1 "C:\build\example"</buildArgs>
            </exec>

            <!-- trac에 버전을 등록 -->
            <exec>
                <executable>powershell.exe</executable>
                <baseDirectory>C:\tools</baseDirectory>
                <buildArgs>C:\tools\tracversion.ps1 "example" "example"</buildArgs>
            </exec>

            <!-- PDB 파일에 Subversion 저장소 정보를 인덱싱 하고, 압축 한 뒤 심볼 서버에 등록 -->
            <exec>
                <executable>powershell.exe</executable>
                <baseDirectory>C:\tools</baseDirectory>
                <buildArgs>C:\tools\symbols.ps1 C:\build\example C:\build\example\release example</buildArgs>
                <buildTimeoutSeconds>10</buildTimeoutSeconds>
            </exec>

            <!-- 빌드된 결과물을 Releases 디렉터리 아래 각 프로젝트 디렉터리로 복사 -->
            <buildpublisher>
                <sourceDir>C:\build\example\release</sourceDir>
                <publishDir>C:\Releases\example</publishDir>
                <useLabelSubDirectory>true</useLabelSubDirectory>
            </buildpublisher>
        </tasks>
    </project>
</cruisecontrol>

빌드를 하게 되면 [그림 9-1]과 같이 C:\Releases\example 디렉터리 아래에 각 버전별 디렉터리가 생기는 것을 볼 수 있습니다. 각 버전별 디렉터리에 빌드된 실행 파일과 심볼(PDB) 파일이 들어있습니다.


그림 9-1 버전별로 생성된 디렉터리들

웹을 통한 배포

빌드된 파일을 IIS 웹 서버로 배포할 수 있도록 설정하겠습니다. IIS 웹 서버의 가상 디렉터리와 디렉터리 검색 기능을 이용하여 설정하는 것입니다.

  1. IIS 서버의 포트를 바꾸려면 제어판 → 관리 도구 → 인터넷 정보 서비스를 실행합니다. 기본 웹 사이트를 선택하고 마우스 오른쪽 버튼을 누르면 팝업 메뉴가 나옵니다. 새로 만들기(N) → 가상 디렉터리(V)를 선택합니다.


    그림 9-2 기본 웹 사이트에 가상 디렉터리 생성

  2. 가상 디렉터리 만들기 마법사가 실행되면 다음을 눌러 넘어갑니다.


    그림 9-3 가상 디렉터리 만들기 마법사

  3. 별칭(A)에는 release를 입력합니다. 여기에 입력한 것이 접근 URL이 됩니다. (http://localhost/release)


    그림 9-4 가상 디렉터리 별칭

  4. 찾아보기 버튼을 누르고 C:\Releases 디렉터리를 선택합니다.


    그림 9-5 웹 사이트 컨텐트 디렉터리

  5. 액세스 권한에서는 찾아보기(O)에 꼭 체크를 합니다. 이 부분을 체크해야 디렉터리 안의 파일들을 웹에서 볼 수 있습니다.


    그림 9-6 액세스 권한

  6. 마침을 눌러 설정을 끝냅니다.


    그림 9-7 가상 디렉터리 생성 완료

TIP
Trac 서버와 릴리스 서버가 같은 컴퓨터이고, Trac 서버가 http로 작동 중일 때에는 IIS 웹 서버의 포트를 바꾸어 주어야 합니다. Apache, IIS 웹 서버 모두 80번 포트를 사용하기 때문입니다. 단 Trac 서버가 https(443번 포트)로 작동 중일 때에는 IIS 웹 서버의 포트를 바꿀 필요는 없습니다.
  1. IIS 서버의 포트를 바꾸려면, 아래와 같이 기본 웹 사이트를 선택하고 마우스 오른쪽 버튼을 누른 뒤 속성을 선택합니다.


    그림 9-8 인터넷 정보 서비스의 기본 웹사이트

  2. TCP 포트(T)에 8000을 입력합니다. 꼭 8000이 아니라도 됩니다. 80번만 아니면 됩니다.


    그림 9-9 포트 설정

  3. 이제 웹 브라우저에서 http://192.168.1.100:8000/release에 접속합니다. 포트를 바꾸지 않았다면 http://192.168.1.100/release에 접속하면 example 프로젝트가 보일 것이고, example을 클릭하면 아래와 같이 빌드된 각 버전들의 디렉터리를 볼 수 있습니다.


    그림 9-10 가상 디렉터리로 설정한 release로 접속한 화면

  4. 각 버전을 클릭하면 빌드된 example.exe, example.pdb를 볼 수 있고, 각 파일을 클릭하면 파일을 받을 수 있습니다.


    그림 9-11 버전을 클릭한 화면

TIP
Internet Explorer에서 URL을 입력할 때 http://는 꼭 입력하기 바랍니다.

FTP를 통한 배포

이번에는 웹이 아닌 FTP로 배포하는 것을 설정해 보겠습니다. FTP 서버도 IIS를 이용하겠습니다. 먼저 IIS FTP 서버를 설치합니다.

  1. 제어판 → 프로그램 추가/제거 → Windows 구성 요소 추가/제거 → 인터넷 정보 서비스(IIS)를 더블 클릭하고 File Transfer Protocol(FTP) 서비스를 선택하여, FTP 서버를 설치합니다.


    그림 9-12 구성 요소 마법사 → 인터넷 정보 서비스(IIS)에서 File Transfer Protocol(FTP) 서비스 선택

  2. FTP 서버를 설치하고 관리 도구 → 인터넷 정보 서비스를 실행합니다. FTP 사이트가 표시되지 않으면 인터넷 정보 서비스 창을 종료 한 뒤 다시 실행합니다. 기본 FTP 사이트를 선택하고 마우스 오른쪽 버튼을 누른 다음 새로 만들기(N) → 가상 디렉터리(V)를 선택합니다.


    그림 9-13 기본 FTP 사이트에 가상 디렉터리 생성

  3. 가상 디렉터리 만들기 마법사가 실행되면 다음을 눌러 넘어갑니다.


    그림 9-14 가상 디렉터리 생성 마법사

  4. 별칭(A)에는 release를 입력합니다.


    그림 9-15 가상 디렉터리 별칭

  5. 찾아보기 버튼을 누르고 C:\Releases 디렉터리를 선택하여 컨텐트가 있는 폴더 경로를 설정합니다.


    그림 9-16 FTP 사이트 컨텐트 디렉터리

  6. 가상 디렉터리에 설정할 액세스 권한에서는 기본 설정대로 읽기(R)만 체크합니다.


    그림 9-17 액세스 권한

  7. 가상 디렉터리 만들기를 완료했으면 마침을 눌러 설정을 끝냅니다.


    그림 9-18 가상 디렉터리 생성 완료

  8. 탐색기에서 ftp://192.168.1.100/release에 접속하면 각 프로젝트 디렉터리가 표시될 것입니다. 그리고 각 프로젝트 디렉터리를 클릭하면 아래와 같이 각 버전별 디렉터리를 볼 수 있습니다. 물론 탐색기가 아닌 FTP 클라이언트를 사용해도 됩니다.


    그림 9-19 가상 디렉터리로 설정한 release에 접속한 화면

  9. 각 버전을 클릭하면 빌드된 example.exe, example.pdb를 볼 수 있고, 각 파일을 받을 수 있습니다.


    그림 9-20 버전을 클릭한 화면

Trac을 통한 배포

이번 방식은 좀 특이한 방식입니다. 파일을 Subversion 저장소에 저장하고 Trac의 소스 브라우저에서 파일을 받아가는 방식입니다. 여기에 부가적으로 각 버전별 파일에 커밋 로그를 표시해 줄 수 있습니다. release 프로젝트를 만듭니다. 물론 Trac 프로젝트와 Subversion 저장소가 동시에 필요합니다. Windows PowerShell을 실행하고 아래와 같이 입력합니다.

C:\tools\create-project.ps1 release


그림 9-21 release Subversion 저장소 및 Trac 프로젝트 생성

이제 release 프로젝트가 만들어졌습니다. 탐색기를 열고 C:\Releases 디렉터리로 이동합니다. 이전에 빌드한 결과물은 모두 삭제합니다. 그리고 체크아웃을 선택합니다.


그림 9-22 체크아웃 실행

저장소 URL을 http://192.168.1.100/svn/release로 지정합니다.


그림 9-23 release 저장소를 체크아웃

저장소 URL 부분의 … 버튼을 눌러 저장소 브라우저를 실행합니다. 그리고 branches, tags, trunk 3개의 디렉터리를 모두 삭제합니다. 각 디렉터리를 선택하고 마우스 오른쪽 버튼을 눌러 팝업 메뉴를 띄운 다음 삭제(D)를 누르면 됩니다.


그림 9-24 trunk, branches, tags 디렉터리 삭제

디렉터리를 모두 삭제하고 확인을 누릅니다.


그림 9-25 모든 디렉터리가 삭제된 화면

이제 확인을 눌러 체크아웃을 시작합니다.


그림 9-26 체크아웃 시작

TIP
체크아웃이 되지 않을 경우 C:\Repos\authz 파일에 release 저장소의 권한을 지정했는지 확인합니다.
  • 다운로드 주소 : https://github.com/pyrasis/windowsprojectbook/blob/master/Scripts/PowerShell/release.ps1
  • 부록 CD 경로 : Scripts/PowerShell/

release.ps1

# release.ps1 <Release Path> <Source Path>

$release = $args[0]
$source = $args[1]
$logfile = "C:\Windows\temp\log.xml"

# $source 디렉터리의 마지막 커밋 로그를 $logfile로 저장
svn log $source -r COMMITTED > $logfile
$log = get-content -path $logfile

# trac에서 로그 메시지를 정상적으로 보여주기 위해 줄바꿈이 될 때 [[BR]] 추가.
# 단 ---는 통과
for ($i = 1; $i -lt 128; $i++)
{
    if ($log[$i] -match "---") {break}

    $message += "[[BR]]" + $log[$i]
}

# 로그 내용에서 " 과 ' 삭제
# 로그 내용에 " 과 '이 있으면 커맨드 라인에서는 경로로 인식하여 커밋이 정상적으로 되지 않음.
$message = $message.replace("`"", "")
$message = $message.replace("`'", "")

# 빌드된 파일을 추가한 뒤 커밋, 사용자명과 암호를 지정해야 함. 
svn add $release --force
svn ci $release -m "$message" --username sampleuser --password abcd1234 --no-auth-cache

# 임시 저장한 로그 파일 삭제.
del $logfile

# $source 디렉터리를 원래대로 되돌림.
C:\tools\revert.ps1 $source

svn ci $release -m “$message” –username sampleuser –password abcd1234 –no-auth-cache 부분에서 sampleuser는 빌드된 파일을 추가할 사용자 ID이며 abcd1234는 해당 사용자의 암호입니다(한 줄로 입력해야 합니다).

release.ps1 스크립트는 CruiseControl.NET에서 빌드된 파일을 release 저장소에 추가해줍니다. 덧붙여 빌드한 소스의 마지막 커밋 로그 메시지를 가져와서 빌드된 파일을 추가할 때 로그 메시지로 사용합니다. 로그 메시지에 [[BR]]을 추가하는 이유는 Trac 모든 텍스트를 위키 문법으로 처리하기 때문입니다. 즉 위키 문법에서 한줄 줄바꿈은 두 줄로 인정되지 않고 한 줄로 처리됩니다. 따라서 [[BR]] 문법으로 강제 줄바꿈을 해주는 것입니다.

또한 로그 메시지에서 “ 와 ‘를 삭제하는 이유는 svn ci 명령에서 -m 옵션으로 로그 메시지를 지정하는데, 이때 svn ci가 실행되는 상태가 커맨드 라인(명령 프롬프트)상태라 “ 와 ‘를 경로로 인식하여 커밋이 정상적으로 되지 않기 때문입니다.

release.ps1 스크립트를 C:\tools로 복사하여 사용합니다. 이 스크립트는 release.ps1 “릴리스 디렉터리” “빌드한 소스의 경로” 형식으로 사용합니다. 이제 빌드 스크립트에도 적용을 합니다.

C:\Program Files\CruiseControl.NET\server\ccnet.config

<cruisecontrol>
    <!-- 순차적 빌드를 위해 buildserver로 queue를 지정 -->
    <project name="example" queue="buildserver">
        <!-- example 프로젝트의 Web Dashboard 주소 -->
        <webURL>http://192.168.1.100:8000/ccnet/server/local/project/example/ViewProjectReport.aspx</webURL>

        <!-- 기본 레이블로 지정 -->
        <labeller type="defaultlabeller">
            <prefix>1.0.0.</prefix>
        </labeller>

        <!-- Subversion 저장소 주소 및 작업 디렉터리 설정 -->
        <sourcecontrol type="svn">
            <trunkUrl>http://192.168.1.100/svn/example/trunk</trunkUrl>
            <workingDirectory>c:\build\example</workingDirectory>
        </sourcecontrol>

        <!-- 매일 20:00에 자동 빌드 하도록 설정 -->
        <triggers>
            <scheduleTrigger time="20:00" buildCondition="ForceBuild" name="Scheduled" />
        </triggers>

        <tasks>
            <!-- 자동 버전 업데이트 스크립트 -->
            <exec>
                <executable>powershell.exe</executable>
                <baseDirectory>C:\tools</baseDirectory>
                <buildArgs>C:\tools\rcversion.ps1 "example" "C:\build\example\example\example.rc"</buildArgs>
            </exec>

            <!-- Visual Studio 2005 빌드 설정 -->
            <devenv>
                <solutionfile>C:\build\example\example.sln</solutionfile>
                <configuration>Release</configuration>
            </devenv>

            <!-- 수정 된 소스 코드를 원래대로 되돌림 -->
            <exec>
                <executable>powershell.exe</executable>
                <baseDirectory>C:\tools</baseDirectory>
                <buildArgs>C:\tools\revert.ps1 "C:\build\example"</buildArgs>
            </exec>

            <!-- trac에 버전을 등록 -->
            <exec>
                <executable>powershell.exe</executable>
                <baseDirectory>C:\tools</baseDirectory>
                <buildArgs>C:\tools\tracversion.ps1 "example" "example"</buildArgs>
            </exec>

            <!-- PDB 파일에 Subversion 저장소 정보를 인덱싱 하고, 압축 한 뒤 심볼 서버에 등록 -->
            <exec>
                <executable>powershell.exe</executable>
                <baseDirectory>C:\tools</baseDirectory>
                <buildArgs>C:\tools\symbols.ps1 C:\build\example C:\build\example\release example</buildArgs>
                <buildTimeoutSeconds>10</buildTimeoutSeconds>
            </exec>

            <!-- 빌드된 결과물을 Releases 디렉터리 아래 각 프로젝트 디렉터리로 복사 -->
            <buildpublisher>
                <sourceDir>C:\build\example\release</sourceDir>
                <publishDir>C:\Releases\example</publishDir>
                <useLabelSubDirectory>true</useLabelSubDirectory>
            </buildpublisher>

            <!-- 빌드된 결과물을 Release 저장소로 커밋 -->
            <exec>
                <executable>powershell.exe</executable>
                <baseDirectory>C:\tools</baseDirectory>
                <buildArgs>C:\tools\release.ps1 C:\Releases\example C:\build\example</buildArgs>
            </exec>
        </tasks>
    </project>
</cruisecontrol>

여기서 중요한 부분은 반드시 모든 프로젝트 설정에 <project name="example" queue="buildserver"> 처럼 queue="buildserver"로 설정되어 있어야 한다는 것입니다. CruiseControl.NET에서 <project>에 queue가 설정되어 있지 않으면, 지정된 시간에 빌드가 될 때나 Force Build를 했을 때 동시에 여러 프로젝트를 빌드하게 됩니다. 이렇게 되면 컴파일에는 문제가 없지만 컴파일된 파일을 Subversion 저장소에 커밋할 때 문제가 발생하게 됩니다. 즉 동시에 커밋을 진행할 때 그중 하나의 프로젝트만 커밋이 성공하고 나머지는 실패하게 됩니다.

따라서 이런 문제를 방지하기 위해서는 queue를 지정하여 순차적으로 빌드해야 합니다. 즉 여러 프로젝트에 Force Build 명령을 내리더라도 먼저 명령을 내린 프로젝트부터 순서대로 빌드가 됩니다. 지정된 시간에 빌드하는 것도 순차적으로 빌드 됩니다. queue를 지정할 때 어느 프로젝트는 queue="build1"이고 어느 프로젝트는 queue="build2"처럼 해서는 안됩니다. 이렇게 되면 queue를 설정하지 않은 것과 마찬가지로 동시에 빌드가 됩니다. 모든 프로젝트에 설정되는 queue의 이름은 동일해야 합니다.

한가지 참고할 사항은 여러 프로젝트를 동시에 빌드를 한다고 해도 순차적으로 빌드하는 것과 빌드 되는 속도는 동일합니다. 여러 프로젝트를 동시에 빌드하면 그만큼 속도는 느려지겠죠.

TIP
release.ps1을 사용하지 않고 웹이나 FTP를 통해 배포할 때에는 queue를 지정할 필요는 없습니다.

빌드를 하기전 C:\Program Files\CruiseControl.NET\server 디렉터리의 Example.state 파일을 삭제합니다. 이전에 빌드했던 것을 무시하고 새로 시작하기 위해서 입니다.

이제 빌드를 하고 웹 브라우저에서 http://192.168.1.100/trac/release로 접속합니다. 그리고 Browse Source 메뉴를 보면 빌드한 프로젝트가 표시되고, 해당 프로젝트를 클릭하면 아래와 같이 빌드된 버전들이 표시됩니다.


그림 9-27 프로젝트의 Browse Source 페이지

각 버전으로 들어가면 빌드된 파일을 확인할 수 있습니다.


그림 9-28 버전을 클릭한 화면

파일을 클릭하면 해당 버전을 빌드했을 때의 커밋 로그 메시지를 볼 수 있습니다. 그리고 downloading 링크를 누르면 파일을 받을 수 있습니다.


그림 9-29 빌드된 파일의 로그 메시지

어느날 필자가 관리하고 있는 파일에 버그가 있다는 보고를 받았습니다. 별로 특별할 것이 없는 WinAPI인 ShellExecute라는 함수가 실행되지 않고 계속 실패해서 발생한 문제였습니다. 소스 코드에는 아무 이상이 없었고, Subversion 로그를 조회하여 지금까지 있었던 코드 변화를 살펴보아도 달라진 것은 없었습니다.

어느 버전부터 문제가 생겼는지 알아내기 위해 하는 수 없이 릴리스 서버에 올라와 있는 20여개의 버전을 하나 하나 실행해보기 시작했습니다. 아래 위로 하나씩 범위를 좁혀가던 중 9번째 버전까지는 실행이 잘 되는데 그 다음 버전의 파일은 버그가 있다는 것을 발견하게 되었습니다. 문제가 최초로 발생한 버전이 빌드된 날짜를 알아내어 Subversion 로그를 조회해본 결과 소스 코드상의 변화는 없었지만, 컴파일 옵션의 최적화 옵션이 달라져 있었습니다. 결국 필자는 최적화 옵션을 사용하지 않도록 하여 문제를 해결하였습니다.

이렇게 소스 코드로는 원인을 알기 힘든 버그를 해결할 때 릴리스 서버가 큰 도움됩니다.


저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.