Dev container features (preview) が便利すぎる

VScode Remote Containers の新機能 Dev container features について紹介します。

## VSCode Remote Containers とは

Docker コンテナを開発環境として使用するための VSCode 拡張機能です。

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

コンテナ内に開発環境を構築することで、PC の環境を汚さずにプロジェクトごとに言語やツールを使い分けることができます。
また、Dockerfile で開発環境を定義できるためチーム内で開発環境を共有することが容易になります。

Qiita 等に紹介記事がたくさんあるため詳しい説明や使い方については割愛します。

## Dev container features とは

9月のアップデート ([VSCode v1.61](https://code.visualstudio.com/updates/v1_61#_remote-development), [Remote - Containers v0.202](https://github.com/microsoft/vscode-docs/blob/main/remote-release-notes/v1_61.md#containers-version-0202x)) で追加された新機能 ~~(preview)~~ です。
**2022-11-24 追記: 現在は preview ではありません。**

https://code.visualstudio.com/docs/remote/containers#_dev-container-features

開発用コンテナ (以下 DevContainer と呼びます) に追加の機能 (feature) のセットアップを簡単に行えるというものです。
例えば、よく使われる CLI や言語のインストールを行うことができます。

上記のドキュメントに記載されている通り、`> Remote-Containers: Add Development Container Configuration Files` で新規の DevContainer を作成する際にチェックボックスで有効にする features を選択することができるようになっています。

作成済みの DevContainer の場合は `devcontainer.json` に次のような数行の設定を書くことで features を利用できます(設定の変更後は DevContainer の Rebuild が必要です)。

```json:devcontainer.json
{
    "features": {
        "golang": "latest"
    }
}
```

## feature 紹介

利用できる feature のうち、よく使いそうなものを簡単に紹介します。
**2022-11-24 追記: 以下で紹介している features は Dev Container 仕様のオープン化に伴い古くなっています。
今後は [こちら](https://containers.dev/features) の一覧にある features を使うことが推奨されます。**

### 最新の Git を使えるようにする

DevContainer では Debian 系の OS をベースにすることが多い (多くの言語の公式イメージが Debian ベースであるため) と思いますが、Debian では古いバージョンの Git しか利用できず困った方が多いのではないでしょうか。

そんな時は `git` feature を使うことでソースからビルドした Git を簡単に利用することができます。

```json:devcontainer.json
{
    "features": {
        "git": {
            "version": "latest"
        }
    }
}
```

https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/git-from-src.md

### Node.js をインストールする

Web アプリケーション開発ではバックエンドで使用するメインの言語に加えてフロントエンド用に Node.js もインストールしたいというケースが結構あると思います。

`node` feature を利用すれば簡単にセットアップが可能で、バージョン指定も行えます。

```json:devcontainer.json
{
    "features": {
        "node": {
            "version": "lts"
        }
    }
}
```

https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/node.md

### DevContainer の中で Docker CLI を利用できるようにする

`docker-from-docker` feature で簡単にセットアップできます。
ただし DevContainer 内での Docker CLI の利用にはいくつか注意すべき点があるので[こちらの記事](https://qiita.com/frozenbonito/items/776baeea9b26079a2add)も参照してみてください。

```json:devcontainer.json
{
    "features": {
        "docker-from-docker": {
            "version": "latest"
        }
    }
}
```

### その他

[GitHub CLI](https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/github.md) や [kubectl + helm + minikube](https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/kubectl-helm.md)、[terraform](https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/terraform.md) など定番の CLI も feature でセットアップ可能です。

## 利用できる feature の探し方

Development Container Scripts (後述) の一覧から探すことができます。

https://github.com/microsoft/vscode-dev-containers/tree/main/script-library#scripts

各 script のドキュメントに `Feature use` の記載があれば feature が利用可能です。

**2022-11-24 追記: 前述の通り Dev Container 仕様のオープン化に伴い上記のリポジトリはアーカイブ化される予定です。次の一覧から探すと良いでしょう。**

https://containers.dev/features

## Development Container Scripts との比較

従来は DevContainer に追加する言語や CLI のセットアップには Development Container Scripts を利用するのが一般的でした。

https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/README.md

これは [microsoft/vscode-dev-containers](https://github.com/microsoft/vscode-dev-containers) リポジトリの [script-library](https://github.com/microsoft/vscode-dev-containers/tree/main/script-library) ディレクトリ以下で配布されているスクリプトで、Dockerfile から呼び出すことによって、DevContainer に追加の言語や CLI などをセットアップすることができるというものです。

公式で定義された DevContainer を利用すると、ものによっては `.devcontainer/library-scripts/` 以下に Development Container Scripts がコピーされることがあるので意識せずに利用しているケースもあると思います。

新機能の Dev container features は Development Container Scripts とほぼ同一のもので、多くの Development Container Scripts が features としても利用可能になっています。

利用者目線での両者の違いを簡単にまとめてみました。

|                            | Development Container Scripts           | Dev container features              |
| -------------------------- | --------------------------------------- | ----------------------------------- |
| 設定                       | 複雑                                    | 簡単                                |
| スクリプトの管理           | 必要                                    | 不要                                |
| スクリプトのバージョン固定 | 可能                                    | 不可能                              |
| カスタマイズ性             | 高い (多くの引数をサポート、改造も可能) | 低い (利用可能なオプションが少ない) |

一番のポイントは **「スクリプトそのものを管理する必要があるかどうか」** という点です。

Development Container Scripts の場合、一般的にはローカルにスクリプトをダウンロードし、Dockerfile で `COPY`, `RUN` して利用します。
問題なのはこのローカルに保存したスクリプトを更新したくなることがあり、その作業が結構面倒だということです。
(私の実体験として、ruby のセットアップを行う [ruby-debian.sh](https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/ruby-debian.sh) が[壊れてしまい](https://github.com/microsoft/vscode-dev-containers/issues/908)、更新せざるを得なくなったことがあります)

Dev container features の場合、必要なのは devcontainer.json の設定だけでスクリプトそのものを保持しておく必要がないため、面倒な更新作業を行う必要はありません。
一方で使用されるスクリプトのバージョンを明示的に指定することはできないため、ある日突然壊れてしまった場合に古いバージョンに固定するなどの対応はできないというデメリットはあります。

また、カスタマイズ性という点においては Development Container Scripts の方に軍配が上がります。
Dev container features は preview ということもあってか、現時点では利用可能なオプションが Development Container Scripts に比べて少ないものが多いようです。