이제 Buildr 시리즈도 막바지를 향해 달려가는군요. 이번에는 빌드 결과물을 원격의 서버에 배포해보겠습니다.
Maven에 install, deploy 단계(phase)가 있는 것처럼 Buildr에도 배포와 관련된 install, upload, release 타스크가 있습니다만, 일반적인 웹어플리케이션 개발 프로젝트에서는 그닥 쓸모가 없습니다. 웹어플리케이션 프로젝트에서 배포는 대부분 WAS에 deploy하는 것을 의미하기 때문입니다.

로컬 WAS에 배포하는 건 간단하니 넘어가고, 리모트에 있는 개발서버나 운영서버에 우리가 빌드한 어플리케이션을 전송하는 방법을 알아보겠습니다.

요즘은 원격 서버에 연결하기 위한 프로토콜로 거의 ssh, scp를 이용합니다. Ant에서는 ssh,scp 프로토콜로 원격 서버에 연결하기 위해서 흔히 jsch 라이브러리를 사용했죠. Buildr에서는 net-ssh, net-scp 라이브러리를 사용하여 이런 작업을 처리합니다. 

먼저 profiles.yaml에 다음처럼 WAS 서버에 대한 접속정보가 설정되어 있습니다(이건 예제입니다).

development:
  wasserver:
    host: 111.111.111.111
    username: weblogic
    password: weblogic
    deploy_dir: /home/weblogic/webapps
    restart: sh /home/weblogic/restart.sh

production:
  wasserver:
    host: 222.222.222.222
    username: prod
    password: prod
    deploy_dir: /home/prod/webapps
    restart: sh /home/prod/restart.sh

WAS 서버에 웹어플리케이션을 업로드하는 타스크는 아래와 같이 작성할 수 있습니다. Net::SCP.start 메소드에 접속정보를 넘겨주고 블록 안에서 scp의 upload 메소드를 이용하여 'target/webapp' 디렉토리를 업로드하고 있습니다.

task :deploy_webapp => :package do
  require 'net/ssh'
  require 'net/scp' 

  wasserver = Buildr.settings.profile['wasserver']
  Net::SCP.start(wasserver['host'], wasserver['username'],
                       :password => wasserver['password']) do |scp|
    scp.upload!('target/webapp',  wasserver['deploy_dir'], :preserve=>true, :recursive=>true) 
  end
end

이렇게 업로드하면 로컬의 target/webapp/index.jsp 파일은 원격의 /home/weblogic/webapps/webapp/index.jsp에 위치하게 됩니다. 그러니까 로컬의 webapp 디렉토리 자체가 원격 타겟 디렉토리 밑으로 들어갑니다. 아 이건 우리가 원하는게 아닙니다. target/webapp 디렉토리는 빼고, 그 밑의 파일/디렉토리들만 원격 타겟 디렉토리 밑으로 보내고 싶습니다. 고민할 필요없이 FileList를 이용하면 됩니다.

FileList['target/webapp/*'].each do |file|
  scp.upload! file, file.sub('target/webapp',wasserver['deploy_dir']),:preserve=>true,:recursive=>true
end

exploded 모드로 빌드된 target/webapp 디렉토리 말고 그냥 하나의 패키징된 war 파일을 업로드하는 건 어떨까요? 

  scp.upload! package(:war).name, wasserver['deploy_dir'], :preserve=>true

패키징된 war 파일의 이름은 package(:war).name 로 구할 수 있었습니다. 무지 쉽군요. 

실제 실행해보시면 알겠지만, 이렇게 원격으로 업로드 시 콘솔에는 아무 것도 찍히지 않습니다. 재미가 없죠. 지금 어떤 파일이 전송되고 있는지 최소한 파일 이름이라도 나와주면 좋을 겁니다. 업로드 진행현황은 upload 메소드에 블록을 넘겨주어 표현할 수 있습니다. 다음은 업로드되는 파일의 이름과 파일의 전체 크기를 출력하는 예제입니다.

scp.upload!(package(:war).name, 
                 wasserver['deploy_dir'],
                 :preserve=>true) do |ch, name, sent, total|
  puts "uploading #{name} (#{total} byte)" if sent == 0
