Dockerを使ってHaskellアプリをHerokuにデプロイする

コンパイル時間に制限されないデプロイ方法

Posted by Kadzuya Okamoto

これまで、HaskellのコードをHerokuで実行しようとすると、コンパイルがHerokuの制約時間内に終わらず、面倒なハックが必要でつらい状態でした。 でも、HerokuDockerをサポートするようになった今なら、Haskell製のウェブアプリケーションをHeroku上で公開するのはずっと簡単です。 この記事では、Servant(HaskellWebフレームワークの1)で作ったアプリケーションを、Dockerの力を借りてHerokuにデプロイする方法について、具体的なプログラムを使って順を追って説明していきます。

本記事について

この記事は、Releasing a Haskell Web App on Heroku with DockerとしてHaskell-jpオフィシャルスポンサーである株式会社ARoW公式ブログに公開されている英語の記事を、許可を得て日本版にローカライズしたものです1

はじめに

今回、実際にHerokuにデプロイして試せるように、サンプルアプリを用意しました。 この記事の最初の章では、このサンプルアプリをローカル環境で動かす方法について述べます。 「今度はDockerをつかってアプリを動かしてみよう!」では、同じくローカル環境において、Dockerを使って動かす方法について触れます。 Herokuで動かす」で、ついにHerokuにこのサンプルアプリをHerokuにデプロイする方法についてお伝えします。

もし、ローカル環境で動かしたりするのが面倒で、「いきなりHerokuにデプロイしたい!」という方は、 Herokuで動かす」から読んでいただいても問題ないように構成しているつもりです。

Docker使わずに サンプルアプリを実行してみる

今回用意したサンプルアプリは、以下の通りAPI2つだけ提供する、とても単純なものです。

  • かんたんなコメントのようなものを送信するためのAPI
  • これまでに送信された全コメントを表示するためのAPI

このサンプルアプリでは、コメントを保存するのにPostgreSQLを利用しています。

では、まずはDockerHerokuをつかわないで、実際にローカルな環境でこのアプリをビルドして実行する手順を追っていきましょう。

ローカル環境でサンプルアプリをビルドする

まず最初に、このサンプルアプリを公開しているgithubレポジトリをcloneして、アプリをビルドしてみましょう。

$ git clone https://github.com/cdepillabout/servant-on-heroku
$ cd servant-on-heroku/
$ stack setup  # このアプリが使っているバージョンのGHCをインストールします
$ stack build  # 依存パッケージをインストールし、ビルドします

もしかしたら、PostgreSQLのライブラリが入っていなくて、ビルドに失敗してしまうかもしれません。

Arch Linuxの場合は、以下のコマンドで必要なライブラリをインストールできます。

$ sudo pacman -Ss postgresql-libs

Ubuntuユーザの方は、以下のコマンドで大丈夫です。

$ sudo apt-get install libpq-dev

上記以外のプラットフォームでは別のコマンドを使うことになると思うので、いい感じにググってください。

では、PostgreSQLの必要なライブラリを入れたところで、stack buildをもう一度試してみましょう。今度はうまくいきましたよね?

うまくビルドできたら、アプリの実行をしてみます。

$ stack exec -- servant-on-heroku-api

わーお!なにかエラーが出ちゃいますね…

servant-on-heroku-api: libpq: failed (could not connect to server: Connection refused
        Is the server running on host "localhost" (::1) and accepting
        TCP/IP connections on port 5432?
        could not connect to server: Connection refused
        Is the server running on host "localhost" (127.0.0.1) and accepting
        TCP/IP connections on port 5432?
)

サンプルアプリがPostgreSQLに接続しようとして失敗しているようです。 このアプリは、コメントをPostgreSQLに保存しているので、PostgreSQLがローカルな環境で動いていないと、うまく動きません。

PostgreSQLのセットアップ

お使いの環境によって、PostgreSQLのインストール方法はまちまちなので、 そのプラットフォームが提供しているドキュメントにしたがって、PostgreSQLのインストールを行ってください。

