RackサーバーのPumaについて調べてみる

f:id:arakaji-yuu:20150803143727p:plain

はじめに

いま開発中のRailsアプリケーションのRackサーバーは最初Unicornを使っていたのですが、諸々の事情でPumaの方を使いたいということになった。 まだリリースもしていないのでやるなら早めに変えちゃおうということでPumaについて調べてみた。

Pumaとは

Pumaとはスピードと並列性を追求したRubyのWebサーバーです。 RubyでWebサーバーを作るときの標準となっているRackに対応したライブラリになっています。

スレッドベースのWebサーバー

Pumaではリクエストの並列処理を実現するためにスレッドを利用しています。 リクエストを処理するためのスレッドを予めスレッドプールに指定した数だけ用意しておきます。リクエストが来るとそのスレッドに処理を任せることでスレッドベースの並列処理を行っています。

Rubyの処理系について

Pumaではスレッド用いるため、Rubyの処理系はRubinusやJRubyを推奨していますが、MRI(Ruby標準の処理系)でも利用することが可能です。

MRIにおけるスレッドの扱い

MRIでのスレッドはGlobal Interpreter Lockという機能によって利用が制限されています。 Global Interpreter Lockとは「同時には一つのスレッドしか動かないようにする」というものです。

たとえばRubiusのようにスレッドによる並列処理をサポートしている処理系では3つのスレッドを立ち上げて処理を行った場合、同時に処理をおこないます。 ただしMRIはGlobal Interpreter Lockのよって3つのスレッドを立ち上げて処理を行っても一度に一つのスレッドしか処理をおこないません。1つのスレッドが処理をおこなっている最中にBlocking IO(ファイルやの書き込みやTwitterAPIを叩くなど)によって待ち時間が発生したタイミングで別スレッドに切り替えて処理を進めます。

使い方

起動

Pumaを起動する場合、以下のコマンドを実行します。

$ bundle exec puma

railsの場合、以下のコマンドでも実行可能です。

$ bundle exec rails s Puma

もしサーバーをデーモンとして実行したい場合は-dオプションを付けましょう

$ bundle exec puma -d

設定ファイルを指定する場合は-Cオプションのあとに設定ファイルへのパスを指定します。

$ bundle exec puma -C config/puma.rb

停止

Pumaを停止するにはプロセスに対してQUITシグナルを送ります。 rails s Pumaで起動した場合のデフォルトのpidファイルはtmp/pids/server.pidにありますので、そこに記載されているプロセスIDに対してシグナルを送信します

$ kill -QUIT `(cat tmp/pids/server.pid)`

設定ファイルconfig/puma.rbを指定して起動した場合、その設定ファイルから*.pidファイルを探せるのでpumactlというコマンドを使って簡単に停止することもできます。

$ bundle exec pumactl halt

再起動

Pumaの再起動はhot restartに対応しています。 hot restartとはnginxやunicornと同様にサーバーのソケットを開いたままリスタートすることを可能にします。それにより、ユーザーからのリクエストを待たせる事なくサーバーを再起動して新しいバージョンのアプリケーションに切り替える事ができます。

hot restartするにはpumaのプロセスに対してSIGUSR2シグナルを送信します。

$ kill -SIGUSR2 `(cat tmp/pids/server.pid)`

設定ファイルを指定した実行した場合、pumactlで再起動をかけることも出来ます。

$ bundle exec pumactl restart

設定ファイル

puma起動時のオプションの指定である程度カスタマイズできるがやはり設定ファイルに書いて起動する方が管理しやすいです。 pumaの設定ファイルでなにをどう設定できるかまとめておきます。

ちなみに設定ファイルのサンプルはpumaのgithubレポジトリpuma/examples/config.rbから手に入ります。

environment

pumaをどの環境で動作させるかを指定します。デフォルトは'development'になっています。 railsで使う場合は環境変数RAILS_ENVを直接environmentに指定するのが良いと想います。

# Set the environment in which the rack's app will run. The value must be a string
#
# The default is "development".
#
# environment 'production'
environment ENV['RAILS_ENV'] # Railsで使う場合

daemonize

rackサーバーをデーモンにして起動するかどうかを設定します。

# Daemonize the server into the background. Highly suggest that
# this be combined with "pidfile" and "stdout_redirect".
#
# The default is "false".
#
# daemonize
# daemonize false
daemonize true

pidfile

pidファイルを配置するパスを指定します。 Railsの場合、tmp/pids/puma.pidに配置するほうが良いと思います。

# Store the pid of the server in the file at "path".
#
# pidfile '/u/apps/lolcat/tmp/pids/puma.pid'
pidfile "#{Dir.pwd}/tmp/pids/puma.pid"

state_path

サーバー情報を記載したstateファイルを配置するパスを指定します。 stateファイルはpumactlコマンドでサーバーを操作するのに使用します。 railsの場合、特別このようなファイルを配置するディレクトリはないので今回はtmp/pids/以下に配置しておきます。

# Use "path" as the file to store the server info state. This is
# used by "pumactl" to query and control the server.
#
# state_path '/u/apps/lolcat/tmp/pids/puma.state'
state_path "#{Dir.pwd}/tmp/pids/puma.state"

Cluster mode

クラスタモードとは複数のワーカープロセスを起動し、そのプロセスそれぞれでスレッドプールを持ちリクエストを処理する仕組みです。 ワーカープロセスの数は以下のように指定します。 Workerの数をマシンが持つCore数を超えないように気をつけて下さい。

# === Cluster mode ===
# How many worker processes to run.
#
# The default is "0".
#
workers 2

Thread Pool

Pumaはスレッドによってリクエストを処理します。スレッドをスレッドプールに貯めておく数の下限から上限を指定することが出来ます。

# Configure "min" to be the minimum number of threads to use to answer
# requests and "max" the maximum.
#
# The default is "0, 16".
#
# threads 0, 16
threads 16, 16

bind

サーバーをどのように接続するかをURIで指定できます。 シンプルにTCPで接続する場合tcp://0.0.0.0:80、またWebサーバーの前段にnginxをなどを置き、そこからUNIX Socket経由で接続する場合はunix:///var/run/puma.sockのように指定します。

# Bind the server to "url". "tcp://", "unix://" and "ssl://" are the only
# accepted protocols.
#
# The default is "tcp://0.0.0.0:9292".
#
# bind 'tcp://0.0.0.0:9292'
# bind 'unix:///var/run/puma.sock'
# bind 'unix:///var/run/puma.sock?umask=0111'
# bind 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert'
bind 'unix:///var/share/sockets/puma.sock'

その他、なにが設定可能か調べるには

上記で指定したこと以外にも設定可能な項目はあります。 それを調べるには設定ファイルのサンプルか、設定ファイルのDSLが定義されたファイルを読んで探してみると大体何ができるか把握出来ます。

参考URL

https://github.com/puma/puma

slimからJSの世界にデータをjsonで渡す

いまWebのフロントエンド開発において、JSで一部のHTMLを動的に構築することが増えてきた。 JSでデータからHTMLを構築する場合、そのデータの部分はやっぱりjsonの方が扱いやすい。

自分はRailsのテンプレートエンジンにslimを使っているので、slimでJSの世界にjsonを渡す方法を考えたので書いておく。

htmlのdata-attributeにjsonを渡す

こんな感じ。

div.data-getter data-getter=@value.to_json

javascript:
  var value = JSON.parse($('.data-getter').attr('data-getter'));

jsにデータ受け取り用のglobal変数を作って、そこに渡す

こんな感じ。

javascript:
  DataGetter = {};
  DataGetter.value = #{raw @value.to_json}

Roswellで使ってみる

色んなプログラミング言語を遊びで触ることが多いのですが、Common Lispもやっています。

日本でCommon Lispとういうと八発白中というブログですが、そこで紹介されていたRoswellというプロダクトを使ってみました。

インストール

Mac OSならbrewでインストールできるらしい。

$ brew tap snmts/roswell
$ brew install roswell

処理系をインストール