end

음... 전송파일의 크기가 큰 경우에는 콘솔에 변화가 별로 없으니 이것도 솔직히 별로 재미가 없습니다. 좀더 다이내믹하게 진짜로 현재의 업로드 진행 현황을 막대그래프에 #이나 * 문자가 늘어나는 식으로 보여주면 좋겠는데....
Buildr에서 메이븐 저장소에서 아티팩트를 다운로드 받을 때 다운로드 그래프가 나온다는 사실을 떠올리며 Buildr 소스를 여기저기 까보다가 ProgressBar 클래스를 발견했습니다. 나름 쓸만하네요.

local_file = package(:war).name
remote_dir = wasserver['deploy_dir']

ProgressBar.start :total=>File.size(local_file),:title=>local_file do |progress|
  scp.upload!(local_file, remote_dir, :preserve=>true) do  |ch, name, sent, total|
    progress.inc sent
  end
end

콘솔에 이렇게 출력됩니다.


웹어플리케이션을 업로드했으니, WAS를 재기동해볼까요? 서버에 있는 WAS를 재기동 or 웹어플리케이션을 재배포하는 여러 방법중에서 가장 간단한 방법은 아마도 쉘스크립트를 실행하는 방법일 겁니다. 원격 명령어 실행은 Net::SSH를 이용하여 처리합니다. Net::SCP와 유사하니 설명은 생략합니다.

task :restart_was do
  require 'net/ssh' 

  wasserver = Buildr.settings.profile['wasserver']
  Net::SSH.start(wasserver['host'],
                 wasserver['username'],
                 :password => wasserver['password']) do |ssh|
    ssh.exec! wasserver['restart']
  end
end

만약 WAS 재기동 스크립트 실행을 :deploy_webapp 타스크 안에서 처리하고 싶으면 다음처럼 합칠 수 있습니다. (ssh.scp로 쓰인 부분을 주목하세요)

task :deploy_webapp do
  require 'net/ssh'
  require 'net/scp'
  wasserver = Buildr.settings.profile['wasserver']
  Net::SSH.start(wasserver['host'],
                 wasserver['username'],
                 :password => wasserver['password']) do |ssh|    
    ssh.scp.upload! package(:war).name, wasserver['deploy_dir'], :preserve=>true     
    ssh.exec! wasserver['restart']
  end
end

 

'Build&Deploy > Buildr' 카테고리의 다른 글

Buildr : Hudson CI 연계하기  (0) 2009.01.14
Buildr : 설정정보 관리  (0) 2008.12.24
Buildr : 코드분석 리포트 생성  (0) 2008.12.23
Buildr : 빌드결과 패키징  (0) 2008.12.18
Buildr : 아티팩트 다운로드 받기  (0) 2008.12.17
Posted by 에코지오
,

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

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

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

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

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 에코지오
,

허드슨에서 scp 프로토콜을 통해 서버에 접속하는 작업을 포함하는 스크립트를 실행시 인증실패 에러가 발생하면서 빌드 작업이 중단되는 경우가 있다.

아이디/패스워드 인증 방식으로 접속하더라도 스크립트에 아이디와 패스워드만 설정해 놓고 인증에 필요한 모든 준비가 다 끝났다고 안심하면 안된다.

        <sshexec host="${wasserver.ip}" username="${wasserver.username}"
         password="${wasserver.password}" trust="true" timeout="20000" failonerror="false"
         command="${wasserver.cmd.restart}" />

허드슨으로 스크립트를 실행하기에 앞서 허드슨이 설치된 머신의 커맨드 창에서 먼저 scp에 접속해서 접속인증을 획득해 주어야한다.

The authenticity of host 'xxx.xxx.xxx.xxx' can't be established.
RSA key fingerprint is 13:70:69:51:0b:84:a3:d6:fa:ab:ab:89:1a:05:2d:22.
Are you sure you want to continue connecting (yes/no)?  => 여기서 yes를 입력해야 한다.


 

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 에코지오
,

