Skip to content

Instantly share code, notes, and snippets.

@rmanzoku
Last active March 24, 2021 09:50
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rmanzoku/3af0f633dcda33948ae9a57040536e0f to your computer and use it in GitHub Desktop.
Save rmanzoku/3af0f633dcda33948ae9a57040536e0f to your computer and use it in GitHub Desktop.
Puma readme

Puma ドキュメント

2017/05/16 v3.8.2 READMEの翻訳。

Description

Pumaは、シンプルで、速くて、マルチスレッド、そして高い並列性を持ったRuby/RackアプリケーションのHTTP1.1サーバです。 Pumaは、開発とプロダクション環境どちらにも使えます。 ベストなスループットを得るために、スレッド実装のあるRubiniusもしくはJRubyの使用を強く勧めます。

Built For Speed & Concurrency

Pumaはシンプルで、速くて、マルチスレッド、そして高い並列性を持ったRuby WebアプリケーションのためのHTTP1.1サーバです。 Rackをサポースするアプリケーションであれば、Pumaを利用でき、WebrickやMongrelの代用となります。 PumaはRubiuniusのためのgo-to serverとして設計されましたが、JRubyやMRI(CRuby/Matz' Ruby Implementation)でも動作します。 Pumaは、開発とプロダクション環境どちらにも使えます。

具体的には、Pumaは、リクエストをC-optimized Ragel extension(Mongrelから継承した)を使って処理します。 C-optimized Ragel extensionは、高速で、正確なHTTP1.1パースを手軽に行えます。 Pumaは、(コントロール可能な)内部スレッドプールから1つのスレッドを取り出し、あるリクエストを処理します。 つまり、Pumaは、あなたのWebアプリケーションに本当の並列性を提供します。

Rubinius 2.0を使うことで、Pumaは、あなたのCPUのすべてのコアを本当(VMでない?)のスレッドとして利用できます。 つまり、スループットの応じて複数のプロセスを立ち上げる必要がありません。 これは、JRubyでも同様の効果が得られます。

MRIにおいては、同時に1つのスレッドしか利用できないGlobal Interpreter Lock (GIL) があります。 しかし、(Twitterのような外部APIをHTTPで呼ぶような)ブロッキングIOが発生している間、Pumaは、ブロッキングIOを並列に行うことでMRIのスループットを向上させます。 (ThinのようなEventMachineベースのサーバではこの効果が無効になります。効果を得るには特別なライブラリを使う必要があります。) 実際はケースによって異なりますが、Pumaでベストなスループットを得るためには、RubiniusかJRubyのようなスレッドが利用できるRuby実装を使うことを強くオススメします。

Quick Start

PumaをRubyGemsからインストールします。

$ gem install puma

pumaコマンドがPATHに通っていれば、ルートフォルダで以下のようにしてRackアプリケーションを動かすことが出来ます。

$ puma app.ru

Plugins

Puma3.0から、プラグイン機能をサポートしました。

開発を助ける謹製のプラグインとして、2つのプラグインがあります。

  • tmp_restart
    • tmp/restart.txt をtouchすることでサーバが再起動する
  • heroku
    • Herokuにデフォルト設定でPumaを使ってアップする

プラグインはPumaのコンフィグ(config/puma.rbなど)で、plugin "heroku" のようにplugin "name"で有効にできます。

プラグインは単純なPath指定によって有効にすることができます。例えばherokuプラグインの場合、require "puma/plugin/heroku"と指定するだけです。これは、1つのGemで複数のプラグインを提供できようになっています。

tmp_restartプラグインはPumaに同梱されています。 herokuプラグインを利用する場合、puma-herokuをgemからインストールしてください。

API

現在、プラグインは、startconfigの2つのフックを利用できます。

startは、サーバーの起動時に実行され、プラグインがPumaを補うために他の機能を開始できるようになります。

configは、サーバーが構成されているときに実行され、構成を追加するために使用できるPuma::DSLオブジェクトが渡されます。

Puma::Plugin内の、パブリックメソッドをプラグインは利用することができます。

将来、もっと多くのフックとAPIがプラグインに提供される予定です。

Advanced Setup

WAFごとに起動方法、略

Configuration

Pumaはサーバをコントロールする多くのオプションがあります。 詳しくはpuma -hでhelpをみてください。

Thread Pool

Pumaは設定可能な動的なスレッドプールを利用できます。 -tフラグでプールするスレッドの最小数と最大数を設定できます。

$ puma -t 8:32

Pumaは、現在のトラフィック量にもとづいて、最小数から最大数の間で、スレッドの数を、自動で変更します。 現在のデフォルトは0:16です。 自由に実験するしてください。 しかし、巨大な数の最大スレッド数には気をつけてください。 リソースを使い果たすかもしれません。

Clustered mode

Puma2は、クラスターモードがあります。 リクエストを並列処理するために、スレッドに加えて、プロセスをフォークすることができます。 ワーカーの数は-wフラグで指定できます。

$ puma -t 8:32 -w3

ネイティブスレッドを利用するRuby実装の場合、利用できるコアの数だけ指定するべきです。 ここで、クラスターモードであってもスレッドを利用されます。 -t フラグで指定したスレッドはワーカーごとの数です。 つまり、-w 2 -t 16:16は合計で32スレッドになります。

クラスターモードを利用する場合、ワーカーが立ち上がる前に、アプリケーションをプリロードすることができます。 これは、MRI Ruby 2.0で導入されたCopy on Write機能を利用するために必要です。 --preloadフラグをつけることで有効になります。

# CLI invocation
$ puma -t 8:32 -w 3 --preload

コンフィグファイルを使う場合、preload_app!メソッドを使ってください。 コンフィグファイルは-Cフラグで指定できます。

$ puma -C config/puma.rb
# config/puma.rb
threads 8,32
workers 3
preload_app!

さらにコンフィグファイルで、ワーカー起動時の挙動を記述できます。

# config/puma.rb
on_worker_boot do
  # configuration here
end

ここのコードは、アプリケーション起動前のセットアッププロセスに利用できます。 Puma特有の挙動をアプリケーション内に書く必要がなくなります。 例えば、ワーカー起動したことをログに残したり、statsdに情報を送ったりできます。 フックを追加するために、これを複数回呼び出すことができます。

もし、ActiveRecordを利用する状況でプリロードを使用するなら、コネクションプールをここに記載することをオススメします。

# config/puma.rb
on_worker_boot do
  ActiveSupport.on_load(:active_record) do
    ActiveRecord::Base.establish_connection
  end
end

さらに、ワーカーがフォークされる前に実行されるブロックをコンフィグファイルに記述できます。

# config/puma.rb
before_fork do
  # configuration here
end

ここのコードは、クライアントにフォークするまえに、クリーンアップするのに使うことができます。 Puma特有の挙動を記載することで、アプリケーションに余計なコードを書く必要がなくなります。

もし、ActiveRecordを利用する状況でプリロードを使用するなら、コネクションの切断をここに記載することで、コネクションリークを防ぐことができます。

# config/puma.rb
before_fork do
  ActiveRecord::Base.connection_pool.disconnect!
end

このルールは、フレームワークが自動で起動する外部サービス(Redis, databases, memcache, ...)へのコネクションにも適用すべきです。

このプリロードを使う際、新しいコードはマスタープロセスに反映され、ワーカーにコピーされます。 (つまり、クラスターモードにのみ適用できます。) 一般的に、ワーカーがたびたび死し、すぐに起動する必要がある環境でプリロードを使うべきです。 もし、多くのワーカーが必要ないなら、プリロードを使うべきではありません。

プリロードは、段階的再始動(phased restart)と一緒に使うことは出来ません。 段階的再起動は、ワーカーを1つずつキルし、再起動します。 プリロードはすべてのマスターをノードをワーカーにコピーします。

Error handler for low-level errors

もしPumaがアプリケーション外のエラーにあったとき、lowlevel_errorに定義されるようなメッセージとともにステータス500のエラーを返します。 この挙動をカスタマイズすることができます。 例えば、サードパーティのエラートラッキングサービスにレポートすることができます。 (rollbarの例)

lowlevel_error_handler do |e|
  Rollbar.critical(e)
  [500, {}, ["An error has occurred, and engineers have been informed. Please reload the page. If you continue to have problems, contact support@example.com\n"]]
end

Binding TCP / Sockets

複数のフラグが必要な他のサーバとは違い、PumaはBindingをシンプルなURIで指定できます。

$ puma -b tcp://127.0.0.1:9292

TCPの代わりに(5-10%パフォーマンスが上がる)UNIX Socketを利用したいですか?問題ありません!

$ puma -b unix:///var/run/puma.sock

UNIX Socketのパーミッションを変更したい場合は、umaskを追加します。

$ puma -b 'unix:///var/run/puma.sock?umask=0111'

セキュリティが必要な場合は、SSLソケットを使えます!

$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert'

Control/Status Server

Pumaは、status/controlのアプリケーションをビルドインしています。 以下の例のようにコントロールサーバ起動できます。

$ puma --control tcp://127.0.0.1:9293 --control-token foo

この例では、Pumaはlocalhost:9293を直接リッスンするコントロールサーバを立ち上げます。 更に、コントロールサーバへのすべてのリクエストにはtoken=fooをクエリストリングに付与する必要があります。 簡易的な認証です。 status.rbでどのようなことができるか確認できます。

コントロールサーバはpumactlコマンドを受け付けます。 以下のコマンドPumaを再起動できます。

$ pumactl restart --control-token foo

pumactlのオプションは、pumactl --helpをみてください。

Configuration file

-Cフラグで、コンフィグファイルを指定することができます。

$ puma -C /path/to/config

デフォルトでは、config/puma.rbを参照します。 環境特有のものを設定する場合、-e--environmentフラグを使用してください。 また、RACK_ENV環境変数を使用した場合、デフォルトのファイルの場所はconfig/puma/environment_name.rbになります。

もし、コンフィグファイルを使わないようにしたい場合、ダッシュを-Cフラグの引数に指定してください。

$ puma -C "-"

コンフィグファイルの詳細は、サンプルを見るか、実装を見てください。

Restart

Pumaは、アプリケーションのアップグレードを簡単に実行する仕組みを持っています。 利用可能な場合(MRI、Rubinius、JRuby)、Pumaはホットリスタートが可能です。 これは、unicornやnginxと同様に、サーバのソケットを開きっぱなしで実行できます。 これにより、リクエストを落とすことなく再起動することができます。

この再起動を実施するため、2つのビルドイン機能があります。

  • pumaプロセスにSIGUSR2を送る。
  • ステータスサーバに/restartのリクエストを送る。

現行とものと再起動後のものでコードの共有は行われません。 したがって、手でstop/startするのと変わりません。

もし新規プロセスがロードできなかった場合、プロセスはそのまま終了します。 したがって、プロダクション環境でPumaを使用する場合は、プロセスモニタ(下記参照)の下でPumaを実行する必要があります。

Normal vs Hot vs Phased Restart

ホットリスタートは、サーバソケットを開き放しにすることで、デプロイの間のリクエストを落とすことなく、実施できます。 ただし、ホットリスタートは、新しいコードが完全に展開されていない間にリクエストが全くハングしないことを意味するものではありません もし、ゼロダウンタイムかつゼロハングを実現する場合は、段階的再起動(phased restart)を使うべきです。

pumactl phased-restartを実行したとき、Pumaはワーカーを1つずつキルします。 つまり、少なくとも他のワーカーがリクエストを処理することができ、ゼロハングを実現できます。

しかし、アプリケーションのアップグレードには、データベーススキーマのアップグレードが必要な場合があります。 段階的再起動の場合、デプロイの間、新しいバージョンと古いバージョンが混在する時間ができます。 データベーススキーマのアップグレードは、古いバージョンへの後方互換があるように実施しなければいけません。

多くのデータベースマイグレーションがある場合は、段階的再起動ではなく、通常の再起動もしくはホットリスタートを実行すべきです。 この方法では、デプロイ中にコードを共有することはありません(この場合、preload_appはデプロイメントの迅速化に役立ちます)。

Puma Signals

Pumaクラスタは以下のシグナルに反応します。

  • TTIN
    • ワーカーの数を1増やす
  • TTOU
    • ワーカーの数を1減らす
  • TERM
    • ワーカーにTERMを送ることで、ワーカーは終了する
  • USR2
    • ワーカーを再起動する
  • USR2
    • ワーカーを階段的に再起動する。ローリングリスタート
  • HUP
    • stdout_redirect設定値で定義されたログファイルを再オープンする
  • INT
    • クラスタにCtrl-Cを送るのと同様。クラスタを終了する
  • CHLD

詳細はこちら

Release Directory

共通のディレクトリにシンボルリンクでデプロイする場合(capistranoだと/current)、 Pumaは、段階的再起動時に、設定なしでは変更を読みません。 Pumaのコンフィグファイルにワーキングディレクトリを明治する必要があります。 これは、古いPuma(2.15以前)から変更されました。

# config/puma.rb
directory '/var/www/current'

Cleanup Code

Pumaは、全てのアプリケーションのリソースが使用されているか知ることができません。 したがって、コンフィグファイルのon_restartでフックすることができます。 on_restartに渡されたブロックは、Pumaが再起動する直前に呼び出されます。

このブロック内にグローバルログファイル、redis接続などを閉じるコードを配置して、ファイルディスクリプタが再起動されたプロセスに漏れないようにする必要があります。

そうしないと、ゆっくりとディスクリプタが使い果たされ、サーバーが何度も再起動されると、最終的には不明瞭なクラッシュが発生します。

Platform Constraints

様々なプラットフォームで単一のものを実装することができないため、 Pumaは、異なるプラットフォームで次のような差異が発生します。

  • JRuby、Windows:サーバソケットは再起動時にシームレスではないため、閉じて再オープンする必要があります。 これらのプラットフォームは、Rubyにさらされている新しいプロセスに記述子を渡す方法がありません
  • JRuby、Windows:fork(2)がないためクラスタモードがサポートされていません。
  • Windows:fork(2)がないためデーモンモードがサポートされていません。

pumactl

pumactlはシンプルなCLIツールで、上述したコントロールサーバへ接続します。 詳細はpumactl --helpで確認できます。

Process Monitors

プロセスモニタもしくはスーパーバイザは、システム起動時にPumaの起動などを提供します。 systemdやupstartのようなモダンなプロセスモニタは、継続的な監視と再起動を提供し、プロダクション環境の生産性を向上させることができます。

Capistrano deployment

使わないので略

License

Puma is copyright 2014 Evan Phoenix and contributors. It is licensed under the BSD 3-Clause license. See the included LICENSE file for details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment