Vue.jsの開発環境をDockerで構築する手順

Vue.jsの開発環境をDockerで構築する手順を整理します。

実際に環境構築した後のソースコードは[こちら](https://github.com/kei178/vue-cli-test)

# 事前準備

Dockerで開発環境を構築するために必要な事項を確認します。

## Vue CLI

今回は Vue CLI でアプリを作ります。

Vue CLI をインストールしてない場合は、そのインストールからおこないます。（ついでに @vue/cli-init もインストールします。）

```
sudo npm install -g @vue/cli @vue/cli-init
```

バージョン確認できれば、正常にインストールできています。

```
vue -V
@vue/cli 4.0.5
```

インストール後、`webpack-simple` でアプリを作成します。

```
vue init webpack-simple your_app_name
```

## Docker

Docker をインストールしていない場合は、[Docker Desktop on Mac](https://docs.docker.com/docker-for-mac/install/) もしくは [Docker Desktop on Windows](https://docs.docker.com/docker-for-windows/install/) 等からインストールしてください。

バージョン確認できれば、正常にインストールできています。

```
docker version
```

合わせて `docker-compose` コマンドが使えることも確認しておきます。

```
docker-compose --version
```

# 開発環境 Docker 化の手順

それでは本題に入り、Vue.jsの開発環境をDockerで構築する手順を整理します。

## 1. vue-cli-service のインストール

まず Docker コンテナ内で Vue.js を起動するのに必要な `vue-cli-service` をインストールします。

```
npm install --save @vue/cli-service
```

インストール後、`package.json` に `scripts.serve` の設定を追加します。（該当箇所以外は省略してます。）

```json:package.json
{
  "scripts": {
    "serve": "node_modules/.bin/vue-cli-service serve"
  }
}
```

## 2. webpack のインストール

アプリ起動時にコンパイルエラーが出ないように、webpack を追加しておきます。

```
npm add webpack@latest
```

## 3. Docker の設定

必要な下準備が整ったので、Docker の設定ファイルを書いていきます。

### Dockerfile.dev の作成

アプリのルートディレクトリに `Dockerfile.dev` を作成します。

```dockerfile:Dockerfile.dev
FROM node:10.17.0-alpine3.9

# make the 'app' folder the current working directory
WORKDIR /app

# copy both 'package.json' and 'package-lock.json' (if available)
COPY package*.json ./

# install project dependencies
RUN npm install

# copy project files and folders to the current working directory (i.e. 'app' folder)
COPY . .

CMD ["npm", "run", "serve"]

```

* `FROM` Docker イメージのベース（バージョンは [DockerHub](https://hub.docker.com/_/node) で選んでください）
* `WORKDIR` ワーキングディレクトリの作成
* `COPY package*.json ./` 依存関係のコピー
* `RUN npm install` 依存関係のインストール
* `COPY . .` Docker コンテナ内にファイルを渡す
* `CMD ["npm", "run", "serve"]` コマンドでサーバ起動

### .dockerignore の作成

また、`.dockerignore` を作成し、無視してOKなファイルを Docker に知らせます。

```:.dockerignore
node_modules
.git
.gitignore
```

Docker の設定は以上です。

## 4. Dockerコンテナ内で起動

コマンドを叩けば、Docker コンテナ内で Vue.js のアプリを起動できます。

### docker build

まず、`Dockerfile.dev` から Docker イメージをビルドします。

```
docker build --tag your_app_name:latest --file Dockerfile.dev .
```

* `--tag` タグ名（タグをつけて `build` すれば、その名前で `run` できる）
* `--file` 設定ファイルを指定（デフォルトは `Dockerfile`）

### docker run

ビルドした Docker イメージを使ってコンテナを起動します。

```
docker run --rm -it --name your_app_container -p 8080:8080 -v ${PWD}:/app -v /app/node_modules your_app_name:latest
```

* `--rm` 終了時にコンテナを自動削除
* `-i` ターミナルのテキストコマンドをコンテナに入力
* `-t` いい感じのフォーマットでターミナルに出力
* `--name` コンテナに名前をつける
* `-p` ポートマッピング
* `-v` Docker ボリューム（ローカルのコードを参照させれば、変更をただちに反映可）
* `$(PWD)` present working directory

問題なければ、ブラウザから `http://localhost:8080/` でアクセスできます。Yay!

<img width="1008" alt="localhost:8080" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138789/439e3b83-2b35-6dcd-a16e-332c4226fd0e.png">

ターミナルで `Ctrl + C` でコンテナを終了できます。

# docker-compose でより簡単に起動

毎回毎回 `docker build...`、`docker run...` とコマンドを叩くのは面倒なので、docker-compose を使ってより簡単に起動できるようにします。

## docker-compose の設定

`docker-compose.yml` をルートディレクトリに作成し、Docker コマンドの内容を YML 形式で記述します。

```yml:docker-compose.yml
version: '3.7'

services:
  web:
    container_name: web
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - '.:/app'
      - '/app/node_modules'
    ports:
      - '8080:8080'
```

設定はこれだけです。

## docker-compose コマンド

これにより以下のコマンドが使えます。

### docker-compose up

コンテナの起動します。

```
docker-compose up
```

先ほどと同様に`http://localhost:8080/` でアクセスできます。

かなりシンプルになりました。

### docker-compose up --build

コンテナを再ビルドして起動します（変更を反映させられる）。

```
docker-compose up --build
```

### docker-compose up -d

コンテナをバックグラウンドで起動します。

```
docker-compose up -d
```

### docker-compose down


コンテナを終了させます。

```
docker-compose down
```

## docker-compose にテストを追加

`docker-compose.yml` に `test` サービスを追加し、そのコンテナ内でテストを実行できるようにします。

```yml:docker-compose.yml
version: '3.7'

services:
  web:
    container_name: web
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - '.:/app'
      - '/app/node_modules'
    ports:
      - '8080:8080'
  test:
    container_name: test
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - '.:/app'
      - '/app/node_modules'
    command: ["npm", "run", "test"]
```

※ *Vue.js でのテストのセットアップ手順は、「[Vue.js ユニットテストの基本まとめ](https://qiita.com/kskinaba/items/d23259060e6e13b7353c)」参照

次のコマンドでテスト用のコンテナサービスを起動できます。

```
docker-compose up test
```

サービス名を指定しない場合は、web・test の両サービスが起動します。

```
docker-compose up
```

# トラブルシューティング

発生する可能性のあるエラーとその解決方法をまとめておきます。

## エラー（その１）

### エラー内容

`docker run` 実行時に `vue-cli-service` がないと怒られる場合。

```
sh: vue-cli-service: command not found
```

### 解決方法

`vue-cli-service` をインストールすることで解決します。

```
npm install --save @vue/cli-service
```

## エラー（その２）

### エラー内容

`docker run` 実行時に `script: serve` がないと怒られる場合。

```
npm ERR! missing script: serve
```

### 解決方法

`package.json` に `scripts.serve` を追加することで解決します。

```json:package.json
{
  "scripts": {
    "serve": "node_modules/.bin/vue-cli-service serve"
  }
}
```

## エラー（その３）

### エラー内容

`docker run` 実行時に `compilation.templatesPlugin...` のコンパイルエラーが起きる場合。

```
ERROR  Failed to compile with 1 errors 
　　TypeError: compilation.templatesPlugin is not a function
```

### 解決方法

Webpack を追加することで解決します。

```
npm add webpack@latest
```

## エラー（その４）

### エラー内容

`docker-compose up` 実行時にコンテナ名が被っていると怒られる場合。

```
ERROR: for web  Cannot create container for service web: Conflict. The container name "/web" is already in use by container "f4acb3dbb5". You have to remove (or rename) that container to be able to reuse that name.
```

### 解決方法

名前が重複しているコンテナを取り除くことで解決します。

```
docker rm <CONTAINER_ID>
```