Як зупинити всі процеси в chroot?


16

У мене є ряд розділів 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, як видається, оновлення має на меті]

Однак після оновлення до точного цей процес більше не працює - остаточний вміст вийде з ладу, оскільки у /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 здається, робити все правильно, і лише перелічити процеси, розпочаті саме в цьому корені. Я також спробував додати це, як запропонував Туміноїд:

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, щоб я міг безпечно відключити файлову систему? Чи є у запуску якийсь засіб для SIGTERM / SIGKILL усіх дочірніх процесів (як це робиться під час регулярного відключення) в межах chroot?


Це не відповідь на ваше фактичне запитання, але може бути корисним: я рекомендую переглянути пакет lxc. lxc надає прості інструменти для запуску та чистого відключення екземплярів у контейнерах.
іон

Відповіді:


16

Я нічого не вірю, окрім ядра, щоб зберегти здоровий стан тут, тому я (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

А для підрахунку хротів я використовую:

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, мабуть, невірно, як це розглядати, якщо тільки у вас немає init у chroot та окремий технологічний простір (тобто: у випадку контейнерів LXC) . З єдиним init (за межами chroot) та спільним процесовим простором це більше не є "проблемою" init, а скоріше лише за вами, щоб знайти процеси, які, можливо, мають образливий шлях, звідси вищезгаданий процес прогулянки.

З вашої початкової публікації незрозуміло, чи це повністю завантажувані системи, які ви просто модернізуєте зовні (саме так я читаю), чи це хроні, які ви використовуєте для таких речей, як збірка пакетів. Якщо це останнє, ви також можете захотіти поліс-rc.d на місці (як той, який потрапив у mk-sbuild), який просто забороняє починати роботу, починаючи в першу чергу. Очевидно, це не є розумним рішенням, якщо вони також мають бути завантажувальними системами.


Вони завантажувальні системи, але policy-rc.dвиглядає як цікавий підхід (я міг би просто зняти його після взаємодії з chroot). Чи впливає це як на, так /etc/rc*.dі на /etc/init/*.confстиль роботи?
Джеремі Керр


Ні виконайте, ні sysvinit "проконсультуйтесь у політиці-rc.d", це викликає-rc.d, що робиться так, що всі постінст-сценарії покликані використовувати для взаємодії з роботами init. На практиці це здається DTRT, за винятком випадків, коли ушкоджені пакети (які слід виправити). Проте вищезазначений сценарій "очищення вогнем" виконує цю роботу, будь то проблема, яка є ковзанням минулої політики, не проводиться жодна політика, чи тривалий процес іншого типу, який залишився навколо (головний приклад використання для тут складається з речей, які перебувають у фоновому режимі під час самої збірки або не відрізняються від побудови).
нескінченність

1
Одна проблема із спробою вирішити підтримку chroot utpstart. Я досить впевнений, що вбивство -9 не завадить перезапустити програму для початкової роботи, якщо вона вказана заново. Тож вам справді все-таки потрібно допитуватись з початку в середині chroot, щоб з’ясувати, чи все ще працює. Я думаю, що це дуже прикро, і нам слід було б якось із зовнішніх хротонів знищити ці роботи. Це означає, що я бачу, де список initctl / awk / grep підхід + ваш має бути повним.
SpamapS

1
@SpamapS: хороший момент - вбивство init завдань вручну дійсно призводить до їх перезапуску. Було б чудово, щоб можна було сказати на початку, щоб виконати специфічне вимкнення chroot, зупиняючи визначені завдання, а потім знищуючи будь-який залишився відремонтований процес, який має кореневий каталог у chroot.
Джеремі Керр

0

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


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 можна відключити:

Вбийте всі процеси, які можуть працювати в 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 ще не в змозі цього зробити, але це планується

Я успішно протестував його в OpenVZ VPS, який не може використовувати докер або lxc.

Будь ласка, прочитайте авторський блог, щоб отримати детальну інформацію:

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 over chroot може допомогти вирішити проблему OP.
видаліть мене,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.