たとえば、Arch Linuxの場合はこのドキュメントです。 Ubuntuならここにドキュメントがあります。

さて、PostgreSQLをインストールして、動いているのが確認できたら、もう一度アプリを起動してみましょう。

$ stack exec -- servant-on-heroku-api

わーお… またもやエラーです…

servant-on-heroku-api: libpq: failed (FATAL:  role "mydbuser" does not exist
)

どうやら、このサンプルアプリ用に、PostgreSQLのユーザとデータベースを用意しないといけないようですね。 実際にサンプルアプリのソースコード(src/Lib.hs)を見てみると、DATABASE_URLという環境変数の値を見てPostgreSQLサーバに接続しているのがわかります。

DATABASE_URL環境変数が指定されていない場合は、以下のデフォルト値が使われます。

postgres://mydbuser:[email protected]:5432/mydb

mydbuserというユーザ名で、mydbpassというパスワードを使ってmydbという名前のデータベースにアクセスしようとしているということですね。 では、実際にこのユーザとデータベースをPostgreSQLで作成してみましょう。 次のコマンドはArch Linuxでしか動かないかもしれません。 もし動かないようであれば、お使いのプラットフォームが提供するドキュメントを参照してください。

最初に、mydbuserという名前のユーザを、mydbpassというパスワードで作成しましょう。

$ sudo -u postgres -- psql --command "CREATE ROLE mydbuser NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN ENCRYPTED PASSWORD 'mydbpass'"

次にmydbという名前のデータベースを作成します。

$ sudo -u postgres -- createdb mydb

mydbusermydbデータベースにアクセスできるようにするのも忘れちゃいけませんね。

$ sudo -u postgres -- psql --command "GRANT ALL PRIVILEGES ON DATABASE mydb TO mydbuser"

ここで、PostgreSQLの再起動をしておいた方が無難でしょう。

$ sudo systemctl restart postgresql

これで、実際にmydbデータベースに、mydbuserとしてログインすることができるようになったはずです。

$ psql -U mydbuser -d mydb -h 127.0.0.1

APIを実際にたたいてみる

では、PostgreSQLのセットアップが無事終了したところで、次のコマンドでアプリケーションを立ち上げてみましょう。

$ stack exec -- servant-on-heroku-api
running servant-on-heroku on port 8080...

無事に立ち上がったら、コメントを送ってみます。 アプリが立ち上がった状態で、別のターミナルなどを開いて次のコマンドを打ってみてください。

$ curl --request POST \
    --header 'Content-Type: application/json' \
    --data '{"author": "DG", "text": "Pretty good"}' \
    'http://localhost:8080/add-comment'
{ "text": "Pretty good", "author": "DG" }

よさそうですね!

では、全コメントを取得してみます。

$ curl --request GET \
    --header 'Content-Type: application/json' \
    'http://localhost:8080/get-comments'
[{"text":"Pretty good","author":"DG"} ]

いいですね! DG (Dennis Gosnell / 原著者)さんが「チョベリグ!」と言っています。 以上で、ローカル環境でアプリを動かすことができたので、次はDockerを使ってみましょう!

今度はDockerをつかってアプリを動かしてみよう!

Dockerはコンテナ技術を用いたプログラムで、これを使うと仮想環境下でアプリをビルドしたり実際に動かしたりすることができます。 以降では、読者のみなさまがある程度Dockerについて知っている前提で進めていきますが、たぶんそんなによく知らなくても「まぁそんなもんなんだろう」と思いながら読んでいただければ差し支えないと思います。 実際、日本語ローカライズ版を作ってる僕だって、そんなにDockerに詳しいわけではありません。

Dockerをインストールする

Dockerのインストール方法は環境によってまちまちなので、ご自身の環境に合わせて信用できるドキュメントを参照してください。 Arch Linuxの場合Ubuntuの場合はリンク先を読めばなんとかなると思います。