maven antrun plugin을 통해 ant의 scp 타스크를 이용하여 파일을 전송하기 위해 딸랑 jsch 라이브러리만 의존성에 추가하면 scp 타스크가 없다고 에러가 떨어진다. 

   <plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
     ... ...
    </executions>
    <dependencies>
     <dependency>
      <groupId>com.jcraft</groupId>
      <artifactId>jsch</artifactId>
      <version>0.1.38</version>
     </dependency>
    </dependencies>
   </plugin>

jsch 뿐 아리나 ant-jsch 아티팩트도 antrun 플러그인 의존성에 추가해야 한다. maven을 통하지 않고 순전히 scp작업을 build.xml에 정의해서 ant로 실행하면 jsch 라이브러리만 ant에 추가해주면 된다. ant-jsch.jar는 이미 ant에 포함되어 있기 때문이다.

   <plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
     <execution>
      <id>remote-exploded-deploy-scp</id>
      <phase>integration-test</phase>
      <goals>
       <goal>run</goal>
      </goals>
      <configuration>
       <tasks>
        <scp todir="user:user@myserver:/home/user/temp" trust="true">
         <fileset dir="${project.basedir}/temp" />
        </scp>
       </tasks>
      </configuration>
     </execution>
     <execution>
      <id>server-restart</id>
      <phase>integration-test</phase>
      <goals>
       <goal>run</goal>
      </goals>
      <configuration>
       <tasks>
        <sshexec host="myserver" username="user" password="user" trust="true"
         timeout="20000" failonerror="false" command="sh restart.sh" />
       </tasks>
      </configuration>
     </execution>
    </executions>
    <dependencies>
     <dependency>
      <groupId>org.apache.ant</groupId>
      <artifactId>ant-jsch</artifactId>
      <version>1.7.1</version>
     </dependency>
     <dependency>
      <groupId>com.jcraft</groupId>
      <artifactId>jsch</artifactId>
      <version>0.1.38</version>
     </dependency>
    </dependencies>
   </plugin>

마찬가지로 ftp 타스크를 쓸 때도 commons-net 뿐 아니라 ant-commons-net도 추가해주어야 한다.

   <plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
     <execution>
      <id>remote-exploded-deploy-ftp</id>
      <phase>integration-test</phase>
      <goals>
       <goal>run</goal>
      </goals>
      <configuration>
       <tasks>
        <ftp server="myserver" remotedir="/home/user/temp" userid="user"
         password="user">
         <fileset dir="${project.basedir}/temp" />
        </ftp>
       </tasks>
      </configuration>
     </execution>
    </executions>
    <dependencies>
     <dependency>
      <groupId>org.apache.ant</groupId>
      <artifactId>ant-commons-net</artifactId>
      <version>1.7.1</version>
     </dependency>
     <dependency>
      <groupId>commons-net</groupId>
      <artifactId>commons-net</artifactId>
      <version>1.4.1</version>
     </dependency>
    </dependencies>
   </plugin>
Posted by 에코지오
,

허드슨의 장점 중 하나가 여러가지 플러글인을 끼워넣어 기능을 확장할 수 있다는 것인데,
그런 플러그인 중에 ftp, scp 처럼 빌드 결과물을 서버로 전송할 수 있는 플러그인이 있다.

프로젝트 통신환경이 ssh라서 scp 플러그인을 설치하고 빌드 성공후 빌드 산출물을 서버에 업로드하도록
설정을 하고 빌드를 해보았다.

빌드는 성공했으나 실제로 산출물은 업로드가 되지 않았다.
콘솔 아웃풋도 에러업이 깨끗하였고 허드슨 로그를 뒤져보아도 scp 플러그인에서 에러가 났다는 내용은 없었다.

허탈한 마음에 구글링해보니 역시나...

http://www.nabble.com/Job-using-Maven-does-not-run-SCP-plugin-td17765416.html

현재 scp 플러그인은 job이 maven 스타일인 경우 작동하지 않는 버그가 있다고 한다.

The SCP plugin does not execute with the Maven2 job type, but works fine with a free style job type.


아직은 maven style job 보다는 free style job이 안정적인 거 같다.

ps. 사용한 hudson버전은 1.252다.

Posted by 에코지오
,