Как я останавливаю все процессы в chroot?

У меня есть много разделов LVM, каждый содержащий установку Ubuntu. Иногда, я хочу сделать 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 и /proc, поскольку dist-обновление, кажется, ожидает, что они будут присутствовать]

Однако после обновления до точного, этот процесс больше не работает - финал umount перестанет работать, потому что существуют все еще открытые файлы на /mnt/chroot-0 файловая система. lsof подтверждает, что существуют процессы с открытыми файлами в chroot. Эти процессы были запущены во время dist-обновления, я предполагаю, что это вызвано тем, что определенные сервисы в chroot должны быть перезапущены (например, через service postgresql restart) после того, как пакет обновлен.

Так, я полагаю, что должен сказать выскочке останавливать все сервисы, которые работают в этом chroot. Существует ли способ надежно сделать это?

Я попробовал:

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

Однако они, кажется, не ловят все; процессы, которые имеют daemonised и повторно порожденный к PID 1, не становятся остановленными. Я также попробовал:

sudo chroot /mnt/chroot-0 telinit 0

Но в этом случае, init не различает отдельные корни и закрывает всю машину.

Так, есть ли какой-либо способ сказать init останавливать все процессы в конкретном chroot, так, чтобы я мог безопасно размонтировать файловую систему? У выскочки есть какое-либо средство к SIGTERM/SIGKILL всеми дочерними процессами (как был бы сделан во время регулярного завершения работы) в chroot?

16
задан 18 July 2012 в 12:48

5 ответов

Я не доверяю ничему, кроме ядра, чтобы поддерживать здесь нормальное состояние, поэтому я не (ab) использую 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 и отдельного пространства процессов (например, в случае контейнеров LXC). С одним init (вне chroot) и общим пространством процессов это уже не «проблема init», а скорее только ваша задача - найти процессы, которые имеют неправильный путь, отсюда и вышеприведенный обход процесса. [ 114]

Из вашего первоначального поста не ясно, являются ли они полностью загружаемыми системами, которые вы просто обновляете извне (как я это прочитал), или они являются chroot-файлами, которые вы используете для таких вещей, как сборки пакетов. Если это последнее, вам также может понадобиться policy-rc.d (например, тот, который был добавлен в mk-sbuild), который просто запрещает запускать задания init, начиная с самого начала. Очевидно, что это не вменяемое решение, если речь идет и о загрузочных системах.

0
ответ дан 18 July 2012 в 12:48

Вы уже определили проблему самостоятельно: некоторые вещи запускаются service ... во время dist-upgrade и service не является частью Upstart, а частью sysvinit. Добавьте аналогичную магию awk вокруг service --status-all, чтобы остановить sysvinit сервисы, которые вы использовали для сервисов Upstart.

0
ответ дан 18 July 2012 в 12:48

Я знаю, что этот вопрос довольно старый, но я думаю, что он так же актуален сегодня, как и в 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
}

Теперь вы должны сделать 2 вещи, чтобы убедиться, что chroot может быть размонтирован:

Убить все процессы, которые могут выполняться в 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

Примечание. Запустить весь код от имени пользователя root

Кроме того, для менее сложной версии замените KILL_PID на kill -SIGTERM или kill -SIGKILL

0
ответ дан 18 July 2012 в 12:48

jchroot: chroot с большей изоляцией.

После того, как Ваша команда была выполнена, любой процесс, запущенный выполнением этой команды, будет уничтожен, любой IPC будет освобожден, любая точка монтирования будет размонтирована. Все убирают!

schroot еще не может сделать это, но это планируется

Я протестировал его успешно в OpenVZ VPS, который не может использовать докера или lxc.

Прочитайте блог автора для деталей:

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

0
ответ дан 18 July 2012 в 12:48

schroot: у него есть особенность управления сессиями. Когда вы останавливаете сеанс, все его процессы уничтожаются.

https://github.com/dnschneid/crouton/blob/master/host-bin/unmount-chroot : эти сценарии убивают весь процесс chroot и отключают все подключенные устройства.

0
ответ дан 18 July 2012 в 12:48

Другие вопросы по тегам:

Похожие вопросы: