VSCodeのDev Containersが進化してる

![MYGwDpKWB37C8LsD-700D2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577076/14195801-e08b-5a40-577f-2fca09860b53.png)


## はじめに

こんにちは、foo_7235492と申します。
今回は、私が勤めている、株式会社ヌーラボの企画、[ヌーラボブログリレー2024 for Tech Advent Calendar 2024](https://adventar.org/calendars/10356)に参加いたしました。

この記事では、Visual Studio Code（以下、VSCode）の拡張機能の一つ、[Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)についてご紹介します。これは、私が日々の開発で非常に便利だと感じているツールで、色んな言語で開発を行う方に役に立つと思います。

## なぜDev Containersを使うのか

私がDev Containersを使い始めたきっかけは、VSCodeの起動に時間がかかるようになったことでした。当時、拡張さえ入れればあらゆる言語での開発が可能という、VSCodeの汎用性に感動し、多くの拡張機能をインストールして使い込んでいました。

### VSCodeの利点と課題  
VSCodeは、インストール直後はあくまで軽量なテキストエディタなため動作が軽く、必要な機能は拡張機能として追加できます。この仕組みによって、使用する言語や用途に応じて自由にカスタマイズできるのが最大の魅力です。しかし、その一方で、多くの拡張機能をインストールすると、起動時間や動作が重くなるという課題に直面しました。

### 開発環境もコンテナ化したい
当時、私はDockerを触り始めており、「コンテナのように、VSCodeの拡張機能もプロジェクトごとに切り替えられたら便利なのに」と感じていました。そんな時、リリースされたのが「Dev Containers」でした。

## Dev Containersとは？

Dev Containersは、開発時に専用のDockerコンテナを立ち上げ、その中でVSCodeを使って開発を行えるようにする拡張機能です。この機能を使うことで、ランタイムやパッケージマネージャーなどのツールに加え、VSCodeにインストールする拡張機能もプロジェクトごとに個別に設定可能になります。

## 何が嬉しい？

例えば以下のような悩みに対しては有効です。

- **新しいPCを買ったから開発環境を0から構築するのが面倒**：コード化されているのでVSCodeをインストールしたら`git clone`してコンテナをビルドするだけで構築完了します。
- **このプロジェクトでこの拡張機能使わないし、重くなるから無効にしたいけど、毎回切り替えるの面倒**：各プロジェクトで必要な拡張などを設定ファイルに記述し、コンテナがビルドされる時にインストールされるので、ホスト側のVSCodeには最低限の拡張以外は入れる必要ありません。
- **言語のバージョンを上げたから、チーム全体で共有したい**：コンテナの設定を書き換えてpushすれば、あとは各自でpullしてリビルドするだけで大丈夫です。

## 使い方

1. 拡張をインストールして、以下の様な設定を書きます。
    ```JSON:[プロジェクトのディレクトリ]/.devcontainer/devcontainer.json
    {
      "name": "container-name", // コンテナの名前
      "image": "mcr.microsoft.com/devcontainers/base:bullseye", // 使用するベースイメージ
      "remoteUser": "vscode", // コンテナ内でVSCodeを実行するユーザー
      "remoteEnv": {
        "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" // ローカルのワークスペースフォルダを指定
      },
      "mounts": [], // Volumeマウントの設定
      "features": { // コンテナに追加したい機能の設定(後述)
        "ghcr.io/devcontainers/features/git:1": {} // git
      },
      "customizations": { // コンテナ内のVSCodeに適用するエディタなどの設定
        "vscode": {
          "extensions": [ // コンテナ内のVSCodeにインストールする拡張機能
            "esbenp.prettier-vscode"
          ]
        }
      }
    }
    ```
2. F1を押して`Open Folder in Container`を選択し、プロジェクトのディレクトリを選択
3. コンテナのビルドが走ってVSCodeのウィンドウが出てきます。
   ![スクリーンショット 2024-12-10 17.55.30.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577076/14f73c5b-a1ea-217b-5a89-09853c1cd72a.jpeg)

開いたVSCodeはもうコンテナの中です。下部のターミナルもコンテナの中で動いています。
あとは、プロジェクトに応じて初期化コマンドを実行したりしてソースファイルを書いていくだけです。

特定のツールやライブラリを含んだ独自の環境が必要な場合、Dockerfileを使ってカスタムイメージを作成することもできます。

```dockerfile:.devcontainer/Dockerfile
FROM mcr.microsoft.com/vscode/devcontainers/base:0-ubuntu-22.04

ARG USER

RUN adduser ${USER} \
 && echo "${USER} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

USER ${USER}
```
```json:.devcontainer/devcontainer.json
{
    ...
	"build": { // imageの代わりにbuildを使う
		"dockerfile": "Dockerfile",
		"args": {
			"USER": "${localEnv:USER}"
		}
	},
    ...
}
```

## 何が進化したのか？

さて、では主題にある何が進化したのかについてです。VSCodeも然りなのですが、この拡張機能は日々進化していて、苦労したポイントがどんどん解消されて行ってます。

### 設定構築の簡略化

先程の説明を聞いて、「設定ファイル書くのが面倒」だと思った方もいると思います。私もそうです。面倒くさかったです。
しかし、アップデートによって現在は初期設定を自動で構築できるようになりました。
1. F1キーを押してコマンドパレットを開きます。
2. 「Add Development Container Configuration Files」と入力して選択します。
3. 設定テンプレートを選ぶだけで、自動的に`.devcontainer/devcontainer.json`が作成されます。
![スクリーンショット 2024-12-10 18.33.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577076/5ef8cc50-9125-4d55-0d73-e059e0c63a70.png)

上記の手順を進めると、このようにテンプレートが表示されるので、ベースイメージや追加したい`features`を選ぶだけで初期設定が作成されます。
これにより、ゼロから設定ファイルを書く必要がなくなり、初めてでも簡単に使い始めることができます。

### 権限周りの設定

以前、コンテナで作成したファイルがホスト側で編集できないことがありました。コンテナのUID（ユーザーID）とホストのUIDが異なっていて、権限が合わないことで発生していました。
この問題を解消するために、当時はDockerfileに頑張ってUSERを作成する処理を書いていたのですが、現在は`devcontainer.json`にこの設定を有効にするだけでその問題は解消されます。

```json:.devcontainer/devcontainer.json
{
    "updateRemoteUserUID": true
}
```
むしろ、この設定はデフォルトで`true`になっているので、現在は何もしなくてもこの問題が発生しなくなりました。万歳。

### 便利構成がfeaturesとして提供されるようになった

VSCodeの設定だけではなく、コンテナ内にインストールするツールや環境に関しても、`features`としてパッケージで提供されるようになりました。

#### 例1:Docker-in-Docker
開発している時、動作確認のためにMySQLやRedisといったサービスをコンテナで起動したいことがよくあると思います。
これをDocker-in-Docker構成でやる場合、以前はDockerfileにDockerのインストール処理を書いたり、Dockerデーモンを起動させたりしないといけませんでした。
現在、その構成は`features`にパッケージとして提供されているので、以下のように`features`を追加すればできます。
```json:.devcontainer/devcontainer.json
{
    ...
    "features": {
        "ghcr.io/devcontainers/features/docker-in-docker:1": {}
    }
    ...
}
```
#### 例2:node
バックエンドとフロントエンドで異なる言語を使用する場合、以下のようにfeaturesを使えば、簡単にNode.jsを追加できます。
```json:.devcontainer/devcontainer.json
{
    ...
    "features": {
        "ghcr.io/devcontainers/features/node:1": {
            "version": "16"
        }
    }
    ...
}
```
このように、featuresを活用することで、従来の面倒だった設定が大幅に簡略化されました。次は、実際に私がよく使う設定についてご紹介します。

## よく使う設定

ここで、私がよく使う`features`や`customizations`の設定をご紹介します。
```json:.devcontainer/devcontainer.json
{
    "name": "container-name", // コンテナの名前
    "image": "mcr.microsoft.com/devcontainers/base:bullseye", // 使用するベースイメージ
    "remoteUser": "vscode", // コンテナ内でVSCodeを実行するユーザー
    "remoteEnv": {
        "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" // ローカルのワークスペースフォルダを指定
    },
	"mounts": [
		"source=${localEnv:HOME}/.aws,target=/home/vscode/.aws,type=bind,consistency=cached" // AWSの設定共有
	],
	"features": {
		"ghcr.io/devcontainers/features/docker-outside-of-docker:1": { // Docker-outside-of-Dockerの構成
			"version": "latest",
			"enableNonRootDocker": "true",
			"moby": "true"
		},
		"ghcr.io/devcontainers/features/aws-cli:1": {} // AWS CLIのインストール
        // 言語ごとの追加構成
	},
	"customizations": {
		"vscode": {
            "settings": {
                "editor.tabSize": 2,
                "editor.insertSpaces": true
            },
			"extensions": [
				// 言語ごとの便利拡張
			]
		}
	},
}
```
昔はもっとゴテゴテ書いていたのですが、今は開発コンテナ用のコンテナイメージがたくさん提供されていて、Gitなどはすでに入っていますし、大体は`features`で解決するので、あまり初期設定から追加する必要性は減りました。

### 構成のポイント

#### Docker-outside-of-Docker
```json:.devcontainer/devcontainer.json
{
    ...
    "features": {
        "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { // Docker-outside-of-Dockerの構成
            "version": "latest",
            "enableNonRootDocker": "true",
            "moby": "true"
        },
    },
    ...
}
```
コンテナ内のDockerが、ホストのDockerエンジンを使うようにする設定です。
`Docker-in-Docker`より、ホストから各コンテナへのアクセスの利便性や、複数コンテナ間の統合が容易になるため、こちらを好んで使用しています。

#### AWS CLI
```json:.devcontainer/devcontainer.json
{
    ...
    "mounts": [
        "source=${localEnv:HOME}/.aws,target=/home/vscode/.aws,type=bind,consistency=cached" // AWSの設定共有
	],
    ...
    "features": {
		"ghcr.io/devcontainers/features/aws-cli:1": {} // AWS CLIのインストール
    },
    ...
}
```
AWSへのデプロイや、開発用の互換サービスとの接続に利用しています。`.aws`ディレクトリをマウントすることで、ホストと設定を共有することもできます。

#### エディタ設定
インデントサイズはとりあえず揃えて置きたいですね。この辺りはLinterで十分という方はいるかもしれません。設定に関してはVSCodeにある設定は大体書けると思います(多分)。
```json:.devcontainer/devcontainer.json
{
	"customizations": {
		"vscode": {
            "settings": {
                "editor.tabSize": 2,
                "editor.insertSpaces": true
            },
		}
	},
}
```

他にも様々な拡張機能があるため、興味のある方は[Available Dev Container Features](https://containers.dev/features)に、現在利用可能な`features`の一覧が載っているので、自分の環境にマッチしたものがあるか、探してみるといいかもしれません。

## まとめ

Dev Containersが便利になったおかげで、新しいプロジェクトを始める際の開発環境構築が驚くほど簡単になりました。煩雑だった設定がシンプルに整理され、チームでの環境共有も容易です。
今後もDev Containersはさらに進化していくと思います。これを機会に、ぜひ一度触ってみてください。