まえがき
こちらの記事の続編です。
目的
VueとSpringで作成したプロジェクトの構築手順の備忘録。 備忘録のため、詳細な説明を省略している部分があります。
前提
環境
構成
やること
の続き
実装
ローカル環境でTomcatにデプロイしてみる
前回は取り乱して終わったので、今回は冷静になってやっていきます。まず、ローカルのTomcatでもこの現象が再現するかを確認してみます。(SpringBoot組み込みのTomcatで正常に画面が表示されることは確認ずみという前提で進めます)
- ダウンロードしたTomcatのフォルダを解凍します。
- webapps配下にwarファイルを格納します。
- Tomcatの場所までターミナルないしコマンドプロンプトで移動します。
chmod -R 755 ./[Tomcatのフォルダ名]
コマンドなどで実行権限を与えてやりましょう。- その後に、binフォルダに移動し、
sh startup.sh
コマンドでTomcatを起動します。 - webapps配下にwarが解凍されていることを確認してください。
http://localhost:8080/
にアクセスし、Tomcatの画面が表示されることを確認してください。http://localhost:8080/TMS-0.0.1-SNAPSHOT/frontend
にアクセスします- 残念ながら再現されてしまいました。。。
- 一応、
tail -f ./logs/catalina.out
でログを確認しときます。(問題なさそうです)
Tomcat内包のjarファイルなら動く?
- build.gradleの依存関係を外して、jarを出力できる状態にします。
plugins { id 'org.springframework.boot' version '2.6.0' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' id 'war'←これを削除 }
java -jar
コマンドで実行し、URLにアクセスしてみます。こちらは問題なく動くようです。
Spring Boot 実行可能な Jar 形式 - リファレンス
build.gradleの依存関係が不足している?
- 公式17.17.1. Create a Deployable War File曰く、下記のようにクラスを継承してメソッドをオーバーライドせよとのことなので、従います。
@SpringBootApplication public class TmsApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(TmsApplication.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(TmsApplication.class); } }
これによりサ ーブレットコンテナによって起動される際にアプリケーションを設定することができるようになるとのことです。
また、依存関係も不足していたようなのでこちらも追加
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
ここまで行きましたらローカルからgit push、EC2上でgit pullしてソースを最新に更新します。
ビルドして、生成されたwarをTomcatのwebapps配下に移動します。
- warが展開されたことを確認したのちに再度URLにアクセスしてみます。
- まだ動きません。(遠い目...)
Javaのバージョンを確認する
- こちらの記事にJavaのバージョン違いでハマったという内容が記載されているため確認。
- EC2上で
java --version
コマンドを叩くと
openjdk 17.0.1 2021-10-19 LTS OpenJDK Runtime Environment Corretto-17.0.1.12.3 (build 17.0.1+12-LTS) OpenJDK 64-Bit Server VM Corretto-17.0.1.12.3 (build 17.0.1+12-LTS, mixed mode, sharing)
- build.gradleは、
sourceCompatibility = '11'
これはいけそうな気がしてきました。TomcatはJAVA_HOMEに設定されているJavaを読みに行くので、EC2にインストールしたTomcatはJava17で動いていると考えられます。
find . | grep jdk-11
などのコマンドでJDKのパスを探し出して、コピーします。/usr/lib/jvm/java-11-openjdk-11.0.13.0.8-1.amzn2.0.3.x86_64
vi ~/.bash_profile
を確認すると
PATH=$PATH:$HOME/.local/bin:$HOME/bin export PATH
すでにPATHが設定されていました。下記のようにJAVA_HOMEを設定します。
export JAVA_HOME="/usr/lib/jvm/java-11-openjdk-11.0.13.0.8-1.amzn2.0.3.x86_64" export PATH="${JAVA_HOME}:/usr/lib/jvm/java-11-openjdk-11.0.13.0.8-1.amzn2.0.3.x86_64/bin $PATH:$HOME/.local/bin:$HOME/bin"
source ~/.zshrc
コマンドで更新した後にecho $JAVA_HOME
コマンドでJavaのバージョンが11に設定されていることを確認します。
[root@ip-10-0-10-54 jvm]# echo $JAVA_HOME /usr/lib/jvm/java-11-openjdk-11.0.13.0.8-1.amzn2.0.3.x86_64
java -version
コマンドを実行したところ、JDK-17を参照していました。- /usr/lib/jvm/java-11-openjdk-17を削除して、javaコマンドを叩くと、
usr/bin/java not found
とのこと - そこでそのディレクトリに移動して、
ls -l | grep java
で調べてみると、java -> /etc/alternatives/java
シンボリックリンクが貼られているようなので、リンク先に移動してls -l | grep java
を叩くと、
一部抜粋 lrwxrwxrwx 1 root root 52 1月 13 22:18 java -> /usr/lib/jvm/java-17-amazon-corretto.x86_64/bin/java
先ほど削除したJDKがリンク先となっていたようです。
alternatives --config java
コマンドを叩くと以下のように出てくるので、該当のJDKを指定します。java --version
コマンドを叩いて該当のJavaのバージョンが表示されていればOKです
openjdk 11.0.13 2021-10-19 LTS OpenJDK Runtime Environment 18.9 (build 11.0.13+8-LTS) OpenJDK 64-Bit Server VM 18.9 (build 11.0.13+8-LTS, mixed mode, sharing)
それではここで例のごとくURLにアクセスしてみます。
はい。Damn it...
alternativesコマンドとは?
alternativesはCentOS(Amazon Linux2は公表されていないがCentOSがベースになっている的な話を聞いたことがあります)に標準で入っているコマンドです。 シンボリックリンクを活用して、同様の機能を持つソフトウェアや、バージョン違いのソフトを切り替えられる」という優れもののコマンドです。 つまり、 Javaだけに限らず様々なコマンドで応用することが可能ということでした。
Tomcatのコンテキストパスを確認する
めげずにやっていきます。お次はTomcatのコンテキストパスを確認していきます。 その前にこれまでの対応でローカル環境ではどうなるか確認しておきます。
- 真っ白な画面が返ってきましたね。。。でもこれはTomcatが404を返していなさそうなので、ちょっと希望が見えてきました。
デベロッパーツールのネットワークタブを確認すると/frontendのURLへのステータスコードは200で返却されており、バンドルされたcssやjsファイルが404で取得できていない模様です。
一旦、Tomcatを停止して、Spring Bootを起動して同様のパスにポート8080でアクセスするとどうなるのか検証します。
こちらは正常に返却されていることが分かります。
一応Tomcatのバージョンに起因していないかを確認するために、Tomcat8.5系のものをインストールして同様にアプリケーションをデプロイして検証してみます。(今回は8.5.75をインストールしてきました)
…普通に404が返ってきました。分からん。
- とりあえず、、、コンテキストパスの設定を変更してみます。(非推奨ですが)conf/server.xmlのHOSTタグの子要素としてContextタグを追加して、下記のように記載します。
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="/TMS-0.0.1-SNAPSHOT" /> </Host>
これでTomcatを再起動して、URLにアクセスしてみますと。。。
状態は変わらずです。
一旦これは保留にして、内包Tomcatのjarファイルで構成することに方針を転換することにしました。
アプリケーションをサービス化して、EC2起動時に自動起動設定する
この前に、以下のポートフォワーディングが失敗する現象に見舞われたので対応。(結局EC2インスタンスを再起動することで解消できたので原因が謎のまま)
まず、
sudo systemctl disable tomcat.service
でEC2にインストールしたTomcatの自動起動設定を解除しておきます。systemctl is-enabled tomcat.service
で状態を確認して下記のようになっていればOkです。
[ec2-user@ip-10-0-10-54 share]$ systemctl is-enabled tomcat.service disabled
- EC2起動時にjarを実行する形にするためにはサービスに登録する方法があるようなのでそちらが一番手取り早そうなのでその方法を採用しようと思います。(AWSへのデプロイ方法がたくさんあるのでこれができたら今度はCodeDeployとかに挑戦してみたい)
Deploying Spring Boot Applications
A fully executable jar can be executed like any other executable binary or it can be registered with init.d or systemd
- fully executive jarという形式にするといけるそうで、いくつか注意点があるみたいです
Fully executable jars work by embedding an extra script at the front of the file. Currently, some tools do not accept this format, so you may not always be able to use this technique. For example, jar -xf may silently fail to extract a jar or war that has been made fully executable. It is recommended that you make your jar or war fully executable only if you intend to execute it directly, rather than running it with java -jar or deploying it to a servlet container.
A zip64-format jar file cannot be made fully executable. Attempting to do so will result in a jar file that is reported as corrupt when executed directly or with java -jar. A standard-format jar file that contains one or more zip64-format nested jars can be fully executable.
zip64形式のjarファイルは、完全に実行可能な状態にすることができないので、一つ以上ネストしたjarファイルにせよ的なことですかね。。。
下記の記述をbuild.gradleに追加するだけで良いという簡単さ!
bootJar { launchScript() }
- この記述を加えることで
./myapplication.jar(環境により可変)
コマンドでアプリケーションの実行が可能になるとの事です。
You can then run your application by typing ./my-application.jar (where my-application is the name of your artifact).
変更をプッシュしてここから先はEC2のWebサーバー上で行なっていきます。
アプリケーションをサービスに登録する事で容易に実行できるようになるそうです。
Spring Boot application can be easily started as Unix/Linux services by using either init.d or systemd.
systemdかinit.dのどちらにすべきか判断できないのですが、下記記事を参考に、init.dはスクリプトのため、挙動を柔軟に制御できる反面、スクリプトに問題があると気付きにくいという点がエラーの元になると嫌なので、systemdに登録していきます。
systemdではこれまでサービス起動スクリプトで定義されていたものがUnitという形で定義されますので、サービスの管理=Unitの管理
デーモン:OSにおいて動作するプロセス(プログラム)で、主にバックグラウンドで動作するプロセス
Linux の任意のスクリプトをサービス登録し OS 起動時に自動起動させる [init.d/SystemD 編] - Qiita
まずは
/var/lib/app/TMS/current
この場所にjarファイルを配置します。/var/lib/app/TMS/current
[Unit] Description = This is TwmpleManagementSystem's daemon [Service] ExecStart = /home/ec2-user/TTM/build/libs/TMS-TMS.jar User = ec2-user Group = ec2-user SuccessExitStatus = 143 [Install] WantedBy = multi-user.target
Serviceファイルの書き方などはこちらを参考: systemd.unit
systemdの*.serviceファイルの書き方 - Qiita
sudo systemctl enable <自分で作成したファイル名>.service
コマンドを実行Failed to execute operation: Cannot send after transport endpoint shutdown
こんなエラーが出たので対処systemctl list-unit-files --type=service | grep <自分で作成したファイル名>
コマンドでコマンドが登録されていることを確認
ステータスがmaskedになっており、この状態だとサービスの起動ができないという状態のようなので、
systemctl unmask myapp.service
コマンドを実行します。すると、linkが削除された旨のメッセージが出てきて
systemctl list-unit-files --type=service | grep <自分で作成したファイル名>
でも参照できなくなりました。Unitファイルの設定場所は
/usr/lib/systemd/system
または/etc/systemd/system
であり、後者に上書き用のファイルなどを設置する事で解決できそうなので、mvコマンドなどでServiceファイルをそちらに移行します。
- 再度確認してみると、disabled状態ですがServiceファイルが登録されていることが確認できました。
# systemctl enable TMS-TMS.service
コマンドを実行した後に、# systemctl is-enabled TMS-TMS.service
コマンドを実行し、enbledにステータスが変更されていることを確認できればOKです。# systemctl start TMS-TMS.service
を実行し、アプリケーションのURLにアクセスした際に画面が表示されれば完了です。EC2のSSH接続を切った状態でもアプリケーションにアクセスできればEC2上でアプリケーションが常時起動しているということになるためこれで完了です。
長くなりましたがここまでお付き合い頂きましてありがとうございます! またの機会にWarファイルのデプロイを行なっていきたいと思います。。。
参考:
【SpringBoot】CentOS7でjar(Gradle)を自動起動させるまで - タイガー!タイガー!じれったいぞー!(SE編)
SpringBootで作ったjarをinit.dのサービスとして動かしてみた - Qiita
SpringBoot アプリをサービスとして動かす方法 - Qiita
Deploying Spring Boot Applications
【Linux】systemd:Unit定義ファイル(サービス)の自作とsystemctlによる登録 | OFFICE54