Dockerのインストールが終わったら、以下のコマンドを実行してDockerがちゃんと動いているか確認してみてください。

$ docker info

Dockerを使ってビルドする

では、実際にDockerを使ってサンプルアプリをビルドし、そのアプリを動かすためのDockerイメージを作成します。

アプリケーションをビルドするには、docker buildコマンドを使います。

$ docker build -t servant-on-heroku .

このコマンドを実行すると、実行したディレクトリ内に存在するDockerfileという名前のファイルにしたがってアプリをビルドしてくれます。 このDockerfileには、アプリをビルドするための具体的な手順がすべて記述されており、その手続きにしたがって、まったく別の環境でもDockerさえあればアプリを実行できる「イメージ」を作成できます。

ためしに、このサンプルアプリに含まれるDockerfileの中身を見てみましょう。 以下の各処理を実行するようになっています。

  1. apt-getコマンドを使って、依存パッケージをインストール
  2. stackをインストール
  3. stack.yamlを見て、実際に必要なバージョンのGHCstackを使ってインストールする
  4. *.cabalファイルの記述にしたがって、アプリが使っているHaskellパッケージをインストールする
  5. stackを使って実際にアプリをビルドする
  6. rootユーザでアプリを実行したくないので、root権限をもつ別のユーザを作成しておく
  7. 実際にアプリを実行する

前述したdocker buildコマンドを実行してservant-on-herokuという名前のイメージを作成するには1時間近くかかるので2、その間にご飯を食べたり録画しておいたアニメを2本見れます。

DockerをつかってAPIをテストする

docker buildが終わったら、docker imagesでローカル環境に存在する全イメージを一覧表示してみましょう。

$ docker images
REPOSITORY           TAG       IMAGE ID       CREATED         SIZE
servant-on-heroku    latest    ff591d372461   30 seconds ago  3.92 GB
...

さきほど作成したservant-on-herokuのイメージが作成されているのがわかりますね?

では、servant-on-herokuのイメージを走らせてみましょう。次のコマンドを実行すれば、Docker内でこのサンプルアプリが動くはずです。

$ docker run --interactive --tty --rm servant-on-heroku

あぁ… またPostgreSQLの例の問題が出てしまったみたいですね…

servant-on-heroku-api: libpq: failed (could not connect to server: Connection refused
        Is the server running on host "localhost" (::1) and accepting
        TCP/IP connections on port 5432?
could not connect to server: Connection refused
        Is the server running on host "localhost" (127.0.0.1) and accepting
        TCP/IP connections on port 5432?
)

これはどういうことでしょうか。 servant-on-herokuコンテナはDockerコンテナとして動いているため、初期設定では我々のローカル環境が見えず、もちろんローカル環境にセットアップしてlocalhost:5432で動いているPostgreSQLも見えないのです。

では、ちょっとしたワザを使ってこの問題を解決してみましょう。 servant-on-herokuコンテナを動かしている時に、Dockerに我々のローカル環境のネットワークインタフェースを使うように指示することができます。

$ docker run --interactive --tty --rm --network host servant-on-heroku
running servant-on-heroku on port 8080...

ほら、こうすれば、確かにDockerコンテナからPostgreSQLにアクセスできているようです。

servant-on-herokuコンテナが動いている状態で別のシェルを立ち上げて、前の章でやったようにcurlコマンドでAPIが動いているか確かめてみましょう。 まずはコメントの投稿です。

$ curl --request POST \
    --header 'Content-Type: application/json' \
    --data '{"author": "EK", "text": "Not enough CT"}' \
    'http://localhost:8080/add-comment'
{ "text": "Not enough CT", "author": "EK" }

今度はコメントの取得をしてみます。

$ curl --request GET \
    --header 'Content-Type: application/json' \
    'http://localhost:8080/get-comments'
[{"text":"Pretty good","author": "DG"},{"text":"Not enough CT","author":"EK"}]

