同一リポジトリでLaradock ＋ Nuxt(SSRモード)の環境構築

# 前提
Docker version 18.09.2
docker-compose version 1.23.2
npm 6.9.0

# 参考サイト
 [laradock と nuxt で開発環境構築](https://qiita.com/aoarashi/items/535feeca48d15516d450) 
 [Laravel+Nuxt.js](http://landau.jp/blog/355/) 
 [LaravelとNuxt.jsを同一レポジトリで管理するときの構成](https://www.wantedly.com/companies/scouter/post_articles/84615) 
 [Laravel に Nuxt.js プロジェクトを入れたい](https://qiita.com/mikakane/items/a76b230d91dc5ae80574) 

### Nuxtをバックグラウンドで起動したい場合
そのまま実行したら、npm run startでコマンドが止まってしまい
nginxコマンドが実行されずにWebサーバが起動できてなかった

そのためバックグラウンドで**npm run start**を実行するために
foreverモジュールを入れる

 [Nuxt.jsのアプリケーションをinitd-foreverを使ってデーモン化、自動起動する](https://qiita.com/isao_e/items/36b99b0e186b2d656b50) 

### DockerfileのCMDコマンドを複数実行したい場合
Dockerfileでは、CMDコマンドを複数実行できない
そのため**startup.sh**でコマンドを記載して実行してもらう

 [Dockerで複数CMDを実行する方法](https://sleepless-se.net/2018/05/26/docker%E3%81%A7%E8%A4%87%E6%95%B0cmd%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/) 


# Laradockの構築

Laradockをインストール用のディレクトリの作成

```sh
mkdir {ディレクトリ名}
```

Laradockのプロジェクトをクローンする

```
cd {ディレクトリ名}
git clone https://github.com/Laradock/laradock.git
```

作成したディレクトリに移動

```
cd laradock/
```

laradock用の`.env`ファイルを作成する

```
cp env-example .env
```

workspaceコンテナ内でNuxtサーバを起動した際にアクセスできるように
docker-compose.ymlを編集し、Nuxt用のポートをマッピングする

```
ports:
        - "${WORKSPACE_SSH_PORT}:22"
        - "3000:3000"
```

.envファイル開発環境の構築をするのでルートディレクトリではなく以下のように変更を行う。

```
- DATA_PATH_HOST=~/.laradock/data
+ DATA_PATH_HOST=.laradock/data
```


Dockerコンテナを立ち上げる

```
docker-compose up -d nginx mysql workspace
```

指定のDockerコンテナが立ち上がる

```
Creating volume "laradock_mosquitto" with local driver
Recreating laradock_mysql_1            ... done
Recreating laradock_docker-in-docker_1 ... done
Recreating laradock_workspace_1        ... done
Recreating laradock_php-fpm_1          ... done
Recreating laradock_nginx_1            ... done
```

workspaceコンテナ内に入る

```
docker-compose exec workspace bash
```
※ ちなみにコンテナから抜けるには、Ctrl + P + Q


# laravel プロジェクト作成

workspaceに入ったままlaravelプロジェクトをインストール

```
composer create-project --prefer-dist laravel/laravel {プロジェクト名}
```
インストール結構時間かかる。。。
立ち上がって、laradockフォルダと同じ階層にLaravelプロジェクト名のディレクトリができていたらOK!!


Laradock側の`.env`ファイルを編集する
今回は、本番環境でAmazon Auroraのmysql5.7で構築予定なので
ローカルも5.7に合わせる

**./laradock/.env**

```sh
- APP_CODE_PATH_HOST=../
+ APP_CODE_PATH_HOST=../{プロジェクト名}

- MYSQL_VERSION=latest
+ MYSQL_VERSION=5.7
```

dockerを立ち上げ直す

```
docker-compose down
docker-compose up -d nginx mysql workspace
```

mysqlコンテナに入る

```
docker-compose exec mysql bash
```

バージョン確認する

```
root@ca2f854dfc76:/# mysql --version
mysql  Ver 14.14 Distrib 5.7.24, for Linux (x86_64) using  EditLine wrapper
```

# Nuxtのインストール

 [参考サイト](https://qiita.com/high-u/items/1d2e91e97495ac90d10c) 

Nuxt公式で展開してるtemplateにする
LaravelProjectの直下のclientディレクトリに配置

```
docker-compose exec workspace bash
npx create-nuxt-app ./client
```

初期設定で色々聞かれるのでとりあえず下記で設定

```
? Project name client
? Project description My ace Nuxt.js project
? Use a custom server framework none
? Choose features to install Linter / Formatter, Prettier, Axios
? Use a custom UI framework buefy
? Use a custom test framework jest
? Choose rendering mode Universal
? Author name 
? Choose a package manager npm
```


# package.json と nuxt.config.jsの編集

## package.json
laravel プロジェクトを作成した際に作られた package.json は捨てて、
nuxt の方と入れ替える。

```sh
$ rm package.json # laravelインストール時に生成された方を削除
$ mv ./client/package.json package.json #nuxtの方と入れ替える
```

## nuxt.config.js

```sh
$ mv ./client/nuxt.config.js nuxt.config.js
```

内容編集

```
module.exports = {
  ~ 省略 ~
  build: {
    ~ 省略 ~
  },
  srcDir: './client',　　　　<- nuxt プロジェクトディレクトリパス
  watchers: {                     <- nuxt コマンドでホットリロードを有効にするため
    webpack: {
      poll: true
    }
  }
}
```

パッケージをnpmでインストール

```
npm install
```

完了後にpackge.jsonの編集を行う
なぜ必要なのかは要調査

```
{
  "name": "guild-test-nuxt",
  ~ 省略 ~
  "scripts": {
    - "dev": "nuxt",
    + "dev": "HOST=0.0.0.0 PORT=3000 nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate"
  },
  ~ 省略 ~
}
```

開発者モードで起動

```
npm run dev
```

```
> client@1.0.0 dev /var/www/guild-test
> HOST=0.0.0.0 PORT=3000 nuxt

ℹ Preparing project for development                                                                                                                  13:37:38
ℹ Initial build may take a while                                                                                                                     13:37:38
✔ Builder initialized                                                                                                                                13:37:38
✔ Nuxt files generated                                                                                                                               13:37:38

✔ Client
  Compiled successfully in 17.35s

✔ Server
  Compiled successfully in 15.14s

ℹ Waiting for file changes                                                                                                                           13:38:05

   ╭─────────────────────────────────────────────╮
   │                                             │
   │   Nuxt.js v2.4.5                            │
   │   Running in development mode (universal)   │
   │   Memory usage: 157 MB (RSS: 295 MB)        │
   │                                             │
   │   Listening on: http://172.31.0.2:3000      │
   │                                             │
   ╰─────────────────────────────────────────────╯
```

# LaravelのAPIからHellowoaldを取得する

client/pages/inspire.vue

```
<template>
  <div>{{ data }}</div>
</template>
<script>
export default {
  async asyncData({ app }) {
    const data = await app.$axios.$get('/api')
    return { data }
  }
}
</script>
```

routes/api.php

```
//APIテスト用
Route::get('/', function(){ return 'helloworld';});
```

プロキシモジュールを追加

```
npm i @nuxtjs/proxy
```

デフォルトのソースコードは、ESlint + Prettierの規則を尊重しないので
下記のコマンドを実行する
`Module Error (from ./node_modules/eslint-loader/index.js)`

```
npx prettier --write "**/*.{vue,js}"
```

/nuxt.config.js

```
module.exports = {
  modules: [
    '@nuxtjs/proxy'
  ],
  /*
   ** Axios module configuration
   */
  axios: {
    // See https://github.com/nuxt-community/axios-module#options
    baseURL: '/'
  },
  proxy:{
    '/api' : "http://localhost:8000"
  },
}
```

workspaceコンテナ内でNuxt/Laravelサーバを立ち上げる

```
npm run dev
php artisan serve
```

`http://localhost:3000/inspire`にアクセスして
画面にhelloworldが出てるはず

# nginxサーバをプロキシとしてNuxtとLaravel APIと切り替える

### nginxプロキシ設定の参考サイト
 [nuxt.js(フロント)とLaravel(API)を同一リポジトリ/サーバで動かす時](https://qiita.com/tyoshii/items/4e7de11f4d364d93584b) 

 [Laravel公式](https://readouble.com/laravel/5.5/ja/deployment.html) 

 [Nuxt公式](https://ja.nuxtjs.org/faq/nginx-proxy/) 

Laradockでは、nginxの設定を下記のパスで設定を行えるため
そこでnginxに対してリクエストが来た際に振り分けを行うようにする

**/laradock/nginx/sites/default.conf**

```
server {

    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    # For https
    # listen 443 ssl default_server;
    # listen [::]:443 ssl default_server ipv6only=on;
    # ssl_certificate /etc/nginx/ssl/default.crt;
    # ssl_certificate_key /etc/nginx/ssl/default.key;

    server_name localhost;
    root /var/www/public;
    index index.php index.html index.htm;

    # 基本はすべてLaravelのpublicにアクセスがいくが、/api 以降のパスだけLaravelとする
    # location / {
    #      try_files $uri $uri/ /index.php$is_args$args;
    # }

    # 基本は3000ポートのNuxtサーバに転送を行う
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
    }

    # Laravel API用にnginxをプロキシサーバとして転送させる
    location /api {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_pass php-upstream;
        fastcgi_index index.php;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        #fixes timeouts
        fastcgi_read_timeout 600;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }

    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt/;
        log_not_found off;
    }
}
```

nuxtプロキシの転送先のポートを変更する
/{プロジェクト名}/nuxt.config.js

```
proxy:{
    '/api' : "http://localhost:80"
  }
```
# Dockerコンテナ起動時にNuxtサーバが立ち上がるようにする

`docker-compose`でコンテナ起動時に、Nuxtにアクセスできるように
`npm run start`を実行できるようにする

./laradock/nginx/Dockerfile

```
# Set upstream conf and remove the default conf
RUN echo "upstream php-upstream { server ${PHP_UPSTREAM_CONTAINER}:${PHP_UPSTREAM_PORT}; }" > /etc/nginx/conf.d/upstream.conf \
    && rm /etc/nginx/conf.d/default.conf


+ # nginxコンテナ内でNuxtをSSRモードで立ち上げるためにnpmをインストールする
+ RUN apk add --update nodejs nodejs-npm

+ # ワーキングディレクトリの指定
+ WORKDIR /var/www

+ # Nuxtをビルドする準備をする
+ RUN npm install

+ # foreverモジュールを入れて、Nuxtサーバをバックグラウンドで起動できるようにする
+ RUN npm install -g -y forever

ADD ./startup.sh /opt/startup.sh
RUN sed -i 's/\r//g' /opt/startup.sh
CMD ["/bin/bash", "/opt/startup.sh"]
```


./laradock/nginx/startup.sh

```
if [ ! -f /etc/nginx/ssl/default.crt ]; then
    openssl genrsa -out "/etc/nginx/ssl/default.key" 2048
    openssl req -new -key "/etc/nginx/ssl/default.key" -out "/etc/nginx/ssl/default.csr" -subj "/CN=default/O=default/C=UK"
    openssl x509 -req -days 365 -in "/etc/nginx/ssl/default.csr" -signkey "/etc/nginx/ssl/default.key" -out "/etc/nginx/ssl/default.crt"
fi

+ # Nuxt server lunch
+ npm run build
+ forever start -c "npm run start" ./

nginx

```

Dockerイメージをビルドを実行し、立ち上げる

```
docker-compose down
docker-compose build --no-cache nginx
docker-compose up -d nginx mysql workspace
```

`npm run start`が実行されているか確認する

```sh
docker-compose exec nginx bash
forever list
info:    Forever processes running
data:        uid  command       script forever pid id logfile                 uptime       
data:    [0] TwFe npm run start        42      53     /root/.forever/TwFe.log 0:0:0:11.555 
```

`localhost`にアクセスして、画面が表示されるか確認する

# DBマイグレーションを実行する

Laravelプロジェクト側の.envをLaradockの.envファイルのDB情報と合わせる


./{プロジェクト名}/.env

```
DB_CONNECTION=mysql
- DB_HOST=mysql
+ DB_SLAVE_HOST=mysql
+ DB_SLAVE_PORT=3306
+ DB_MASTER_HOST=mysql
+ DB_MASTER_PORT=3306
DB_DATABASE=default
- DB_PORT=3306
DB_USERNAME=default
DB_PASSWORD=secret
```

本番環境でAmazon AuroraでMaster/Slave構成で
ReadとWriteと切り分ける予定なので下記のようにする

※開発モードと本番モードで切り替える方法があるかもなのでもっと良い方法あれば教えていただきたいです。

./{プロジェクト名}/config/database.php

```
- 'host' => env('DB_HOST', '127.0.0.1'),
- 'port' => env('DB_PORT', '3306'),
+ 'read' => [
+     'host' => env('DB_SLAVE_HOST', '127.0.0.1'),
+     'port' => env('DB_SLAVE_PORT', '3306'),
+ ],
+ 'write' => [
+     'host' => env('DB_MASTER_HOST', '127.0.0.1'),
+     'port' => env('DB_MASTER_PORT', '3306'),
+ ],
```

マイグレーションを実施し、DBの接続確認

```sh
docker-compose exec workspace bash
root@30a2d85662fb:/var/www# php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table
```

# まとめ
今回は、本番環境を踏まえてLaradockで開発環境を構築しました。
もっとこうした方が良いなど指摘をいただけると幸いです！