(웹)어플리케이션을 '개발' 서버에 배포(deploy)할 때는 소스저장소의 중심개발축(HEAD)에서 전체 최신 개발소스를 가져와서 빌드하고, 빌드결과 파일들을 일괄적으로 통째로 전부 배포해도 대부분 큰 문제가 없다.

배포코스 설계에서 설명한 것처럼 빌드서버중심 코스에서는 빌드서버에서 빌드 후 개발서버로 전송하여 배포하면 될 것이고, 소스저장소중심 코스에서는 개발서버에서 빌드 및 배포를 모두 처리하면 될 것이다.

그러나 배포 대상 서버가 '운영' 서버라면 개발소스 전체를 무차별적으로 배포하는 것은 문제가 된다. 버그가 고쳐지지 않은 모듈, 검증이 덜된 모듈, 아직 공개해서는 안될 모듈 등이 함께 반영될 수 있기 때문이다.

따라서 중심개발축의 개발소스 중에서 stable하고 production-ready 상태의 소스만 선별해서 운영에 반영해야 한다

그럼 어떻게 선별할 것인가? (내가 아는 한) 실현 가능한 선별방법 2가지가 있다(이전 글 참조).

○ 중심개발축으로부터 빌드된 전체 파일들 중에서 반영할 파일들을 하나하나 수작업으로 골라내는 방법
○ 소스저장소에 릴리스 브랜치를 만들어서 여기에 반영할 소스만 모아놓는 방법

전자는 컴파일,테스트,코드검사,패키징,배포까지의 흐름 중간에 사람이 개입하게 되어 자동화가 불가능하는 점, 배포모듈의 정합성이 깨질 가능성이 큰 점 등 여러가지 문제점을 안고 있기 때문에 권장하지 않는 방식이다.

대개는 널리 알려진 후자의 방법을 택한다.(어느 나라? 누구? 어느 분야에서?)
외국의 자료(사이트, 책 등)를 살펴보면 보통 아래와 같은 내용이 포함된다(주관적인 판단일 수 있음).

- 어플리케이션은 외부에 릴리스하는 방식으로 배포(distribution)됨
- 브랜치는 릴리스 브랜치(Release Branch)로 불림
- 주요 마일스톤/릴리스 마다 새로운 브랜치를 생성
- 주로 릴리스 브랜치에서 버그를 픽스하고 중심 개발축으로 파일을 병합함
- 동시에 여러 릴리스 브랜치가 유지보수됨
- 주로 릴리스 브랜치 -> 중심 개발축으로 소스가 병합됨

그러나 이러한 설명은 솔루션/프레임워크/라이브러리의 개발,유지보수 환경에 치우쳐 있다는 느낌을 갖는다(솔직히 Agile, TDD, Maven에 대해서도 마찬가지 느낌을 갖고 있다).
외국에서 '개발'은 솔루션 개발이 default일지 모르나, 국내에서는 개발이라고 하면 (SI/SM프로젝트에서) 웹시스템 개발이 default라고 생각한다.

다음은 내가 생각하는 국내 개발환경에 적합한 배포 방식이다(웹시스템 개발/유지보수에 초점).

- 어플리케이션은 외부에 릴리스되는 것이 아니라 운영서버에 배포(deployment)됨
- 그래서 릴리스 브랜치 대신 배포 브랜치(Deploy Branch)라고 이름 바꿈(또는 운영브랜치?)
- 기능추가시 중심 개발축에서 배포브랜치로 운영에 반영할 소스파일을 추가
- 기능수정시 중심 개발축의 소스 수정 후 배포브랜치의 소스와 병합
- 개발 도중 발견된 버그는 중심개발축의 소스를 고친 후 배포브랜치의 소스에 병합시킴
- 운영에서 발견된 버그는 일단 배포브랜치의 소스를 고쳐서 운영에 반영한 뒤에 중심개발축에 병합함
- 하나의 배포 브랜치만 생성하여 유지보수
- 주로 중심개발축 -> 배포 브랜치로 소스가 병합됨



고백하건데 이렇게 하는게 국내환경에서 좀더 실용적일 거라고 생각은 하지만, 아직은 실전 프로젝트에서 이를 적용해본 적은 없다. 해보지도 않고 떠들다니...살짝 부끄럽군... -.-;;

태클 대환영!
Posted by 에코지오
,

자바에서 웹어플리케이션은 JEE 스펙에서 정의한 디렉토리 구조를 갖는 war 파일의 형태로 컨테이너에 배포됩니다. 메이븐에서는 웹어플리케이션을 package 단계에서 기본적으로 war 파일로 포장합니다.

그런데 꼭 war라는 아카이브 파일로만 배포할 수 있는 건 아닙니다. maven-war-plugin 플러그인에는 war:war 골뿐만 아니라 war:exploded나 war:in-place도 있습니다.

웹어플리케이션을 배포하기 위한 패키징 유형을 3가지로 나눌 수 있습니다.

1. package(archive) : 아카이브(war,ear) 파일로 배포
- 아카이브는 결국 WAS에 의해 압축이 풀림
- 파일이 많을 경우 압축해제 시간 오래걸릴 수 있음
- 리모트 서버에 배포시 한개의 파일만 전송하면 됨
- WAS에서 제공하는 업로드를 통한 배포기능 활용가능

2. exploded(expanded) : 아카이브를 압축해제한 디렉토리 형태 구조
- 압축 및 해제 과정이 불필요
- 별도의 디렉토리에 원본 소스를 복사하여 만듬
- 파일이 많은 경우 복사 시간 오래걸릴 수 있음
- 원본 소스를 건드리지 않고 배포를 원하는 경우 적합
- 리모트 서버에 배포시 파일이 많은 경우 전송 시간이 오래걸릴 수 있음.(rsync는 빠르다?)

3. in-place : 소스 디렉토리(전체 또는 일부)를 그대로 배포
- 추가적인 복사 과정 불필요
- 로컬 서버에 배포하는 경우에 적합
- WAS가 런타임시 생성하는 파일이 소스와 섞일 수 있음

Posted by 에코지오
,

배포용 어플리케이션(deployable application)이 생성된 위치와 설치될 위치가 다를 경우 어플리케이션을 어떤 방식으로 전송할까?

1. 로컬 전송
-생성위치와 설치위치가 같은 서버에 있는 경우
-copy, sync 등

2. 리모트 전송
-생성위치와 설치위치가 다른 서버에 존재하는 경우
-ftp, sftp, scp, rsync 등

3. 업로드 전송
-어플리케이션이 배치되는 WAS가 업로드 방식의 배치를 지원하는 경우
-WAS 어드민화면 또는 WAS 제공 API를 통해 업로드

Posted by 에코지오
,

리소스설정치환에 관한 글에서 얘기했듯이 개발소스와 빌드결과물은 여러 서버를 거치면서 생성,이동,배치됩니다.

개발자 로컬PC -> (소스저장소) -> 빌드서버(CI서버) -> 개발서버 -> 테스트서버 -> 스테이징서버 -> 운영서버

1차원적으로 일렬로 나열하니까 간단해보이지만 실상 어플리케이션의 이동경로는 2차원적으로 설계됩니다. 마치 수학자 오일러가 최단코스를 찾기 위해 고민한 것처럼 우리는 각각의 서버를 거점으로 하는 배포 코스를 그려가야 합니다.(배포의 위상수학? 배포 아키텍처? 음.. 배포 아키텍처가 맘에 드는군요.)

하지만 너무나 다양하고 특이한 개발환경이 많이 존재하기 때문에 배포코스에 관한 뚜렷한 공식은 없는것 같습니다. 아래에 저의 경험과 동료들의 의견 그리고 온라인상의 자료 등을 종합해서 크게 3가지의 대표적인 코스를 그려보았습니다. 어떤게 베스트 프랙티스다라고 딱부러지게 말씀은 못드리겠군요.
(코스에서 테스트서버는 포함하지 않았습니다. 솔직히 제가 겪었던 모든 프로젝트에서는 개발서버가 테스트서버,스테이징서버의 역할을 모두 겸하고 있었습니다.)

1.빌드서버 중심 코스
-개발서버용, 스테이징서버용, 운영서버용 어플리케이션(빌드산출물)을 모두 빌드서버에서 생성
-어플리케이션은 빌드서버에서 각각의 서버로 전송되어 배포됨


-또는 스테이징 서버에 전송된 어플리케이션을 다시 운영서버로 전송하기도 함



 

2.소스저장소 중심 코스
-개발서버, 스테이징서버, 운영서버가 개별적으로 소스장소로부터 전체 개발소스를 취합하여 각각의 환경에 맞는 어플리케이션을 빌드하여 배포

 

3.혼합된 코스
-개발서버용 어플리케이션은 빌드서버에서 생성하여 개발서버로 전송되어 배포
-스테이징서버용은 스테이징 서버가 소스저장소로부터 전체 개발소스를 취합하여 빌드하고 배포
-스테이징서버에서 빌드된 어플리케이션을 운영서버로 전송

자, 어떤 코스를 타시렵니까?

Posted by 에코지오
,

메이븐다운(?) 방법으로 웹어플리케이션을 원격서버로 전송하기 위해서 이것저것 찾아보다가 알게 된 거 정리해본다.

1. deploy:deploy
packaging한 artifacts를 리모트의 메이븐저장소로 전송한다. 원격 저장소는  <distributionManagement>엘리먼트의 <repository>에 설정한다. 임의의 파일을 전송하는 건 불가능하다. package 가 war 이고 저장소 경로가 ftp://repository.mycompany.com/repository 라면, 실제로 war 파일은 저장소 layout에 맞춰서 ~/repository/groupId/artifactId/version 디렉토리에 전송된다.

2. site:deploy
 site phase에서 생성된 사이트를 리모트 (웹)서버로 전송한다. 리모트 서버정보는<distributionManagement>엘리먼트의 <site>에 설정한다. inputDirectory 옵션을 통해 target/site 디렉토리가 아닌 다른 디렉토리의 파일들을 전송할 수 있다. 그러나 <site>는 하나만 설정가능하기 때문에 2군데 이상의 서버로 파일들을 전송하는 건 불가능하다.

3. cargo 플러그인
일부 컨테이너에 대해 remote container에 expanded war(war 파일의 압축을 풀어놓은 것. exploded war)를 배포할 수 있다고 cargo 웹사이트에 나온다. 하지만 과연 이게 가능할지는 의구심이 든다. 실제로 작동여부를 테스트해봐야 할 듯하지만, 아마도 war 파일만 원격배포가 가능하지 않을까 싶다. 아직 최신 버전의 상용 WAS에 대한 지원이 미비하다.

4. myfaces의 wagon-maven 플러그인 
임의의 디렉토리 내의 파일들을 원격 서버로 전송한다. 임의의 디렉토리를 2군데 이상의 원격서버에 전송할 수 있다. 실제 프로젝트에서 개발서버 배포를 이 플러그인으로 처리했다. target/webapp 디렉토리의 파일들은 WAS서버로 전송하고, target/htdocs 디렉토리의 파일들은 WEB서버로 전송하도록 말이다. 
아쉽다면, 지정된 디렉토리 내의 모든 파일들을 전송하며 그 디렉토리의 몇몇 파일들만 골라서 전송할 수는 없다는 것이다. 즉 fileset 개념이 없다(사실 이건 이 플러그인의 문제라기 보다 이 플러그인이 이용하는 메이븐 wagon의 문제이다. 몇몇파일만 골라내는 건 ant copy를 써서 target/htdocs처럼 별도의 디렉토리에 전송할 대상만 따로 모아놓으면 해결할 수 있다).
이 플러그인의 장점이라면 디렉토리를 압축하여 전송후 리모트에서 압축을 해제하는 방식의 wagon 기능을 이용하기 때문에 전송할 파일이 많은 경우에 ant의 ftp/scp 타스크를 이용하는 것보다 전송시간이 현저히 줄어든다는 거.
아래는 실제 pom.xml의 일부인데 별거 아닌게 내용이 긴거 같아 맘이 편하지만은 않다.

   <plugin>
    <groupId>org.apache.myfaces.buildtools</groupId>
    <artifactId>myfaces-wagon-plugin</artifactId>
    <version>1.0.0</version>
    <executions>
     <!-- config 배포 작업 -->
     <execution>
      <id>deploy-conf</id>
      <phase>pre-integration-test</phase>
      <goals>
       <goal>deploy</goal>
      </goals>
      <configuration>
       <id>deploy-config</id>
       <url>
        scp://${wasserver.username}:${wasserver.password}@${wasserver.ip}:${wasserver.config.dir}
       </url>
       <inputDirectory>${config.home.dir}</inputDirectory>
      </configuration>
     </execution>
     <!-- 웹어플리케이션 배포 작업 -->
     <execution>
      <id>deploy-web</id>
      <phase>pre-integration-test</phase>
      <goals>
       <goal>deploy</goal>
      </goals>
      <configuration>
       <id>deploy-web</id>
       <url>
        scp://${wasserver.username}:${wasserver.password}@${wasserver.ip}:${wasserver.web.dir}
       </url>
       <inputDirectory>${web.output.exploded.dir}</inputDirectory>
      </configuration>
     </execution>
     <!-- 웹파일 배포 작업 -->
     <execution>
      <id>deploy-html</id>
      <phase>pre-integration-test</phase>
      <goals>
       <goal>deploy</goal>
      </goals>
      <configuration>
       <id>deploy-html</id>
       <url>
        scp://${webserver.username}:${webserver.password}@${webserver.ip}:${webserver.htdocs.dir}
       </url>
       <inputDirectory>${web.output.html.dir}</inputDirectory>
      </configuration>
     </execution>
    </executions>
   </plugin>


현재까지 패턴을 적용하여 임의의 파일/디렉토리를 내 맘대로 전송할 수 있는 방법은 메이븐에서는 antrun을 이용하는 방법밖에 없는 듯싶다. maven의 wagon api와 file management api 이용하여 직접 플러그인을 만드는 것도 재밌을거 같다.

Posted by 에코지오
,
지금 몸담고 있는 회사의 빌드관련 문서에는 build와 deploy를 이렇게 정의하고 있다.

- build : 소스를 실행가능한 모듈로 변환하는 것
- deploy : 빌드되어 실행가능한 결과물을 컨테이너에서 인식가능한 곳에 배치하는 것

build는 별로 어렵지 않지만 deploy는 곰곰이 생각할 것이 많다. 내용상으로는 install과 비슷하고, 번역용어가 distribution과도 헷갈린다.

실행가능한 모듈을 실행 가능한 곳에 위치시키고 설정하는 것. install 아닌가?  그러나 OS위에서 작동하는 소프트웨어에 대해서는 대개 install이라 하고, WAS같은 컨테이너에서 작동하는 어플리케이션이나 재사용 레포지토리에 저장되는 서비스모듈에 대해서는 deploy라고 구별하는 것 같다 (이게 소프트웨어와 어플리케이션/라이브러리의 차이에서 오는 개념구분인지도 모르겠다).

deploy 및 관련된 deployables, distribution의 개념에 대해 개인적으로 다시 정리해보았다.

* deployables(deployable application)
- 정의 : WAS같은 컨테이너 상에서 실행가능한 어플리케이션.
- 설명 : 자바 환경이라면 war,ear,jar 같은 것들을 말한다. deployables는 소프트웨어 distribution의 한가지 형태라고 봐도 될듯하다. 근데 이걸 뭘로 번역할지는? 배포물? 배포가능 어플리케이션?

* deploy (deployment)
- 정의 : deployables를 실제 컨테이너에 배치(arrange? allocate?)하는 작업. 
- 설명 : 간단히 말하면 어플리케이션을 WAS 상에 설치(install)하는 것이다. deploy는 전개,배치,배포 등으로 다양하게 번역되며 보통 디플로이 또는 배포라고 부른다. 그러나 배포라는 표현은 distribution과 혼동을 일으키기 쉽기 때문에 개인적인 의견으로는 배치가 더 맞는 표현이 아닐까 생각하지만, 역시나 배치라고 불렀을 때 batch와 발음이 같아서 또다른 혼동을 일으킬 수 있다.

* distribution
- 정의: 누군가에게 전달(delivery)하기 위해 패키징된 소프트웨어. (또는 그것을 전달하는 행위)
- 설명 : 사전적으로는 분배,배분,배포의 의미를 가지며 보통 배포본, 배포판으로 번역한다.

