2015/06/26

DockerでClojureを動かす

Docker containerでClojureのWAFであるLuminusを動かしてみる

事前準備

プロジェクト生成と実行

$ lein new luminus hello-world
$ cd hello-world/
$ lein run
Retrieving org/clojure/clojure/1.7.0-RC2/clojure-1.7.0-RC2.pom from central
Retrieving org/clojure/clojure/1.7.0-RC2/clojure-1.7.0-RC2.jar from central
2015-6-26 13:02:18 +0900 SPC-072.local INFO [hello-world.handler] -
-=[ hello-world started successfullyusing the development profile]=-
2015-06-26 13:02:18.357:INFO:oejs.Server:jetty-7.6.13.v20130916
2015-06-26 13:02:18.439:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:3000

ブラウザで開く

$ open http://localhost:3000

Dockerfile

方針

  • imageはdocker hubのjava8を使う
  • containerにuberjarを含める
    • leiningen runだとcontainer起動後から接続可能になるまで時間がかかる
    • Elastic Beanstalkだと結構致命的
  • CMDexec formじゃなくshell formで書く
    • sh経由で立ち上がるので、ENVでセットした環境変数が受け取れる
    • CMDならdocker run時に簡単に書き換えられる

Dockerfile

    FROM java:8

    ENV TZ JST-9
    ENV JVM_OPTS -server -Xms512m -Xmx512m -Xmn256m

    RUN mkdir -p /home/app
    COPY ./target/hello-worldjar /home/app/

    EXPOSE 3000

    WORKDIR /home/app
    CMD /usr/bin/java $JVM_OPTS -jar hello-world.jar 3000

詳しくはDockerfile referenceを参照。

containerの作成と立ち上げ

uberjar作成

$ lein uberjar
Compiling hello-world.core
Compiling hello-world.handler
Compiling hello-world.layout
Compiling hello-world.middleware
Compiling hello-world.routes.home
Compiling hello-world.session
Created /path/to/project/hello-world/target/hello-world-0.1.0-SNAPSHOT.jar
Created /path/to/project/hello-world/target/hello-world.jar

container作成

$ docker build -t luminus/hello-world .
Sending build context to Docker daemon 56.42 MB
Sending build context to Docker daemon
Step 0 : FROM java:8
 ---> 433801eb0894
Step 1 : ENV TZ JST-9
 ---> Running in ac5801626c85
 ---> 77b3c91b3d78
Removing intermediate container ac5801626c85
Step 2 : ENV JVM_OPTS -server -Xms512m -Xmx512m -Xmn256m
 ---> Running in 5c6a50907c9d
 ---> 9db9e3ecf034
Removing intermediate container 5c6a50907c9d
Step 3 : RUN mkdir -p /home/app
 ---> Running in 4e0e9d8c0b15
 ---> 9af2a4c10750
Removing intermediate container 4e0e9d8c0b15
Step 4 : COPY ./target/hello-world.jar /home/app/
 ---> 4e89615ff644
Removing intermediate container 88d653f5d04f
Step 5 : EXPOSE 3000
 ---> Running in 383fc9ad8ee1
 ---> 0ecb672ec459
Removing intermediate container 383fc9ad8ee1
Step 6 : WORKDIR /home/app
 ---> Running in eb575c33ce63
 ---> 158e8dbc87eb
Removing intermediate container eb575c33ce63
Step 7 : CMD /usr/bin/java $JVM_OPTS -jar hello-world.jar 3000
 ---> Running in 344eac7db8b2
 ---> 132b7b08093a
Removing intermediate container 344eac7db8b2
Successfully built 132b7b08093a

docker run

$ docker run --rm -it -p 3000:3000 luminus/hello-world
2015-Jun-26 13:47:52 +0900 9ae20b5deccf INFO [hello-world.handler] -
-=[ hello-world started successfully]=-
2015-06-26 13:47:52.424:INFO:oejs.Server:jetty-7.x.y-SNAPSHOT
2015-06-26 13:47:52.513:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:3000

-pオプションでcontainer側のTCP/3000をhost側(localhostじゃない)のTCP/3000にマッピングしているので、
boot2dockerコマンドでhost側のIPを調べて接続する

