Linux 環境でのSpring Bootプロジェクトってどう動かすのが良いのか

f:id:ts0818:20191104222330p:plain

Because I am hard you will not like me.
But the more you hate me the more you will learn.
I am hard but I am fair.
There is no racial bigotry here.
I do not look down on n*ggers, kikes, wops or greasers.
Here you are all equally worthless.
And my orders are to weed out all non-hackers who do not pack the gear to serve in my beloved Corps.
Do you maggots understand that?

貴様らは厳しい俺を嫌う
だが憎めば、それだけ学ぶ
俺は厳しいが公平だ
人種差別は許さん
黒豚、ユダ豚、イタ豚を、俺は見下さん
すべて平等に価値がない!
俺の使命は役立たずを刈り取ることだ 愛する海兵隊の害虫を!
分かったか、ウジ虫ども!

http://www.visual-memory.co.uk/amk/doc/0065.html

⇧  ご存知、「フルメタルジャケット(監督:スタンリー・キューブリック)」のハートマン軍曹のセリフですね。
Boot Camp」の恐ろしいイメージに、トラウマになりそうですかね。

ブートキャンプアメリカ英語Boot Camp)とは、アメリカ合衆国で「新兵訓練施設」を意味する口語表現である。転じてアメリカ軍の新兵に対して行われる教育・訓練プログラム(新兵訓練)自体を指すようになり、さらには軍隊式トレーニング全般を意味するようになった。

ブート(Boot)とは俗語で新兵を意味する。

ブートキャンプ - Wikipedia

Wikipediaの写真も、むっちゃ怖い...

そんなこんなで、今回は、Javaフレームワークである「Spring Framework」の機能を利用しているという「Spring Boot」について。

まぁ、「Spring Boot」は、

ブートboot)またはブートストラップbootstrap)は、コンピュータシステムの電源投入時、あるいはシステムのリセット後、モニタOSなどなんらかの基本的なシステムソフトウェア主記憶に展開し、ユーザプログラムを実行できるようにするまでの処理の流れをいう。ブートローダboot loader)は、以上のプロセスで使われるローダ、すなわち不揮発性補助記憶にある目的のプログラムを読み出し、揮発性の主記憶に書き込むプログラムのことである。

ブート - Wikipedia

⇧  こっちのイメージでしょうけど。

そんでは、レッツトライ~。 

 

Spring Bootって?

ネットとかの情報だと、「Spring MVC vs Spring Boot」って比較が多いから、てっきり、Spring Frameworkの一部かと思ってたんだけど、

spring.io

⇧  プロジェクトが分かれてるところを見ると、まったく別物って考えないといけないってことですかね。

っていうか、「Spring Framework」「Spring MVC」「Spring Boot」の関係性が分かりにくいんだが...

 

『はじめての Spring Boot』の著者である槙俊明さんのスライドによりますと、

⇧ 「Spring Boot」は、あくまで、「Spring Framework」の外側にいて、「Spring MVC」は「Spring Framework」の機能の「Web」の一部ってことなので、「Spring Framework」に含まれるっていう扱いになりそうですかね。  

 

情報が旧いんですが、 

⇧ https://spring.io/ にあるドキュメントでも、「Spring MVC」は「Spring Framework」の機能の一部である「Web」に属するってことでしょうかね。

「Spring Boot」は、

Spring Boot is designed to get you up and running as quickly as possible, with minimal upfront configuration of Spring. Spring Boot takes an opinionated view of building production-ready applications.

https://spring.io/

⇧ っていうか、この説明で分かれっていうのは、疑問なんだが...

槙さんのスライドの画像から推測するに、「Spring Boot」は、「Spring Framework」にある様々な機能の内、使いたいものを自分で選んで、プロジェクトを作成できるっていうことではないかと。

 

Spring Boot は、Tomcatが内蔵されてるらしい

Eclipseなんかで、Java の標準APIだけ使っていると、「Java アプリケーション」として実行しかできないんですが、今時のアプリケーションといえばWebアプリケーションが主流ではないかと思われるわけです。

Webとしての機能が欲しい場合、Javaの場合だと、だいたいServlet APIを導入して実現することが多いかと。

ash.jp

