Skip to content
Published on

Spring Boot Embedded Tomcat完全ガイド:設定、最適化、本番環境チューニング

Authors

目次

  1. Embedded Tomcatの基本構造
  2. 主要設定オプション
  3. SSL/TLS設定
  4. HTTPとHTTPSの同時運用
  5. スレッドプールのチューニング
  6. Undertowへの切り替え
  7. カスタムErrorPageとFilterRegistration
  8. Graceful Shutdown
  9. クイズ

1. Embedded Tomcatの基本構造

Spring BootはEmbedded Tomcatを標準で内蔵しており、別途WASをインストールすることなく単独で実行可能なJARファイルを作成できます。spring-boot-starter-web依存関係を追加すると、自動的にTomcatが含まれます。

spring-boot-starter-web依存関係ツリー

spring-boot-starter-web
└── spring-boot-starter-tomcat
    ├── tomcat-embed-core
    ├── tomcat-embed-el
    └── tomcat-embed-websocket

組み込みサーバー比較表

項目TomcatUndertowJetty
デフォルト搭載ありなしなし
メモリ使用量
HTTP/2サポートありありあり
WebSocketありありあり
スループット非常に高い
成熟度/安定性非常に高い
コミュニティ非常に大きい大きい

Tomcatは最も実績のあるサーブレットコンテナであり、ほとんどのエンタープライズSpring Bootアプリケーションのデフォルト選択肢です。Undertowは低メモリ使用量と高い並行処理が必要なマイクロサービスに適しています。


2. 主要設定オプション

application.ymlでEmbedded Tomcatのほぼすべての設定を制御できます。

全体設定例

server:
  port: 8080
  servlet:
    context-path: /api
    session:
      timeout: 30m
      cookie:
        http-only: true
        secure: true
  tomcat:
    threads:
      max: 200 # 最大ワーカースレッド数(デフォルト: 200)
      min-spare: 10 # 最小アイドルスレッド数(デフォルト: 10)
    max-connections: 8192 # 同時接続の最大数
    accept-count: 100 # max-connections超過時の待機キューサイズ
    connection-timeout: 20000 # 接続タイムアウト(ミリ秒)
    max-http-form-post-size: 2MB
    max-swallow-size: 2MB
    uri-encoding: UTF-8
    accesslog:
      enabled: true
      directory: logs
      prefix: access_log
      suffix: .txt
      pattern: combined
      rotate: true
  shutdown: graceful
  http2:
    enabled: true
  compression:
    enabled: true
    mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
    min-response-size: 1024

主要パラメータの詳細説明

threads.max(最大スレッド数)

  • デフォルト: 200
  • 同時に処理できるリクエストの最大数
  • CPUコア数とアプリケーションのI/O特性に応じて調整が必要

max-connections

  • デフォルト: 8192
  • NIOコネクタが同時に維持できる最大TCP接続数
  • Keep-Alive接続を効率的に管理するため、threads.maxよりはるかに大きく設定する

accept-count

  • デフォルト: 100
  • max-connectionsが上限に達したときのOSレベルのソケット待機キューサイズ
  • この値を超えるとクライアントにConnection Refusedが返される

connection-timeout

  • デフォルト: 20000ms(20秒)
  • クライアントがリクエストデータを送信するのに許可された最大時間

3. SSL/TLS設定

テスト用自己署名証明書の生成

keytool -genkeypair -alias tomcat -keyalg RSA -keysize 2048 \
  -storetype PKCS12 -keystore keystore.p12 -validity 3650 \
  -storepass changeit -dname "CN=localhost, OU=Dev, O=Example, L=Tokyo, S=Tokyo, C=JP"

application.yml SSL設定

server:
  port: 8443
  ssl:
    key-store: classpath:keystore.p12
    key-store-password: changeit
    key-store-type: PKCS12
    key-alias: tomcat
    enabled: true
    protocol: TLS
    enabled-protocols: TLSv1.2,TLSv1.3
    ciphers: TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_GCM_SHA256

Let's Encrypt証明書の連携

Let's EncryptのPEM形式の証明書をPKCS12に変換してTomcatで使用します。

