ローカル環境に入れるものはDockerだけでnode.jsの開発環境作れるんじゃないの？

## ゴール

`node ファイル名`とか`npm init`とか`npm install jquery --save`とかが実行できる。
今回作った環境は一応[ここ](https://github.com/togana/my-dev)に上げておきました。こうしたほうがいいなどあればコメント残していただけると幸せです。


## きっかけ

node.jsを勉強したいと思って環境作ろうとした時、nodeのバージョン管理ツール入れるべきと助言をもらい、勉強始めるまでの敷居の高さ(本質じゃないところに時間をとられる)を感じた。
rbenvを使ってたこともあり、新しい言語を勉強し始めるタイミングで、また別の管理ツールを入れるのは違うなと思い始めた。
dockerならば、imageのバージョンを書き換えるだけで、バージョンの切り替えが可能なので、docker+docker-composeによる開発環境を作れないかなと考えた。

## 今回作る環境

Windowsにvagrantを使ってVM(ubuntu/trusty64)を立ててその中にdockerとdocker-composeを導入してnode.jsの環境を作る。
おそらくWindows以外の環境でも同じような形で作れると思います。

バージョンは以下です。

* Vagrant
    * 1.7.4
* VirtualBox
    * 5.0.2
* Docker
    * version 1.9.1, build a34a1d5
* docker-compose
    * version 1.5.2, build 7240ff3
* node.js
    * v4.2.3
* npm
    * 2.14.7

## ステップ1 VMを用意する

vagrantのインストール手順等は調べたらいっぱい出るので省きます。
boxも公式のものを使用します。
今回はubuntu/trusty64を使用するので下記コマンドでひな形を生成します。

```bash:コマンドプロンプト
$ vagrant init ubuntu/trusty64
```

雛形ができたらネットワークの設定とメモリの設定をしておきます。
メモリの設定をするのは初期で割り当てられてるメモリが512MBと少ないのでdocker build中に突然死したことがあるためです。

```diff:ネットワーク設定
-  # config.vm.network "private_network", ip: "192.168.33.10"
+  config.vm.network "private_network", ip: "192.168.33.10"
```


```diff:メモリ設定
-  # config.vm.provider "virtualbox" do |vb|
+  config.vm.provider "virtualbox" do |vb|
   #   # Display the VirtualBox GUI when booting the machine
   #   vb.gui = true
   #
   #   # Customize the amount of memory on the VM:
-  #   vb.memory = "1024"
-  # end
+    vb.memory = "1024"
+  end
```


```diff:プロビジョニング設定
   # documentation for more information about their specific syntax and use.
-  # config.vm.provision "shell", inline: <<-SHELL
-  #   sudo apt-get update
-  #   sudo apt-get install -y apache2
-  # SHELL
+  config.vm.provision "shell", inline: <<-SHELL
+    wget -qO- https://get.docker.com/ | sh
+    apt-get -y install python-pip
+    pip install docker-compose
+  SHELL
 end
```

Vagrantfileの準備ができたらVMを起動します。

```bash:コマンドプロンプト
$ vagrant up
```

.vagrantが生成されるので.gitignoreでgit管理から外しておきます。

```bash:.gitignore
.vagrant/
```

## ステップ2 docker-composeを使ってコンテナを作る

docker-composeを使うと構成管理がyamlに記載できるます。
複数コンテナのlinksとかvolumesとかdockerコマンドを叩くときに指定してたオプション等を記載できるので便利です。

node.jsには[公式のイメージ](https://hub.docker.com/_/node/)を使います。

**※vagrantの共有ディレクトリ内にnode_modulesを直接置くとnpm ERR! ETXTBSY: text file is busy, renameなどと言って来たのでコンテナに押しこむことにしました。(--no-bin-linksもつけてもダメだったのでこうした方がいいという案があれば教えて下さい。)**

というわけで、datastoreを用意しておきます。

```yaml:containers/datastore/Dockerfile
FROM busybox:latest
VOLUME /usr/src/app/node_modules
CMD /bin/sh
```


```yaml:docker-compose.yml
node:
  image: node:4.2.3
  volumes:
    - .:/usr/src/app
  working_dir: /usr/src/app
  volumes_from:
    - datastore

datastore:
  build: containers/datastore
```

buildします。

```bash:VM内
$ docker-compose build
```

コンテナを起動します。-dオプションはバックグラウンド起動です。
buildする必要のなかったnodeのイメージを初回にプルされるのでちょっと時間かかります(imageで指定したnode:4.2.3はすでにbuildされてるため)。

```bash:VM内
$ docker-compose up -d
```

コンテナの状態を確認します。

```bash:VM内
$ docker-compose ps
```


```bash:ps結果
       Name                Command         State    Ports 
---------------------------------------------------------
vagrant_datastore_1   /bin/sh -c /bin/sh   Exit 0         
vagrant_node_1        node                 Exit 0 
```

node, datastoreともに終了してるはずです。フォアグランドでうごくコンテナを使う場合はCMDをdocker-compose.ymlに記述するといいです。

## ステップ3 動かしてみる

### サンプルを動かす

コンソールログを吐くだけですが、、、

```js:hello.js
console.log('hello world');
```

実行

```bash:VM内
$ docker-compose run node node hello.js
# hello world
```

### npm initする


```bash:VM内
$ docker-compose run node npm init
```

### npm install パッケージする

jquery入れたくなった気がするので導入します。

```bash:VM内
$ docker-compose run node npm install jquery --save
```

ローカルにあるpackage.jsonに追記されました。

```diff:package.json
   "description": "",
   "main": "main.js",
-  "dependencies": {},
+  "dependencies": {
+    "jquery": "^2.1.4"
+  },
   "devDependencies": {},
```

## (書きたい人は)エイリアス書く

頭にdocker-compose run nodeをつければ基本的に大体のことができるようになりました。
これが長い嫌だって人は適当にbashrcとかにコマンドエイリアス書けばいいんじゃないですかね。開発ディレクトリ以外では実行できないですが。。。

```vim:.bashrc
# 最終行とかに
# --rm 実行後にコンテナ破棄
# --service-ports docker-compse.ymlに書いたポート設定使ってくれる
# (up時には必要ないがrunの時にportsの設定が消失したので入れてる←なぞ)
alias node='docker-compose run --rm --service-ports node node'
alias npm='docker-compose run --rm --service-ports node npm'
```

## まとめ

ゴールに設定したところまでは行けた。これからnode.jsは勉強していくつもりなので問題が出てくることもあると思うが開発環境の作り方の一つの方法としてあり何じゃないかと思ってます。

あと、同じ要領で別言語の環境構築もできると思う。