概要
Web アプリケーションを Java 17 から Java 21 へ移行しました。
移行前は「Jakarta 移行が大変そう」「Java 21 の互換性でハマるのでは」と身構えていましたが、実際に作業を進めてみると、一番時間を使ったのは Java そのものではありませんでした。
この記事では、Java 21 移行の実体験を通して感じた次の点について整理します。
- 想定していた課題と、実際に大変だった点のズレ
- Jakarta 移行をやってみた所感
- Java 移行をきっかけに表面化した、Java 以外の問題
前提
今回の内容は、Java のバージョンアップ単体ではなく、
Java 21 対応を前提にした周辺基盤を含めた移行を行った上での振り返りです。
具体的には、Java 17 → 21 への更新にあわせて、
- Spring Boot のメジャーバージョン更新
- Jakarta 移行(javax.* → jakarta.*)
- ビルド基盤(Gradle 7.x → 8.7)
- DB マイグレーション(Flyway 8.x → 10.x)
- 業務基盤ライブラリ(nablarch)
といった Java 周辺を含めた一通りの対応を行っています。
そのうえで振り返ると、Java や Jakarta 自体の移行は、
想定していたほど難易度の高い作業ではありませんでした。
今回の話は、以下のような前提環境でのものです。
| 項目 | 内容 |
|---|---|
| Java | 17 → 21 |
| フレームワーク | Spring Boot |
| 実行環境 | Docker(開発) |
| 開発環境 | VS Code Remote / EC2 |
全体方針
移行作業は、影響を局所化するために段階的に進めました。
- ビルド基盤を先に上げる
Java 21 / Gradle 8 系でビルドできる状態を作る - アプリケーション側を対応
Spring Boot 3.x + Jakarta 移行 - 実行環境を最後に変更
Docker ベースイメージ更新
この順序にしたことで、「何が原因で壊れたのか」を追いやすくなりました。
Jakarta 移行に関する実態
移行前に想定していたこと
Java 21 移行と聞いて、最初に思い浮かんだ懸念は Jakarta 移行でした。
- javax.* → jakarta.* の全面変更
- Validation / Servlet / Annotation 周りの影響
- ライブラリ非対応による詰まり
実際にやったこと
実作業としては、以下が中心でした。
- Spring Boot 3.x への更新
- import の置き換え
- コンパイルエラー・起動エラーの修正
コード修正の内容自体は「新しい書き方を覚える」というより、明確な置換作業が大半でした。
正直な感想
Jakarta 移行は、
思っていたよりずっと機械的で分かりやすい
という印象でした。
VSCode の補完やコンパイルエラーがしっかりガイドしてくれるため、「何を直せばよいか分からない」状態にはなりませんでした。
本当に時間を取られた問題①:Docker ベースイメージの変更
なぜ変更したか
Java 21 への移行にあたり、Docker のベースイメージも見直す必要がありました。
これまで使用していた eclipse-temurin:17-alpine は、Java 17 時点では公式に提供されていましたが、Java 21 では同等の Alpine ベース公式イメージが、移行時点では十分に揃っていませんでした。
そのため、継続して使い続けるには少し不安が残る状況でした。
そこで Java 21 対応を機に、Dockerfile の記述や運用をシンプルに保ちやすいDebian/Ubuntu 系ベースの eclipse-temurin:21-jdk へ切り替える判断をしました。
結果として、Docker ベースイメージは次のように変更しています。
| 変更前 | 変更後 |
|---|---|
eclipse-temurin:17-alpine |
eclipse-temurin:21-jdk |
/bin/ash |
/bin/bash |
apk |
apt-get |
この変更によって、Java とは無関係な部分が一気に壊れ始めました。
具体的なトラブル
-
/bin/ashが存在しない -
apk addが使えない - 既存 Dockerfile が前提としていた OS が違う
Alpine 前提で書かれていた Dockerfile が、Debian/Ubuntu 系ではそのままでは動かない、という当たり前の事実に改めて向き合うこととなりました。
本当に時間を取られた問題②:UID とファイル所有権
発生した症状
Docker イメージ変更後、次のような問題が発生しました。
- Gradle が build/ を削除できない
- Git の checkout / reset が Permission denied で失敗する
- VS Code からファイルを保存できない
表面的には別々の不具合に見えましたが、いずれも「ファイル操作ができない」という点で共通していました。
原因の正体
原因は、Docker ベースイメージ変更に伴う ユーザ・UID・ファイル所有権の前提のズレでした。
| 要因 | 内容 |
|---|---|
| UID | コンテナ内ユーザとホストの UID が一致していなかった |
| ユーザ | ベースイメージ変更により、従来と同じユーザ前提で構成できなくなった |
| 所有者 | root や別ユーザ所有のファイルが混在した |
| Git | unlink(削除)ができず操作が失敗した |
Alpine ベース時代は、ユーザや UID の前提を特に意識せずに運用できていましたが、Debian/Ubuntu 系ベースに切り替えたことで、その前提が成り立たなくなりました。
結果として、ホストとコンテナ間でユーザや UID がズレた状態のままファイルが生成され、権限エラーが連鎖的に発生していました。
対応
対応としては、コンテナ内で使用するユーザと UID を明示的に定義し、ホスト側と揃える構成に見直しました。
root 実行や所有権の混在を避けることで、Gradle・Git・VS Code の操作はすべて正常に戻りました。
Java の移行作業中にもかかわらず、Git 操作そのものができなくなる状況に陥ったのは、今回の移行で最も時間を取られたポイントでした。
「Java 21 移行」の何が難しかったか
振り返ると、Java 21 移行そのものが難しかったのではありません。
| 移行前に意識していたこと | 実際に向き合うことになったもの |
|---|---|
| Java 21 の互換性 | Docker / 実行環境 |
| Jakarta 移行 | OS やユーザの前提 |
| 言語仕様や API 変更 | 開発・実行環境の暗黙知 |
Java のバージョンを上げたことで、それまで「問題になっていなかった前提」が一気に表に出ました。
今回表面化したのは、
- Docker ベースイメージ
- ユーザ / UID 不一致
- ファイル所有権や Git 操作
でしたが、これは Java 以外の要素が原因で詰まった一例に過ぎません。
これから移行する人へのメッセージ
今回の移行を振り返って、これから Java 21 への移行を考えている人に一つだけ伝えたいことがあります。
Java のバージョンアップは、Java だけの問題では終わらない
特に Docker やリモート開発環境を使っている場合、
- ベース OS の違い
- 実行ユーザや権限
- ファイルの扱われ方
- 周辺ツール(Git / エディタ / CI)
といった Java の外側にある前提まで含めて見直す必要があります。
今回の移行では UID やファイル所有権、Git 操作が問題として顕在化しましたが、プロジェクトや環境が違えば、別の形で現れる可能性もあります。
まとめ
- Jakarta 移行は想像よりシンプルだった
- 本当に時間を使ったのは Java 以外の部分だった
- Java 21 移行は、結果的に環境の前提を見直す作業でもあった
- Java を上げると、それまで意識していなかった層が見えてくる
Java 21 自体はとても安定していました。
結果的に、Java そのものよりもこれまで当たり前だと思っていた環境の前提に一番向き合うこととなりました。