NuxtJs2.13 + FirebaseプロジェクトをDockerを使ってコンテナ化


NuxtJs2.13 + FirebaseプロジェクトをDockerのコンテナ化

今回のプロジェクトをDockerのコンテナで管理することにしました。開発環境のためのディレクトリーを作っておきます。

下記のようなディレクトリー構成にしました。

├── docker 
├   ├── node 
         ├── Dockerfile
├   ├── firestore_saver
         ├── Dockerfile
├   ├── firebase
    │    ├── firestore
         │   ├── Dockerfile
         │   ├── firestore-run.sh
         │   ├── export
              ├──firebase-export-metadata.json     
              │──firestore_export
                │──firestore_export.overall_export_metadata
                │──all_namespaces        
                  │──all_kinds
                   │──all_namespaces_all_kinds.export_metadata
                   │──output-<番号> 
└── docker-compose.yml
└── .env
└── firebase-blog → アプリケーションコード

まずdocker-compose.ymlファイルを作っておきましょう。
3つのコンテナを設定します。

  • node-front
  • firestore
    Firestoreのエミュレーターの起動と管理するコンテナ
  • firestore-saver
    ローカルデータを保存するためのコンテナ

docker-compose.ymlのNodeコンテナ設定をみていきます。

node:
  build:
  context: ./docker/node
  volumes:
    - ./firebase-blog:/app/firebase-blog:cached
  container_name: node-front
  tty: true
  working_dir: /app/firebase-blog
  ports:
    - "3000:3000"
  environment:
    - HOST=0.0.0.0

アプリケーションのコードをキャッシュすることでビルドが速くなります。ポートの設定はyarn dev を実行する時使われるポートを開くためです。localhostにアクセスするため環境変数をHOST 0.0.0.0 の設定をします。

nodeのDockerfileの中身はこちらです

FROM node:14
RUN apt-get update && \
apt-get install -y vim procps net-tools && \
apt-get clean;

nodeコンテナ内でVimを使うため、vimをインストールしています。

docker-compose.ymlとnode用のDockerfileを作成したら以下コマンドでコンテナのビルドと起動をしてみましょう。
docker-compose up --build

Nuxtバージョン12.3以上を使っている場合package.jsonのdevコマンドでポートとホストの指定が必要です

