Docker ComposeでNuxt3+PM2+NGINX環境構築

## 概要　
Nuxtをdocker composeを使って構築します。
vuetifyも導入して、本番用にpm2とNGINXを使っていきます！

最終的なコードのリポジトリは以下ですね。
https://github.com/gaitou2048/nuxt3-vuetify-template

## 前提
- Docker, Docker Composeインストール済み
    - 実行環境はMac + Docker for Desktop(Mac) ですが、Windows(WSL)やLinuxでもできるはず。

## 環境構築

Dockerfileとdocker-compose.yml用意します。
[以前書いた記事](https://qiita.com/gaitou2048/items/4c187ff3ed60de1cb366)のnodeコンテナとほぼ同じですね。
```
.
├── docker-compose.yml
└── front
    └── Dockerfile
```
```Dockerfile
FROM node:19.6-slim

WORKDIR /app
ENV LANG=C.UTF-8 \
    TZ=Asia/Tokyo
```

```docker-compose.yml
version: '3'
services:
  front:
    build:
      context: ./front
      dockerfile: Dockerfile
    ports:
      - 3001:3000
    volumes:
      - ./front/app:/app
      - front_node_modules:/app/node_modules
    tty: true
    #restart: on-failure
    working_dir: /app

volumes:
  front_node_modules:
```

コンテナを作成+起動していきます。
立ち上がったら、コンテナ内にイン。

```zsh
$ docker compose up -d --build
$ docker compose exec front bash 
```

ここからお待ちかねのNuxt3環境構築。
公式通りにいきましょう！
https://nuxt.com/docs/getting-started/installation
英語は中学文法すら怪しいですが、Google大先生の翻訳力に全てを委ねます。
何とかなります。本当に助かる。

コンテナ内でNuxtプロジェクト作成。
する前に、`/app`をプロジェクトにしたいのに、working_dirとしてしまいました。
移動します。移動してからNuxtプロジェクト作成。


```bash
cd /
npx nuxi init app --force 
cd /app
yarn install
```

インストールが完了したらコンテナ内で以下を叩きます。　
```
yarn dev
```

で、http://localhost:3001 にアクセスしてみましょう。

ページが表示されたら、とりあえずOKです。

ポートが3001なのは、docker-compose.ymlでホスト側のポートの3001にマウントしているからですね。
3000はよく使うので、少しずらしました。
以前の記事でも3000は使ってますね。

Nuxtプロジェクトの作成で、package.jsonが出力されています。
コンテナビルド時に、パッケージインストールできるようにDockerfileを書き換えます。


```Dockerfile
FROM node:19.6-slim

WORKDIR /app
ENV LANG=C.UTF-8 \
    TZ=Asia/Tokyo

COPY app/package.json app/yarn.lock ./
RUN yarn install
```

これでコンテナ内で `yarn add`でパッケージを加えても大丈夫です。

## vuetify3 インストール
こちらに記事を大いに参考にしています。
というか、こちらの方法です。
https://qiita.com/MS-0610/items/a16670713de929a5b9b1

### 1. コンテナ内に入り、ライブラリインストール

```bash
$ docker compose exec front bash
yarn add vuetify@next
yarn add sass
```

### 2. 設定ファイル記載

```plugins/vuetify.js
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'

export default defineNuxtPlugin((nuxtApp) => {
  const vuetify = createVuetify({
    components
  })

  nuxtApp.vueApp.use(vuetify)
})
```
```nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
    css: ['vuetify/lib/styles/main.sass'],
    build: {
      transpile: ['vuetify'],
    },
})
```

### 3. Vueitfyのワイヤーフレームページ

- 以下のワイヤーフレームを動かすために、app.vueを修正します。
https://github.com/vuetifyjs/vuetify/blob/master/packages/docs/src/examples/wireframes/three-column.vue

```app.vue
<template>
  <v-app id="inspire">
    <v-app-bar
      class="px-3"
      color="white"
      flat
      density="compact"
    >
      <v-avatar
        color="grey-darken-1"
        size="32"
      ></v-avatar>

      <v-spacer></v-spacer>

      <v-tabs
        centered
        color="grey-darken-2"
      >
        <v-tab
          v-for="link in links"
          :key="link"
        >
          {{ link }}
        </v-tab>
      </v-tabs>
      <v-spacer></v-spacer>

      <v-avatar
        class="hidden-sm-and-down"
        color="grey-darken-1"
        size="32"
      ></v-avatar>
    </v-app-bar>

    <v-main class="bg-grey-lighten-3">
      <v-container>
        <v-row>
          <v-col
            cols="12"
            sm="2"
          >
            <v-sheet
              rounded="lg"
              min-height="268"
            >
              <!--  -->
            </v-sheet>
          </v-col>

          <v-col
            cols="12"
            sm="8"
          >
            <v-sheet
              min-height="70vh"
              rounded="lg"
            >
              <!--  -->
            </v-sheet>
          </v-col>

          <v-col
            cols="12"
            sm="2"
          >
            <v-sheet
              rounded="lg"
              min-height="268"
            >
              <!--  -->
            </v-sheet>
          </v-col>
        </v-row>
      </v-container>
    </v-main>
  </v-app>
</template>

<script setup>
  
    const links = [
        'Dashboard',
        'Messages',
        'Profile',
        'Updates',
    ]
</script>
```

### 4. ページの表示
http://localhost:3001 にアクセス。
以下のようなページが表示されたら、OKです。

![img.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3113048/3153c48d-e5ec-13b0-2b90-b481ef7e568c.png)

## PM2
Nuxtサーバを常時起動するためにPM2を導入します。
`yarn dev`で起動しているアプリを止めておきましょう。Ctrl+Cとかで。

止めたらコンテナ内でインストール。

```bash
yarn add pm2
```

次は設定ファイルを作成します。
scriptに指定しているファイルは、これからのビルドで生成されるものですね。
```front/app/ecosystem.config.js
module.exports = {
    apps: [
        {
            name: 'app',
            cwd: '/app',
            exec_mode: 'cluster',
            instances: 1,
            script: './.output/server/index.mjs',
            env: {
                NODE_ENV: 'production'
            }
        }
    ]
}

```

ということで、Nuxtのビルド。
色々と`.output`に出力されます。

```bash
yarn build
```

できたらPM2を使って動かしてみましょう。

```bash
yarn pm2 start
```

ここまでできたら、またｍた http://localhost:3001 にアクセス。
`yarn dev` していないのに起動しています。やったね！

確認できたら、次のNGINX導入確認のために、止めておきましょう。
0はPM2で起動しているアプリのIDです。
0しか起動していないので、0で止めます。


```
yarn pm2 stop 0
```
　
## NGINX

新しくコンテナを追加します。こちらも[以前書いた記事](https://qiita.com/gaitou2048/items/4c187ff3ed60de1cb366)と同じですね。

serverディレクトリと、Dockerfileを作ります。
nginxの設定ファイルも作るので、こんな感じになります。
```
.
├── README.md
├── docker-compose.yml
├── front/
└── server/
    ├── Dockerfile
    ├── conf.d/
    │   └── server.conf
    └── nginx.conf
```

ひとまずDockerfileから作りましょう。

```server/Dockerfile
FROM nginx:latest
ENV TZ=Asia/Tokyo
```
ほぼ変更のないnginxのDockerfileです。
`docker-compose.yml`でimageを指定するのもありです。
が、後から何か(NGINX用モジュールとか)を追加したくなるときも踏まえて作ってあります。

次は設定ファイル。
```server/nginx.conf
worker_processes auto;

events {
    worker_connections 512;
    multi_accept on;
}

http {
    include /etc/nginx/conf.d/server.conf;
    server_tokens off;
    send_timeout 60; # タイムアウト60秒
}
```

```server/conf.d/server.conf
server{
    listen 5000;

    location / {    
        proxy_pass http://front:3000/;
    }
}
```

ここまで作成したら、`docker-compose.yml`にNGINXコンテナの設定を追加していきます。


```docker-compose.yml
version: '3'
services:
  front:
    build:
      context: ./front
      dockerfile: Dockerfile
    ports:
      - 3001:3000
    volumes:
      - ./front/app:/app
      - front_node_modules:/app/node_modules
    tty: true
    #restart: on-failure
    working_dir: /app

  server:
    build:
      context: ./server
      dockerfile: Dockerfile
    ports:
      - 5001:5000
    volumes:
      - ./server/nginx.conf:/etc/nginx/nginx.conf
      - ./server/conf.d:/etc/nginx/conf.d
      - ./server/log:/var/log/nginx
    tty: true
    #restart: on-failure

volumes:
  front_node_modules:

```

あとはコンテナを起動して、pm2起動するだけです。

```bash
docker compose up -d --build
dokcer compose exec yarn pm2 start
```

Nuxtのサーバ起動コマンドを毎回打つのが面倒であれば、
docker-compose.ymlのcommandに `yarn dev`や`yarn pm2 start`を書くのもありですね。
ともかく、ここまでできたら http://localhost:5001 にアクセスしてみましょう。

正常にページが表示できればOKです！
お疲れ様でした。