DockerでのVue.js + Firebaseの開発環境設定 in AWS Cloud9

# 実行環境
- Amazon Linux2(シンガポールリージョンのEC2上で動作）
- Cloud9
- zsh

# やりたいこと
Cloud9上で
- Vue.jsをビルドできるコンテナを作成する
- Firebaseにデプロイできるコンテナを作成する

# 目標のディレクトリ構成
以下のようなディレクトリ構成になるように設定していく

```
.
├── docker-compose.yml
├── firebase.dockerfile
├── LICENSE
├── markdown
│   ├── babel.config.js
│   ├── dist
│   │   ├── css
│   │   │   └── app.a2762106.css
│   │   ├── favicon.ico
│   │   ├── img
│   │   │   └── logo.82b9c7a5.png
│   │   ├── index.html
│   │   └── js
│   │       ├── app.485b9e24.js
│   │       ├── app.485b9e24.js.map
│   │       ├── chunk-vendors.f3d3e1a9.js
│   │       └── chunk-vendors.f3d3e1a9.js.map
│   ├── firebase.json
│   ├── node_modules
│   │   └── ...
│   ├── package.json
│   ├── public
│   │   ├── favicon.ico
│   │   └── index.html
│   ├── README.md
│   ├── src
│   │   ├── App.vue
│   │   ├── assets
│   │   │   └── logo.png
│   │   ├── components
│   │   │   └── HelloWorld.vue
│   │   └── main.js
│   ├── vue.config.js
│   └── yarn.lock
├── README.md
└── vue.dockerfile
```

# 方法
- Dockerfileは作成するコンテナ毎に分ける
- docker-composeでコンテナを管理

## vue-cli3をインストールするコンテナの作成
### vue.dockerfile
まずはVue.js用のdockerfileを作成する

```dockerfile
# 最初のvue.dockerfile
FROM node:8.11.3-alpine
WORKDIR /markdown
RUN apk update && \
    npm install -g @vue/cli && \　#vue-cli3をインストール
```

## Firebaseのcliツールをインストールするコンテナの作成
### firebase.dockerfile
Firebaseのcliツールをインストールし、コードをデプロイするためのコンテナを作成する

```dockerfile
# firebase.dockerfile
FROM node:8.11.3-alpine
WORKDIR /markdown
RUN apk update && \
    npm install -g firebase-tools
```

## コンテナを管理するdocker-compose.yml

```yml
version: '3'
services:
    vue:
        build:
            context: ./
            dockerfile: vue.dockerfile
        volumes:
            - ./markdown:/markdown
        ports:
            - '8080:8080'
        tty: true

    firebase:
        build:
            context: ./
            dockerfile: firebase.dockerfile
        volumes:
            - ./markdown:/markdown
        ports:
            - '9005:8081'
        tty: true
```

## コンテナを立ち上げて、vue-cli3でプロジェクトを作成する

```shell
# tty: trueでコンテナをupしたままにする
% docker-compose up & 
# vue（コンテナ名）のコンテナ内に入る
% docker-compose exec vue sh

#vue create コマンドで新しいVue.jsのプロジェクトを作成する
root# vue create
#実行後に色々な質問に答えていく
#プロジェクト名はmarkdown
#パッケージ管理は今回はyarnを選択
#全て終わると、markdownという名前のディレクトリが作成される
```

## vue.dockerfileとdocker-compose.ymlを修正
`yarn`markdown以下でyarnが使えるようになっているので、vue.dockerfileとdocker-compose.ymlでコンテナを立ち上げたときに、yarn installとyarn run serveで開発ビルドと開発サーバが立ち上がるように変更を加える

```diff
  FROM node:8.11.3-alpine
  WORKDIR /markdown
  RUN apk update && \
      npm install -g @vue/cli && \
+     yarn install 
```

```diff
version: '3'
services:
    vue:
        build:
            context: ./
            dockerfile: vue.dockerfile
        user: "${CURRENT_UID}"
        volumes:
            - ./markdown:/markdown
        ports:
            - '8080:8080'
+       command: 'yarn run serve'
-       tty: true

      省略
```

これで、`docker-compose up`した時に開発ビルドと開発サーバが立ち上がるようになる

## コンテナを立ち上げてFirebaseにログインをする
続いて、Firebaseにログインをする

コンテナ内でfirebase login叩いた後に出るURLに飛んでgoogleアカウントの認証をするが、その後飛ばされるlocalhost:9005はCloud9からアクセスできない
なので、`--no-localhost`オプションをつけると、URLが表示され、その遷移先で表示されるトークンを入力することでログインができる


```shell

firebaseコンテナに入る
% docker-compose exec firebase sh

root# firebase login --no-localhost

#login後にfirebaseのprojectの作成
root# firebase init
#色々な質問に答える
#deployするディレクトリはmarkdown/dist以下に設定する
```

## Vue.js本番ビルドをmarkdown/dist以下からdeploy

vue.config.jsでビルドファイルの出力先をmarkdown/dist以下にする

```js
module.exports = {
  devServer: {
    contentBase: 'dist',
  },
}
```

その後、vueコンテナに入るビルドコマンドを叩くと、markdown/dist以下にビルドファイルが出力される

```sh
% docker-compose up &
% docker-compose exec vue sh

root# yarn run build
```

ビルド後に

```sh
% docker-compose exec firebase sh

root# firebase deploy
```

を実行するとfirebaseにdeployされる
これで、Dockerを使った、Vue.jsとFirebaseの開発環境設定ができた

# ハマったポイント
## Invalid Host header
vueコンテナ内でビルドしたVue.jsの開発ビルドをプレビューしようと、Cloud9の'Previewタブ' -> 'Running Preview Application'クリックすると`Invalid Host header`というエラーメッセージが出てしまう
![スクリーンショット 2018-12-18 21.22.08.png](https://qiita-image-store.s3.amazonaws.com/0/272298/8a26f907-acd0-caf1-852d-2d754d080e8c.png)

最初はコンテナのPortの設定がうまくいっていないのかと、いろいろ検索したが、最終的にWebpackの設定の問題ということがわかった

Webpackの何が問題なのか、またなぜこのように設定を書くと接続できるようになるかはわかっていないが`vue.config.js`に以下のように設定すると

```js
module.exports = {
  devServer: {
    disableHostCheck: true,
    contentBase: 'dist',
  },
}
```

![スクリーンショット 2018-12-18 21.26.59.png](https://qiita-image-store.s3.amazonaws.com/0/272298/20b82e33-a30d-567e-eb11-dd69cc616164.png)
Vue.jsの最初の画面がCloud9上のプレビューに表示される

Webpackの設定に関してはまだまだわかっていないことが多いので、実践して知識とノウハウをつけていきたい

## コンテナ内でのrootユーザでのコマンド実行
コンテナ内に入ってシェルを立ち上げると、何も設定しなければrootユーザで立ち上がると思います
そのまま、rootユーザでVue.jsやFirebaseの新規プロジェクトを作成すると、ファイルの所有者がrootユーザになってしまい
ボリューム（ディレクトリ）を共有しているコンテナのホスト側からファイルを編集、削除などできなくなってしまいます
また、プロジェクトを作成した後も、Vue.jsの本番ビルドもコンテナから行うので、出力ファイルの所有者もrootになってしまいます
そのつど、`chown -R ec2-user markdown/`などすれば問題ないといえばないのですが、なんか気持ち悪い...

なので、以下のように`docker-compose.yml`を変更しました

```diff

version: '3'
services:
    vue:
        build:
            context: ./
            dockerfile: vue.dockerfile
+       user: "${CURRENT_UID}"
        volumes:
            - ./markdown:/markdown
        ports:
            - '8080:8080'
        command: 'yarn run serve'
        tty: true

    firebase:
        build:
            context: ./
            dockerfile: firebase.dockerfile
        volumes:
            - ./markdown:/markdown
        ports:
            - '9005:8081'
        tty: true
```
そして、

```
% CURRENT_UID=$(id -u):$(id -g) docker-compose up
```

こうすることで、コンテナ内でシェルを実行したさいのUIDとGIDが、ホストのUIDとGIDと同じになります
この状態でコンテナからファイルを作成しても、ホストからそのファイルを見たときの所有者が、ホストのユーザになるので便利です
ただ、firebaseやvueコマンドの実行権限がないので、新たにプロジェクトなどは作成できませんが...
が、これがベストプラクティスかというと、かなり怪しいですが
もっと良い方法があれば、知りたいです

参考：[Running a Docker container as a non-root user](https://medium.com/redbubble/running-a-docker-container-as-a-non-root-user-7d2e00f8ee15)