GridEngineでDockerコンテナをジョブ投入することができます。ただしqdelでジョブを削除してもDockerコンテナは我知らずと動き続けてしまいます。ジョブと一緒にコンテナを停止するにはどうしたら良いか。困ったなと彷徨っているとコンテナ内のPID1でシグナルハンドルするrun_wrapper.pyなるスクリプトを発見しました。これで一件落着、としても良いのですが、このラッパースクリプトをコンテナに含めるためにDockerイメージをいちいちビルドするのは面倒です。
ビルドしなくて良い方法を考えてみました。
次のシェル関数でDockerコマンドをラップするとGridEngineのコマンドでコンテナジョブを停止、サスペンド、アンサスペンドできます。
#
# docker run wrapper for OGS/GE
#
# Copyright (c) 2016 Akihiro Matsushima
# Released under the MIT license
# http://opensource.org/licenses/mit-license.php
#
function sigconthandler() {
docker unpause $cid
echo "caught sigcont, container unpaused."
wait
}
function sigusr1handler() {
docker pause $cid
echo "caught sigusr1, container paused."
wait
}
function sigusr2handler() {
if [ `docker inspect --format="{{ .State.Status }}" $cid` == "paused" ]; then
docker unpause $cid
fi
docker stop $cid
echo "caught sigusr2, container stopped."
}
function docker() {
# emulate fairly POSIX sh in zsh
$(type "emulate" >/dev/null 2>&1) && emulate -L sh
local IFS=$' \t\n'
if [ "$1" = "run" ]; then
local DOCKER_RUN_LOCALOPTS=${DOCKER_RUN_OPTS:-'-u `id -u`:`id -g` -v $PWD:$PWD -w $PWD'}
if [ -n "$JOB_ID" ]; then
# define the unique cidfile name
TEMPDIR=/var/tmp/${LOGNAME:-$USER}
CIDFILE="${TEMPDIR}/${JOB_NAME:-SOMEJOB}.o${JOB_ID}.${SGE_TASK_ID:-SOMETASK}_$(date +%Y%m%d%H%M%S%3N).cid"
if [ ! -e "$TEMPDIR" ]; then
mkdir "$TEMPDIR"
fi
/usr/bin/docker run $(eval echo $DOCKER_RUN_LOCALOPTS) -i --cidfile="$CIDFILE" "${@:2:($#-1)}" &
pid=$!
while [[ -d /proc/$pid && -z $cid ]]; do
sleep 1
if [ -s "$CIDFILE" ]; then
read -r cid < "$CIDFILE"
rm -f "$CIDFILE"
fi
done
trap sigconthandler SIGCONT
trap sigusr1handler SIGUSR1
trap sigusr2handler SIGUSR2
wait
else
/usr/bin/docker run $(eval echo $DOCKER_RUN_LOCALOPTS) "${@:2:($#-1)}"
fi
else
/usr/bin/docker "$@"
fi
}
if [[ ! $(readlink /proc/$$/exe) =~ "zsh" ]]; then
export -f sigconthandler sigusr1handler sigusr2handler docker
fi
ただし、このシェル関数だけではジョブは止まりません。qsubオプションに-notify
を指定する必要があります。qsubスクリプトでは次の一行を追記してください。
#$ -notify
-notify
オプションについてはman qsub
に次のように記載されています。
-notify
Available for qsub, qrsh (with command) and qalter only.
This flag, when set causes Grid Engine to send "warning" signals to a running job
prior to sending the signals themselves. If a SIGSTOP is pending, the job will
receive a SIGUSR1 several seconds before the SIGSTOP. If a SIGKILL is pending, the
job will receive a SIGUSR2 several seconds before the SIGKILL. This option pro‐
vides the running job, before receiving the SIGSTOP or SIGKILL, a configured time
interval to do e.g. cleanup operations. The amount of time delay is controlled by
the notify parameter in each queue configuration (see queue_conf(5)).
Note that the Linux operating system "misused" the user signals SIGUSR1 and SIGUSR2
in some early Posix thread implementations. You might not want to use the -notify
option if you are running multi-threaded applications in your jobs under Linux,
particularly on 2.0 or earlier kernels.
Qalter allows changing this option even while the job executes.
Only if this option is used the parameter named notify with the value y will be
passed to defined JSV instances. (see -jsv option above or find more information
concerning JSV in jsv(1))
要は「-notify
指定したときはジョブに停止信号を送る前に別の信号を送るから、その信号を頼りに止まらないプログラムをきちんと止めてね」です。
シェル関数内のDOCKER_RUN_LOCALOPTS
環境変数で指定するデフォルトのdocker run
オプションはお好みで書き換えてください。docker run
のオプションには後ろに書いたオプションが前に書いたオプションを打ち消せるものがあるので、それも念頭にデフォルトを決めるといいと思います。上記シェル関数を例にするとdocker run --rm=false -u 0:0 -w /otherdir
といった具合です。-v
は何個でも書けるので打ち消せないですね。他にも状況によっては都合の悪いオプションはあると思うのでDOCKER_RUN_OPTS
環境変数を定義しておくとそちらを優先するようにしています。
追伸
シェル関数ではコンテナIDを取得するためにCIDファイルを生成してそのファイルからコンテナIDを読み込んでいます。CIDファイルを生成せずともdocker run --detach
すればコンテナIDが返り値となるのに、それを利用せず何ゆえ周りくどい方法を取っているのか。それはdocker run
は--detach
オプションと--rm
オプションを同時に指定するが出来ないからです。ユーザが指定するdocker run
オプションに--rm
が含まれている場合も考慮して--detach
オプションを使わずにコンテナIDを取得しています。