Javaで開発したWebアプリケーションをJenkinsを使って自動デプロイする[準備編]


CI/CDを実践するため、JenkinsでArtifactsを生成してアプリケーションサーバーにデプロイする所まで設定してみました。

この記事では準備編として、Javaで書いたWebアプリケーションプロジェクトをGithubで管理し、Jenkinsと連携してビルドするまでの設定までとなります。

Artifacts

デプロイの方法には、一般的に"warでデプロイ"と"jarでデプロイ"の2通りがあると思いますので、どちらのパターンでも試すことにしました。

すなわち
1. warファイルを出力し、アプリケーションサーバーにデプロイします。
2. jarファイルを出力し、javaコマンドで実行します。
今回は、前者はアプリケーションサーバーにTomcatを利用し、後者はSpring Bootで組み込みTomcatを使って、jarを実行します。

環境

  • Java: OpenJDK 11.0.2
  • Tomcat: 9.0.14
  • Maven: 3.6
  • Spring framework: 5.1.3
  • Thymeleaf: 3.0.11
  • Spring-boot: 2.1.2
  • Jenkins: 2.161
  • VirtualBox: 6.0.2
  • Vagrant: 2.2.3
  • CentOS: 7

Javaプロジェクトの作成

warファイルとjarファイルの2種類のArtifactsを用意するため、SpringFrameworkを利用したJavaのWebアプリケーションプロジェクトを2つ用意しました。

GithubのRepositoryはこちら

パターン1: warファイルを出力するプロジェクト

Spring MVCを利用したWebアプリケーションを作成

ベースはSpringのHPにあるServing Web Content with Spring MVCを元に、Mavenプロジェクトを用意しようとしましたが、SpringBootを利用していてパッケージングもjarでしたので、以前買ったSpring徹底入門と多田さんがJJUG CCCで行なったハンズオンの資料Spring 5 & Spring Boot 2ハンズオン準備手順も参考にさせていただきました。

IntelliJ IDEAの[File > Project Structure > Artifacts]で、warファイルが登録されていることを確認し、無ければ追加しておく。

Maven Wrapperの作成

Maven Wrapperを用意して、JenkinsサーバーにMavenをインストールせずにmvnコマンドを実行できるようにしました。
mvn -N io.takari:maven:wrapper -Dmaven=3.6.0
Maven Wrapper(mvnw)の作り方はこちらを参照しました。

開発マシンのIntelliJ IDEAからTomcatを起動する

開発環境でTomcatを立ち上げてブラウザで確認するまでに手こずったのでメモ

  1. まず開発PCにTomcatをインストールしていなかったので、homebrewでインストールした。brew install tomcat
  2. IntelliJ IDEAの[Run > Edit Configurations > Add New Configuration]から[Tomcat Server > local]を指定して、設定画面を表示する
  3. [Application Server]のPathは/usr/local/Cellar/tomcat/9.0.14/libexecを指定する。
  4. [URL]はhttp://localhost:8080/web-spring-mvc/greetingと入力する。
  5. Deploymentタブの[Deploy at the server startup]でArtifactsとして指定できるwarからwar explodedを追加する
  6. Deploymentタブの[Application Context]で、/web-spring-mvcと指定する。(4のURLのContextPathと合わせる)

IntelliJ IDEAからTomcatを実行すると、http://localhost:8080/web-sprig-mvc/greeting にアクセスして画面が表示される。

パターン2: jarファイルを出力するプロジェクト

Spring Bootアプリケーションで開発しjarで出力したArtifactsをjavaコマンドで実行します。
サンプルプロジェクトはBuilding an Application with Spring Bootを元にMavenプロジェクトを作成します。
こちらのサンプルも出力がRestAPIだったのでhtml(Thymeleaf)を出力するように編集しました。

まずはSpring Initializerを使ってプロジェクトを作成します。

MavenのDependency設定

以下、手こずった点について

  • spring-boot-starter-thymeleafを追加しないと、Thymeleafが利用できないので画面が出力されない。
  • JUnit5を使う場合、spring-boot-starter-testの依存関係からjunitを除く(どうも依存先のjunitから存在しないバージョンを指定しようとしてエラーが出る。)
  • 出力したjarファイルは、JVMサーバーでサービス起動するが、実行可能なjarにするために、spring-boot-maven-pluginのexecutableオプションをtrueにしておくこと。

サーバーの作成

以下の3つの仮想環境をローカルマシン上に構築しました。

  1. Jenkinsサーバー
  2. アプリケーションサーバー(Tomcat)
  3. アプリケーションサーバー(Javaのみ)

構築は、Vagrantで複数の環境を同時に起動し、ansible-playbookを利用して必要なソフトをインストールしています。

Jenkinsサーバーの構築

主なインストールソフトウェア

  • Git
  • OpenJDK 8 JRE
  • OpenJDK 11 JDK
  • Jenkins

JRE 8はJenkins動作用に実行環境として利用し、OpenJDK 11はアプリケーションのビルド用に利用します。

