WindowsのVagrant-ubuntu上でDockerを使ってjavascriptの開発環境を作ってみたメモ

#まえがき
javascriptでブラウザ上で動く何かを作りたいんだけど、
最近は周辺ツールがいろいろ出ている。
使ったら便利だろうなーと思いつつ手が出せていなかったので、今回いろいろ試してみた。
試して見たのは以下の通り。

- Vagrant
- Docker
- webpack 
- babel
- gulp
- express
- socket.io
- mongodb
- karma

#開発環境
環境は以下のとおり。

- Windows7
- git 1.9.4
- vagrant1.7.4
- virtualbox5.0.10
- 仮想マシンubuntu14.04
- 仮想マシン上Docker 1.6.2

#Vagrantの設定

vagrantでubuntu14.04を使用。
dockerのインストールまで記述。

普段、私はテキストエディタにsublimeを使用。
vagrant上のファイルをsublimeで編集したいので、rsubの設定もする。
sublimeへのrsubのインストールについては割愛。

```Vagrantfile.
Vagrant.configure(2) do |config|
  config.vm.box = "ubuntu14.04"
  config.vm.network "private_network", ip: "192.168.50.10"
  config.vm.box_url = "https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box"

# windows上のフォルダ(Vagrantfileと同じ階層のwebappフォルダ)とvagrant上のディレクトリの同期設定
  config.vm.synced_folder "./webapp", "/home/vagrant/Docker/webapp"

  config.vm.provision "shell", inline: <<-SHELL
# dockerを入れる設定
    sudo apt-get update
    sudo apt-get install -y docker.io
    sudo chown vagrant:vagrant /home/vagrant/Docker

# sudo しなくてもdockerコマンドが使えるようにする
    sudo gpasswd -a vagrant docker

# rsubを入れる設定
    sudo wget -O /usr/local/bin/rsub https://raw.github.com/aurora/rmate/master /rmate
    sudo chmod +x /usr/local/bin/rsub
  SHELL
end
```

rsub用のssh設定。`RemoteForward 52698 127.0.0.1:52698`がrsubの使用するポート設定。

```.ssh/config.
Host rsub
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile E:/VagrantFolder/.vagrant/machines/default/virtualbox/private_key
  IdentitiesOnly yes
  LogLevel FATAL
  RemoteForward 52698 127.0.0.1:52698
```


gitをwindowsにインストールしておく。gitbashで以下作業を行う。
以下のコマンドを入力。

```
cd E:/VagrantFolder
vagrant box add ubuntu14.04 https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box
```

インストールできたら、`vagrant up`で起動。 `ssh rsub`で接続。
切るときは `vagrant halt`。
dockerのbulidに失敗したら再起動すると直ることが多かったので、
この３つは覚えておくとよい。

gitbashのショートカットを作って、ショートカットのプロパティを開いて、
作業フォルダにE:/VagrantFolderと設定しておくと以降楽になる。

#webpackのインストール

package.jsonでのインストールはメモリ不足になる可能性があるので、
１つずつインストールする。エラーがどれで出たかもわかりやすい。

```Docker/webpack/Dockerfile.
FROM node:5.3.0

WORKDIR /usr/local/src
RUN npm install -g webpack
RUN npm i babel-core babel-preset-es2015 babel-preset-react
RUN npm i eslint
RUN npm i pre-commit precommit-hook
RUN npm i lodash
RUN npm i react

RUN npm i eslint-loader
RUN npm i babel-loader
RUN npm i css-loader
RUN npm i react-hot-loader
RUN npm i style-loader
CMD [ "node" ]
```

ビルドは何回かやることになるので、シェルを作っておく。
シェルファイルは作ったら権限を与えるのを忘れずに。`chmod 755 shell/build_webpack.sh`

```shell/build_webpack.sh
cd /home/vagrant/Docker/webpack && docker build -t hibo/node_webpack .
```

