Dev Containers 開発用イメージのバージョンのTagはどれを選べばいい？

# はじめに
開発環境を開発者に提供する時、"×××をインストールして"とか"×××を設定して"とかを各自に同じことをしてもらったり、
他のプロジェクトで開発しているツールやミドルウェアが入っているのに、いちいちそれらを切り替えたり追加で何かをインストールしたくない。

そんな不満へのソリューションの1つに、Visual Studio Codeで利用できるDev Containersがあります。
社内向けアプリの開発で導入したのですが、ベース イメージのバージョン指定(Tag)がよくわからなかったため、まとめてみました。

# Dev Containersとは
Visual Studioの拡張機能とDockerを利用して、開発に特化した環境を構築・再利用できる仕組みです。

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

拡張機能のDev Containersは2023/4/13現在、プレビュー扱いとなっています。

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

# Visual Studio Code 拡張機能での操作
DockerやDev Containers拡張機能はインストールされた状態とします。
何もないディレクトリをカレントにしてVS Code左下にある`><`のような記号の部分をクリックすると、ドロップダウンが現れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116331/87266bba-471c-2400-9138-ca967835fc7d.png)

- *リモート ウィンドウを開くオプションを選択します*
**Reopen in Container** をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116331/475187f2-bba2-996c-6cbc-83b852e1ed42.png)

- *Select a container configuration template*
**Node.js & JavaScript** をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116331/4fcebfcb-7dd0-6afd-ca7c-50b0b198a7a9.png)

- *Node.js version (use-bullseye variants on local arm64/Apple Silicon):*
**18 (default)** をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116331/ba19da4f-2e08-d289-4d20-7a3491feb9f4.png)

- *Select additional features to install*
インストールする追加機能(Azure CLIやTerraform等)を選択し、**OK** をクリックします。
必要がなければ選択する必要はありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116331/9745178b-51e5-ce6d-4d70-d1034360930c.png)

- *Reading Dev Container Configuration* ～ *Starting Dev Container*
しばらく待つと、左下の表示が`>< Dev Container: Node.js`に変わり、`.devcontainer/devcontainer.json`というファイルが作成されます。

```javascript:.devcontainer/devcontainer.json
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node
{
	"name": "Node.js",
	// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
	"image": "mcr.microsoft.com/devcontainers/javascript-node:0-18"

	// Features to add to the dev container. More info: https://containers.dev/features.
	// "features": {},

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	// "forwardPorts": [],

	// Use 'postCreateCommand' to run commands after the container is created.
	// "postCreateCommand": "yarn install",

	// Configure tool-specific properties.
	// "customizations": {},

	// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
	// "remoteUser": "root"
}
```

イメージは、`javascript-node:0-18`になっています。

# バージョン タグについて
Dev Containers拡張機能で選択したものと作成されるdevcontainer.jsonファイルの `image` のバージョン関係は次のようになります。

|Dev Containers|devcontainer.json|
|---|---|
|18|0-18|
|16|0-16|
|14|0-14|
|18-bullseye|0-18-bullseye|
|16-bullseye|0-16-bullseye|
|14-bullseye|0-14-bullseye|
|18-buster|0-18-buster|
|16-buster|0-16-buster|
|14-buster|0-14-buster|

チームでは、Node.jsは18で開発することは決まっている。
Debianは11 bullseyeを使う。
0って何？

という事で、この開発用イメージのドキュメントを見てみました。

https://github.com/devcontainers/images/tree/main/src/javascript-node

