18
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

puma-devがMacOSX上でどう動いているのかコードリーディングして調べてみた

Last updated at Posted at 2018-04-19

はじめに

Railsアプリケーションの開発用Webサーバとして、puma-devが便利ということで何気なく使っていました。しかしその仕組みについてはいまいちブラックボックス感が拭えませんでした。

そこで勉強も兼ねて、実際にpuma-devのコードを読んで仕組みを調べてみました。

調べたテーマ

コードを全て読み切るというのは大変だったので、puma-devについて自分が疑問に思っていたことを2つテーマとして設けて調べました。

  1. puma-devの初期セットアップで何をしているか?
  2. Webブラウザからリクエストを投げたときpuma-devは何をしているか?

前提

環境

puma-devはLinuxでも利用できるかと思いますが、本記事ではMac OS Xの環境で調べています。

  • Mac OS X 10.13.3
  • puma-dev v0.11

コードリーディングの対象リポジトリ

2018/4/19時点で最新のreleaseであるv0.11のpuma-devをコードリーディングの対象としました。

puma-devというくらいなのでrubyかと思いましたが、golangで書かれていました。しかし割とシンプルなCLIツールの構成になっており、読みやすかった印象です。

調べたテーマ1: puma-devの初期セットアップで何をしているか?

まずはpuma-devを利用する前の初期設定で何をしているのかを調べました。

多分puma-devを使うときは、おおよそ下記のようなコマンドを実行するかと思います。

それぞれのコマンドで何をしているかざっくり確認します。

$ puma-dev -install -d test
$ sudo puma-dev -setup
$ cd /path/to/app
$ puma-dev link -n app_name
$ puma-dev -uninstall

コマンド:puma-dev -install -d test

コード該当箇所

  • LaunchAgentsの設定ファイル~/Library/LaunchAgents/io.puma.dev.plistを書き出している。-dで指定したドメイン名testもここで.plistファイルに反映される
  • ~/Library/Logs/puma-dev.logをログファイルのパスに指定している
  • launchctl loadで配置した.plistファイルをサービス起動している

puma-devはMacのLaunchAgentsを使って、プロセスは自動起動する挙動をとるようです。

インストール後、下記のようなコマンドでpuma-devのプロセスが存在することを確認できると思います。

$ ps x | grep puma-dev
1086   ??  S      1:14.41 /usr/local/Cellar/puma-dev/0.11/bin/puma-dev -launchd -dir ~/.puma-dev -d test -timeout 15m0s

コマンド:puma-dev -setup

コード該当箇所

  • etcDirで指定されているディレクトリ/etc/resolverの所有者をpuma-devコマンドを実行しているユーザに変更する
  • /etc/resolver以下にDNS名前解決用のドメイン名(test)のファイルを作成する

resolverを使い、ローカルのMacにpuma-dev用のDNSサーバ設定を追加しているようです。下記のようなコマンドで確認できます。

$ cat /etc/resolver/test
# Generated by puma-dev
nameserver 127.0.0.1
port 9253

コマンド:puma-dev -stop

コード該当箇所

  • pkill -USR1 puma-devを実行し、puma-devプロセスを再起動する

puma-dev -installでLaunchAgentに常時起動プロセスとして登録されているので、プロセスは停止せず再起動する挙動をとるみたいです。

コマンド:puma-dev link -n app_name

コード該当箇所

  • -nオプションで指定したファイル名でシンボリックリンクを作成する
  • シンボリックリンクは~/.puma-dev/app_nameのように作成され、コマンド実行したディレクトリまたは-n app_name以降に設定したディレクトリパスを紐付ける

コードを読む限り、単純なシンボリックリンク作成のみしているようです。

コマンド:puma-dev -uninstall

コード該当箇所

  • launchctl unloadでpuma-devプロセスを停止する
  • ~/Library/LaunchAgents/io.puma.dev.plistを削除する
  • /etc/resolver以下のファイルを削除する

アンインストールということもあり、puma-dev用の設定ファイルの削除まで実施するみたいです。

余談ですが、puma-devプロセスだけを停止したい場合は、下記コマンドを実行すれば良いということになりそうです。
(※puma-devのissueにも書いてありました。)

$ launchctl unload ~/Library/LaunchAgents/io.puma.dev.plist

調べたテーマ2: Webブラウザからリクエストを投げたときpuma-devは何をしているか?

おおまかにpuma-devの設定で何をしているかは確認できたので、次は実際にpuma-devを利用するときの挙動を調べてみました。

概要図

まず、自分がpuma-devのコードやプロセスの挙動を調べて理解した内容を図にしてみました。