設定ファイルをwebpackフォルダに保存。
entryやoutputのパスは設定ファイルから見たパスとなる。

```webapp/webpack/webpack.config.js
/* global module, __dirname */
module.exports = {
    entry: __dirname +"/../public/app/scripts/entry.js",
    output: {
        path: __dirname + "/../public/app/dist",
        filename: "bundle.js"
    },
    module: {
        loaders: [
            { test: /\.css$/,  loader: "style!css" },
            { test: /\.md$/,   loader: "html!markdown?gfm=false" },
            { test: /\.jsx?$/, loader: 'babel?presets[]=react,presets[]=es2015', exclude: /node_modules/},
            { test: /\.js?$/,  loader: 'babel?presets[]=react,presets[]=es2015', exclude: /node_modules/},
            { test: /\.js$/,   loader: "eslint-loader",        exclude: /node_modules/},
            { test: /\.less$/, loader: "style!css!less" }
        ]
    },
    watchOptions: {
     poll: true
    }
};
```

ここで、以下のようにファイルを置いている。

```
webapp
  -webpack
    -webpack.config.js … webpackの設定ファイル
  -public
    -app
      -scripts … ここのフォルダにjsファイルをまとめておく
        -.eslintrc …eslintの設定ファイル
        -entry.js … webpackの監視するentryファイル
        -content.js … 確認用
      -dist … ここのフォルダにwebpackがoutputする
        -bundle.js …webpackを起動すると生成されるbundleファイル。
      -views
        -index.html …確認用
```

確認用のファイルを以下にメモ。

```webapp/public/app/scritps/entry.js
document.write(require("./content.js"));
```

```webapp/public/app/scripts/content.js
module.exports = "It works from content.js! test! ";
```

```webapp/public/app/views/index.html
<!doctype html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>webpack tutorial</title>
        <script src="../dist/bundle.js"></script>
    </head>
    <body>
    </body>
</html>

```

ちなみに、eslintファイルは以下のように設定。
es6を試したいのでその設定とか。

```webapp/public/app/scripts/.eslintrc
{
    "extends": "eslint:recommended",
    "env": {
        "browser": true,
        "es6": true,
        "node":true
    },
    "ecmaFeatures": {
        "modules": true
    },
    "rules": {
        "semi": 2,
        "no-console":0
    }
}
```

起動のコマンドもシェルにしておく。

```shell/webpack_docker_start.sh
docker run --rm -it --name webpack -p 8080:8080 -v /home/vagrant/Docker/webapp:/usr/local/src/app hibo/node_webpack /bin/bash -c 'cd /usr/local/src; webpack --config ./app/webpack/webpack.config.js --progress --colors --watch;'
```


これで、`shell/webpack_docker_start.sh`を起動すると、webappフォルダがdocker上のappフォルダと共有されて、そこでwebpackが監視を行うようになる。

```Docker上のフォルダ構成.
usr
  -local
    -src … このフォルダでwebpackコマンドを実行する。
      +node_modules … 最初にnpmでインストールされたパッケージ郡
      -app …このフォルダとwebappを共有
        +webpack …webapp以下のwebpack
        +public  …webapp以下のpublic
```

node_moduleのある階層から１つ下のフォルダを共有しているのは、
node_moduleを共有したくないから。
ubuntuで動かす分には共有しても問題ないけど、今回はwindowsと共有している。
windowsではファイル名+パスの限界が260文字くらいに設定されている。
node_module以下は階層が深くなりやすいので、これを共有しようとするとエラーになる。

自分のフォルダ構成は以下の感じにしてみている。今思うと、だいぶ汚い。
vagrant上のwebappフォルダはDockerフォルダ以下に置かなくてもよかったなぁ。

```Windows上のフォルダ構成.
E
  -VagrantFolder … このフォルダで vagrant up や vagrant ssh や ssh rsubなどする。
    -Vagrantfile
    +.vagrant
    -webapp … vagrantで起動したlinuxと共有するフォルダ
      +webpack
      +public
```


