Yesod on Heroku

@thimura hontai

   2012-04-22




                  1 / 19
What is Heroku?


• PaaS (Platform as a Service) 型
• 当初は Ruby on Rails のみ
• Cedar Stack の導入で Python, Java, Scala,
  Clojure, Node.js などに対応
• 1dyno (≒ プロセス), メモリ 300MB,
  PostgreSQL 5MB まで 無料 で使える
• たくさんのアドオン




                                          2 / 19
Dyno




                http://www.heroku.com/how


• プロセスのようなもの
• サーバーや VM を意識する必要なし
• web, worker, clock などの Process Type
• Procfile をもとに生成される
  yesod init した場合は deploy/Procfile に自動的に生成
• Process Type 毎に拡張可能
   $ heroku ps:scale web=10 worker=2
   $ heroku ps:scale web-1 worker+2
• 不安定になった Dyno は自動的に再起動される
                                            3 / 19
Slug


• Slug = Dyno の元になるオブジェクト
• git push した際の hook で生成される
• 言語・フレームワーク独自のファイルの有無に
  よって環境を識別する
• 判定に失敗した場合はデプロイを拒否さ
  れる……
• package.json を置いておくと Node.js として
  判定



                                     4 / 19
Haskell は?




             5 / 19
Haskell on Heroku


           Celadon Cedar Stack なら
   Haskell アプリケーションをデプロイ可能!!!

Celadon Cedar Stack
  • Ubuntu 10.04 LTS (lucid) (64bit)
  • package.json ファイルを置いておくと Node.js
    アプリケーションとして認識される
12.04 LTS が出た後はどうなるの……?


                                       6 / 19
Heroku gem


• Heroku gem をインストール
  $ gem install heroku 
    --user-install --no-ri --no-rdoc
• 公開鍵を登録
  $ heroku login
  Enter your Heroku credentials.
  Email: user@example.com
  Password:



                                       7 / 19
Heroku で Haskell を動かす


• Node.js アプリに擬態
  $ cat package.json
  { "name": "yesod-test",
    "version": "0.0.1",
    "dependencies": {} }
• Procfile を作成
 $ cat Procfile
 web: ./dist/build/yesod-on-heroku/yesod-on-heroku production -p $PORT




                                                                         8 / 19
データベースを使う


• 環境変数 DATABASE_URL,
  SHARED_DATABASE_URL を読んで自力で頑張る
• heroku package を使う
  http://hackage.haskell.org/package/heroku

    deploy/Procfile に詳しいやりかたが
           書いてあるので……




                                         9 / 19
デプロイ

• Cedar Stack にアプリを作成
  $ heroku create --stack cedar
  Creating strong-mist-1328... done, stack is cedar
  http://strong-mist-1328.herokuapp.com/ | git@heroku.com:strong-mist-1328.gi
  Git remote heroku added
• コンパイル
  $ cabal configure -fproduction
  $ cabal build
• デプロイ
  $   git   checkout -b deploy
  $   git   add dist/build/yesod-test/yesod-test
  $   git   commit -m "deploy"
  $   git   push heroku deploy:master

• $ heroku open



                                                                       10 / 19
コンパイルする際注意すること

• Ubuntu 10.04 LTS (lucid) 64bit で動作するバイ
  ナリを作成
• VM に lucid を入れてコンパイルするのが楽?
  (lucid の GHC のバージョンは 6.12.1 …… orz)
• GHC はデフォルトでは……
   • 可能な限り Haskell パッケージの共有ライブラリ を
     利用せず静的にリンク
   • 実行ファイル自体は動的リンク
   • libyaml などのライブラリは動的リンク

→ 共有ライブラリが発見できない


                                           11 / 19
エラーが起きたときは

• ログを確認してみる
 $ heroku logs
 2011-11-15T17:38:20+00:00 app[web.1]: ./dist/build/yesod-test/yesod-test:
  error while loading shared libraries: libyaml-0.so.2:
   cannot open shared object file: No such file or directory
 2011-11-15T17:38:20+00:00 heroku[web.1]: Process exited

