tag:crieit.net,2005:https://crieit.net/users/dorakueyon/feed
dorakueyonの投稿 - Crieit
Crieitでユーザーdorakueyonによる最近の投稿
2019-12-30T14:15:44+09:00
https://crieit.net/users/dorakueyon/feed
tag:crieit.net,2005:PublicArticle/15658
2019-12-30T14:15:44+09:00
2019-12-30T14:15:44+09:00
https://crieit.net/posts/Firebase-Vue-js-Cloud-Functions-docker-compose
Firebase フロントエンド(Vue.js)/Cloud Functions を同一docker-composeで実行する際に感じたこと
<h2 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h2>
<p>2019/12/30現在において、Firebaseを開発する際に生じる不都合の一つとして、node version管理があると思います.</p>
<p>Cloud Functionsの開発において、nodeのバージョンはversion8、あるいは10がサポートされています.</p>
<blockquote>
<p>Cloud Functions ランタイムに関数をデプロイするには Firebase CLI が必要です。Node.js バージョン 8 と 10 がサポートされています。Node.js と npm をインストールする場合は、Node Version Manager をおすすめします。<br />
<a target="_blank" rel="nofollow noopener" href="https://firebase.google.com/docs/functions/get-started?hl=ja">https://firebase.google.com/docs/functions/get-started?hl=ja</a></p>
</blockquote>
<p>つまり、こんなことが頻発します.</p>
<pre><code class="bash">[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.
</code></pre>
<p>このように、異なるversionが依存する開発では、その依存性をDockerに閉じ込めることで幸せになれます.</p>
<p>参考として、githubにcodeを置かせていただきました.<br />
- <a target="_blank" rel="nofollow noopener" href="https://github.com/dorakueyon/vue-firebase-cloud-function-docker-starter">github.com/dorakueyon/vue-firebase-cloud-function-docker-starter</a></p>
<h2 id="Vue.js + Cloud Functionsのフォルダ構成"><a href="#Vue.js+%2B+Cloud+Functions%E3%81%AE%E3%83%95%E3%82%A9%E3%83%AB%E3%83%80%E6%A7%8B%E6%88%90">Vue.js + Cloud Functionsのフォルダ構成</a></h2>
<p>Docker化する前に、プロジェクトを作成した上でフォルダ構成をみてみます.</p>
<ul>
<li>Vue.js + Firebaseプロジェクト作成</li>
</ul>
<pre><code class="bash">$ vue create project-name
$ firebase init
</code></pre>
<ul>
<li>フォルダ構成 (node_modules配下を除いています)</li>
</ul>
<pre><code class="bash">.
├── 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
</code></pre>
<p>FrontとCloud Functionsを開発する場合、プロジェクト直下と./funcions直下とで開発環境が分かれます.</p>
<p>それぞれにDockerfileを配置した上で、プロジェクト直下にdocker-compose.ymlを配置します.</p>
<ul>
<li>Dockerファイルを追加したフォルダ構成 (node_modules配下を除いています)</li>
</ul>
<pre><code class="bash">.
├── 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
</code></pre>
<h2 id="Docker化"><a href="#Docker%E5%8C%96">Docker化</a></h2>
<p>Docker化にむけて、下記の要件があります.</p>
<ul>
<li>プロジェクト直下のfirebase関連ファイル(firebase.json, .firebaserc)を、./functions側でも参照したい</li>
<li>./functions側からプロジェクト直下の定数やtypeをシンボリックリンクで参照する場合がある</li>
</ul>
<p>上記を念頭に、<code>Dockerfile/docker-compose.yml</code>を作成します.</p>
<h3 id="プロジェクト直下のDockerfile"><a href="#%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E7%9B%B4%E4%B8%8B%E3%81%AEDockerfile">プロジェクト直下のDockerfile</a></h3>
<ul>
<li>こちらは特に考えることはありません</li>
</ul>
<pre><code class="Docker">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
</code></pre>
<h3 id="./functionsのDockerfile"><a href="#.%2Ffunctions%E3%81%AEDockerfile">./functionsのDockerfile</a></h3>
<p>./functions/Dockerfileと、./docker-comose.ymlを比較しながらご確認ください.</p>
<ul>
<li>WORKDIRを<code>/work/functions</code>とする</li>
<li>./functions直下のファイルCOPYは、コピー元をプロジェクト直下からの相対パスで指定する</li>
<li>プロジェクト直下のファイルCOPYは、コンテナの<code>/work</code>配下に設置する</li>
</ul>
<pre><code class="Docker">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
</code></pre>
<h3 id="プロジェクト直下のdocker-compose.yml"><a href="#%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E7%9B%B4%E4%B8%8B%E3%81%AEdocker-compose.yml">プロジェクト直下のdocker-compose.yml</a></h3>
<ul>
<li>上記Cloud FunctionsのDockerfileにあわせるため<code>build:</code>のcontext, dockerfileを別に指定する</li>
<li>symbolic linkされている(プロジェクト直下にある)実体ファイルの変更もfunctions側のdockerに反映させるため、<code>./src:/work/src</code>を追加</li>
</ul>
<pre><code class="Docker">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
</code></pre>
<h2 id="開発の場合"><a href="#%E9%96%8B%E7%99%BA%E3%81%AE%E5%A0%B4%E5%90%88">開発の場合</a></h2>
<p>開発環境立ち上げます</p>
<pre><code class="bash">$ docker-compose build && docker-compose up
</code></pre>
<p>その後の開発は下記のようにすすめます</p>
<h3 id="front"><a href="#front">front</a></h3>
<p><code>http://localhost:8080</code>にVue.jsプロジェクトがホットリロードされます</p>
<h3 id="Cloud Functions"><a href="#Cloud+Functions">Cloud Functions</a></h3>
<p>こちらは多少面倒ではあります</p>
<ol>
<li>dockerコンテナに入る</li>
<li>firebase loginしていなければfirebase loginを実施</li>
<li>firebase serveやfirebase functions:shellなどでdebugging</li>
</ol>
<p>以下 <code>docker exec functions -it sh</code> してコンテナ内で</p>
<pre><code>$ firebase login # if you are not logged in.
$ firebase serve
$ firebase functions:shell
</code></pre>
<p>Cloud Functionsのデバッグについてはまだペインが多いです..</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://firebase.google.com/docs/functions/local-shell?hl=ja">関数をインタラクティブにテストする(Firebase official)</a></li>
</ul>
<p>firebase loginの認証情報を永続化(都度<code>fireabase login</code>したくない)人は下記のサイトが参考になるかもしれません(試していません..)</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/pannpers/items/244a7e3c18d8c8422e4f">[Firebase] Cloud Functionsで消耗したくない人のために、開発環境のベストプラクティスをまとめていったらDockerに行き着いた話</a></li>
</ul>
<h2 id="deploy"><a href="#deploy">deploy</a></h2>
<ul>
<li>github actions/ circleCIなど利用したほうがよいでしょう.</li>
</ul>
<h2 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h2>
<p>少し強引な形となってしまいましたが、Dockerの恩恵をうけることができる状態までできました.<br />
ただし、<code>./functions/Dockerfile</code>のフォルダのコンテキストが親階層直下にある点に気持ち悪さがあります.</p>
<p>ここの気持ちわるさの解消は今後の課題とします.</p>
dorakueyon