6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

テコテックAdvent Calendar 2018

Day 22

Nuxt.jsをFirebaseでSSRするための開発環境をDockerで作る

Last updated at Posted at 2018-12-21

はじめに

Nuxt.jsをFirebaseへデプロイするための開発環境を整えたDockerイメージを作成します。
ちなみにこちらこちらの記事は、今回の開発環境を作るための前段階になります!

作業環境と作成するDockerイメージの内容は以下になります。

作業環境

  • Windows10 pro
  • Docker for Windows

作成するDockerイメージの内容

  • Alpine Linux
  • nvm : v0.33.11
  • Node.js : v6.11.5、v8.11.1
  • Package Manager : yarn
  • Node Modules : create-nuxt-app、firebase-tools

Nuxt.jsの開発環境の構築

(2019-02-21 追記)
create-nuxt-app@2.4.0ではv8以降でないとインストールできなくなったようです。
そのため、下記のようにバージョンを分けることで@google-cloud/functions-emulatorをインストールできなくなってしまってしまいました。
もし、ご存知の方がおられましたらご教授いただきたいです!

まずは、Node.jsのv6.11.5v8.11.1をインストールします。
なぜ2つもインストールするかというとNuxt.jsv2では、Node.jsのv8以降が必要になりますが、ローカルでFirebase Functionsの動作確認をするために必要なモジュール@google-cloud/functions-emulatorv6以前にしか対応していないためです。
そのため上記モジュールに依存しているfirebase-toolsv6.11.5を使ってインストールすることになります。
ちなみにv6.11.5v8.11.1Cloud Functionsが公式に対応しているとされているバージョンになります。
https://cloud.google.com/functions/docs/writing/
https://cloud.google.com/functions/docs/concepts/nodejs-8-runtime

Dockerfileの作成

さて、今回は最初からDockerfileの作成から入ります。
なぜかというとこちらの記事の手順でNode.jsを入れていると時間がかかりすぎるからです・・・
なので、あらかじめビルドされた各バージョンのNode.jsをコピーしてくる方法をとることにします。
それには「Dockerのマルチステージビルド」という機能を使います。
まずはnvmをインストールしたイメージへNode.jsのv6.11.5v8.11.1をインストールしたイメージからビルドしたファイルだけをコピーしてきます。

FROM iidateco/alpine-nvm-node:v6.11.5 as node6
FROM iidateco/alpine-nvm-node:v8.11.1 as node8
FROM iidateco/alpine-nvm

COPY --from=node6 /root/.nvm/versions/node/v6.11.5 /root/.nvm/versions/node/v6.11.5
COPY --from=node8 /root/.nvm/versions/node/v8.11.1 /root/.nvm/versions/node/v8.11.1

ローカルにalpine-nvmalpine-nvm-node:v6.11.5がある場合は、iidateco/を削除しても大丈夫です。
とりあえずいったんこれでビルドしてみましょう。

PS C:\works\docker\alpine-nuxt-firebase> docker build -t alpine-nuxt-firebase .

イメージからビルドされたNode.jsをコピーしてくるだけなのでそれほど時間はかからないはず・・・。

PS C:\works\docker\alpine-nuxt-firebase> docker images
REPOSITORY                 TAG                 IMAGE ID            CREATED             SIZE
alpine-nuxt-firebase       latest              3437acd9ced7        10 seconds ago      310MB
iidateco/alpine-nvm-node   v8.11.1             f675583791a2        35 minutes ago      266MB
iidateco/alpine-nvm-node   v6.11.5             6ba8a1f65a76        2 hours ago         252MB
iidateco/alpine-nvm        latest              a2f9534c5a8e        4 hours ago         208MB

イメージからコンテナを起動してちゃんとコピーできているかを確認します。

PS C:\works\docker\alpine-nuxt-firebase> docker run --rm -it alpine-nuxt-firebase bash
bash-4.4# nvm ls
        v6.11.5
        v8.11.1
node -> stable (-> v8.11.1) (default)
stable -> 8.11 (-> v8.11.1) (default)
iojs -> N/A (default)

コピーしただけではデフォルトのNode.jsのバージョンが設定されていないので設定します。
Nuxt.jsv2で開発したいのでデフォルトはv8.11.1にしておきます。

bash-4.4# nvm alias default 8.11.1
default -> 8.11.1 (-> v8.11.1)

ただし、firebase-toolsv6.11.5を使ってインストールするので、以下のようにします。
また、パッケージマネージャはyarnを使いたいのでapk addでインストールします。