image.png

  1. Webブラウザからhttps://XXX.testというURLでリクエスト
  2. /etc/resolver/testで定義されている設定に基づき、puma-devプロセス上のDNSサーバ経由でURLの名前解決をリクエスト
  3. puma-devプロセス上のDNSサーバで、HTTPSならlocalhost:443に、HTTPならlocalhost:80に名前解決
  4. puma-devプロセス上のHTTP/HTTPSサーバから、UNIXドメインソケット通信を使ったpumaプロセスを起動・リクエスト
  5. pumaがリクエストに応じてRailsアプリケーションにリクエストする

こちらを踏まえ、Webブラウザからリクエストが投げられた後の挙動をざっくり見ていこうと思います。

1 ~ 2までの挙動

Webブラウザからhttps://XXX.testというURLでリクエストすると、まずDNSの名前解決を試みます。

puma-dev -install -d testで作成されたファイルをおさらいすると、IPアドレス127.0.0.1:9253が指定されており、こちらに対して名前解決を試みます。

# Generated by puma-dev
nameserver 127.0.0.1
port 9253

ポート9253番はpuma-devのデフォルトのDNSサーバのポート番号として設定されています。

2 ~ 3までの挙動

127.0.0.1:9253 に対するDNSの名前解決のリクエストは、LaunchAgentsによって常時起動しているpuma-devのプロセスに対して実行されます。
改めて、puma-devのプロセスが起動していることを確認します。

$ ps x | grep puma-dev
1086   ??  S      1:14.41 /usr/local/Cellar/puma-dev/0.11/bin/puma-dev -launchd -dir ~/.puma-dev -d test -timeout 15m0s

またpuma-devがLISTENしているポート番号を確認してみます。9253番とhttp/httpsのポートをLISTENしているのがわかります。(http/httpsについては後ほどまとめます)

つまり、ポート9253番への通信があるとpuma-devのプロセスに通信されることになります。
ここから、puma-devプロセスがDNSサーバとしてリクエストを受けることが予想できます。

$ lsof -i | grep puma-dev
puma-dev   1086 gotchane    5u  IPv4 0x7ab086f0e36ca1fd      0t0  TCP localhost:9253 (LISTEN)
puma-dev   1086 gotchane    7u  IPv4 0x7ab086f0cff974d5      0t0  UDP localhost:9253
puma-dev   1086 gotchane    8u  IPv4 0x7ab086f0e36991fd      0t0  TCP *:http (LISTEN)
puma-dev   1086 gotchane   11u  IPv4 0x7ab086f0e36925dd      0t0  TCP *:https (LISTEN)

実際のコードも読んでみると、抜粋した箇所でリクエストをLISTENする処理が書かれているようです。
puma-devはhttp/httpsのポートもLISTENしているので、リクエストはそのままpuma-devのhttp/httpsサーバに移る感じになると思います。

3 ~ 5の挙動

ここではhttp/httpsサーバからRailsアプリケーションがどう起動されるかに絞って調べてみました。
(※puma-devで複数アプリケーション登録した時の切り替え処理などもありましたが、その辺はちょっと複雑だったので割愛します。近々追記予定)

Railsアプリケーションが起動する処理は、下記抜粋箇所に実装されています。

読んでいただくとわかるのですが、こちらのコード内にpumaをUNIXドメインソケット通信で起動するコマンドが定義されています。

下記にコマンドの部分だけ抜粋します。-b unix:%sと書いてあるので、UNIXドメインソケット通信のファイルパスにbindして起動していることがわかります。

const executionShell = `exec bash -c '
cd %s
if test -e ~/.powconfig; then
	source ~/.powconfig
fi
if test -e .env; then
	source .env
fi
if test -e .powrc; then
	source .powrc
fi
if test -e .powenv; then
	source .powenv
fi
if test -e Gemfile && bundle exec puma -V &>/dev/null; then
	exec bundle exec puma -C $CONFIG --tag puma-dev:%s -w $WORKERS -t 0:$THREADS -b unix:%s
fi
exec puma -C $CONFIG --tag puma-dev:%s -w $WORKERS -t 0:$THREADS -b unix:%s'
`

こちらを踏まえると、特にrails sbundle exec pumaなどのコマンドを別途実行する必要はなく、puma-devで実行してくれるということがわかります。

pumaプロセスを確認してみると、UNIXドメインソケット通信のファイルを作ってpumaが起動していることがわかると思います。

$ ps x | grep puma  
 1086   ??  S      1:15.38 /usr/local/Cellar/puma-dev/0.11/bin/puma-dev -launchd -dir ~/.puma-dev -d test -timeout 15m0s
80819   ??  S      5:28.50 puma 3.6.2 (unix:/Users/gotchane/.puma-dev/sample_app/tmp/puma-dev-1086.sock) [puma-dev:sample_app]

ここでようやくRailsアプリへのリクエストまで疎通できるところまで辿ってこれました。

終わりに

だいぶ大まかに調べてきましたが、コードを読みつつ挙動を調べることでだいぶ理解が進んだと思いました。golangの勉強にもなってよかったです。

18
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?