ポートとホストを指定しないとコンテナ上でyarn dev を実行する際にsseのエラーがおきてしまいます。“scripts”: {
 “dev”: “HOST=0.0.0.0 PORT=<docker-compose.ymlで定義したポート> nuxt “,

nodeのコンテナ上でyarn devを起動してみましょうdocker exec -it node-front bash
yarn dev

ブラウザーでlocalhost:3000で表示を確認しましょう

次に用意するコンテナはFirestoreのローカルエミュレーター用のコンテナ

firebase.jsonファイルの中身を確認するとそれぞれのエミュレーターはどのポートにサーバされているの設定ができます。

管理をしやすくするためにFirebase関連のファイルをfirebase-configディレクトリーに入れておきます。中身はFirebaseの設定、configファイル、ログファイルなどが入ります。

ローカルシステムとマッチするため、ホストを0.0.0.0に指定して、ポートはデフォルトは好きな使ってないポートに設定します。

この設定にすることによるエミュレーターを起動したらブラウザーでlocalhost:<ポート>でアクセスできるようになります。

"emulators": {
  "firestore": {
    "port": 8080,
    "host": "0.0.0.0"
  },
  "ui": {
    "port": "4000",
    "host": "0.0.0.0"
  },
}

Firestoreのdocker-compose.yml 設定

firestore:
  image: firestore
  depends_on:
    - node
    - firestore-saver
  build:
  context: ./docker/firebase/firestore
  volumes:
    - ./firebase-blog/firebase-config:/app/firebase-config:cached                
    - ./firebase-blog/firebase.json:/app/firebase-      
      config/firebase.json:cached
    - ./firebase-blog/.firebaserc:/app/firebase- 
      config/.firebaserc:cached
    - ./docker/firebase/firestore/export:/app/firebase-config/export
    - ./docker/firebase/firestore/emulator_cache:/root/.cache
  container_name: firestore
  tty: true
  working_dir: /app/firebase-config
  ports:
    - "8080:8080"
    - "4000:4000"
  environment:
    - HOST=0.0.0.0
    - TOKEN=${TOKEN}
    - PROJECT_ALIAS=${PROJECT_ALIAS}
  command: /bin/bash run-firestore.sh

このコンテナだけで使うことがないので、depends_onの設定でfirestoreコンテナが立ち上がるとnodeとfirestore-saverの両方が起動されるように設定します。

各Volumeを説明します

  • ./docker/firebase/firestore/export:/app/firebase-config/export
  • ./firebase-blog/firebase.json:/app/firebase-config/firebase.json:cached
  • ./firebase-blog/.firebaserc:/app/firebase-config/.firebaserc:cached

Firebaseに関する設定やコンフィグのファイルはfirebase-configのディレクトリーに入っています。

ディレクトリーの中身はこちらです

.
├── firebase-blog
├   ├── firebase-config
    │    ├── .firebaserc
         ├── firebase.json
         ├── firestore-debug.log
         ├── ui-debug.log
         ├── firstore.indexes.json
         ├── firestore.rules
         ├── export
             ├──firebase-export-metadata.json     
             │──firestore_export
               │──firestore_export.overall_export_metadata
               │──all_namespaces        
                 │──all_kinds
                  │──all_namespaces_all_kinds.export_metadata
                  │──output-<番号>

毎回エミュレーターをインストールしないためにエミューレーターのキャッシュファイルもマウントしておきます

  • ./docker/firebase/firestore/emulator_cache:/root/.cache

FirestoreのDockerfileの中身はこちらです。

FROM node:14
RUN apt-get update && \
apt-get install -y openjdk-8-jdk jq\
ca-certificates-java \
ant \
vim procps net-tools && \
apt-get clean && \
update-ca-certificates -f;
RUN yarn global add firebase-tools
COPY run-firestore.sh /usr/local/bin/run-firestore.sh

ベースはNodeでエミュレーターを使うためのイメージにJavaとVimを使いたかったので一緒にインストールしました。。また、コンテナ内で使うためVimもインストールしました。
今回、起動時の シェルでFirebaseコマンドを使うためにFirebaseCLIもインストールしました。

Firestoreコンテナの起動時にエミューレーターを起動したいので、run-firestore.shで起動しています。エミュレーターを起動する時にローカルに保存したデータを使いたいので、マウントされているディレクトリーからインポートしています。

#!/bin/bash
set -eu
auth=" - token $TOKEN"
echo "プロジェクト設定"
firebase use $PROJECT_ALIAS $auth
echo "Firestoreのエミュレーター起動開始 約2分"
firebase emulators:start - only firestore - import="./export/" $auth

最後にローカルデータを保存するためのコンテナを用意します。

firestore-saver:
  build:
  context: ./docker/firebase/firestore_saver
  container_name: firestore-saver
  environment:
    - PROJECT_ID=${PROJECT_ID}
    - FIRESTORE_EMULATOR_HOST=firestore:8080
  volumes:
    - ./docker/firebase/firestore/export:/firebase/firestore/export
  command: /bin/bash run-saver.sh

起動時に実行するrun-saver.shの中身はこちらです。

#!/bin/bash
while true
do
curl -X POST -H "Content-Type: application/json" -d "{\"database\": \"projects/$PROJECT_ID/databases/(default)\", \"export_directory\": \"./export\", \"export_name\": \"firestore_export\"}" http://firestore:8080/emulator/v1/projects/$PROJECT_ID:export
sleep 5
done

Firestore-saverのDockerfile中身

FROM google/cloud-sdk:alpine
RUN apk add --update --no-cache openjdk8 \
&& gcloud components update \
&& gcloud components install cloud-firestore-emulator beta --quiet
COPY run-saver.sh /usr/local/bin/run-saver.sh

コマンドの詳しい説明はゆうくんのブログを参考に

Firestore Local Emulator のデータを擬似的に永続化する
Firestore Local Emulator にimport/export機能が追加されたらしいので、それを利用して擬似的に永続化しました。

環境を使ってみましょう

この三つのコンテナが用意すれば使い回せる開発環境ができます。

まずdocker-compose up —-build を実行してみます。

ターミナルでエミュレーターの起動が終わるまでfirestore-exportのコンテナからエラーが表示されます。上から見てみると Could not resolve host: firestore のエラーはホストとなっているコンテナが立ち上がってないという意味です。次は Failed to connect to firestore port 8080: Connection refused Firestoreのコンテナが立ち上がっているけどポート8080にローカルエミュレーターはまだ起動されていないの意味です。その次は正常な動きです。

firestore-saverの起動中のエラーと正常な動き

正常に立ち上がったNodeコンテナのログは以下です。

Nodeコンテナの正常な起動

そして、最後にFirestoreとエミュレーターが正常に立ち上がったら以下のようなメッセージが表示されているはずです。

Firestoreコンテナが正常に立ち上がった場合

エミュレーターが起動されたら5秒ずつfirestore-saverが動いてローカルデータが保存されます。

全部が立ち上がったらNodeコンテナを入りyarn devを実行しましょう

各ポートにアクセスすると表示される画面を載せておきます

localhost:3000 → Nuxtプロジェクトのホーム画面
localhost:4000 → Firebaseのローカルエミュレータースイート
localhost:8080 → OK (Firestoreのエミュレーターが正常に動いているの確認)

Dockerで環境を管理すると新しい開発メンバーのジョインやバージョン管理がよりやりやすくなります。
みなさんもDockerで環境構築してみましょう。