驚くほど便利なツール:lsof

私は、シンプルで直感的なインターフェースを備えた素敵なコントロールパネルに表示されるログまたは監視インジケータを使用して、コードまたはシステムの問題を探すことに慣れています。 ただし、何らかの理由でコントロールパネルにデータが届かない場合、または一部のサービスのログが利用できない場合、デバッグは複雑になります。 現在、このような問題はほとんどありませんが、まれですが、それでも発生します。 したがって、私たちの時代では、あるコンピューターの特定のプロセスの何が問題なのかを理解するのに役立つツールの知識は非常に貴重です。



画像






ログや監視インジケーターがないものをデバッグするとき、sshを介してリモートコンピューターに接続します。 もちろん、このアプローチは限られているため、それほど単純ではなく、DevOpsのファッショントレンドや、インターネットで読むことができるすべての現代的なものには対応していませんが、驚くほど状況をすばやく分析するのに適しています。



実際、これはプログラムのデバッグ時にprint



コマンドを使用するのと似ています。 ここで私は、私がSREまたはIT運用エンジニアではないことをすぐに明確にしたいと思います。 私の活動の主な分野は開発です。



時々、私が書いたコードをデプロイし、何か問題が発生したときにデバッグする必要があります。 ほとんどの場合、自分自身の新しいシステムに自分自身を見つけるとき 、私にとって最も難しいことは何か見つけることです。 たとえば、プロセスがリッスンしているポートを見つけます。 または、特定のデーモンがログを書き込むファイルを見つけるために、より頻繁に必要なもの。 そして、 ps



pstree



、およびls



の呼び出しを多数使用し、 grep



多くの呼び出しを使用して、これらの質問に対する答えを見つけることができたとしても、多くの場合、「答え」には有用なものが含まれていないか、間違っていることが判明します。



現在読んでいるのがCPythonの主任開発者であるRaymondGöttingerによるプレゼンテーションである場合、「より良い方法が必要です」というフレーズを聴衆が待っているときが来るでしょう。



そして、実際、そのような方法があります。 必要なものをシステムで検索するために常に使用するツールは、 lsof



と呼ばれる素晴らしいツールです。



lsof



ユーティリティ(名前はel-soffに似ていますが、liss-offやel-es-o-effのようなものを好む人もいます)は、開いているすべてのファイル(LiStはすべてOpen Files)のリストを表示する非常に便利なコマンドです。



Unixのようなシステムではすべてがファイルであるため、 lsof



何かを見つけるのに特に適しています。 これは、 ps



netstat



ユーティリティ、およびその他のツールを非常に簡単に置き換えることができる、驚くほど汎用性の高いデバッグツールです。



LSOFオプション



「SRE」という用語が登場する数十年前にこのビジネスに携わってきたSREのベテランは、かつて私にこう語った。「必要なものをすべて認識したらすぐにlsofオプションの勉強をやめました。 最も重要なことを学び、それがあなたが必要とするすべてです。」



lsof



ユーティリティには、広範なオプションセットがあります。



 NAME lsof - list open files SYNOPSIS lsof  [  -?abChKlnNOPRtUvVX  ]  [ -AA ] [ -cc ] [ +cc ] [ +|-dd ] [+|-DD ] [ +|-es ] [ +|-f [cfgGn] ] [ -F [f] ] [ -g [s] ] [ -i [i] ] [-k  k  ]  [  +|-L  [l] ] [ +|-mm ] [ +|-M ] [ -o [o] ] [ -ps ] [ +|-r[t[m<fmt>]] ] [ -s [p:s] ] [ -S [t] ] [ -T [t] ] [ -us ] [ +|-w ] [ -x[fl] ] [ -z [z] ] [ -Z [Z] ] [ -- ] [names]
      
      





あなたはそれらすべてを勉強したい場合- はあなたを助けます。 ここで、私が普段使っているものについてお話したいと思います。



▍-uオプション



-u



オプションは、特定のユーザーが開いたファイルをリストします。 次の例は、 cindy



が開いているファイルの数を確認する方法を示しています。



 cindy@ubuntu:~$ lsof -u cindy | wc -l 248
      
      





通常、パラメータ「^」(カバー)がオプションのパラメータの前に置かれている場合、これは否定を意味し、このパラメータに一致するファイルがプログラムの出力から除外されます。 たとえば、 cindy



を除くすべてのユーザーが開いているコンピューター上のファイルの数を調べる方法を次に示します。



 cindy@ubuntu:~$ lsof -u^cindy | wc -l 38193
      
      