```Vagrant+ubuntu1.04のフォルダ構成.
home
  -vagrant … vagrant ssh や ssh rsub を実行したときに接続されるディレクトリ
    -shell
      -build_webpack.sh
      -webpack_docker_start.sh
   -Docker
     -webapp … windows上のwebappフォルダと共有
       + webpack
       + public
     -webpack
       - Dockerfile
```



#gulp + express + socket.io

次はexpressの設定をしたい。
ファイルを編集したらexpressサーバー再起動、ブラウザのリロードの自動化を行う。
そのために、タスクランナーのgulpを導入。
gulpでサーバー側用のjsファイルの変更を感知したら再起動。
クライアント側用のファイル（public以下のファイル）の変更を感知したらブラウザリロードを設定する。

```Docker/express/Dockerfile.
FROM node:5.3.0

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

#gulpfileを書き出し
RUN echo "var requireDir = require('require-dir'); requireDir('webapp/gulp/tasks', { recurse: true }); " > gulpfile.js 

#package.jsonを書き出し
RUN echo '{ "name": "node_express",  "scripts": { "start": "node webapp/bin/www" }}' > package.json

#タスクランナーインストール
RUN npm install -g gulp
RUN npm install gulp 
RUN npm install --save-dev require-dir 
RUN npm install --save-dev gulp-if 
RUN npm install --save-dev gulp-livereload

#サーバーインストール
RUN npm install --save-dev express 
RUN npm install --save-dev body-parser 
RUN npm install --save-dev cookie-parser 
RUN npm install --save-dev debug 
RUN npm install --save-dev jade 
RUN npm install --save-dev morgan 
RUN npm install --save-dev serve-favicon 

# ウェブソケットインストール
RUN npm install --save-dev socket.io

# DBドライバインストール
RUN npm install --save-dev mongodb

CMD [ "gulp", "watch" ]
```

```shell/build_experss.sh
cd /home/vagrant/Docker/express && docker build -t hibo/express . 
```

ビルドするときに、socket.ioのインストールでコケることが多かった。
vagrantの再起動でたいてい直ったのでおそらくメモリ不足の問題。


```
webapp
  +public
  -gulp
    -tasks
      -express.js
```

gulp関連の設定メモ。
サーバーの自動起動とブラウザのリロードを設定している。


```webapp/gulp/tasks/express.js
var gulp = require('gulp');
var spawn = require('child_process').spawn;
var server;

var livereload = require('gulp-livereload');

// ブラウザリロードタスク。
gulp.task('reload',function(){
     gulp.src(['webapp/public/*/*','webapp/views/*.jade'])
     .pipe(livereload());  
});

gulp.task('server',function(){
    if(server){
         //サーバーを終了
         server.kill('SIGKILL');
    }
     //サーバーを起動
     server = spawn('node',['./webapp/bin/www']);

     // サーバーの出力をキャッチして出力。
     server.stdout.on('data', function (data) {
        console.log('stdout: ' + data);
     });
     server.stderr.on('data', function (data) {
        console.log('stderr: ' + data);
     });
});

gulp.task('watch',['server'],function(){
    // webapp配下のpublicフォルダを除くjsファイルが更新されたらサーバーを再起動
    gulp.watch(['webapp/**/*.js','!webapp/public/**/*.js'],['server']);

    // publicフォルダ以下のファイルを更新したらブラウザリロード
    livereload.listen();
    gulp.watch(['webapp/public/**/**', 'webapp/views/*.jade'], ['reload']);
});
```

express関連のファイルの設定メモ。
express-generatorで作ったファイルを参考にしている。
socket.ioのサイトのチュートリアルではapp.js内にsocket.ioの設定を書いているが、
express4ではbin/wwwファイルにそれを書かないと404エラーとなった。

