【開発環境コンテナ化】Dev Containersで生産性向上!

## 最新版はこちら

https://qiita.com/sigma_devsecops/items/9e9e703f418d73dfa4c2

## 記事を書いた理由
- リポジトリはこちら[devcontainer-test](https://github.com/RyosukeDTomita/devcontainer-test)
- Dev Containersを開発現場で導入するにあたって各種機能を試してナレッジが溜まったのでDev Containers初心者の方の役に立つのでは?と思って記事を執筆しました。

---

## Dev Containersとはなにか

コンテナ内の環境をVSCodeで維持れるようになります。
ssh先でVSCodeを使うやつのコンテナバージョンでより便利な機能がたくさんあるよ〜くらいのイメージを持っていただければと思います。

### Dev Containersを使うメリット

コンテナを使うことでチームで同じ開発環境を使うことができますが，Dev Containersを使うことでより同じ開発環境を使うことができ，新規開発者の参画のスピードアップが図れます。
- VSCodeのExtensionsをDev Containers使用時に自動インストール＆set upできる
→Formatterやlinter，その他必要なExtensionsのセットアップを自動化できる。運用でExtensionsを導入してもらうのではなく，仕組みとしてExtensionsをチーム全体で使用できる。
- Dockerfile外でツールのインストールができる
→Dockerfileを汚さずに，使用するデバックツール等をコンテナで管理できる。
- Dev Containers用のcompose.yamlを配置して使えるのが結構便利
→開発用のcompose.yamlを使ってビルドできるので本番用のcompose.yamlを汚さずにすむ。

---

## 作成した検証用リポジトリで学べること

検証用リポジトリ→[devcontainer-test](https://github.com/RyosukeDTomita/devcontainer-test)

### dotfilesを使って開発環境に必要な設定ファイルを持ち込む

- ~/.bashrc等をDev Containersに持ち込む機能。
- **VSCodeの個人用のsettings.json**に記載する。
- 設定は自分のGitHub上で管理している`dotfiles`リポジトリに合わせて変更すること。

```json
  // Dev Containersでdotfilesを使う。
  // NOTE: .gitconfigは何もしなくてもDev ContainersのHOMEディレクトリにコピーされる。
  "dotfiles.repository": "https://github.com/RyosukeDTomita/dotfiles.git",
  "dotfiles.targetPath": "~/dotfiles",
  "dotfiles.installCommand": "install.sh", // ~/dotfilesのrepository topからみたスクリプトのパスを指定する。
```

> [!NOTE]
> `dotfiles.installCommand`には~/dotfilesのrepository topから見たスクリプトのパスを指定する。--> dotfilesのGitHub Repositoryにinstall.shは配置が必要。
> 指定しない場合のデフォルトファイル名は以下が認識される。
> - install.sh
> - install
> - bootstrap.sh
> - setup.sh
> - setup

---

### Extensions関連

#### 拡張機能の設定ファイルをどこにおくか
- devcontainer.jsonと.vscode/settings.json両方に記載ができる。
- 基本は.vscode/settings.json配下で良いと思っているが一部コンテナ名等を使う場合にはdevcontainer.jsonに記載したほうが楽かも。

#### Extensionsを追する方法

例えば，[peacock](https://marketplace.visualstudio.com/items?itemName=johnpapa.vscode-peacock)をインストールしたい場合には，インストールコマンド`ext install johnpapa.vscode-peacock`にて記載される`johnpapa.vscode-peacock`をdevcontainer.jsonに記載すればよい。

#### DevContainerに自分だけが使用するExtensionsを持ち込む

- **個人用のsettings.json**に記載する。.vscode/settings.jsonではないので注意。

```json
  // Devcontainerの個人的に使うExtensionsを入れる。
  "dev.containers.defaultExtensions": [
    "formulahendry.auto-rename-tag",
  ],
}
```

---

### Dockerfileのマルチステージビルドを使ってDevContainer用の環境を作る

- Dockerfileを使う場合にはdevcontainer.jsonでtargetの指定ができる。
- 一方，compose.yamlを使う場合には別のcompose.yamlを準備する必要がある。

> [!NOTE]
> compose.yamlの優先順位は.devcontainer/compose.yamlが優先される。

```yaml
# for devcontainer
version: '3'

services:
  react-app:
    build:
      target: devcontainer // targetを指定
      context: ./
      dockerfile: Dockerfile
    image: react-img-devcontainer:latest
    container_name: react-container-devcontainer
    ports:
      - 80:8080 # localport:dockerport
```

```
# DevContainer
FROM node:20-bullseye AS devcontainer # targetに指定する値
WORKDIR /app
COPY . .
RUN cd react-default-app && npm install

# Build Image
FROM node:20-bullseye AS build
WORKDIR /app
COPY ./react-default-app .
RUN npm install && npm run build
```

> [!NOTE]
> - compose.yamlのサービス名をdevcontainer.jsonのserviceを同じにする必要がある。

```yaml
# compose.yaml
version: '3'

services:
  react-app: # devcontainer.jsonに指定するサービス名
    build:
      target: devcontainer
      context: ./
      dockerfile: Dockerfile
    image: react-img-devcontainer:latest
    container_name: react-container-devcontainer
    ports:
      - 80:8080 # localport:dockerport
```

```json
{
  "name": "dev-container-test", // 任意の名前
  "dockerComposeFile": [
    "../compose.yaml",
    "compose.yaml"
  ],
  "service": "react-app", // compose.yamlのサービス名
```

> [参考1](https://github.com/microsoft/vscode-remote-release/issues/7810)
> [参考2](https://stackoverflow.com/questions/78421879/devcontainer-docker-compose-best-practice)

---

### 各種コマンドの使い方

```json
{
  "name": "dev-container-test",
  "dockerComposeFile": [
    "../compose.yaml",
    "compose.yaml"
  ],
  "service": "react-app",
  "workspaceFolder": "/app",
  "overrideCommand": true, // コンテナを起動したままにする DockerfileのCMDで永続するコマンドを実行しているなら不要
  // rvest.vscode-prettier-eslintに使うパッケージをinstall
  "postCreateCommand": "yarn add -D prettier@^3.1.0 eslint@^8.52.0 prettier-eslint@^16.1.2 @typescript-eslint/parser@^5.0.1 typescript@^4.4.4",
  "postStartCommand": ["echo", "Hello, DevContainer"], // DevContainer起動時
  "postAttachCommand": ["echo", "Hello, DevContainer"], // DevContainerに既存コンテナをattach時
  "initializeCommand": ["echo", "Starting DevContainer..."], // DevContainerのビルド前，実行前にローカルで実行されるコマンド
```

#### installスクリプトを使う

必要そうなパッケージをまとめた`install-pkg.sh`を作成し，postCreateCommandで実行する。

---

### ディレクトリのマウント

- ファイル単体でのマウントはできないぽい。設定ファイルとかは前述のdotfilesを使って共有する。

```json
{
  "name": "dev-container-test",
  "dockerComposeFile": [
    "../compose.yaml",
    "compose.yaml"
  ],
  "service": "react-app",
  "workspaceFolder": "/app",
  "overrideCommand": true,
  "mounts": [
    "source=~/.aws,target=/home/root/.aws,type=bind" // ~/.awsをマウントできる
  ],
```

---

## 参考: Dev Containers関連の記事
https://qiita.com/sigma_devsecops/items/c40031fc05eeb0811410