bash-4.4# apk update
bash-4.4# apk add --no-cache yarn
bash-4.4# yarn global add create-nuxt-app firebase-tools

(create-nuxt-app@2.4.0v8以降でないとインストールできなくなったのでnvm use 6.11.5の記述を削除しました。)

適当なディレクトリで動作確認します。
実行前にNode.jsをv8.11.1に変えておきます。

bash-4.4# nvm use 8.11.1
bash-4.4# cd /tmp
bash-4.4# create-nuxt-app sample
> Generating Nuxt.js project in /tmp/sample
? Project name sample
? Project description My lovely Nuxt.js project
? Use a custom server framework none
? Use a custom UI framework none
? Choose rendering mode Universal
? Use axios module no
? Use eslint no
? Use prettier no
? Author name
? Choose a package manager yarn

ちゃんと動きました!
最後のパッケージマネージャだけyarnにしてあとはデフォルトにしてあります。
試しに動かしてみます。

bash-4.4# cd sample/
bash-4.4# yarn dev

ローカルサーバがhttp://localhost:3000に立ち上がりますが、ブラウザからアクセス出来ないと思います。
なぜならホストOSとゲストOSのポートの紐付けができていないからです。
またゲストOSの環境変数の設定も必要になります。
以下のコマンドで先ほど作ったイメージを起動し直してみます。

PS C:\works\docker\alpine-nuxt-firebase> docker run --rm -it -e HOST=0.0.0.0 -p 3000:3000 alpine-nuxt-firebase bash

-eオプションで環境変数、-pオプションでポートの紐付けができます。
これで立ち上がったコンソールで先ほどの手順をもう一度行ってみましょう。
コンソールにはhttp://127.0.0.1:3000と表示されますが、http://localhost:3000をブラウザで表示してください。
今度はちゃんと表示されたかと思います!

では上記の手順をDockerfileに追記しましょう。

FROM iidateco/alpine-nvm-node:v6.11.5 as node6
FROM iidateco/alpine-nvm-node:v8.11.1 as node8
FROM iidateco/alpine-nvm

COPY --from=node6 /root/.nvm/versions/node/v6.11.5 /root/.nvm/versions/node/v6.11.5
COPY --from=node8 /root/.nvm/versions/node/v8.11.1 /root/.nvm/versions/node/v8.11.1

SHELL ["/bin/bash", "-c"]

RUN \
    source ~/.bashrc && \
    apk update && \
    apk add --no-cache yarn && \
    nvm alias default 8.11.1 && \
    nvm use 6.11.5 && \
    yarn global add create-nuxt-app firebase-tools && \
    yarn cache clean

ENV HOST 0.0.0.0

環境変数の指定はDockerfileでできますが、ポートの紐付けはDockerfileではできないので、引き続き起動コマンドで指定するようにします。
上記のファイルで再度イメージをビルドします。
Dockerfileで環境変数を指定したので起動のコマンドは以下のようになります。

PS C:\works\docker\alpine-nuxt-firebase> docker run --rm -it -p 3000:3000 alpine-nuxt-firebase bash

Nuxt.jsの動作確認をしてちゃんと動いたら成功です!

ホストOSのディレクトリのマウント

さて、これからFirebaseでSSRするための設定をしていくのですが、いままで使っていた起動コマンドでは作業がやりにくいので、以下のようにコマンドを変えます。

PS C:\works\project> docker run -it --mount type=bind,src=$(pwd)/,dst=/project -p 3000:3000 -p 5000:5000 -p 5001:5001 alpine-nuxt-firebase bash

上記コマンドをプロジェクトを作成したいディレクトリまでコンソールで移動してから実行してください。
いままで--rmオプションをつけていましたが、これだとDockerコンテナを終了したときにコンテナが削除されてしまい、作業途中のデータが無くなってしまうのでなくしました。
--mount type=bind,src=$(pwd)/,dst=/projectのオプションで、コマンド実行ディレクトリが/projectにマウントされるようになります。
では、起動したコンテナのコンソールで/projectに移動して、create-nuxt-appでプロジェクトを作成してみましょう。
ホストOSのマウントしたディレクトリにもファイルが作成されていると思います。

FirebaseでSSRするための設定

Dockerの環境はこれで準備ができたので、Nuxt.jsをFirebaseでSSRするための設定をしていきます。
こちらにサンプルを用意しましたので、それを参照しながら要点を説明していきます。
https://github.com/iida-teco/nuxt-firebase-ssr-template.git

ディレクトリ構成