ps1. IBM 프로젝트 자동화 아티클에서는 deployment를 전개로, distribution을 배포로 번역했다. 헷갈리게시리...-.-;

ps2. 위의 deploy 정의는 웹어플리케이션 개발 영역에서나 통하는 정의이고 이전에 언급했듯이 메이븐에서는 아티팩트를 저장소에 올리는 걸 deploy라고 부른다. 그래서 메이븐 처음 접하면서 오해하게 되는 게, 메이븐이 웹어플리케이션을 WAS에 배포하는 것까지 간단하게 해준다고 생각한다. 천만에 만만에 콩떡인거다.

ps3. 소스를 컴파일/빌드하여 생긴 결과물을 뭐라고 부르는게 좋을까?
빌드결과물? 빌드산출물? 배포본? 에셋? 바이너리? 실행파일번들? 빌드모듈?
개인적으로는 빌드산출물이 맘에 든다.

ps3. deployables. 이건 deployable thing을 표현한 단어인데, cargo에서 아이디어를 따왔다. 실제 이런 단어가 있는건 아닌거 같다.
Posted by 에코지오
,

흔히 개발서버나 테스트서버에 웹어플리케이션을 배포할 때는 프로젝트의 모든 빌드된 파일(class,jsp,html,...)을 한꺼번에 배포한다.

그러나 운영서버 반영은 상황이 다르다. 운영서버에는 전체 어플리케이션 파일을 한꺼번에 배포하지 않고 테스트가 통과된 검증된 파일만 반영해야 하기 때문이다. 또한 대개 업무팀에서 특정 기능 또는 화면을 골라서 운영에 반영해달라고 요청이 들어온다.

전체 프로젝트 소스에서 일부만 운영에 반영하기 위해 어떻게 하는게 최선일까?

1. FTP 프로그램 등에서 그냥 반영할 파일을 골라서 업로드하는 방법.
난 지금껏 이 방법을 써왔다. 물론 시스템오픈 전에 막바지 구축단계에서.

2. 무조건 최신 소스를 받아서 반영.
소스저장소의 중심개발축(HEAD, Trunk)에 소스를 받아서 빌드하고 그냥 전부 일괄로 반영. 배째라식.

3. 중심개발축 소스를 태깅하고 태깅된 소스를 받아서 반영.
태깅은 전체소스에 다 태그를 붙이기 때문에 반영할 소스가 아닌 것도 반영될 수 있다. 정말로 반영을 원하는 것만 가져오려면 운영에 반영할 소스들만 커밋하게 해야 하는데 이게 쉽지 않다.

4. 운영 반영용 브랜치(릴리스브랜치)를 따서 반영하는 방법
릴리스 브랜치를 만들고 중심개발축에서 운영에 반영할 소스만 골라서 릴리스 브랜치에 병합한 뒤
릴리스브랜치 소스를 커밋한다. 이후 운영반영용 빌드는 릴리스 브랜치의 소스만 취합해서 빌드,테스트하고 운영서버에 반영하는 식이다.

관련 책이나 자료를 보면 4번 방법을 많이 적용하는 것이 정석이라고 나온다.
그러나 내 경험도 그렇고 내 주위에는 1,2번 방법으로 흔히 처리한다. 내가 SM 경험이 없고 SI만 해봐서 그런 걸수도 있고...

시스템 오픈 전에는 그냥 1,2번 방식으로 하고 오픈 후 안정화되고 나면 4번 적용하는게 맞는걸까?

Posted by 에코지오
,
단위 테스트케이스 소스가 없는 프로젝트를 메이븐으로 빌드할 때 test phase(단계)는 불필요한 것이 명백하다.

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
      <skip>true</skip>
    </configuration>
  </plugin>

또는 maven.test.skip 프로퍼티 값을 true로 설정하면 test phase에 바인딩된 surefire:test 골의 실행을 skip할 수 있다.

