systemptapでプロセスが所属する各pid名前空間におけるpid番号を取得

systemptapでプロセスが所属する各pid名前空間におけるpid番号を取得する方法のめも。

プロセスのPIDは普通に見れますが、そこで見えているPIDというのは自身が所属しているPID名前空間においてのものです。なので、あるプロセスがunshareなりでpid名前空間を親プロセスと別の場合に、親プロセスの名前空間から見たPIDを知ることはできません。逆に親プロセスからは自身のPID名前空間での子プロセスのPIDを見ることはできますが、子プロセスのPID名前空間におけるPIDはわかりません。ってことで、あるプロセスが所属するPID名前空間でのPIDを全部見ることができるツールsystemtapで書いてみました。

ソースはこちらです。stapのコマンドライン

gist.github.com

まず、dockerコンテナ内でnginxを動かしてみます。

[root@6d02a9c04d45 /]# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 15:13 ?        00:00:00 /bin/bash
root        51     1  0 15:15 ?        00:00:00 nginx: master process nginx
http        52    51  0 15:15 ?        00:00:00 nginx: worker process
root        59     1  0 15:19 ?        00:00:00 ps -ef

このときのホスト側ではプロセスはこう見えています。docker(2768)の下にnginxのプロセスがいますね。

$ pstree -p
systemd(1)-+-agetty(259)
           |-agetty(260)
           |-dbus-daemon(235)
           |-dhcpcd(328)
           |-docker(2768)-+-bash(12494)---nginx(12572)---nginx(12573)
           |              |-{docker}(2769)
           |              |-{docker}(2770)
           |              |-{docker}(2771)
           |              |-{docker}(2772)
           |              |-{docker}(2773)
           |              |-{docker}(2774)
           |              |-{docker}(2775)
           |              |-{docker}(2776)
           |              |-{docker}(2777)
           |              |-{docker}(3019)
           |              |-{docker}(3023)
           |              |-{docker}(3024)
           |              `-{docker}(3025)
           |-sshd(330)-+-sshd(1587)---sshd(1595)---bash(1596)---docker(12432)-+-{docker}(12433)
           |           |                                                      |-{docker}(12434)
           |           |                                                      |-{docker}(12435)
           |           |                                                      |-{docker}(12436)
           |           |                                                      |-{docker}(12437)
           |           |                                                      |-{docker}(12438)
           |           |                                                      |-{docker}(12439)
           |           |                                                      `-{docker}(12532)
           |           |-sshd(1674)---sshd(1676)---bash(1677)---pstree(12949)
           |           `-sshd(12270)---sshd(12272)---bash(12273)
           |-systemd(1589)---(sd-pam)(1590)
           |-systemd-journal(169)
           |-systemd-logind(244)
           |-systemd-timesyn(192)---{sd-resolve}(195)

そして、ホスト側でスクリプトを動かすとこうなって、コンテナ内 -> ホストという感じでpidが表示できました。

masami@nlkb:~$ sudo stap -v -g ./pid.stp 12573
Pass 1: parsed user script and 113 library scripts using 69844virt/36104res/4924shr/31496data kb, in 90usr/10sys/109real ms.
Pass 2: analyzed script: 2 probes, 2 functions, 1 embed, 0 globals using 70636virt/37136res/5180shr/32288data kb, in 10usr/0sys/3real ms.
Pass 3: translated to C into "/tmp/stapfubRbf/stap_6dd962b4096d287515d5450e69c15b96_2149_src.c" using 70636virt/37136res/5180shr/32288data kb, in 0usr/0sys/0real ms.
Pass 4: compiled C into "stap_6dd962b4096d287515d5450e69c15b96_2149.ko" in 1550usr/510sys/2322real ms.
Pass 5: starting run.
pid 52
  pid 12573
Done
Pass 5: run completed in 10usr/80sys/467real ms.

つぎはnsenterでdockerコンテナのpid名前空間に入り、そこでスクリプトを実行してみます。 まずはnsenter。

$ sudo nsenter -t 12494 -p

systemtapスクリプトを実行。

# ./tools/systemtap/bin/stap -v -g ./pid.stp 52                                                                                                                                                  
Pass 1: parsed user script and 113 library scripts using 69840virt/36220res/5048shr/31492data kb, in 100usr/10sys/105real ms.
Pass 2: analyzed script: 2 probes, 2 functions, 1 embed, 0 globals using 70632virt/37252res/5304shr/32284data kb, in 0usr/0sys/3real ms.
Pass 3: translated to C into "/tmp/stapRubZUb/stap_96165e2d709fb6669fa3a3854fe6f030_2146_src.c" using 70632virt/37252res/5304shr/32284data kb, in 0usr/0sys/0real ms.
Pass 4: compiled C into "stap_96165e2d709fb6669fa3a3854fe6f030_2146.ko" in 1540usr/480sys/2312real ms.
Pass 5: starting run.
pid 52
  pid 12573
Done
Pass 5: run completed in 20usr/80sys/496real ms.

nsenterの場合、ファイルシステムは元の名前空間を使ってるので/procが完全に切り替わってません。なので、/procまで完全に切り離した状態でやってないのですが多分動くはず。。。

以下、systemtapのめもをば。 systemtapは実行時にコマンドライン引数を渡すことができます。今回は./pid.stp 52という感じでpidを渡しています。コマンドライン引数をprobeのところで使用するには$1や@1などで使用できます。$や@は型で、$は整数、@は文字列です。

このようにc言語で書いた関数にパラメータを渡しています。

probe begin {
        /* pass pid that is command line argument */
        show_pidtree($1)
        exit()
}

今回作ったスクリプトc言語の関数を組み込んでいて、これも引数を取ることができます。使えるのはstringかlong型です。

function show_pidtree:long(pn:long) %{
%}

このスクリプトだとlong型のpnという引数を受け取ります。 ただ、引数はpnという名前ではアクセスできなくて(undefinedで怒られる)、STAP_ARGというprefixが必要です。 そのため、スクリプトでは下記のようにSTAP_ARG_pnとして引数のpnにアクセスしています。

        int pid_nr = STAP_ARG_pn;

systemtapスクリプトでは多少癖はありますが、c言語を使えるので便利ですね。

マイクロサービスアーキテクチャ

マイクロサービスアーキテクチャ