$ open http://$(boot2docker ip):3000

TODO

そのうち以下について書く

  • Docker Compose
  • Docker + ElasticBeanstalk

2015/03/04

Railsアプリをruby-profとqcachegrindでプロファイリング

遅いサイトをなんとかする必要があったので、まずは計測する。

最初に使ったrack-mini-profilerは大して役に立たないので無駄だった。

qcachegrindのインストール

% brew install qcachegrind graphviz

graphvizはqcachegrindでコールグラフの生成に使う

手順

  1. Gemfileにruby-profを追加

    group :profile do
      gem 'ruby-prof'
    end
    
  2. config.ruにprofの設定追加

    if Rails.env.profile?
      use Rack::RubyProf, :path => 'tmp/profile',
        :printers => {
          ::RubyProf::FlatPrinter => 'flat.txt',
          ::RubyProf::GraphPrinter => 'graph.txt',
          ::RubyProf::GraphHtmlPrinter => 'graph.html',
          ::RubyProf::CallStackPrinter => 'call_stack.html',
          ::RubyProf::CallTreePrinter => 'call_grind.txt',
        }
    end
    
  3. RAILS_ENV=profileの設定は適宜developmentあたりをコピーして作成。

  4. サーバを起動して、問題のページにアクセスする。

    ./bundle/bin/spring rails s -p 3001 -e profile
    
  5. tmp/profile/-call_grind.txtが生成されているのでqcachegrindに読み込ませる。

  6. Incl.やSelfの値の大きいところを目安にして、実際に遅い処理の部分を探す

2014/09/14

YAPC::Asia Tokyo 2014に行ってきました #yapcasia

YAPC::Asia Tokyo 2014

YAPC::Asia Tokyo 2014 公式サイト

大分報告が遅くなりました。

諸事情でトークは2,3つくらいしか見れなかったのですが、昨年以前と同様、各地のPerl Mongerと交流できたので満足です。無限コーヒーとワッフルが最高でした!周りに著名Perl Mongerがいるのでお話もできます。コーヒー最高!

PM繋がりで2日目の午後から、「地域.pmミートアップ 2014」というイベントでHokkaido.pmを代表してお話させていただきました。その様子はエンジニアtypeさんで記事になっています。

「『やろう』と言ったらそれはもう『.pm』」~YAPC::Asia Tokyo 2014に見る地方コミュニティの盛り上げ方

最近はSapporo.cljというClojureのイベントも主催しておりますが、Perlに限らず地域コミュニティはやりたい人が「やろう」と周りに宣言することから始まります。やりたい言語、技術があれば、どんどん自分から声をかけて行けばいいんじゃないでしょうか。

次のHokkaido.pm

非常によく「次のHokkaido.pmはいつですか?」と訊かれまして、お待たせてして申し訳ない気持ちでいっぱいでした。子育ても落ち着いてきましたので、10月末〜11月にHokkaido.pm#12を開催したいと思います。便乗帰省を目論んでいる方も暖かく迎え入れますので、是非ご参加ください。

2014/07/11

ClojureでFibonacci(10) Calculation per Request

Express vs Flask vs Go vs Sparkjava
https://medium.com/@tschundeee/express-vs-flask-vs-go-acc0879c2122

Clojureだとどの程度なのか気になったのでベンチとってみたら、
比較用のNodeの4倍程度速度だった。

GolangとSparkjavaはやはり速い……

環境

Hardware: Macbook Air 2011mid Core i7@1.8GHz and 4GB RAM
Software: OSX 10.9.3
Java 1.8.0_05 Java HotSpot(TM) 64-Bit Server VM

ソースコード

github.com/ysasaki/clj-fib-serverにアップしてある

src/fib/handler.clj


(ns fib.handler
  (:gen-class)
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [ring.middleware.params :as mp]
            [org.httpkit.server :as httpkit]))

(defn- ^long fib [^long n]
  (case n
    0 0
    1 1
    (+ (fib (- n 1)) (fib (- n 2)))))