• リモートのシェル環境で調査
 $ heroku run ldd dist/build/yesod-test/yesod-test
 Running ldd ./dist/build/yesod-test/yesod-test attached to terminal... up, run.37
 linux-vdso.so.1 => (0x00007fffb7dff000)
 libz.so.1 => /lib/libz.so.1 (0x00007fe9963ba000)
 libyaml-0.so.2 => not found
 librt.so.1 => /lib/librt.so.1 (0x00007fe9961b1000)
 libutil.so.1 => /lib/libutil.so.1 (0x00007fe995fae000)
 libdl.so.2 => /lib/libdl.so.2 (0x00007fe995daa000)
 libgmp.so.10 => not found
 libffi.so.5 => not found
 libm.so.6 => /lib/libm.so.6 (0x00007fe995b26000)
 libpthread.so.0 => /lib/libpthread.so.0 (0x00007fe995908000)
 libc.so.6 => /lib/libc.so.6 (0x00007fe995585000)
 /lib64/ld-linux-x86-64.so.2 (0x00007fe9965d9000)




                                                                                     12 / 19
解決策




• 静的リンクする
• 共有ライブラリを含めてデプロイする




                      13 / 19
解決策: 静的リンク
                    全てのライブラリを静的にリンク




• yesod-test.cabal の ghc-options を書き換え
 ghc に対して -optl-pthread -optl-static
 フラグを指定
 ... (略)
 executable         yesod-test
     if flag(devel)
         Buildable: False

     if flag(production)
         cpp-options:    -DPRODUCTION
         ghc-options:    -Wall -threaded -O2 -optl-pthread -optl-static
     else
         ghc-options:    -Wall -threaded -O0
 ... (略)




                                                                          14 / 19
解決策: 静的リンク
                     問題がある物のみを静的にリンク

• package.conf.d/integer-gmp-*.conf を書き換え
• extra-libraries から yaml を消去
• ld-options: ”-Wl,-Bstatic” -lyaml ”-Wl,-Bdynamic”
  と書き換えてしまう
  これを
  $ cat /var/lib/ghc/package.conf.d/yaml-0.5.2.conf
  name: yaml
  version: 0.5.2
  id: yaml-0.5.2-90d2ec65096da81d098bd329cb16d6bf
  ... (略)
  extra-libraries: yaml
  extra-ghci-libraries:
  include-dirs:
  includes:
  depends: aeson-0.6.0.1-1adff47713ff03277e972cd2e23ec8fc
  ... (略)
  hugs-options:
  cc-options:
  ld-options:
  framework-dirs:
  frameworks:
  haddock-interfaces: /usr/lib/ghc-doc/haddock/yaml-0.5.2/yaml.haddock
  haddock-html: /usr/share/doc/libghc-yaml-doc/html/

                                                                         15 / 19
解決策: 静的リンク
                     問題がある物のみを静的にリンク

• package.conf.d/integer-gmp-*.conf を書き換え
• extra-libraries から yaml を消去
• ld-options: ”-Wl,-Bstatic” -lyaml ”-Wl,-Bdynamic”
  と書き換えてしまう
  こうする
  $ cat /var/lib/ghc/package.conf.d/yaml-0.5.2.conf
  name: yaml
  version: 0.5.2
  id: yaml-0.5.2-90d2ec65096da81d098bd329cb16d6bf
  ... (略)
  extra-libraries:
  extra-ghci-libraries:
  include-dirs:
  includes:
  depends: aeson-0.6.0.1-1adff47713ff03277e972cd2e23ec8fc
  ... (略)
  hugs-options:
  cc-options:
  ld-options: "-Wl,-Bstatic" -lyaml "-Wl,-Bdynamic"
  framework-dirs:
  frameworks:
  haddock-interfaces: /usr/lib/ghc-doc/haddock/yaml-0.5.2/yaml.haddock
  haddock-html: /usr/share/doc/libghc-yaml-doc/html/

                                                                         16 / 19
解決策: 共有ライブラリを含めてデプロイする




 • 環境変数 LD_LIBRARY_PATH を指定する
 • コンパイル時に -optl-Wl,-rpath,’$ORIGIN’
  等として RPATH を埋め込む
今回は LD_LIBRARY_PATH を使う方法について解説




                                       17 / 19
解決策: 共有ライブラリを含めてデプロイする


 • Heroku 環境に含まれていないライブラリをコ
   ピーし git の管理下に
   libyaml, libffi, libgmp などなど
 • シェルスクリプト run を作成
  $ cat run
  #!/bin/sh
  LD_LIBRARY_PATH=$PWD/dist/build/yesod-test 
  ./dist/build/yesod-test/yesod-test -p $PORT -e production

 • Procfile を書き換え
  $ cat Procfile
  web: ./run




                                                              18 / 19
