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


16

우분투 설치를 포함하는 많은 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하는 기능이 있습니까?


이것은 실제 질문에 대한 답변은 아니지만 도움이 될 수 있습니다. lxc 패키지를 보는 것이 좋습니다. lxc는 컨테이너에서 인스턴스를 시작하고 깨끗하게 종료 할 수있는 손쉬운 도구를 제공합니다.
ion

답변:


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

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

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


그것들은 부팅 가능한 시스템이지만 policy-rc.d흥미로운 접근법처럼 보입니다 (chroot와 상호 작용 한 후에 간단히 제거 할 수 있습니다). 이것이 스타일 작업 /etc/rc*.d/etc/init/*.conf스타일 작업 모두에 영향을 줍니까 ?
Jeremy Kerr


upstart 나 sysvinit "consult policy-rc.d"가 아니라 invoke-rc.d는 모든 postinst 스크립트가 초기화 작업과 상호 작용하는 데 사용됩니다. 실제로는 깨진 패키지 (고정되어야 함)의 경우를 제외하고 DTRT로 보입니다. 그럼에도 불구하고, 위의 "불과 함께 퍼지"스크립팅은 문제가 정책을 넘어서고 있거나 정책이 존재하지 않거나 다른 종류의 장기 프로세스가 남아 있는지 여부에 관계없이 작동합니다 (주요 사용 사례). 여기에 빌드 된 것은 빌드 자체에 배경이 있거나 sbuild에서 부모가 아닌 것입니다).
무한대

1
utpstart의 chroot 지원 문제를 해결하는 데 문제가 있습니다. 필자는 kill -9가 respawn이 지정된 경우 upstart 작업이 respawning되는 것을 막지 않을 것이라고 확신합니다. 따라서 여전히 chroot 내부에서 upstart를 조사 하여 여전히 실행 중인지 확인해야합니다. 나는 이것이 불행한 일이라고 생각하며, 우리는이 일을 끝내기 위해 chroot 외부에서 어떤 길을 가져야합니다. 그것은 initctl list / awk / grep 접근 방식 + 당신의 접근 방식이 완료되어야하는 곳을 보았습니다.
SpamapS

1
@SpamapS : 좋은 점-초기화 작업을 수동으로 종료하면 실제로 다시 시작됩니다. upstart에게 chroot 특정 종료를 수행하고 정의 된 작업을 중지 한 다음 chroot 내에 루트 디렉토리가있는 나머지 상위 프로세스를 종료하도록 지시 할 수 있다면 좋을 것입니다.
Jeremy Kerr

0

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


3
아 고마워 거의 나아지지만 모든 서비스를 다루지는 않습니다. 나는 실행했습니다 sudo chroot /mnt/chroot-0 service --list-allsudo chroot /mnt/chroot-0 initctl list어떤 서비스가 실행하지 않는 두 보고서. 그러나 /usr/bin/epmd(erlang-base에서) 여전히 실행 중입니다.
Jeremy Kerr

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


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.