Spring Boot でマイクロサービスを作る

#はじめに
私が前に参画していたプロジェクトでは、一部でマイクロサービスというアーキテクチャが採用されていました。
マイクローサービスという言葉は、前々からかなり流行っていたらしいですが、当時の私はそんなことはつゆ知らず、ずっとMicrosoftのサービスなのだと勘違いをして、赤っ恥を掻いた覚えがあります。
ただ、当時も案件の忙しさから曖昧な理解のまま開発を終えて今に至るため、このままでは完全に全てを忘れてしまうと危機感を覚え、しっかりと勉強しようと思い立ちました。
以前JavaとSpringBootで１つサービスを作ったのですが、今回は復習として同じような環境でマイクロサービスを作成してみたいと思います。
作成するのは物凄く簡単なRESTサービスです。
#マイクロサービスとは
実際に私が受けた説明としては、１つのアプリケーションに全ての機能が詰まっているこれまでのアプリケーション(モノリシックなアプリケーションというらしいです)とは違い、個々に複数の小さな（マイクロ）サービスを開発し、連携させて１つの大きなシステムを作っていくといったものでした。
つまりマイクロサービスは、広い意味で言うとソフトウェア作成手法です。
この思想を元に作成したサービスも、マイクロサービスと呼んでいました。
※ちなみに今回は、この狭義のマイクロサービスを１つ作成します。

当時はそれで納得し分かった気になっていたのですが、改めて調べれば調べるほど、人によって説明が少しずつ違い、マイクロサービスの定義がよく分からずに頭を抱えることとなりました。それもそのはず、マイクロサービスはいわゆるバズワードというやつで、どうやら厳密な定義は無いらしいです。
そんな中でも、よく挙げられている特徴は、下記の通りです。

・サービス１つ１つが独立してデプロイ可能なこと
・各サービスはプロセス内で動作し、軽量なAPI（HTTPなど）で通信
・サービスはビジネス観点で分割されている
・それぞれのサービスは自動でデプロイされる
・中央集権的な管理を極力排する。言語、DB、開発チームを統制しない(要するにそれぞれのサービスは異なったプログラム言語等で書いても問題ない)

###メリット
ここではあまり触れませんが、マイクロサービスの開発によるメリットは調べると沢山出てきます。
私が実際に１つサービスを作ることになった際の話ですが、他のサービスはほとんどがgo言語で開発されていました。
勿論私も特別な理由がない限りは同じ言語で開発をするように言われたのですが、その時の私は触れたことが無く、「納期も近いのに今から勉強しながら実装だなんて無理デスゥ」と精一杯駄駄を捏ねた結果、苦笑いと共に書き慣れたJavaで開発する許可と、環境の作り方等を教えてもらいました。
こういった我儘が通ったのも、
>中央集権的な管理を極力排する。言語、DB、開発チームを統制しない

という、マイクロサービスの特徴による利点の一つだと思います。
また、１つ１つが疎結合なサービスなため、何か新しい技術を試してみたいけれども、ちょーベリー怖い。といった場合でも、リスクの低いサービスを選んで試すことが出来ます。
###デメリット
デメリットも勿論色々あります。サービスを分割することで１つ１つのサービスはシンプルですが、全体としてみるとすごく複雑になります。
また、
・サービスごとに異なる言語等で書いても良い
・新しい技術を試しやすい
といったメリットとして挙げた点に関しても、新規で開発する際は良いのですが、保守となるとそれぞれに対応できるエンジニアを集める必要が出てくるため、デメリットとなるようです。
#SpringBootとは
JavaのフレームワークであるSpringFrameworkでWebアプリケーションを簡単に構築するためのフレームワークです。
実行ファイルにWebコンテナ（tomcatやjetty）が内包されているため、別途Webサーバーを用意しなくても１つのアプリケーションとして立ち上げることができます。


#開発環境を用意
Javaで作成していくため、JavaとMavenをインストールします。(私はJDK1.8で動作を確認しました。)
私の使用したPCはMacだったため、↓が参考になりました。
https://qiita.com/amemolee/items/6f33b8e321cc9395b0cf

Spring Tool Suite (STS)をインストールします。
STSとは、Spring Frameworkの開発に特化した統合開発環境です。
ベースはEclipseです。基本的な操作方法などはほぼEclipseです。
↓からダウンロードしてインストールします。
https://spring.io/tools

作成したアプリケーションをdockerで動かしたいので、dockerを入れます。
https://docs.docker.com/docker-for-mac/install/

ちなみに私の環境は下記の通りです。
・JDK 1.8
・Maven 3.6.0
・Spring Tool Suite 4.0.1

