허드슨, 지속적인 통합 도구

프로젝트를 진행할 때 업무 단위별로 팀을 나눠서 개발을 하고 난 뒤 오픈 한 달 전부터 코드를 통합하는 작업을 따로 했던 시기가 있었습니다. 이때 팀 간의 인터페이스가 맞지 않아서 이를 수정해서 맞추는 작업에 굉장히 많은 노력과 시간이 들었습니다.
전체 프로젝트 팀의 소스를 매일 매일 합쳐서 큰 문제가 생기기 전에 매일매일 소스를 빌드하는 방법이 가능해졌습니다. CVS, SVN과 같은 버전관리 시스템을 적용하고, 이 장에서 설명할 허드슨과 같은 지속적인 통합(Continuous Integration) 도구를 사용하면 손쉽게 프로젝트를 관리할 수 있습니다.

허드슨은 http://hudson-ci.org 사이트에서 정보를 얻을 수 있습니다. 오픈소스로 태어났지만 썬을 인수한 오라클과의 운영 이슈로 허드슨의 원 개발자가 자바닷넷(http://java.net)을 떠나 깃허브(https://github.com/jenkins)로 자리를 옮겨서 동일한 제품을 만들었습니다. 젠킨스(Jenkins)이며 http://jenkins-ci.org 사이트를 통해서 공개되고 있습니다. 두 제품의 사용법은 동일합니다.

표준프레임워크 개발환경 서버를 설치하면 톰캣이 설치됩니다. 톰캣의 webapps/ 폴더에 hudson.war 파일이 위치합니다. 톰캣이 실행되면 자동으로 압축이 풀리면서 hudson 사이트가 준비됩니다.

<그림> hudson.war 파일 위치


윈도우7에서는 서비스에 등록된 톰캣을 일반 cmd창에서 실행할 수 없습니다. 권한이 없기 때문이죠. cmd창을 관리자 모드로 실행하고, net start tomcat6 명령으로 실행할 수 있습니다. 만약 실행중이라면 "요청한 서비스가 이미 시작되었습니다."라는 메시지를 보게 될 것입니다.

<그림> 관리자 모드 톰캣 서비스 시작



브라우저를 통해서 http://localhost:7070 페이지로 접속하면 자동을 /hudson 컨텍스트로 이동합니다. 허드슨이 준비가 완료된 화면입니다. 왼쪽 메뉴에서 새 작업을 클릭합니다.

<그림> 허드슨 페이지 접속


새 작업 화면에서는 프로젝트명과 스타일을 정합니다. ok.egov 라고 프로젝트명을 입력합니다. 작업 스타일은 메이븐을 사용하기 때문에, 두 번째 항목인 Build a maven2/3 project 를 선택합니다. ok 버튼을 클릭하면 메이븐 기반의 프로젝트를 다루는 작업이 만들어집니다.

http://www.okjsp.pe.kr:8080/ 허드슨을 이용해서 사이트를 관리를 하고 있습니다. SVN에 소스를 커밋하면 1시간마다 또는 즉시 운영에 반영할 수 있는 시스템이고, 1시간마다 테스트케이스를 돌리기 때문에 언제 문제가 발생했는지 모니터링할 수 있는 지속적인 통합(Continuous Integration) 도구입니다.
관련해서 포스팅한 글도 조금 있지요. http://okjsp.tistory.com/tag/hudson

오늘 RSS를 통해서 본 것인데, 충격적이라 포스팅합니다.

허드슨은 http://hudson.dev.java.net 에서 오픈소스로 진화하고 있었는데, 이게 java.net을 떠나서 github로 둥지를 바꿨다고 합니다. http://www.infoq.com/news/2011/01/hudson-jenkins 
위 글에 따르면 oracle 관리 아래 java가 들어간 이후로 java.net의 대대적인 개편이 있었고, 이게 서비스를 마이너스로 만들었다고 합니다. 그래서 Github로 이전했다네요.

예전엔 http://hudson-ci.org/ 와 화면이 같았었습니다.


Oracle had applied for the Hudson trademark on October 29th, when the developers were in progress of moving the code off to GitHub.

