はじめに

2019/12/30現在において、Firebaseを開発する際に生じる不都合の一つとして、node version管理があると思います.

Cloud Functionsの開発において、nodeのバージョンはversion8、あるいは10がサポートされています.

Cloud Functions ランタイムに関数をデプロイするには Firebase CLI が必要です。Node.js バージョン 8 と 10 がサポートされています。Node.js と npm をインストールする場合は、Node Version Manager をおすすめします。
https://firebase.google.com/docs/functions/get-started?hl=ja

つまり、こんなことが頻発します.

[dorakueyon]% yarn build
yarn run v1.19.2
error functions@: The engine "node" is incompatible with this module. Expected version "8". Got "10.15.3"
error Commands cannot run with an incompatible environment.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

このように、異なるversionが依存する開発では、その依存性をDockerに閉じ込めることで幸せになれます.

参考として、githubにcodeを置かせていただきました.

Vue.js + Cloud Functionsのフォルダ構成

Docker化する前に、プロジェクトを作成した上でフォルダ構成をみてみます.

  • Vue.js + Firebaseプロジェクト作成
$ vue create project-name
$ firebase init
  • フォルダ構成 (node_modules配下を除いています)
.
├── README.md
├── babel.config.js
├── firebase.json
├── functions
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   ├── src
│   └── tsconfig.json
├── node_modules
├── package.json
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── App.vue
│   ├── assets
│   ├── components
│   ├── main.ts
│   ├── router
│   ├── shims-tsx.d.ts
│   ├── shims-vue.d.ts
│   └── views
├── tsconfig.json
└── yarn.lock

FrontとCloud Functionsを開発する場合、プロジェクト直下と./funcions直下とで開発環境が分かれます.

それぞれにDockerfileを配置した上で、プロジェクト直下にdocker-compose.ymlを配置します.

  • Dockerファイルを追加したフォルダ構成 (node_modules配下を除いています)
.
├── Dockerfile <-
├── README.md
├── babel.config.js
├── docker-compose.yml <-
├── firebase.json
├── functions
│   ├── Dockerfile <-
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   ├── src
│   │     ├── index.ts
│   │     └── services
│   │         └── project-name -> ../../../src/services/project-name <- シンボリックリンク
│   └── tsconfig.json
├── node_modules
├── package.json
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── App.vue
│   ├── assets
│   ├── components
│   ├── main.ts
│   ├── router
│   ├── services
│   │   └── project-name <- シンボリックリンク先
│   │       └── constants.ts
│   ├── shims-tsx.d.ts
│   ├── shims-vue.d.ts
│   └── views
├── tsconfig.json
└── yarn.lock

Docker化

Docker化にむけて、下記の要件があります.

  • プロジェクト直下のfirebase関連ファイル(firebase.json, .firebaserc)を、./functions側でも参照したい
  • ./functions側からプロジェクト直下の定数やtypeをシンボリックリンクで参照する場合がある

上記を念頭に、Dockerfile/docker-compose.ymlを作成します.

プロジェクト直下のDockerfile

  • こちらは特に考えることはありません
FROM node:12-alpine

ENV WORKDIR /work
WORKDIR $WORKDIR

COPY package.json $WORKDIR
RUN yarn

COPY tsconfig.json $WORKDIR
COPY *.config.js $WORKDIR/
COPY public $WORKDIR/
COPY src $WORKDIR/src

EXPOSE 8080
CMD yarn serve

./functionsのDockerfile

./functions/Dockerfileと、./docker-comose.ymlを比較しながらご確認ください.

  • WORKDIRを/work/functionsとする
  • ./functions直下のファイルCOPYは、コピー元をプロジェクト直下からの相対パスで指定する
  • プロジェクト直下のファイルCOPYは、コンテナの/work配下に設置する
FROM node:8-alpine

ENV WORKDIR /work/functions
WORKDIR $WORKDIR

COPY ./functions/package.json $WORKDIR
RUN yarn
# firebase
RUN yarn global add firebase-tools

COPY ./functions/tsconfig.json $WORKDIR
COPY ./functions/lib $WORKDIR/lib
COPY ./functions/src $WORKDIR/src
# # for symbolic link (./functions/src/services/project-name)
# COPY ./src/services/project-name /work/src/services/project-name

# firebase
COPY firebase.json /work
COPY .firebaserc /work
# COPY ./functions/.runtimeconfig.json $WORKDIR # if needed
# COPY ./functions/credentials $WORKDIR/credentials # if needed

# settings for runtime emulator
ENV HOST 0.0.0.0
EXPOSE 5000

EXPOSE 9005

プロジェクト直下のdocker-compose.yml

  • 上記Cloud FunctionsのDockerfileにあわせるためbuild:のcontext, dockerfileを別に指定する
  • symbolic linkされている(プロジェクト直下にある)実体ファイルの変更もfunctions側のdockerに反映させるため、./src:/work/srcを追加
version: '3'

services:
    main:
        build: .
        container_name: front
        volumes:
            - ./public:/work/public
            - ./src:/work/src
        ports:
            - 8080:8080
        tty: true
        command: yarn serve

    functions:
        build:
            context: ./
            dockerfile: ./functions/Dockerfile
        container_name: functions
        volumes:
            - ./functions/lib:/work/functions/lib
            - ./functions/src:/work/functions/src
            - ./src:/work/src # for symbolic link
        # environment:
        #    - GOOGLE_APPLICATION_CREDENTIALS=./credentials/firebase-adminsdk.json
        ports:
            - 5000:5000
            - 9005:9005
        tty: true

開発の場合

開発環境立ち上げます

$ docker-compose build && docker-compose up

その後の開発は下記のようにすすめます

front

http://localhost:8080にVue.jsプロジェクトがホットリロードされます

Cloud Functions

こちらは多少面倒ではあります

  1. dockerコンテナに入る
  2. firebase loginしていなければfirebase loginを実施
  3. firebase serveやfirebase functions:shellなどでdebugging

以下 docker exec functions -it sh してコンテナ内で

$ firebase login # if you are not logged in.
$ firebase serve
$ firebase functions:shell

Cloud Functionsのデバッグについてはまだペインが多いです..

firebase loginの認証情報を永続化(都度fireabase loginしたくない)人は下記のサイトが参考になるかもしれません(試していません..)

deploy

  • github actions/ circleCIなど利用したほうがよいでしょう.

おわりに

少し強引な形となってしまいましたが、Dockerの恩恵をうけることができる状態までできました.
ただし、./functions/Dockerfileのフォルダのコンテキストが親階層直下にある点に気持ち悪さがあります.

ここの気持ちわるさの解消は今後の課題とします.