chroot内のすべてのプロセスを停止するにはどうすればよいですか?


16

UbuntuがインストールされたLVMパーティションがいくつかあります。時々、apt-get dist-upgradeインストールを最新のパッケージに更新するために、したいことがあります。私はこれをchrootで行います-プロセスは通常次のようなものです:

$ sudo mount /dev/local/chroot-0 /mnt/chroot-0
$ sudo chroot /mnt/chroot-0 sh -c 'apt-get update && apt-get dist-upgrade'
$ sudo umount /mnt/chroot-0

[:私はまた、マウントとアンマウント示されていない/mnt/chroot-0/{dev,sys,proc}リアルにバインドマウントとして/dev/sysおよび/procdist-upgradeのは、これらが存在することを期待しているようだとして、]

ただし、正確にアップグレードすると、このプロセスは機能しなくなり/mnt/chroot-0ます。ファイルシステム上にまだ開いているファイルがあるため、最終的なumountは失敗します。lsofchrootにファイルが開いているプロセスがあることを確認します。これらのプロセスはdist-upgrade中に開始されました。これはservice postgresql restart、パッケージのアップグレード後にchroot内の特定のサービスを再起動する必要があるためです(例:を介して)。

したがって、このchroot内で実行されているすべてのサービスを停止するようにupstartに指示する必要があると考えています。これを確実に行う方法はありますか?

私はもう試した:

cat <<EOF | sudo chroot /mnt/chroot-0 /bin/sh
# stop 'initctl' services 
initctl list | awk '/start\/running/ {print \$1}' | xargs -n1 -r initctl stop
EOF

どこinitctl list が正しいことをしているようで、この特定のルートで開始されたプロセスのみをリストします。Tuminoidが示唆するように、私もこれを追加しようとしました。

cat <<EOF | sudo chroot /mnt/chroot-0 /bin/sh
# stop 'service' services
service --status-all 2>/dev/null |
    awk '/^ \[ \+ \]/ { print \$4}' |
    while read s; do service \$s stop; done
EOF

ただし、これらはすべてをキャッチするわけではありません。デーモン化され、PID 1にリペアレント化されたプロセスは停止しません。私も試しました:

sudo chroot /mnt/chroot-0 telinit 0

ただし、この場合、init 個別のルートを区別せず、マシン全体をシャットダウンします。

だから、ファイルシステムを安全にアンマウントできるように、特定のchrootのすべてのプロセスを停止するようにinitに指示する方法はありますか?upstartには、chroot内のすべての子プロセスをSIGTERM / SIGKILLする機能がありますか?


これはあなたの実際の質問に対する答えではありませんが、役に立つかもしれません:lxcパッケージを見ることをお勧めします。lxcは、コンテナ内のインスタンスを起動してクリーンにシャットダウンするための簡単なツールを提供します。
イオン

回答:


16

ここでは健全な状態を維持するためにカーネル以外は何も信用していないため、initを使用してこのジョブを実行したり、マウントされているかどうかを実際に知っている(一部のパッケージ) binfmt_miscなどの追加のファイルシステムをマウントできます。だから、プロセス虐殺のために、私は使用します:

PREFIX=/mnt/chroot-0
FOUND=0

