Docker 環境で Java JAX-RS Webサービスを起動する

# Docker 環境で Java JAX-RS Webサービスを起動する

こんにちは、@studio_meowtoon です。今回は、WSL Ubuntu 22.04 の Docker 環境で Java JAX-RS Web アプリケーションをコンテナとして起動する方法を紹介します。
![java_on_docker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/bae348c7-5527-6f0f-5eb6-38c684377bc5.png)

## 目的

Windows 11 の Linux でクラウド開発します。

こちらから記事の一覧がご覧いただけます。

https://qiita.com/studio_meowtoon/items/6119ab05d8b13527efd8

## 実現すること

ローカル環境の Ubuntu の Docker 環境で、Dockerfile からビルドした Java JAX-RS Web サービスのカスタムコンテナを起動します。

**WAR ファイル形式のアプリをコンテナとして起動**
```mermaid
flowchart LR
    terminal --> app[app.war]
    subgraph Ubuntu
        subgraph Docker
            subgraph container[app-hello-jaxrs]
                subgraph JVM
                    subgraph jetty
                        app[app.war]
                    end
                end
            end
        end
    end
style Ubuntu fill:#FFBCA4, stroke:#E0460F
style Docker fill:#EEFDFF, stroke:#74B9DC
style JVM fill:#DCFFDC, stroke:#2F872F
style container fill:#FFD7E4, stroke:#E13C73
style app fill:#ECECFF, stroke:#9674DC
style jetty fill:#FFFFDE, stroke:#B4AE43
```

**実行環境**

|要素|概要|
|:--|:--|
|terminal|ターミナル|
|Ubuntu|OS|
|Docker|コンテナ実行環境|

**Web サービス コンテナ**

|要素|概要|
|:--|:--|
|app-hello-jaxrs|カスタムコンテナ|
|JVM|Java 実行環境|
|jetty|Web サーバー|
|app.war|Java アプリケーション|

## 技術トピック

### Dockerfile とは？  

<details>
<summary>こちらを展開してご覧いただけます。</summary>

### [Dockerfile](https://docs.docker.jp/engine/reference/builder.html#dockerfile)

:::note info
Dockerfile は、Docker コンテナを構築するためのテキストファイルです。Docker コンテナはアプリケーションやサービスを実行するための環境を含む軽量でポータブルな仮想化ユニットです。

|キーワード|内容|
|:--|:--|
|スクリプト形式|Dockerfile はシンプルなスクリプト形式で記述されるため、コンテナのビルドプロセスを自動化することが容易です。|
|レイヤー構造|Docker イメージは Dockerfile の各命令が実行される際にレイヤーとして生成され、再利用やキャッシュが可能な構造となっています。|
|バージョン管理|Dockerfile はテキストベースであるため、コードと同様にバージョン管理システムで管理しやすいです。|
|ポータビリティ|Dockerfile により、アプリケーションとその依存関係が1つのコンテナイメージにパッケージ化されるため、異なる環境間での移植性が高まります。|
|自動化と効率化|Dockerfile を使用することで、アプリケーションのビルドや環境構築を自動化できます。これにより、手動での作業時間やヒューマンエラーが減り、開発・デプロイプロセスが効率的になります。|
|再現性|Dockerfile はビルド手順を完全に定義するため、異なる環境で同じアプリケーションを再現できます。これにより、開発、テスト、本番環境間での一貫性が確保されます。|
|環境の分離|Docker コンテナはホストシステムから分離されるため、アプリケーションの依存関係やライブラリの衝突を回避し、より安全な環境で実行できます。|
|拡張性|Dockerfile を使用することで、カスタムイメージを作成することができます。このため、特定のニーズに合わせてカスタマイズされたコンテナイメージを容易に作成できます。|
:::

</details>

## 開発環境

+ Windows 11 Home 22H2 を使用しています。  

