【Java環境構築】WSL+Docker+DevContainer+VSCode で開発環境を構築する

# はじめに

以下の記事に引き続き、 WSL で実行する Docker 上で Java の開発環境を構築する手順も整理しておきます。

[【Java 環境構築】Windows+Scoop+VSCode で開発環境を構築する](https://qiita.com/nimzo6689/items/72898eb9dc3896a41269)
[【Java 環境構築】WSL+SDKMAN!+VSCode で開発環境を構築する](https://qiita.com/nimzo6689/items/c399840b360e409b0c61)

Docker を Windows で利用する際、Docker Desktop を利用するか WSL 上で直接インストールするかの２パターンに分かれます。Docker Desktop はライセンスの関係で利用可能な場面が制限されるため、環境に依存しない手順とするべく、この記事では WSL 上へ Docker をインストールする方法を採用しました。

構成図はこちらです。
ソースコードは WSL2 に配置して、コンテナから読み書きできるようにマウントされます。ソースコードも図に載せるとレイアウトがどうしても崩れるので、割愛しました。

```mermaid
flowchart LR
    %% スタイル定義
    classDef class_windows fill:#f0f8ff,stroke:#1E90FF,stroke-width:2px
    classDef class_wsl fill:#E8F5E9,stroke:#2E7D32,stroke-width:2px
    classDef class_container fill:#FFF3E0,stroke:#EF6C00,stroke-width:2px
    classDef class_vscode fill:#F3E5F5,stroke:#7B1FA2,stroke-width:2px
    classDef class_component fill:#ffffff,stroke:#666,stroke-width:1px

    subgraph Windows["Windows 環境"]
        direction TB
        subgraph vscode_local["Visual Studio Code 📝"]
            wsl_ext_local["WSL Extension 🔌"]
        end
    end

    subgraph WSL["WSL2 (Linux) 環境"]
        direction TB
        subgraph vscode_wsl["VSCode サーバー💻"]
            devcon_ext_wsl["Dev Containers<br>Extension 🔌"]
            wsl_ext_wsl["WSL Extension 🔌"]
        end
        docker["Docker Engine<br>🐳"]
        subgraph container["Dev Container 環境"]
            direction TB
            jdk["JDK 21<br>☕"]
            maven["Maven<br>🔧"]
            vscode_devcon
        end
    end

    subgraph vscode_devcon["VSCode サーバー💻"]
        direction TB
        java_ext["Extension Pack<br>for Java 📦"]
        java_ext --> jdk
        java_ext --> maven
    end

    wsl_ext_local --> wsl_ext_wsl
    wsl_ext_wsl --> wsl_ext_local
    devcon_ext_wsl --> vscode_devcon
    devcon_ext_wsl --> docker
    docker --> container

    %% スタイルの適用
    class Windows class_windows
    class WSL class_wsl
    class container class_container
    class vscode_local,vscode_wsl,vscode_devcon class_vscode
    class docker,jdk,maven,java_ext,devcon_ext_local,wsl_ext_local,devcon_ext_wsl,wsl_ext_wsl class_component
```

# 1. WSL2 のインストール

Windows 10 ビルド 19041 以上、または、Windows 11 であれば、以下のコマンドだけで大丈夫です。

```powershell:PowerShell(管理者モード)
# OS のバージョンを確認
$PSVersionTable.OS
#=> Microsoft Windows 10.0.22631

# WSL をインストール
wsl --install

# インストールが完了すれば、wsl コマンドで Ubuntu を起動 & ログインする。
wsl
# 初回はユーザ名とパスワードを設定する。
```

これで Ubuntu ディストリビューションが WSL として利用できます。

それ以外の PC 環境の方は、以下の公式ページをご参照ください。

https://learn.microsoft.com/ja-jp/windows/wsl/install

# WSL に Docker をインストール

WSL にログイン後、 `docker run hello-world` をすぐに実行できる状態までセットアップします。また、以下の手順は Docker Compose も利用可能になります。

```bash:bash on WSL
# Docker 公式が提供しているセットアップスクリプトを実行する
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# sudo なしで docker コマンドを実行できるようにする
# `sudo groupadd docker` は既に docker ユーザが上記のコマンドで自動作成されるため不要
sudo usermod -aG docker $USER
newgrp docker

# Ubuntu では Dcoker サービスはデフォルトで起動時に開始されるため、以下のコマンドは不要です
# sudo systemctl enable docker.service
# sudo systemctl enable containerd.service
```

:::note info
Docker をアップグレードしたい場合は APT を使って更新できます。

<details><summary>Docker の更新手順</summary>

```bash
# 利用可能なバージョンを確認
apt-cache madison docker-ce | awk '{ print $3 }'
#=> 5:27.4.0-1~ubuntu.24.04~noble
#=> 5:27.3.1-1~ubuntu.24.04~noble
#=> ...

# バージョンを指定してアップグレードする
VERSION_STRING=5:27.4.0-1~ubuntu.24.04~noble
sudo apt-get install \
    docker-ce=$VERSION_STRING \
    docker-ce-cli=$VERSION_STRING \
    containerd.io \
    docker-buildx-plugin \
    docker-compose-plugin
```

</details>
:::

他のインストール方法や最新のインストール方法を確認したい場合はこちらをご参照ください。

[dockerdocs - Install Docker Engine on Ubuntu](https://docs.docker.com/engine/install/ubuntu/)
[dockerdocs - Linux post-installation steps for Docker Engine](https://docs.docker.com/engine/install/linux-postinstall/)

# Portainer のインストール

Docker Desktop を使わないので、 Docker の何かしらの操作をしたい場合、WSL に入って docker コマンドを実行する必要があります。ただ、 Portainer を WSL にインストールして自動起動するように設定しておけば、ブラウザでコンテナの状態の確認や停止などができるようになります。

ただ、こちらは Java 環境構築において必須というわけではないので、興味がある方だけで大丈夫です。

<details><summary>Portainer のインストール方法</summary>

```bash:bash on WSL
docker volume create portainer_data
docker run -d -p 8000:8000 -p 9443:9443 \
    --name portainer --restart=always \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v portainer_data:/data portainer/portainer-ce:2.21.5
```

上記のコマンドを実行すれば、 https://localhost:9443 から Portainer のダッシュボードが開けます。

最新のインストール方法を確認したい場合はこちらをご参照ください。

[docs.portainer.io - Install Portainer CE with Docker on WSL](https://docs.portainer.io/start/install-ce/server/docker/wsl)

初回アクセス時は以下のようにアカウントの作成画面から始まります。パスワードは 12 文字以上が必須です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/9867e4c1-484a-919e-9810-aaabf2647cb4.png)

アカウント作成後、「Get Started」のメニューを選ぶと Docker Desktop のように Docker Image やコンテナの確認や削除ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/bb11fb5b-90f9-f628-9672-f70889b040f8.png)

</details>

# VSCode のセットアップ

以下の拡張機能をインストールします。

https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl

https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers

また、 DevContainers 拡張機能が内部で実行するコマンドを WSL 上で実行させる必要があるため、 `dev.containers.executeInWSL` にチェックを入れます。この設定により、あたかも Docker Desktop と同じように WSL にインストールした Docker が使えるようになります。スゴィ！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/b0d59569-bda7-4541-6d49-1b45a6878007.png)

:::note info
もし、WSL のディストリビューションとしてデフォルトでインストールしたディストリビューション以外を指定したい場合は、`remote.containers.executeInWSLDistro` にて設定できます。本記事のように `wsl --install` でインストールした Ubuntu であれば特に設定は不要です。
:::

# DevContainer を使ったプロジェクトの作成

それでは、デモ用のプロジェクトを作成していきます。

```bash:bash on WSL
mkdir ~/repos/devcontainer-demo
code ~/repos/devcontainer-demo
```

こちらのコマンドで WSL に接続された状態で VSCode を起動できます。
起動できましたら、コマンドパレット（F1）にて `dev containers` と入力し、出てきた候補から「Dev Containers: Add Dev Container Configuration Files...」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/9994326b-c87a-cbae-74f2-2cfa0c2181a2.png)

次にどの技術スタックの devcontainers を使用するかを聞かれるので、`java` と入力し、「Java devcontainers」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/4f08c1ee-f94d-1b9e-790c-a0474dae4ae2.png)