```webapp/bin/www.
#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('../app');
var debug = require('debug')('myapp:server');
var http = require('http');

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.PORT || '3333');
app.set('port', port);

/**
 * Create HTTP server.
 */

var server = http.createServer(app);

var io = require('socket.io').listen(server);

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  });
});


/**
 * Listen on provided port, on all network interfaces.
 */

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}

```


```webapp/app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');


// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

module.exports = app;
```

ここで、expressに合わせてファイルを配置する。

```
 webapp
  +public … webpackで監視しているクライアント側ファイル
  +gulp … gulp用の設定ファイル
  -bin
    -www …expressの起動ファイル
  -routes
    -index.js
    -users.js
  -views
    -error.jade
    -layout.jade
    -index.jade

```

ルートの設定。接続したらindex.htmlを表示するようにしている。

```webapp/routes/index.js
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  //res.render('index', { title: 'Express' });
  res.sendFile(__dirname + '/../public/app/views/index.html');
});

router.get('/test', function(req, res, next) {
  res.render('index', { title: 'Express' });
});


module.exports = router;
```

users.jsで/userとしたときに表示が変わるかテストできる。

```webapp/routes/users.js
var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

module.exports = router;
```

更新するファイル側からもlivereloadを呼び出ように変更。
参考にしたサイトではlocalhost:35729/livereload.jsだが、
vagrantの仮想環境上にdockerを立ち上げて接続などとしているので
 http: //192.168.50.10:35729 とする。

```webapp/public/app/views/index.html
<!doctype html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>webpack tutorial</title>
        <script src="../dist/bundle.js"></script>
        <script src="http://192.168.50.10:35729/livereload.js"></script>
    </head>
    <body>
    </body>
</html>

```


起動コマンドをシェルにしておく。

```shell/express_docker_start.sh
docker run --name express --rm -it -p 80:3333 -p 35729:35729 -v /home/vagrant/Docker/webapp:/usr/src/app/webapp hibo/express
```

# mongodb

DBの設定をしてみる。
imageをpullする。

```shell/build_mongodb.sh
docker pull mongo
```

DBのデータ保存用にディレクトリを作る。

```
home
  -vagrant
   -DB
     -mongodb
   +shell
   +Docker
```

mongodbを起動する。

```shell/mongodb_docker_start.sh
docker run --name mongodb -p 27017:27017 -v /home/vagrant/DB/mongodb:/data/db -d mongo
docker run -it --link mongodb:mongo --rm mongo sh -c 'exec mongo "$MONGO_PORT_27017_TCP_ADDR:$MONGO_PORT_27017_TCP_PORT/test"'
```

expressからつなぐ設定をする。

```webapp/routes/mongodb.js
var mongodb = require('mongodb'),
    mongoServer = new mongodb.Server(
      '192.168.50.10',
      27017),
    dbHandle = new mongodb.Db('spa',mongoServer, {safe:true});

dbHandle.open(function(){
  console.log('**connected to mongoDB');
});

module.exports = dbHandle;
```

```webapp/app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');
var dbHandle = require('./routes/mongodb');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');


// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

module.exports = app;
```

#Karmaでテスト

テスト用にKarmaの設定を行う。

```shell/build_karma.sh
cd /home/vagrant/Docker/karma && docker build -t hibo/karma .
```

ビルド用のシェルを作ったら、以下を設定。

```Docker/karma/Dockerfile.
FROM node:4.2.3

RUN mkdir -p /usr/src/app/spec
WORKDIR /usr/src/app

RUN npm install -g --unsafe-perm phantomjs
RUN npm install -g --unsafe-perm jasmine-core
RUN npm install -g --unsafe-perm karma-jasmine
RUN npm install -g --unsafe-perm karma-phantomjs-launcher
RUN npm install -g --unsafe-perm karma

CMD [ "node" ]
```

karma内でsocket.ioを使っているのでこれもビルド時にメモリ不足になりやすい。

```
webapp
  -karma
    -karma.conf.js
  +public
  +gulp
  +bin
  +routes
```

