Dockerコンテナに依存関係は全て閉じ込めてWebStormで開発 ~ローカルを汚さずにReactやFirebaseの環境構築を簡単に~

ローカル環境に色々入れずに開発環境をDocker上で構築したいけど、コーディングはIDEを使いたいので自分が辿り着いた手順をまとめました。
似たようなことをすれば`IntelliJ`, `PyCharm`, `GoLand`, `RubyMine`, `PhpStorm`でもできそう。

# できること
- ローカルPCにnodeなどを入れずにReactやFirebaseのアプリを開発できる
- Firebaseの複数プロジェクトに対してデプロイしたい場合、ディレクトリごとにFirebaseプロジェクトを割り当てることで`firebase use`で切り替えずに複数のアプリケーションをデプロイできる

# 前提
- WebStorm(or IntelliJ)をインストールしている
- MacでDocker for Macをインストールしている

# WebStormで新規Project作成
[File] -> [New] -> [Project]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/7810a785-1120-eb75-344d-7220da7dfff3.png)

[Empty Project]を選択し、Locationにプロジェクト名を設定して[CREATE]

|Setting| Value | Remark |
|---------|--------|------|
| Location: | {PROJECT PATH} | PATHの末尾がプロジェクト名 |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/4ad52e71-827c-1095-0c5e-ef01e051694b.png)

空のプロジェクトが作成される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/af8b0615-5922-6874-b1a6-82475c9927bf.png)


# Dockerの設定
コマンドラインかWebStormのGUIのどちらかで設定すれば良い

## Dockerコマンドラインで設定する場合

Terminalで以下を実行したあと、`Ctrl ＋ p`->`Ctrl ＋ q`で接続したコンテナから抜ける

```bash
[LOCAL]$ docker run \
-it \
-p 13000:3000 \
-v {PROJECT PATH}:/app \
--name {CONTAINER NAME} \
-w /app \
node:lts-alpine
```

|Setting| Value | Remarks|
|---------|--------|---|
| -p | `13000:3000` |`HOST:DOCKER`の順で設定し、DockerコンテナのportへLocalからアクセスできるようにする。何個も起動させる場合、HOST側のportは重ならないように変えること。|
| -v | `{PROJECT PATH}:/app` |`HOST:DOCKER`の順で設定し、Dockerコンテナの`/app`ディレクトリと先ほどLocalで作成したWebStormのプロジェクトディレクトリへ同期させることができる。|
| --name | `{CONTAINER NAME}` | 起動したコンテナにつける名前。今回は`node-docker`としている。 |
| -w | `app`| コンテナ起動時に`/app`ディレクトリを作成してコンテナへ接続した時のデフォルトディレクトリになる|


## WebStormのDockerプラグインで設定する場合
[WebStorm] -> [Preferences...] で [Build, Execution, Deployment]にあるDockerの設定画面を選択し、`+`ボタンで新規作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/f5f0a94e-57fd-6ad7-450f-3c5590a14542.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/d21e5807-6a68-2893-6015-6121cd9cdc1b.png)

Docker for Macを利用しているので、これを選択し適当に名前をつけて[OK]を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/4be02d5d-2cf8-20ba-17e8-9d4d75c2ac12.png)

（今回は画面の左下に）Dockerのタブが現れたので`▷`(Connect)を押すと、Connectedとなり`Containers`と`Images`というのが表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/a999f2f1-106f-6518-fbe0-ecae27d535f2.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/7070593d-06c8-e2b6-561f-9aaea03d37f4.png)

`Images`を選択して`Pull Image`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/87c98ae4-bf34-4191-676c-009e75888b48.png)

今回は`node:lts-alpine`を利用

|Setting| Value | Remarks |
|---------|--------|-----------|
| Registry: | registry.hub.docker.com|デフォルトの設定|
| Repository: | node | |
| Tag: | lts-alpine | ここは好きなのを選んで良い。今回は軽量と聞くalpineのlts版にした|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/2e4c002f-f748-4c75-2931-6ecc44963e82.png)

`Images`に選択したDockerのimageが表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/c8e5ba2c-88f0-6739-92c2-d2ca386114e5.png)

このimageからコンテナ作成のため`+`->`Create..`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/6d7a6a8e-31b2-a269-2c70-f72f3b2ac081.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/f3d5e37b-6ab1-a9bd-35ef-2b89f230ded4.png)

コンテナ起動の設定を以下のようにして`RUN`

|Setting| Value | Remarks|
|---------|--------|---|
| Container name: | `{CONTAINER NAME}` | 起動したコンテナにつける名前。今回は`node-docker`としている。 |
| Bind ports: | `13000:3000` |`HOST:DOCKER`の順で設定し、DockerコンテナのportへLocalからアクセスできるようにする|
| Bind mounts: | `{PROJECT PATH}:/app` |`HOST:DOCKER`の順で設定し、Dockerコンテナの`/app`ディレクトリとLocalで作成したWebStormのプロジェクトディレクトリへ同期させることができる|
| Run options: | `-w app`| コンテナ起動時に`/app`ディレクトリを作成してコンテナへ接続した時のデフォルトディレクトリになる|


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/3775dca0-b168-2377-356a-c15bbea774e9.png)

`Deploy log`にコンテナ作成が成功されたログが表示されればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/07ccd112-f7e3-2896-cbda-5a929c69ea3d.png)


# Reactのプロジェクト作成
TerminalからDockerコンテナに接続して、Docker上にReactプロジェクトを作成する

```bash
# コンテナの起動確認
[LOCAL]$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS                     NAMES
febece633a44        aa57b0242b33        "node"              About a minute ago   Up About a minute   0.0.0.0:13000->3000/tcp   node-docker

# コンテナへ接続
[LOCAL]$ docker exec -it node-docker /bin/sh
/app # 
```