次の、 Java version は 21-bullseye に選択し、その次のビルドツールは Maven を選択します。次の features の選択は何も選択せずに OK とします（正直、ここで出されても項目が多すぎてわからない）。`.github/dependabot.yml` を追加するかは聞かれますが、ここではチェックをつけずに OK とします。

上記の回答が終わると右下のポップアップで Container として開き直すか聞かれるので、「Reopen in Container」で開きなおします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/4591316f-0030-7c09-8639-d93b7b2898c3.png)

「Opening Remote...」状態になるので、そこからしばらく待つと、 Dev Container でプロジェクトのディレクトリが開かれます。
`.devcontainer/devcontainer.json` が作成されていて、左下のステータスバーが以下のように「Dev Container: Java」と表示されていれば問題ないです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/47009cc4-1c07-8419-4db5-d43ca5f174cb.png)

# `devcontainer.json` ファイルを修正

デフォルト設定のままですと、 Java の拡張機能が何も使えない状態のため、 [Extension for Java Pack](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack) をインストールするために json ファイルを以下のように修正します。

```diff_json:.devcontainer/devcontainer.json
{
	"name": "Java",
	"image": "mcr.microsoft.com/devcontainers/java:1-21-bullseye",
	"features": {
		"ghcr.io/devcontainers/features/java:1": {
			"version": "none",
			"installMaven": "true",
			"installGradle": "false"
		}
	},
+	"customizations": {
+		"vscode": {
+			"extensions": [
+				"vscjava.vscode-java-pack"
+			]
+		}
+	}
}
```