> You can also directly reference pre-built versions of `.devcontainer/base.Dockerfile` by using the `image` property in `.devcontainer/devcontainer.json` or updating the `FROM` statement in your own `Dockerfile` with one of the following:
> 
> - `mcr.microsoft.com/vscode/devcontainers/javascript-node` (latest)
> - `mcr.microsoft.com/vscode/devcontainers/javascript-node:18` (or `18-bullseye`, `18-buster` to pin to an OS version)
> - `mcr.microsoft.com/vscode/devcontainers/javascript-node:16` (or `16-bullseye`, `16-buster` to pin to an OS version)
> - `mcr.microsoft.com/vscode/devcontainers/javascript-node:14` (or `14-bullseye`, `14-buster` to pin to an OS version)
> 
> You can decide how often you want updates by referencing a [semantic version](https://semver.org/) of each image. For example:
> 
> - `mcr.microsoft.com/vscode/devcontainers/typescript-node:0-16` (or `0-16-bullseye`, `0-16-buster`)
> - `mcr.microsoft.com/vscode/devcontainers/typescript-node:0.204-16` (or `0.204-16-bullseye`, `0.204-16-buster`)
> - `mcr.microsoft.com/vscode/devcontainers/typescript-node:0.204.0-16` (or `0.204.0-16-bullseye`, `0.204.0-16-buster`)
> 
> However, we only do security patching on the latest [non-breaking, in support](https://github.com/microsoft/vscode-dev-containers/issues/532) versions of images (e.g. `0-16`). You may want to run `apt-get update && apt-get upgrade` in your Dockerfile if you lock to a more specific version to at least pick up OS security updates.
> 
> See [history](history) for information on the contents of each version and [here for a complete list of available tags](https://mcr.microsoft.com/v2/vscode/devcontainers/typescript-node/tags/list).

日本語訳(by Google):
> `.devcontainer/devcontainer.json`の`image`プロパティを使用するか、独自の`Dockerfile`の`FROM`ステートメントを次のいずれかで更新することにより、`.devcontainer/base.Dockerfile`のビルド済みバージョンを直接参照することもできます。
> - `mcr.microsoft.com/vscode/devcontainers/javascript-node` (最新)
> - `mcr.microsoft.com/vscode/devcontainers/javascript-node:18` (または OS バージョンに固定するための `18-bullseye`、`18-buster`)
> - `mcr.microsoft.com/vscode/devcontainers/javascript-node:16` (または OS バージョンに固定するための `16-bullseye`、`16-buster`)
> - `mcr.microsoft.com/vscode/devcontainers/javascript-node:14` (または OS バージョンに固定するための `14-bullseye`、`14-buster`)
> 
> 各イメージのセマンティック バージョンを参照することで、更新頻度を決定できます。例えば、
> 
> - `mcr.microsoft.com/vscode/devcontainers/typescript-node:0-16` (または `0-16-bullseye`, `0-16-buster`)
> - `mcr.microsoft.com/vscode/devcontainers/typescript-node:0.204-16` (または `0.204-16-bullseye`, `0.204-16-buster`)
> - `mcr.microsoft.com/vscode/devcontainers/typescript-node:0.204.0-16` (または `0.204.0-16-bullseye`, `0.204.0-16-buster`)
> ただし、セキュリティ パッチは、サポート バージョンのイメージ (例: `0-16`) で、最新の非破壊的なものにのみ適用されます。 少なくとも OS セキュリティ アップデートを取得するために、より具体的なバージョンにロックする場合は、Dockerfile で「apt-get update && apt-get upgrade」を実行することをお勧めします。

`0-18-bullseye`と`18-bullseye`の違いがわからない...
[タグ](https://mcr.microsoft.com/v2/vscode/devcontainers/javascript-node/tags/list)を見てみると

```javascript
{
  "name": "vscode/devcontainers/javascript-node",
  "tags": [
    // ...
    "0-14",
    "0-14-bullseye",
    "0-14-buster",
    "0-14-stretch",
    "0-16",
    "0-16-bullseye",
    "0-16-buster",
    "0-18",
    "0-18-bullseye",
    "0-18-buster",
    "0-bullseye",
    "0-buster",
    // ...
    "0.204.19-14",
    "0.204.19-14-bullseye",
    "0.204.19-14-buster",
    "0.204.19-16",
    "0.204.19-16-bullseye",
    "0.204.19-16-buster",
    "0.204.19-18",
    "0.204.19-18-bullseye",
    "0.204.19-18-buster",
    "0.204.19-bullseye",
    // ...
    "0.204.20-14",
    "0.204.20-14-bullseye",
    "0.204.20-14-buster",
    "0.204.20-16",
    "0.204.20-16-bullseye",
    "0.204.20-16-buster",
    "0.204.20-18",
    "0.204.20-18-bullseye",
    "0.204.20-18-buster",
    "0.204.20-bullseye",
    "0.204.20-buster",
    // ...
    "14",
    "14-bullseye",
    "14-buster",
    "14-stretch",
    "16",
    "16-bullseye",
    "16-buster",
    "18",
    "18-bullseye",
    "18-buster",
    "bullseye",
    "buster",
    // ...
    "latest"
  ]
}
```

そしてここにヒントが

https://github.com/devcontainers/images/blob/main/src/javascript-node/history/0.204.20.md

> **Tags:**
> ```
> mcr.microsoft.com/devcontainers/javascript-node:0.204.20-18-bullseye
> mcr.microsoft.com/devcontainers/javascript-node:0.204.20-18
> mcr.microsoft.com/devcontainers/javascript-node:0.204.20-bullseye
> ```
> > *To keep up to date, we recommend using partial version numbers. Use the major version number to get all non-breaking changes (e.g. `0-`) or major and minor to only get fixes (e.g. `0.200-`).*

# つまり、

- `javascript-node:0.204.20-18-bullseye`のタグの読み方は、
  - `0.204.20` : 開発用イメージ(Node.js & JavaScript)のバージョン
  - `18` : Node.jsのバージョン
  - `bullseye` : Debianのバージョン

そして、いずれかを省略する場合

- `0.204.20-18`
  - 開発用イメージ : 0.204.20
  - Node.js : 18
  - Debian : *latest*

- `0.204.20-bullseye`
  - 開発用イメージ : 0.204.20
  - Node.js : *latest*
  - Debian : bullseye

- `0.204-18-bullseye`
  - 開発用イメージ : 0.204.*latest*
  - Node.js : 18
  - Debian : bullseye

- `0-18-bullseye`
  - 開発用イメージ : 0.*latest*.*latest*
  - Node.js : 18
  - Debian : bullseye

- `18-bullseye`
  - 開発用イメージ : *latest*
  - Node.js : 18
  - Debian : bullseye

# ちなみに
- `0.204-18-bullseye`
  - 開発用イメージ : 0.204.*latest*
  - Node.js : 18
  - Debian : bullseye

- `0.204.18-bullseye`
  - 開発用イメージ : 0.204.18
  - Node.js : *latest*
  - Debian : bullseye

↑のように、ハイフンとピリオドで区切り位置が変わります。
そして、

- `0-bullseye`
  - 開発用イメージ : 0.*latest*.*latest*
  - Node.js : *latest*
  - Debian : bullseye

`18-bullseye`が Node.js : 18 なので`0-bullseye`なら Node.js : 0 になるのでは？
とか開発用イメージのバージョンが18まで上がっていたらどうするのだろうとか
へそ曲がりな事を思いましたが、当然違います。

Gitリポジトリ上でのTagは開発用イメージのバージョンではなくDev Containers全てのバージョンなので、混乱しました。

Rubyとかも同じような形式でした。

https://github.com/devcontainers/images/blob/main/src/ruby/history/0.203.21.md