Servlet APIは、ApacheなどのWebサーバでサーブレットを動作させるために必要なモジュールです。 Servlet APIは、Javaエクステンションで、javaxの標準パッケージとして提供されます。

サーブレットについて

サーブレットコンテナとは、Servlet APIモジュールとともに、ApacheなどのWebサーバをサーブレットを動作させるために拡張するためのモジュールで、サーブレットエンジンとも呼びます。

サーブレットについて

⇧  上記サイト様が詳しいです。

Tomcat」+「サーブレット」== 「サーブレットコンテナ(サーブレットエンジン)」があれば、実は、Webサーバは無くても大丈夫だったりします。なぜなら、AppサーバであるTomcatは簡易的なWebサーバの機能も持ち合わせているため。

ですが、Appサーバだけに処理を集中させると、パフォーマンスが悪くなるため、通常は、「Webサーバ」、「Appサーバ」の両方を使っていく感じになるかと。

 

Javaだと、

  • javax.servlet-api JAR
    • Tomcat なんかの Appサーバ(Application Server) には、javax.servlet-api JARが同梱されてるらしい

があれば、Web通信を実現できるかと。

普通のJavaのWebアプリケーションであれば、「Appサーバ」に、「JAR」や「WAR」などの形式にまとめたファイルを配置することでデプロイされるかと思われますが。

 

だいぶ、脱線しましたが、Spring Boot のややこしいところは、Tomcat を内蔵しているということかと。そのため、デプロイする手段として「Appサーバ」に配置って方法は適さないかと。(「Appサーバ」込みのアプリケーションを「Appサーバ」に載せるってことになってしまうから)

 

サービスとして起動するのが主流らしいと思ったけど...

なんか、Spring Boot をLinuxで動かす場合、サービスとして起動するのが一般的らしい。と、

teratail.com

⇧  上記サイト様の参考サイトの方はGradleでSpring Boot プロジェクトを作成してる感じですかね。

ちょっと、

www.codeflow.site

⇧  トリッキーなことをしている人もおられるけど。

 

最近だと(最近でもないのかもしれないですが)、

creasys.org

www.geeeek.site

www.greptips.com

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

Spring BootのFully Executable Jarをサーバにリリースする方法 - grep Tips *

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

Spring BootのFully Executable Jarをサーバにリリースする方法 - grep Tips *

⇧  「Fully Executable Jar」という形のJARファイルを作れば、サービス起動のためのスクリプトも用意してくれるらしい。 

だが、しかし!

qiita.com

Spring BootのTomcatのバージョンを調べてみるの冒頭でも述べた通り、Spring BootではTomcatが組み込まれており、非常に手軽に開発ができます。
ただ、開発中はSTS + Spring Bootで組み込みTomcatを使って、本番環境や結合テスト環境には別でTomcat(とかJettyとかJBossとか)を立てて、そっちで稼働させたい、なんてケースもあるかと思います。
現に自分の今の現場はそんな構成になっています。

Spring BootでWARを作成して別のTomcatにデプロイする - Qiita

⇧  上記サイト様が仰るように、現場では、「Appサーバ」にデプロイするケースが多いんですかね。

いまいち、Spring Boot の方向性が分からんよね...

 

CentOS7ベースの仮想マシンに、AdoptOpenJDK 11 を導入で

とりあえず、デプロイ先として、Linux環境を用意するため、仮想マシンを作成して、CentOS7をインストールして、AdoptOpenJDK 11 をインストールします。

qiita.com

Docker ToolBoxを利用して、CentOS7のDockerコンテナを用意し、そのコンテナにAdoptOpenJDK 11  をインストールするということで。

 

2019年11月5日(火)追記:↓ ここから

OpenJDKは、特にこだわりが無ければ、CentOS7なら、yum でインストールしたほうが早いです。

www.server-world.info

2019年11月5日(火)追記:↑ ここまで

 

ここからは、Dockerを使える環境が整っているという前提で話を進めていきます。

 

まずは、docker-machine createで作成した仮想マシンの一覧を表示。

docker-machine ls    

f:id:ts0818:20191103174537p:plain

そしたらば、新たに仮想マシンを作成します。 

docker-machine create --driver [仮想化ソリューション] [仮想マシン名] 

f:id:ts0818:20191103174924p:plain