▍オプション-U



-U



オプション-U



、Unixドメイン内のすべてのソケットファイルを-U



します。



 cindy@ubuntu:~$ lsof -U | head -5 COMMAND     PID       USER   FD   TYPE             DEVICE SIZE/OFF    NODE NAME init          1       root    7u  unix 0xffff88086a171f80      0t0   24598 @/com/ubuntu/upstart init          1       root    9u  unix 0xffff88046a22b480      0t0   22701 socket init          1       root   10u  unix 0xffff88086a351180      0t0   39003 @/com/ubuntu/upstart init          1       root   11u  unix 0xffff880469006580      0t0   16510 @/com/ubuntu/upstart
      
      





▍-cオプション



-c



オプション-c



、指定された文字で始まる名前のコマンドを実行するオープンプロセスを保持するファイルに関する情報を-c



。 たとえば、次のコマンドは、コンピューターで実行されているすべてのPythonプロセスによって開かれた最初の15ファイルを表示します。



 cindy@ubuntu:~$ lsof -cpython | head -15 COMMAND     PID USER   FD   TYPE             DEVICE SIZE/OFF       NODE NAME python2.7 16905 root  cwd    DIR                9,1     4096  271589387 /home/cindy/sourcebox python2.7 16905 root  rtd    DIR                9,1     4096       2048 / python2.7 16905 root  txt    REG                9,1  3345416  268757001 /usr/bin/python2.7 python2.7 16905 root  mem    REG                9,1    11152 1610852447 /usr/lib/python2.7/lib-dynload/resource.x86_64-linux-gnu.so python2.7 16905 root  mem    REG                9,1   101240 1610899495 /lib/x86_64-linux-gnu/libresolv-2.19.so python2.7 16905 root  mem    REG                9,1    22952 1610899509 /lib/x86_64-linux-gnu/libnss_dns-2.19.so python2.7 16905 root  mem    REG                9,1    47712 1610899515 /lib/x86_64-linux-gnu/libnss_files-2.19.so python2.7 16905 root  mem    REG                9,1    33448 1610852462 /usr/lib/python2.7/lib-dynload/_multiprocessing.x86_64-linux-gnu.so python2.7 16905 root  mem    REG                9,1    54064 1610852477 /usr/lib/python2.7/lib-dynload/_json.x86_64-linux-gnu.so python2.7 16905 root  mem    REG                9,1    18936 1610619044 /lib/x86_64-linux-gnu/libuuid.so.1.3.0 python2.7 16905 root  mem    REG                9,1    30944 1207967802 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.1 python2.7 16905 root  mem    REG                9,1   136232 1610852472 /usr/lib/python2.7/lib-dynload/_ctypes.x86_64-linux-gnu.so python2.7 16905 root  mem    REG                9,1    77752 1610852454 /usr/lib/python2.7/lib-dynload/parser.x86_64-linux-gnu.so python2.7 16905 root  mem    REG                9,1   387256 1610620979 /lib/x86_64-linux-gnu/libssl.so.1.0.0
      
      





別の興味深い例を示します。 たとえば、Python 2.7プロセスとPython 3.6プロセスが多数あり、Python 2.7プロセスではないプロセスによって開かれているファイルを把握する必要があります。 次の方法で実行できます。



 cindy@ubuntu:~$ lsof -cpython -c^python2.7 | head -10 COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF       NODE NAME python  20017 root  cwd    DIR    9,1     4096       2048 / python  20017 root  rtd    DIR    9,1     4096       2048 / python  20017 root  txt    REG    9,1  3345416  268757001 /usr/bin/python2.7 python  20017 root  mem    REG    9,1    11152 1610852447 /usr/lib/python2.7/lib-dynload/resource.x86_64-linux-gnu.so python  20017 root  mem    REG    9,1     6256  805552236 /usr/lib/python2.7/dist-packages/_psutil_posix.x86_64-linux-gnu.so python  20017 root  mem    REG    9,1    14768  805552237 /usr/lib/python2.7/dist-packages/_psutil_linux.x86_64-linux-gnu.so python  20017 root  mem    REG    9,1    10592  805451779 /usr/lib/python2.7/dist-packages/Crypto/Util/strxor.x86_64-linux-gnu.so python  20017 root  mem    REG    9,1    11176 1744859170 /usr/lib/python2.7/dist-packages/Crypto/Cipher/_ARC4.x86_64-linux-gnu.so python  20017 root  mem    REG    9,1    23560 1744859162 /usr/lib/python2.7/dist-packages/Crypto/Cipher/_Blowfish.x86_64-linux-gnu.so
      
      





