NimでHeroku


はじめに

ちょっと前にFlaskで作ったアプリをHerokuにデプロイしました。
そういえばNimにもjesterあったなー、と思いだしたので簡単なwebアプリを作ってHerokuにデプロイしてみます。
どっちかというとHerokuでNimですね。

環境

  • nim 0.20.0
  • Heroku Freeプラン

Herokuの準備

こちらのbuildpackを使ってHerokuにnimの環境を作ります。

heroku create --stack cedar --buildpack https://github.com/vic/heroku-buildpack-nim.git

新しいアプリができました

Config VarsにKey=NIM_BRANCH, Value=develを設定します。
理由がよくわかってませんがこれがないとnimのビルドができませんでした。(そのうち調べます)

アプリの準備

jesterのチュートリアルを参考にアプリを作ります。他にnimble, procfileも必要です。

main.nim
import jester, posix, json, logging, os, strutils, asyncdispatch
import htmlgen as h

onSignal(SIGABRT):
  echo "<2>Received SIGABRT"
  quit(1)

let
  fl   = newFileLogger("logs.log",
                       fmtStr = "$datetime $levelname ")
addHandler(fl)

proc log_debug(args: varargs[string, `$`]) =
  debug args
  fl.file.flushFile()

proc log_info(args: varargs[string, `$`]) =
  info args
  fl.file.flushFile()

var settings = newSettings()
if existsEnv("PORT"):
  settings.port = Port(parseInt(getEnv("PORT")))

routes:
  get "/":
    resp h.h1("Hello myservice!!!")

when isMainModule:
  log_info "starting"
  runForever()
main.nimble
# Package
bin           = @["main"]
# Dependencies
requires "nim >= 0.20.0, jester"
Procfile
web: ./main

いざデプロイ

必要なものがそろったのでデプロイします。

git add .
git commit -m "first commit"
git push heroku master

しばらくしてからアプリを起動すると...

成功です!!!

おわりに

とりあえず動かしてみた、のレベルなのでwebアプリとは呼べませんね...
ただ、デプロイまでにあたった壁を自力で解決できたので、ちょっとスキルアップした気がします。(ログみたり、ソース追ったり)
次はDBと連携させたいです。

はまったポイント

ポートの指定エラー

デプロイしても起動せず...

Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch

Herokuが設定するポートをアプリで使うように設定する必要がありました。この記述が抜けてました。

var settings = newSettings()
if existsEnv("PORT"):
  settings.port = Port(parseInt(getEnv("PORT")))

タイポでエラー

ポートを設定したのに起動しない...

Error: unhandled exception: index 20 not in 0 .. 19 [IndexError]

これはHerokuではなく自分のタイポが原因でした。newFileLoggerの引数にfmtStrを設定してますが、こう書くべきところを

let fl = newFileLogger("log.txt", fmtStr = "$datetime $levelname ")

こう書いており、$levelnameの後ろにスペース入れ忘れてました。

let fl = newFileLogger("log.txt", fmtStr = "$datetime $levelname")

logging.nimのsubstituteLogでFileLoggerのfmtStrからログの種類を判定します。
fmtStrを1文字ずつresultに追加し['a'..'z', 'A'..'Z', '0'..'9', '_']以外の文字になったら追加をやめ、いままで追加した文字から種類を判定します。
スペースを入れ忘れたせいで20文字のfmtStrなのに21文字目をresultに追加しようとしてIndexErrorが発生しました。てへぺろ

参考