Windowsサーバー上のTomcatにデプロイするbatファイルを作ってみた


はじめに

今のPJでは開発サーバー(Windows Server 2008 R2)は社内環境上に、Jenkinsは別の環境上にあるという状況のため開発サーバーにあるTomcatにデプロイするために、
1. 自分のローカルマシンにインターネットでJenkinsに繋いで、ビルドしたwarをダウンロード
2. 自分のローカルマシンから開発サーバーにリモートデスクトップで繋いで、warをデプロイ
ということを手作業で行っています。
※開発サーバーはインターネットに繋いじゃダメという前提です。

1回、2回の作業なら別にいいんですが、毎日となると面倒臭くてしょうがなかったため、batファイルを使って一発でデプロイできるものを作ってみました。

Windows用のwgetとPsToolsのインストール

今回作ったbatファイルでは、Jenkinsからwarをダウンロードするのにwgetコマンドを、Tomcatのworkディレクトリを削除したりするのにPsToolsのpsexecコマンドを使っています。
wgetはLinuxに入っているのとまったく同じように使えるものなので、説明は割愛します。
psexecはリモートマシン上にあるコマンドを実行することができるコマンドです。

というわけで、まずwgetとpsexecが使えるようにします。

wgetをインストール

  1. http://gnuwin32.sourceforge.net/packages/wget.htm からBinariesとDependenciesのzipをローカルに保存
  2. 保存したzipを両方解凍してDependencies側のbinフォルダ配下の全てのdllをBinaries側のbinフォルダにコピー
  3. Binaries側のbinにPathを通す

PsToolsのインストール

  1. https://technet.microsoft.com/ja-jp/sysinternals/bb896649.aspx からPsToolsをダウンロード
  2. ダウンロードしたzipを解凍して、Pathを通す

デプロイするbatファイル

作ったbatファイルは以下の様な感じです。

deploy.bat

@echo off

rem ***************************************
rem ログファイル名取得
rem ***************************************
echo %date%
echo %time%

set yyyy=%date:~0,4%
set mm=%date:~5,2%
set dd=%date:~8,2%

set time2=%time: =0%

set hh=%time2:~0,2%
set mn=%time2:~3,2%
set ss=%time2:~6,2%

set filename=%yyyy%-%mm%%dd%-%hh%%mn%%ss%

set log=%filename%.log

rem **********************************************************
rem Jenkinsからwarを取得
rem **********************************************************
wget -N -P "C:\for_remote_deploy_temp"  http://<jenkins_host>/jenkins/job/<target_job>/lastSuccessfulBuild/artifact/dist/target.war >> %log% 2>&1
if %ERRORLEVEL% neq 0 goto error

rem **********************************************************
rem APサーバーのTomcatを停止する
rem **********************************************************
for /F "tokens=1,2,3,4,5 usebackq delims= " %%a in (`sc <deploy_server_host> query "Tomcat6" ^| findstr -i state`) do ( 
  if "%%a"=="STATE" ( 
      if "%%d"=="RUNNING" ( 
           sc <deploy_server_host> stop "Tomcat6"  >> %log% 2>&1
           if %ERRORLEVEL% neq 0 goto error
      ) 
   ) 
 ) 
rem **********************************************************
rem サーバーが止まっているか確認して、止まっていなかったら30秒待つ
rem **********************************************************
set /a CNT=0

:wait_for_stopping
for /F "tokens=1,2,3,4,5 usebackq delims= " %%a in (`sc <deploy_server_host> query "Tomcat6" ^| findstr -i state`) do (
    if "%%a"=="STATE" ( 
        if "%%d" neq "STOPPED" ( 
            timeout 30 
        ) else ( 
            goto continue_process 
        ) 
    ) 
 ) 
set /A CNT=%CNT%+1

if "%CNT%" lss "2" ( 
  goto wait_for_stopping 

 ) else ( 
  goto exit 
 )


:continue_process
rem ******************************************************************
rem psexecコマンドを使用して、warと展開ディレクトリ、workを全削除する
rem ******************************************************************
rem war削除
psexec <deploy_server_host> cmd /c del <tomcat_catalina_base>\webapps\target.war  >> %log% 2>&1
if %ERRORLEVEL% neq 0 goto error