▍オプション+ d



+d



オプションを使用すると、特定のディレクトリで開いているフォルダとファイルを見つけることができます(ただし、そのサブディレクトリではありません)。



 cindy@ubuntu:~$ lsof +d /usr/bin | head -4 COMMAND     PID     USER  FD   TYPE DEVICE SIZE/OFF   NODE NAME circusd    1351     root txt    REG    9,1  3345416 268757001 /usr/bin/python2.7 docker     1363     root txt    REG    9,1 19605520 270753792 /usr/bin/docker runsvdir   1597     root txt    REG    9,1    17144 272310314 /usr/bin/runsvdir
      
      





▍-dオプション



おそらく-d



オプションは、私が最も頻繁に使用するオプションの1つです。 -p



オプションのみが失われます。 このオプションを使用すると、出力に含めるか出力から除外する必要があるファイル記述子のリストをコンマで区切って指定できます。 これについてはドキュメントに次のように書かれています:



, «^». , «^». .



, , , - : «0-7» «3-10».



, «^», - «^0-7» 0 7.



, , .



, lsof .








▍-pオプション



lsof



使用するときに-p



オプションを使用しなかった時期を思い出せません。 PIDコマンドの呼び出し時に指定されたプロセスで開かれたすべてのファイルを表示できます。



たとえば、プロセスによって開かれたすべてのファイル(PID 1など)に関する情報を表示するUbuntuの例を次に示します。





Ubuntuで-pオプションを指定して呼び出されたlsofコマンドの出力



これが私のMacBook Airに表示されるものです。





MacBook Airで-pオプションを使用して呼び出されたlsofコマンドの出力



▍オプション-P



-P



オプションは、ネットワークファイルの場合、ポート番号からポート名への変換を抑制します。 ポート名の解決が正しく機能しない場合に使用すると便利です。



このオプションは、別のオプション-n



とともに使用できます。これは、ネットワーク番号のネットワークファイルのホスト名への変換を抑制します。 また、ホスト名を誤って解決する場合にも役立ちます。



上記の両方の変換を抑制すると、 lsof



高速化できる場合あります。



▍オプション-i



-i



オプション-i



、指定されたアドレスに対応するインターネットアドレスを持つファイルに関する情報を-i



ます。 コマンドを呼び出すときにアドレスを指定しない場合、このオプションを使用すると、すべてのインターネットソケットとネットワークファイルに関する情報を表示できます。



たとえば、 lsof



を使用すると、SlackまたはDropboxクライアントによって開かれたTCP接続を確認できます。 楽しみのために、Chromeタブが開いている接続の数を見てみてください。各接続は個別のプロセスです。 Slackが開いた接続を見てみましょう。



 lsof -i -a -u $USER | grep Slack
      
      







Slackによって開かれた接続の一覧表示



Dropboxクライアントによって開かれたTCPソケットについてlsof



使用すると、次のことがわかります。





開かれた接続Dropboxのリスト



Lsof



では、 lsof -iUDP



を使用してUDP接続に関する情報を表示するLsof



できLsof









UDP接続情報の表示



lsof -i 6



コマンドを使用すると、開いているIPv6接続をリストできます。





IPv6接続情報の表示



▍オプション-t



-t