Jenkinsのインストールは、Jenkins Wikiを参照しました。

Tomcatサーバーの構築

主なインストールソフトウェア

  • OpenJDK 11 JDK
  • Tomcat 9

Ansible-playbookの書き方は、ansible本家のサンプルを参考にしました。

firewall設定でつまづいたが、「Tomcatのサービス起動をstartedからrestartedに変更する」と「firewalldのポート指定の前に、firewalldを起動する」ことで、接続できました。

/roles/tomcat/tasks/main.yml(抜粋)
- name: Start Tomcat
  service: name=tomcat state=restarted enabled=yes

- name: running firewall daemon
  service: name=firewalld state=restarted

ローカルPCからTomcatのmanager画面(/manager/html)にアクセスすると「403 access denied」と表示されてしまうのでエラー画面やこちらを見たところ、manager画面へのアクセスはTomcatをインストールしたマシンからのみできるように初期値が設定されいているとのことだったので、 /webapps/manager/META-INF/context.xml を編集して、仮想環境のネットワーク内(192.168.33.0/24)からアクセスできるように変更しました。

また、tomcatマネージャーをコマンドから実行できるようにするため、tomcat-users.xmlには、実行ユーザーにmanager-sciprtロールを付与しておきます。参考

JVMサーバーの構築

SpringBootアプリケーションを実行するための設定を行います。

  1. OpenJDK 11をインストールして、javaコマンドが実行できるようにします
  2. アプリケーションを実行するjvmユーザーを作成します。
  3. SpringBootアプリケーションを実行するサービスを作成します。

尚、作成時点ではjarファイルはまだ存在していないので、サービスを起動してもステータスはFailになります。

Vagrantの起動

vagrant upを実行すると、3つの環境が順番に起動し、Ansibleを実行して必要なソフトや設定がインストールされます。
インストールが完了した後、仮想環境にvagrant ssh [ホスト名]でアクセスできます。

Jenkins

http://192.168.33.10:8080/ にアクセスするとJenkinsにログインできます。

後は、画面上から初期セットアップを実施し、インストールしたJDKとGitのパスを設定します。

Tomcat

http://192.168.33.30:8080/ にアクセスするとTomcatコンソールにアクセスできます。

サンプル含まれるroles/tomcat/vars/main.ymlのユーザーを使うと、管理画面やScriptが利用できます。

JVM

vagrant ssh jvmでアクセスしてjava -versionを実行するとopenjdk versionが表示されることを確認します。

また、# systemctl status appを実行するとSpringBootアプリケーションサービスの実行ステータスが確認できますが、初回はjarファイルが無いのでエラーとなります。(/var/log/messageを見るとわかります。)

JVMサーバーにSSHでアクセスするための設定

JenkinsサーバーからJVMサーバーにjarファイルを転送してアプリケーションのサービスを実行するためにSSH接続を利用します。
今回のサンプルではvagrantで複数環境を同時に起動した都合から、あらかじめ公開鍵を作らなかったので以下のようにコマンドで鍵の受け渡しを行いました。
(実際は公開鍵をあらかじめ用意しておいて、ansibleのauthorized_keyモジュールで登録すると良いと思います。)

JVMサーバーで一時的にパスワード認証でアクセスを有効にする
1. # vi /etc/ssh/sshd_config を開き、"PasswordAuthentication"をyesに切り替える
2. # systemctl restart sshdでsshを再起動する。
3. # passwd jvmでjvmユーザーのパスワードを登録する

JenkinsサーバーのJenkinsユーザーでSSH鍵を作りJVMサーバーに公開鍵を送る
$ sudo su -s /bin/bash jenkinsを実行するとJenkinsユーザーでコマンド実行できるようになるので、SSH鍵を作成するコマンドを実行する

1. jenkinsユーザーのホームディレクトリ[/var/lib/jenkins]に移動
$ cd ~

2. ssh鍵を作る 
$ ssh-keygen -b 2048 -t rsa -f /var/lib/jenkins/.ssh/id_rsa -N ""

3. 公開鍵をJVMサーバーに送る(送るときにjvmユーザーのパスワードを聞かれる)
$ ssh-copy-id -i .ssh/id_rsa.pub [email protected]

4. 接続テスト
$ ssh -i .ssh/id_rsa [email protected]

5. 終了
$ exit

sshアクセスできたらJVMサーバーのパスワード認証を無効に戻すことを忘れないように
ちなみにJVMサーバーに作成したjvmユーザーのホームディレクトリは、/var/jvmにしているので、SSHの接続情報は/var/jvm/.sshに格納されます。

Jenkinsでビルドとデプロイを実行する準備が完了しました。

Githubで管理するMavenプロジェクト(Java + Spring)Jenkinsサーバーデプロイ環境が準備できました!
この後のビルドとデプロイを実行する設定方法はTomcatアプリケーションSpringBootアプリケーションに分けてまとめましたのでこちらを参照してください。