from: http://www.infoq.com/news/2011/01/hudson-jenkins 
프로젝트 이름을 바꾸는 이유는 오라클이 Hudson으로 상표를 만들고 있는 것 같습니다. 으악~ 이거 뭐... 영어사전의 모든 단어로 상표권을 만들 기세입니다.

이름을 바꾸지 않고도 잘 해결되었으면 좋겠습니다. 혼동이 가중되지 않도록 말이죠.
개인적인 심정으로는 그냥 IBM이 Sun을 가져갔으면 더 발전적이지 않았을까 생각됩니다.

오라클은 나중에 영화나 미드 시리즈를 만들어도 흥행할 것 같습니다. 드라마를 쓰고 있네요. 파란만장 자바 이야기.

허드슨의 건승을 기원합니다.
JUnit을 이용한 테스트케이스를 실행한 결과를 api형태의 보고서로 또는 그래픽으로 비주얼하게 보여줄 수 있습니다.

http://www.okjsp.pe.kr/docs/report 에서 확인할 수 있습니다.

아울러 hudson에서도 다른 형태의 리포트가 나옵니다.
우측 상단의 파란색 그래프입니다.

더 자세한 정보도 볼 수 있습니다.

http://www.okjsp.pe.kr:8080/job/okjsp%20site%20build/

아주 오래 미뤄왔던 일을 해내었습니다. 이제 버그 잡아야겠습니다.
Hudson 메인 페이지의 Manage Hudson 메뉴로 이동하면 신규 버전 알림이 노란 줄로 뜹니다. download 링크를 클릭하면 바로 다운 받을 수 있죠. 그리고 플러그인의 업데이트는 Manage Plugins 설명에 (updates available) 이라고 빨간 글씨로 보입니다.


Manage Plugins로 가면 Updates 탭에 해당 플러그인이 보입니다. 체크하고 우측 하단의 Install 버튼을 클릭하면 업데이트가 진행됩니다. 

대체로 설치 후에 hudson을 재시작해줘야 됩니다.



그리고 다른 얘기인데, findbugs 플러그인에서 보여주듯이 okjsp사이트의 버그는 다 잡았습니다. 냐호~



hudson의 최상위 페이지의 메뉴 "Manage Hudson(Hudson의 관리)" 메뉴로 갑니다.

Manage Plugins 를 선택하고, Available탭에서 findbugs 플러그인을 설치합니다.

hudson을 콘솔에서 재시작하고, 프로젝트의 configure 메뉴에 가면 findbugs 활성화 체크박스가 하단에 보입니다. 체크하면 hudson에서 findbugs 플러그인이 활성화됩니다.

Build 섹션에서 Add Build Step버튼을 클릭해서 findbugs 빌드를 추가합니다. 빌드서버에 설치된 findbugs 경로를 findbugs.home 에 지정합니다. 환경변수에 $FINDBUGS_HOME으로 지정되면 이 과정이 필요없습니다.

build.xml 의 findbugs 타겟 소스는 참고로 다음과 같습니다.
    <taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"/>
   <target name="findbugs">
      <findbugs home="${findbugs.home}"
                output="xml:withMessages"
                outputFile="findbugs.xml" >
        <sourcePath path="${basedir}/src" />
        <class location="${publish.home}/WEB-INF/classes" />
      </findbugs>
    </target>

콘솔에 다음과 같은 메시지가 찍히면서 findbugs에 대한 보고서를 확인할 수 있습니다.

참조하는 jar파일의 경로가 지정되지 않으면 이런 메시지가 나타납니다.

 [findbugs] The following classes needed for analysis were missing:
[findbugs]   javax.servlet.http.HttpServlet
[findbugs]   javax.servlet.Servlet

findbugs 태스크에 다음과 같은 jar경로가 있는 줄을 추가하면 위 경고가 해결됩니다.
      <findbugs home="${findbugs.home}"
                output="xml:withMessages"
                outputFile="findbugs.xml" >
       <auxClasspath path="${catalina.home}/common/lib/servlet-api.jar" />
        <sourcePath path="${basedir}/src" />
        <class location="${publish.home}/WEB-INF/classes" />
      </findbugs>


버그의 추이가 그래프로 나타납니다.