# PEMをPKCS12に変換
openssl pkcs12 -export \
  -in /etc/letsencrypt/live/yourdomain.com/fullchain.pem \
  -inkey /etc/letsencrypt/live/yourdomain.com/privkey.pem \
  -out keystore.p12 \
  -name tomcat \
  -passout pass:yourpassword

本番環境では、キーストアのパスワードを環境変数で注入することがセキュリティ上安全です。

server:
  ssl:
    key-store: file:/etc/ssl/keystore.p12
    key-store-password: ${SSL_KEYSTORE_PASSWORD}

4. HTTPとHTTPSの同時運用

Spring Bootはデフォルトで1つのポートのみをサポートしますが、TomcatServletWebServerFactoryをカスタマイズしてHTTPとHTTPSの両方を同時に開くことができます。

2ポートの同時開放

@Configuration
public class TomcatConfig {

    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addAdditionalTomcatConnectors(createHttpConnector());
        return factory;
    }

    private Connector createHttpConnector() {
        Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
        connector.setScheme("http");
        connector.setPort(8080);
        connector.setSecure(false);
        connector.setRedirectPort(8443);
        return connector;
    }
}

HTTPからHTTPSへの自動リダイレクト

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .requiresChannel(channel ->
                channel.anyRequest().requiresSecure()
            );
        return http.build();
    }
}

5. スレッドプールのチューニング

サイジングの公式

I/Oバウンドアプリケーション(DB、外部API呼び出し)

適切なスレッド数 = CPUコア数 x (1 + 待機時間 / 処理時間)

CPUバウンドアプリケーション(画像処理、暗号化)

適切なスレッド数 = CPUコア数 + 1

ほとんどのWebアプリケーションはI/Oバウンドのため、CPUコア数の10〜20倍が出発点となります。JMeterやGatlingを使った負荷テストで最適値を見つけることが重要です。

Java 21 Virtual Threadの適用

Java 21のVirtual Threadを活用することで、スレッドプールサイズの手動調整が大幅に不要になります。

spring:
  threads:
    virtual:
      enabled: true

またはコードで直接設定:

@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
    return protocolHandler -> {
        protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
    };
}

Actuatorでのスレッド監視

management:
  endpoints:
    web:
      exposure:
        include: metrics,health,threaddump

/actuator/metrics/tomcat.threads.busy/actuator/metrics/tomcat.threads.config.maxでリアルタイムのスレッド使用量を確認できます。


6. Undertowへの切り替え

依存関係の変更

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

Undertow設定オプション

server:
  undertow:
    threads:
      worker: 200 # ワーカースレッド数(デフォルト: CPUコア x 8)
      io: 4 # I/Oスレッド数(デフォルト: CPUコア x 2)
    buffer-size: 16384 # バッファサイズ(バイト)
    direct-buffers: true
    max-http-post-size: 10MB
    accesslog:
      enabled: true
      dir: logs
      pattern: combined

TomcatとUndertowのパフォーマンス比較

Undertowのノンブロッキングアーキテクチャは、高並行環境でTomcatよりも低いメモリ使用量と高いスループットを実現します。WebSocketやサーバープッシュを多用するアプリケーションでその差が顕著です。標準的なREST APIサーバーでは、2つのサーバー間のパフォーマンス差は比較的小さくなります。


7. カスタムErrorPageとFilterRegistration

TomcatServletWebServerFactoryのカスタマイズ

@Configuration
public class WebServerConfig {

    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

        // カスタムエラーページの登録
        factory.addErrorPages(
            new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"),
            new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"),
            new ErrorPage(Exception.class, "/error/general")
        );

        // Tomcatコンテキストのカスタマイズ
        factory.addContextCustomizers(context -> {
            context.setSessionTimeout(30);
            context.setUseHttpOnly(true);
        });

        return factory;
    }
}

カスタムFilterの登録

@Bean
public FilterRegistrationBean<RequestLoggingFilter> loggingFilter() {
    FilterRegistrationBean<RequestLoggingFilter> registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new RequestLoggingFilter());
    registrationBean.addUrlPatterns("/api/*");
    registrationBean.setOrder(1);
    return registrationBean;
}

リバースプロキシ・ロードバランサー設定

nginxやAWS ALBなどのリバースプロキシの背後で動作させる場合、クライアントIPアドレスとプロトコルを正しく解決するためにRemoteIPバルブを設定します。

