CoreOS上でnode.jsを起動するメモ

### この投稿内容について

　そろそろDocker歴が1年になるのですが，CoreOSは殆ど使ったことがなかったことに気付きました．そこで，CoreOSを使って，Docker上でGitHubに登録されているWebアプリケーションを動かすまでの私的なメモです．特におかしなことはしていません（していないはずです）．
　MacOS X上で，VMWare Fusion7を使って実行しましたが，他の環境でもほぼ同じ操作内容で再現可能と思います．

### CoreOSとは

　[CoreOS](https://coreos.com/)とは，Dockerを利用するために作られたLinux Distributionの一つです．Docker以外の用途を考慮しておらず，非常に軽量なことが利点です．
　執筆時のバージョンはCoreOS 494.0.0です．

### CoreOSのインストール形態

　CoreOSには様々なインストール形態があります．ベアメタル，クラウドサービス（Amazon EC2，DigitalOcean，Rackspace，Azure，Google Compute Engine），仮想計算機環境（Vagrant，VMWare，QEMU，Openstack，Eucalyptus）などに対応しています．
　紹介記事を見るとたいていVagrantかクラウドサービスを使ってCoreOSを起動している例が多いので，ここではVMWareを利用してみたいと思います．ただし，VMWareはOfficial PlatformではなくCommunity-Supported Platformであることに注意してください．詳しくは[Documentation](https://coreos.com/docs/)を御覧ください．

### CoreOSのインストールと起動

VMWare上でCoreOSを動かす方法は，[Running CoreOS on VMware](https://coreos.com/docs/running-coreos/platforms/vmware/)に書いてあります．基本的にalphaかbetaリリースしか用意されていません．

```shell-session
$ curl -LO http://alpha.release.core-os.net/amd64-usr/current/coreos_production_vmware_insecure.zip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  190M  100  190M    0     0  8433k      0  0:00:23  0:00:23 --:--:-- 16.3M
$ unzip coreos_production_vmware_insecure.zip -d coreos_production_vmware_insecure
Archive:  coreos_production_vmware_insecure.zip
  inflating: coreos_production_vmware_insecure/coreos_production_vmware_insecure_image.vmdk
  inflating: coreos_production_vmware_insecure/coreos_production_vmware_insecure.vmx
  inflating: coreos_production_vmware_insecure/insecure_ssh_key
$ cd coreos_production_vmware_insecure
$ ls

coreos_production_vmware_insecure.vmx
coreos_production_vmware_insecure_image.vmdk
insecure_ssh_key
```

　以上でインストールは完了です．次にCoreOSを起動して，VMWareのコンソール上でEnterキーを押してみてください．sshのキーとIPアドレスが表示されるはずです．私の場合は172.16.11.136と表示されたので，このアドレスにsshでログインしてみましょう．
　sshで接続しようとすると，キーが表示されて「接続を続けて良いですか？」と聞かれるのでyesを入力してください．すると，プロンプトが変わり，無事にログインできたことが分かります．

```shell-session
$ open coreos_production_vmware_insecure.vmx
$ ssh -i insecure_ssh_key core@172.16.11.136
The authenticity of host '172.16.11.136 (172.16.11.136)' can't be established.
RSA key fingerprint is f7:a4:40:7b:8e:2c:c0:29:30:6e:77:af:21:ee:aa:fa.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.16.11.136' (RSA) to the list of known hosts.
CoreOS (alpha)
core@localhost ~ $
```

### Docker上にWebアプリをインストールする

ここまで来たら後はDockerを起動してガンガン使うだけです．と言っても，初めての人は何をやれば良いか分からず途方に暮れると思いますので，ここではnode.jsを使って見たいと思います．

まずは利用するDockerイメージを探します．適当に作っても良いのですが，今回は[Docker Hub](https://registry.hub.docker.com/)から検索してみようと思います．Docker Hubを開くと，検索窓とOfficial Repositoriesが表示されます．ちょうど[node.jsの公式リポジトリ](https://registry.hub.docker.com/_/node/)があるようなので利用します．
　ページを開くと，さまざまなバージョンのイメージが用意されています．大きく分けて，0.10系，0.11系，0.8系バージョンがあり，それぞれ無印，onbuild，slimが存在します．

```
Supported tags and respective Dockerfile links

・0.10.33, 0.10, 0, latest (0.10/Dockerfile)
・0.10.33-onbuild, 0.10-onbuild, 0-onbuild, onbuild (0.10/onbuild/Dockerfile)
・0.10.33-slim, 0.10-slim, 0-slim, slim (0.10/slim/Dockerfile)
・0.11.14, 0.11 (0.11/Dockerfile)
・0.11.14-onbuild, 0.11-onbuild (0.11/onbuild/Dockerfile)
・0.11.14-slim, 0.11-slim (0.11/slim/Dockerfile)
・0.8.28, 0.8 (0.8/Dockerfile)
・0.8.28-onbuild, 0.8-onbuild (0.8/onbuild/Dockerfile)
・0.8.28-slim, 0.8-slim (0.8/slim/Dockerfile)
```

それぞれのDockerfileの内容をチェックしたところ，大雑把に言って以下の様な違いが見られました．

```
◆無印＝素の状態のOSに必要最低限のパッケージを追加してnode.jsをインストールする
◆onbuild＝無印をベースにして，同じディレクトリに有るpackage.jsonなどをコピーして起動する
◆slim＝素の状態のOSに必要最低限＋αのパッケージを追加してnode.jsをインストールする
```

　ここでは0.10.33のonbuild版を利用して，手っ取り早くnode.jsによるWebアプリケーションを起動してみようと思います．Webアプリケーションは何でも良いのですが，ここでは[amirrajan/nodejs-chat](https://github.com/amirrajan/nodejs-chat)を使用します．
　手順は以下のとおりです．
1.nodejs-chatの入手
2.package.jsから起動スクリプトのファイル名を確認（ログでは省略）
3.起動スクリプトから使用するポート番号の確認（ログでは省略）
4.Dockerfileの作成（3で調べたポート番号を記述）
5.Dockerイメージの作成
6.nodejs-chatの起動
7.Webブラウザから接続（次節）


```shell-session
core@localhost ~ $ git clone https://github.com/amirrajan/nodejs-chat.git
Cloning into 'nodejs-chat'...
remote: Counting objects: 31, done.
remote: Total 31 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (31/31), done.
Checking connectivity... done.
core@localhost ~ $ cd nodejs-chat
core@localhost ~/nodejs-chat $ cat > Dockerfile
FROM node:0.10-onbuild
EXPOSE 3000
^D
core@localhost ~/nodejs-chat $ docker build -t nodechat .
Sending build context to Docker daemon 745.5 kB
Sending build context to Docker daemon
Step 0 : FROM node:0.10-onbuild
node:0.10-onbuild: The image you are pulling has been verified
36fd425d7d8a: Pull complete
aaabd2b41e22: Pull complete
3c20e07c38ce: Pull complete
b6ef456c239c: Pull complete
b045b0cd49ad: Pull complete
210d9bc26f2f: Pull complete
27ecce8bd36c: Pull complete
fcac83abd52d: Pull complete
edc7d098628f: Pull complete
b5ac041b53f9: Pull complete
387247331d9c: Pull complete
2080990fe5fe: Pull complete
04b1a9d3a23d: Pull complete
c0555bd85c9e: Pull complete
7e80806406ad: Pull complete
1c53475b3cc2: Pull complete
c6c4351d1205: Pull complete
511136ea3c5a: Pull complete
Status: Downloaded newer image for node:0.10-onbuild
# Executing 3 build triggers
Trigger 0, COPY package.json /usr/src/app/
Step 0 : COPY package.json /usr/src/app/
Trigger 1, RUN npm install
Step 0 : RUN npm install
 ---> Running in 722777567e74
npm WARN package.json nodejs-chat@0.0.4-21 No README data

> ws@0.4.32 install /usr/src/app/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws
> (node-gyp rebuild 2> builderror.log) || (exit 0)

make: Entering directory '/usr/src/app/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build'
  CXX(target) Release/obj.target/bufferutil/src/bufferutil.o
  SOLINK_MODULE(target) Release/obj.target/bufferutil.node
  SOLINK_MODULE(target) Release/obj.target/bufferutil.node: Finished
  COPY Release/bufferutil.node
  CXX(target) Release/obj.target/validation/src/validation.o
  SOLINK_MODULE(target) Release/obj.target/validation.node
  SOLINK_MODULE(target) Release/obj.target/validation.node: Finished
  COPY Release/validation.node
make: Leaving directory '/usr/src/app/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build'
underscore@1.5.2 node_modules/underscore

ejs@0.8.8 node_modules/ejs

express@3.0.6 node_modules/express
├── methods@0.0.1
├── fresh@0.1.0
├── range-parser@0.0.4
├── cookie-signature@0.0.1
├── buffer-crc32@0.1.1
├── cookie@0.0.5
├── commander@0.6.1
├── mkdirp@0.3.3
├── debug@2.1.0 (ms@0.6.2)
├── send@0.1.0 (mime@1.2.6)
└── connect@2.7.2 (pause@0.0.1, bytes@0.1.0, formidable@1.0.11, qs@0.5.1)

socket.io@0.9.17 node_modules/socket.io
├── base64id@0.1.0
├── policyfile@0.0.4
├── redis@0.7.3
└── socket.io-client@0.9.16 (xmlhttprequest@1.4.2, uglify-js@1.2.5, active-x-obfuscator@0.0.1, ws@0.4.32)
Trigger 2, COPY . /usr/src/app
Step 0 : COPY . /usr/src/app
 ---> c6edcf3d1bcc
Removing intermediate container e26d4efe3918
Removing intermediate container 722777567e74
Removing intermediate container 1d7d9794350d
Step 1 : EXPOSE 8888
 ---> Running in f8bd71610ef6
 ---> 5cac1f68e77e
Removing intermediate container f8bd71610ef6
Successfully built 5cac1f68e77e
core@localhost ~/nodejs-chat $
```

### Webアプリの起動

　nodejs-chatのserver.jsによると3000番ポートを使用することが分かります．そこで，DockerfiileでもEXPOSEにより3000番ポートをコンテナ外でも使用することを宣言します．最後に起動時に「-p 3000:3000」でコンテナ内の3000番ポートをコンテナ外の3000番ポートとするようにします．また，「--rm」は，実行を終えたコンテナを自動的に削除するオプションです．
　以上でnodejs-chatが起動したので，Webブラウザから接続します．
　ブラウザからはCoreOSのIPアドレスとポート番号を使い，「 http://172.16.11.136:3000/ 」にアクセスします．上手く接続できれば，以下の様なログが延々とコンソールに出力されます．

```shell-session
core@localhost ~/nodejs-chat $ docker run -p 3000:3000 --rm --name nodechat nodechat

> nodejs-chat@0.0.4-21 start /usr/src/app
> node server.js

info: socket.io started
debug: served static content /socket.io.js
debug: client authorized
info: handshake authorized FkslWD_gYHxyQD859UFZ
debug: setting request GET /socket.io/1/websocket/FkslWD_gYHxyQD859UFZ
debug: set heartbeat interval for client FkslWD_gYHxyQD859UFZ
debug: client authorized for
debug: websocket writing 1::
debug: websocket writing 5:::{"name":"servernotification","args":[{"connected":true,"to_self":true,"username":"Andy Williams"}]}
debug: broadcasting packet
debug: websocket writing 5:::{"name":"updateusers","args":[{"Andy Williams":"Andy Williams"}]}
・・・以下ログが延々と吐出される・・・
```

### まとめ

　1年の間にDockerを取り巻く環境が急速に整備され，まだまだと思われていた実運用環境への導入でしたが，最近では導入に対する障壁がなくなりつつあります．今後，Dockerの守備範囲がさらに広がりそうな勢いです．この投稿が，「利用してみたいけど，どこから手を付けてよいか分からない」と言う方のお役に立てれば幸いです．