Haskell + OpenShiftでWarp + HaskellのウェブアプリケーションをPaaS上にデプロイするてはじめ
HaskellをサポートしているPaaSは少ないのだけど、OpenShiftが対応しているらしいのでいっちょ頑張ってHello, world.までやってみる。RedHadがやってるので、そんなに早く閉鎖することはないだろう。HaskellをサポートしているPaaSはWeb/Cloud - HaskellWikiによればOpenShiftとHeroku、あとはfluxflexが、あっ…
Fluxflexが6月30日に閉鎖、今後はRackhubに注力
今回参考にするURLは
Functional Programming in the Cloud: How to Run Haskell on OpenShift | Openshift Blog
Haskell - warpではじめる最小ウェブアプリケーション - Qiita
基本ここに沿いながら、次の違いに気をつけてみる
OpenShiftのアカウントを取る
まずは、アカウントを取得する。OpenShiftのトップページからできるよ。
登録を終えるとメールが来るのでVerifyしよう。
Verifyしたら規約に同意して…
できあがり!
RHCをインストールしてコマンドラインから作業できるようにする
Getting Started with OpenShift Online | OpenShift by Red Hatに従ってコマンドラインツールRHCをインストールしよう。
やってみると:
$ sudo gem install rhc assword: Fetching: net-ssh-2.9.1.gem (100%) Successfully installed net-ssh-2.9.1 <省略truncated...> Fetching: rhc-1.28.5.gem (100%) =========================================================================== If this is your first time installing the RHC tools, please run 'rhc setup' =========================================================================== Successfully installed rhc-1.28.5 Parsing documentation for net-ssh-2.9.1 <省略truncated...> Parsing documentation for rhc-1.28.5 Installing ri documentation for rhc-1.28.5 10 gems installed
途中省略してみたが、言われたとおりインストール後にrhc setupを実行してみる。
$ rhc setup OpenShift Client Tools (RHC) Setup Wizard This wizard will help you upload your SSH keys, set your application namespace, and check that other programs like Git are properly installed. If you have your own OpenShift server, you can specify it now. Just hit enter to use the server for OpenShift Online: openshift.redhat.com. Enter the server hostname: |openshift.redhat.com|
ここはエンターでデフォルトを使う。
You can add more servers later using 'rhc server'. Login to openshift.redhat.com: (登録時のメールアドレス) Password: ***************
ここでは登録時のメールアドレスとパスワードを入力する。
OpenShift can create and store a token on disk which allows to you to access the server without using your password. The key is stored in your home directory and should be kept secret. You can delete the key at any time by running 'rhc logout'. Generate a token now? (yes|no) yes Generating an authorization token for this client ... lasts about 1 month Saving configuration to /Users/username/.openshift/express.conf ... done No SSH keys were found. We will generate a pair of keys for you. Created: /Users/username/.ssh/id_rsa.pub Your public SSH key must be uploaded to the OpenShift server to access code. Upload now? (yes|no) yes Since you do not have any keys associated with your OpenShift account, your new key will be uploaded as the 'default' key. Uploading key 'default' ... done Checking for git ... found git version 1.8.5.2 (Apple Git-48) Checking common problems .. done Checking for a domain ... none Applications are grouped into domains - each domain has a unique name (called a namespace) that becomes part of your public application URL. You may create your first domain here or leave it blank and use 'rhc create-domain' later. You will not be able to create an application without completing this step.
基本的には全部yesで構わない。gitのためにSSHキーをアップロードしますとかそういうやつ。懸念がある人はよく読んで対応してみてください。
Please enter a namespace (letters and numbers only) |<none>|: ndk Namespace 'ndk' is already in use. Please choose another. Please enter a namespace (letters and numbers only) |<none>|: euptech Your domain 'euptech' has been successfully created
ここではアプリケーションに共通のドメインをつけることができるらしい。3文字は殆ど取られている模様なので少し長めにつけた。
Checking for applications ... none Run 'rhc create-app' to create your first application. Do-It-Yourself 0.1 rhc create-app <app name> diy-0.1 JBoss Application Server 7 rhc create-app <app name> jbossas-7 JBoss Data Virtualization 6 rhc create-app <app name> jboss-dv-6.0.0 JBoss Enterprise Application Platform 6 rhc create-app <app name> jbosseap-6 Jenkins Server rhc create-app <app name> jenkins-1 Node.js 0.10 rhc create-app <app name> nodejs-0.10 PHP 5.3 rhc create-app <app name> php-5.3 PHP 5.4 rhc create-app <app name> php-5.4 PHP 5.4 with Zend Server 6.1 rhc create-app <app name> zend-6.1 Perl 5.10 rhc create-app <app name> perl-5.10 Python 2.6 rhc create-app <app name> python-2.6 Python 2.7 rhc create-app <app name> python-2.7 Python 3.3 rhc create-app <app name> python-3.3 Ruby 1.8 rhc create-app <app name> ruby-1.8 Ruby 1.9 rhc create-app <app name> ruby-1.9 Ruby 2.0 rhc create-app <app name> ruby-2.0 Tomcat 6 (JBoss EWS 1.0) rhc create-app <app name> jbossews-1.0 Tomcat 7 (JBoss EWS 2.0) rhc create-app <app name> jbossews-2.0 Vert.x 2.1 rhc create-app <app name> jboss-vertx-2.1 You are using 0 of 3 total gears The following gear sizes are available to you: small Your client tools are now configured.
これで完成。なのでHello, world.アプリを動かすところまで行ってみよう。
cartridgeを使ってHaskellのアプリケーションを作る
今回はほんとにプレーンバニラのウェブアプリケーションを作るのでWai + Warpなんだけど、networkのcartridgeだとなんかうまくライブラリが入らないのでWai + WarpのフレームワークであるScottyのcartridgeを使うことにする。Web/Cloud - HaskellWikiからScottyのcartridge manifestを指定して、以下のコマンドでアプリケーションを作る。コードのチェックアウトも同時に行われると思うので、コードをチェックアウトしたい場所で以下を叩こう:
$ rhc app create hello http://www.accursoft.com/cartridges/scotty.yml
作成したアプリケーションはhttps://openshift.redhat.com/app/console/applicationsに表示されるはず。
コードをチェックアウトして書く
おそらくapp create
だけでチェックアウトもされているはず。チェックアウトされていなければ以下のコマンドでcloneできる。
rhc git-clone hello
cabal sandboxをつくる
これはお決まりのsandbox。さっきチェックアウトしたディレクトリの中で:
cd hello cabal sandbox init
コードを書く
以下の様な簡単なWai + Warpアプリケーションを書いてみた。Main.hsという名前で保存しよう。
{-# LANGUAGE OverloadedStrings #-} import Data.String (fromString) import System.Environment (getArgs) import qualified Network.Wai as Wai import qualified Network.Wai.Handler.Warp as Warp import qualified Network.HTTP.Types.Status as Status import qualified Data.ByteString.Lazy.UTF8 as BSLU import qualified Data.List as List helloApp :: Wai.Application helloApp req = do putStrLn . show . Wai.pathInfo $ req putStrLn "Processing request." return $ Wai.responseLBS Status.status200 [("Content-Type", "text/html")] contents contents :: BSLU.ByteString contents = BSLU.fromString $ List.intercalate "\n" [ "<html>" , "<head>" , "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">" , "</head>" , "<body>" , "<h1>Test</h1>This is test page. Running Warp 2 with Haskell." , "</body>" , "</html>" ] main :: IO () main = do (ip, port) <- commandLineOptions let setting = Warp.setPort port . Warp.setHost (fromString ip) $ Warp.defaultSettings putStrLn $ "start server port = " ++ show port Warp.runSettings setting helloApp commandLineOptions :: IO (String, Int) commandLineOptions = do (ip:port:_) <- getArgs return (ip, read port :: Int)
このMain.hsにあわせてcabalファイルを書いてみる。
server.cabalファイルを書く
server.cabalにはbaseやwai, warp, http-types,utf8-stringが必要なはずなので、テンプレートを参考に以下を書く:
name: server version: 0.0 cabal-version: >= 1.2 build-type: Simple executable server other-extensions: OverloadedStrings main-is: Main.hs build-depends: base,http-types,warp,wai,utf8-string
ここで名前がserverになっていることを確認しよう。
ライブラリをインストールする
早速
cabal install --only-dependencies
とやりたいところだけど2014年8月27日現在、古いバージョンのWarpしか動かないようなので、バージョン指定でライブラリをsandboxにインストールする必要があるみたい。面倒だね…。
cabal install http-types-0.8.3 cabal install wai-2.1.0 cabal install warp-2.1.3 cabal install utf8-string
ビルドしてみる
cabal configure cabal build
$ cabal configure Resolving dependencies... Configuring server-0.0... $ cabal build Building server-0.0... Preprocessing executable 'server' for server-0.0... [1 of 1] Compiling Main ( Main.hs, dist/build/server/server-tmp/Main.o ) Linking dist/build/server/server ...
できたようだ。ローカルで実行してみよう。
テストしてみる
Haskell cartridgeに対応したアプリケーションは引数にhostのipとportを指定する約束になっているので、localhostの3000番で走らせるには
cabal run 127.0.0.1 3000
とすればよい。
$ cabal run 127.0.0.1 3000 Preprocessing executable 'server' for server-0.0... start server port = 3000
で実行中。http://localhost:3000にアクセスしてみよう。 もしMacならファイアウォールのダイアログが出るので許可してあげよう。
うまくビルドされていれば以下の様なページにアクセスできるはず:
変更をコミットしてプッシュする
必要なファイルはMain.hsとserver.cabalだけでとりあえず。
git add Main.hs git add server.cabal git commit -m "Initial check-in" git push
するとpush時にcabal buildが走って、ビルドの様子が見られるはず。注意としては他のファイルとかsandboxのファイルとかをアップロードするとビルドできなくなるので、必要なファイルだけをアップロードしよう。
$ git push Counting objects: 7, done. Delta compression using up to 4 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 1.01 KiB | 0 bytes/s, done. Total 4 (delta 1), reused 0 (delta 0) remote: Building git ref 'master', commit 3b19052 remote: Resolving dependencies... remote: Downloading utf8-string-0.3.8... remote: Configuring utf8-string-0.3.8... remote: Building utf8-string-0.3.8... remote: Preprocessing library utf8-string-0.3.8... remote: [1 of 7] Compiling Codec.Binary.UTF8.String ( Codec/Binary/UTF8/String.hs, dist/build/Codec/Binary/UTF8/String.o ) remote: [2 of 7] Compiling Codec.Binary.UTF8.Generic ( Codec/Binary/UTF8/Generic.hs, dist/build/Codec/Binary/UTF8/Generic.o ) remote: [3 of 7] Compiling Data.String.UTF8 ( Data/String/UTF8.hs, dist/build/Data/String/UTF8.o ) remote: [4 of 7] Compiling System.IO.UTF8 ( System/IO/UTF8.hs, dist/build/System/IO/UTF8.o ) remote: [5 of 7] Compiling System.Environment.UTF8 ( System/Environment/UTF8.hs, dist/build/System/Environment/UTF8.o ) remote: [6 of 7] Compiling Data.ByteString.UTF8 ( Data/ByteString/UTF8.hs, dist/build/Data/ByteString/UTF8.o ) remote: [7 of 7] Compiling Data.ByteString.Lazy.UTF8 ( Data/ByteString/Lazy/UTF8.hs, dist/build/Data/ByteString/Lazy/UTF8.o ) remote: In-place registering utf8-string-0.3.8... remote: Creating package registration file: /tmp/pkgConf-utf8-string-0.3337181.8 remote: Installing library in remote: /var/lib/openshift/53fdd0bc5004461e980000cd/app-root/runtime/build-dependencies//lib/utf8-string-0.3.8/ghc-7.6.3 remote: Registering utf8-string-0.3.8... remote: Installed utf8-string-0.3.8 remote: Configuring server-0.0... remote: Building server-0.0... remote: Preprocessing executable 'server' for server-0.0... remote: [1 of 1] Compiling Main ( Main.hs, dist/build/server/server-tmp/Main.o ) remote: Linking dist/build/server/server ... remote: Installing executable(s) in remote: /var/lib/openshift/53fdd0bc5004461e980000cd/app-root/runtime/dependencies/ remote: Installed server-0.0 remote: stripping libraries ... remote: Preparing build for deployment remote: Deployment id is 870f9d0e remote: Activating deployment remote: ------------------------- remote: Git Post-Receive Result: success remote: Activation status: success remote: Deployment completed with status: success To ssh://53fdd0bc5004461e980000cd@<つくったドメイン>.rhcloud.com/~/git/hello.git/ 2ed7af2..3b19052 master -> master
完了したらOpenShiftの自分のページの"Applications"からデプロイしたhelloアプリケーションを選んでウェブにアクセスしてみよう。
やったね!
その他に
IntelliJとのインテグレーションが可能なようだけど、私の環境では"Clouds"の設定項目がIntelliJにないんだよね。。。
Getting Started with OpenShift in IntelliJ IDEA - YouTube Getting Started with OpenShift in IntelliJ IDEA - YouTube
そのうち調べてみます。とりあえずデプロイまで。次はScottyかYesodできちんとURLパラメータ処理できるような例を示しつつ、スペル修正プログラムを組み込んでいってみようと思う。