버그의 자세한 리포트는 프로젝트 왼쪽 메뉴의 FindBugs Warnings 를 통해서 확인가능합니다.

이제 남은 일은 버그의 수를 줄이는 것이겠죠.
허드슨 강의하면서 배경지식으로 설명했던 지속적인 통합의 팀 적용에 관한 이야기입니다.

위 링크에 첨부파일로 올려놓았습니다.

관련 마인드맵은 이전 블로그에 올려놓았습니다.

Hudson을 이용한 프로젝트 모니터링

Continuous Integration in Practice


http://freemind.sf.net 형식의 파일입니다.

지속적인 통합 빌드에서 crontab 처럼 주기적으로 빌드가 돌게 할 수 있습니다. 아래는 10분 주기로 실행하도록 한 결과입니다. trend 링크를 누르면 다음과 같이 그래프로도 확인이 가능합니다.

빌드의 실행 시점은 다음과 같이 정할 수 있습니다. 우측의 ? 아이콘을 클릭하면 해당항목에 대한 설명이 보이게 됩니다. 빌드에는 분 시간 일 월 요일 5가지 설정을 하게 됩니다. 매 10분마다 설정하려면 0,10,20,30,40,50 처럼 공백없이 분을 나열하고 이후 4항목을 * * * * 로 채우면 됩니다.

빌드 로그가 많이 남을까봐 Discard Old Builds처럼 옛날 빌드기록을 삭제할 수 있습니다. 7일간 최대 200개의 빌드기록을 보관한다는 의미입니다.


소스코드를 가져오는 방법은 다음과 같습니다. CVSROOT를 적어주면 됩니다. 아이디와 패스워드가 노출되는데, 인트라넷용이라는 전제가 있습니다. 또한 빌드전용 CVS아이디를 만들어 주는 것도 괜찮을 것입니다.

간략하게 hudson의 설정에 대해서 살펴보았습니다.