f:id:ts0818:20191103175144p:plain

⇧  指示通りに、コマンドを実行します。

f:id:ts0818:20191103175236p:plain

f:id:ts0818:20191103175333p:plain

⇧  作成した仮想マシンが起動してます。

そんでは、Docker Hubから、CentOS7のコンテナimage を取得します。(Docker Hubのアカウント登録をしてない場合は、登録しちゃいましょう)

f:id:ts0818:20191103181514p:plain

そしたらば、コンテナを作成・起動していきますが、ホスト側とゲスト側のディレクトリをマウントしていこうと思うので、「C:\Users\[ユーザ名]」の配下に適当にディレクトリを作成で。

f:id:ts0818:20191103182827p:plain

注意する点としましては、

teratail.com

すでに作ったコンテナでホスト側のディレクトリをマウントする方法はあるでしょうか?

できません。

他の run のオプションも含めて、コンテナ化した後は変更できません。

一度、コンテナの内容を docker commit で、イメージに書き戻して run しなおすことで run のオプションを変更できます。

Docker - すでに作ったコンテナでホスト側ディレクトリをマウントするには?|teratail

⇧  とありますように、docker run の段階で、いろいろ決めておかないといけないってことですかね。

というのも、docker run コマンドは、

docs.docker.jp

docker run コマンドは、まず指定されたイメージ上に書き込み可能なコンテナ・レイヤを create (作成)します。それから、指定されたコマンドを使って start (開始)します。

この docker run は、 API の /containers/create の後で /containers/(id)/start を実行するのと同じです。以前に使っていたコンテナは docker start で再起動できます。

全てのコンテナを表示するには docker ps -a を使います。

docker run コマンドは、 コンテナの内容を確定するために、 docker commit コマンドと連携して使えます。

run — Docker-docs-ja 17.06.Beta ドキュメント

⇧  「create」+「start」ってことをやっているので、「create」の段階でコンテナの構成が決まってしまうと思われるので、後から、マウントしたり、ポートフォワーディングしたりってことは変更できないってことですかね。

 

そんじゃ、コンテナ作成・起動で。

docker run -it --name [コンテナ名] -d -v //c/Users/[ユーザ名]/[マウントしたいディレクトリまでのパス]:/[ゲスト側のディレクトリ] [REPOSITORY]:[TAG]

f:id:ts0818:20191103184205p:plain

コンテナにログインして、マウントしたディレクトリにファイルを作成してみます。

f:id:ts0818:20191103184814p:plain

ホスト側のディレクトリからゲストOSのマウントしたディレクトリが見えましたんで、ホスト側とゲスト側のディレクトリがマウントできてます。

f:id:ts0818:20191103184927p:plain

2019年12月5日(木)追記 ↓  ここから

2019年11月5日の追記で言及したように、特にどこのOpen JDKにするかにこだわりが無ければ、今回で言えば、CentOS7のパッケージ管理ツールのyumでインストールが早いで、yum install で、Open JDKをインストールする場合は、下記の"AdoptOpenJDKをインストール"の手順は不要です。

2019年12月5日(木)追記 ↑  ここまで

そんでは、AdoptOpenJDKをインストールしていきたいと思います。

まずは、git をインストールで。

f:id:ts0818:20191103185425p:plain

f:id:ts0818:20191103185557p:plain

そしたらば、git cloneします。

f:id:ts0818:20191103185648p:plain

f:id:ts0818:20191103185738p:plain

依存ライブラリが必要らしいので、インストールしておきます。

 

yum -y install bzip2
yum -y install autoconf
yum -y install unzip
yum -y install zip
yum -y install java-11-openjdk-devel
yum -y groupinstall "Development Tools"
yum -y install libXtst-devel libXt-devel libXrender-devel libXi-devel
yum -y install cups-devel
yum -y install fontconfig-devel
yum -y install alsa-lib-devel

f:id:ts0818:20191103193706p:plain

f:id:ts0818:20191103193748p:plain

f:id:ts0818:20191103193832p:plain

f:id:ts0818:20191103193912p:plain

f:id:ts0818:20191103193941p:plain

f:id:ts0818:20191103194533p:plain

f:id:ts0818:20191103194200p:plain

f:id:ts0818:20191103194247p:plain

