Docker

Docker Composeで動かす超軽量API検証環境の作り方

本記事では、TomcatもSpring Bootも使わない、Java 21内蔵HttpServer + Docker Composeによる「究極にシンプルなAPI検証環境」の作り方をご紹介します

Java 21でさらに強化された「単一ソースファイルプログラム実行機能」を活用すれば、スクリプト言語のような手軽さで、コンテナ上でJava APIを動かすことができます。ファイル共有(ボリュームマウント)と組み合わせれば、ローカルのソースコードを書き換えるだけでコンテナ側の挙動が即座に変わる、最高の実験場が手に入ります。接続確認にはもってこいじゃないですかね。

フォルダ構成
/opt/docker
    └ java-api          ← 【プロジェクトのルートディレクトリ】(作業の起点となる場所)
        ├ compose.yaml  ← Docker Composeの設定ファイル
        └ apps          ← Javaソースコードを配置する共有フォルダ
            └ sample-api.java
composeファイルの作成

compose.yamlというファイル名で以下作成します。

services:
  java-api:
    # Java 21 の実行環境(JRE)の公式イメージ
    image: eclipse-temurin:21-jdk
    container_name: my-java-api
    ports:
      - "9090:8080"  # [手元のPCのポート] : [JAR内のアプリが待ち受けるポート]
    volumes:
      # 手元の apps フォルダを、コンテナ内の /app フォルダにマウント
      - ./apps:/app
    working_dir: /app
    # コンテナが起動したときに実行するコマンド(JARを起動する)
    #command: java -jar sample-api.jar
    # Java21の機能で、ソースコードをダイレクトに実行!
    command: java sample-api.java
    restart: always
コンテナで実行するJavaソースコード(sample-api.java)

今回、コンテナの中で起動させるJavaのソースコードです。Java 21の機能を活かすため、事前コンパイルやJARへのビルドは一切行わず、この未コンパイルの .java ファイルのままコンテナに読み込ませて直接実行します。

import com.sun.net.httpserver.HttpServer;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.time.LocalDateTime;

public class SampleApi {
    private static final int PORT = 8080;
    public static void main(String[] args) throws Exception {

        // サーバインスタンス生成(まだスレッドは起動しない)
        HttpServer server = HttpServer.create(new InetSocketAddress(PORT), 0);

        // ハンドラ登録
        server.createContext("/sample-api3", exchange -> {
            String clientIp = exchange.getRemoteAddress().getAddress().getHostAddress();

            // アクセスログ(IPアドレス・メソッド・パス)
            System.out.printf(
                "[%s] %s %s from %s%n",
                LocalDateTime.now(),
                exchange.getRequestMethod(),
                exchange.getRequestURI(),
                clientIp
            );

            String json = String.format(
               "{\"status\":\"SUCCESS\",\"time\":\"%s\",\"clientIp\":\"%s\"}",
                LocalDateTime.now(),
                clientIp
            );

            byte[] bytes = json.getBytes();
            exchange.getResponseHeaders().set("Content-Type", "application/json; charset=UTF-8");
            exchange.sendResponseHeaders(200, bytes.length);

            try (OutputStream os = exchange.getResponseBody()) {
                //レスポンスボティをバイト列でソケットに書き込む
                os.write(bytes);
            }
        });

        // シャットダウンフックでCtrl+cをキャッチし終了
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("Stopping server...");
            server.stop(0); // 0 = 即時停止(graceful ではない)
            System.out.println("Server stopped.");
        }));

        // 受付スレッド起動(内部でServerSocket.accept()が呼ばれ待機状態(I/O待ち)となる)
        server.start();
        System.out.println("Connectivity Test Server started on port " + PORT);
    }
}
composeファイルの作成
[root@localhost java-api]# pwd
/opt/docker/java-api
# コンテナの起動
#
[root@localhost java-api]# docker compose up -d
[root@localhost java-api]# docker compose up -d
[+] up 2/2
 ? Network java-api_default Created                                                                                                                                     0.3s
 ? Container my-java-api    Started

# コンテナの出力ログ(標準出力)をリアルタイムで監視
#
[root@localhost java-api]# docker compose logs -f
my-java-api  | Connectivity Test Server started on port 8080
my-java-api  | [2026-06-14T15:26:41.595374469] GET /sample-api3 from 192.168.56.1
my-java-api  | [2026-06-14T15:27:03.696797032] GET /sample-api3 from 192.168.56.1
my-java-api  | [2026-06-14T15:27:56.336340576] GET /sample-api3 from 192.168.56.1
my-java-api  | Stopping server...   ←別コンソールからコンテナの停止
my-java-api  | Server stopped.
my-java-api exited with code 143
[root@localhost java-api]#
# コンテナの停止
#
[root@localhost java-api]# docker compose down
[+] down 2/2
 ? Container my-java-api    Removed                                                                                                                                     0.8s
 ? Network java-api_default Removed                                                                                                                                     0.4s
[root@localhost java-api]#
今回の記事は以下したものをDockerコンテナ化したものです
最小構成の Java API を作りながら HttpServer の内部を理解する
Tomcat や Jetty といったサーブレットコンテナは、Servlet API(HttpServlet / doGet / doPost)を使って HTTP リクエストを処理します。しかし今回は、Servlet API を使わず、JD…

スポンサーリンク
タイトルとURLをコピーしました