herokuでdockerコンテナを動かす(2015年10月版)

以前[heroku上でhubotのdockerコンテナを動かす記事](http://qiita.com/mpppk/items/cc27cb0dc9cd365d671c)を書いたのだが、2015年8月にアップデートがあり、docker-composeに対応した。
これに伴い、heroku toolbeltのdocker pluginからは```docker:start```が無くなり、普通に```docker-compose up```するだけでローカルで動作させることが可能になった。(前述の記事は意味の無い文章の羅列と化した。)

なお、内容は[公式のGetting Started](https://devcenter.heroku.com/articles/docker)をやるだけだが、途中自分がよく分からなかった所は詳しく書こうと思う。
今回はnodejsでやるけど、Railsとかでも多分だいたい一緒。

# 準備
あらかじめ、以下のものをインストールしておく必要がある。

* docker
* docker-compose
* Heroku Toolbelt

gitとかはもう書かなくてもいいよね。

あと、[Node.js/Memcached](https://github.com/RedisLabs/memcachedcloud-node-sample)のサンプルリポジトリを引っ張ってきておく。
これはmemchachedを操作する簡単なwebアプリ。


```
$ git clone https://github.com/RedisLabs/memcachedcloud-node-sample.git
```



heroku toolbeltのdocker pluginも導入しておく。

```
$ heroku plugins:install heroku-docker
```

# リポジトリの構成を確認
## package.json
npmのパッケージを管理するためのファイル。

## app.json
docker:initのために必要なファイル。色々書いてあるけど、重要なのは以下の２つ。

### image
docker hubのどのimageを利用するかを指定する。
サンプルだと以下の部分。

```app.json
...
"image": "heroku/nodejs",
...
```

重要な点として、herokuで動かすdocker imageは何でも良いわけではない。
基本的にはherokuが提供しているimageを用いるが、自分でheroku対応のimageを作ることも可能。(→ [Modifying the Dockerfile](https://devcenter.heroku.com/articles/docker#modifying-the-dockerfile))
公式に提供されているimageの一覧は[こちら](https://devcenter.heroku.com/articles/docker#app-json)。

### addons
heroku上で利用するアドオン名を指定する。
サンプルだと以下の部分。

```app.json
  ...
  "addons": [
    "memcachedcloud:30"
  ]
}
```

## Procfile
heroku上で実行するコマンドを指定するファイル。

```Procfile
web: node web.js
```

# ローカルで動かしてみる

まずは以下のコマンドを入力。

```
$ heroku docker:init
```

このコマンドは、Procfileやapp.jsonに従って、Dockerfileとdocker-compose.ymlを生成する。

まずDockerfileの中を見てみると、単にapp.jsonで指定したimageを取ってくるだけであることがわかる。

```Dockerfile
FROM heroku/nodejs
```

次に、docker-compose.ymlだが、ここでは```web```と```shell```という二つのコマンドの定義と、memchachedcloudというエイリアス名のmemchachedコンテナを立ち上げるようになっている。

```docker-compose.yml
web:
  build: .
  command: 'bash -c ''node web.js'''
  working_dir: /app/user
  environment:
    PORT: 8080
    MEMCACHEDCLOUD_SERVERS: 'memcachedcloud:11211'
  ports:
    - '8080:8080'
  links:
    - memcachedcloud
shell:
  build: .
  command: bash
  working_dir: /app/user
  environment:
    PORT: 8080
    MEMCACHEDCLOUD_SERVERS: 'memcachedcloud:11211'
  ports:
    - '8080:8080'
  links:
    - memcachedcloud
  volumes:
    - '.:/app/user'
memcachedcloud:
  image: memcached
```

webでは、Procfileで指定したコマンドを実行するコンテナを立ち上げる。
それに対してshellは単にbashを実行するので、任意のコマンドを実行できる状態になる。

また、app.jsonでaddonsにmemchachedを指定したので、環境変数とmemchachedコンテナも自動的に追加されている。
MEMCACHEDCLOUD_SERVERのホスト名に単にmemcachedcloudと指定するだけで接続できるのがよくわからなかったが、dockerのlinkオプションは/etc/hostsに指定したエイリアス名で追加するようだ。(環境変数を追加するだけだと思ってた……)

さて、生成されたファイルの確認が終わったので、おもむろにサーバを立ち上げてみる。

```
$ docker-compose up web
```

すると```web_1 | Listening on 8080```みたいなのが出るので、```http://localhost:8080```にアクセス。

こんな感じの画面が表示されれば、正常に動作している。

![Screenshot 2015-09-26 13.48.17.png](https://qiita-image-store.s3.amazonaws.com/0/28464/8e75e91e-2277-09b3-dce7-19b204a40346.png "Screenshot 2015-09-26 13.48.17.png")

Actionsのリンクを適当にクリックしてみて、memchachedと繋がっているのを確認する。

# herokuにdeployする
deployは極めて簡単で、以下のコマンドを実行するだけ。

```
$ heroku docker:release
``` 
らくちん。

# ところでMEMCHACEDCLOUD_USERNAMEって何やねん
web.jsを見ていると、最初にmemjsのclientを以下のように生成している。

```web.js
var client = memjs.Client.create(process.env.MEMCACHEDCLOUD_SERVERS, {
  username: process.env.MEMCACHEDCLOUD_USERNAME,
  password: process.env.MEMCACHEDCLOUD_PASSWORD
});
```

MEMCACHEDCLOUD_USERNAMEとか、deployしたらherokuが勝手に設定するのは分かるけど、ローカルではいつ設定したんだよ…と思ったら、ローカルでは普通にundefinedだった。
memjsの仕様として値が入ってなかったら単に無視されるのかなーと思ってるけど、ちゃんと調べてない。

# 感想
今回の変更で個人的には以下の2点が嬉しいポイントだと感じている。

* heroku toolbelt無しでも、docker-composeがあればローカルで動く
* heroku add-onsをあまり意識しなくてよくなった

なので、 ~~herokuのことが嫌いになっても別の適当な場所で動かせる~~ 
dockerのメリットであるポータビリティが活かせるようになった。
自前のサーバでもいいし、OpenShiftもdocker対応したので動くんじゃないかなーたぶん。
とりあえずheroku対応のdocker imageでアプリ作っとけば幸せになれるんじゃないかと思います。