まとめ?



 • Ruby (heroku gem)
 • Ubuntu 10.04 LTS (64bit)
   用にビルド
 • package.json を置いて
   Node.js アプリを擬態
 • 必要な知識 : Binary Hacks




                              19 / 19
ご静聴ありがとうございました




                 20 / 19

Yesod on Heroku

  • 1.
    Yesod on Heroku @thimurahontai 2012-04-22 1 / 19
  • 2.
    What is Heroku? •PaaS (Platform as a Service) 型 • 当初は Ruby on Rails のみ • Cedar Stack の導入で Python, Java, Scala, Clojure, Node.js などに対応 • 1dyno (≒ プロセス), メモリ 300MB, PostgreSQL 5MB まで 無料 で使える • たくさんのアドオン 2 / 19
  • 3.
    Dyno http://www.heroku.com/how • プロセスのようなもの • サーバーや VM を意識する必要なし • web, worker, clock などの Process Type • Procfile をもとに生成される yesod init した場合は deploy/Procfile に自動的に生成 • Process Type 毎に拡張可能 $ heroku ps:scale web=10 worker=2 $ heroku ps:scale web-1 worker+2 • 不安定になった Dyno は自動的に再起動される 3 / 19
  • 4.
    Slug • Slug =Dyno の元になるオブジェクト • git push した際の hook で生成される • 言語・フレームワーク独自のファイルの有無に よって環境を識別する • 判定に失敗した場合はデプロイを拒否さ れる…… • package.json を置いておくと Node.js として 判定 4 / 19
  • 5.
  • 6.
    Haskell on Heroku Celadon Cedar Stack なら Haskell アプリケーションをデプロイ可能!!! Celadon Cedar Stack • Ubuntu 10.04 LTS (lucid) (64bit) • package.json ファイルを置いておくと Node.js アプリケーションとして認識される 12.04 LTS が出た後はどうなるの……? 6 / 19
  • 7.
    Heroku gem • Herokugem をインストール $ gem install heroku --user-install --no-ri --no-rdoc • 公開鍵を登録 $ heroku login Enter your Heroku credentials. Email: user@example.com Password: 7 / 19
  • 8.
    Heroku で Haskellを動かす • Node.js アプリに擬態 $ cat package.json { "name": "yesod-test", "version": "0.0.1", "dependencies": {} } • Procfile を作成 $ cat Procfile web: ./dist/build/yesod-on-heroku/yesod-on-heroku production -p $PORT 8 / 19
  • 9.
    データベースを使う • 環境変数 DATABASE_URL, SHARED_DATABASE_URL を読んで自力で頑張る • heroku package を使う http://hackage.haskell.org/package/heroku deploy/Procfile に詳しいやりかたが 書いてあるので…… 9 / 19
  • 10.
    デプロイ • Cedar Stackにアプリを作成 $ heroku create --stack cedar Creating strong-mist-1328... done, stack is cedar http://strong-mist-1328.herokuapp.com/ | git@heroku.com:strong-mist-1328.gi Git remote heroku added • コンパイル $ cabal configure -fproduction $ cabal build • デプロイ $ git checkout -b deploy $ git add dist/build/yesod-test/yesod-test $ git commit -m "deploy" $ git push heroku deploy:master • $ heroku open 10 / 19
  • 11.
    コンパイルする際注意すること • Ubuntu 10.04LTS (lucid) 64bit で動作するバイ ナリを作成 • VM に lucid を入れてコンパイルするのが楽? (lucid の GHC のバージョンは 6.12.1 …… orz) • GHC はデフォルトでは…… • 可能な限り Haskell パッケージの共有ライブラリ を 利用せず静的にリンク • 実行ファイル自体は動的リンク • libyaml などのライブラリは動的リンク → 共有ライブラリが発見できない 11 / 19
  • 12.
    エラーが起きたときは • ログを確認してみる $heroku logs 2011-11-15T17:38:20+00:00 app[web.1]: ./dist/build/yesod-test/yesod-test: error while loading shared libraries: libyaml-0.so.2: cannot open shared object file: No such file or directory 2011-11-15T17:38:20+00:00 heroku[web.1]: Process exited • リモートのシェル環境で調査 $ heroku run ldd dist/build/yesod-test/yesod-test Running ldd ./dist/build/yesod-test/yesod-test attached to terminal... up, run.37 linux-vdso.so.1 => (0x00007fffb7dff000) libz.so.1 => /lib/libz.so.1 (0x00007fe9963ba000) libyaml-0.so.2 => not found librt.so.1 => /lib/librt.so.1 (0x00007fe9961b1000) libutil.so.1 => /lib/libutil.so.1 (0x00007fe995fae000) libdl.so.2 => /lib/libdl.so.2 (0x00007fe995daa000) libgmp.so.10 => not found libffi.so.5 => not found libm.so.6 => /lib/libm.so.6 (0x00007fe995b26000) libpthread.so.0 => /lib/libpthread.so.0 (0x00007fe995908000) libc.so.6 => /lib/libc.so.6 (0x00007fe995585000) /lib64/ld-linux-x86-64.so.2 (0x00007fe9965d9000) 12 / 19
  • 13.
  • 14.
    解決策: 静的リンク 全てのライブラリを静的にリンク • yesod-test.cabal の ghc-options を書き換え ghc に対して -optl-pthread -optl-static フラグを指定 ... (略) executable yesod-test if flag(devel) Buildable: False if flag(production) cpp-options: -DPRODUCTION ghc-options: -Wall -threaded -O2 -optl-pthread -optl-static else ghc-options: -Wall -threaded -O0 ... (略) 14 / 19
  • 15.
    解決策: 静的リンク 問題がある物のみを静的にリンク • package.conf.d/integer-gmp-*.conf を書き換え • extra-libraries から yaml を消去 • ld-options: ”-Wl,-Bstatic” -lyaml ”-Wl,-Bdynamic” と書き換えてしまう これを $ cat /var/lib/ghc/package.conf.d/yaml-0.5.2.conf name: yaml version: 0.5.2 id: yaml-0.5.2-90d2ec65096da81d098bd329cb16d6bf ... (略) extra-libraries: yaml extra-ghci-libraries: include-dirs: includes: depends: aeson-0.6.0.1-1adff47713ff03277e972cd2e23ec8fc ... (略) hugs-options: cc-options: ld-options: framework-dirs: frameworks: haddock-interfaces: /usr/lib/ghc-doc/haddock/yaml-0.5.2/yaml.haddock haddock-html: /usr/share/doc/libghc-yaml-doc/html/ 15 / 19
  • 16.
    解決策: 静的リンク 問題がある物のみを静的にリンク • package.conf.d/integer-gmp-*.conf を書き換え • extra-libraries から yaml を消去 • ld-options: ”-Wl,-Bstatic” -lyaml ”-Wl,-Bdynamic” と書き換えてしまう こうする $ cat /var/lib/ghc/package.conf.d/yaml-0.5.2.conf name: yaml version: 0.5.2 id: yaml-0.5.2-90d2ec65096da81d098bd329cb16d6bf ... (略) extra-libraries: extra-ghci-libraries: include-dirs: includes: depends: aeson-0.6.0.1-1adff47713ff03277e972cd2e23ec8fc ... (略) hugs-options: cc-options: ld-options: "-Wl,-Bstatic" -lyaml "-Wl,-Bdynamic" framework-dirs: frameworks: haddock-interfaces: /usr/lib/ghc-doc/haddock/yaml-0.5.2/yaml.haddock haddock-html: /usr/share/doc/libghc-yaml-doc/html/ 16 / 19
  • 17.
    解決策: 共有ライブラリを含めてデプロイする •環境変数 LD_LIBRARY_PATH を指定する • コンパイル時に -optl-Wl,-rpath,’$ORIGIN’ 等として RPATH を埋め込む 今回は LD_LIBRARY_PATH を使う方法について解説 17 / 19
  • 18.
    解決策: 共有ライブラリを含めてデプロイする •Heroku 環境に含まれていないライブラリをコ ピーし git の管理下に libyaml, libffi, libgmp などなど • シェルスクリプト run を作成 $ cat run #!/bin/sh LD_LIBRARY_PATH=$PWD/dist/build/yesod-test ./dist/build/yesod-test/yesod-test -p $PORT -e production • Procfile を書き換え $ cat Procfile web: ./run 18 / 19
  • 19.
    まとめ? • Ruby(heroku gem) • Ubuntu 10.04 LTS (64bit) 用にビルド • package.json を置いて Node.js アプリを擬態 • 必要な知識 : Binary Hacks 19 / 19
  • 20.