この通り、無事にEK (Edward Kmett / Haskell界のすごい人)さんが「圏論を、圏論をもっとくれぇええええい!」と言っているコメントが追加されました。

ちなみに、Docker内でシェルを開いて、手動でDockerイメージをいじりながらいろいろ確かめてみるには、次のコメントのようにすればOKです。

$ docker run --interactive --tty --rm --network host servant-on-heroku /bin/bash

では、Docker上でアプリがちゃんと動いたことを確認したところで、ようやくHerokuの出番です。

Herokuで動かす

Docker上でビルドと実行ができていさえすれば、Herokuにデプロイするのは難しくありません。 まず最初にHerokuのアカウントを作りましょう。

Herokuアカウントを作成する

Herokuアカウント作成ページでアカウントを作成してください。 もちろん、すでにアカウントを持っているのであればあえて別のアカウントを作りなおす必要はないですよ!

今回はHerokuの無料枠を使ってアプリをデプロイするので、クレジットカードの登録は必要ないです。 こわくないですね!

ここで説明する内容は、ほとんどHeroku公式ドキュメントを参照しているので、なにかわからないところがあったらそちらをチェックしてみてください。

Herokuのコマンドラインプログラムをインストールする

HerokuCLIで操作するためのコマンドを用意してくれているので、これを使って便利にHerokuを使い倒せます。 ちょうど、AWSCLIとか、Digital OceanCLIプログラムと同じような感じです。

Arch Linux使いの方は、下記のコマンドでHerokuCLIプログラムをインストールできます。

$ yaourt -S heroku-toolbelt

このコマンドは、herokuコマンドのバイナリを直接取得してインストールしてくれます。

他の環境の方は、Herokuの公式ドキュメントをご覧ください。

CLIプログラムのインストールができたら、コマンドライン上でログインして、権限を必要とする操作ができる状態にしておきましょう。

$ heroku login

このコマンドを実行すると、ユーザ名とパスワードをたずねられるので、事前に作成しておいたアカウントの情報を入力してください。

Heroku上でアプリケーションを登録する

今回のサンプルアプリをHerokuで公開するには、まずHeroku上でアプリケーションの登録をする必要があります。

以下のコマンドを実行すると、servant-on-herokuという名前のアプリケーションをHerokuに登録できます。 必要に応じてservant-on-herokuの部分を別の名前に変更してアプリケーションを登録してください。

$ heroku apps:create servant-on-heroku

以下のコマンドで、いま新規登録されたアプリケーションについての情報を一応取得できます。

$ heroku apps:info servant-on-heroku
=== servant-on-heroku
Auto Cert Mgmt: false
Dynos:
Git URL:        https://git.heroku.com/servant-on-heroku.git
Owner:          [email protected]
Region:         us
Repo Size:      0 B
Slug Size:      0 B
Stack:          cedar-14
Web URL:        https://servant-on-heroku.herokuapp.com/

Web URLの項目だけ、あとで使うのでどこかにメモしておいてください。 他の項目は、いまは特に気にしなくて大丈夫です。

Heroku Docker Pluginをインストールする

Herokuのコマンドラインプログラムは、プラグインを追加することで、どんどん便利な機能を使えるようにできます。

今回は、Heroku Container Registryというプラグインを使いましょう。

以下のコマンドで、このプラグインがインストールされます。

$ heroku plugins:install heroku-container-registry

インストールが終わったら、次のコマンドを実行して、ちゃんと動いているか確認してください。

$ heroku container
4.1.1

きっと、このプラグインのバージョンナンバーが表示されたはずです。

実際にプラグインを使うためには、以下のコマンドでHeroku Container Registryにログインする必要があります。

$ heroku container:login

このコマンドによって、Container Registryのログイン情報が、~/.docker/config.jsonというファイルに追加されます。

$ cat ~/.docker/config.json
{
  "auths": {
    "registry.heroku.com": {
      "auth": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx="
    }
  }
}

アプリケーションをHeroku上で動かす

