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を立ち上げてブラウザで確認するまでに手こずったのでメモ
- まず開発PCにTomcatをインストールしていなかったので、homebrewでインストールした。
brew install tomcat
- IntelliJ IDEAの[Run > Edit Configurations > Add New Configuration]から[Tomcat Server > local]を指定して、設定画面を表示する
- [Application Server]のPathは
/usr/local/Cellar/tomcat/9.0.14/libexec
を指定する。 - [URL]は
http://localhost:8080/web-spring-mvc/greeting
と入力する。 - Deploymentタブの[Deploy at the server startup]でArtifactsとして指定できるwarから
war exploded
を追加する - 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つの仮想環境をローカルマシン上に構築しました。
- Jenkinsサーバー
- アプリケーションサーバー(Tomcat)
- アプリケーションサーバー(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を起動する」ことで、接続できました。
- 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アプリケーションを実行するための設定を行います。
- OpenJDK 11をインストールして、javaコマンドが実行できるようにします
- アプリケーションを実行するjvmユーザーを作成します。
- 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サーバーで一時的にパスワード認証でアクセスを有効にする
-
# vi /etc/ssh/sshd_config
を開き、"PasswordAuthentication"をyesに切り替える -
# systemctl restart sshd
でsshを再起動する。 -
# 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 jvm@192.168.33.20
4. 接続テスト
$ ssh -i .ssh/id_rsa jvm@192.168.33.20
5. 終了
$ exit
sshアクセスできたらJVMサーバーのパスワード認証を無効に戻すことを忘れないように
ちなみにJVMサーバーに作成したjvmユーザーのホームディレクトリは、/var/jvm
にしているので、SSHの接続情報は/var/jvm/.ssh
に格納されます。
Jenkinsでビルドとデプロイを実行する準備が完了しました。
Githubで管理するMavenプロジェクト(Java + Spring)とJenkinsサーバーとデプロイ環境が準備できました!
この後のビルドとデプロイを実行する設定方法はTomcatアプリケーションとSpringBootアプリケーションに分けてまとめましたのでこちらを参照してください。