2 日目の記事です。

node --inspect ./app.js のようにして DevTools で Node.js アプリをデバッグできました。

手元のはそれで良いんですが、サーバーアプリを他のサーバーで実行している場合、そっちで --inspect 付けても手元からは見えません。それは当然だし本番サーバーでデバッグとか怖いんで避けたいんですが、まあそうじゃなくてもポート転送設定してない開発用仮想環境とかあるじゃないですか。

そんなときは ssh でポート転送しましょう。

サーバー上の server.js もデバッグできる。

ポート転送して DevTools でサーバーで実行しているアプリをデバッグ

コマンドラインの SSH でやります。

-L でローカルのポート 9229 番とリモートサーバーのポート 9229 番を繋げながら SSH 接続し、サーバー上でアプリを起動します。もちろん --inspect を忘れずに。

$ ssh -L 9229:localhost:9229 [email protected]
[[email protected] ~]$ node --inspect /path/to/app.js

これで繋がりました。SSH の接続が残っている間はリモートの 9229 番がローカルの 9229 番へ転送されてきます。

-L local_port:remote_host:remote_port

ポート転送の指定です。順にローカルのポート、サーバーのホスト名、サーバーのポート番号です。

サーバーの情報は外部からみたものではなく内部で認識する情報を指定します。デバッグ用の WebSocket は localhost で起動するようなので、サーバーの IP アドレスじゃなくて localhost のままにしておいてください。

user_name@host

SSH の接続先です。

通常の SSH で見るやつ。[email protected] とか [email protected] とか。~/.ssh/config があるならそこで記述した設定名だけで良いです。

node --inspect /path/to/app.js

デバッグしたいアプリの実行コマンドです。

その他

-L って?

いわゆる「SSH ポートフォワーディング」をするやつです。ローカルマシンのポートをサーバーマシンのポートへ接続します。

man ssh より。

Specifies that connections to the given TCP port or Unix socket on the local (client) host are to be forwarded to the given host and port, or Unix socket, on the remote side.

ローカル(クライアント)ホスト上の与えられた TCP ポートか Unix ソケットへの接続が、リモート側の与えられたホストとポート、あるいは Unix ソケットへ転送されるよう指定します。

なんで “L” なんだろ?

なんか接続失敗のエラーが垂れ流される

延々とこういうのが出てくることがあります。(数字は場合によります。)

channel 3: open failed: connect failed: Connection refused

これは SSH が出してるエラーで、ポート転送を通して接続を確立しようとしたが失敗した、てなときに出ます。転送元のサーバーで誰もそのポートを利用していない可能性があります。

今回の話に限ると、以下の 3 点がありそう。

  • --inspect を付け忘れた
  • 起動するより先に DevTools を開いた、あるいは終了後も開きっぱなし
  • -L オプションのうちホスト名が誤っている

最後、-L オプションに指定するのはサーバーに紐づいた外部から見えるホスト名とは限らない点にご注意ください。サーバー側で Node.js 実行した際のメッセージはたぶんこうです。

$ node --inspect tmp/app/server.js
Debugger listening on ws://127.0.0.1:9229/0565bb7e-ed7c-470b-8556-d534f09afa14
For help, see: https://nodejs.org/en/docs/inspector
Listening at 8000

注目するのは出力の一行目にある ws://127.0.0.1:9229/ の部分です。ここが 127.0.0.1:9229 なので、ローカル側で実行する SSH へ与える指示も -L 9229:127.0.0.1:9229 になります。

サーバー側で 127.0.0.1 === localhost となっている場合は置換しても大丈夫です。

  • ✔ OK: -L 9229:127.0.0.1:9229
  • ✘ NG: -L 9229:example.com:9229

アドレス使用済みエラー

Starting inspector on 127.0.0.1:9229 failed: address already in use

サーバーのどこかでアプリが起動しっぱなしになってる気がします。

$ lsof -i :9229
COMMAND   PID   USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
node    12345 ginpei   22u  IPv4 36991283      0t0  TCP localhost:9229 (LISTEN)

PID を指定して強制終了。

$ kill 12345

ちなみにローカル側のポートが被っててもエラーにならないっぽい?

単発なら直接コマンド実行でも

-t を加えると ssh でシェルを開く代わりに直接コマンド実行させることもできるはできる。

$ ssh -t -L 9229:localhost:9229 [email protected] node --inspect /path/to/app.js

-t を付けなくても動くんだけど、その場合は止めようと思って Ctrl+C を押しても手元の ssh が止まるだけでサーバー上で実行されている Node.js アプリは走ったままになってしまう。忘れずに付けよう。

おしまい

あんまり使う機会ないかも、というかこんなことしなくちゃいけない状況にはならないよう願いたいものではあります。

それはそれとして SSH ポートフォワーディングは便利なので使えるようになると良いですね。DevTools 関係ないけど。自分は IP アドレスで接続制限してるデータベースへの踏み台に使ってます。

参考