Rundeck の Kill Job や Timeout でプロセスが生き続ける問題の対処方法
Rundeck の “Kill Job” や timeout がちゃんと動いてないという issue は何個も上がっていて、rundeck/#2911 辺りにまとめられているんですが、ここでは server node で (Execute locally) inline script や script file を実行する場合を扱います。
inline script の実行の仕組み
どうも次の手順で inline script を実行しているみたいです。local script/remote script もスクリプトの内容を取得するロジックが違うだけで、同じパスを通るみたいです。
何故 “Kill Job” や Timeout が機能しないのか
inline script を実行する上で肝になるのが LocalNodeExecutor#executeCommand です。このメソッド内で ExecTaskParameterGeneratorImpl#generate
を実行するわけですが、必ず scriptfile
引数が null
になり、command
として実行権限を付与したファイルのパスが渡されます。scriptfile
が null
で command
が指定されている場合は /bin/sh -c
経由で作成したスクリプトを実行することになります。
そして、Rundeck が “Kill Job” で SIGTERM を送るのは /bin/sh -c
のプロセスだけです。
よって、”Kill Job” が機能しないのは /bin/sh -c
が起動した子プロセスにシグナルが送られないことが原因です。
対処方法
次のような inline script を考えます。
sleep 60
この書き方だと “Kill Job” をしてもプロセスが生き続けます。これは実際に /bin/sh -c
のプロセスに SIGTERM を送ってみるとよくわかります。
% docker run --rm -it centos:centos7 bash
[root@e0d5d4380043 /]# echo sleep 60 > tmp.sh
[root@e0d5d4380043 /]# chmod +x tmp.sh
[root@e0d5d4380043 /]# /bin/sh -c ./tmp.sh &
[1] 14
[root@e0d5d4380043 /]# ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.2 0.1 11788 2908 pts/0 Ss 20:37 0:00 bash
root 14 0.0 0.1 11648 2584 pts/0 S 20:37 0:00 /bin/sh -c ./tmp.sh
root 15 0.0 0.0 4328 672 pts/0 S 20:37 0:00 \_ sleep 60
root 16 0.0 0.1 47456 3436 pts/0 R+ 20:37 0:00 ps auxf
[root@e0d5d4380043 /]# kill 14
[root@e0d5d4380043 /]# ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.1 11788 2908 pts/0 Ss 20:37 0:00 bash
root 15 0.0 0.0 4328 672 pts/0 S 20:37 0:00 sleep 60
root 17 0.0 0.1 47456 3336 pts/0 R+ 20:37 0:00 ps auxf
[1]+ Terminated /bin/sh -c ./tmp.sh
/bin/sh -c ./tmp.sh
が終了しても sleep 60
のプロセスが残っていることがわかると思います。
そこで次のように exec
を使うようにします。
exec sleep 60
試してみます。
[root@e0d5d4380043 /]# echo exec sleep 60 > tmp_with_exec.sh
[root@e0d5d4380043 /]# chmod +x tmp_with_exec.sh
[root@e0d5d4380043 /]# /bin/sh -c ./tmp_with_exec.sh &
[1] 20
[root@e0d5d4380043 /]# ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 11788 2916 pts/0 Ss 20:37 0:00 bash
root 20 0.0 0.0 4328 636 pts/0 S 20:39 0:00 sleep 60
root 21 0.0 0.1 47456 3376 pts/0 R+ 20:39 0:00 ps auxf
[root@e0d5d4380043 /]# kill 20
[1]+ Terminated /bin/sh -c ./tmp_with_exec.sh
[root@e0d5d4380043 /]# ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 11788 2916 pts/0 Ss 20:37 0:00 bash
root 22 0.0 0.1 47456 3340 pts/0 R+ 20:40 0:00 ps auxf
ちゃんと sleep 60
も終了しましたね!
exec
は help exec
にあるように、shell を指定したコマンドで差し替えるコマンドです。
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
Replace the shell with the given command.
Execute COMMAND, replacing this shell with the specified program.
ARGUMENTS become the arguments to COMMAND. If COMMAND is not specified,
any redirections take effect in the current shell.
Options:
-a name pass NAME as the zeroth argument to COMMAND
-c execute COMMAND with an empty environment
-l place a dash in the zeroth argument to COMMAND
If the command cannot be executed, a non-interactive shell exits, unless
the shell option `execfail' is set.
Exit Status:
Returns success unless COMMAND is not found or a redirection error occurs.
これによって Rundeck が /bin/sh
のプロセスに SIGTERM を送ったにも関わらず exec
経由で実行したコマンドのプロセスに SIGTERM が送られることになります。
それでもダメなケース
CentOS の /bin/sh
は bash
のシンボリックリンクなんですが、Debian の /bin/sh
は dash
のシンボリックリンクです。これによって子プロセスの作られ方が変わってきます。
先ほどは CentOS の Docker image を使いましたが、今度は Debian の Docker image を使ってみます。
% docker run --rm -it debian:jessie bash
root@3f63206e332e:/# echo sleep 60 > tmp.sh
root@3f63206e332e:/# chmod +x tmp.sh
root@3f63206e332e:/# /bin/sh -c ./tmp.sh &
[1] 6
root@3f63206e332e:/# ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.3 0.1 20248 3180 pts/0 Ss 20:47 0:00 bash
root 6 0.0 0.0 4340 684 pts/0 S 20:47 0:00 /bin/sh -c ./tmp.sh
root 7 0.0 0.0 4340 752 pts/0 S 20:47 0:00 \_ /bin/sh ./tmp.sh
root 8 0.0 0.0 4240 692 pts/0 S 20:47 0:00 \_ sleep 60
root 9 0.0 0.1 17500 2076 pts/0 R+ 20:47 0:00 ps auxf
CentOS の場合は /bin/sh -c ./tmp.sh
が sleep 60
を起動していましたが、Debian の場合は /bin/sh -c ./tmp.sh
が /bin/sh ./tmp.sh
を起動し、/bin/sh ./tmp.sh
が sleep 60
を起動していますね!
よって、Debian の場合は exec sleep 60
にしても sleep 60
が生きたままになります。
root@3f63206e332e:/# echo exec sleep 60 > tmp_with_exec.sh
root@3f63206e332e:/# chmod +x tmp_with_exec.sh
root@3f63206e332e:/# /bin/sh -c ./tmp_with_exec.sh &
[1] 11
root@3f63206e332e:/# ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 20248 3180 pts/0 Ss 20:47 0:00 bash
root 11 0.0 0.0 4340 768 pts/0 S 20:51 0:00 /bin/sh -c ./tmp_with_ex
root 12 0.0 0.0 4240 668 pts/0 S 20:51 0:00 \_ sleep 60
root 13 0.0 0.1 17500 2088 pts/0 R+ 20:51 0:00 ps auxf
root@3f63206e332e:/# kill 11
root@3f63206e332e:/# ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 20248 3180 pts/0 Ss 20:47 0:00 bash
root 12 0.0 0.0 4240 668 pts/0 S 20:51 0:00 sleep 60
root 14 0.0 0.1 17500 2084 pts/0 R+ 20:51 0:00 ps auxf
[1]+ Terminated /bin/sh -c ./tmp_with_exec.sh
これに対処するには “Invocation String” に exec
を指定します。Step を追加する際に “Script” や “Script file or URL” を指定すると “Advanced” の中で指定することができます。
YAML で Job の内容を書くと次のように scriptInterpreter
に exec
を指定することになります。
- description: ''
executionEnabled: true
id: a81da3db-f450-4825-a615-f44caf2baf31
loglevel: INFO
name: sleep
nodeFilterEditable: false
scheduleEnabled: true
sequence:
commands:
- interpreterArgsQuoted: false
script: exec sleep 600
scriptInterpreter: exec
keepgoing: false
strategy: node-first
uuid: a81da3db-f450-4825-a615-f44caf2baf31
それでも困ること
“Log Output” に “Kill Job” や timeout 後のログが流れないの何とかならないですかね…。Graceful stop したいケースもあるだろうに。
おまけ
IntelliJ IDEA を使った Rundeck のデバッグ方法についてです。普段全然 IntelliJ を使わないし Java も全然読み書きしないので非効率なやり方かもしれないですが、とりあえず以下の手順でデバッグできます。
まず rundeck リポジトリ直下で次のコマンドを実行することでプロジェクトを作成します。
./gradlew idea
次に Import Project で rundeck リポジトリを選択して取り込みます。
あとは Edit Configurations… で Remote を追加して、”Command line arguments for running remote JVM” に書いてある引数を jar ファイルを実行する際に指定し、Debug を実行して接続すればブレークポイントを埋め込んだり色々できます。
./gradlew build -x javadoc -x test
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 \
-Xmx1024m -Xms256m -XX:MaxMetaspaceSize=256m -server \
-jar rundeck-launcher/launcher/build/libs/rundeck-launcher-2.10.7-SNAPSHOT.jar