Visual Studio Code Remote - Containersで開発環境をコンテナ化する

## はじめに

Visual Studio Codeの拡張機能`Remote-Containers`を利用して開発環境をコンテナ化するためのテンプレートプロジェクトの様な物を作りました。公式ドキュメントを見てもいまいち設定の全体像が見えてこないので、とりあえず動くものがあると良いかなと考えました。

とりあえず動くものを作ったという感じなのでRemote-Containersの詳細な使い方やカスタマイズは公式ドキュメントをご参照ください。

https://code.visualstudio.com/docs/remote/containers

### Java Spring Bootアプリケーション

https://github.com/rltnm7/devboot

### Python Flaskアプリケーション

https://github.com/rltnm7/devpython

(Flaskについてはド素人なのでアプリケーションの出来はあまり気にしないでください笑)

## 開発環境のコンテナ化

### そもそも開発環境をコンテナ化するとは？

VSCodeにはリモート開発機能として、IDEはホストマシンで実行したまま、デバッグやビルド、ユニットテストの実行はリモートサーバで実行できる仕組みがあります。そのリモートサーバとして自身のPC上で稼働しているDockerコンテナを利用する開発スタイルになります。
メリット・デメリットを簡単にまとめてみました。

### メリット

- 開発環境の標準化
  - すべてリポジトリ内のテキストベースでの定義ファイルで環境定義が可能
  - コンテナ定義などでバージョン指定が容易のため、各種バージョンの統一が容易
- 開発環境構築の容易性
  - VSCodeとDockerのみインストールすれば、各開発者の端末上に標準化された環境の展開が可能
  - 開発者にミドルウェア等の知識がなくても各開発者専用のDB等も用意することが出来る

### デメリット

- ビルド時間の長時間化
  - コンテナオーバーヘッドにより、`mvn test`などホストマシンで実行する場合より完了時間が遅くなる
  - 個人開発ではメリットは少ないかもしれない

## テンプレートプロジェクトの使い方

本題です。
前述の[Java Spring Bootアプリケーション](https://github.com/rltnm7/devboot)をベースに解説したいと思います。実行後のアプリケーション構成については本記事の対象外させていただくため、[README.md](https://github.com/rltnm7/devboot/blob/main/README.md)をご参照ください。

### 利用するために

- Visual Studio Codeのインストールされていること
- Visual Studio Codeには拡張機能`Remote Development`が導入されていること
- Docker Desktopのインストールされていること (※1)

※1 Docker Desktopは2021/8/31より一部条件下を除き有償化されているのでご注意ください。

https://www.docker.com/blog/updating-product-subscriptions/

### ファイル構成

Remote Containersを利用するための定義ファイルは以下になります。

```
.devcontainer
  ├ devcontainer.json
  ├ docker-compose.yml
  └ Dockerfile
```

#### `Dockerfile`

開発に利用するコンテナイメージ定義になります。このコンテナにログインして開発を行うことになるため、開発に必要なツールなどを導入すると良いと思います。
テンプレートでは`openjdk`の17のイメージをベースに、`git`と`maven`を追加しています。

#### `docker-compose.yml`

開発サーバだけでなく、データベースなどのその他ミドルウェアを実行するコンテナも一緒に定義できます。テンプレートではPostgreSQLをデータベースとして追加しています。必要に応じてApacheやNginx、Redisなどを追加するのも良いと思います。

#### `devcontainer.json`

Remote Containersのメイン定義ファイルです。
port設定などテンプレートに記載していない設定値も、`Dockerfile`や`docker-compose.yml`での定義よりも優先されるみたいです。

| 設定値            | 概要                                                                                                 |
| ----------------- | ---------------------------------------------------------------------------------------------------- |
| dockerComposeFile | 利用する`docker-compose.yml`ファイルのパス                                                           |
| service           | VSCodeでターミナル等からログインするコンテナ。`docker-compose.yml`で指定したサービス名を指定する。   |
| workspaceFolder   | VSCodeでターミナル等からログインした際に最初に開くディレクトリ。                                     |
| shutdownAction    | VSCodeからリモートコンテナを切断した際のコンテナの動作を指定する。正直詳細な動作はよく分かってない。 |
| extensions        | 利用する拡張機能。後述。                                                                             |
| settings          | 利用するVSCode設定。後述。                                                                           |

##### `devcontainer.json`の`extensions`

一部はホスト側で利用している拡張機能がコンテナ内でも引き継がれます。
ただし明示的に設定しないと引き継がれないものがあるので、本項ではそれを配列形式で記載します。
記載するのは配列のIdentifier (識別子) です。各拡張機能の画面から確認できます。2022/2現在は拡張機能詳細画面の右ペイン一番下に記載がありますが、画面配置はたまに変更されるのでご注意ください。

```json
    "extensions": [
        // Extension Pack for Java
        "vscjava.vscode-java-pack",
        // Spring Boot Extension Pack
        "pivotal.vscode-boot-dev-pack",
        // Lombok Annotations Support for VS Code
        "gabrielbb.vscode-lombok",
    ]
```

テンプレートでは引き継がれるものも、導入するものを明記するという意味合いで指定しています。

##### `devcontainer.json`の`settings`

VSCodeの基本設定 (`settings.json`) の追加・上書きを行うことができます。
分かりやすいところだと`JAVA_HOME`の上書きなどを行えます。またフォーマッタなど開発者に強制したい設定などもここに記載すると良いと思います。
記載方法は`settings.json`と同じです。

### 実行手順

1. VSCodeの左下から以下アイコンをクリックし、`Open Folder in Container...`を選択  
　　![remote_container_icon.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500177/fce6cc4b-e577-8835-33e8-c18a90d73245.jpeg)
2. `devboot`ディレクトリを選択する
3. コンテナのビルドが開始されるので、しばらく待つ
4. VSCode内でコンテナ上のディレクトリが開かれる

以上でコンテナ上での開発が可能になります。
試しにVSCodeのターミナルから`./mvnw spring-boot:run`を実行し、ホスト側のブラウザで`http://localhost/users`にアクセスすると、コンテナ内で実行しているアプリケーションにアクセス出来ると思います。
アプリケーション自体で何か特殊なことをしているわけではないので、ソースコードなどは全面的に入れ替えても問題なく動作します。

## その他

リポジトリ上ではGitHub ActionsやSonarCloudを使って色々遊んでしまいましたが、Remote-Containersとは全く関係ないので、関連する設定は消してもらって大丈夫です。

- `.devcontainer/devcontainer.json`内の`settings`にある`sonarlint.connectedMode.project`
- `.github/workflows/*`
- `sonar-project.properties`