karmaの設定ファイルを記述

```webapp/karma/karma.conf.js
// Karma configuration
// Generated on Mon Dec 21 2015 10:38:17 GMT+0000 (UTC)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',


    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],


    // list of files / patterns to load in the browser
    files: [
      '../public/app/dist/*.js',
      '../public/test/spec/**Spec.js'
    ],


    // list of files to exclude
    exclude: [
    ],


    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },


    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],


    // web server port
    port: 9876,


    // enable / disable colors in the output (reporters and logs)
    colors: true,


    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,


    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],


    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultanous
    concurrency: Infinity
  })
}
```

起動のシェルは以下。

```shell/karma_docker_start.sh
docker run -it --name karma --rm -v /home/vagrant/Docker/webapp:/usr/src/app hibo/karma  /bin/bash -c 'karma start ./karma/karma.conf.js'
```

これでテストが可能になる。

以上、いろいろテストしながら環境を作ったけど、vagrantを噛ませる必要はあんまりないかも。

#参考にしたページ

[参考:DockerfileのONBUILD][link-1]
[参考：Docker Registryを使う][link-2]
[参考：vagrantでinaccessibleが出た時][link-3]
[参考：Windows 7マシンで、VirtualBox+Vagrantを使いUbuntu 14.04を動かす][link-4]
[参考:Vagrantでアプリケーション開発環境をローカルPCに作ってみよう][link-5]
[参考:Vagrant、chef、 Dockerって最近よく聞くけどなにが違うの？ ][link-6]
[参考：Express][link-8]
[参考:Docker チートシート][link-9]
[参考：Dockerコマンドメモ][link-10]


[link-1]:http://deeeet.com/writing/2014/03/21/docker-onbuild/
[link-2]:http://dev.classmethod.jp/cloud/docker-registry-recipes/
[link-3]:http://qiita.com/pictzzz/items/8744efe5fb54eb104748
[link-4]:http://blog.kondoyoshiyuki.com/2014/06/12/windows7-vagrant-ubuntu14-04/
[link-5]:http://codezine.jp/article/detail/8362
[link-6]:http://arakaji.hatenablog.com/entry/2015/02/11/145520
[link-7]:http://qiita.com/kitar/items/2a7bc9ea98930bf08473
[link-8]:http://expressjs.com/en/starter/hello-world.html
[link-9]:https://www.qoosky.net/references/206/
[link-10]:http://qiita.com/curseoff/items/a9e64ad01d673abb6866

[webpack-dev-serverで継続的なクライアントサイドテスト][link-11]
[webpackとwebpack-dev-server、html-loaderと合わせて、JsRenderを試す（再）][link-12]
[webpack dev server][link-13]

[link-11]:http://blog.namiking.net/post/2015/09/test-webpack-browser/
[link-12]:http://d.hatena.ne.jp/Kazuhira/20150927/1443359079
[link-13]:http://webpack.github.io/docs/webpack-dev-server.html

[参考:node初心者がexpress-generatorで吐き出されたapp.jsを読んでみる][link-23]
[参考:Express application generator][link-24]
[web-socketのチュートリアル][link-1]
[【Node.js】express4 + socket.io で socket.io.js が 404 not found になる。Add Star][link-2]

[link-21]:http://socket.io/get-started/chat/
[link-22]:http://d.hatena.ne.jp/pospome/20150314/1426357192
[link-23]:http://qiita.com/mito_log/items/735f7079f99ec78ea7e6
[link-24]:http://expressjs.com/ja/starter/generator.html

[参考：webpack+babel-loader+power-assert+jsdomでフロントエンド開発環境を作る][link-31]
[React+FluxでTodoMVCを作ってFluxについて学んでみた][link-32]

[link-31]:http://yutapon.hatenablog.com/entry/webpack%2Bbabel-loader%2Bpower-assert%2Bjsdom-skeleton
[link-32]:http://yutapon.hatenablog.com/entry/2015/04/27/150000


