NuxtアプリをDocker化する

## はじめに

Nuxt で作成したアプリケーションの開発用の環境をDockerコンテナ化してみました。

## バージョン

|  | バージョン |
|:-:|:-:|
| Windows  | 10 |
| Node.js  | v14.15.0  |
| yarn  | v1.22.10  |

## Nuxt アプリを作成する

`yarn create nuxt-app`で Nuxt アプリをサクッと作って、まずはアプリが正しく動くのを確認します。

```terminal
> yarn create nuxt-app webapp
> cd webapp
> yarn dev
```

http://localhost:3000 で実行されているのが確認できるはずです。

## Dockerfile を作成する

次に、Nuxt アプリ内のルートディレクトリに`Dockerfile`ファイルを作成し、以下のように記述します。

```dockerfile:Dockerfile
FROM node:14.15.0-alpine3.12

WORKDIR /app

COPY package.json .
COPY yarn.lock .

RUN yarn install

COPY . .

ENV HOST 0.0.0.0
EXPOSE 3000

CMD [ "yarn", "dev" ]
```

`ENV HOST 0.0.0.0`を指定しないとホストから接続できません。

また、一応 `.dockerignore` を作成して同ディレクトリに配置します。

```:.dockerignore
node_modules
npm-debug.log
```

## Docker イメージをビルドする

さきほど作成した`Dockerfile`が存在するディレクトリで下記のコマンドを実行し、Docker イメージをビルドします。

```docker
> docker build -t qianer-fengtian/webapp .
```

```
> docker images
REPOSITORY                              TAG                  IMAGE ID            CREATED             SIZE
qianer-fengtian/webapp                  latest               1a89469eeab5        37 seconds ago      982MB
node                                    14.15.0-alpine3.12   fdcffd80e219        6 weeks ago         117MB
```

## Docker イメージを実行する

Docker イメージがビルドされたら、実行します。

```
> docker run -p 3000:3000 -d qianer-fengtian/anyplus-portal-webapp
```

ちゃんと実行できています。

```
> docker ps
CONTAINER ID        IMAGE                                   COMMAND                  CREATED             STATUS      
        PORTS                    NAMES
705fb13c8369        qianer-fengtian/webapp                  "docker-entrypoint.s…"   3 seconds ago       Up 2 seconds        0.0.0.0:3000->3000/tcp   magical_euclid
> docker logs 705f
yarn run v1.22.5
$ nuxt-ts
ℹ Listening on: http://172.17.0.2:3000/
ℹ Preparing project for development
ℹ Initial build may take a while
✔ Builder initialized
✔ Nuxt files generated
ℹ Compiling Client
✔ Client: Compiled successfully in 3.08s
ℹ Waiting for file changes
ℹ Memory usage: 197 MB (RSS: 317 MB)
ℹ Listening on: http://172.17.0.2:3000/
No issues found.
```

http://localhost:3000 でブラウザでもアクセスをできることを確認したら、いったん停止します。

```
> docker stop 705f
```

## docker-compose で運用する

今回作成した`/webapp`の親ディレクトリに`docker-compose.yml`を作成し、Docker アプリを docker-compose で運用したいと思います。

```:docker-compose.yml
version: "3.8"
services:

  webapp:
    build: ./webapp
    ports:
      - "3000:3000"
```

作成後、下記コマンドを実行して起動を確認します。

```
> docker-compose up
```

## HMRを有効にする

http://localhost:3000 でブラウザでもアクセスをできることを確認したら、試しに 適当なファイル（`index.vue` とか）を編集して変更を試してみると、変更できません。
**HMR（Hot Module Replacement）**が有効になっていないので、有効にします。

`docker-compose.yml`に以下の設定を追記します。

```diff:docker-compose.yml
version: "3.8"
services:

  webapp:
    build: ./webapp
    ports:
      - "3000:3000"
+    volumes:
+      - ./webapp:/app
```

ホストのディレクトリ`/webapp/`をコンテナのディレクトリ`/app`にマウントし、コードの変更を反映できるようにしています。

さらに、webpack 側で変更を検知できるように`nuxt.config.js`に以下の設定を追記します。

```diff:config
+  watchers: {
+    webpack: {
+      aggregateTimeout: 300,
+      poll: 1000,
+    },
+  },
```

- `aggregateTimeout` ... ファイル変更した後、ビルドが始まるまでの遅延時間（ms）。
- `poll` ... ポーリングの間隔時間（s）。ここでは1秒ごとにチェックしている。

ちなみに、以上の設定後に`Dockerfile`を以下のように修正しても問題ありません。

```diff:Dockerfile
FROM node:14.15.0-alpine3.12

WORKDIR /app
-
- COPY package.json .
- COPY yarn.lock .

RUN yarn install
-
- COPY . .

ENV HOST 0.0.0.0
EXPOSE 3000

CMD [ "yarn", "dev" ]
```

これで HMR できるようになりました。

## 参考

- https://nodejs.org/ja/docs/guides/nodejs-docker-webapp/
- [watchers プロパティ - NuxtJS](https://ja.nuxtjs.org/docs/2.x/configuration-glossary/configuration-watchers/)