(defroutes app-routes
  (GET "/:number" {{number :number} :params}
       {:status 200 :header {"Content-Type" "text/plain"} :body (str (fib (Integer/parseInt number)))}))

(def app
  (mp/wrap-params app-routes))

(defn -main [port]
  (httpkit/run-server app {:port (Integer. port)}))

ベンチマーク

1回目

Running 30s test @ http://localhost:5000/10
  2 threads and 64 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.84ms   10.50ms 169.24ms   96.29%
    Req/Sec     9.76k     3.31k   14.07k    77.83%
  571981 requests in 30.00s, 51.82MB read
  Socket errors: connect 0, read 25, write 0, timeout 0
Requests/sec:  19065.88
Transfer/sec:      1.73MB

2回目。JVMが頑張っているのか2回目は大体1000req/secくらい速い

% wrk -c 64 -d 30s http://localhost:5000/10
Running 30s test @ http://localhost:5000/10
  2 threads and 64 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.42ms    5.11ms 133.44ms   99.18%
    Req/Sec    10.75k     1.43k   14.22k    85.30%
  605999 requests in 30.00s, 54.90MB read
  Socket errors: connect 0, read 3, write 0, timeout 0
Requests/sec:  20199.93
Transfer/sec:      1.83MB

fibにtype hintsを付けないと1000req/sec減る感じだった

Node

比較用にNodeも元記事のソースコードでベンチマーク

% node -v
v0.10.7

% wrk -c 64 -d 30s http://localhost:3000/10
Running 30s test @ http://localhost:3000/10
  2 threads and 64 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    12.72ms    1.51ms  30.10ms   71.00%
    Req/Sec     2.55k   224.26     3.03k    68.94%
  150699 requests in 30.00s, 31.19MB read
Requests/sec:   5022.71
Transfer/sec:      1.04MB

2014/06/24

Sapporo.clj#0 開催しました

参加者の皆様、@tnoborioさんありがとうございました。
誰にも参加意思を確認せずに開催に踏み切ってしまったので、最悪 @tnoborio
さんと二人きりかと思いましたが、予想外にあつまっていただいて感謝しております。

特に赤字覚悟で札幌まで来ていただいた登尾さん、本当にありがとうございました。
ニャンパスステッカーを寄付していただきましたので、#1以降で配っていきたいと思います。

次回以降ですが、隔月くらいの頻度でもくもく会なんかを開いていきたいところです。
参加者の皆さんにご協力いただくこともあるかと思いますので、よろしくお願いします。


最後に私のLTで使ったスライドのリンクを貼っておきます。

Web開発時におけるClojureとPerlのライブラリ比較です。
Clojureはさっとしか触っていないので、他にベターなもの等あるかと思います。
http://ysasaki.github.io/presentations/sapporoclj0/#/title

2014/06/20

Sapporo.cljを開催します

明日6/21(土)にSapporo.cljを開催します。

はじめてのClojureの著者 @tnobrio さんが来札されるので、 それに合わせての開催となります。

イベント詳細ページ

Sapporo.clj #0 「はじめてのClojure」出版記念
http://atnd.org/events/51603

私自身もClojureの勉強を始めたばかりで、わからないことだらけです。 参加者同士で互いにお役立ち情報をシェアしましょう。 需要があれば、今後もSapporo.cljを時々開いていきたいと思います。

はじめてのClojureについては著者割引があるそうなので、 事前に連絡をすると買えるんじゃないでしょうか。

2014/03/28

CentOS 6.x向けにremiのchef cookbook書いた

気づいたらopscodeのyumからremiが消えていて、何度も書くのが段々めんどうになったので作成

https://github.com/ysasaki/yum-remi

よんどころのない事情によりCentOS 6.x系でphp55が使いたい、そんな時に使ってください。

Berksfileに以下のようなものを追加

cookbook 'yum-remi', git: 'git://github.com/ysasaki/yum-remi.git'

後はrun listにrecipe[yum-remi]を追加して、remi-php55のリポジトリを有効にするだけです。

default['remi']['php55']['enabled'] = 1

remiのrpmをinstallして、/etc/yum.repos.d/remi.repoを上書きしているだけです。