[2015年はgulpで決まり！開発環境をgruntから乗り換えよう！（コーダー編）][link-41]
[gruntを知らない人がgulpを使ってみた][link-42]
[gulp.jsを使ってフロントエンドのビルドをする【webpack, stylus】][link-43]
[Getting Started][link-44]
[Gulp.js入門 – コーディングを10倍速くする環境を作る方法まとめ][link-45]
[これからはじめるGulp（1）：bundler, rbenv, nodebrewでGulp環境を作ってみる][link-46]

[link-41]:http://lab.sonicmoov.com/markup/gulp-instead-of-grunt/
[link-42]:http://qiita.com/PianoScoreJP/items/1764854f91ac447bd7e1
[link-43]:http://yutapon.hatenablog.com/entry/2014/12/06/123000
[link-44]:https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md
[link-45]:http://liginc.co.jp/web/tutorial/117900
[link-46]:http://whiskers.nukos.kitchen/2014/12/01/gulp.html

[webpack-dev-serverで継続的なクライアントサイドテスト][link-51]
[webpackとwebpack-dev-server、html-loaderと合わせて、JsRenderを試す（再）][link-52]
[webpack dev server][link-53]

[link-51]:http://blog.namiking.net/post/2015/09/test-webpack-browser/
[link-52]:http://d.hatena.ne.jp/Kazuhira/20150927/1443359079
[link-53]:http://webpack.github.io/docs/webpack-dev-server.html


[Node.js + express4 + gulp 簡単にブラウザリロードを実装][link-61]
[gulp - ブラウザのリロード自動化][link-62]
[2014/11/27 ブラウザ確認が一瞬！ Grunt・Gulpと始めるBrowserSync入門][link-63]
[【Gulp.jsインストール】フロントエンドタスクランナーGulp.jsの使い方][link-64]
[【エラー】Refusing to install as a dependency of itself][link-66]
[gulp-livereload][link-67]
[参考：dockerで複数個のポートフォワーディングを行う][link-65]

[link-61]:http://qiita.com/PianoScoreJP/items/bf513fc173f5db9453ab
[link-62]:http://web-develop.hatenablog.jp/entry/2015/08/27/072523
[link-63]:https://ics.media/entry/3405
[link-64]:http://www.unionnet.jp/develop/gulp/
[link-65]:http://qiita.com/h_demon/items/096dafed2b8fec362323
[link-66]:http://qiita.com/kaznmb/items/047c1560edda683a63f4
[link-67]:https://github.com/vohof/gulp-livereload#usage


[MongoDBをDockerでインストールする][link-71]
[Official Repository mongo][link-72]

[link-71]:http://qiita.com/fetaro/items/34e29a21b5f0da6449ef
[link-72]:https://hub.docker.com/_/mongo/
[link-73]:https://nodejs.org/api/child_process.html
[link-74]:http://nodejs.jp/nodejs.org_ja/docs/v0.4/api/child_processes.html
[link-75]:http://mid0111.hatenablog.com/entry/2015/08/19/200657



[Jenkinsの公式Dockerイメージ使ってみた][link-81]
[コードの健康状態を保て！ Jenkinsとtestemを使って、JavaScriptで継続的インテグレーション（CI）を行う][link-82]
[PonteのインストールとMQTT over SSL/TLS設定][link-83]
[[Node.js] npmの設定 属性一覧][link-84]
[Karma, PhantomJS, JasmineでBDDなJavaScriptテスト環境をつくる][link-85]

[link-81]:http://knjname.hateblo.jp/entry/2015/02/10/040833
[link-82]:http://codezine.jp/article/detail/8391
[link-83]:http://qiita.com/masato/items/963e3248f2d825d6df34
[link-84]:http://esheep.doorblog.jp/archives/22432883.html
[link-85]:http://nobyu.hatenadiary.jp/entry/2013/10/27/170227