はじめに

Spring Bootはバージョン1.3.0以降、Executable JarとしてJarファイルに固めるだけでなく、実行スクリプトがJarファイル内に埋め込まれたFully Executable Jarが作成できる。

サーバに常駐させるため、普通のJarファイルであれば起動スクリプトを書く必要があるが、Fully Executable Jarであれば不要となる。
通常のJarファイルの起動スクリプトが公式に提供されていない状況だが、Fully Executable Jarの登場によって、実質的に起動スクリプトが公式に提供されたことになる。
これによって、簡単にserviceコマンドでアプリをdeamon起動することができるようになった。

ここでは、ビルド方法と次の環境にリリースする場合の構築手順と解説を記載する。
※基本はhttp://docs.spring.io/spring-boot/docs/1.3.5.RELEASE/reference/htmlsingle/#deployment-installに書いてある通り。

環境

CentOS 6.8
Java8
Spring Boot 1.3.5
アプリ名:myapp.jar
アプリ用ユーザ名:myappusr

ビルド方法

pom.xmlにspring-boot-maven-pluginというbuildプラグインを追加し、executableをtrueにする。
falseを設定するとExecutable Jarファイルを作成し、trueを設定するとFully Executable Jarファイルを作成してくれる。

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <executable>true</executable>
                </configuration>
            </plugin>
        </plugins>
    </build>

構築手順

[root@host ~]# #アプリ用に任意のディレクトリを作成する。jarにworking directoryとして利用される。
[root@host ~]# mkdir /usr/local/bin/myapp
[root@host ~]# chown myappusr.myappusr /usr/local/bin/myapp
[root@host ~]#
[root@host ~]# #アプリを配置する。
[root@host ~]# cp /tmp/myapp.jar /usr/local/bin/myapp/
[root@host ~]# #myapp.jarの所有者がmyapp.jarの実行ユーザになるので、rootではなくアプリ用ユーザにする。※1
[root@host ~]# chown myappusr.myappusr /usr/local/bin/myapp/myapp.jar
[root@host ~]# #Fully Executable Jarはスクリプトが内蔵されているため、実行可能ファイルである必要があり、最低限、権限xを所有者につける。
[root@host ~]# chmod 500 /usr/local/bin/myapp/myapp.jar
[root@host ~]#
[root@host ~]# #jarファイルと同名のconfファイルを作成し、環境変数やJVMに渡すパラメータを設定する。※2
[root@host ~]# echo -e 'export LANG="ja_JP.UTF-8"\nJAVA_OPTS="-Dspring.profiles.active=development"' > /usr/local/bin/myapp/myapp.conf
[root@host ~]# #confファイルの中身確認
[root@host ~]# cat /usr/local/bin/myapp/myapp.conf
export LANG="ja_JP.UTF-8"
JAVA_OPTS="-Dspring.profiles.active=development"
[root@host ~]#
[root@host ~]# #deamon起動のため/etc/init.d/に登録
[root@host ~]# ln -s /usr/local/bin/myapp/myapp.jar /etc/init.d/myapp
[root@host ~]# #自動起動のためchkconfigに登録
[root@host ~]# chkconfig --add myapp
[root@host ~]# #起動
[root@host ~]# service myapp start
Started [3061]
[root@host ~]#
[root@host ~]# #起動確認
[root@host ~]# service myapp status
Running [3061]
[root@host ~]# #起動確認2
[root@host ~]# ps -ef | grep jar
myappusr  3061     1  3 11:58 ?        00:01:19 /usr/bin/java -Dsun.misc.URLClassPath.disableJarChecking=true -Dspring.profiles.active=development -jar /usr/local/bin/myapp/myapp.jar
[root@host ~]#
[root@host ~]# #ログ確認※3
[root@host ~]# ll /var/log/myapp.log
-rw-r--r--. 1 myappusr root 402  6月 23 12:36 2016 /var/log/myapp.log
[root@host ~]# #PID確認※4
[root@host ~]# ll /var/run/myapp/ -d
drwxr-xr-x. 2 myappusr root 4096  6月 23 12:36 2016 /var/run/myapp/
[root@host ~]# ll /var/run/myapp/myapp.pid
-rw-r--r--. 1 myappusr root 5  6月 23 12:36 2016 /var/run/myapp/myapp.pid

解説

※1)myapp.jarの所有者がmyapp.jarの実行ユーザになるので、rootではなくアプリ用ユーザにする。