実際にアプリをHeroku上で動かすには、以下のコマンドを使います。

$ heroku container:push web

これを実行すると、実行したディレクトリ内にあるDockerfileの設定にしたがってDockerイメージを作成します。 内部では、ローカル環境でDockerイメージを作成するときに使ったのと同じdocker buildを呼んでいます。 前の章で実際にdocker buildを実行した方は、その際に作成したイメージがそのままDocker Container Registryに送られるので安心してください。また1時間も待つなんてイヤですよね。

では、heroku apps:infoをもう一度実行して確認してみましょう。

$ heroku apps:info servant-on-heroku
=== servant-on-heroku
Auto Cert Mgmt: false
Dynos:
Git URL:        https://git.heroku.com/servant-on-heroku.git
Owner:          [email protected]
Region:         us
Repo Size:      0 B
Slug Size:      0 B
Stack:          cedar-14
Web URL:        https://servant-on-heroku.herokuapp.com/

あれ? なにかおかしいですね… Dynos:のところになにも書いてありません。 dynoというのはHerokuが独自に使っている用語で、ウェブアプリを実行する1台のサーバのことを意味します。ここになにも書かれていないということは、アプリを実行しているサーバがいないということになります。

これをどうにかするためには、heroku ps:scaleを使います。

$ heroku ps:scale web=1

これで、“webdyno1台分作成され、その上で今回のサンプルアプリが動くようになります。3

では、次のコマンドを実行して、dynoがちゃんと動いていることを確認しましょう。

$ heroku ps
Free dyno hours quota remaining this month: 549h 2m (99%)
For more information on dyno sleeping and how to upgrade, see:
https://devcenter.heroku.com/articles/dyno-sleeping

=== web (Free): /bin/sh -c /opt/servant-on-heroku/bin/servant-on-heroku-api (1)
web.1: starting 2017/03/22 19:05:04 +0900 (~ 8s ago)

なんだか余計な情報もだらだら出てきますが、web dyno1台分動いていることが確認できます。

これで、サンプルアプリが動くようになったので、curlを使って、Web URLにアクセスしてみましょう。 (サンプルアプリのWeb URLは、heroku apps:infoに書いてありましたよね?)

$ curl --request POST \
    --header 'Content-Type: application/json' \
    --data '{"author": "MS", "text": "Gotta make it professional"}' \
    'https://servant-on-heroku.herokuapp.com/add-comment'

なにかおかしいですね… なにもレスポンスが返ってきません。 なにかエラーが出ているはずなので、Heroku上で起こったエラーを実際に見てみたいです。

Heroku上で動いているアプリのエラーを見てみる

Herokuには、とってもすばらしいログ機能があり、アプリの標準エラーや標準出力を簡単にチェックできます。

$ heroku logs
2017-03-22T10:05:49 heroku[web.1]: proc start `/opt/servant-on-heroku/bin/servant-on-heroku-api`
2017-03-22T10:05:52 app[web.1]: servant-on-heroku-api: libpq: failed (could not connect to server: Connection refused
2017-03-22T10:05:52 app[web.1]:    Is the server running on host "localhost" (127.0.0.1) and accepting
2017-03-22T10:05:52 app[web.1]:    TCP/IP connections on port 5432?
2017-03-22T10:05:52 app[web.1]: )
2017-03-22T10:05:52 heroku[web.1]: State changed from starting to crashed

とても便利ですね! どうやらこれまで何度も見てきた例のエラーがまた出ているようです…

今回は、Heroku上で動いているPostgreSQLデータベースをちゃんとセットアップしていないのが理由です。

HerokuPostgreSQLサポート

HerokuPostgreSQLについてしっかりサポートしてくれている上に、なんと無料枠まで設けてくれています。

以下のコマンドを実行すれば、PostgreSQLのアドオンが使えるようになります。

$ heroku addons:create heroku-postgresql:hobby-dev