#サービスの作成
それでは早速作っていきます。
###プロジェクトの作成
1. STSを開いたら、[File]→[New]→[Spring Starter Project]を選択します。
2. プロジェクトの名前やらパッケージやらを決めて、「Next」を押下します。
![スクリーンショット 2018-12-16 18.57.53.png](https://qiita-image-store.s3.amazonaws.com/0/152886/f6bd4971-901e-3bd7-7ee9-f64850ee1f1c.png)
3. Dependenciesの設定へ移るので、[Web]→[Web]にチェックを入れ、「Finish」を押下します。
![スクリーンショット 2018-12-16 18.52.33.png](https://qiita-image-store.s3.amazonaws.com/0/152886/81ff7cac-aa9c-5f1d-3424-745053a8daa5.png)
２つ目の手順で設定した名前で、プロジェクトができると思います。さらに、設定したパッケージの中に自動生成されたJavaのソースコードが入っています。
上記画像の通り作成をした場合、「testservice」プロジェクトが作成され、src/main/javaの下のcom.microserviceパッケージの中に、「TestserviceApplication.java」が作成されているはずです。
![スクリーンショット 2018-12-17 1.30.33.png](https://qiita-image-store.s3.amazonaws.com/0/152886/204d8e88-564c-d0ea-a3ac-f4a6f7c19c01.png)

###ソースの編集
生成されたJavaソースファイルを編集します。

```ruby:TestserviceApplication.java
package com.microservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class TestserviceApplication {
    public static void main(String[] args) {
        SpringApplication.run(TestserviceApplication.class, args);
    }
}
@RestController
class HomeController {
    @RequestMapping("/")
    public HogeTest getSentence() {
        return new HogeTest("テスト");
    }
}
class HogeTest {
    private String info;
    public HogeTest (String info) {
        this.setInfo(info);
    }
    public String getInfo() {
        return info;
    }
    public void setInfo(String info) {
        this.info = info;
    }
}
```
簡単なJSONを返す処理です。ソースコードの内容は今回は割愛します。
ソースを編集した時点で、STS場で[Run]→[Run As]→[Spring Boot App]で実行して確認できるのですが、とりあえず実行ファイルの作成の説明に移ります。
※試しに実行した場合、次の手順に進む前に停止させておいてください。
###実行ファイルの作成
Mavenのinstall機能で、jarファイルを作成します。
ターミナルで、プロジェクトのディレクトリ「testservice」直下まで移動し、下記のコマンドを実行します。

```
mvn install
```
成功すると、targetディレクトリの下に「testservice-0.0.1-SNAPSHOT.jar」という名前の実行ファイルが出来ているはずです。これを実行してみましょう。
プロジェクトのディレクトリ「testservice」直下から、下記のコマンドを実行します。

```
java -jar target/testservice-0.0.1-SNAPSHOT.jar
```
ブラウザで「http\://localhost:8080」にアクセスして、下記のように表示されればOKです。

<img width="606" alt="スクリーンショット 2018-12-18 2.10.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/152886/be597312-0e29-da84-4a80-88650677a948.png">


確認が出来たら、ターミナルで「control＋c」を押したりとかしてサービスを停止してください。

#Dockerへデプロイ
最後に、実行ファイルをDockerイメージとしてデプロイします。
Dockerはコンテナ型の仮想化サービスです。
コンテナは、他のプロセスからは隔離された、ある程度独立したプロセス実行環境です。
今回作成したプログラムも、Dockerコンテナ上で動かすことができます。
DockerはJVMと似たように、OSの違い等を吸収してくれます。そのため、プログラムをコンテナ上で動かせるようになれば、本番環境等の他の環境に簡単に移動させることができます。(その環境にもDockerが入っていること前提です。)
#Dockerイメージの作成
では、Dockerコンテナを作成する際に必要となるファイルシステムであるDockerイメージを作成します。
今回作成した実行ファイル「testservice-0.0.1-SNAPSHOT.jar」を用意し、同じディレクトリに「Dockerfile」という名前のテキストファイルを作成します。
Dockerfileというのは、イメージをビルドするための指示が書かれたファイルです。
ファイル内は、下記のように記載します。

```ruby:Dockerfile
FROM frolvlad/alpine-oraclejdk8:slim
ADD testservice-0.0.1-SNAPSHOT.jar app.jar
RUN sh -c 'touch /app.jar'
ENV JAVA_OPTS=""
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:dev/./urandom -jar /app.jar"]
```
Dockerfileができたら、下記のコマンドでイメージをビルドできます。

```
docker build -t microtest ./
```
#コンテナの起動
下記のコマンドで、作成したイメージを元にコンテナを起動します。

```
docker run -p 8080:8080 -d -t microtest
```

バックグラウンドで起動しているため分かりにくいですが、下記のコマンドで動いているかどうかを確認できます。

```
docker ps
```
↓のような感じで表示されればOKです。
![スクリーンショット 2018-12-18 2.06.10.png](https://qiita-image-store.s3.amazonaws.com/0/152886/5c21a37e-ae1b-9d8a-0d1e-6e88dcbeeda0.png)

試しにまたブラウザで「http\://localhost:8080」にアクセスすると、先ほどと同じようにJSON形式で{"info":"テスト"}と表示されるかと思います。

#さいごに
とりあえず一通り作成してみましたが、デプロイの自動化等、まだ上で挙げた特徴を満たせていない部分が残っています。
SpringBootでは、JUnit等でテストコードを書いて所定の場所に配置すれば、ビルド時に自動的に実行される機能があります。また、Dockerへのデプロイも可能です。
つまり、ビルドした際にテストが走り、パスしたら実行ファイルが作成され、Dockerにデプロイ、といったことが可能ですが、~~やり方忘れた~~長くなるのでまたの機会に。。


###参考サイト
https://blog.guildworks.jp/2015/01/27/docker-with-spring-boot/