ここからdocker上での実行コマンド
コンテナシェルのPS1部分`/app #`は`[DOCKER]$`に読みかえてる
https://facebook.github.io/create-react-app/docs/adding-typescript を参考にtypescriptのReactプロジェクトを作成

```bash
# nodeのversion確認
[DOCKER]$ node --version
v10.15.3

# /appディレクトリにプロジェクト作成
[DOCKER]$ npx create-react-app /app --typescript
npx: installed 63 in 3.522s

Creating a new React app in /app.

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts...

yarn add v1.13.0
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@1.2.7: The platform "linux" is incompatible with this module.
info "fsevents@1.2.7" is an optional dependency and failed compatibility check. Excluding it from installation.
info fsevents@1.2.4: The platform "linux" is incompatible with this module.
info "fsevents@1.2.4" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 13 new dependencies.
info Direct dependencies
├─ @types/jest@24.0.11
├─ @types/node@11.13.4
~ 省略 ~

Your tsconfig.json has been populated with default values.


Success! Created app at /app
Inside that directory, you can run several commands:

  yarn start
    Starts the development server.

  yarn build
    Bundles the app into static files for production.

  yarn test
    Starts the test runner.

  yarn eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

  cd /app
  yarn start

Happy hacking!
```

Dockerコンテナの`/app`ディレクトリに作成されたプロジェクトがWebStorm上からも見えるようになった!!
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/4f1b5f91-38d5-99ee-4c37-e421b8c7c426.png)


## 動作確認

```bash
# デフォルトプロジェクトの起動
[DOCKER]$ yarn start
~省略
Compiled successfully!

You can now view app in the browser.

  Local:            http://localhost:3000/
  On Your Network:  http://172.17.0.2:3000/

Note that the development build is not optimized.
To create a production build, use yarn build.
```

ブラウザでhttp://localhost:13000/ へアクセスし、デフォルトアプリが表示されることが確認できた！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/45595c8b-0dbd-23d4-79c0-185a14441b4d.png)

**あとは好きに開発するだけ!!**


# FirebaseプロジェクトごとにDocker開発環境を作成

[WebStormで新規Project作成](https://qiita.com/sugikeitter/items/339e8cae7e4e4893baab#webstorm%E3%81%A7%E6%96%B0%E8%A6%8Fproject%E4%BD%9C%E6%88%90)しておく

```bash
# firebaseを利用するためまずはnodeのimageからコンテナを起動させる
[LOCAL]$ docker run \
-it \
--name firebase-skeleton \
-v {WEBSTORM PROJECTPATH}:/app \
-w /app \
node:lts-alpine

# 起動したコンテナに接続する
[LOCAL]$ docker exec -it firebase-skeleton /bin/sh

# firebaseのツールをインストール
[DOCKER]$ npm install -g firebase-tools
# 手順にしたがって利用したいgoogleアカウントでログインする
[DOCKER]$ firebase login --no-localhost
# Firebaseプロジェクトを選択or新規作成し、セットアップする(今回はCloud Functionsをセットアップ)
[DOCKER]$ firebase init functions

# ログイン状態のコンテナをcommitしてimage化する
[LOCAL]$ docker stop firebase-skeleton
[LOCAL]$ docker commit firebase-skeleton {FIREBASE PROJECT NAME}:{YOUR TAG}
# 不要になったコンテナは削除
[LOCAL]$ docker rm firebase-skeleton

# imageが作成されていることの確認
[LOCAL]$ docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
{FIREBASE PROJECT NAME}     {YOUR TAG}          c939d073f6a0        8 seconds ago       297MB
node                        lts-alpine          aa57b0242b33        4 days ago          71MB
```

```bash

# 指定したFirebaseプロジェクトにログイン状態のimageをコンテナ起動
[LOCAL}$ docker run \
-it \
--name {CONTAINER NAME} \
-v {WEBSTORM PROJECTPATH}:/app \
-w /app \
myfirebase/{FIREBASE PROJECT NAME}:{YOUR TAG}
```

WebStormでファイルを修正できるようになるのでfunctionを作成
今回はTypeScriptでCloud Functionsを作成するので`functions/src/index.ts`をWebStormから変更
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/88066cdd-a02a-bd69-cc40-6695b9a7d03b.png)

```typescript:index.ts
import * as functions from 'firebase-functions';

export const helloWorld = functions.https.onRequest((request, response) => {
 response.send("Hello from Firebase!!!!");
});
```

## 動作確認
ログイン状態のimageを利用しているのでloginせずにFirebaseへデプロイできる!!

```bash
# 起動したコンテナに接続する
[LOCAL]$ docker exec -it {CONTAINER NAME} /bin/sh

# functionを作成してデプロイ(init時にTypeScriptを選択していると、このコマンド内でtsのコンパイルもしてくれる)
[DOCKER]$ firebase deploy --only functions
~省略~
✔  functions: Finished running predeploy script.
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (33.39 KB) for uploading
✔  functions: functions folder uploaded successfully
i  functions: creating Node.js 6 function helloWorld(us-central1)...
✔  functions[helloWorld(us-central1)]: Successful create operation. 
Function URL (helloWorld): https://XXXXXXX.cloudfunctions.net/helloWorld

✔  Deploy complete!

Please note that it can take up to 30 seconds for your updated functions to propagate.
Project Console: https://console.firebase.google.com/project/{FIREBASE PROJECT NAME}/overview
```

Function URLで表示されたURLへアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35310/0dd8490c-b827-0aa0-7f81-96949f009f21.png)

**これで複数のFirebaseプロジェクトへデプロイしたい場合に、ローカルでloginを切り替えずに実行できる!!**