Dokku環境を構築してHaskellのアプリケーションをデプロイする

この記事は [Recruit Engineers Advent Calendar 2017](https://adventar.org/calendars/2324) の7日目の記事です。

# はじめに

普段はiOSエンジニアをしていますが、学習目的でちょっとしたWebサイトやツールなどをHaskellで作って遊んでいます。プロジェクト作成からデプロイまでを気軽に行いたいのでDokku環境を構築しています。今回はUbuntuにDokku環境を構築してHaskellのアプリケーションをデプロイする、入門的なお話をします。

# 開発環境

* Hostマシン
    * Ubuntu `16.04`
    * [Dokku](http://dokku.viewdocs.io/dokku/#install-curl) `0.10.5`
* Haskell
    * [stack](https://docs.haskellstack.org/en/stable/README/) `1.5.1`

以下、HostとClientを切り替えながら作業するので、紛らわしくないようにタイトルに（Host）/（Client）のタグを付けました。

# なぜDokkuなのか

PaaSを選定する上で [Deis](https://deis.com/) も検討したのですが、Dokkuのほうがマシンの推奨環境がより小さいためこちらを選択しました。それだけです。どちらもBuildpack/Dockerfile/DockerImageでのデプロイに対応しており使い勝手などは似ていそうです。現在は適当なVPSにDokkuをインストールして利用しています。

# Dokkuをインストール （Host）

Dokkuのインストールは以下の２行です。とっても簡単ですね。

```
$ wget https://raw.githubusercontent.com/dokku/dokku/v0.10.5/bootstrap.sh
$ sudo DOKKU_TAG=v0.10.5 bash bootstrap.sh
```

Dokkuがインストールされていることを確認します。

```
$ dokku version
0.10.5
```

# Dokkuの初期化 （Client）

DokkuがインストールされるとHostマシンで初期化用のWebページが立ち上がった状態になります。ブラウザでHostマシンのIPにアクセスすると以下のようなWebサイトが表示されます。

<img width="723" alt="スクリーンショット 2017-12-04 23.56.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/50541/187a3784-e274-7048-3a7b-104a35b5bad1.png">

各入力項目の説明をします。

* SSH Public Key
    * git push/fetch などに使うSSH公開鍵です
    * GitHubにSSH公開鍵を登録するのと同じ感覚です
* Hostname
    * ドメインを入力します
* Use virtualhost naming for apps
    * チェックを入れるとアプリケーション名がサブドメインになります。 `http://<app-name>.<yourdomain>` でアプリケーションへアクセスすることができます。

# Dokkuアプリケーションを作成　（Host）

`dokku apps:create` でアプリケーションを作成します。今回は `haskell-dokku-sample` というアプリケーションを作りました。

```
$ dokku apps:create haskell-dokku-sample
Creating haskell-dokku-sample... done

$ dokku apps:list
=====> My Apps
haskell-dokku-sample
```

Dokkuの操作に関しては [dockland](https://github.com/uetchy/dockland) というgemを使えば、Client側から簡単に操作することも可能です。

# Haskellのアプリケーションを作成 （Client）

## 初期化

`stack new` でプロジェクトを初期化します。

``` bash
$ stack new haskell-dokku-sample simple
$ stack build
$ stack exec haskell-dokku-sample
hello world
```

## アプリケーション

今回はシンプルなWebのアプリケーションとします。 `build-depends` に `warp` `wai` `http-types` を追加します。

```text:haskell-dokku-sample.cabal
executable haskell-dokku-sample
  hs-source-dirs:      src
  main-is:             Main.hs
  default-language:    Haskell2010
  build-depends:       base >= 4.7 && < 5
                     , warp
                     , wai
                     , http-types
```

Webアプリケーションのコードは以下のとおりです。リクエストに対し `Hello!` を返すだけの単純なものです。

```haskell:src/Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main where

import Network.Wai (responseLBS, Application)
import Network.HTTP.Types (status200)
import Network.Wai.Handler.Warp (run, Port)
import System.Environment (getEnvironment)
import Data.List (lookup)
import Data.Maybe

main :: IO ()
main = do
  port <- getPort
  run port helloApp

helloApp :: Application
helloApp req respond = respond $ responseLBS status200 [] "Hello!"

getPort :: IO Port
getPort = getEnvironment >>= return . port
  where
    port = fromMaybe defaultPort . fmap read . lookup "PORT"

defaultPort :: Port
defaultPort = 80
```

Dokkuにデプロイする前にビルドが通ることを確認します。

```
$ stack build
```

## Buildpack

DokkuはHerokuのBuildpackに対応しています。HaskellのBuildpackはHerokuで公式にサポートされていないため以下を利用します。

[mfine/heroku-buildpack-stack](https://github.com/mfine/heroku-buildpack-stack)

 `.buildpacks` ファイルを以下のように作成します。

``` bash:.buildpacks
https://github.com/mfine/heroku-buildpack-stack
```

## Procfile

DokkuはHerokuのように `Procfile` を認識してプロセスを実行します。上記Buildpackでは実行ファイルにパスが通った状態になるので `Procfile` を以下のように作成します。

``` bash:Procfile
web: haskell-dokku-sample
```

# Dokkuにデプロイする （Client）

DokkuはこれまたHerokuのように `git push` コマンド一発でデプロイできます。

``` bash
$ git push dokku master
...
=====> Downloading Buildpack: https://github.com/mfine/heroku-buildpack-stack
=====> Detected Framework: Haskell
...
=====> Application deployed:
       http://haskell-dokku-sample.yourdomain.com

To yourdomain.com:haskell-dokku-sample
 * [new branch]      master -> master
```

これで　`http://haskell-dokku-sample.yourdomain.com` に対してWebアプリケーションがデプロイされました。
お手持ちのブラウザから確認します。

<img width="579" alt="スクリーンショット 2017-12-05 0.55.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/50541/15c8aa34-6ba3-c67a-0c09-b7c8b84b215f.png">

表示されましたね :tada: :tada: :tada: 

# 最後に

今回はDokku環境の構築からHakellのアプリケーションのデプロイまでを紹介しました。気軽にデプロイできる環境を一つ用意しておくと非常に重宝します。趣味レベルのアプリケーションにHeroku使うのもな、、と考えている方におすすめです。次はSwiftアプリケーションのデプロイなんかも試してみたいと思います。