これで、heroku-postgresqlアドオンを、無料で使えるhobby-dev利用枠で使えるようになりました。

では、本当にPostgreSQLが作成されたか、以下のコマンドを使って確認してみましょう。

$ heroku addons:info heroku-postgresql
=== postgresql-tetrahedral-44549
Attachments:  servant-on-heroku::DATABASE
Installed at: Wed Mar 22 2017 19:22:14 GMT+0900 (JST)
Owning app:   servant-on-heroku
Plan:         heroku-postgresql:hobby-dev
Price:        free
State:        created

データベースの詳細情報については、pg:infoコマンドを使って見れます。

$ heroku pg:info
=== DATABASE_URL
Plan:        Hobby-dev
Status:      Available
Connections: 0/20
PG Version:  9.6.1
Created:     2017-03-22 10:22 UTC
Data Size:   7.2 MB
Tables:      1
Rows:        0/10000 (In compliance)
Fork/Follow: Unsupported
Rollback:    Unsupported
Add-on:      postgresql-tetrahedral-44549

アプリケーションを再起動する

これでPostgreSQLのデータベースが動くようになったので、アプリを再起動しましょう。

$ heroku ps:restart

もう一度ログを見て、本当にこれでエラーが出なくなったか確かめてみます。

$ heroku logs
2017-03-22T10:22:15 heroku[web.1]: State changed from crashed to starting
2017-03-22T10:22:54 heroku[web.1]: proc start `/opt/servant-on-heroku/bin/servant-on-heroku-api`
2017-03-22T10:22:56 app[web.1]: Migrating: CREATe TABLE "comment"("id" SERIAL8  PRIMARY KEY UNIQUE,"author" VARCHAR NOT NULL,"text" VARCHAR NOT NULL)
2017-03-22T10:22:57 heroku[web.1]: State changed from starting to up

すごーい!ついに、ついにちゃんと動いたみたいです!!

もう一度curlコマンドを使ってAPIがちゃんと動いているか確認してみます。

$ curl --request POST \
    --header 'Content-Type: application/json' \
    --data '{"author": "SPJ", "text": "Avoid heroku-at-all-costs"}' \
    'https://servant-on-heroku.herokuapp.com/add-comment'
{"text":"Avoid heroku-at-all-costs","author":"SPJ"}

ちゃんとレスポンスが返ってきています! 今度はコメントを取得してみましょう。

$ curl --request GET \
    --header 'Content-Type: application/json' \
    'https://servant-on-heroku.herokuapp.com/get-comments'
[{"text":"Avoid heroku-at-all-costs","author":"SPJ"}]

いいですね! SPJ(Simon Peyton Jones / Haskellの父)さんが「目先の便利さにとらわれてHerokuに余計な機能をいれるのは、ダメ。ゼッタイ。」4と言っています。 これで全てうまくいったようです。

Heroku上のアプリは接続先のDBをどうやって見つけているのか

賢明な読者のみなさんは、「Heroku上のアプリはどうやってデータベースを見つけているんだろう?」と疑問に思ったかもしれません。 実は、Herokuにはアプリに環境変数を与える仕組みがあります。

この環境変数の設定値を確かめるには、以下のコマンドが使えます。

$ heroku config
=== servant-on-heroku Config Vars
DATABASE_URL: postgres://someusername:[email protected]:5432/databasename

heroku-postgresqlアドオンでPostgreSQLのデータベースを作成した際に、DATABASE_URLという名前の設定値が追加されます。 Herokuはアプリの起動時にこの設定値を環境変数として与えているのです。 先に述べたとおり、今回のサンプルアプリは、DATABASE_URLという環境変数を接続先DBの情報として受け取るようになっています5

Herokuに設定されている環境変数は、heroku config:get VAR_NAMEで取得できるので、次のコマンドを使ってDBに接続することもできます。

$ psql "$(heroku config:get DATABASE_URL)"
psql (9.6.1)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)

databasename=> select * from comment;
 id | author |           text