ruby でいうrbenvのように処理系をバージョン指定してインストール可能らしい。 とりあえずいまはバージョンどうでもいいので処理系のみ指定してインストールする。

$ ros install sbcl

Repl起動

ros run コマンドでreplが起動出来る。

$ ros run 
* (+ 1 2 3)

6

雑感

CommonLispを始めるときに処理系をインストールするのは結構面倒だったので、こういうツールはありがたいです。 (まあ、いうほどLisp書いてないけど・・

windows7でvagrant ssh コマンドが使えるようにする

はじめに

windows7でvagrantを使うの続きの記事です。

もしvagrantのインストールがまだであればwindows7でvagrantを使うを試してみて下さい。

vagrant sshとは

vagrant sshとは、vagrantで立ち上げた仮想環境にsshで簡単にログインできるコマンドです。しかしWindowsではデフォルトでsshクライアントが入っていないため、このコマンドが使用出来ません。

このままでは辛いので、どうにかしてwindowsでもvagrant sshが使えるようにします。

mingw-getをインストールする

Windows上でGNU/Linuxコマンドラインツールを使えるようにしたMinGWというものがあります。 mingw-getとはMinGWのパッケージ管理ツールのようなもので、これを使ってsshをインストールして使えるようにします。

mingw-getはここからmingw-get-setup.exeをダウンロードして、そのファイルを実行するとインストールが開始します。

MinGW   Minimalist GNU for Windows   Browse  Installer at SourceForge.net.png

インストールの手順は、ここも説明に従って行えば問題ないです。

インストールが完了するとMinGW Install Managerが起動されます。

スクリーンショット 2015-06-02 18.03.41.png

MinGW用にPathを通す

MinGWでインストールされるコマンドなども環境変数Pathを通さないと実行できません。 なにも変更せずにインストールしていればC:\MinGW\binにコマンドをインストールされるのでこのパスをユーザー環境変数Pathに設定してください。

MinGWを使って基本ソフトウェアをインストールする

MinGWを使ったインストール方法はこのブログの詳しく乗っているので、このブログの「パッケージのインストール:GUI編」を参考にしてください。

MinGWsshクライアントをインストールする

MinGW Install Managerを開いて右側にあるMSYSをクリックするとMSYSのパッケージがリストで表示されます。その中にmsys-opensshとmsys-opensslというパッケージがあるのでそれらにチェックをいれます。

その後、左上メニューのInstallation->Apply Changesをクリックするとインストールが開始されます。

注意点

MSYSのパッケージはデフォルトだとC:\MinGW\msys\1.0\binにインストールされます。ここにもPathを通さないとコマンドが実行できないので、ユーザー環境変数PathにC:\MinGW\msys\1.0\binを追記してください。

vagrant sshを試す

sshのインストールが完了しましたら、コマンドプロンプトを立ち上げ、Vagrantiflleがあるディレクトリに移動します。 そこでvagrant upでvagrantを起動し、vagrant sshコマンドを実行します。

そこでエラーが発生せずに仮想環境の中にログインできたら成功です。

スクリーンショット 2015-06-02 18.44.17.png

さいごに

vagrant ではrsyncコマンドも使うのでsshと同じ要領でrsyncコマンドをインストールしておいた方がいいです。 ちなみにsshと同じようにMSYSにパッケージが入っています。

参考URL

windows7でvagrantを使う

はじめに

現在行っているプロジェクトで最初は自分一人エンジニアで開発を行っており開発環境にVagrantを利用していたのですが、この度マークアップと連携する段階に入り、いままで自分のMacvagrantで開発環境を作って開発してきたので、その環境をマークアップwindowsで再現するためにwindowsvagrantを使う方法を調査したのでまとめおきたいと思います。

vagrantとは

vagrantとは仮想環境の起動、Stop、削除をコマンドラインで簡単に操作でき、かつその仮想環境の設定(os、ネットワーク、メモリ割り当てなど)をVagrantfileという一つファイルで行なう事が出来るツールです。

このVagrantfileを共有することが簡単に仮想環境を構築できること、他の人と同じ開発環境を簡単に再現できることが魅力で使われているツールです。

vagrantについての説明はよりわかりやすい説明がされているブログがあるので、ここでの説明は省略します。

インストール手順

vagrant インストール

まず公式サイトからダウロードします。Windowsを選択して、ダウロードして下さい。

インストールしたファイルをダブルクリックして、あとは説明に従えば上手く行きます。

VirtualBoxをインストール

vagrantで利用する仮想環境を作るためにはなんらかの仮想化ツールが必要です。ここでは自分も使っているVirtualBoxを利用するのでインストールします。

公式サイトのダウロードページにある「VirtualBox 4.3.28 for Windows hosts -> x86/amd64」を選択してダウロードして下さい。

スクリーンショット 2015 06 02 15.46.05.png

ダウンロードしたファイルをクリックしたらインストールが始まります。ここも説明どおり進めていけば問題ありません。

VirtualBoxのパスをユーザー環境変数Pathに設定する

vagrantVirtualBoxを使用するためにユーザー環境変数PathにVirtualBoxのパスを設定する必要があります。 特別に変更せずに進めていれば、C:\Program Files\Oracle\VirtualBoxにインストールされているはずなので確認して下さい。

VirtualBoxをインストールしたパスが確認できれば、それを環境変数に設定します。

環境変数の設定するにはWindowボタン->Computer->System properties -> Change settingsとを辿ると以下の設定画面を表示させます。(画像は英語になっているので日本語に読みかえて下さい。)

スクリーンショット 2015-06-02 16.29.28.png

画面の下にある「Environment Variables」ボタンをクリックすると環境変数設定画面に入ります。

環境変数の設定画面には上部の「ユーザー環境変数(User variables for ユーザー名)」と下部の「システム環境変数(System variables)」があります。

ユーザー環境変数のPathに先ほど確認したVirtualBoxのPathを追加したのですが、もしすでにPathがあればすでに書かれている値の後に追記してください。 もしなければNewボタンをクリックして、新規に作成します。

スクリーンショット 2015-06-02 16.32.16.png

vagrantで仮想環境を立ち上げる

インストール&設定が上手くいったか確認するため、vagrantを使ってみる。

好きなディレクトリに移動して、以下のコマンドを実行してください。

$ vagrant init hashicorp/precise32

成功するとVagrantfileが作成されます。 そのVagrantfileで定義された仮想環境を立ち上げるには以下のコマンドを実行します。

$ vagrant up

上手くいけば指定された仮想環境のboxのインストールから立ち上げまで行ってくれます。

注意点

もしvagrant upしたときにエラーが発生し、「VirtualBoxがインストールされていない」というエラーメッセージが表示された場合まだ環境変数Pathがコマンドプロンプトに反映されていないのが原因かもしれません。 一度コマンドプロンプトを再起動させると上手くいくかもしれないので試してみて下さい。(自分はWindows自体を再起動したけど。。)

課題

vagrantで立ち上げた仮想環境にsshで簡単にログインできるコマンドでvagrant sshというのがありますが、Windowsだとデフォルトでsshクライアントが入っておらず使えない。。

このvagrant sshを使えるようにするのは別記事にまとめましたのでそちらを参考にして下さい。

生のHTMLを書くことが辛くなってきた。

自分はWebアプリケーションエンジニアで、 1年程前まではサーバーサイドをPHPで書いていたのでその延長でHTMLを書くことには特に何も思うことはありませんでした。

ただこの一年ぐらいはサーバーサイドにRubyonRailsを採用してSlimというテンプレートエンジンを使っています。 SlimというのはHTMLをもっと簡易的にかけるツールで、例えば以下のようなHTMLを

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Sample File</title>
  </head>
  <body class="sample">
    <div id="contents">
      <h1>Sample File</h1>
      <img alt="yterajima" src="http://www.e2esound.com/images/yterajima.jpg" />
      <p>テキストテキストテキスト</p>
      <p>テキストテキストテキスト
        テキストテキストテキスト</p>
    </div>
  </body>
</html>

以下のように簡易記法でかけます。

doctype html
html
  head
    meta charset="utf-8"
    title Sample File
  body.sample
    #contents
      h1 Sample File

      img src="http://www.e2esound.com/images/yterajima.jpg" alt="yterajima"

      p テキストテキストテキスト

      p
        | テキストテキストテキスト
          テキストテキストテキスト

もし興味があれば、slimの詳しい説明は公式サイト速習テンプレートSlim(HTML作成編)を見ていただけるとよくわかるので参考にしてみてください。

一度Slimのようなテンプレートエンジンを使うと、書くコードが格段に減りますし、コードが減るので効率、可読性、メンテナンス性も格段に上がります。 その恩恵を受けると、生でHTMLが辛くなってくるんですよねーー。

フロントエンド開発にRailsアプリでもnodeのエコシステムを使うという選択

はじめに

RailsではデファルトでAsset Pipelineという機能により、SassやaltJs(CoffeeScriptやTypeScript)のコンパイルやファイルの結合、圧縮を行ってくれる機能があります。 なので通常はバックエンドをRailsで開発する場合はフロントエンドもRailsに沿って開発を行います。

最近自分がメイン開発者(というかほぼ一人?)として新Webサービス開発プロジェクトが始まり、バックエンドのフレームワークRailsを使うことにしたのですが、様々な検討の結果フロントエンドはRailsのエコシステムではなく、nodeのエコシステムを使うことを選択しました。

振り返ったときにこの選択が正解だったのかどうかを確認するためにも、ここでなぜこの選択をおこなったのかをまとめておきたいと思います。

依存性の管理にnpmを使いたい

フロントエンド開発を生産的に行うに、当然様々なライブラリを扱います。

そのライブラリを管理するのに一番ベタな方法はライブラリのソースをダウンロードしてライブラリ用ディレクトリに設置することです。

そこから少し進化したのが、Bowerというパッケージ管理ツールのbower.jsonにアプリが依存しているライブラリを明記してインストールするようになりました。

ただ最近ではnodeが提供している標準のパッケージ管理システム npmを、そのままフロントエンドJavaScriptの依存性管理にも使えるようにするbrowserifyというツールも使用されるようになっています。 このbrowserifyが優れもので、サーバサイドのnodeでライブラリを使用するときに行うrequire('module名')をフロントエンドJavaScriptの世界でも使えるようにしてくれます。

var react = require('react')

あくまで私の観測範囲ですが、フロントエンドに関する記事やライブラリのREADMEなどを見ているとフロントエンドJavaScriptの世界での依存管理でもnpmを使うことが前提となっていく感じがしていて、それが出来ないがために何かを行うときに無駄にハマってしまう予感がしています。

他チーム、他プロジェクトへノウハウの共有がしやすい

フロントエンド開発は高度化、複雑化しており、上記にあげた依存性の管理の他にもSassやAltJSのコンパイルや圧縮、結合、ライブラリの選定や設計など様々な問題を解決しなければなりません。 これらをRailsエコシステムにのった方法で解決しても、その知識は他のフレームワークを利用したプロジェクトやチームと共有できません。

うちの会社で行われる開発でRailsを選択するのは私がメンバーに入るときぐらいで、他の場合はCakePHPやほとんどバックエンドが必要ないWebサイトの制作が大多数を占めています。

自分が学んだ知識を他のチームとお互いに共有したり、また他のチームの開発に入った時に自分がスムーズにヘルプできるようにし、会社全体でのフロントエンド開発の生産性を高めるためにもフロントエンド開発ではnodeのエコシステムに乗るべきだと判断しました。

おわりに

今回のプロジェクトではサービスの企画立案から、開発に関わる技術選択、実際の開発まですべてに関わることができるのですが、あらためて「どの技術を選択するか」を考えることの難しさと面白さを感じました。

良い技術選択を行えば、開発者のモチベーションアップや生産性アップにもつながれば、その逆に陥ることもあります。

今回意識したのは「1年後の当たり前になっているか」ということで、1年後はいま以上にフロントエンド開発のnode前提は加速すると予測し、この選択を行いました。

この選択によって、このブログにフロントエンド界隈のポストが増えてくると思います^^*