메이븐에서 deploy는 개발 결과 아티팩트(jar,war,....)를 리모트의 메이븐 저장소에 등록하는 것을 의미한다.
공통라이브러리나 오픈소스 라이브러리를 개발한다면 이게 deploy의 의미에 합당할지도 모르겠다.
그러나 아마도 대부분의 JEE 기반 웹시스템 개발 프로젝트에서 deploy는 웹어플리케이션을 WAS에 배치하는 것을 의미한다. test 단계를 skip한 것처럼 deploy 단계에 기본으로 바인딩된 deploy:deploy 골도 skip할 수 있다.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-deploy-plugin</artifactId>
    <configuration>
     <skip>true</skip>
    </configuration>
</plugin>

이렇게 deploy:deploy를 skip해놓고 anttrun 등으로 WAS에 배포본을 올리는 작업을 deploy phase에 바인딩하면 deploy가 진정 내가 원하는 deploy로 바뀌게 된다.

한가지 또 아쉬운게 있다면 deploy 단계 이전에 실행되는 install 단계도 skip할 수 있으면 좋겠는데 안타깝게도 install:install 모조에는 skip 옵션이 없다. 젠장.
Posted by 에코지오
,

메이븐에는 wagon이라는 게 있는데 ftp, http, scp, webdav 같은 전송 프로토콜을 추상화한 것인데 site:deploy는 site 파일들을 리모트에 전송하기 위해 wagon 기능을 이용한다.  리모트 경로는 아래처럼 "프로토콜://~" 형식으로 설정하면 되며, 메이븐은 설정된 프로토콜에 적당한 프로바이더를 찾아서 파일을 전송해준다.

  <distributionManagement>
    <site>
      <id>www.yourcompany.com</id>
      <url>scp://www.yourcompany.com/www/docs/project/</url>
    </site>
  </distributionManagement>

site:deploy를 실행하고 콘솔에 찍히는 로그를 살펴보면 

- 로컬에서 .wagon12345.zip 과 같은 임시 zip 파일이 만들어진다
- zip 파일을 리모트 서버로 전송한다. ######## 표시가 늘어나는 식로 전송 진행상태를 보여준다.
- 리모트서버에서 unzip 명령으로 해당 경로에 압축을 풀고, 압축이 다 풀리면 zip 파일을 삭제한다.

그러니까 ant의 scp 타스크나 ftp 타스크가 지정된 fileset에 대해서 파일을 낱개로 하나씩 보내는 반면에,
wagon은 파일에셋을 로컬에서 압축하여 하나의 zip파일로 보낸 뒤 리모트에서 zip을 풀어내는,
나름 효율적인 방식으로 작동한다.

그러나 리모트에서 zip 파일을 푸는 방법에 문제가 있다. 리모트 서버에서 unzip이 없으면 에러가 발생하는 것이다.
실제로 지금 있는 프로젝트의 hp-ux 서버에는 unzip이 설치되어 있지 않았다.
(또 이상한 건 unzip 설치후 ssh 클라이언트로 접속해서 unzip을 실행해보면 잘 실행이 되는데도 불구하고,
wagon으로 파일을 전송해보면 unzip이 없다는 에러가 발생한다. unzip을 /usr/bin에 두어 해결은 했지만,
지금도 잘 이해가 되질 않는다. /usr/bin 이외의 다른 위치에 있으면 unzip이 없다는 에러가 발생한다는게..쫌...)

구걸링을 해보니 메이븐 1.x에서는 압축파일의 포맷과 압축해제 실행파일의 위치를 지정할 수 있게했다고 한다.
즉 tar로 묶는 것도 되고 gunzip으로 zip을 풀 수도 있었단 거다. 그러던 것이 메이븐 2.x에 와서 zip, unzip으로 고정이 돼버렸다.

http://jira.codehaus.org/browse/MSITE-30?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=147326#action_147326

황당한 생각에 wagon-ssh 프로바이더의 소스를 다운받아 까보니
org.apache.maven.wagon.providers.ssh.ScpHelper 클래스의 putDirectory() 메소드에
이렇게 하드 코딩이 돼있다.

executor.executeCommand( "cd " + path + "; unzip -q -o " + zipFile.getName() + "; rm -f " + zipFile.getName() );

왜 이렇게 fix 시켰을까?  막돼먹은 코딩인가? 아니면 관습의 강요인가?

Posted by 에코지오
,