----|--------|---------------------------
  1 | SPJ    | Avoid Heroku at all costs
(1 row)

アプリのアップデート

Heroku上で動いているアプリをアップデートするのは、とっても簡単です。 単に以下のコマンドを実行するだけで大丈夫です。

$ heroku container:push web

このコマンドは、DockerイメージをビルドしなおしてHeroku container registryにアップします。 その後関係するdynoを全て再起動して、アップデート後のアプリを実行するようにします。

もっと良くするために

このサンプルアプリは、いまのままでもいい感じですが、いくつかまだ改善の余地があります。 一番手っ取り早い改善箇所は、Dockerfileでしょう。 Dockerfileをもっと良くするためのアイディアをいくつか挙げてみます。

  • Dockerfileのベースイメージに、もっとファイルサイズが小さいものを使う

    現状では、Herokuのイメージを使っていますが、 たぶんもっと軽いAlpine Linuxを使っても問題はないと思います。

  • stackGHC、その他よく使うHaskellライブラリが最初から入っているイメージをベースにする

    こうすることで、一番最初のdocker buildに要する時間をガッツリ削ることができます。

  • Dockerfileの一番最後で、stackGHC、全Haskellライブラリを削除するようにする

    こうすることで、Dockerイメージのサイズを少し減らせる可能性があります。 Heroku container registryにイメージをアップロードするのが、いくらか早くなるでしょう。

また、docker-composeなどを使って、ローカルで実行する際にもDockerを使ってPostgreSQL DBをセットアップするのも良いかもしれません。

まとめ

ローカル環境上でDockerが動いていれば、Heroku上でHaskellのコードを動かすのはとても簡単です。 Herokuの無料枠はアプリのプロトタイプを他の人に試してもらったりするのに最適です。 もちろん、そのままリリースしたら負荷にたえられないかもしれませんが、アプリ開発の最初期段階にコンセプトを検証したりするのには十分でしょう。

もし、検証の結果うまくいきそうだと分かったら、クレジットカードを登録して、 もっと負荷にたえられる有料利用枠でアプリを動かすようにするのだって簡単です。

脚注


  1. 僕が自分で許可して、原著者に日本語の内容をチェックしてもらいました。また、翻訳ではなくローカライズなので、原文の逐語訳ではなく、日本語話者にとって理解しやすいように一部加筆修正してあります。

  2. ここで挙げた7ステップはちょっと複雑です。 もちろん、1コマンドだけでGHCのインストールから依存ライブラリのインストール、 アプリ自体のビルドまで完了することもできますが、ここではDockerのキャッシュ機構を活用するために いくつものコマンドに分けて記述してあります。 Dockerのキャッシュ機構によって、docker buildを再実行するときには、入力値が変わったコマンドだけが実行されるようになっています。 たとえば、servant-on-heroku.cabalのファイルを変更してdocker buildを再実行すると、 .cabalファイルに書かれた依存ライブラリをインストールする(4)のステップからイメージを再ビルドし始めます。 キャッシュされているデータを利用するので、(1)から(3)までのステップを省略できるのです。

    同じように、src下のファイルだけを変更してdocker buildを再実行すると、 (5)のステップ以降のみが実行されます。 GHCや依存ライブラリをわざわざ再インストールする必要はないからです。

    このように、ステップをいくつかに分割することで、ビルド時間を大きく節約することができ、 2回目以降のビルドが数分で終わるようになります。最初のビルドは1時間もかかっていたのに、ちょろいですね。

  3. dynoにはいろいろな種類のものがありますが、 今回のような単純なWeb APIであれば別にこだわる必要はないです。

  4. 元ネタは同氏のAvoid success at all costsという言葉で、「目先の便利さにとらわれて、Haskellに余計な機能をいれるのは、ダメ。ゼッタイ。」みたいな意味。

  5. Herokuは、他にもPORTという環境変数も使っていて、アプリケーションにどのポートでリクエストを待ち受けるかを指定できます。