DevContainer で開いている VSCode からでも拡張機能をインストールできますが、 devcontainer.json に記載されていないと、せっかく開発環境を共有しやすい DevContainers のメリットが薄れてしまうので、ここはファイル修正という手段を取っています。

修正後、左下ステータスバーの「Dev Container: Java」をクリックして、コマンドパレットから「Rebuild Container」を選択すれば、拡張機能が入った状態でもう一度プロジェクトが開きなおされます。

:::note info
WSL で開いているときに、既に `.devcontainer/devcontainer.json` ファイルがあれば「Reopen in Container」で Dev Container として開き直しできます。また、逆に Dev Container から WSL で開き直したい場合は、「Reopen Folder in WSL」で可能です。
コンテナの起動状態としては Dev Container で開き直す際に自動でコンテナが開始され、 WSL で開き直した時に、こちらも自動でコンテナが停止されます。
:::

# Java コードの実行

これ以降の開発環境の構築や拡張機能の利用方法は Windows 版の記事と同じになるため、詳しくはそちらをご参照ください。ただ、せっかくなので簡単な動作確認用のコードだけ書いて、本記事を締めたいと思います。

1. pom.xml にて Java のバージョンを 21 に変更
2. 以下の Java コードを書いて実行

```java
public class Main {
    public static void main(String[] args) {
        // Java実行環境の情報
        System.out.println("\n=== Java実行環境情報 ===");
        System.out.println("Javaバージョン: " + System.getProperty("java.version"));
        System.out.println("Javaベンダー: " + System.getProperty("java.vendor"));
        System.out.println("Java VMバージョン: " + System.getProperty("java.vm.version"));
        System.out.println("Java VMベンダー: " + System.getProperty("java.vm.vendor"));
        System.out.println("Java VMname: " + System.getProperty("java.vm.name"));
        System.out.println("Java仕様バージョン: " + System.getProperty("java.specification.version"));
        System.out.println("Java仕様ベンダー: " + System.getProperty("java.specification.vendor"));

        // OS情報
        System.out.println("\n=== OS情報 ===");
        System.out.println("OSの名前: " + System.getProperty("os.name"));
        System.out.println("OSのバージョン: " + System.getProperty("os.version"));
        System.out.println("OSのアーキテクチャ: " + System.getProperty("os.arch"));
    }
}
```

実行結果はこちら。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/0e97c68e-fabd-d331-ed13-707df05d5d6b.png)

これで無事 WSL の Docker 上で実行ができました。

# おわりに

ここまで書いておいてなんですが、Java みたいに環境差異を言語のランタイムでカバーしている実行環境において、 DevContainers は単にモダンな開発環境ではないと思っています。メモリもそれなりに食いますし。