:::note info
[WSL](https://learn.microsoft.com/ja-jp/windows/wsl/about) の Ubuntu を操作していきますので macOS の方も参考にして頂けます。  
:::

<details>

WSL (Microsoft Store アプリ版) [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
```

Ubuntu [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
```

Java JDK [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/4d11e94a2389758759cd)

```sh
$ java -version
openjdk version "11.0.18" 2023-01-17
OpenJDK Runtime Environment (build 11.0.18+10-post-Ubuntu-0ubuntu122.04)
OpenJDK 64-Bit Server VM (build 11.0.18+10-post-Ubuntu-0ubuntu122.04, mixed mode, sharing)
```

Maven [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/f4cdc4039158153365fb)

```sh
$ mvn -version
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 11.0.18, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
```

Docker [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/3bceee25ac67cc73ebbe)

```sh
$ docker --version
Docker version 23.0.1, build a5ee5b1
```

</details>

:::note warn
この記事では基本的に **Ubuntu のターミナルで操作**を行います。**Vim を使用してコピペする方法**を初めて学ぶ人のために、以下の記事で手順を紹介しています。ぜひ挑戦してみてください。
:::

https://qiita.com/studio_meowtoon/items/7771ad48676926e41463

## 作成する Web アプリケーションの仕様

|No|エンドポイント|HTTPメソッド|MIME タイプ|
|:--|:--|:--|:--|
|1|/api/data|GET|application/json|

:::note info
**/api/data** というエンドポイントに対して HTTP GET リクエストを送信すると、JSON データがレスポンスされるシンプルな Web サービスを実装します。
**{"message":"Hello World!"}**
:::

## Hello World を表示する手順 

### Java JAX-RS Web サービスの作成  

こちらの関連記事で手順がご確認いただけます。

https://qiita.com/studio_meowtoon/items/de843e5ce499825bd13d

#### アプリケーションのメインコード

```java:HelloResource.java
package com.example.jaxrs;

import java.util.Map;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("api")
public class HelloResource {

    @GET
    @Path("data")
    @Produces(MediaType.APPLICATION_JSON)
    public Map<String, String> getData() {
        Map<String, String> map = Map.of("message", "Hello World!");
        return map;
    }
}
```

### プロジェクトフォルダに移動

プロジェクトフォルダに移動します。  
※ ~/tmp/hello-jaxrs をプロジェクトフォルダとします。    

```sh
$ cd ~/tmp/hello-jaxrs
```

### アプリのビルド

Java アプリをビルドします。
※ target/app.war が作成されます。

```sh
$ mvn clean package
```

:::note info
ここまでの手順で、ローカル環境の **Ubuntu** に **WAR ファイル形式**のアプリをビルドすることができました。
:::

### コンテナイメージの作成

Dockerfile を作成します。

```sh
$ vim Dockerfile
```

ファイルの内容

```Dockerfile:Dockerfile
# set up the container.
FROM jetty:10.0.15-jre11-alpine

# copy the app as context root.
COPY target/*.war /var/lib/jetty/webapps/ROOT.war

# expose the port.
EXPOSE 8080

# command to run the app using jetty.
CMD ["java","-jar","/usr/local/jetty/start.jar"]
```

Docker デーモンを起動します。

```sh
$ sudo service docker start
 * Starting Docker: docker    [ OK ]
```

:::note warn
Docker 環境をお持ちでない場合は、以下の関連記事から Docker Engine のインストール手順をご確認いただけます。
:::

https://qiita.com/studio_meowtoon/items/3bceee25ac67cc73ebbe

コンテナイメージをビルドします。

```sh
$ docker build \
    --no-cache \
    --tag app-hello-jaxrs:latest .
```

コンテナイメージを確認します。

```sh
$ docker images | grep app-hello-jaxrs
app-hello-jaxrs    latest    a810eb763163    9 seconds ago    185MB
```

:::note info
ここまでの手順で、ローカル環境の **Docker** にアプリの**カスタムコンテナイメージ**をビルドすることができました。
:::

### コンテナを起動

ローカルでコンテナを起動します。
※ コンテナを停止するときは ctrl + C を押します。

```sh
$ docker run --rm \
    --publish 8080:8080 \
    --name app-local \
    app-hello-jaxrs:latest
```

:::note info
ここまでの手順で、ローカル環境の **Docker** でアプリの**カスタムコンテナ**を起動することができました。
:::

### コンテナの動作確認

別ターミナルから curl コマンドで確認します。

```sh
$ curl -v http://localhost:8080/api/data -w '\n'
```

出力

```sh
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /api/data HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json
< Content-Length: 26
< Server: Jetty(10.0.15)
<
* Connection #0 to host localhost left intact
{"message":"Hello World!"}
```

:::note info
ここまでの手順で、ターミナルに **{"message":"Hello World!"}** と表示され、**JSON データ**を取得することが出来ました。 
::: 

コンテナの状態を確認してみます。

```sh
$ docker ps
CONTAINER ID   IMAGE                    COMMAND                   CREATED         STATUS         PORTS                                       NAMES
81f317bf4f61   app-hello-jaxrs:latest   "/docker-entrypoint.…"   8 minutes ago   Up 8 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   app-local
```

### コンテナに接続

別ターミナルからコンテナに接続します。  

```sh
$ docker exec -it app-local /bin/sh
```

コンテナに接続後にディレクトリを確認します。
※ コンテナから出るときは ctrl + D を押します。

```
$ pwd
/var/lib/jetty
$ cd webapps
$ ls -lah
total 6M
drwxr-xr-x    1 jetty    jetty       4.0K Aug  1 06:59 .
drwxr-sr-x    1 jetty    jetty       4.0K Aug  1 07:04 ..
-rw-r--r--    1 root     root        6.2M Aug  1 06:05 ROOT.war
```

top コマンドで状況を確認します。

```sh
Mem: 4677204K used, 3409432K free, 2032K shrd, 182556K buff, 1859192K cached
CPU:   0% usr   0% sys   0% nic  99% idle   0% io   0% irq   0% sirq
Load average: 0.00 0.02 0.07 1/569 113
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    1     0 jetty    S    3576m  44%   4   0% /opt/java/openjdk/bin/java -Djetty.home=/usr/local/jetty -Djetty.base=/var/lib/jetty --class-path /var/lib/jet
  106     0 jetty    S     1672   0%   5   0% /bin/sh
  113   106 jetty    R     1600   0%   6   0% top
```

コンテナの情報を表示してみます。

```c
$ cat /etc/*-release
3.18.2
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.18.2
PRETTY_NAME="Alpine Linux v3.18"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
```

:::note warn
このコンテナは Alpine Linux をベースに作成されています。つまり、Linux と同じように扱うことができます。
:::

## まとめ

WSL Ubuntu の Docker 環境で、Dockerfile からビルドした Java JAX-RS Web サービスのカスタムコンテナを起動することができました。

:::note warn
クラウド開発においては、**Dockerfile の理解**は重要です。自動ビルドツールもありますが、手動で書く必要があるケースもあります。**Ubuntu** を使うと **Linux の知識**も身に付きます。最初は難しく感じるかもしれませんが、徐々に進めていけば自信を持って書けるようになります。
:::

どうでしたか？ WSL Ubuntu で、Java JAX-RS Web アプリケーションを Docker 環境でコンテナとして手軽に起動することができます。ぜひお試しください。今後も Java の開発環境などを紹介していきますので、ぜひお楽しみにしてください。

## 推奨コンテンツ

https://qiita.com/studio_meowtoon/items/7fdaad559236f3f782f2

## 関連記事

https://qiita.com/studio_meowtoon/items/9c07e20b4124d8c5f972

https://qiita.com/studio_meowtoon/items/b7ab1619143669329498