f:id:ts0818:20191103194329p:plain

f:id:ts0818:20191103194715p:plain

f:id:ts0818:20191103194743p:plain

で、ビルドしようとしたら、

f:id:ts0818:20191103195209p:plain

エラー。which コマンド入ってないらしい。

f:id:ts0818:20191103195239p:plain

インストールで。

f:id:ts0818:20191103195628p:plain

f:id:ts0818:20191103195541p:plain

改めて、

f:id:ts0818:20191103195718p:plain

で、エラー。

f:id:ts0818:20191103201637p:plain

依存ライブラリ足りなかったらしい...

インストールで。

f:id:ts0818:20191103201817p:plain

f:id:ts0818:20191103201839p:plain

f:id:ts0818:20191103201903p:plain

もう一回やり直しで。

f:id:ts0818:20191103201957p:plain

で、むっちゃ時間かかる...ここから異様に進まない

f:id:ts0818:20191103213122p:plain

あ...少し進んだ、でも、こっからまためちゃ時間かかるし...

f:id:ts0818:20191103220112p:plain

めっちゃ時間かかって、何かエラーっぽいメッセージが

f:id:ts0818:20191103231025p:plain

f:id:ts0818:20191103232019p:plain

f:id:ts0818:20191103230849p:plain

展開します。

f:id:ts0818:20191103232625p:plain

インストールできたらしい。

f:id:ts0818:20191103232923p:plain

f:id:ts0818:20191103233028p:plain

時間の都合上、翌日に持ち越しということで、仮想マシンを停止。

f:id:ts0818:20191103233458p:plain

 

ホスト側でSpring Bootのプロジェクトを作成

翌日。仮想マシンを起動で。

f:id:ts0818:20191104100307p:plain

指示通り、コマンドを実行していきましょう。

f:id:ts0818:20191104100413p:plain

コンテナを起動しておきましょう。

docker start [コンテナ名]    

f:id:ts0818:20191104102933p:plain

コンテナが起動しました。ゲスト側は、一旦、ここまで。
 

そんじゃ、ホスト側でSpring Boot プロジェクトを作成で。

Eclipse(バージョンは2019-09使ってます)を起動して、 「ファイル(F)」>「新規(N)」>「その他(O)...」で。

f:id:ts0818:20191104103245p:plain

「Spring Boot」>「Spring スターター・プロジェクト」を選択で。「次へ(N)>」。

f:id:ts0818:20191104103423p:plain

Java バージョン:」を「11」にしときます。あとは、「名前」だけ変えてみました。「次へ(N) >」で。

f:id:ts0818:20191104103659p:plain

依存ライブラリは、一旦、「Spring Web」だけ追加しました。後から追加とかもできるはずなので。「完了(F)」で。

f:id:ts0818:20191104104909p:plain

しばし時間がかかりますが、「Spring Boot」なプロジェクトできました。

f:id:ts0818:20191104105305p:plain

もし、pom.xml でエラーが出た場合、

dzone.com

⇧  自分は上記サイト様の方法で解決できました。(会社のパソコンで、Windows 10 Proで、Hyper-vを有効にしてた時に、ネットワークが不安定になって「C:\Users\[ユーザ名]\.m2\repository」に上手く依存ライブラリがダウンロードできてなかったのかと思われる時に、上記サイト様の方法で解消できました。)

 

そんじゃ、ブラウザで「Hello Spring World」って表示できるようにしていきましょう。

ちなみに、依存関係で「Spring Web」を選択したので、おそらく、「Spring Framework」の「Spring MVC」が使えるってことだと思われるので、

stackoverflow.com

⇧  上記のような、仕組みが利用できるかと。

なので、今回は、「Hello Spring World」って表示するだけなので、

  • Controller用のJavaファイル
  • View用のhtmlファイル

の2つを用意してあげればOKということになるかと。

ビジネスロジックは使わないので、「Business Service Facade」でデータベースなんかとやり取りするってことも、今回は行いません。

ちなみに、プロジェクトを作成した段階で存在する、「[プロジェクト名]Application.java」にmainメソッドが存在するので、プロジェクトのエントリポイントになるかと。

org.springframework.boot

Class SpringApplication