server:
  tomcat:
    remoteip:
      remote-ip-header: x-forwarded-for
      protocol-header: x-forwarded-proto

8. Graceful Shutdown

設定

server:
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

Graceful Shutdownを有効にすると、SIGTERM受信後に新しいリクエストの受け付けを停止し、処理中のリクエストが完了するまで最大30秒待ってからシャットダウンします。

Kubernetes連携

# kubernetes deployment.yaml
spec:
  template:
    spec:
      terminationGracePeriodSeconds: 60 # Kubernetesレベルの終了待機
      containers:
        - name: app
          lifecycle:
            preStop:
              exec:
                command: ['/bin/sh', '-c', 'sleep 5'] # ロードバランサー除外待機

KubernetesのterminationGracePeriodSecondsは、Spring Bootのtimeout-per-shutdown-phaseより5〜10秒余裕を持って設定する必要があります。

ヘルスプローブ連携

management:
  endpoint:
    health:
      probes:
        enabled: true
  health:
    livenessstate:
      enabled: true
    readinessstate:
      enabled: true

Readiness ProbeがOUT_OF_SERVICEに切り替わってロードバランサーから除外された後に実際のシャットダウンが開始されるよう設定します。


9. クイズ

Q1. Spring Boot TomcatでTCP同時接続の最大数を制御する設定は?

答え: server.tomcat.max-connections

解説: max-connectionsはTomcat NIOコネクタが同時に維持できる最大接続数を制御します。デフォルト値は8192です。Keep-Alive接続を効率的に処理するため、この値はthreads.maxよりはるかに大きく設定します。accept-countはmax-connectionsが上限に達したときのOSレベルのソケット待機キューサイズを意味します。

Q2. Graceful Shutdownで終了シグナルを受け取った後の動作は?

答え: 新しいリクエストの受け付けを拒否し、設定されたtimeout-per-shutdown-phaseの時間内に処理中のリクエストが完了するまで待機してからシャットダウンします。

解説: server.shutdown=gracefulに設定すると、SIGTERM受信後にTomcatは新しい接続の受け付けを停止し、アクティブなリクエストの完了を待ちます。spring.lifecycle.timeout-per-shutdown-phaseで最大待機時間を設定します。Kubernetes環境ではterminationGracePeriodSecondsをSpring Bootのタイムアウトより高く設定して、完全なグレースフルシャットダウンシーケンスが完了するようにする必要があります。

Q3. Java 21のVirtual ThreadをSpring Boot Tomcatに適用する方法は?

答え: spring.threads.virtual.enabled=trueを設定するか、TomcatProtocolHandlerCustomizer BeanでExecutors.newVirtualThreadPerTaskExecutor()をexecutorとして設定します。

解説: JDK 21で安定版として導入されたVirtual Threadは、ブロッキングI/O操作中もOSスレッドを占有しない軽量スレッドで、大規模な並行リクエスト処理に適しています。Spring Boot 3.2以降では、spring.threads.virtual.enabled=trueプロパティのみで簡単に有効化できます。

Q4. TomcatをUndertowに交換する際に必ず行うべき作業は?

答え: spring-boot-starter-webからspring-boot-starter-tomcatをexclusionで除外してから、spring-boot-starter-undertowを追加する必要があります。

解説: Spring Bootのオートコンフィギュレーションはクラスパスにあるサーバー実装を自動検出します。単にUndertowを追加するだけでは、2つのサーバー実装が競合します。Tomcatを明示的にexclusionで除外してからUndertowを追加する必要があります。

Q5. server.tomcat.threads.maxとserver.tomcat.max-connectionsの違いは?

答え: threads.maxはリクエストを実際に処理するスレッドの最大数であり、max-connectionsは同時に維持できるTCP接続の最大数です。

解説: HTTP/1.1のKeep-Aliveにより、1つのTCP接続で複数のリクエストを順次処理できます。そのため、max-connectionsは通常threads.maxよりはるかに大きく設定します。例えば200スレッド・8192接続の設定では、8192のTCP接続を維持しながら、そのうち最大200を同時に処理できます。accept-countはすべての接続が使用中の場合に追加で待機できるリクエスト数です。