Spring Bootのサービス稼働環境
弊社では食品の検索に対してXMLで結果を返すRESTfulのAPIを運営しているのですが、今回、機能追加やJSONでの返却を可能にするバージョンアップを計画しています。
今まではJSP+Servletでの実装だったんですが、今回はSpring Bootで実装してみました。 Spring BootではFully Executable JARで簡単にサービス稼働環境を作ることができたので、ここで紹介します。
Fully Executable JARとは
Spring Bootでは、作成したJARファイルをjava -jar
で起動することができますが、UNIX環境でより簡単に起動することができる仕組みです。
詳しくは
Spring Boot Reference Guide
をどうぞ。
JARファイルの作成
APIのプロジェクトはmavenで構成管理をしているのですが、mavenのpluginで簡単にFully Executable JARを作ることができました。
まず、pom.xmlのspring-boot-maven-plugin定義に<executable>true</executable>
を追加します。
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> + <configuration> + <executable>true</executable> + </configuration> </plugin>
次に、maven package
でビルドします。
$ mvn package [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building XXXXXXXX [INFO] ------------------------------------------------------------------------ ... [INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ xxxx --- [INFO] Building jar: /xxx/xxx/target/XXXXXXXX.jar [INFO] [INFO] --- spring-boot-maven-plugin:2.0.3.RELEASE:repackage (default) @ xxxx --- [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 10.709 s [INFO] Finished at: 2018-11-09T13:03:46+09:00 [INFO] Final Memory: 42M/290M [INFO] ------------------------------------------------------------------------ $
という流れで、targetフォルダ下にJARファイルが作成されます。
起動のしかた
Fully Executable JARができると、起動はそのJARファイル自身を実行するだけです。例えば、JARファイルをapp.jarで作ったとすると、
$ cd target/ $ ./app.jar . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.3.RELEASE) 2018-11-09 13:51:58.709 INFO 11707 --- [ main] xxx.Application : Starting Application v2.0 on xxx with PID 11707 (app.jar started by xxx in /xxx/xxx/xxx/target) 2018-11-09 13:51:58.721 INFO 11707 --- [ main] xxx.Application : No active profile set, falling back to default profiles: default 2018-11-09 13:51:58.886 INFO 11707 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@12f40c25: startup date [Fri Nov 09 13:51:58 JST 2018]; root of context hierarchy
という感じで、簡単に起動できます。
全てのライブラリはJARファイル内に含まれていますので、JARファイルを好きなところに配置して起動することができます。 先ほどの公式ドキュメントでは、init.dやsystemdのサービスとして起動する方法も書いてありました。
設定のしかた
Fully Executable JARでは、JARファイルと同じフォルダに、JARと同じ名前の.confファイルを作ることで、起動時の設定を行うことができます。
今回、Spring Bootを使うにあたり、JSP+Servletのシステムと異なるバージョンのJAVAを使用します。 また、アプリケーションサーバーのポート番号や、環境に応じたプロファイルの選択をしたいと思っています。
そのため、app.jarと同じフォルダに、以下の内容のapp.confというファイルを作成しました。
export JAVA_HOME=/usr/java/jre1.8.0_192 export JAVA_OPTS="-Dserver.port=8080 -Dlog4j.configuration=log4j-production.properties" export SPRING_PROFILES_ACTIVE=production export MODE="service"
JREは、既存の環境の/usr/javaフォルダ配下に展開だけして、デフォルトでの使用はしないようにしていますが、app.jarの起動時にJAVA_HOMEを指定して使用しています。
アプリケーションサーバーのポートやlog4jのプロパティは、JAVA起動オプションで指定して、Springのプロファイルも環境変数で与えています。
MODEはserviceを指定することで、start,stop等を使い、デーモンとして起動できます。
起動用の様々なオプションはこちら Spring Boot Reference Guide
Spring Bootの様々なオプションはこちら Appendix A. Common application properties
を参考にして下さい。
なぜ動くの?
なぜJARファイル自身を実行して、このようなことができるのでしょうか?
少し調べてみたところ、JARファイル自身の先頭に細工があるようです。
JARファイルの中身を見てみると
$ less app.jar #!/bin/bash # # . ____ _ __ _ _ # /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ # ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ # \\/ ___)| |_)| | | | | || (_| | ) ) ) ) # ' |____| .__|_| |_|_| |_\__, | / / / / # =========|_|==============|___/=/_/_/_/ # :: Spring Boot Startup Script :: # ### BEGIN INIT INFO # Provides: caloriecheck-api # Required-Start: $remote_fs $syslog $network # Required-Stop: $remote_fs $syslog $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: XXXXXXXX # Description: Parent pom providing dependency and plugin management for applications built with Maven # chkconfig: 2345 99 01 ### END INIT INFO [[ -n "$DEBUG" ]] && set -x # Initialize variables that cannot be provided by a .conf file WORKING_DIR="$(pwd)" # shellcheck disable=SC2153 [[ -n "$JARFILE" ]] && jarfile="$JARFILE" [[ -n "$APP_NAME" ]] && identity="$APP_NAME" ...
となっており、JARファイルの先頭がサービス起動のシェルスクリプトになっています。
そういえば、JARパッケージ時にspring-boot-maven-plugin:2.0.3.RELEASE:repackage
と、何かの処理をされていたようです。
JARファイルというのは、実体はZIPファイル(拡張子を.zipに変更するとunzipできる)なのですが、こちらのブログ( Spring Boot の Fully Executable Jar はなぜ動くのか - RAMBO )によると、ZIPの解凍はファイルの後ろ側から処理をしていくために、先頭にスクリプトを載せても動くようです。
面白いことをするなあと感心しました。