Class that can be used to bootstrap and launch a Spring application from a Java main method. By default class will perform the following steps to bootstrap your application:

SpringApplication (Spring Boot Docs 2.2.0.RELEASE API)

⇧ Spring Bootも、mainメソッドを起点としているんですかね。

 

そんでは、まずは、Javaのクラスファイルを作成していきます。

f:id:ts0818:20191104114439p:plain

「パッケージ(K):」「名前(M):」を適当に決めて、「完了(F)」で。

f:id:ts0818:20191104122049p:plain

f:id:ts0818:20191104122125p:plain

そしたらば、「HelloController.java」のソースコードを編集で。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController {

 @RequestMapping(value="/hello")
 private String hello() {
  return "/index.html";
 }
}    

そしたらば、今度は、htmlファイルを作成します。「src/main/resource」配下の「static」ディレクトリに、ファイルを作成します。

f:id:ts0818:20191104122717p:plain

f:id:ts0818:20191104122910p:plain

 

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>Spring Boot デビュー</title>
  </head>
  <body>
    <p>Hello Spring Boot</p>
  </body>
</html>

そんでは、「Spring Boot」なプロジェクトを起動してみます。

f:id:ts0818:20191104123638p:plain

f:id:ts0818:20191104123714p:plain

コンソールに、

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.0.RELEASE)

2019-11-04 12:36:54.477  INFO 28704 --- [           main] com.example.demo.HelloSpringApplication  : Starting HelloSpringApplication on Toshinobu-PC with PID 28704 (C:\Eclipse_2019-09\pleiades-2019-09-java-win-64bit-jre_20191007\pleiades\workspace\work_00\HelloSpring\target\classes started by Toshinobu in C:\Eclipse_2019-09\pleiades-2019-09-java-win-64bit-jre_20191007\pleiades\workspace\work_00\HelloSpring)
2019-11-04 12:36:54.484  INFO 28704 --- [           main] com.example.demo.HelloSpringApplication  : No active profile set, falling back to default profiles: default
2019-11-04 12:36:55.302  INFO 28704 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-11-04 12:36:55.313  INFO 28704 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-11-04 12:36:55.313  INFO 28704 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.27]
2019-11-04 12:36:55.398  INFO 28704 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-11-04 12:36:55.398  INFO 28704 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 870 ms
2019-11-04 12:36:55.545  INFO 28704 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-11-04 12:36:55.592  INFO 28704 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page: class path resource [static/index.html]
2019-11-04 12:36:55.692  INFO 28704 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-11-04 12:36:55.696  INFO 28704 --- [           main] com.example.demo.HelloSpringApplication  : Started HelloSpringApplication in 1.56 seconds (JVM running for 3.25)

ってなったら、起動できているので、ブラウザから、「http://localhost:8080/hello」にアクセスすると、

f:id:ts0818:20191104124035p:plain

⇧ index.htmlが表示できています。

停止するには、コンソールの横にある f:id:ts0818:20191104124314p:plain をクリックすればOK。

f:id:ts0818:20191104124209p:plain

停止すると、ブラウザからのアクセスもできなくなります。

f:id:ts0818:20191104124422p:plain

 

Spring Boot なプロジェクトをWARファイルにする

Linux環境へのSpring Bootなプロジェクトのデプロイ方法としては、 

  1. JARファイルをサービスとして起動させる
  2. AppサーバにWARファイルを配置する

の2つが考えられそうですが(他にもいろいろ方法はあると思われますが、ネットで調べた感じでは、上記の2つの方法が多かったので)、今回は、「2. AppサーバにWARファイルを配置する」方法でデプロイしたいと思います。

 

qiita.com

⇧  上記サイト様を参考にさせていただきました。

 

Linux環境に、「Appサーバ」を用意して、「Appサーバ」に「Spring Boot なプロジェクト」をデプロイするために、「WAR」ファイルを用意します。

ホスト側でやることとしては、 

  • pom.xmlの修正
    • packagingをwarにすること
    • spring-boot-starter-tomcatのscopeをprovidedにすること
  • エントリポイントのクラスファイルを用意
    • SpringBootServletInitializerクラスを継承すること
    • configureメソッドをoverrideすること

の2つらしいです。

では、まず、「pom.xml」の修正。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>HelloSpring</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>HelloSpring</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

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

