chroot에서 모든 프로세스를 어떻게 중지합니까? 프로세스는 dist-upgrade 중에

우분투 설치를 포함하는 많은 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 별도의 루트를 구분 하지 않고 전체 시스템을 종료합니다.

그래서 파일 시스템을 안전하게 마운트 해제 할 수 있도록 init에게 특정 chroot의 모든 프로세스를 중지하도록 지시 할 수있는 방법이 있습니까? upstart는 chroot 내에 모든 하위 프로세스 (정기 종료 중에 수행됨)를 SIGTERM / SIGKILL하는 기능이 있습니까?



답변

나는 커널을 제외하고는 제정신 상태를 유지하기 위해 커널을 믿지 않으므로이 작업을 수행하는 데 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

부록으로, 나는 이것을 init 문제로 접근하는 것이 chroot에 별도의 init이 있고 별도의 프로세스 공간이 없다면 (즉, LXC 컨테이너의 경우) 그것을 보는 잘못된 방법 일 것이라고 지적했다. . 하나의 init (chroot 외부)와 공유 프로세스 공간을 가진이 프로세스는 더 이상 “초기 문제”가 아니라 오히려 문제가되는 경로를 갖는 프로세스를 찾는 것입니다.

완전히 부팅 가능한 시스템이 외부에서 업그레이드하는 시스템인지 (내가 읽는 방식인지) 또는 패키지 빌드와 같은 용도로 사용하는 chroot인지 여부는 초기 게시물에서 명확하지 않습니다. 후자 인 경우, 처음부터 시작하는 init 작업을 금지하는 policy-rc.d (예 : mk-sbuild에서 삭제 한 것)를 원할 수도 있습니다. 부팅 가능한 시스템이기도 한 경우에는 제정신의 솔루션이 아닙니다.


답변

이미 문제를 직접 확인했습니다. service ...dist-upgrade 중 일부가 실행 service되며 Upstart의 일부가 아닌의 일부입니다 sysvinit. service --status-allUpstart 서비스에 사용한 것과 비슷한 awk magic을 추가 하여 sysvinit 서비스를 중지하십시오.


답변

나는이 질문이 꽤 오래되었다는 것을 알고 있지만 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


답변

jchroot : 더 고립 된 chroot.

명령이 실행 된 후이 명령을 실행하여 시작된 모든 프로세스가 종료되고 모든 IPC가 해제되며 마운트 지점이 마운트 해제됩니다. 모두 깨끗합니다!

schroot는 아직이 작업을 수행 할 수 없지만 계획되어 있습니다.

Docker 또는 lxc를 사용할 수없는 OpenVZ VPS에서 성공적으로 테스트했습니다.

자세한 내용은 저자의 블로그를 참조하십시오 :

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


답변

schroot : 세션 관리 기능이 있습니다. 세션을 중지하면 모든 프로세스가 종료됩니다.

https://github.com/dnschneid/crouton/blob/master/host-bin/unmount-chroot :이 스크립트는 모든 chroot 프로세스를 종료하고 마운트 된 모든 장치를 마운트 해제합니다.