for ROOT in /proc/*/root; do
    LINK=$(readlink $ROOT)
    if [ "x$LINK" != "x" ]; then
        if [ "x${LINK:0:${#PREFIX}}" = "x$PREFIX" ]; then
            # this process is in the chroot...
            PID=$(basename $(dirname "$ROOT"))
            kill -9 "$PID"
            FOUND=1
        fi
    fi
done

if [ "x$FOUND" = "x1" ]; then
    # repeat the above, the script I'm cargo-culting this from just re-execs itself
fi

そして、chrootをアンマウントするために、私は使用します:

PREFIX=/mnt/chroot-0
COUNT=0

while grep -q "$PREFIX" /proc/mounts; do
    COUNT=$(($COUNT+1))
    if [ $COUNT -ge 20 ]; then
        echo "failed to umount $PREFIX"
        if [ -x /usr/bin/lsof ]; then
            /usr/bin/lsof "$PREFIX"
        fi
        exit 1
    fi
    grep "$PREFIX" /proc/mounts | \
        cut -d\  -f2 | LANG=C sort -r | xargs -r -n 1 umount || sleep 1
done

補遺として、実際にchrootと別のプロセス空間にinitがなければ(つまり:LXCコンテナの場合)、これをinit問題として扱うことはおそらく間違った見方であると指摘します。 。単一のinit(chrootの外側)と共有プロセススペースにより、これはもはや「initの問題」ではなく、問題のパスを持つプロセスを見つけるのはあなた次第です。したがって、上記のproc walkです。

最初の投稿から、これらが完全にブート可能なシステムであり、外部からアップグレードするだけの場合(これは私が読んでいる方法です)、またはパッケージビルドなどに使用するchrootであるかどうかは明らかではありません。後者の場合、最初の場所から開始するinitジョブを禁止するpolicy-rc.d(mk-sbuildによってドロップされたものなど)を配置することもできます。明らかに、これらがブート可能なシステムであることを意図している場合、それは正解ではありません。


それらはブート可能なシステムですがpolicy-rc.d、興味深いアプローチのように見えます(chrootと対話した後、単純に削除できます)。これは/etc/rc*.d/etc/init/*.confスタイルとスタイルの両方のジョブに影響しますか?
ジェレミーカー


upstartもsysvinitも「consult policy-rc.d」ではなく、invoke-rc.dです。これは、すべてのpostinstスクリプトがinitジョブと対話するために使用されることを意図しています。実際には、破損したパッケージの場合を除き、DTRTのようです(修正する必要があります)。それでも、上記の「purge with fire」スクリプティングは、問題がポリシーをすり抜けるものであるか、ポリシーが設定されていないか、または他の種類の長期実行プロセスが残されているか(ジョブの主なユースケース)ここでのbuilddsは、ビルド自体の間にバックグラウンド化されたもの、またはそうでなければsbuildの親ではないものです。
無限

1
utpstartのchrootサポートを回避しようとすることに関する1つの問題。kill -9は、再起動が指定されている場合、upstartが再起動ジョブを再起動することを防ぐことはできません。したがってまだ実行されているかどうかを確認するために、chroot内から upstartに問い合わせる必要があります。これは非常に残念なことだと思います。外部のchrootから何らかの方法でこれらのジョブを削除する必要があります。それは、initctl list / awk / grepのアプローチ+あなたのアプローチがどこにあるべきかを私は知っているということです。
SpamapS

1
@SpamapS:良い点-initジョブを手動で強制終了すると、実際に再起動されます。upstartにchroot固有のシャットダウンを実行し、定義済みのジョブを停止し、chroot内にルートディレクトリがある残りの親プロセスを強制終了するように指示できると便利です。
ジェレミーカー

0

あなたはすでに自分で問題を特定しています:service ...dist-upgrade中に実行されるものもserviceあり、Upstartの一部ではなく、の一部ですsysvinitservice --status-allUpstartサービスで使用したのと同様のawkマジックを追加して、sysvinitサービスを停止します。


3
ああ、ありがとう。それはほとんどより良いですが、それはすべてのサービスをカバーしません。とを実行sudo chroot /mnt/chroot-0 service --list-allしましたがsudo chroot /mnt/chroot-0 initctl list、どちらも実行されているサービスはありません。ただし、/usr/bin/epmd(erlang-baseから)まだ実行中です。
ジェレミーカー

0

この質問はかなり古いことはわかっていますが、今日は2012年と同じくらい関連性があると思います。うまくいけば、誰かがこのコードを見つけてくれることを願っています。私は自分がやっていることのコードを書きましたが、共有したいと思いました。

私のコードは異なりますが、アイデアは@infinityに非常に似ています(実際-/ proc / * / rootについて今知っている唯一の理由は、彼の答えのためです-@infinityに感謝!)クールな追加機能も追加しました

#Kills any PID passed to it
#At first it tries nicely with SIGTERM
#After a timeout, it uses SIGKILL
KILL_PID()
{
        PROC_TO_KILL=$1

        #Make sure we have an arg to work with
        if [[ "$PROC_TO_KILL" == "" ]]
        then
                echo "KILL_PID: \$1 cannot be empty"
                return 1
        fi

        #Try to kill it nicely
        kill -0 $PROC_TO_KILL &>/dev/null && kill -15 $PROC_TO_KILL

        #Check every second for 5 seconds to see if $PROC_TO_KILL is dead
        WAIT_TIME=5

        #Do a quick check to see if it's still running
        #It usually takes a second, so this often doesn't help
        kill -0 $PROC_TO_KILL &>/dev/null &&
        for SEC in $(seq 1 $WAIT_TIME)
        do
                sleep 1

                if [[ "$SEC" != $WAIT_TIME ]]
                then
                        #If it's dead, exit
                        kill -0 $PROC_TO_KILL &>/dev/null || break
                else
                        #If time's up, kill it
                        kill -0 $PROC_TO_KILL &>/dev/null && kill -9 $PROC_TO_KILL
                fi
        done
}

ここで、chrootをアンマウントできるようにするために2つのことを行います。

chrootで実行されている可能性のあるすべてのプロセスを強制終了します。

CHROOT=/mnt/chroot/

#Find processes who's root folder is actually the chroot
for ROOT in $(find /proc/*/root)
do
        #Check where the symlink is pointing to
        LINK=$(readlink -f $ROOT)

        #If it's pointing to the $CHROOT you set above, kill the process
        if echo $LINK | grep -q ${CHROOT%/}
        then
                PID=$(basename $(dirname "$ROOT"))
                KILL_PID $PID
        fi
done

chrootの外部で実行されている可能性のあるすべてのプロセスを強制終了しますが、干渉します(例:chrootが/ mnt / chrootで、ddが/ mnt / chroot / testfileに書き込みを行う場合、/ mnt / chrootはアンマウントに失敗します)

CHROOT=/mnt/chroot/

#Get a list of PIDs that are using $CHROOT for anything
PID_LIST=$(sudo lsof +D $CHROOT 2>/dev/null | tail -n+2 | tr -s ' ' | cut -d ' ' -f 2 | sort -nu)

#Kill all PIDs holding up unmounting $CHROOT
for PID in $PID_LIST
do
        KILL_PID $PID
done

注:すべてのコードをルートとして実行する

また、より複雑でないバージョンの場合は、KILL_PIDを次のいずれかkill -SIGTERMまたはkill -SIGKILL


0

jchroot:より分離されたchroot。

コマンドが実行されると、このコマンドの実行によって開始されたプロセスは強制終了され、IPCは解放され、マウントポイントはアンマウントされます。すべてきれい!

schrootはまだこれを行うことができませんが、これは計画されています

Dockerまたはlxcを使用できないOpenVZ VPSで正常にテストしました。

詳細については、著者のブログをご覧ください。

https://vincent.bernat.im/en/blog/2011-jchroot-isolation.html


-1

schroot:セッション管理の機能があります。セッションを停止すると、そのすべてのプロセスが強制終了されます。

https://github.com/dnschneid/crouton/blob/master/host-bin/unmount-chroot:このスクリプトは、すべてのchrootプロセスを強制終了し、マウントされているすべてのデバイスをアンマウントします。


schrootをchrootで使用すると、OPの問題を解決する方法を説明すると役立つ場合があります。
私を削除してください14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.