Dockerでnode.jsプロジェクトの開発環境構築

# 動機
- 新しくMac Book Pro 2019を購入。nodeやpytonの開発環境構築にあたっては, 素の環境を綺麗なままにしたいことや、いろいろな環境での開発を行いためDockerを用いることにした。
- Docker周りの設定や概念の理解で少し躓いたのでここに記す。エキスパートには当たり前すぎる内容だとしてもだ。

# やりたいこと
node.js, angular, ionic, webpack を使ったweb appやPWAの開発を、
- ホストのローカル環境を汚すことなく(Dockerを用い)
- ホストで動くVS Codeを用いたコーディング&デバッグしつつ
- ホストのターミナルからgitを用いたバージョニングしつつ
- 仮想マシンでionic server等で起動したサーバとその上で動くweb appを、ホストのブラウザからアクセスし動作確認・デバッグ

# 手順
## 1. Docker desktop for Macをインストール

https://hub.docker.com からダウンロード。ユーザ登録も済ます。

## 2. プロジェクトのディレクトリ構造

最終的に下記になります。順番に作っていきましょう。

```
- Dockerfile
- docker-compose.yml
- app
   |- ここに諸々のソースを置く
```
##  3. Dockerfile

`Dockerfie` : Imageを作成する設定ファイル
`Image` : 仮想マシンの雛形。OS, インストールされたソフトウェアセット等からなる。

node.js入りのalpine linuxのimageをベースに、ionicとcordovaをインストールしたimageを作成する。

`alpine linux` : 超軽量linux. Dockerの仮想マシンOSとして頻繁に利用されている.

```bash:host
$ cat Dockerfile
FROM node:10.13-alpine
WORKDIR /app
RUN npm install -g ionic cordova
CMD ["sh"]
```

下記コマンドでDocker imageを作成します。最初に実行する場合ローカルにnode:10.13-alpineのイメージがないのでdocker hubからダウンロードされてきます。2回目以降はローカルのイメージが使われます。

```bash:host
$ docker build . -t watashino-image
```

-tはイメージの名前を与えるオプションです。適当につけてOK。
正常に完了すればdocker imagesでimageが作成されていることが確認できます。nodeのイメージもダウンロードされローカルに保存されています。

```bash:host
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
watashino-image                latest              da6f921ba540        2 minutes ago       133MB
node                10.13-alpine        93f2dcbcddfe        13 months ago       70.3MB
```

このimageが仮想マシンの雛形となります。

## 4. docker-compose.yml

続いて、imageを実際に起動するためのファイルを記述します。

`docker-compose.yml` : docker-composeというコマンドへ与える設定ファイル

`docker-compose` :imageからcontainerを作成・起動するプログラム。

`container` : 厳密な話は置いておいて、ざっくりいうと、雛形であるimageから作成された仮想マシンを表す。型とそのインスタンス、という関係に近い。

実際のところ、"docker"コマンドだけでもcontainerの作成・起動はできるが、オプションの指定等が面倒だったり複数のcontainerの起動等も同時に行えるdocker-composeの方が便利そうなので、最初からこちらを採用。

```bash:host
$ cat docker-compose.yml
version: '3'
services:
  app:
    build: .
    image: watashino-image
    volumes:
      - ./app:/app
    ports:
      - "8100:8100"
    tty: true

```

下記のコマンドでcontainerの作成・起動。まだローカルにappフォルダを作っていない場合には作っておく。

```bash:host
$ mkdir app
$ docker-compose up -d
```
何事もなくコマンドが終了すれば成功。仮想マシンはバックグラウンドで動作している。下記コマンドで実行中のcontainerの一覧が見れる。

```bash:host
$ docker ps 
```

お気づきの方もいるでしょうが、実はstep 3のdocker buildのコマンドは実行しなくてもよかったのです。"build: ."の行はビルドに関する指示で、docker-composeファイルは指定されたimageが存在しない場合には自動的にDockerfileを参照してimageのbuildと、続いてcontaierの作成と起動を行います。imageとcontainerの関係を明らかにするために冗長な手順とさせてもらいました。

他の設定の意味: 
`services` docker-composeでは仮想マシンをserviceという呼び方をするようです。ここでは"app"というサービスを一つ定義しています。複数の仮想マシン＝サービスの設定を書いてまとめて起動することもできます。というかそれこそがdocker-composeが便利な点で使う理由です。
`volumes` ローカルの./appフォルダと仮想マシンにおける/app (root直下)を結びつけます。これによより仮想マシンとホストマシンのファイルが同期されるようになります。

`ports` ポートフィワーディングの設定。仮想マシンのport 8100とローカルのport 8100を結びつけます。仮想マシンにおけるlocalhost:8100に、ホストマシンからlocalhost:8100でアクセスできるようになります。（ただし後述の注意点あり）

`tty` これがtrueではないと、containerは起動後すぐに終了してしまう。

## 5. 動作中の仮想マシンを操作

バックグランドで実行されているcontainerには下記のコマンドでアクセスできます。

```bash:host
$ docker exec app sh
```

何をしているかというとappサービス(docker-compose.xmlで指定した名称)でshの実行を指示しています。上記を実行するとシェルで仮想環境の中をいじれるようになります。

仮想環境から退出するには

```bash:container
# exit
```

します。


## 6. gitからソースを持ってくる&VS Codeで読み込む

app以下にソースコードを持ってきましょう。ローカルのappフォルダで通常通りgitでソースファイルを持ってきます。

```bash:host
$ cd app
$ git clone アドレス
```

仮想環境にgitをinstallすれば仮想環境で直接gitを使えるでしょうが(未確認)、わざわざそうする必要もないかと思います。

ファイルは同期されていますから、特に意識することなくローカル環境のVS Codeを開き、ローカルのappフォルダを開けばOK


## 7. 仮想環境でビルド＆実行

```bash:host
$ docker-compose exec app sh
```
で仮想環境に入りappフォルダを覗けばソースコードが見えるはずです。あとは通常通りの開発ルーチンに従います。ここではionicプロジェクトの例では、下記のようなことを行います。ionic(ng)のビルドインサーバをポートフォワーディングで設定したportで起動しています。

```bash:container
# npm install
# ionic serve -p 8100 --address 0.0.0.0
```

これによりホスト環境から localhost:8100 で仮想環境内のWEBサーバにアクセスできます。--address 0.0.0.0 を付けないとホストからは見れません。理由は[こちら](https://qiita.com/amuyikam/items/01a8c16e3ddbcc734a46)をご参考ください。

## 8. Containerの停止, 再開
開発が終わったら起動させておくのは無駄でしょうから、Containerを停止します。

```bash:host
$ docker-compose stop
```

で停止, 

```bash:host
$ docker-compose up -d
```
で再開します。なお、containerを停止すると、Continerの起動中に作成したファイルは、`volumes`でホストのディレクトリに保存しているもの以外は全て消えてしまいます。

containerのライフサイクル、データの永続化については以下のような記事を参考ください。
- https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32
- https://qiita.com/onokatio/items/fcc9f8f94f8533bb030a

なお今回はホストのファイルシステムとの同期、という方法のみを扱っていますが、そのほかにも永続化手段もある模様（未トライ）

## 9. container, imageの削除
ニッチもさっちも行かなくなった場合、全ての過去を消し去りたいと思ったら、`docker-compose down`コマンドを使います。下記が参考になります。

- https://qiita.com/suin/items/19d65e191b96a0079417

気軽に環境丸ごと削除してやり直せるというのが、Dockerを用いた開発の最大のメリットでしょう。


以上です。