http://docs.spring.io/spring-boot/docs/1.3.5.RELEASE/reference/htmlsingle/#deployment-initd-service-securingにセキュリティ面を考慮した設定方法が列挙されている。
次の二点は、構築手順で達成できている。

  • myapp.jarの所有者をrootからmyappusrに変更する
  • myapp.jarの権限を500に変更する
  • myapp.confの所有者をrootにする(rootで構築しているため、自動で所有者がrootになっている)

他は設定していないので、必要に応じてセキュリティを高めていく必要がある。

  • myappusrのシェルを/usr/sbin/nologinにする
  • jarファイルにchattr +i
  • confファイルにchmod 400

※2)jarファイルと同名のconfファイルを作成し、環境変数やJVMに渡すパラメータを設定する。

jarファイルと同じディレクトリに同名のconfファイルを置くと、起動時に読み込んでくれる。
読み込む方法は、jarファイルの初めの部分がシェルスクリプトになっているので、jarファイルをlessして見るとわかる。

[myappusr@host myapp]$ less myapp.jar
#!/bin/bash
#
#    .   ____          _            __ _ _
#   /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
#  ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
#   \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
#    '  |____| .__|_| |_|_| |_\__, | / / / /
#   =========|_|==============|___/=/_/_/_/
#   :: Spring Boot Startup Script ::
#
 
中略
 
# Source any config file
configfile="$(basename "${jarfile%.*}.conf")"
# shellcheck source=/dev/null
[[ -r "${jarfolder}/${configfile}" ]] && source "${jarfolder}/${configfile}"
 
中略
 
# Build actual command to execute
command="$javaexe -Dsun.misc.URLClassPath.disableJarChecking=true $JAVA_OPTS -jar $jarfile $RUN_ARGS $*"
 
中略
 
    if [ $USE_START_STOP_DAEMON = true ] && type start-stop-daemon > /dev/null 2>&1; then
      arguments=(-Dsun.misc.URLClassPath.disableJarChecking=true $JAVA_OPTS -jar $jarfile $RUN_ARGS "$@")
      start-stop-daemon --start --quiet \
        --chuid "$run_user" \
        --name "$identity" \
        --make-pidfile --pidfile "$pid_file" \
        --background --no-close \
        --startas "$javaexe" \
        --chdir "$working_dir" \
        -- "${arguments[@]}" \
        >> "$log_file" 2>&1
      await_file "$pid_file"
    else
      su -s /bin/sh -c "$command >> \"$log_file\" 2>&1 & echo \$!" "$run_user" > "$pid_file"
    fi

source "${jarfolder}/${configfile}"となっているため、環境変数を設定したければexportを、JVMに渡す引数を設定したければJAVA_OPTSに値を設定すればいい。

今回は以下の二つを設定した。

export LANG="ja_JP.UTF-8"
JAVA_OPTS="-Dspring.profiles.active=development"
  • JAVA_OPTS="-Dspring.profiles.active=development"

    Spring Bootの実行環境をJVM引数で渡すことができ、propertiesファイルの切り替え等ができるため、おそらくどのプロジェクトでも必要になる引数だろう。
    開発環境であればdevelopment、ステージング環境であればstaging、本番環境であればproductionなどの引数を渡す。

  • export LANG="ja_JP.UTF-8"

    文字コードを設定した理由は、今回の環境がCentOSというところにある。
    /etc/init.dにあるスクリプトを直接実行する場合と異なり、manによれば、serviceコマンドで実行するとプログラムを起動する際に環境変数LANGとTERM以外が引き継がれない。

    ENVIRONMENT
    LANG, TERM
    The only environment variables passed to the init scripts.

    しかし、CentOSではこの記述が当てはまらず、LANGも引き継がれない。
    原因は/sbin/serviceスクリプトを見るとわかるが、env -iで環境変数をクリアしており、LANGを再設定してないから。
    参考:http://heartbeats.jp/hbblog/2013/06/service-start-stop.html

    LANG="ja_JP.UTF-8"が設定されないと、Javaが日本語のファイルを扱う際に文字化けを起こしてしまうため、Red HatやFedoraも含めCentOS系のディストリを使用する場合は、confファイル内でLANG変数をexportしなければならない。
    Ubuntu系を使用する場合でも、LANGの設定があることでバグを起こすわけではないので、一律この設定を入れてもいいかもしれない。

他にもconfファイルで設定できる項目はたくさんあるので、公式ドキュメントを確認してほしい。
http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/deployment-install.html#deployment-script-customization

※3)ログ確認

ログファイルは所定の場所に出力される。
出力場所を変えたければconfファイルで設定ができる。

※4)PID確認

PIDファイルは所定の場所に出力される。あらかじめPIDファイルが格納されるディレクトリを作る必要はない。
出力場所を変えたければconfファイルで設定ができる。