</project>

続きまして、エントリポイントクラスを作成。

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class App  extends SpringBootServletInitializer {

 public static void main(String[] args) {
  // TODO 自動生成されたメソッド・スタブ
  SpringApplication.run(App.class, args);
 }

 @Override
 protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
  return application.sources(App.class);
 }

}

んで、実行しようとすると、

f:id:ts0818:20191104151802p:plain

mainメソッドが2つあることになっちゃうんで、どっちのクラスで実行するかを選ぶことになるので、新しく作った「App.java」を選択して「OK」で。

f:id:ts0818:20191104151846p:plain

 

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.0.RELEASE)

2019-11-04 15:20:34.567  INFO 31304 --- [           main] com.example.demo.App                     : Starting App on Toshinobu-PC with PID 31304 (C:\Eclipse_2019-09\pleiades-2019-09-java-win-64bit-jre_20191007\pleiades\workspace\work_00\HelloSpring\target\classes started by Toshinobu in C:\Eclipse_2019-09\pleiades-2019-09-java-win-64bit-jre_20191007\pleiades\workspace\work_00\HelloSpring)
2019-11-04 15:20:34.572  INFO 31304 --- [           main] com.example.demo.App                     : No active profile set, falling back to default profiles: default
2019-11-04 15:20:35.239  INFO 31304 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-11-04 15:20:35.247  INFO 31304 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-11-04 15:20:35.248  INFO 31304 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.27]
2019-11-04 15:20:35.323  INFO 31304 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-11-04 15:20:35.323  INFO 31304 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 715 ms
2019-11-04 15:20:35.455  INFO 31304 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-11-04 15:20:35.499  INFO 31304 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page: class path resource [static/index.html]
2019-11-04 15:20:35.583  INFO 31304 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-11-04 15:20:35.587  INFO 31304 --- [           main] com.example.demo.App                     : Started App in 1.269 seconds (JVM running for 2.592)

App.javaのほうのmainメソッドの中のSpring Boot用の処理で、Spring Bootなプロジェクトが起動。

んで、ブラウザから「http://localhost:8080/hello」でアクセスすれば、 

f:id:ts0818:20191104152426p:plain

⇧ index.html が表示されました。

動作することが確認できたので、プロジェクトを停止します。

f:id:ts0818:20191104152648p:plain

そんでは、WARファイルを作成で。

f:id:ts0818:20191104152821p:plain

「ゴール(G):」に「package」と入力し、「実行(R)」で。

f:id:ts0818:20191104152940p:plain

で、エラー。

