Dockerコンテナ内にJSの開発環境を構築し、VS Codeでリモート接続する

# 概要

エンジニア内で一人だけWindowsを使っていることもあり、他の方と微妙に環境が異なっていましたが、VS Codeはコンテナ内へ気軽にリモート接続出来るようになっているため、中で環境を構築してみました。
Dockerfile等で注意点もあったので、主にそちらを記載しています。
※Dockerにさほど詳しいわけではないので、誤りがあったらごめんなさい。

## 環境

- VS Code 1.41.1
- WSL1(Ubuntu)
- docker desktop 2.1.0.5

## 前提

- DockerやVS Codeはインストール済み
- プロジェクト内のコンフィグファイル（package、tsconfig等）は設定済み

## やりたかったこと

- ビルド用のDockerイメージを作成
- VS Codeで接続し、開発したりビルドしたり __npm installしたり__（←重要）できるようにする。
- ホストマシンの方にいらないファイルを持ってこないようにする

## 手順

### Dockerfile等の設定

環境変数を設定し、composeを経由してコンテナを立て直すスクリプトです。
こちらを叩けば開発環境用のコンテナが立ち上がります。

```sh:start.sh
#! /bin/bash

export CONTAINER_USER=dev
export CONTAINER_WORK=/home/$CONTAINER_USER/

docker-compose build
docker-compose down
docker-compose up -d
```

compose用の設定ファイルです。
動機は後述しますが、`package.json`や`tsconfig.json`等の設定ファイルは`config_files`の中にまとめておき、Dockerfile内でシンボリックリンクを作成します。

```yml:docker-compose.yml

version: '3.7'
services:
  dev:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        USER: $CONTAINER_USER
        WORK: $CONTAINER_WORK
    tty: true
    volumes:
      - $PWD/src:$CONTAINER_WORK/src
      - $PWD/config_files:$CONTAINER_WORK/config_files
    user: $CONTAINER_USER
```

Dockerのイメージ作成部分です。
`config_files`以下にあるファイルのシンボリックリンク作成、およびモジュールのインストールを行います。

```dockerfile:Dockerfile

FROM node:13.5 as env

ARG USER
ARG WORK

RUN addgroup ${USER} \
  && useradd -m -g ${USER} ${USER}

WORKDIR $WORK
COPY --chown=${USER}:${USER} ./config_files ${WORK}/config_files

RUN ls -a --file-type config_files | grep -v / | xargs -I {} -n1 ln -s "config_files/"{} {} \
  && npm ci
```

#### config_filesに設定ファイルをまとめる動機

`node_modules`をホストと同期させたくない場合、該当ディレクトリを別のボリュームにマウントする方法がよく見つかりました。

```yml

volumes:
  - $PWD:$CONTAINER_WORK
  - $CONTAINER_WORK/node_modules
```

この方法だと、権限エラーが発生しコンテナ内でモジュールの追加・削除が出来ませんでした。
ホスト側で追加しイメージを再ビルドすれば解決できますが、作業前に一度`node_moduels`を作らざるを得ないため、同期しない旨味が少なくなります。
また、今回ホーム直下に作成しているため、VS Codeで接続した際に作成される`.bash_history`などのファイルがホスト側にも同期されます。

次に考えたのが、volumesでファイルを直接指定する方法です。

```yml

volumes:
  - $PWD/src:$CONTAINER_WORK/src
  - $PWD/package.json:$CONTAINER_WORK/package.json
  # 以下マウントしたいファイルを列挙
```

この方法だとモジュールのインストールは出来ましたが、ロックエラー（`EBUSY: resource busy or locked, rename '/home/user/package.json.3654907157' -> '/home/user/package.json'`）が発生し、追加したモジュールがpackage.jsonへ反映出来ませんでした。

最終的に、stackoverflowで見つけた下記の回答で解決できたため、コンフィグ類をまとめる形にして採用しました。
https://stackoverflow.com/questions/48960320/locked-package-json-files-in-docker-container-using-docker-compose/58880425#58880425

### VS Codeの設定

1. VS Codeに`Remote - Containers`（ms-vscode-remote.remote-containers）をインストール
  - WSL環境だと、`Remote - WSL`も必要かもしれません。`Remote - SSH`は同一マシン内だからか不要でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281108/c055b32f-139e-1770-c1e9-184308b3d1e4.png)

2. 上で作った`start.sh`を実行し、コンテナを起動
3. リモートエクスプローラーで該当のコンテナに接続
  - 下のSSで、現在選択されているアイコンです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281108/b6291d45-a466-7f6c-b2e6-74a13832cb60.png)

4. 初回接続時、接続設定が作成されるので保存します。また、拡張機能はホスト側と同期しない？ので、入れ直す必要があります。
  - ホスト側で入れている拡張機能の一覧は表示されます。

### Webpackの設定

`webpack.config.js`の実体は`config_files`にいるので、`path.resolve(__dirname, '../src')`という風にパスを補正してください。
忘れると`config_files`以下に出力されていて、「あれ？ビルドしたのに更新されてない？」みたいな感じになります。

## 動作確認

あとは通常通りVS Codeでファイルを開いて編集したり、ターミナルでコマンドを叩いてみてください。
ビルドが正常に実行され、編集内容がホストマシンにも同期されていれば無事成功です。