herokuにhaskellのウェブアプリをdocker:releaseをつかってデプロイしてみる


herokuにHaskellのウェブアプリをデプロイするのにbuildpack APIを調べようとしたらDockerをつかったリリースができることを知ったので、試した。

デプロイするアプリケーションは[ここでつくった](http://qiita.com/fujimura/items/b619e0a4ca2028b0eb03)ものがあるのでこれを利用する。イメージの名前は`eiel/wai-sample`です。

# 準備

dockerコマンドが使える状態になっている必要があります。

また、herokuコマンドをインストールして、dockerプラグインをインストールする必要があります。

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

# Procfileの作成

Procfileを作成します。/app/wai-sampleで実行できるようにしてありますので、

```Procfile
web: /app/wai-sample
```

とします。

# app.json
 app.jsonに image キーが必要になります。`eiel/wai-sample`を利用します。

```app.json
{
  "image": "eiel/wai-sample"
}
```

# heroku docker:init

ここまで進めると`heroku docker:init`で必要なファイルを生成できます。

```
$ heroku docker:init
```

このコマンドを実行するとDockerfileとdocker-compose.ymlが生成されます。

```Dockerfile
FROM eiel/wai-sample
```

```docker-compose.yml
web:
  build: .
  command: 'bash -c ''wai-sample'''
  working_dir: /app/user
  environment:
    PORT: 8080
  ports:
    - '8080:8080'
shell:
  build: .
  command: bash
  working_dir: /app/user
  environment:
    PORT: 8080
  ports:
    - '8080:8080'
  volumes:
    - '.:/app/user'
```

# デプロイする

herokuにアプリケーション領域をつくってデプロイします。

```
$ git init
$ git add .
$ git commit -m "initial commit"
$ heroku create
$ heroku docker:release
```

これで終了です。

```
$ heroku open
```

で、ウェブアプリをブラウザでひらくことができます。

# もっと詳しく

docker:releaseはDockerイメージ内の`/app`をherokuにアップロードしてくれます。その後`cd /app/user && 「Procfile」`の内容を実行してくれます。
これだけわかっていればHaskell以外のアプリケーションも動かせると思います。

docker release内部では[Release API](https://devcenter.heroku.com/articles/platform-api-deploying-slugs)を使用しているだけなので、dockerを使わずともLinux用のバイナリを生成してRelease APIをつかってデプロイすることもできると思います。

## バイナリの生成

Docker内で生成しています。これを補助するために作成したDockerImageも用意しています。

* イメージ [eiel/haskell-stack:lts-3.11-warp](https://hub.docker.com/r/eiel/haskell-stack/tags/)

Dockerfileはこっちにあります。

* [eiel/docker-haskell-stack - Dockerfile_WithWarp
](
https://github.com/eiel/docker-haskell-stack/blob/master/Dockerfile_WithWarp)

ビルド時間短縮するために warp のビルドまで済ませてあります。

このイメージをもとに wai-sample のイメージを生成しています。

* [eiel/wai-sample - Dockerfile](https://github.com/eiel/wai-sample/blob/master/Dockerfile)

/appにバイナリを配置して、/app/userディレクトリをつくっておくことで`heroku docker:release`でそのまま使えるようにしています。

### CIでビルド

Docker内で生成してもよいですが、CIでビルドしてバイナリだけとりだして、Docker化してあげる方法があります。
イメージも小さくなるのでおすすめです。(いっそCIでheroku releaseしたいけど今回はやりませんでした)
CircleCIだとコンパイル結果のキャッシュをきかせたりできるので2回目以降のビルドはとても早いです。

circle.ymlの例

```circle.yml
machine:
  services:
    - docker
dependencies:
  cache_directories:
    - "~/.stack"
  pre:
    - wget -q -O- https://s3.amazonaws.com/download.fpcomplete.com/ubuntu/fpco.key | sudo apt-key add -
    - echo 'deb http://download.fpcomplete.com/ubuntu/precise stable main'|sudo tee /etc/apt/sources.list.d/fpco.list
    - sudo apt-get update && sudo apt-get install stack -y
  override:
    - stack setup
    - stack install
    - stack build --test --only-dependencies

test:
  override:
    - stack test

deployment:
  docker:
    branch: master
    commands:
      - mkdir dist
      - cp `stack path --local-bin-path`/wai-sample dist/
      - docker build -t eiel/scotty-sample -f Dockerfiles/CircleCI .
      - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
      - docker push eiel/wai-sample
```

```Dockerfiles/CircleCI
FROM google/debian:wheezy

COPY dist/scotty-sample /app/
RUN mkdir /app/user
```

google/debian:wheezyを指定しているのは、私がGCEで使うことが多いので、指定しているだけです。お好みで。

# 参考文献

* [Heroku | Introducing 'heroku docker:release': Build & Deploy Heroku Apps with Docker](https://blog.heroku.com/archives/2015/5/5/introducing_heroku_docker_release_build_deploy_heroku_apps_with_docker)
* [Stackで管理しているHaskellのプロジェクトをCircleCIでビルドする - Qiita](http://qiita.com/fujimura/items/b619e0a4ca2028b0eb03)
* [samdoshi/haskell-stack - Docker Hub](https://hub.docker.com/r/samdoshi/haskell-stack/~/dockerfile/)