Nuxt.jsをFirebaseでSSRするためにはHostingにアップロードするものとFuncstionsにアップロードするものに分けることになります。
appというディレクトリがNuxt.jsのファイルがおいてあります。
functionsというディレクトリにはFunctionsで使われるファイルがおいてあります。
以下のようなディレクトリ構成になっていると思います。

bash-4.4# ls -la
total 226
drwxrwxrwx 2 root root   4096 Dec  6 02:27 .
drwxrwxrwx 2 root root      0 Dec  6 02:23 ..
-rwxr-xr-x 1 root root    207 Dec  3 08:20 .editorconfig
drwxrwxrwx 2 root root      0 Dec  6 02:27 .git
-rwxr-xr-x 1 root root   1214 Dec  6 08:03 .gitignore
-rwxr-xr-x 1 root root    291 Dec  6 02:29 README.md
drwxrwxrwx 2 root root      0 Dec  3 08:43 app
-rwxr-xr-x 1 root root    272 Dec  6 02:21 firebase.json
drwxrwxrwx 2 root root      0 Dec  3 09:55 functions
-rwxr-xr-x 1 root root    889 Dec  6 02:19 nuxt.config.js
-rwxr-xr-x 1 root root    666 Dec  6 08:04 package.json
-rwxr-xr-x 1 root root 210503 Dec  3 08:22 yarn.lock
bash-4.4# ls -la app/
total 4
drwxrwxrwx 2 root root    0 Dec  3 08:43 .
drwxrwxrwx 2 root root 4096 Dec  6 02:27 ..
drwxrwxrwx 2 root root    0 Dec  3 08:20 assets
drwxrwxrwx 2 root root    0 Dec  3 08:20 components
drwxrwxrwx 2 root root    0 Dec  3 08:20 layouts
drwxrwxrwx 2 root root    0 Dec  3 08:20 middleware
drwxrwxrwx 2 root root    0 Dec  3 08:20 pages
drwxrwxrwx 2 root root    0 Dec  3 08:20 plugins
drwxrwxrwx 2 root root    0 Dec  3 08:20 static
drwxrwxrwx 2 root root    0 Dec  3 08:20 store
bash-4.4#
bash-4.4# ls -la functions/
total 6
drwxrwxrwx 2 root root    0 Dec  3 09:55 .
drwxrwxrwx 2 root root 4096 Dec  6 02:27 ..
-rwxr-xr-x 1 root root  619 Dec  6 02:20 index.js
-rwxr-xr-x 1 root root  496 Dec  6 02:22 package.json

ビルド設定の変更

nuxt.config.jsで設定を行います。
変更箇所を抜粋して説明します。

nuxt.config.js
module.exports = {
  mode: 'universal',
  srcDir: 'app',
  buildDir: 'functions/nuxt',
}

srcDirにNuxt.jsのファイルがおいてある場所を指定します。
buildDirにビルドした結果を出力する先を指定します。
Cloud Functionsでビルドした結果のファイルを参照するためfunctions以下に出力します。

nuxt.config.js
  /*
  ** Build configuration
  */
  build: {
    publicPath: '/assets/',
    /*
    ** You can extend webpack config here
    */
    extend(config, ctx) {
    }
  }
}

publicPathにはCDNのURLを記述するようなのですが、ビルドしたファイルをhostingしているディレクトリ(後述)を指定します。

ビルド後のファイルの配置をpackage.jsonで設定します。

package.json
  "scripts": {
    "setup": "yarn && cd functions && yarn && cd ..",
    "dev": "nuxt dev",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "build": "nuxt build && yarn build:copy",
    "build:copy": "rimraf public/* && cpx \"functions/nuxt/dist/**/*\" public/assets/ && cpx \"app/static/*\" public/",
    "serve": "firebase serve --host 0.0.0.0",
    "deploy": "firebase deploy"
  },

build:copyでビルドされたファイルをpublicにコピーしています。
このディレクトリがFirebaseにhostingされます。

Firebaseの設定

firebase.jsonで設定します。

firebase.json
{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "function": "ssr"
      }
    ]
  },
  "functions": {
    "source": "functions"
  }
}

publicにはhostingするディレクトリを指定します。
rewritesですべてのアクセスをssrファンクションへリダイレクトするようにしておきます。
functionsにはCloud Functionsで動かすディレクトリを指定します。

SSRするファンクションの説明

functions/index.jsがSSRするファンクションになります。

functions/index.js
const functions = require('firebase-functions');
const express = require('express');
const { Nuxt } = require('nuxt');