[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.315 s <<< FAILURE! - in com.example.demo.HelloSpringApplicationTests
[ERROR] com.example.demo.HelloSpringApplicationTests  Time elapsed: 0.313 s  <<< ERROR!
java.lang.IllegalStateException: Found multiple @SpringBootConfiguration annotated classes [Generic bean: class [com.example.demo.App]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [C:\Eclipse_2019-09\pleiades-2019-09-java-win-64bit-jre_20191007\pleiades\workspace\work_00\HelloSpring\target\classes\com\example\demo\App.class], Generic bean: class [com.example.demo.HelloSpringApplication]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [C:\Eclipse_2019-09\pleiades-2019-09-java-win-64bit-jre_20191007\pleiades\workspace\work_00\HelloSpring\target\classes\com\example\demo\HelloSpringApplication.class]]

[INFO] 
[INFO] Results:
[INFO] 
[ERROR] Errors: 
[ERROR]   HelloSpringApplicationTests » IllegalState Found multiple @SpringBootConfigura...
[INFO] 
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  13.960 s
[INFO] Finished at: 2019-11-04T15:30:43+09:00
[INFO] ------------------------------------------------------------------------
[WARNING] The requested profile "pom.xml" could not be activated because it does not exist.
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.2:test (default-test) on project HelloSpring: There are test failures.
[ERROR] 
[ERROR] Please refer to C:\Eclipse_2019-09\pleiades-2019-09-java-win-64bit-jre_20191007\pleiades\workspace\work_00\HelloSpring\target\surefire-reports for the individual test results.
[ERROR] Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream.
[ERROR] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

なんでも、

programmer.help

Avoid @SpringBootConfiguration conflicts

When @SpringBootTest is undefined (classes=... and nested @Configuration class is not found), an attempt is made to query @SpringBootConfiguration, which throws an exception if more than one is found:

Spring, Spring Boot and TestNG Test Guide - Using Spring Boot Testing Tools

⇧  デフォルトで作成されてた「/HelloSpring/src/test/java/com/example/demo/HelloSpringApplicationTests.java」が見つけられんっていってるらしい...

一応、解決方法としては、 

The solution to this problem is to avoid automatically querying @SpringBootConfiguration:

  1. Define @SpringBootTest(classes=...)

  2. Provide nested@Configuration class

Spring, Spring Boot and TestNG Test Guide - Using Spring Boot Testing Tools

⇧  って、なってるけど、分かりにくいな...どっちか片方を実施すれば良いのか、それともどっちもやらなきゃいけないのか...

 

codeday.me

@SpringBootTestと一緒に使用するSpring Boot Mainクラスを指定する必要があります。

@SpringBootTest(classes = YourUiSpringBootApp.class)

@SpringBootApplicationを使用した2つのSpring Bootプロジェクト - コードログ

⇧  上記サイト様によりますと、 「1.Define @SpringBootTest(classes=...)」だけでイケそうかしら。

デフォルトのテストクラスを修正。 

package com.example.demo;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(classes=HelloSpringApplication.class)
class HelloSpringApplicationTests {

 @Test
 void contextLoads() {
 }
}

んで、再度WARファイル作成で。

エラー。

[WARNING] The requested profile "pom.xml" could not be activated because it does not exist.
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.2.0.RELEASE:repackage (repackage) on project HelloSpring: Execution repackage of goal org.springframework.boot:spring-boot-maven-plugin:2.2.0.RELEASE:repackage failed: Unable to find a single main class from the following candidates [com.example.demo.App, com.example.demo.HelloSpringApplication] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException

複数の「@SpringBootApplication」アノテーションがあると起こる問題らしい。

ただ、マイクロサービスとかでは、結構、「@SpringBootApplication」アノテーションを複数にする要件が出てきそうな感じなんですかね。

複数あるままいきたい場合は、

sujoyu.hatenadiary.com

⇧  上記サイト様のように設定する必要があるらしい。

 

今回は、「HelloSpringApplication.java」側の「@SpringBootApplication」をコメントアウトしました、Spring よく分からんな...。

f:id:ts0818:20191104175618p:plain

んで、再度WARを作成すると、できました。

「C:\Eclipse_2019-09\pleiades-2019-09-java-win-64bit-jre_20191007\pleiades\workspace\work_00\HelloSpring\target」にできてました。(Eclipseの「パッケージエクスプローラー」からだと確認できないけど...)

f:id:ts0818:20191104180107p:plain

とりあえず、ゲスト側とマウントしていたホスト側のディレクトリに配置しときます。

f:id:ts0818:20191104180319p:plain

 

仮想マシン側にTomcatをインストール

CentOS7のコンテナにTomcatをインストールしていきます。

www.server-world.info

⇧  上記サイト様を参考にさせていただきました。

まずは、コンテナにログインし、Tomcatをダウンロード。

f:id:ts0818:20191104181057p:plain

展開します。

f:id:ts0818:20191104181215p:plain

そしたら、ディレクトリをリネームします。

f:id:ts0818:20191104181408p:plain

Tomcatを起動するユーザを追加で。

f:id:ts0818:20191104181513p:plain

Tomcatを起動する権限を付与。

f:id:ts0818:20191104181543p:plain

Systemd 設定ファイルを作成します。

f:id:ts0818:20191104181701p:plain

以下内容で保存。

f:id:ts0818:20191104182019p:plain

で、サービス起動しようとするも、

f:id:ts0818:20191104182407p:plain

エラー。

すっかり忘れてたけど、

Systemd is now included in both the centos:7 and centos:latest base containers, but it is not active by default. In order to use systemd, you will need to include text similar to the example Dockerfile below:

FROM centos:7
ENV container docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]

Docker Hub - centos

⇧  systemctl  とか、システム系のコマンドを使えるようにしないといけなかったという...手遅れやん。

