블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

Apt.ly를 이용한 데비안 리포지토리 생성


조대협 (http://bcho.tistory.com)




앞의 글에서 Jenkins + Maven 조합을 통해서 애플리케이션 설치 파일을 데비안 패키지로 패키징하는 방법에 대해서 알아보았다. 이제 이 패키지를 서버에 설치하는 방법을 살펴본다.

패키지를 설치하는 방법은 간단하게 데비안 패키지 파일을 설치하고자 하는 서버에 복사해놓은 다음에, sudo apt-get install을 이용해서 설치하는 방법도 있지만, 설치하고자 하는 서버마다 복사하기가 번거롭기 때문에 조금 더 쉬운 접근을 위해서 데비안 패키지 서버를 올리는 방법이 있다. 우리가 JDK나  node.js 등 다양한 유닉스 패키지를 apt-get 을 이용하여 설치가 가능한것은 미리 데비안 패키지 리파지토리 서버가 지정되어 있고, 그 서버내에 패키지들이 등록되어 있기 때문인데, 애플리케이션 패키지를 같은 방법으로 설치할 수 있게 하려면 애플리케이션 데비안 패키지 파일을 등록할 리포지토리 서버를 설정하면 된다.


지금까지 구현해온 파이프라인은 아래 그림과 같다

  1.  gitHub 로 부터 Jenkins가 자바 코드를 자바 코드를 당겨온다

  2. 이 코드를 Maven을 이용하여 빌드한다

  3. Maven은 코드 빌드가 끝나고 이를 데비안 패키지로 패키징 한다.

  4. Jenkins는 데비안 패키징 파일을 로컬 리포지토리인 apt.ly에 저장한다.

  5. Spinnaker에서 설치할때 데비안 패키지를 apt.ly에서 당겨서 설치한다.


앞의 글에서 까지 1,2,3과정까지 진행을 하였고, 이 글에서는 4번 과정을 구현할 예정이다.

다양한 오픈소스가 있지만, 플랫폼 종속성이 없고 손쉽게 설치가 가능한 apt.ly (www.aptly.info) 를 기준으로 설명을 하고자 한다.


apt.ly 설치

여기서 설명하는 설치는 데비안 리눅스 9을 기준으로 하여 설명한다.

설치는 앞의 글에서 설치한 Jenkins 서버에 그대로 설치하도록 한다.

설치 방법은 간단하다. www.aptly.info 사이트에서 설치 메뉴얼을 체크하여, 현재 사용하는 리눅스 버전에 맞는 바이너리를 wget을 이용해서 다운 로드 받은 후에, chmod +x 로 실행 권한만 주면 된다.


$ wget https://dl.bintray.com/smira/aptly/0.9.5/debian-squeeze-x64/aptly

$ chmod +x aptly


명령어가 설치 되었으면 리파지토리를 생성해야 한다.

리포지토리 생성 과 확인

리포지 토리 생성은 aptly repo crete {리포지토리 명} 을 입력하면 된다.  아래 명령은 “terry-repo”라는 이름의 리포지토리를 생성한것이다.

%./aptly repo create terry-repo


아래 명령은 terry-repo 라는 리포지토리에 대한 정보를 조회 하는 명령이다.

%./aptly repo show -with-packages terry-repo


아래는 실제 실행결과 인데, 테스트를 위해서 helloterry_1.0_all 이라는 패키지를 등록해놨기 때문에 하나의 패키지가 등록되서 보이는 것을 확인할 수 있다.



apt.ly 에 패키지 등록하기

리포지토리가 생성되었으면, maven 에서 빌드한 패키지를 apt.ly 리포지토리에 등록해보자

등록하는 방법은 aptly repo add -force-replace {리포지토리명} {데비안 패키지 파일명} 식으로 사용하면 된다.

아래는 terry-repo에 helloworld.deb 파일을 등록하는 명령이다.

%./aptly repo add -force-replace terry-repo helloworld.deb

apt.ly 리포지토리 퍼블리슁하기

패키지를 등록했으면 외부에서 억세스사 가능하도록 리포지토리 퍼블리쉬를 해야 하는데, 퍼블리쉬는 어떤 버전의 OS와 CPU 타입에 설치할 수 있는지등의 메타 정보를 함께 등록한다.

명령어 사용법은 aptly publish repo -distribution=”{OS 버전 정보}" -architecture=”{CPU 타입}” -skip-signing=true {리포지토리명}

식으로 사용한다.

원래 데비안 패키지를 외부로 배포를 할때는 패키지의 변경(원하지 않은)을 막기 위해서 패키지에 사이닝을 하는데, 여기서는 -skip-signing 을 이용하여 사이닝 단계를 건너뛰도록 하였다. 이 리파지토리는 외부에서 억세스하는 용도가 아니라 내부에서 CI/CD 파이프라인 단계에서만 사용되기 때문에 사이닝을 생략하였다.


아래 명령은 데비안 stretch 버전에 amd64 (intel CPU)에 terry-repo 이름으 리포지토리를 퍼블리슁한것이다.

%./aptly publish repo -distribution=stretch -architectures="amd64" -skip-signing=true terry-repo


apt.ly 서버 기동

퍼블리슁이 되었다고 당장 리포지토리를 접근 가능한것이 아니다. apt-get을 이용한 인스톨은 HTTP 프로토콜을 이용해서 접근하기 때문에 apt.ly 파일 저장소를 접근 가능하게 하는 웹서버를 올려야 한다.

간단한 방법으로는 aptly serve 명령어를 이용해서 웹서버를 올리는 방법이 있다.

아래 명령어 처럼 aptly serve -listen={IP:포트}를 적으면 된다.


% ./aptly serve -listen=:9090 > aptly.log &


이 보다는 제대로 서비스를 하기위해서는 웹서버에 올리는게 좋은데,

(참고 : https://www.spinnaker.io/guides/tutorials/codelabs/hello-deployment/)

% sudo apt-get install nginx

를 통해서 nginx 를 설치한 후에, /etc/nginx/sites-enabled/default 파일을 다음과 같이 편집한다.


server {
       listen 9999 default_server;
       listen [::]:9999 default_server ipv6only=on;
       root /var/lib/jenkins/.aptly/public;
       index index.html index.htm;
       server_name localhost;
       location / {
               try_files $uri $uri/ =404;
       }
}


이때 root에 aptly의 public 디렉토리를 명시해줘야 하는데, aptly를 설치한 디렉토리의 .aptly/public 이 되는게 일반적이다. 여기서는 /var/lib/jenkins 디렉토리 아래에 리포지토리를 만들었기 때문에 /var/lib/jenkins/.aptly/public 디렉토리를 홈 디렉토리로 설정하였다.


nginx 를 기동하면 http 9999번 포트로 데비안 패키지 서비스를 시작한다.

apt.ly 를 통한 패키지 설치

데비안 패키지 서버를 설치하고 패키지를 등록했으면 실제로 패키지를 다른 서버에서 인스톨 해보자

다른 서버에서 이 패키지 서버에 대한 정보를 알고 있어야 하는데 (서버 주소) 이 정보는 /etc/apt/sources.list 라는 파일에 아래와 같은 형태로 등록 되어 있다.


deb http://deb.debian.org/debian/ stretch main

deb-src http://deb.debian.org/debian/ stretch main

deb http://security.debian.org/ stretch/updates main

deb-src http://security.debian.org/ stretch/updates main

deb http://deb.debian.org/debian/ stretch-updates main

deb-src http://deb.debian.org/debian/ stretch-updates main


이 파일에 앞서 설정한 데비안 리포지토리 서버 (apt.ly) 서버의 주소와 정보를 입력해주면 된다.

만약 http://myserver-ip:9999 포트로 서버를 올렸다면 아래와 같은 정보를 /etc/apt/sources.list 에 추가해주면 된다.


deb http://myserver-ip:9999 stretch main


설정이 끝났으면

%sudo apt-get update

명령을 실행하면 아래와 같이 새로운 리포지토리에서 정보를 읽어오는 것을 확인할 수 있다.


모든 준비가 끝났다.

인스톤을 해보자. 인스톨은 sudo apt-get install을 이용하면 된다.

앞서 등록한 패키지 명이 helloterry 였기 때문에 간단하게 아래와 같이 sudo apt-get install helloterry 명령어를 실행하면 된다.



이외에도 유사한 툴로 pulp (https://docs.pulpproject.org/user-guide/introduction.html#what-pulp-can-do)

클라우드 서비스로는 cloudsmith.io (https://cloudsmith.io/)등이 있다. 

작은 규모의 팀이라면 관리 문제도 있으니 클라우드 서비스를 쓰는 것도 좋은 방안이 되지 않을까 한다.



Maven으로 데비안 패키지를 만들어보자


(http://bcho.tistory.com)

조대협

애플리케이션 배포

CI/CD 빌드 배포 프로세스에서, 컴파일된 애플리케이션을 배포하는 방법은 여러가지가 있다. 빌드된 바이너리를 Ansible과 같은 Configuration management 도구를 이용해서 배포하는 방법이 일반적이지만, 작업이 복잡한 경우에는 많은 스크립트 작업이 필요한 경우가 있다.

보통 애플리케이션 배포는 단순하게 바이너리만을 복사하는 것이 아니라, 이에 필요한 의존성이 있는 패키지 (예를 들어 JDK나 기타 의존되는 라이브러리)를 배포해야 하는 경우도 있고, 경우에 따라서는 의존되는 파일이나 복잡한 디렉토리 구조를 생성해야 하는 경우가 있다. 이를 Ansible과 같은 Configuration management 툴을 사용하게 되면 스크립트가 복잡해질 수 있는데, Debian 패키지 (apt-get install 로 설치하는)로 패키징을 하면, 의존성이 있는 패키지나, 복잡한 디렉토리 구조 설정, 그리고 여러 파일 설치를 한번에 끝낼 수 있다.

데비안 패키지 파일 생성

디렉토리 구조

데비안 패키지 생성은 어렵지 않다. 먼저 데비안 패키지 파일에 패키징할 파일을 저장한 “작업 디렉토리"를 하나 만든다. 그리고 그 아래 “홈 디렉토리”에 설치될 경로와 파일을 저장한다.

예를 들어 “홈 디렉토리”가 “/home/terrycho”면 이 패키지를 설치하는 시스템에 /home/terrycho라는 디렉토리가 생성이되고 “작업 디렉토리"아래에 /home/terrycho/my.jar 라는 파일이 있으면 대상 시스템에도 같은 경로에 파일이 설치된다.

파일 구조

패키지에 대한 정보를 설정하기 위해서는 “작업 디렉토리” 아래에 “/DEBIAN” 디렉토리 안에 각종 설정 파일을 넣어둘 수 있는데, control 이라는 파일은 필수로 필요한 파일이다. 이 파일에는 데비안 패키지의 필수 정보인 패키지명, 정보들이 들어간다.  아래는 샘플 내용이다.


Package: ${build.finalName}

Version: ${project.version}

Section: misc

Priority: low

Architecture: all

Depends: oracle-java8-installer | openjdk-8-jre

Description: spinnaker spring test

Maintainer: {my email}


각 항목을 살펴보자

  • Package : 가장 중요한 항목으로 패키지 명을 정의한다. apt-get install 시, 이 패키지명으로 지정해서 인스톨을 한다.

  • Version : 패키지 버전이다. 설치된 패키지를 업그레이드할때 이 버전을 비교하기 때문에 매우 중요한 필드 이다. 버전이 같으면 내용이 다르더라도 업데이트가 되지 않으니 주의가 필요하다.

  • Section : 패키지의 분류인데, 크게 중요하지는 않다.

  • Architecture : 설치 가능한 CPU 플랫폼 종류를 정의한다.

  • Depends : 이 패키지를 실행하기 위해서 필요한 다른 패키지명을 리스팅 한다. 예를 들어 자바 애플리케이션의 경우 JDK를 설치하도록 할 수 있다. 위의 예제는 oracle-java8이나, openjdk-8 런타임을 설치하도록 정의되어 있다.

  • Description : 패키지에 대한 설명을 적는다.

  • Maintainer : 패키지를 관리하는 개발자 이메일을 적는다.


control 파일 이외에도 추가 설정 파일을 통해서 인스톨전 후의 추가 작업을 preinstall이나, postinstall 스크립트를 지정할 수 있다.

패키지를 만들어보자

대략적인 개념을 이해 했으면 실제로 패키지를 만들어보자

아래와 같이 hellodebian 디렉토리에 hello.txt와 control 파일을 생성하였다.


/home/terrycho/hellodebian/home/hello.txt

/home/terrycho/hellodebian/DEBIAN/control


hello.txt 는 간단한 텍스트 내용이 들어있는 파일이고, control 파일의 내용은 다음과 같다.


Package: hellodebian

Version: 1.0

Section: misc

Priority: low

Architecture: all

Depends: oracle-java8-installer | openjdk-8-jre

Description: spinnaker spring test

Maintainer: {my email}


이 디렉토리를 패키지로 묶으려면 “dpkg-deb --build {작업 디렉토리명}” 명령어를 실행하면 된다.



위와 같이 명령을 실행하면 hellodebian.deb 파일이 생성된다.

설치 확인은

% sudo apt-get install ./hellodebian.deb

명령을 실행하면 설치가되는 것을 확인할 수 있고, 의존성에 정의한 Jre가 같이 설치되는것을 확인할 수 있다.




Maven을 이용한 데비안 패키지 파일 생성

그러면 실제 애플리케이션 빌딩 과정에서 이렇게 디렉토리를 구조를 정의하고 dpkg 명령어를 사용해서 패키징을 해야 하는가? 다행이도 언어마다 빌드 스크립트에 데비안 패키지로 패키징을 해줄 수 있는 플러그인이 있다.

여기서는 자바 빌드를 위한 maven에서 데비안 패키지를 묶어주는 jdeb 플러그인에 대해서 알아보자. (https://github.com/tcurdt/jdeb)

아래는 jdeb을 사용하는 pom.xml이다.


   <build>

       <plugins>

           <plugin>

               <groupId>org.springframework.boot</groupId>

               <artifactId>spring-boot-maven-plugin</artifactId>

           </plugin>

           <plugin>

               <artifactId>jdeb</artifactId>

               <groupId>org.vafer</groupId>

               <version>1.5</version>

               <executions>

                   <execution>

                       <phase>package</phase>

                       <goals>

                           <goal>jdeb</goal>

                       </goals>

                       <configuration>

                           <verbose>true</verbose>

                           <snapshotExpand>true</snapshotExpand>

                           <!-- expand "SNAPSHOT" to what is in the "USER" env variable -->

                           <snapshotEnv>USER</snapshotEnv>

                           <verbose>true</verbose>

                           <controlDir>${basedir}/src/deb/control</controlDir>

                           <dataSet>

                    <data>

                       <src>${project.build.directory}/${build.finalName}.jar</src>

                       <type>file</type>

                       <mapper>

                           <type>perm</type>

                           <prefix>/var/${build.finalName}</prefix>

                           <filemode>755</filemode>

                       </mapper>

                   </data>


                           </dataSet>

                       </configuration>

                   </execution>

               </executions>

           </plugin>

       </plugins>

   </build>



<goal>에 jdeb을 설정하고, <configuration>에 상세 설정 정보를 넣는다.

데비안 패키지는 앞에서 설명 했듯이, control 파일이 필요한데, <controlDir>에 control 파일이 저장되어 있는 디렉토리 위치를 지정한다.

다음으로, <dataSet>에 데비안 패키지에 대한 설정 정보를 지정하는데, 디렉토리 정보등을 정의할 수 있다. <type>file</type>은 파일에 대한 정보를 넣는데, <src>에는 소스 파일을 정의하고 <prefix>에 파일이 저장될 위치를 지정한다. <filemode> 에 Unix file permission을 지정한다.

위의 스크립트는 ${prject.build.directory}/${build.finalName}.jar 파일을 /var/${build.finalName}으로 복사한다.


생성된 jar 파일명은 ${build.finalName}으로 생성되는데, 이 이름은

${artifactId}_{$version}_all 이라는 이름으로 된다.

이 예제에서는 artifactId가 hello-springboot 이고, version은 1.0이기 때문에 최종 생성된 이름은

hello-springboot_1.0_all 로 된다.


Jenkins와 gitHub 연동


조대협 (http://bcho.tistory.com)


가장 널리 사용하는 Jenkins와, 소스 코드 리포지토리 서비스인 GitHub를 연동하는 방법에 대해서 알아본다. 시나리오는 gitHub에 코드를 푸쉬하면 Jenkins가 이를 인지해서 자동으로 코드를 내려 받아서 빌드 스크립트를 실행하는 순서로 한다.


GitHub에서 Credential 생성


gitHub 자신의 계정으로 로그인 한 후 우측 상단의 자신의 사진이 있는 아이콘을 누르면 메뉴가 나오는데, 여기서 Setting > Developer settings 메뉴로 들어간 후에 아래와 같이 Personal access tokens 메뉴로 들어간다.

다음 우측 상단의 Generate new token 메뉴를 선택한다.



다음 토큰으로, 접근할 수 있는 범위를 설정한다. 접근 범위는 “repo”와 “admin:repo_hook” 을 선택한다.




선택이 끝나고 토큰을 생성하면 문자열로 된 토큰이 생성된다.


Jenkins에서 GitHub 연결 설정

앞에서 생성된 토큰을 Jenkin의 GitHub 연결 부분에 설정하도록 하겠다.

Jenkins 초기화면에서 Jenkins > Manage Jenkins > Configure System 메뉴로 들어가면 GitHub 계정을 설정하는 부분이 있다.



Name은 이 GitHub 연결 설정을 구별할 이름으로 정의하고 API URL은 default로 https://api.github.com 으로 설정되어 있는데 default 값을 사용한다.

다음 접속 credential을 설정해야 하는데, credentials 부분에서 Add 버튼을 눌러서 Credential 설정 메뉴를 실행한다.




위와 같은 메뉴가 나오면 Kind는 “Secret text”를 선택하고 Secret 에 앞에 gitHub에서 생성한 키를 입력한다. ID에는 본인 gitHub ID를 입력한다.  Credential 입력이 끝나면,  아래 그림과 같이 Credentials 메뉴 아래에 Test Connection 버튼이 있는데, 이 버튼을 눌러서 제대로 github와 연결이 되는지를 테스트 한다.




Jenkins 프로젝트 생성 및 설정

Jenkins와 gitHub 연결 설정이 끝났으면, Jenkins에서 프로젝트를 생성한다.

Git 연결 설정

프로젝트 설정에서 아래와 같이 Git 메뉴로 이동한다.



여기서 Repository URL을 입력한다. Repository URL은 본인 gitHub Repository에서 우측 상단의 녹색 “Clone or download” 버튼을 누르면 HTTPS 로 된 URL이 나온다. 이 URL을 입력하면 된다.



다음 이 repository에 연결할 연결 정보를 입력해야 하는데, Jenkins에서 credentials 메뉴로 들어간다.

이 메뉴에서 Kind를 “Username with password” 를 선택하고 Username에는 본인의 github id, Password에는 github 비밀번호를 입력한다.



빌드 트리거 설정

다음 어떤 조건에서 Jenkins 빌드를 실행할지를 설정하는데, GitHub에 코드가 푸쉬되면 빌드를 트리거링 하도록 설정을 할것이다. 아래 그림과 같이 Build Triggers 메뉴에서 GitHub hook trigger for GitScm Polling을 선택한다.




이렇게 설정하면 GitHub에서 코드 푸쉬가 될때 webHook 메세지를 Jenkins에 보내주는데, 이 WebHook 메세지를 받을 때마다 빌드를 하게 된다.


GitHub에서 WebHook 설정

Jenkins 가 GitHub 에서 보내는 WebHook에 의해서 Triggering이 되도록 설정했으면, 이제 GitHub에서 코드가 푸쉬 될때 마다 WebHook을 Jenkins에 보내도록 설정해야 한다.




GitHub Repository로 들어가면 우측 상단에 Settings라는 메뉴가 있다.

이 메뉴에 들어가서 좌즉에 Integration & Service 라는 메뉴를 선택한다.


Services 메뉴에서 “Add service” 버튼을 클릭한 후에 “Jenkins (GitHub plugin)” 을 선택한다.



다음 플러그인 설정에 Jenkins hook url에 Jenkins가 WebHook을 받을 HTTP 경로를 입력한다.

일반적으로 http://{Jenkins server의 URL}/github-webhook 이 된다.




이제 모든 설정이 끝났다.

제대로 작동하는 것을 확인하기 위해서 코드를 commit 한 후에 Push를 해보면 빌드가 자동으로 진행이 된다.

Jenkins의 해당 project에서 좌측의 “GitHub Hook Log”를 보면 WebHook을 잘 받았는지 확인이 가능하다. 아래는 실제로 WebHook이 발생한 내용을 확인한 화면이다.




Bazel 빌드툴


Tensorflow Serving을 살펴보다보니, Serving Server는 C++기반에 gRPC 인터페이스 기반이라는 것을 알았는데,

빌드 환경이 bazel이라는 것을 사용한다. 그래서 Bazel이 무엇인가 찾아봤는데. 쉽게 말하면 빌드 툴이다





위키에 설명이 가장 잘나와 있는데, 구글에서 만든 빌드 시스템으로, 구글의 경우 큰 소스코드를 빌드하기 때문에, 이를 위해서 만들어진 빌드 시스템을 오픈소스화 한것으로, 분산 빌드등을 제공하고 빠른 성능을 제공한다.


쉽게 말해서 make,ant,gradle,maven과 같은 빌드 시스템으로 보면 된다.

Java,C,C++,Python,Object C등의 언어를 지원한다.


https://en.wikipedia.org/wiki/Bazel_(software)

In software developmentBazel is an open source tool that allows for the automation of building and testing of software.[2] The company Google uses the build tool Blaze internally[3] and released and open-sourced part of the Blaze tool as Bazel, named as an anagram of Blaze.[4] Bazel was first released in March 2015 and achieved beta status by September 2015.[5]

Similar to build tools like MakeApache Ant, or Apache Maven,[2][4] Bazel builds software applications from source code using a set of rules. Rules and macros are created in the Skylark language, a subset of Python.[4] There are built-in rules for building software written in the programming languages of JavaCC++PythonObjective-C and Bourne shell scripts.[4][5] Bazel can produce software application packages suitable for deployment for the Android and iOS operating systems.[6]

In designing Bazel, emphasis has been placed on build speed, correctness, and reproducibility.[2][4] The tools uses parallelization to speed up parts of the build process.[4] It includes a Bazel Query language that can be used to analyze build dependencies in complex build graphs.[4]


아무래도 개발 환경 설정이 쉽지 않은 만큼, Bazel C++ 빌드 환경이 패키징된 도커 환경을 알아보는것이 더 좋겠다.

maven nexus 설치

ALM/Build Automation (빌드 자동화) | 2013.09.05 23:47 | Posted by 조대협

Nexus 설치 및 Maven 연동

조대협

Nexus maven에서 사용할 수 있는 가장 널리 사용되는 무료 repository 중의 하나이다. www.sonatype.com 에서 다운로드 받아서 설치할 수 있다.

Local nexus를 설치하게 되면, 외부로 부터 dependency를 끌어 오는 수고를 덜고, local nexus proxy (cache)로 사용함으로써 빠르게 라이브러리들을 끌어 올 수 도 있고, 반대로 개발팀내에서 사용하는 공통 라이브러리들을 local nexus에 배포해서 팀간에 공유할 수 있다.

또한 사용자 계정 지정을 통해서 repository에 대한 접근 정책을 정의할 수 도 있다.

Nexus repository의 용도와 목적에 따라서 몇 가지로 나눌 수 있는데, 대표적으로 다음과 같은 종류 들이 있다.

   Snapshots : 빌드등 수시로 릴리즈 되는 바이너리를 배포 하는 장소

   Releases : 정식 릴리즈를 통해서 배포되는 바이너리를 저장하는 저장소

   3rd party : 벤더등에서 배포하는 (Oracle,IBM) 바이너리를 저장해놓는 장소로 특정 솔루션등을 사용할때, 딸려 오는 라이브러리등을 여기에 놓고 사용한다

   Proxy Repository : 원격에 원본 repository가 있는 경우, Local에 캐쉬 용도로 사용한다.

   Virtual Repository : Repository Group은 몇 개의 repository를 하나의 repository로 묶어서 단일 접근 URL을 제공한다.

 

여기서는 가장 널리 사용하는 local repository로 설정 하는 시나리오와 함께, 외부 repository에 대한 proxy 시나리오로 사용하는 설정을 소개한다.

설치

http://www.sonatype.com 에서 nexus 무료 버전을 다운로드 받아서 설치한다. 초기 디폴트 로그인 계정과 비밀번호는 "admin/admin123"이다.


  

Public Repositories라는 repository 그룹에 local repository (Releases Snapshots, 3rd party) proxy repository를 포함시킨다.




 

다음으로 Proxy Repository Remote Repository의 내용들에 대한 라이브러리 목록(Index) Local Caching할 수 있도록 되어 있다. 이렇게 하면, nexus proxy repository에 실제 바이너리가 내려와 있지 않더라도 목록이 미리 내려와 있기 때문에, nexus search 기능을 통해서 검색이 가능하다.

"Maven Central Repository" "Central" repository 에 설정을 해보자 "Central repository"를 선택한 후, 메뉴에서 "Download Remote Indexes" 라는 Option "True"로 변경한다. 다음 SAVE 버튼으로 저장한 후, 상단 테이블 메뉴에서 "Central" repository를 선택한 후 오른쪽 버튼을 눌러서 팝업 메뉴에서 "update index"를 실행하면, 원격 maven repository에서 라이브러리 목록을 읽어서 업데이트가 된다.

 



 

업데이트가 끝나면 "Browse Index" 메뉴에서 라이브러리 목록이 새롭게 업데이트 되어 있는 것을 확인할 수 있다.

 



Maven에서 local nexus Proxy (Cache) repository로 설정하기

Nexus 설정이 끝났으면 다음으로 maven nexus rmirroring repository 설정해보자.

$MAVEN_HOME/.m2/setting.xml

파일에서 <mirrors> section에 아래 내용을 추가하자

 

<mirror>

      <id>nexus</id>

      <mirrorOf>*</mirrorOf>

      <name>Local nexus repository.</name>

      <url>http://localhost:8081/nexus/content/groups/public/</url>

    </mirror>

  </mirrors>

 

설정이 끝난후, maven 빌드를 수행하면 maven script가 원격지가 아닌 local에 있는 nexus repository를 통해서 라이브러리를 다운로드 하는 것을 확인할 수 있다.

 



또한 nexus console을 통해서 "Browse Storage" 메뉴를 통해서 "Central" repository storage 를 보면 빌드에 사용되었던 모든 라이브러리들이 local nexus에 다운로드 받아져 있음을 확인할 수 있다.

 


 



nexus pro에 대한 고급 기능소개


조대협 (bwcho75@지메일)


nexus는 maven repository로 매우 유명한 솔루션이다. 오픈 소스 버전은 maven을 사용하는 경우에는 거의 필수적으로 사용이 된다고 해도 과언이 아니다.

nexus의 상용 버전인 nexus pro의 경우 CLM (Component Life-cycle Management) 개념을 도입하여, 접근제어나 컴포넌트에 대한 security 나 license risk등을 관리 통제할 수 있다.

이 글에서는 nexus pro에 대한 몇 가지 고급 기능에 대해서 살펴보고, 이를 통해서 컴포넌트(라이브러리)의 관리가 단순한 중앙 집중형 공유만이 아닌 일종의 life cycle 개념이 있다는 것을 이해하도록 해보자


 

Nexus의 상용 버전인 Pro 버전에는 단순한 공유나 maven repository cache 용도뿐만이 아니라 조금 더 복잡한 기능의 라이브러리 관리 기능을 제공한다. Nexus에서는 이러한 개념을 CLM (Component Life-cycle Management)이라고 하는데, 라이브러리에 대한 접근 제어나 정책 관리등이 이에 해당한다. 몇가지 대표적인 오픈소스 정책 관리 기능을 살펴보자.

근래에 소프트웨어 개발 패러다임은 오픈소스를 이용한 소프트웨어 개발이 많다. 많은 오픈 소스 라이브러리를 사용하게 되는데, 문제는 각 오픈소스 컴포넌트들의 라이센스 정책이 다르다는 것이다. GPL,Apache,MIT,BSD 등 여러가지 라이센스 정책이 있는데, 라이센스 정책에 따라서, 어떤 오픈 소스는 사용에는 제약이 없지만 배포시 소스코드를 변경해야 하거나, 유료로 비용을 지불해야 하는 경우도 있고, 2.0 버전에서는 멀쩡하게 아무 제약없이 사용할 수 있었던 컴포넌트들이 3.0 버전으로 업그레이드 되면서 제약이 생기는 경우가 있다. (오라클이 인수한 MySQL의 경우 제품에 번들해서 재 배포할 경우 일정의 비용을 지불해야 한다.)  이러한 이유 때문에, 오픈소스 컴포넌트에 대한 라이센스 체크는 점점 필수적인 요건이 되어가는데 문제는 하나의 서비스나 소프트웨어 제품을 개발하는데, 수백개 이상의 라이브러리가 사용된다는 것이고, 각 버전마다 일일이 라이센스를 체크한다는 것은 보통일이 아니다. Nexus는 이런 오픈소스 라이브러리 정책을 repository 차원에서 관리해준다.

오픈소스 라이센스 정책 관리

Nexus proxy repository를 선택하고, Analysis 라는 버튼을 누르면, 현재 repository에 저장되어 있는 오픈 소스에 대한 라이센스 정책을 분석하여 다음과 같이 보여준다. 이 라이센스 정책에 대한 DB nexus 판매사인 sonatype으로 부터 제공된다.



각 라이브러리가 어떤 라이센스 정책을 사용하는지, 그리고 각 라이센스 정책이 문제가 없는 지등을 찾아준다.

Security 체크

또한, 보안에 위험이 있는 라이브러리등을 찾아서 보안 위험도등을 표시해준다. 2013년 기준으로 struts2에 대해서 아주 큰 보안 위험이 발생된 일이 있었다. 이렇게 중앙의 repository에서 회사내에서 사용하는 라이브러리에 대한 보안 위험성을 감지해서 중앙에서 통제하게 되면, 개별 개발자에 대한 수고도 덜 수 있을 뿐더러, 보안 위험에 대해서 피해갈 수 있는 장점을 가질 수 있다.



이렇게 체크만 할뿐만 아니라, 이렇게 검출된 문제 있는 라이브러리들을 접근하지 못하게 막을 수 있다.

Procurement

nexus pro에는 “artifact procurement” 라는 기능이 있는데, 이 기능을 사용하면 proxy repository를 만들고, 여기에 속해 있는 라이브러리에 대해서 white list 또는 black list 방식으로 접근을 제어할 수 있다. 아래 그림은 asm-parent 라이브러리에 대해서 모두 접근을 제어 하는 설정을 적용한 예이다.



 

Staging

또 다른 재미 있는 기능중에 하나는 staging 개념을 지원한다는 것이다.

즉 개발자가 컴포넌트나 라이브러리를 개발하여 nexus에 배포하면 다른 개발자나 사용자들이 바로 그 라이브러리를 사용하게 하는 것이 아니라, 일종의 워크플로우를 통해서 릴리즈 절차가 끝나면 일발 개발자들이 사용할 수 있도록 프로세스를 조정할 수 있다.

이를 위해서 staging repository라는 개념을 제공하는데, 설명하자면 다음과 같다.



개발자가 컴포넌트를 개발하고, 개발이 끝나면 staging repository로 배포를 진행한다. 배포된 repository QA 그룹 엔지니어만 접근이 가능하다. QA 엔지니어는 컴포넌트를 받아서 테스트를 진행하고, 문제가 없으면, 해당 컴포넌트를 베타 테스트 단계로 넘긴다.

베타 테스트 단계에 있는 컴포넌트는 베타 테스트 사용자에게만 접근이 허용되며, 테스트가 끝나면 일반 repository로 이동되어 일반 개발자도 접근이 가능하게 해준다.

이 워크플로우에서 단계별로 넘어갈때, 각 단계 이동별로 정책을 정할 수 있다. 예를 들어 앞서 설명한 security level이 낮은 경우 reject을 하거나, open source 라이센스가 문제가 있는 경우에, reject을 하는 중의 policy를 정의할 수 있다

< 출처 : http://www.sonatype.com/take-a-tour/nexus-pro-tour-start >

Plug in 을 통한 maven 빌드 확장


앞글에서 http://bcho.tistory.com/739 maven을 이용한 가장 기본적인 빌드 시나리오에 대해서 살펴보았다.

maven plug in이라는 기능을 통해서, 여러 기능 들을 추가로 빌드 프로세스내에 추가할 수 있는데, 여기서는 빌드에서 확장하여, 배포 환경 별로 패키징 하는 시나리오를 추가해보기로 한다.

 

다음과 같은 시나리오를 생각해보자, 빌드에 의해서 생성된 웹 애플리케이션은 내 local PC에서도 돌아가야 하며, 다른 개발자와 협업하는 서버의 개발환경에서도 돌아야 하고, 개발 주기마다 주기적으로 QA환경에서 테스트를 받은 후, staging 환경을 거쳐서 최종적으로 production 환경에 배포 되어야 한다고 가정하자.

 

Java Version 지정하기

먼저 대상 환경에 맞는 컴파일 run time시에 소스 코드에 대한 java 버전과, target compiler java 버전을 지정할 있다. 아주 기본적인 내용인것 같지만, 실제 빌드 과정에서 java 버전을 명시적으로 지정하지 않아서 run time등에서 문제가 생기는 경우가 의외로 많다. 예를 들어 runtime jvm 버전이 1.5인데, 최신 버전으로만 컴파일을 하는 것을 정책으로 잡아놓고 1.7로만 컴파일이 되게 하면, 빌드 스크립트에 의해서 컴파일된 바이너리는 run time에서 작동하지 않는다.

 

             <!--  compiler plug in -->

             <plugin>

                <artifactId>maven-compiler-plugin</artifactId>

                       <version>3.0</version>

                <configuration>

                    <source>1.5</source>

                    <target>1.5</target>

                </configuration>

              </plugin>

 

개발환경 별로 컴파일 하기

이를 지원하는 배포 프로세스는 뒤에서 다시 설명하기로 하고, 여기서는 이러한 다중 환경을 지원하기 위해서 같은 소스 코드로 어떻게 다르게 packaging을 할 것인가에 대해서 고민해보자, 각 환경이 다르면, dbms를 접근하기 위한 db url, id,password도 다를 것이며, 기타 디렉토리 구조나, 다른 서버의 ip등이 모두 다를 것이다.

즉 배포 target에 따라서 다른 빌드 프로세스를 타야 한다는 것이다.

 


이를 maven에서는 profile이라는 것으로 지원할 수 있다.

지금 소개하는 Sample 시나리오는 이러한 설정 정보를 WEB-INF/config/config.properties에 정의했다고 하고, 빌드 target에 따라 각각 다른 config.properties 파일을 패키징 하는 시나리오이다.

각각 다른 환경의 config 파일을 저장하기 위해서 ${basedir} 아래에 다음과 같은 이름으로 디렉토리를 만든다.

 

${basedir}/resource-local     : PC 용 설정 정보

${basedir}/resource-dev               : 공용 개발 서버용 설정 정보

${basedir}/resource-qa        : QA 환경용 설정 정보

${basedir}/resource-stage     : Staging 환경용 설정 정보

그리고 각 디렉토리 아래 WEB-INF/config/config.property를 각 환경에 맞게 정의한다.
다음 예제는 dev 환경용 설정 파일이다.

mybatis.jdbc.driverclass=com.mysql.jdbc.Driver

mybatis.jdbc.url=jdbc:mysql://localhost:3306/development

mybatis.jdbc.username=developer

mybatis.jdbc.password=developer

 

s3.url=developer_s3

이렇게 설정한 디렉토리는 다음과 같은 구조를 가지게 된다.

 


 

다음으로, maven pom.xml에서 환경에 맞게 위에 지정한 파일들을 포함해서 패키징 하게 해야 한다. 이를 maven에서는 profile이라고 하고, 다음과 같이 정의한다. 각 빌드 환경마다 여기서는 <environment> 라는 환경 변수를 지정하게 하였다.

<!--  profile definition -->

    <profiles>

        <profile>

               <id>local</id>

               <properties>

                       <environment>local</environment>

               </properties>

        </profile>

        <profile>

               <id>dev</id>

               <properties>

                       <environment>dev</environment>

               </properties>

        </profile>

        <profile>

               <id>qa</id>

               <properties>

                       <environment>qa</environment>

               </properties>

        </profile>

        <profile>

               <id>stage</id>

               <properties>

                       <environment>stage</environment>

               </properties>

        </profile>

  </profiles>

 

그리고 빌드시 ${basedir}/resource-{environment} 디렉토리를  webapp/ 아래에 복사하도록 하고자 한다. 이를 위해서는 war를 패키징 하는 war 플러그인의 속성세 web-resource (WEB-INF 디렉토리)를 빌드 타겟에 맞게 선택이 되도록 다음과 같이 지정한다.

환경에 맞게 정의한다.
다음 예제는 dev 환경용 설정 파일이다.

             <!--  war plug in  -->

             <plugin>

                <groupId>org.apache.maven.plugins</groupId>

                       <artifactId>maven-war-plugin</artifactId>

                       <version>2.3</version>

                       <configuration>

                               <warSourceDirectory>${basedir}/src/main/webapp</warSourceDirectory>

                              <webResources>

                                      <webResource>

                                                     <directory>${basedir}/src/main/resources-${environment}</directory>

                                      </webResource>

                              </webResources>

                       </configuration>

               </plugin>

 

여기 까지 진행한 후에, 각 환경에 맞게 packaging을 하기 위해서는 mvn에서 -P{environment} 이름을 적어주면 된다.

예를 들어 qa환경용으로 war 파일을 만들기 위해서는

 

% mvn -Pqa package

 

와 같이 명령을 실행하면 된다.

 

RPM으로 패키징 및 배포 하기

maven의 빌드 패키징의 문제가 java에 관련된 jar, ear 또는 war 파일만 딱 생성하고 패키징을 한다. 그러나 서버를 배포하기 위해서는 이런 jar형태의 파일 뿐만 아니라 애플리케이션 들이 참고하는 각종 설정 파일들을 함께 배포해야 할 필요가 있다. 운이 좋게 모든 파일들이 jar,ear,war등에 함께 패키징 되면 좋겠지만, 애플리케이션이 어느정도 규모가 되면 함께 패키징이 되지 않는 파일들이 발생한다.

이런 문제를 해결하기 좋은 방법으로는 linux에서 제공하는 rpm 패키징을 이용하는 방법이 있다.

maven에서는 이 rpm 패키징을 플러그인으로 제공한다.

(http://mojo.codehaus.org/rpm-maven-plugin/ )

사용법도 상당히 간단하기 때문에 추천한다. 플러그인은 linux 명령어인 rpmbuild 내부적으로 수행하기 때문에, 반드시 windows 아닌 linux machine에서만 사용이 가능하다.

 

rpm package 여러가지 기능을 수행할 있지만, 여기서 설명하는 것은 기본적으로 file copy 이다.

 

              <!--  rpm plug in -->

              <plugin>

                <groupId>org.codehaus.mojo</groupId>

                <artifactId>rpm-maven-plugin</artifactId>

                <version>2.0-beta-2</version>

                <configuration>

                        <copyright>2013 - Terry Cho</copyright>

                        <group>terry/example</group>

                        <mappings>

                        <mapping>

                               <directoryIncluded>false</directoryIncluded>

                                 <directory>${rpm.install.webapps}</directory>

                                <username>bwcho</username>

                                <groupname>bwcho</groupname>                            

                                 <sources>

                                         <source>

                                                 <location>${basedir}/target/${artifactId}.war</location>

                                         </source>

                                 </sources>

                        </mapping>

                        </mappings>

                    </configuration>

              </plugin>

 

설정을 제공하는 <configuration> 부분을 보자

rpm 패키지에 대한 관리를 위해서 rpm 그룹핑을 제공하는데, <group> rpm 패키지의 그룹을 정의한다. group 여러개의 컴포넌트를 함께 배포할때 매우 유용하게 사용될 있다. 예를 들어 비디오 인코딩 애플리케이션이 있고, 애플리케이션이 upload, download,encoding 컴포넌트 3개로 구성되어 있고, 각각 배포 되어야 한다면 그룹명을 videoencoding/upload, videoencoding/download, videoencoding/encoding 등으로 지정하여 구별할 있다.

다음으로 <mappings> 엘리먼트에서는 복사할 파일 리스트를 지정할 있다. 하위 엘리먼트로 <mapping>이라는 엘리먼트에서 개개별의 디렉토리나 파일을 지정하면 된다.

<sources><source> 엘리먼트에서 복사할 원본 파일 리스트를 지정하고,

<directory> 엘리먼트에서 복사될 타겟 디렉토리를 지정한다.

그리고 <username> <groupname> 파일이 복사될때 (생성되는 파일의) user id group (unix) 정의한다.

위의 예제는 war 파일을 ${rpm.install.webapps} 복사하는 rpm 파일을 생성하는 스크립트이다.

이렇게 생성된 스크립트는

 

% mvn package rpm:rpm  으로 수행될 있다.

앞서 설명한, profile 함께 사용하면, dev rpm 패키지 생성은

% man -Pdev package rpm:rpm

으로 하면된다.

 

이렇게 생성된 rpm 파일들은 대상 시스템으로 복사되서 rpm 수행하는 것만으로도, 모든 의존성을 가진 파일들을 함께 설치할 있다.

아울러 rpm 가지고 있는 고유한 기능으로, 특정 버전으로의 roll back등이 가능하다.

rpm 파일들을 대상 시스템으로 자동으로 복사 실행 하는 부분에 대해서는 뒤에서 다시 살펴 보도록 하자

Maven을 이용한 Jersey + Spring + MyBatis 기반의 REST 애플리케이션 개발


조대협(http://bcho.tistory.com)

빌드 스크립트 설정

개발 환경에서 가장 중요한 빌드에서 부터 알아보자, 우리가 정의하는 빌드는 다음과 같다.

"빌드란, 실행 환경에 맞춰서 소스 코드를 실행 가능한 형태의 바이너리로 변경 및 패키징 하는  일련의 과정을 정의한다."

단순하게 소스코드를 바이너리로 바꾸는 컴파일이 될 수 도 있고, 실행에 필요한 각종 리소스 (이미지, 각종 설정 파일등)을 실행 환경(서버 주소등)에 맞춰서 같이 패키징 하는 과정을 이야기 한다.

이 빌드 여러개의 연속된 작업을 포함하기 때문에, 보통 스크립트를 기반으로 수행이 되는데, C/C++의 경우에는 make 기반의 빌드 스크립트가, 자바의 경우에는 ant maven이 널리 사용된다.

 

ant vs maven

자바 기반에서 현재 가장 인기 있는 빌드 스크립트 툴은 ant maven이다. 요즘은 상당 부분이 maven으로 넘어갔다. 그렇다면 이 각각 빌드 스크립트 툴의 장점은 무엇일까?

 

유연성과 관리

ant의 경우에는 자유도가 상당히 높다. 파일을 복사하거나, 쉘 명령을 실행할 수 도 있다. 스크립트내에서 빌드, 패키징 은 물론이고 배포,테스트, 미들웨어에 대한 기동이나 정지까지 모든 것이 가능하다. 자유도가 높다는 이야기는 반대로 이야기 하면 표준화가 어렵다는 이야기가 된다.

잘 관리 하지 않으면 프로젝트 마다 또는 팀마다 빌드 스크립트가 제각각이다. 표준화가 되지 않은 빌드 스크립트는 새롭게 합류하는 개발자들에게 별도의 learning curve를 요구하게 되고, 실수를 유발한다.

또한 복잡한 형태의 빌드 프로세스를 요구 하는 개발의 경우에는 빌드 스크립트 자체를 만드는 것 자체가 복잡한 일이 된다.

 

maven은 이런 단점을 보완해서 개발되었다.

maven은 템플릿 기반으로 빌드 스크립트를 구성한다. 템플릿 기반이란, 특정 애플리케이션 타입에 대해서, 디렉토리 구조, 빌드 프로세스등이 모두 정해져있다. 그래서 애플리케이션 타입에 따라서 템플릿만 골라서 사용하게 되면 누구나 같은 디렉토리 구조에서 같은 빌드 프로세스에서 개발하게 된다. 그래서 learning curve가 상대적으로 낮고, 누구나 표준화된 환경에서 빌드가 가능하다.

반대로, 템플릿 이외의 기능에 대해서는 유연성이 떨어져서, 마음대로 무엇인가를 추가 하는 것이 어렵다. (예를 들어서 파일을 특정 클라우드에 복사한다던지). 물론 maven도 플러그인이라는 기능을 통해서 템플릿의 기능을 확장할 수 있는 기능을 제공하지만, 이 플러그인이라는 것 자체의 개발이 쉽지가 않기 때문에 템플릿의 기능을 벗어나는 순간 learning curve가 급속하게 올라간다. 다행이도 근래에는 maven에서 사용할 수 있는 플러그인들이 많이 있기 때문에, 이런 문제들은 상대적으로 줄어들고 있다.

 

의존성 관리

다음으로 라이브러리에 대한 의존성에 대해서 고민해볼 필요가 있다.

ant의 경우에는 소스코드와 라이브러리 그리고 기타 의존된 리소스 파일 (설정 파일, 스크립트, 이미지)등이 디렉토리에 있는 것을 가정하고 빌드를 진행한다. 전적으로 이러한 파일들을 챙기는 것은 개발자와 빌드 메니져의 역할인데, 이러다 보니 특히 라이브러리 관련해서 문제가 발생한다.

예를 들어 원 소스는 spring 3.0 라이브러리를 바탕으로 개발이 되었는데, 어떤 개발자는 spring 2.0을 사용해서 컴파일하고 어떤 개발자는 spring 3.1을 사용해서 컴파일 하는, 이런 일들이 발생할 수 있다는 것이다. (실제로 종종 발생하는 일)

maven의 경우 재미있는 것은 이렇게 컴파일을 하는 데 필요한 라이브러리에 대한 의존성을 정의하고, 정확한 버전을 정의하면, 컴파일 타임에 원격에 있는 repository로 부터, 명시된 버전의 라이브러리를 다운 받아서 컴파일과 패키징을 진행하기 때문에, 라이브러리의 버전 불일치가 발생될 염려가 없다. 또한 오픈소스등에서 작성한 well-known 라이브러리가 아니더라도, 자체적으로 repository 시스템을 구축하여, 팀내에서 개발한 라이브러리를 배포해놓고 사용할 수 있다. 이런 시나리오는 여러개의 모듈을 동시에 개발하는 프로젝트 팀의 경우, 모듈간의 의존 관계에서 오는 문제를 해결할 수 있는 좋은 방안이 된다.

 

근래에는 이런 장점 때문에, 유연성이 다소 적더라도 maven이 많이 사용되는 추세이다.

이제 부터 간단하게 maven 기반의 빌드 방법에 대해서 설명해보도록 한다.

여기서 소개하는 빌드 스크립트는 web application 기반의 빌드 스크립트로, 다음과 같은 시나리오를 구성할것이다.

 

-       Jersey 기반의 JSON/HTTP REST API 지원, Spring DI 기반으로 비지니스 로직 구현, MyBatis를 이용한 데이타 베이스 접근

-       빌드 환경은 개발자 PC local, 개발 환경인 dev, 스테이징 환경 stage,검증 환경인 qa 그리고 실 운영 환경이 production 환경으로 구성된다.

-       빌드 스크립트를 통해서 개발자는 빌드,배포 및 Tomcat상에서 실행 및 테스트를 진행할 수 있어야 한다.

 

maven을 이용한 Jersey + Mybatis + Spring 기반의 개발 환경 구축하기

먼저 maven 이용해서 개발을 하려면 프로젝트를 만들어야 한다. 프로젝트는 특정한 애플리케이션 타입에 맞는 템플릿을 이야기 한다. 디렉토리 구조나 빌드 프로세스들이 미리 정해져 있는 개발을 위한 하나의 비어 있는 틀이다.

 

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false  -DgroupId=spring-tutorial -DartifactId=terry -Dversion=1.0-SNAPSHOT -Dpackage=terry.spring.tutorial.ch1

 

이렇게 하면 하나의 비어 있는 프로젝트가 생성이 된다.

프로젝트가 생성이 되면, 해당 애플리케이션을 개발하는데 필요한 디렉토리 구조나 필요한 라이브러리 들이 자동으로 다운로드 되서 설치 된다. 프로젝트는 java web application 지원하는 war형태의 프로젝트이다.

maven 앞서도 설명하였듯이, 프로젝트를 생성할때, 프로젝트의 타입을 정할 있다. 여기서는 war 기반의 개발을 하기 위한 web-app 형태의 프로젝트를 생성하였는데, 프로젝트 타입은 -DarchetypeArtificatId 지정할 있다. (여기서는 가장 기본적은 maven-archetype-webapp 사용하였다.)

 

그럼 생성된 프로젝트의 모양을 살펴보자. 생성된 프로젝트의 디렉토리 구조는 다음과 같다.

 


pom.xml

ant ant.xml이나 make makefile처럼 build 대한 모든 configuration 지정한다.

 

다음으로 생성된 디렉토리를 살펴보자 src에는 *.java 소스 파일과 webapp (war root)디렉토리에 들어가는 내용들과 각종 설정 파일들이 들어간다. 상세한 내용을 살펴보자.

 

src/main/resource

디렉토리는 각종 설정 파일이 위치하는 곳이다. 디렉토리 안에 있는 파일을 class path 포함된다. war 경우에는 디렉토리에 있는 파일들은 WEB-INF/classes 디렉토리 아래에 그대로 들어간다.

예를 들어 src/main/resources/sqlmap/sqlmapper.xml 파일은 컴파일 후에, war파일내의 WEB-INF/classes/sqlmap/sqlmapper 저장된다.

 

src/main/webapp

Web resource 해당 하는 부분이다. war안에 / 디렉토리에서 부터 들어가는 html 각종 리소스 파일들을 모두 정의하낟.

 

그런데, 정작 보면 java 코드를 넣을 소스 디렉토리가 없다.

src/main/ 디렉토리 밑에 java 디렉토리를 하나 만들자.

아래 화면은 src/main/java 디렉토리를 만들고, terry.restapi.model.ContactVo.java 구현한 디렉토리 구조이다.

 

 

아래 코드는 Contact.java 클래스로, 간단하게 사용자의 이메일,이름과 전화번호를 저장하는 VO 클래스 이다.

package terry.restapi.model;

import javax.xml.bind.annotation.XmlRootElement;

 

@XmlRootElement(name="Contact")

public class ContactVo {

        String email;

        String name;

        String phone;

        public String getEmail() {

               return email;

        }

        public void setEmail(String email) {

               this.email = email;

        }

        public String getName() {

               return name;

        }

        public void setName(String name) {

               this.name = name;

        }

        public String getPhone() {

               return phone;

        }

        public void setPhone(String phone) {

               this.phone = phone;

        }

       

}

 

여기 까지 진행을 했으면, war 파일이 어떻게 만들어지는지 테스트를 해보자

% mvn package 실행하면

${HOME} 디렉토리 아래 /target/terry.war 파일이 생성된 것을 있다.

파일을 풀어보면 앞에서 만든 WEB-INF/classes/terry/restapi/model/Contact.class 패키징 되어 있는 것을 확인할 있다.

 

지금까지 간단하게 maven 프로젝트를 만들고, 소스코드를 넣고, 웹에 관련된 리소스를 정의한후에, 컴파일 war 패키징을 해보았다.

 

maven ant make 처럼 일종의 build target 제공하는데, maven에서는 이를 goal이라고 한다. pom.xml 스크립트에 따라서 다양한 goal 정할 있으나, maven에서 미리 정해져 있는 goal 중요한 goal들은 다음과 같다.

 

mvn compile : 컴파일 수행. (프로젝트내의 java 코드를 컴파일 한다.)

mvn package : jar,war,ear pom.xml에서 정해진 형태로 파일로 패키징. (컴파일을 한후, 프로젝트 내용에 따라서 패키징을 수행한다.)

mvn test : JUnit 테스트 수행.

mvn install : local repository (PC내의 디렉토리)에 패키징된 파일을 저장

mvn deploy  : remote repository (Nexus)에 패키징된 파일 저장

mvn clean : 컴파일 내용 모두 삭제

 

여기서 compile,package,clean 등은 거의 모든 빌드 스크립트에서(ant ) 공통적으로 지원하기 때문에 별도의 설명을 하지 않는다.

install deploy 대해서 살펴보자

 

앞서도 설명했듯이, maven library 대한 dependency 지정하고 스크립트를 수행하면, repository라는 곳에서 해당 라이브러리들을 읽어온다. 그러면 repository 어디일까?

mvn install 컴파일된 패키지들를 local pc 라이브러리 저장소에 배포한다. 다른 프로젝트가 라이브러리를 사용하고자 한다면, local pc 내에서 라이브러리를 찾아서 같이 컴파일 있다. 그러나 local pc에만 배포가 되었기 때문에 다른 사람은 라이브러리를 참조해서 사용할 없다. 그래서 다른 사람이 라이브러리를 참조할 있게 하려면, 네트워크 상의 공용 repository 필요하다. 이러한 공용 repository 라이브러리를 배포하는 명령이 mvn deploy이다.

spring이나 기타 라이브러리등은 각각의 공용 repository 가지고 있고, mvn 역시 이러한 repository list 기반으로 해서 라이브러리를 로딩하는데, 우리 회사나 팀에서만 사용할 있는 repository 별도로 구축하려면 어떻게 해야 할까? nexus라는 제품을 설치하면, 사내에 전용 repository 서버를 구축할 있다. (http://www.sonatype.org/nexus/)

 

maven 프로젝트 생성, 디렉토리 구조의 이해 그리고 maven 통한 컴파일 패키징에 대한 설명이 끝났다. 그러면 이제 부터, Jersey + Spring + Mybatis 개발하기 위한 설정을 해보자, 먼저 pom.xml 위의 세가지 프레임웍을 사용하기 위해서 라이브러리를 정의해야 한다.

라이브러리 정의는 <dependencies>엘리먼트 아래에 라이브러리 <dependency> 라는 엘리먼트로 정의한다.

 

<dependencies>

    <!--  jersey dependency -->

        <dependency>

            <groupId>com.sun.jersey</groupId>

            <artifactId>jersey-server</artifactId>

            <version>1.17</version>

        </dependency>

: 아래 생략

 

위의 3가지 프레임웍을 사용하기 위해서 필요한 dependency 다음과 같다.

정확하게 여기서는 JSON/REST 이용하기 위해서 Jersey 사용하고, Spring 3.1 에서 Dependency Injection만을 이용할것이며, MyBatis MySQL 사용을 위해서 MySQL JDBC Driver 사용할것이다.

아래는 지면 관계상, dependency 테이블로 정리해놨다. 글의 뒷부분의 pom.xml 전문을 따로 첨부했으니, 참고하기 바란다.

 

프레임웍 이름

groupId

artifactId

version

Jersey

com.sun.jersey

jersey-server

1.17

com.sun.jersey

jersey-servlet

1.17

com.sun.jersey

jersey-json

1.17

Spring

org.springframework

spring-core

3.1.1.RELEASE

org.springframework

spring-context

3.1.1.RELEASE

org.springframework

spring-beans

3.1.1.RELEASE

org.springframework

spring-web

3.1.1.RELEASE

org.springframework

spring-webmvc

3.1.1.RELEASE

Spring + Jersey 연결

com.sun.jersey.contribs

jersey-spring

1.17

Spring + Mybatis 연결

org.mybatis

mybatis-spring

1.0.1

MySQL JDBC

mysql

mysql-connector-java

5.1.24

 

여기서 spring+Jersey 연결에 사용되는 jsersy-spring artifact 잠시 살펴볼 필요가 있다. jersey-spring artifact 자체적으로 spring 프레임웍을 포함하고 있기 때문에, 우리가 정의한  Spring framework 중복 가능성이 있다. 그래서, jersey-spring dependency 의해서 spring framework 중복적으로 가지고 오지 않도록 해당 모듈들을 다음과 같이 exclude(제외)하도록 선언한다.

<!--  jersey + spring depdendency  -->

     <dependency>

                       <groupId>com.sun.jersey.contribs</groupId>

                       <artifactId>jersey-spring</artifactId>

                       <version>1.17</version>

                       <exclusions>

                              <exclusion>

                                      <groupId>org.springframework</groupId>

                                      <artifactId>spring</artifactId>

                              </exclusion>

                               <exclusion>

                                      <groupId>org.springframework</groupId>

                                      <artifactId>spring-core</artifactId>

                              </exclusion>

                              <exclusion>

                                      <groupId>org.springframework</groupId>

                                      <artifactId>spring-web</artifactId>

                              </exclusion>

                              <exclusion>

                                      <groupId>org.springframework</groupId>

                                      <artifactId>spring-beans</artifactId>

                              </exclusion>

                              <exclusion>

                                      <groupId>org.springframework</groupId>

                                      <artifactId>spring-context</artifactId>

                              </exclusion>

                       </exclusions>

               </dependency>

자아. 이제 빌드 스크립트는 Jersey + Mybatis + Spring DI 이용한 개발 준비가 되었다. 그러면 실제 코딩에 들어가 보자

java/terry/restapi 아래, MVC 모델에 맞춰서 service,dao,model 디렉토리를 만들고 아래와 같이 클래스들을 구현한다.

 

java/terry/restapi/dao/ContactDao.java

package terry.restapi.dao;

 

import java.util.HashMap;

 

import terry.restapi.model.ContactVo;

 

public interface ContactDao {

       

        public void create(ContactVo contact);

        public ContactVo get(String email);

        public void delete(String email);

        public void update(String email,ContactVo contact);

}

 

java/terry/restapi/dao/ContactImpl.java Mybatis 이용한 contact 테이블에 대한 CRUD 구현한다.

package terry.restapi.dao;

 

import org.mybatis.spring.support.SqlSessionDaoSupport;

 

import terry.restapi.model.ContactVo;

 

public class ContactDaoImpl extends SqlSessionDaoSupport implements ContactDao {

 

        public void create(ContactVo contact) {

               // TODO Auto-generated method stub

 

        }

 

        public ContactVo get(String email) {

               // TODO Auto-generated method stub

               ContactVo contact = (ContactVo)getSqlSession().selectOne("contactdao.getUserByEmail", email);

               return contact;

        }

 

        public void delete(String email) {

               // TODO Auto-generated method stub

 

        }

 

        public void update(String email, ContactVo contact) {

               // TODO Auto-generated method stub

 

        }

 

}

 

java/terry/restapi/model/ContactVo.java 앞에서 이미 구현했기 때문에, 앞의 코드 참고

 

다음은 Jersey 이용한 REST API 구현이다. 아래 코드는 편의상 create,select,update 구현하였다.

package terry.restapi.service;

 

import javax.ws.rs.Consumes;

import javax.ws.rs.GET;

import javax.ws.rs.POST;

import javax.ws.rs.PUT;

import javax.ws.rs.Path;

import javax.ws.rs.PathParam;

import javax.ws.rs.Produces;

import javax.ws.rs.core.MediaType;

import javax.ws.rs.core.Response;

 

import terry.restapi.dao.ContactDao;

import terry.restapi.model.ContactVo;

 

@Path("/contact")

public class ContactService {

        static ContactDao dao = null;

        public void setContactDao(ContactDao dao){this.dao = dao;}

        public ContactService(){

               //if(dao == null)      setContactDao(new ContactDao());

        }

       

        /**

         * Create Contact Record

         * @param contact

         * @return

         */

        @POST

        @Consumes(MediaType.APPLICATION_JSON)

        public Response create(ContactVo contact){

               dao.create(contact);

               return Response.status(200).entity(contact).build();

 

        }

        /**

         * Query Contact record by email id

         * @param email

         * @return

         */

        @GET

        @Produces(MediaType.APPLICATION_JSON)

        @Path("{email}")

        public ContactVo get(@PathParam("email") String email){

               return dao.get(email);

        }

        /**

         * Upadte Contact Record by email

         * @param email

         * @param contact

         * @return

         */

        @PUT

        @Path("{email}")

        @Consumes(MediaType.APPLICATION_JSON)

        @Produces(MediaType.APPLICATION_JSON)

        public Response  update(@PathParam("email") String email, ContactVo contact){

               dao.update(email, contact);

               return Response.status(200).entity(contact).build();

        }

}

 

 

자아 코드 구현이 끝났다.

이제 코드를 실행하기 위해서는 다음과 같은 추가 작업이 필요하다.

/webapp/WEB-INF/config/spring-context.xml Spring Bean 정의하기 위한 spring context 파일 작성

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xmlns:context="http://www.springframework.org/schema/context"

        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

 

        <!--  load configuration file -->

         <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

          <property name="locations">

           <value>/WEB-INF/config/config.properties</value>

          </property>

         </bean>

         

        <!--  create rest service object and inject dao -->

        <bean class="terry.restapi.service.ContactService" id="contactService">

               <property name="contactDao" ref="contactdao" />

        </bean>

        <!-- declare dao object  -->

        <bean class="terry.restapi.dao.ContactDaoImpl" id="contactdao">

               <property name="sqlSessionFactory" ref="sqlSessionFactory" />

        </bean>

       

        <!--

               mybatis configuration

               sqlSessionFactory & sqlSessionTemplate are required

         -->

        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

               <property name="dataSource" ref="dataSource" />

               <property name="configLocation" value="/WEB-INF/config/mybatis-config.xml"/>

        </bean>

        <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">

               <constructor-arg ref="sqlSessionFactory" />

        </bean>

       

        <!-- 

               data source configuration

               for testing purpose , it uses simple jdbc datasource

         -->

        <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">

               <property name="driverClass" value="${mybatis.jdbc.driverclass}"/>

               <property name="url" value="${mybatis.jdbc.url}" />

               <property name="username" value="${mybatis.jdbc.username}" />

               <property name="password" value="${mybatis.jdbc.password}" />

        </bean>

</beans>

 

그리고 위의 파일 내용을 보면, /WEB-INF/config/config.properties라는 파일을 읽게 되어 있는데, 여기에는 MYSQL DBMS 접속에 필요한 URL,PORT,사용자 id,passwd등이 들어간다.

/webapp/WEB-INF/config/config.properties

 

mybatis.jdbc.driverclass=com.mysql.jdbc.Driver

mybatis.jdbc.url=jdbc:mysql://localhost:3306/development

mybatis.jdbc.username=developer

mybatis.jdbc.password=developer

 

/webapp/WEB-INF/config/mybatis-config.xml MyBatis 사용하는 데이타 베이스 연결 정보등의 설정 파일

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "HTTP://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

 <settings>

  <setting name="cacheEnabled" value="false" />

  <setting name="useGeneratedKeys" value="true" />

  <setting name="defaultExecutorType" value="REUSE" />

 </settings>

 

 <mappers>

  <mapper resource="sqlmap/ContactDao_map.xml" />

 </mappers>

</configuration>

 

/webapp /WEB-INF/web.xml 초기에 Spring Context Jersey framework 로딩하기 위한 설정

<!DOCTYPE web-app PUBLIC

 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"

 "http://java.sun.com/dtd/web-app_2_3.dtd" >

 

<web-app>

  <display-name>Archetype Created Web Application</display-name>

 

        <!--  load spring context configuration -->

        <context-param>

               <param-name>contextConfigLocation</param-name>

               <param-value>

                       /WEB-INF/config/spring-context.xml

                       <!-- /WEB-INF/config/mybatis-context.xml -->

               </param-value>

        </context-param>

 

        <!--  load listener  -->

        <listener>

               <listener-class>

                       org.springframework.web.context.ContextLoaderListener

               </listener-class>

        </listener>

       

        <!--  configure jersey/JSON servlet -->

        <servlet>

          <servlet-name>Jersey Web Application</servlet-name>

               <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>

                     <init-param>

                              <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>

                              <param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>

                 </init-param>

                 <init-param>

                  <param-name>com.sun.jersey.config.property.packages</param-name>

                  <param-value>terry.restapi</param-value>

                </init-param>

                <init-param>

                              <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>

                              <param-value>true</param-value>

                       </init-param>

                          <load-on-startup>1</load-on-startup>

            </servlet>

       

            <servlet-mapping>

               <servlet-name>Jersey Web Application</servlet-name>

                <url-pattern>/*</url-pattern>

            </servlet-mapping>

</web-app>

 

/resources/sqlmap/ContactDao_map.xml MyBatis 실행하는 SQL 들어 있는 SQL Mapper 파일 작성

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

 

<mapper namespace="contactdao">

        <!-- 

               select user by email id

        -->

        <resultMap type="terry.restapi.model.ContactVo" id="resultmap.terry.restapi.model.ContactVo">

               <id column="email" property="email"/>

               <result column="name" property="name"/>

               <result column="phone" property="phone"/>

        </resultMap>

        <select id="getUserByEmail" parameterType="String" resultMap="resultmap.terry.restapi.model.ContactVo">

               select email,name,phone

               from contact_table

               where email = #{email}

        </select>

</mapper>

 

여기 까지 구현하였으면 디렉토리는 다음과 같은 형태가 된다..


 

이제 mvn package 명령어를 이용하여 war 파일을 만들고, tomcat 배포한후,

MySQL DB 아래와 같은 scheme contact_table 만들면 실행을 위한 준비가 끝난다.

 


수동으로 Tomcat 설치하고 실행하고 배포하면 까다롭기도 하거니와, 개발자 마다 다른 버전이나 다른 설정을 있기 때문에,  이번에는 mvn 스크립트내에서 빌드 과정내에, Tomcat 자동으로 기동 시키고 배포하는 스크립트를 추가해보자.

 

과정은 maven 플러그인이라는 기능을 이용하면 된다.

pom.xml <project> 엘리먼트 아래 다음과 같은 내용을 추가한다.

<build>

    <finalName>restapi</finalName>

         <plugins>

              <!-- 

                tomcat7 plugin

                caution!!. mvn tomcat:xxx will invoke default plugin

                do. mvn tomcat7:xxx

                -->

              <plugin>

                          <groupId>org.apache.tomcat.maven</groupId>

                  <artifactId>tomcat7-maven-plugin</artifactId>

                  <version>2.1</version>

                          <configuration>

                                 <warSourceDirectory>${basedir}/target/${artifactId}</warSourceDirectory>

                          </configuration>

             </plugin>

         </plugins>

  </build>

위의 내용은 tomcat 7 플러그인을 추가하여, 빌드 후에 tomcat 기동할 있게 해주며, tomcat 실행시 ${basedir} (pom.xml 있는 프로젝트의 루트 디렉토리)아래 /target/terry 디렉토리를 war root 디렉토리로 인식하고 기동하게 한다.

${artifactid} maven 프로젝트 생성시 지정했던 프로젝트의 이름으로, war 빌드 스크립트는 별도의 지정이 없는한, war파일명을 ${artifactid}.war ${basedir}/target/ 아래 생성한다. 또한 컴파일 과정에서 war파일이 풀린 모든 파일들을 위치해놓는다.

 

자아 이제 모든 스크립트가 완성이 되었다.

%mvn package tomcat7:run

실행해보자

package 컴파일 war파일과 war 파일이 풀린 모든 파일들(exploded war) 만들게 하고

tomcat7:run tomcat7 위에서 만든 exploded war파일들을 읽어서 기동하게 한다.

 

이제 로컬 환경에서 배포와 실행이 되었으면 REST API 호출하여 테스트를 해보자. 간단한 테스트를 위해서 Google Chrome Browser Advanced REST Client 사용하였다.

 

 



 

 

Maven pom properties

ALM/Build Automation (빌드 자동화) | 2013.03.26 16:35 | Posted by 조대협

POM 파일에 등장하는 설정

build : 프로젝트 빌드와 관련한 기본적인 소스 디렉토리 구조, 빌드 산출물 디렉토리 구조, 빌드시 사용할 플러그인 등을 관리

sourceDirectory : 실제 서비스를 담당하는 자바 소스 코드를 관리하는 디렉토리. 기본 값은 src/main/java

testSourceDirectory : 테스트 소스를 관리하기 위한 디렉토리. 배포시 이 디렉토리는 배포 되지 않는다. 기본값은 src/test/java

outputDirectory : soruceDiectory의 소스를 컴파일한 결과물이 위치하는 디렉토리. 기본값은 target/classes

testOutputDirectory : testSourceDirectory의 소스를 컴파일한 결과물이 위치하는 디렉토리. 기본값은 target/test-classes

resources: 서비스에 사용되는 자원을 관리하는 디렉토리. 기본값은 src/main/resources (클래스 패스에 포함됨)

scriptSourceDirectory


mvn help:effective-pom 명령어를 수행하면, 현재 pom 파일에서 default값을 포함한 full pom 파일을 볼 수 있음


국내에서도 ALM 의 개념을 가지고 접근하는 제품이 있다는 것은 참으로 반가운 일이다.
http://www.snh.co.kr/?s=product&m=shape1
형상관리 제품은 많이 보았는데, ALM의 개념을 가지고 있는 제품은 처음 본것 같다...
그러나 역시 한국 고객 특성에 맞도록 되어 있다...
그말은 실용적이기 보다는 고객의 엉뚱한 요건(?)을 만족 시키기 위한 기능들이 있다는것...
고객의 엉뚱한 요건이란, 실제 업무 프로세스 위주로 구성이 되는것인데.
변경 요청이나 승인 프로세스들은 상당히 프로세스가 고도화된 후에나 ALM에 녹일 수 있는 것인데..
아마도 고객의 커스터마이징 요청에 의해서 그런 그림이 나오지 않았나 싶다...

고객으로 부터 ALM등에 대한 개발 Layer 이상의 관리적인 요건이 있으면 차라리 이런 업체에 넘겨 버리는 것이 좋을것 같다.

이슈 관리 도구도 있네..
http://www.snh.co.kr/brochure/CodeInside2_workitem/CodeInside2.html
TAG ALM, 실루엣

이클립스 ALF

ALM/Build Automation (빌드 자동화) | 2008.08.12 00:34 | Posted by 조대협

이클립스 프로젝트로 진행중인 ALM
업체들이 뭉쳐서 진행하고 있는 것 같은데.
다소 약한 느낌이 들기도...

http://www.eclipse.org/alf/Flash/ALF003Demo.htm

테스트쪽을 분리해놓고 테스트 관리 부분이 강화 된것이 흥미롭다.

'ALM > Build Automation (빌드 자동화)' 카테고리의 다른 글

Maven pom properties  (0) 2013.03.26
국산 형상 관리 솔루션 실루엣  (0) 2008.08.12
이클립스 ALF  (0) 2008.08.12
괜찮은 ALM툴  (5) 2008.06.11
Hudson을 이용한 빌드 배포 테스트 자동화  (8) 2008.04.07
플러그인 개발 순서  (0) 2008.04.02

괜찮은 ALM툴

ALM/Build Automation (빌드 자동화) | 2008.06.11 17:12 | Posted by 조대협

한동안 시스템 테스트 관계로 이쪽을 신경을 못썼는데
간만에 좋은 툴을 소개 받아서 포스팅!!

ALM (Application Life cycle management)
도구로 간단하게 JIRA/MANTIS 와 같은 버그 트랙킹, 이슈 트랙킹에서 부터 Polarion같은 툴들이 있는데
대부분 ALM등은 무게가 무겁고 복잡도가 높다.

http://www.intland.com/ 에서 나온 Code Beamer라는 툴이 있는데
일단 community free license지원하고 5 user까지 무료.

그리고 ALM에 필요한

1. 이슈 관리 밑 트랙킹
2. Wiki를 통한 지식 공유
3. Source repository
4. source version 관리 tracking
5. Discussion forum
6. 문서 변경 관리

들까지 지원한다.
국내에도 레퍼런스 사이트가 몇군데 있고, Jolt Award까지 수상했으니..
시간 나면 한번 써볼만 한듯~~

'ALM > Build Automation (빌드 자동화)' 카테고리의 다른 글

국산 형상 관리 솔루션 실루엣  (0) 2008.08.12
이클립스 ALF  (0) 2008.08.12
괜찮은 ALM툴  (5) 2008.06.11
Hudson을 이용한 빌드 배포 테스트 자동화  (8) 2008.04.07
플러그인 개발 순서  (0) 2008.04.02
Code Inspection Tools  (0) 2008.02.29
Hudson을 이용한 빌드와 테스트의 자동화


 

2007-04-04
BEA Systems Korea
Sr consultant Byungwook Cho (bcho@bea.com)

Continuous Integration(점진적 통합,이하 CI)이란, 개발자가 각각 개발한 소스코드를 모아서 한꺼번에 빌드하는 통합 빌드의 과정을 특정 시점이 아니라 매일이나 매주와 같이 아주 잦은 주기로 수행함으로써 통합에서 발생하는 오류와 시간을 줄이기 위한 기법이다.
Extreme Programming Community (XP)에서 애자일 방법론의 일부로 Kent Beck에 의해서 고안된 방법으로 다음과 같은 특징을 가지고 있다.

1. CI의 특징
(1) 소스코드 일관성 유지
CI툴을 설정하기 위해서는 기본적으로 소스 관리 시스템이 필요하다.
대표적인 소스 관리 시스템은 Subversion,CVS,Perforce등이 있다.
CI툴은 이 소스 관리 시스템으로부터 프로젝트 소스의 메인 브랜치(trunk 라고도 한다.) 코드를 Check out 받아서 빌드를 수행한다.

(2) 자동 빌드
소스 코드에 대한 빌드는 CI툴에 의해서 자동적으로 이루어 져야 한다.
빌드가 이루어지는 시점을 정하는 방법이 두가지가 있는데 다음과 같다.

1) 커밋에 따른 자동 빌드
다른 방법으로는 소스코드가 소스 관리 시스템에 커밋이 되었을 때 마다 CI툴이 이를 감지 하고 자동으로 빌드를 수행하도록 설정할 수 있다.
이렇게 설정할 경우 소스 코드의 변경이 있을 때 마다 빌드 작업을 수행하기 때문에 소스 관리 시스템에 저장된 소스 코드에 대한 무결성을 보장하기는 매우 좋지만, 빌드 시간이 길 경우 빌드가 적체 되는 현상이 발생할 수 있다.
(일반적으로 대규모 애플리케이션의 FULL 빌드는 길게는 2~3시간 까지 소요될 수 있다.) 그래서 이 방법은 빌드 시간이 오래 걸리는 경우나 커밋이 자주 발생하는 경우에는 적절하지 않다.

2) 시간 간격에 의한 빌드
일정 시간 간격을 정해서 빌드를 하는 방법이다. 매일 5시에 빌드를 한다. 또는 매주 금요일 저녁 5시에 빌드를 한다는 것과 같이 주기를 정할 수 있다. 빌드 스케쥴이 미리 정해져있기 때문에 개발자들이 커밋에 대한 스케쥴을 관리할 수 있고 빌드 시간이 오래걸리는 대규모 빌드에도 적정하다.
 빌드 시간을 정할 때 중요한 점은 가급적이면 퇴근 시간 1~2시간 전으로 개발자들이 퇴근하기 전 시간으로 여유를 두는 것이 좋다.
이후 빌드가 깨진 경우는 컴파일이 실패하였거나 테스트가 통과하지 못하였을 경우인데 이때 소스 관리 시스템에 저장된 코드는 문제가 있는 코드이다. (빌드가 깨졌기 때문에) 이 코드들을 다른 개발자가 체크아웃 받아서 개발을 했을 때 잘못된 코드로 인해서 잘못된 개발 방향으로 갈 수 가 있기 때문에 빌드가 깨졌을 때는 가급적 빨리 문제를 수정하여 빌드를 정상화 시키고 소스 관리 시스템에 저장되 코드의 무결성을 회복하거나 빌드가 성공한 전버전으로 BACK(Revert) 해서 무결성이 보장된 코드내에서 작업하도록 한다. Revert를 위해서는 소스 관리 시스템에 빌드때마다 Tagging을 해서 무결성이 보장된 버전에 대한 History를 관리해야 한다.

Silent period에 대해서
CI툴에서는 소스 관리 시스템으로부터 소스를 체크아웃 또는 업데이트 받을 때 Silent Period라는 옵션을 제공한다.
개발자가 소스를 커밋하고 있을 때 커밋이 완료되지 않은 상태에서 CI툴이 소스를 체크아웃하게 되면 불완전한 코드가 내려와서 빌드가 망가질 수 있다. 이를 방지하는 옵션이 Silent Period인데, 커밋이 있은후 일정 시간동안 소스 관리 시스템에 변화가 없을 때 CI툴이 체크아웃을 받아서 불완전한 코드를 내려 받을 수 있는 가능성을 최소화 하는 것이다.

이렇게 자동 빌드를 하면서 얻을 수 있는 이점은, 주기적인 빌드를 통해서 소스코드의 무결성 관리와 빅뱅방식의 통합에서 올 수 있는 리스크를 개발과정 전반으로 분산할 수 있다.

(3) 자동 테스팅
빌드 과정에서 테스트를 포함할 수 있는데, 주기적인 빌드 과정에 테스트를 포함해서 자동 빌드를 통한 Syntax에 대한 검증과 더불어 테스팅을 통한 기능과 비기능적(성능등)에 대한 요건을 매번 검증함으로써 코드의 품질에 대한 무결성을 함께 유지한다.

(4) 일일 체크아웃과 빌드
개발자가 출근후 소스 관리 시스템에서 최신 코드를 내려받고, 출근전에 현재 코드를 소스 관리 시스템에 저장함으로써 소스 코드에 대한 무결성을 유지한다.

예를 들어 개발자 A와 개발자 B가 같이 개발을 할 때, 개발자 A가 작성한 모듈을 개발자 B가 참고해서 사용한다고 하자, 이런 경우 개발자 A가 임의로 컴포넌트에 대한 작동 방식이나 인터페이스를 변경했을 때 일일 체크아웃과 빌드를 하면 개발자 A의 모듈을 사용하는 개발자 B의 모듈의 컴파일 오류나 또는 테스트 오류가 발생할 것이고 코드 변경으로 인한 임팩트를 빠르게 발견하여 수정할 수 있다.
그러나 통합 빌드의 과정이 일일이 아니거나 일일 체크아웃 빌드를 하지 않고 일주일이나 한달 단위로 할 경우 일주일동안 개발자 B는 잘못된 코드를 양산할것이고, 그 만큼의 시간 낭비가 발생한다.

일일 체크아웃과 빌드는 이를 방지해주는 역할을 한다.

2.CI 프로세스

CI에 대한 프로세스를 정리해보면 다음과 같다.
 

사용자 삽입 이미지


<그림. Continous Integration Process >


(1) 개발자
1) 개발자는 아침에 출근해서 소스 관리 시스템으로부터 최신 코드를 Checkout 또는 update받는다.
2) 코드를 가지고 개발을 수행하고 테스트를 작성한다.
3) 로컬에서 빌드 및 테스트를 진행한다.
4) 테스트과정에서 커버러지분석과 Code Inspection을 수행한다. (Optional)
 커버리지 분석
커버러지 분석은 테스트의 대상중에 테스트에 해당하는 부분중에 어느 부분이 테스트가 수행이 되었는지를 체크하는 과정이다. 개발 과정에서의 테스트 커버러지 분석은 일반적으로 코드 커버러지로 분석한다.
코드 커버러지란 테스트가 전체 소스 코드중 어느부분을 수행했는지를 검토하는 것이다.
코드 커버러지를 측정할 때 가장 중요한 것은 목표 커버러지율을 결정하는 것이다. 코드 100%를 테스트하는 것이 좋겠지만, Exception,If 문에 대해서 100% 테스트가 불가능하다. 또한 Setter와 Getter만 있는 ValueObject의 경우 테스트를 작성하는 것도 쉽고 테스트 자체가 의미가 없나 Coverate rate는 많이 올릴 수 있다. 만약 커버러지율을 강제적으로 높이고자 하면 개발팀에서 VO등 테스트가 필요하지 않고 테스트가 쉬운곳에만 테스트를 집중할 수 있기 때문에 컴포넌트의 우선순위를 정해서 중요한 컴포넌트에 대해서 커버러지를 관리하는 것이 필요하다.
커버러지율은 잘 만든 테스트라도 50~70% 내외이고, 고 가용성 시스템도 80%를 넘기 힘들기 때문에, 컴포넌트의 중요도별로 목표 커버러지율을 융통성 있게 결정하는 것이 필요하다.
대표적인 오픈소스 도구로는 EMMA와 Cobertura등이 있고, 상용 도구로는 Atlassian社의 Clover등이 있다.
 Code Inspection
Code Inspection이란, 완성된 코드에 대한 검토를 통해서 코드상에 존재하고 있는 잠재적인 문제를 발견하는 과정이다. 흔히 “정적 분석” 이라는 이름으로도 불리는데, 이 과정에서 Deadlock에 대한 검출 Lock contention과 같은 병목 구간에 대한 검출 Memory Leak이나 Connection Leak과 같은 자원 누수에 대한 검출과 코딩 스타일 (변수명이나 메서드명 규칙등)에 대한 가이드를 수행한다.
보통 이런 도구들은 룰 셋을 추가하여 Inspection을 각 팀의 스타일에 맞춰서 Customizing할 수 있으며 대표적인 오픈 소스 도구로는 PMD, FindBugs등이 있다.

5) 완료된 코드를 소스 관리 시스템에 저장한다.
완료된 코드와 테스트를 소스 관리 시스템에 커밋한다.

(2) CI Tools

1) 체크아웃
CI Tools는 정해진 시간이나 소스가 커밋이 되었을 때 등 정책에 따라서 빌드를 시작한다. 먼저 소스 코드를 체크아웃 받는다.
2) 컴파일
체크아웃 받은 코드에 내장되어 있는 빌드 스크립트를 기동하여 컴파일을 수행한다.
3) 배포
컴파일이 완료된 코드를 테스트 서버에 배포한다.
4) 테스트 수행
체크아웃 받은 코드내에 있는 테스트 코드들을 수행하고 리포팅을 한다.
5) 커버러지 분석
테스트 과정중에 코드 커버러지를 수행한다.
커버러지의 기본 원리는 커버러지 분석 대상이 되는 코드내에 커버러지 분석 코드를 삽입하여 테스트가 완료된 후에 그 결과를 수집하여 분석을 하는데 분석 코드를 삽입하는 과정을 Code Instrument라고 한다. Instrumented된 코드는 커버러지 분석 기능으로 인해서 성능 저하를 유발할 수 있기 때문에 만약에 테스트 과정에 성능이나 응답시간에 관련된 테스트가 있을때는 커버러지 분석을 위해서 테스트를 마친후에 Instrument를 다시하여 3),4) 과정을 다시 거쳐서 커버러지 분석을 해야 테스트 과정에서 성능에 대한 요소를 올바르게 추출할 수 있다.
6) Code Inspection
다음으로 Code Inspection을 수행하고 리포트를 생성한다.
7) 소스 태깅
1)~6) 과정이 정상적으로 수행되었을 때, 현재 소스 관리 시스템에 저장된 버전을 안정적인 버전으로 판단하고 소스 관리 시스템에 현재 버전에 대한 이미지를 저장하기 위해서 Tagging을 수행하여 현재 버전을 저장해놓는다.
8) Reverse (Optional)
만약에 빌드나 테스트가 실패하였을때는 이전에 성공한 빌드 버전으로 소스를 롤백하고, 실패의 원인을 파악한후에 다시 커밋한다.
이것은 소스 관리 시스템에 저장된 소스는 문제가 없는 소스를 항상 유지하여 개발자들이 문제가 없는 소스로 작업을 할 수 있게 보장해주며, 릴리즈가 필요한 시기에 언제든지 릴리즈가 가능하도록 지원해준다.
9) 결과 분석
빌드와 테스트가 완료된 후에 테스트 결과서를 통해서 문제가 있는 테스트를 개발자가 수정하도록 하고, Code Inspection결과를 PM이 검토하여 담당 개발자가 수정하도록 한다.
다음으로 Coverage 분석 결과를 통해서 테스트가 부족한 부분은 PM이 담당 개발자에게 지시항 테스트를 보강하도록 한다.

3.Hudson 설치

(1) Hudson의 설치 및 기동


1) https://hudson.dev.java.net/ 에서 hudson을 다운로드 받는다.
2) 다운로드 받은 Hudson.war를 Apache Tomcat에 deploy해서 구동 하거나 도는 java –jar hudson.war로 hudon으르 기동한다. 디폴트로 설치된 hudson은 8080포트를 통해서 접근이 가능하다. (Tomcat을 통해서 설치 하지 않은 경우)

WAS에 인스톨 정보
http://hudson.gotdns.com/wiki/display/HUDSON/Containers

(2) Hudson과 소스 관리 시스템 연동
좌측 메뉴에서 “New Job”을 선택하여 새로운 작업을 등록한다.
Job name을 선택하고 “Build a free-style software project”를 선택한다. 아직까지 다른 빌드 선택은 안정화 되어있지 않기 때문에 이 메뉴를 중심으로 설명한다.
 

사용자 삽입 이미지


<그림 1. 새로운 프로젝트의 생성>

(3) 소스 관리 시스템과 연동
Job이 생성되고 나면 초기화면에서 Job을 선택할 수 있다 Job을 선택하면 좌측에 Configure라는 메뉴가 생기는데, 그 안으로 들어가면 Job에 대한 설정을 할 수 있다.

먼저 소스 관리 시스템으로부터 코드를 내려받도록 설정해야 한다.
“Source Code Management”에서 사용하는 소스 관리 시스템을 선택한다.
이 예제에서는 Subversion을 선택한다.
Subversion을 선택한후 Repository URL에 SVN접근 주소를 입력한다.
svn://source.example.com/trunk 그 아래에 “Local module directory”에 SVN 레파지토리의 하위 디렉토리를 선택한다. “/myproject” 식으로
이렇게 하면 svn://source.example.com/trunk/myproject 에서 소스 코드를 매번 내려받게 된다. Repository URL과 Location을 지정하면 Hudson이 자동으로 SVN에 접속을 시도해본다. 만약에 id와 passwd가 필요한 경우에는 아래 그림과 같이 “enter credential”이라는 링크가 Repository URL 아래 나타나서 id와 passwd를 입력할 수 있게 한다.
 

사용자 삽입 이미지


<그림 2. SVN 접속 정보 입력>
여기에 소스 관리 시스템 연결에 관련해서 몇가지 옵션을 추가할 수 있다.

 Use Update
소스 관리 시스템에서 소스를 내려받을 때 디폴트가 모든 소스를 매번 다운로드 받는것인데 이런 경우에는 소스양이 많을 경우 다운로드에 많은 시간이 소요되서 전체 빌드 시간이 늘어날 수 있다. 이때 “Use Update”라는 옵션을 사용하면 처음에만 소스 코드를 전체 다운로드 받고, 두번째 부터는 변경된 소스 코드만 다운로드 받기 때문에 소스 코드를 다운 받는 시간을 많이 줄일 수 있다. (svn update와 같은 명령)

Repository Browser
Repository Browser란 소스 관리 시스템에 저장된 소스의 내용 웹에서 브라우징할 수 있는 도구이다. 도구에 따라서 이전 버전과 변경된 부분에 대한 Diff비교 또는 처음 소스 코드가 생성되었을 때부터 매번 커밋되었을 때 변경 내용에 대한 Revision등에 대한 모든 히스토리를 출력해준다. 이런 기능은 빌드가 깨졌을 때, 바로 빌드 버전에 대한 소스 변경 내용을 추적하여 누가 어떤 코드를 변경하였는지를 추적하여 가능한한 빠른 시간내에 문제를 해결하게 해준다.
이 옵션을 체크해놓으면 빌드가 완료된후 Job의 Changes를 보면 소스 코드가 변경된 부분에 대해서 Repository Browser로 링크가 걸려서 소스를 웹에서 바로 확인하거나 전버전에서 바뀐 부분을 확인할 수 있다.
대표적인 Repository Browser로는 오픈소스 제품인 Sventon과 상용제품인 Fisheye등이 있다.

(4) 빌드 트리거링
다음 설정해야 하는 부분이 Build Triggers 설정이다.
이 설정은 언제 빌드가 돌아야 하는가를 설정하는 부분으로 3가지 옵션을 제공한다.

1) Build after other projects are built
이 옵션에는 다른 Job(Project)의 이름을 인자로 넣는다.
이렇게 하면 지정된 프로젝트의 빌드가 정상적으로 끝나면 자동으로 이 프로젝트가 Invoke된다. 만약에 빌드만 하는 Job과 테스트만 하는 Job이 있고 테스트는 자주 사용하고 빌드후에 반드시 테스트를 해야할 때, 테스트 Job에서 이 옵션으로 선행작업을 빌드로 해놓으면 빌드를 수행할 때 마다 빌드가 성공하면 테스트를 수행하게 된다. 테스트만 수행하면 빌드와 상관없이 테스트만 수행된다.
이 옵션은 프로젝트 실행의 전후 관계(Chainning)을 하는데 매우 유용하게 사용할 수 있다.

2) Poll SCM
이 옵션을 사용하면 여기에 지정한 주기별로 소스 관리 시스템을 폴링(체크)하여 변경이 있을 경우에만 빌드를 수행한다.
 

사용자 삽입 이미지


<그림 Build Trigger 등록 >

시간 설정 방법은 unix의 crontab 명령과 같은 형식으로 아래와 같은 형식을 사용한다.
분 시간 날짜 월 요일

사용 예는 다음과 같다.
# 매일 12시에 실행
00 12 * * *
# 매주 일요일 1시에 실행
00 01 * * 7
# 매일 12시와 5시에 실행
00 05 * * *
00 12 * * *

3) Build periodically
마지막으로 Build periodically는 정해진 시간 주기별로 소스가 변경과 상관없이 무조건 주기적으로 빌드를 수행하며 Poll SCM과 마찬가지로 crontab과 같은 형식으로 스케쥴을 등록한다.

(5) Build
정해진 스케쥴 정책에 따라 빌드 프로세스가 기동되면 실제 빌드를 수행할 빌드 스크립트가 구동되어야 하는데, Hudson에서는 ANT,MAVEN,그리고 Unix/Windows shell을 수행할 수 있도록 되어 있다. 여기서는 ANT 기반으로 설명을 한다.
 

사용자 삽입 이미지


<그림. Invoke ANT 설정 >

Invoke ANT를 선택 하면 다음과 같은 옵션을 선택할 수 있다.

 ANT Version
Hudson 초기화면에서 “Manage Hudson”메뉴로 들어가면 “System configuration” 메뉴에서 여러 개의 ANT_HOME을 등록할 수 있다.
ANT 버전에 따라서 플러그인이 차이가 날 수 도 있고, 프로젝트에 따라서 사용해야 하는 ANT 버전이 틀릴 수 있기 때문에 여러 개의 ANT를 등록할 수 있게 해준다.
예를 들어 프로젝트가 JUnit 3.8대와 JUnit 4.X대로 각각 구현되어 있다면 ANT에 설치되어야 하는 JUnit 라이브러리 버전이 틀리기 때문에 두개의 ANT를 설정해야 할 수 있다. 이런 경우에 “System configuration”에서 ANT를 여러 개 등록해놓고, 이 ANT Version 메뉴에서 필요한 ANT 버전을 선택하면 된다.

 Target
ANT 스크립트의 Target을 설정한다.

 Build File
ANT 스크립트를 지정한다. 일반적으로 build.xml을 지정한다.

 Properties
ANT 스크립트에 전달해야 하는 Property를 지정한다.
예를 들어 스크립트내에 $workspace라는 변수를 지정하였을 경우 –Dworkspace=값 이런식으로 텍스트 상자에 정의하면 빌드 스크립트로 인자를 전달할 수 있다.

 Java Options
ANT 를 기동하는데 필요한 자바 옵션을 지정한다. ANT도 자바 프로그램이기 때문에 여러가지 JVM 옵션이 지정되는데 Heap,Perm size등을 여기서 –Xmx256m 식으로 지정하여 이 옵션으로 ANT 프로세스를 실행할 수 있다.

여기 까지 설정하면 주기적으로 빌드를 자동화 하는 설정이  완료 되었다.

(6) Hudson의 사용
초기화면에 들어가면 등록된 프로젝트 리스트들이 나온다.
 

사용자 삽입 이미지


초기화면에서는 현재 빌드 상태와 프로젝트별 빌드 성공 실패 여부가 나타난다. 빌드가 성공하면 맑은 태양이 실패율이 높으면 천둥 모양으로 아이콘이 변경이 된다.

초기화면에서 프로젝트를 클릭하면 아래와 같은 화면이 나오는데
 

사용자 삽입 이미지


< 그림, Hudson 프로젝트별 초기화면 >
Changes는 빌드 버전별로 소스 관리 시스템에서 지난 버전에 비해서 변경된 내용 누가 변경했는지 그리고 커밋시에 개발자가 추가한 Comment를 확인할 수 있다.Workspace는 이 프로젝트의 빌드 디렉토리를 보여준다. 브라우져를 통해서 빌드에 사용된 파일등을 확인할 수 있다.
그 아래 메뉴가 “Build Now”인데 이 메뉴는 스케쥴에 상관없이 지금 강제적으로 빌드를 하게 한다.
좌측 맨 아래 메뉴는 BuildHistory로 언제 빌드가 수행되었고 현재 빌드 상태와 빌드 성공 실패 여부를 나타낸다.

(7) Hudson과 Email 연동
Hudson 초기화면에서 Manage Hudson > System Configuration 메뉴에 들어가면 Email-Notification 설정부분이 있는데, 여기 SMTP 서버를 설정해주면 빌드가 실패하였을 때 등에 담당자들에게 메일로 통보를 할 수 있다. SMTP 설정을 한후 프로젝트의 configuration 메뉴에서 Post-build Actions에서 Email Notification에 Receipients에 이메일 주소를 적어놓으면 빌드가 실패했을때와 실패한 빌드가 복구 되었을 때 이메일이 발송된다.
 

사용자 삽입 이미지


<그림. Email Notification 설정 >


(8) JUnit 테스트 연동
CI에 대해서 설명할 때 Test에 대해서 설명했는데, Hudson에서는 JUnit Test Report를 출력해주는 기능을 지원한다.
프로젝트 configuration에서 Post-build actions의 “Publish JUnit test result report” 메뉴에서 JUnit 리포트 파일명을 지정해주면 아래와 같이 테스트가 끝나고 테스트 리포트가 생성되면 테스트 성공 실패 여부를 그래프로 나타내주고, 테스트의 Progress도 누적 그래프로 프로젝트 초기화면에서 출력해준다.

사용자 삽입 이미지


<그림. 프로젝트 초기화면에서 테스트 히스토리에 대한 그래프 > 

사용자 삽입 이미지


<그림. 프로젝트별 테스트 성공 실패 요약 >

이때 주의할점은 JUnit의 테스트 리포트의 파일 경로는 절대 경로가 아니라 프로젝트 Workspace에 대한 상대 경로이기 때문에 상대 경로로 지정해야 한다.

(9) Hudson과 Japex를 이용한 부하 테스트 연동
Japex는 단위 테스트에 대한 부하 테스트를 할 수 있는 단위 부하 테스트 자동화 프레임웍이다. Japex에 대한 사용 방법은 단위 테스트의 “단위 부하 테스트” 부분을 참고하기 바란다.
Japex 역시 JUnit과 마찬가지로 성능에 대한 결과 (테스트 소요 시간)를 그래프로 출력할 수 있다. JUnit과 설정 방법이 같으며 프로젝트 > configuration > Post-build Actions > Record Japex test report에 Japex 테스트 리포트 경로를 추가하면 된다.
 
설정이 제대로 됐으면 테스트 수행후에 왼쪽에 Japex Trends Report라는 메뉴가 생겨서 성능 테스트에 대한 결과를 누적 그래프로 출력해준다.

Japex 테스트 플러그인은 Hudson에 Default로 포함된 것이 아니기 때문에 http://hudson.dev.java.net에서 다운받아서 Hudson에 추가로 설치해줘야 한다.


(10) Hudson과 Cobertura를 이용한 Coverage분석
Coverage 분석에 대해서는 EMMA와 Cobertura, Clover에 대한 확장 플러그인을 제공하는데, 플러그인을 설치한후 JUnit과 Japex와 동일한 방법으로 리포트에 대한 위치를 등록해주면 아래와 같이 커버러지의 누적 그래프를 클래스별,라인별,브랜치별로 출력해준다.
 

사용자 삽입 이미지

(11) 그외의 기능들
본 문서에서는 Hudson에 대한 대략적인 기능을 살펴보았다.
Hudson은 이 이외에도 여러 개의 Hudson 인스턴스를 구성하여 클러스터된 빌드환경을 구축할 수 있다. 여기서 클러스터란 로드분산이나 장애 대응등의 의미가 아니라,
하나의 소스 코드를 가지고 Windows,AIX,HP 버전으로 각각 빌드가 필요할 때, 각 서버에 Hudson을 따로 설치하고 각각 관리하는 것이 아니라 설치는 각각 하더라도 하나의 콘솔화면에서 컨트롤을 하도록 설정이 가능하다.
또한 여러 확장 플러그인을 통해서 기능 확장이 가능하다.

(12) Hudson 사용시 주의할점
Hudson은 이미 JBoss 프로젝트나 기타 상용 프로젝트에서 사용될정도로 매우 널리 쓰이고 안정적인 버전이다. 그럼에도 불구하고 오픈 소스의 한계점과 잦은 업그레이드로 인해서 잠재적인 버그가 있고 특히 플러그인들의 버전이 Hudson의 버전 업그레이드를 쫓아가지 못해서 일부 플러그인들은 하위 버전에서만 작동하고 최신 버전에서 작동하지 않는 경우가 있기 때문에 자신이 사용하고자 하는 플러그인들에 맞는 Hudson 버전을 사용하는 것이 중요하다.
여기서 소개된 플러그인들은 Hudson 1.7 버전을 기준으로 사용 및 검증이 되었다.

4. 그외의 CI Tools
예전에는 CI 툴이 Cruise Control이나 Ant hill이 주류를 이루고 있었으나,
Hudson이 등장하면서 많은 프로젝트들이 쉽고 직관적인 인터페이스와 확장성으로 인해서 많이 사용되고 있다.
TeamCity의 경우 일반 버전은 무료로 제공되며 TeamCity Pro는 상용이다. 무료 버전도 상용 코드를 기반으로 하는 만큼 Hudson에 비해서 높은 안정성을 가지고 있으다.
AntHill Pro 역시 꾸준히 Java 진영의 CI도구로 사용되고 Atlassian의 Bamboo도 근래에 들어 많이 프로젝트에 사용되고 있다.

Hudson을 이용한 빌드와 테스트의 자동화.doc


'ALM > Build Automation (빌드 자동화)' 카테고리의 다른 글

이클립스 ALF  (0) 2008.08.12
괜찮은 ALM툴  (5) 2008.06.11
Hudson을 이용한 빌드 배포 테스트 자동화  (8) 2008.04.07
플러그인 개발 순서  (0) 2008.04.02
Code Inspection Tools  (0) 2008.02.29
빌드 자동화 업체  (0) 2008.02.12

플러그인 개발 순서

ALM/Build Automation (빌드 자동화) | 2008.04.02 15:05 | Posted by 조대협

http://confluence.atlassian.com/display/DEVNET/How+to+Build+an+Atlassian+Plugin

1. 이클립스에서 플러그인 업데이트

http://mevenide.codehaus.org/mevenide-ui-eclipse/update/index.html

2. 아파치 사이트에서 메이븐 받아서 설치
3. 메이븐 컨피그 지정
http://confluence.atlassian.com/display/DEVNET/Example+settings.xml

4. 템플릿 생성(프로젝트 생성)
http://confluence.atlassian.com/display/DEVNET/Atlassian+Plugin+Archetypes
mvn org.apache.maven.plugins:maven-archetype-plugin:1.0-alpha-7:create  -DarchetypeGroupId=com.atlassian.maven.archetypes  -DarchetypeArtifactId=confluence-plugin-archetype   -DarchetypeVersion=7   -DremoteRepositories=https://maven.atlassian.com/repository/public/   -DgroupId=com.javastudy.confluence.simple -DartifactId=simpleplugin
명령으로 프로젝트 템플릿 생성

다음으로 pom.xml이나 USERHOME/.m2/setting.xml에 아래 내용 추가
http://confluence.atlassian.com/display/DEVNET/Atlassian+Maven+PDK
<pluginRepositories>
  <pluginRepository>
    <id>atlassian-contrib</id>
    <url>https://maven.atlassian.com/contrib</url>
    <snapshots>
      <enabled>false</enabled>
    </snapshots>
    <releases>
      <enabled>true</enabled>
    </releases>
  </pluginRepository>
  <pluginRepository>
    <id>atlassian-contrib-snapshot</id>
    <url>https://maven.atlassian.com/contrib-snapshot</url>
    <snapshots>
      <enabled>true</enabled>
    </snapshots>
    <releases>
      <enabled>false</enabled>
    </releases>
  </pluginRepository>
</pluginRepositories>
4. 생성된 프로젝트 디렉토리 들어가서 mvn eclipse:eclipse 실행해서 Maven 프로젝트를 이클립스 프로젝트로 변경한다.

TAG maven

Code Inspection Tools

ALM/Build Automation (빌드 자동화) | 2008.02.29 17:16 | Posted by 조대협

Code inspection tools
정적 분석 툴이라고도 이야기 하는데...
모냐 하면
소스코드나 컴파일된 클래스를 넣으면 코드 상에서 발생할 수 있는 예상 되는 버그를 알려주는 것이다. (정적으로 소스 코드를 분석해서..)

예를 들어
1: Address addr = get Address("bcho");
2: if(addr != null){ addr.sendGift("Money");}
3: addr.verify();

위의 코드는 addr이 NULL일때 3번 라인에서 Null Pointer Exception(NPE)가 발생할 수 있다. 실행하지 않아도 예측할 수 있는 내용이다.

이런 위험성이외에도 MultiThreading 에서 Lock문제나 단순하게 Class 명명 규칙을 벗어난 경우 등등 이런 여러가지 패턴을 벗어나서 버그의 가능성이 있는 것을 찾아내주는 것을 Code Inspection Tool이라고 한다.

물론 개발자가 의도한 부분도 있고 사람이 생각한 알고리즘을 기계적인 검사를 통해서 검색해 낸다는게.. 얼마나 정확성이 있겠냐만은... 생각외로 도움이 될 수 있다.

대표적인 툴로는 PMD와 FindBugs (둘다 공짜!!) 가 있고.. 이 둘 역시 Hudson에 멋지게 플러그인되어 빌드때마다 Inspection을 수행할 수 있다.

Find bugs : http://www.ibm.com/developerworks/java/library/j-findbug1/
PMD : http://www-128.ibm.com/developerworks/kr/library/j-pmd/

시간 되면 개발환경 자동화 프로세스에 충분히 포함해볼만한 내용..

빌드 자동화 업체

ALM/Build Automation (빌드 자동화) | 2008.02.12 14:25 | Posted by 조대협
Clover,JiRA, CI 툴등. 이런거 수입해서 국내에서 팔았으면 좋겠다 생각했는데..
벌써 하는 업체가 있었네.. 반갑기도 하고.. ^^
역시 사람들 빠르다는 생각도 들고...

마침 업체가 최종명 선배가 있는 업체기도 해서 기술에 신뢰도 간다...
기회가 되면 프로젝트 초반에 같이 한번 일해보고 싶은 업체.

Architecture Group http://www.arctgroup.com
555-4847

Hudson에서 SMTP 서버의 포트를 지정하는 메뉴가 없는데.
이는 Java property를 지정해서 해결할 수 있다.
==
nohup java -Dmail.smtp.host=192.24.3.72 -Dmail.smtp.port=3360 -jar hudson.war --
argumentsRealm.passwd.admin --argumentsRealm.roles.admin=admin > nohup.out &
==

'ALM > Build Automation (빌드 자동화)' 카테고리의 다른 글

Code Inspection Tools  (0) 2008.02.29
빌드 자동화 업체  (0) 2008.02.12
Hudson에서 SMTP PORT 지정하기 (Assign SMTP server port in Hudson)  (0) 2008.02.11
통합 빌드 환경 설정 완료  (0) 2008.02.05
Code Coverage Tools  (0) 2008.02.01
이제는 Trac  (1) 2008.01.24
TAG Hudson, port, SMTP
9개의 프로젝트로 구성된 WLI 시스템에 대한 통합 빌드 시스템을 구축하였다.
Hudson + ANT + WebLogic Workshop ANT TASK 를 조합해서 구성하였고
Free STMP 서버로 Alert 기능은 지원할 생각이고..

인자 자동 DEPLOY 자동화까지 진행할 예정
다음주면 이건 될것이고..

==

표준화 팀에서 JUnit + DBUnit + Hudson으로 기능 테스트에 대한 자동화를 진행중이고
다음주 부터는 JUnit + Cactus 기반의 Unit 테스트를 구현할 예정이다.

이 과정에서 개발팀에게 Test Case와 Tuning을 위한 Issue Tracking 시스템을 설치할 예정인데.. 아직도 Trac을 할지.. 몰 쓸지를 결정을 못했네 그랴... 차라리 익숙한 Bugzilla를 사용할까?

그담에는 Coverage를 할지 말지 고민해봐야겠다.