const app = express();
const nuxt = new Nuxt({
    dev: false,
    buildDir: 'nuxt',
    build: {
        publicPath: '/assets/'
    }
});

function handleRequest(req, res) {
    res.set('Cache-Control', 'public, max-age=600, s-maxage=1200');
    return new Promise((resolve, reject) => {
        nuxt.render(req, res, (promise) => {
            promise.then(resolve).catch(reject);
        });
    });
}

app.use(handleRequest);
exports.ssr = functions.https.onRequest(app);

expressを使って、Nuxt.jsがレンダリングした結果を表示します。
Nuxt.jsを初期化する祭に指定するオプションはnuxt.config.jsと同じ内容にします。
buildDirだけは階層が違うのでnuxtだけにしてあります。

また、functions/package.jsonに以下の設定を入れておきます。

functions/package.json
  "engines": {
    "node": "8"
  },

これはNode.jsをv8で動かすという設定になります。

以上がNuxt.jsをFirebaseでSSRするための変更の要点になります。

Firebaseのローカル環境で実行

実行する前にFirebaseへのログインとプロジェクトの紐付けをします。
Firebaseのアカウントを作成してFirebaseコンソールで適当にプロジェクトを作成してください。

まずはFirebaseにログインします。
ゲストOSのコンソールでのログインは、ブラウザに連携できないのでURL経由でのログインになります。

bash-4.4# firebase login --no-localhost

上記コマンドで表示されるURLをブラウザで開くとアカウントの選択と権限の許可リクエストが表示されるので適宜選択すると、認証コードが表示されます。
その認証コードをコンソールに入力すればログイン完了です。

ログインできたら作ったプロジェクトを紐付けします。

bash-4.4# firebase use --add

上記コマンドを入力するとプロジェクトの選択肢が表示されるので、作ったプロジェクトを選択します。
次のaliasの指定は適当にdefaultでも指定してください。

これで準備ができたので実行してみます!
package.jsonscriptserveコマンドを用意しておいたので、以下を実行すれば開始されます。
firebase serve--host 0.0.0.0オプションをつけないとホストOSのブラウザからアクセスできませんでした。

bash-4.4# yarn setup
bash-4.4# yarn build
bash-4.4# yarn serve

初回だとyarn setupに結構時間がかかると思います。

bash-4.4# yarn serve
yarn run v1.7.0
$ firebase serve --host 0.0.0.0

=== Serving from '/project/nuxt-firebase-ssr-template'...

i  functions: Preparing to emulate functions.
Warning: You're using Node.js v8.11.1 but Google Cloud Functions only supports v6.11.5.
i  hosting: Serving hosting files from: public
✔  hosting: Local server: http://0.0.0.0:5000
✔  functions: ssr: http://0.0.0.0:5001/fana-dev/us-central1/ssr

上記のように表示されたら起動完了なのですが、http://0.0.0.0:5000ではなくhttp://localhost:5000をブラウザで開くと表示が確認できると思います。

これでNuxt.jsをFirebaseでSSRできるようになりました!
あとは、yarn deployでFirebaseにデプロイして動作を確認してみてください!

ハマった箇所

今回使用したサンプルでは出てきませんでしたが、環境を作っていてハマった箇所を共有しておきます。

  • package.jsondependenciesにあるモジュールはfunctions/package.jsondependenciesにも書いておく必要がある

これ、ローカル環境では動くのですが、Firebaseにデプロイすると動かなくなるので、原因が特定できなくて苦労しました。

  • Docker for Windowsではホットリロードが機能しない

ホストOSでファイルを書き換えても自動でリロードしてくれません。
nuxt.config.jsに以下を記述することで対応できます。

nuxt.config.js
  watchers: {
    webpack: {
      poll: true
    }
  },

まとめ

Dockerの環境構築は初めてだったので手探りでやってきました。
今回のDockerの構成も、もっとスマートにできるかもしれません。
これまでの記事でDockerでの開発環境の構築方法などの手助けになれば幸いです!

FirebaseとNuxt.jsも今回初めて触りましたが、いろいろ気が利いてて使いやすいサービスとフレームワークだと思いました。
次はこの構成でなにか動くものを作りたいと思います!

参考にしたサイト

Use multi-stage builds
Dockerのマルチステージビルドを使う
Container networking
nuxt.js + firebase (cloud functions) で最小構成SSR
Create a SSR + Serverless App with Firebase & Nuxt.js within an hour
IT研修でVuePress+Express+Nuxt on Dockerでシステムを作成した話
Hot reloading doesn't work in docker container on Windows

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?