rem 展開ディレクトリ削除
psexec <deploy_server_host> cmd /c rd <tomcat_catalina_base>\webapps\target /s /q  >> %log% 2>&1
if %ERRORLEVEL% neq 0 goto error

rem work削除
psexec <deploy_server_host> cmd /c rd <tomcat_catalina_base>\work\target /s /q  >> %log% 2>&1
if %ERRORLEVEL% neq 0 goto error

rem **********************************************************
rem xcopyでwarをAPサーバーにコピーする
rem **********************************************************
xcopy "C:\for_remote_deploy_temp\target.war" "<tomcat_catalina_base>\webapps"  >> %log% 2>&1
if %ERRORLEVEL% neq 0 goto error

rem **********************************************************
rem APサーバーのTomcatを開始する
rem **********************************************************
sc <deploy_server_host> start "Tomcat6"  >> %log% 2>&1
if %ERRORLEVEL% neq 0 goto error

:error

:exit
exit

適当な解説

scコマンド

サービス管理/操作するためのコマンドです。ホストを指定してあげるとリモートマシンにも使うことができます。対象のサービスは、タスクマネージャのサービスタブに表示されるサービス表示名ではなく、内部KEY名で指定する必要があります。
TomcatをWindowsにデフォルトインストールした場合は、

  • 内部KEY名:Tomcat6
  • サービス表示名:Apache Tomcat 6

となっています。
サービス表示名から内部KEY名を引くためには

GetKeyName.bat
C:\>sc <host> GetKeyName "Apache Tomcat 6"
[SC] GetServiceKeyName SUCCESS
名前 = Tomcat6

とするとわかります。

scコマンドでサービスの起動/停止をすると、起動/停止のシグナルは送信しますが実際にサービスが動き始めたのか、止まったのかまではわかりません。(シグナルを送信した時点でコマンドが返ってきてしまうため)

現時点でのサービスの状態を知るためには、下記のようにqueryを使います。

statusCheck.bat
C:\>sc <host> query "Tomcat6"
SERVICE_NAME: Tomcat6
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 4  RUNNING
                                (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

今回作ったbatファイルではfor文のtokens,usebackq,delimsを使い、STATE部分の文字列を抜き出し、取得したサービスの状態によって次処理を切り替えるようにしています。

もしコピって使いたいという方がいたら・・・

自分が作っていて、ハマったポイントがあるので書いておきます。コピって使う方がいたら注意して下さい。(batファイル作る上で常識っぽい事なので、知っている方は読み飛ばして下さい。)

  1. if文の括弧 "(" と ")"の前後には、”必ず”空白を入れて下さい。
    空白がデリミタになっているようなので、空白がないと正しく動きません。

  2. for文でusebackqを指定しinでコマンドを発行する場合、パイプでコマンドをつなぐことが可能ですが、パイプの前にキャレット"^"でエスケープする必要があります。

  3. for文で半角スペースをデリミタにして文字列を分割するためdelimsを指定しています。なので、delimsの直後には半角スペースが入っています。

psserviceコマンドについて

今回作ったbatファイルでは、Tomcatサービスの起動/停止にscコマンドを使っていますが、PsToolsにもリモートマシンのサービスを管理/操作するpsserviceというコマンドがあります。最初はpsserviceを使っていたのですがバグ?があるようで、psserviceでサービスを操作するとリモートマシンのCPUが100%に張り付いてしまうため、使用を断念しました。

追記(2015/06/25更新)

scコマンドのqueryexについて

上のscコマンドのqueryよりさらに詳細情報を取得できるqueryexというのがあります。

queryex.bat
c:\>sc <host> queryex "Tomcat6"

SERVICE_NAME: Tomcat6
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 4  RUNNING
                                (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0
        PID                : 8872
        FLAGS              :

これを使うとPIDも取得できます。
例えば、サービスがハングアップしてしまっている状態(STOP_PENDINGなど)で強制的にプロセスをkillしたい場合は、以下のようにするといいかもしれません。

taskkill.bat
for /F "tokens=1,2,3,4,5 usebackq delims= " %%a in (`sc <host> queryex "Tomcat6" ^| findstr -i PID`) do ( 
        psexec <host> cmd /c taskkill /PID "%%c" /F >> %log% 2>&1 
        if %ERRORLEVEL% neq 0 goto error 
 ) 

まぁサービスの状態だけで、いきなりプロセスをkillしていいのかっていう話はありますが・・・
ご参考までに