オプションは、プロセスIDを除くすべての情報の出力を抑制します。 PIDリストを他のコマンド(ほとんどがkill-9



にリダイレクトする場合によく使用します。



 cindy@ubuntu:~$ lsof -t /var/log/dummy_svc.log 1235 2171 2188 2189 16758 16761 16762
      
      





オプションの組み合わせ



通常、 lsof



は、論理ORの原則に従って、いくつかのオプションを使用した結果を結合します。 -a



オプションを指定すると、論理Iの規則に従って結果が結合されます。



もちろん、このルールにはいくつかの例外があります。ここでは、通常どおり、ドキュメントを参照することをお勧めしますが、簡単に言えば、次のように機能します。



, , -i , -u foo, , , «foo». :



ID (UID) «^» (), -u;

(PID) «^» (), -p;

(PGID) «^» (), -g;

«^» (), -c;

TCP UDP, -s [p:s].



- , , .



-a . , -a, -U -u foo, UNIX, , «foo».




  1. , , -i , -u foo, , , «foo». :



    ID (UID) «^» (), -u;

    (PID) «^» (), -p;

    (PGID) «^» (), -g;

    «^» (), -c;

    TCP UDP, -s [p:s].



    - , , .



    -a . , -a, -U -u foo, UNIX, , «foo».




  2. , , -i , -u foo, , , «foo». :



    ID (UID) «^» (), -u;

    (PID) «^» (), -p;

    (PGID) «^» (), -g;

    «^» (), -c;

    TCP UDP, -s [p:s].



    - , , .



    -a . , -a, -U -u foo, UNIX, , «foo».




  3. , , -i , -u foo, , , «foo». :



    ID (UID) «^» (), -u;

    (PID) «^» (), -p;

    (PGID) «^» (), -g;

    «^» (), -c;

    TCP UDP, -s [p:s].



    - , , .



    -a . , -a, -U -u foo, UNIX, , «foo».




  4. , , -i , -u foo, , , «foo». :



    ID (UID) «^» (), -u;

    (PID) «^» (), -p;

    (PGID) «^» (), -g;

    «^» (), -c;

    TCP UDP, -s [p:s].



    - , , .



    -a . , -a, -U -u foo, UNIX, , «foo».




  5. , , -i , -u foo, , , «foo». :



    ID (UID) «^» (), -u;

    (PID) «^» (), -p;

    (PGID) «^» (), -g;

    «^» (), -c;

    TCP UDP, -s [p:s].



    - , , .



    -a . , -a, -U -u foo, UNIX, , «foo».




, , -i , -u foo, , , «foo». :



ID (UID) «^» (), -u;

(PID) «^» (), -p;

(PGID) «^» (), -g;

«^» (), -c;

TCP UDP, -s [p:s].



- , , .



-a . , -a, -U -u foo, UNIX, , «foo».








大勝利の物語



ここで少し誇張しているかもしれませんが、「勝利」はそれほど大きくありませんでしたが、何かが起こったとき、議論されることになるので、 lsof



は非常に役に立ちました。



数週間前、テスト環境で新しいサービスのインスタンスを1つ上げる必要がありました。 問題のテストサービスは、稼働中の監視インフラストラクチャに接続されていませんでした。 開始したばかりのプロセスがConsulに登録されなかった理由を見つけようとしました。その結果、他のサービスがそれを検出できませんでした。 「だから、何が問題なのかわかりませんが、ログを見てみます」と思いました。 期待どおりに機能しない場合は、修正しようとしているサービスのログを確認します。ほとんどの場合、ログは問題の根本をすぐに示しています。



問題のサービスは、 サーカスプロセスとソケットマネージャーを使用して起動されました。 circus



下で実行されているプロセスのログは、ホスト上の特別な場所に保存されます-それを/var/log/circusd



と呼びましょう。 ホスト上の新しいサービスは、別の場所にログを書き込む別のマネージャーs6によって開始されました。 次に、 socklog/svlogd,



ログもありますが、これもまた別の場所にあります。 要するに、ログの不足はなく、主な問題は、どのファイル記述子でログが失敗したプロセスを書き込むかを見つけることでした。



私が理解しようとしているプロセスがcircus



下で実行されていることを知っていたので、 /var/log/circusd/whatever_tab_completion_suggested



tail



コマンドで接続すると、このプロセスのstdout



およびstderr



スレッドを見ることができます。 確かに、ログを表示してもまったく何も得られませんでした。 私が間違ったログファイルを読んでいたことがすぐに明らかになり、実際に、詳しく見ると、 /var/log/circusd



stage-svcname-stderr.log



staging-svcname.stderr.log



2つのファイルがある/var/log/circusd



staging-svcname.stderr.log



。 次に、Tabキーを使用してコマンドを自動補完しましたが、自動的に選択されたファイルは必要なものではありませんでした。



興味のあるプロセスがロギングのために実際にどのファイルを使用したかを理解する1つの方法は、 lsof -l filename



を使用することでした。これは、開いているファイル記述子を持つすべてのプロセスに関する情報をlsof -l filename



ます。 実行中のプロセスにログファイルが関連付けられていないことがわかりました。このコマンドはtail



コマンドで調べたため、このファイルは安全に削除できました。



別のファイルを見ると、プロセスがクラッシュした理由がすぐにわかりました(クラッシュ後にcircus



がプロセスを再起動したため、再起動が無限に繰り返されました)。



まとめ



lsof



コマンドを頻繁に使用するほど、より多くのツールが置き換えられ、より便利になります。 lsof



があなたにlsof



利益をもたらすチャンスを持っていることを願っていlsof







親愛なる読者! 特によく知られていないLinuxコマンドラインツールは何ですか?



All Articles