致し方ない、いま作業してるコンテナからDocker imageを作成して、上記のようにDockerfile でコンテナを作り直しますか。

コンテナからログアウトし、コンテナを停止します。

f:id:ts0818:20191104184429p:plain

コンテナからDocker image を作成。

docker commit [コンテナ名] [REPOSITORY]:[TAG]

f:id:ts0818:20191104190055p:plain

imageのサイズがやべぇっす...

そんじゃ、Dockerfileを適当な場所に作成して、

f:id:ts0818:20191104193045p:plain

ファイルを編集。「FROM」には、いま作成したDocker imageを指定で。 

FROM centos-spring:reborn
ENV container docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]

f:id:ts0818:20191104193433p:plain

で、このDockerfileを使って、最終的なDocker imageを作成します。

Dockerfileまでのパスは、普通のWindowsのパスで良いらしい

f:id:ts0818:20191104201436p:plain

Docker imageができました。

f:id:ts0818:20191104201534p:plain

そんでは、作成したDocker imageで、コンテナを作成・起動で。

コンテナを起動する際も条件があったみたい。

In order to run a container with systemd, you will need to mount the cgroups volumes from the host. Below is an example command that will run the systemd enabled httpd container created earlier.

$ docker run -ti -v /sys/fs/cgroup:/sys/fs/cgroup:ro -p 80:80 local/c7-systemd-httpd

Docker Hub - centos

マウント先が限定されると、って、これ、Windowsだと不可能でした。そもそも、Windowsに「sys/fs/cgroup」なんてディレクトリないしね。 

Windowsの場合は、--privilege オプションで、docker run させるしかなさそう。(セキュリティ系に宜しくないらしいけど、他に方法なさそうですしね)

qiita.com

f:id:ts0818:20191104213105p:plain

はい、エラー。

web.plus-idea.net

⇧  上記サイト様を参考に、仮想マシンディレクトリを作成する。

f:id:ts0818:20191104213724p:plain

で、再度、コンテナを作成してみるが、

f:id:ts0818:20191104214042p:plain

同じエラー。やってられん...

 

qiita.com

どうやら、特権付与(privileged)をtrueにしてしまうと初回起動はするが、その後コンテナを更新したときにマウントエラーが発生し、以降コンテナが立ち上がらなくなるみたいだ・・・
そして、systemctlを利用するには、特権付与(privileged)をtrueにしないといけない。

ECS上にamazonlinux2環境を構築するときの落とし穴 - Qiita

⇧ 終わった...systemctl 使わないでいくしかないと...。

っていうか、systemctl って非推奨なの?どうするのがベストプラクティスなのかね?

今回は、systemctl でいかざるを得ないので(というかそれ以外の方法が分からん)、致し方ないけど、仮想マシン再起動で。

f:id:ts0818:20191104215628p:plain

f:id:ts0818:20191104215707p:plain

起動ログインして、systemctl 動くことを確認!

f:id:ts0818:20191104220008p:plain

そんでは、Tomcatをサービスとして起動します。

f:id:ts0818:20191104220139p:plain

そしたらば、Tomcatにデプロイします。

f:id:ts0818:20191104220340p:plain

ホストのブラウザから、コンテナのTomcatにアクセスできました。

f:id:ts0818:20191104220503p:plain

「http://[ホスト名:ポート番号]/[warファイル名]/[Spring Bootのコントローラーで指定したマッピング先]」

Spring Boot のWARもデプロイできてます!

f:id:ts0818:20191104221050p:plain

むちゃくちゃ、ハマったけど、なんとか、Linux環境にSpring Boot なプロジェクトをデプロイできましたね。

とりあえず、本日はここまでということで、仮想マシンを停止。

f:id:ts0818:20191104224503p:plain

 

結局、Spring Boot なプロジェクトは、Linux環境でどうデプロイするべきなのか分からずですね...モヤモヤ感しか残らない...

こうして、自分のような知識のない人間が奮闘して分からないまま試行錯誤した結果、アンチパターンが量産されていくんでしょうね...

『「持てる者はさらに与えられ、持たざる者はさらに奪われるであろう」(マタイ伝)』って言葉には、「熱意」を重視ってあるけど、どうなんでしょうね...

というか、休日が潰れてしまったではないか(涙)
今回はこのへんで。