지속적인 통합 툴인 Hudson(https://hudson.dev.java.net)은 기업 내부 즉 인트라넷용입니다. 회사 밖에서는 접근하지 않는다는 것을 기본으로 설정하게 되어있죠. 만약 인터넷 같은 외부에 노출이 되는 경우는 인증을 걸어서 아무나 컨트롤하지 못하도록 하는 것이 중요합니다.

구글에서 "hudson access control"이라고 검색해봤습니다. wiki페이지가 나오는군요.


첫번째 링크를 클릭해서 들어가면 Hudson 보안강화하기 위키페이지가 나옵니다.
http://hudson.gotdns.com/wiki/display/HUDSON/Securing+Hudson

보안 레벨을 다양하게 지원하는데, 토픽은 다음과 같습니다.

Topics


저는 간략하게 설정해 보았습니다. 아직 경험이 많지 않아서 성큼성큼 나아가기 힘드니까요.
Quick and Simple Security 페이지의 설명에서 발췌한 내용입니다.

$ java -jar hudson.war --argumentsRealm.passwd.user=password --argumentsRealm.roles.user=admin

이러한 형식으로 시작 옵션을 줍니다.

$ java -jar hudson.war --argumentsRealm.passwd.kenu=okjsp --argumentsRealm.roles.kenu=admin

user와 password 부분을 변경해 주면 됩니다. 그리고 Hudson 설정에서 보안을 사용한다고 설정해야 합니다.
Hudson 관리(Manage Hudson) > Configure System 메뉴를 선택합니다.

설정화면에서 위쪽 설정 옵션 중 Enable security를 체크합니다. 하단의 옵션은 Delegate  to servlet container 에 그 아래 옵션은 Legacy Mode 를 선택하고 저장하면 됩니다.


설정이 완료되고 옵션을 주어서 시작하면 다음과 같은 메시지를 볼 수 있습니다.


로그인 id별  권한처리도 가능하다 하니 위키 문서를 참고하시면 될 것입니다.

최신버전에 대한 permalink 가 있군요.
http://hudson.gotdns.com/latest/hudson.war 
현재 1.260 버전이 최신입니다.


wget으로 받으려했는데, https로 포워딩되는군요. 바뀌 주소에 --no-check-certificate 옵션을 주어서 받았습니다.

[hudson@169s /home/hudson]# ls -altr
합계 20
-rw-r--r--    1 hudson   hudson        411 11월 10 22:03 .bashrc
-rw-r--r--    1 hudson   hudson        250 11월 10 22:03 .bash_profile
-rw-r--r--    1 hudson   hudson         25 11월 10 22:03 .bash_logout
drwxr-xr-x   10 root     root         4096 11월 10 22:03 ../
drwx------    2 hudson   hudson       4096 11월 10 22:03 ./
[hudson@169s /home/hudson]# wget http://hudson.gotdns.com/latest/hudson.war
--22:05:57--  http://hudson.gotdns.com/latest/hudson.war
           => `hudson.war'
Resolving hudson.gotdns.com... 192.18.49.133
Connecting to hudson.gotdns.com|192.18.49.133|:80... connected.
HTTP 요청을 보냅니다, 서버로부터의 응답을 기다림...302 Found
장소: https://hudson.dev.java.net/files/documents/2402/116126/hudson.war [following]
--22:05:59--  https://hudson.dev.java.net/files/documents/2402/116126/hudson.war
           => `hudson.war'
Resolving hudson.dev.java.net... 204.16.104.198
Connecting to hudson.dev.java.net|204.16.104.198|:443... connected.
ERROR: Certificate verification error for hudson.dev.java.net: unable to get local
issuer certificate
To connect to hudson.dev.java.net insecurely, use `--no-check-certificate'.
Unable to establish SSL connection.
[hudson@169s /home/hudson]# wget --no-check-certificate  https://hudson.dev.java.net/files/documents/2402/116126/hudson.war
--22:07:28--  https://hudson.dev.java.net/files/documents/2402/116126/hudson.war
           => `hudson.war'
Resolving hudson.dev.java.net... 204.16.104.198
Connecting to hudson.dev.java.net|204.16.104.198|:443... connected.
WARNING: Certificate verification error for hudson.dev.java.net: unable to get local
issuer certificate
HTTP 요청을 보냅니다, 서버로부터의 응답을 기다림...200 OK
길이:   20,804,156 (20M) [text/plain]
[========================================>] 20,804,156   790.53K/s    ETA 00:00
22:08:08 (724.78 KB/s) - `hudson.war' saved [20804156/20804156]


실행은 간단히 됩니다.
java -jar hudson.war


[hudson@169s /home/hudson]# java -jar hudson.war
[Winstone 2008/11/10 22:13:48] - Beginning extraction from war file
[Winstone 2008/11/10 22:13:55] - No webapp classes folder found - /home/hudson/.hudson/war/WEB-INF/classes
hudson home directory: /home/hudson/.hudson
2008. 11. 10 오후 10:13:59 hudson.model.Hudson load
정보: Took 219 ms to load
2008. 11. 10 오후 10:14:00 hudson.TcpSlaveAgentListener <init>
정보: JNLP slave agent listener started on TCP port 48801
2008. 11. 10 오후 10:14:02 org.springframework.context.support.AbstractApplicationContext prepareRefresh
정보: Refreshing org.springframework.web.context.support.StaticWebApplicationContext@1a9d1b: display name [Root WebApplicationContext]; startup date [Mon Nov 10 22:14:02 KST 2008]; root of context hierarchy
2008. 11. 10 오후 10:14:02 org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
정보: Bean factory for application context [org.springframework.web.context.support.StaticWebApplicationContext@1a9d1b]: org.springframework.beans.factory.support.DefaultListableBeanFactory@146e381
2008. 11. 10 오후 10:14:02 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
정보: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@146e381: defining beans [filter,legacy]; root of factory hierarchy
[Winstone 2008/11/10 22:14:02] - HTTP Listener started: port=8080
[Winstone 2008/11/10 22:14:02] - AJP13 Listener started: port=8009
[Winstone 2008/11/10 22:14:02] - Winstone Servlet Engine v0.9.10 running: controlPort=disabled

결과는 다음과 같이 볼 수 있습니다.


job 을 죠브라고 번역해 놓았네요. 구글 번역기 짓인가요? ^^; 리포팅해야할 듯 합니다. 일본 개발자인 Kohsuke님에게 보내면 되겠지요.

+ Recent posts