0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

which rubyが失敗するのにUnicornが動く理由を、インタプリタの仕組みから理解する

0
Last updated at Posted at 2026-05-20

はじめに

サーバにログインして作業していたら、こんな場面に出くわしました。

$ which ruby
ruby not found

$ ps aux | grep unicorn
deploy   12345  ... unicorn master -c /var/www/app/config/unicorn.rb
deploy   12346  ... unicorn worker[0] -c /var/www/app/config/unicorn.rb

ruby コマンドは無いのに、Ruby 製のアプリケーションサーバ Unicorn は元気に動いている。

「Ruby が無いのに Ruby のサーバが動く」というのは一見矛盾しています。この謎を入り口に、インタプリタとコンパイラの違いまで掘り下げてみます。

結論:Unicorn が動くなら Ruby は必ず存在する

先に答えを言ってしまうと、Unicorn が動いている時点で、Ruby はそのインスタンス内に必ず存在しています

Unicorn は Ruby 製のアプリケーションサーバです。Ruby インタプリタなしには 1 プロセスも起動できません。

つまり「shell に ruby が無い」のは Ruby が存在しない のではなく、

あなたのシェルの PATH から見えていないだけ

なのです。

なぜ shell から見えないのか

PATH から Ruby が見えなくなる理由は、主に次の 4 つです。

1. バージョンマネージャ(rbenv / rvm / asdf)配下にある

最も多いパターンです。Ruby は次のような場所にインストールされます。

~/.rbenv/versions/3.3.0/bin/ruby
~/.rvm/rubies/ruby-3.3.0/bin/ruby
~/.asdf/installs/ruby/3.3.0/bin/ruby

これらを PATH に通すのは .bashrc / .bash_profile の初期化スクリプトの仕事です。非対話シェル・別ユーザー・sh の直起動などでは shim が PATH に入らず、ruby: command not found になります。

2. Unicorn を起動したユーザー / 環境が違う

Unicorn は deploywww-data など専用ユーザーで動いていることが多く、Ruby はそのユーザーの環境にだけ入っています。あなたのログインシェルとは別環境、というわけです。

3. systemd / init 経由でフルパス指定で起動されている

サービス定義に絶対パスが書かれていれば、PATH に依存せず起動できます。

ExecStart=/home/deploy/.rbenv/shims/bundle exec unicorn -c /var/www/app/config/unicorn.rb

4. コンテナ境界

Unicorn がコンテナ内で動いており、Ruby はそのコンテナイメージにだけ含まれている(ホストのシェルには無い)ケースです。

実際の Ruby を突き止める方法

「見えていないだけ」なら、実体を突き止められます。

# 1. Unicorn プロセスを特定
ps aux | grep -i unicorn

# 2. 動いているバイナリの実体を確認(Linux)
ls -l /proc/<PID>/exe

# 3. そのプロセスの PATH や環境変数を確認
cat /proc/<PID>/environ | tr '\0' '\n' | grep -E 'PATH|GEM|RBENV|RUBY'

# 4. 起動ユーザーの環境で which
sudo -u deploy -i which ruby

ps の出力には rubyunicorn master のコマンドライン全体(多くはフルパス付き)が見えるので、そこからインストール先をたどれます。

補足:プロセス起動後に Ruby バイナリを削除しても、すでに動いている Unicorn は走り続けます(実行中プロセスが inode を掴んでいるため)。ただしこれは稀なケースで、通常は上記 1〜4 の「PATH の問題」と考えてまず間違いありません。

そもそも、なぜ Ruby が「存在し続ける」必要があるのか

ここが今回の本題です。なぜ Ruby 製サーバは、実行中ずっと Ruby を必要とするのか。

答えは「Ruby がインタプリタ型言語だから」です。ここでコンパイラとインタプリタの違いを整理しておきましょう。

コンパイラ(Compiler)

ソースコードを実行前にまとめて機械語(や中間形式)へ変換するプログラムです。

ソースコード ──[コンパイラ]──> 実行ファイル ──> 実行
              (事前に1回)            (何度でも)
  • 変換(ビルド)と実行が別のフェーズに分かれる
  • 一度変換すれば、生成された実行ファイルを何度でもそのまま実行できる
  • 実行時はネイティブコードが直接 CPU で動くため高速
  • 変換時にコード全体を解析するので、型エラーなどを実行前に検出できる
  • ビルドという手間が増え、生成物は基本的に特定の OS / CPU に依存する

例:C, C++, Rust, Go

インタプリタ(Interpreter)

ソースコードを実行時に逐次読み取って、その場で解釈・実行するプログラムです。

ソースコード ──[インタプリタが読みながら実行]──> 結果
              (実行のたびに毎回)
  • 変換と実行が一体。事前のビルド工程がない
  • ソースコードと、それを動かすインタプリタ本体が実行時に必要
  • 1 行ずつ解釈しながら走るため、一般にコンパイラ型より低速
  • すぐ動かせる、対話実行(REPL)ができる、プラットフォーム差をインタプリタが吸収するので移植しやすい
  • 文法エラーやバグが実行して初めて発覚しやすい

例:Ruby, Python, JavaScript, PHP

C 製サーバと Ruby 製サーバの違い

この違いが、冒頭の謎にそのまま効いてきます。

C 製サーバ(例:nginx) Ruby 製サーバ(例:Unicorn)
配布されるもの コンパイル済み実行ファイル Ruby のソースコード(gem)
実行時に必要なもの 実行ファイルのみ ruby インタプリタ一式
コンパイラ 実行時は不要 (そもそもインタプリタ型)

C で書かれたサーバは、コンパイル済み実行ファイルさえあればコンパイラは要りません。一方 Ruby はソースコードを解釈・実行する ruby インタプリタ本体が、インスタンス内に存在し続ける必要がある。だから「Unicorn が動く= Ruby が存在する」が必ず成り立つのです。

まとめ

  • Unicorn(Ruby 製サーバ)が動いているなら、Ruby は必ずそのインスタンスに存在する
  • ruby: command not found は「無い」のではなく「自分のシェルの PATH から見えていない」だけ
  • 原因はたいてい rbenv 等のバージョンマネージャ、起動ユーザーの違い、systemd のフルパス指定、コンテナ境界のいずれか
  • ps / /proc/<PID>/exe / /proc/<PID>/environ で実体は突き止められる
  • Ruby が実行中ずっと必要なのは、インタプリタ型言語だから。C 製サーバのようにビルド済みバイナリだけでは動かない

「Ruby が無いのに Ruby のサーバが動く」という違和感は、インタプリタとコンパイラの違いを理解する良い入り口でした。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?