Bir kroottaki tüm işlemleri nasıl durdurabilirim?


16

Her biri bir Ubuntu yüklemesi içeren bir dizi LVM bölümüm var. Bazen, apt-get dist-upgradebir yüklemeyi en son paketlere güncellemek için, yapmak istiyorum . Bunu chroot ile yaparım - süreç genellikle şöyle bir şeydir:

$ 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

[gösterilmiyor: Ben de /mnt/chroot-0/{dev,sys,proc}gerçek bağlanma bağları olarak monte ve sökmek /dev, /sysve /procdist-yükseltme bunların mevcut gibi görünüyor]

Ancak, hassas bir sürüme yükselttikten sonra bu işlem artık işe yaramıyor - /mnt/chroot-0dosya sisteminde hala açık dosyalar olduğu için son umount başarısız olacak . lsofchroot'ta açık dosyaları olan işlemler olduğunu doğrular. Bu işlemler dağıtım sırasında başlatıldı, bunun nedeni service postgresql restart, paketin yükseltildikten sonra kroottaki bazı hizmetlerin yeniden başlatılması (örneğin, aracılığıyla ) olması gerektiğine inanıyorum .

Yani, bu chroot içinde çalışan tüm hizmetleri durdurmak için upstart'a söylemem gerektiğini anlıyorum. Bunu güvenilir bir şekilde yapmanın bir yolu var mı?

Denedim:

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

Nerede initctl list doğru olanı yapar gibi görünüyor ve sadece bu kökte başlatılan süreçleri listeler. Tuminoid tarafından önerildiği gibi bunu da eklemeyi denedim:

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

Ancak, bunlar her şeyi yakalamıyor gibi görünüyor; planlanan ve PID 1'e bildirilen işlemler durmaz. Ayrıca denedim:

sudo chroot /mnt/chroot-0 telinit 0

Ancak bu durumda, init ayrı kökleri ayırt etmez ve tüm makineyi kapatır.

Yani, init'e belirli bir kroottaki tüm süreçleri durdurmasını söylemenin bir yolu var mı, böylece dosya sistemini güvenli bir şekilde çıkarabilir miyim? Uptart'ın bir kroot içindeki tüm alt süreçleri (düzenli kapatma sırasında yapılacak gibi) SIGTERM / SIGKILL için herhangi bir tesisi var mı?


Bu, asıl sorunuzun bir cevabı değildir, ancak yardımcı olabilir: lxc paketine bakmanızı tavsiye ederim. lxc, kaplardaki örnekleri başlatmak ve temizlemek için kolay araçlar sağlar.
ion

Yanıtlar:


16

Ben burada aklı başında bir devlet tutmak için çekirdek dışında hiçbir şeye güvenmiyorum, bu yüzden (ab) bu ​​işi yapmak için init kullanmıyorum, ne de gerçekte neyin monte edilip edilmediğini bilmiyorum (bazı paketler) binfmt_misc gibi ekstra dosya sistemleri bağlayabilir). Yani, süreç katliamı için şunu kullanıyorum:

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

Ve kökleri takmak için kullanıyorum:

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

Bir ek olarak, aslında bir init problemi olarak yaklaşmanın, aslında kökte bir init ve ayrı bir işlem alanı (yani: LXC kapları durumunda) olmadığı sürece, ona bakmak için yanlış bir yol olduğuna dikkat çekerim. . Tek bir init (chroot dışında) ve paylaşılan bir süreç alanı ile, bu artık "init problemi" değil, daha çok rahatsız edici yola sahip olan süreçleri, dolayısıyla yukarıdaki proc yürüyüşünü bulmak size kalmıştır.

Bunların harici olarak yeni sürüme geçirdiğiniz (yani nasıl okuduğum) tamamen önyüklenebilir sistemler olup olmadığı veya paket yapıları gibi şeyler için kullandığınız kroketler olup olmadığı ilk yayından net değildir. Eğer ikinciyse, ilk etapta başlangıç ​​işlerini yasaklayan bir politika-rc.d'yi de (mk-sbuild tarafından bırakılan gibi) isteyebilirsiniz. Açıkçası, eğer bunlar da önyüklenebilir sistemler olması gerekiyorsa, bu akılcı bir çözüm değildir.


Önyüklenebilir sistemler, ancak policy-rc.dilginç bir yaklaşım gibi görünüyor (krootla etkileşime girdikten sonra basitçe kaldırabilirim). Bu hem etkiler mi /etc/rc*.dve - /etc/init/*.conftarzı işler?
Jeremy Kerr


Ne uptart ne de sysvinit "consult-rc.d", bunu yapan invoke-rc.d'dir, bu da tüm postinst komut dosyalarının init işleriyle etkileşimde kullanılması içindir. Uygulamada, kırılmış paketler (düzeltilmesi gereken) hariç DTRT'ye benziyor. Yine de, yukarıdaki "ateşle tasfiye" betiklemesi, sorunun politikadan geçmiş bir şey olup olmadığı, herhangi bir politika bulunmadığı veya başka bir türden uzun süre devam eden bir süreç olup olmadığı işi yapıyor ( Buradaki yapılar, yapının kendisi sırasında arka plana sahip olan veya sbuild'den başka bir şekilde görünmeyen şeylerdir).
sonsuzluk

1
Utpstart'ın kroot desteği üzerinde çalışmaya çalışmakla ilgili bir sorun. Ben kesin -9 belirtilen uppawn belirtilen uptart up uptart yeniden doğmasını engellemeyecek eminim. Bu yüzden , hala işlerin devam edip etmediğini öğrenmek için hala rootun içinden yukarı doğru sorgulamaya ihtiyacınız var . Bunun oldukça talihsiz olduğunu düşünüyorum ve bu işleri öldürmek için dış kroelerden bir yol bulmalıyız. Bu, initctl list / awk / grep yaklaşımının + sizinkinin nerede tamamlanması gerektiğini gördüğümü söyledi.
SpamapS

1
@SpamapS: iyi nokta - init işlerini manuel olarak öldürmek gerçekten yeniden başlatılmalarına neden olur. Uptart'a, krota özgü bir kapatma gerçekleştirmesini, tanımlı işleri durdurmasını ve daha sonra, kök içinde kök dizini olan geriye kalan tüm bildirilmiş işlemleri öldürmesini söyleyebilmek harika olurdu.
Jeremy Kerr

0

Sorunu zaten kendiniz belirlediniz: Bazı şeyler service ...dağıtım sırasında çalışıyor ve serviceUpstart'ın bir parçası değil, bir parçası sysvinit. service --status-allUpstart hizmetleri için kullandığınız gibi sysvinit hizmetlerini durdurmak için benzer bir awk büyüsü ekleyin .


3
Ah teşekkürler. Neredeyse daha iyi, ama bu da tüm hizmetleri kapsamıyor. Çalıştım sudo chroot /mnt/chroot-0 service --list-allve sudo chroot /mnt/chroot-0 initctl listher ikisi de çalışan hizmet bildirmiyor. Ancak, /usr/bin/epmd(erlang-base'den) hala çalışıyor.
Jeremy Kerr

0

Bu sorunun oldukça eski olduğunu biliyorum, ama bugün 2012'de olduğu gibi ilgili olduğunu düşünüyorum ve umarım birisi bu kodu yararlı bulur. Yaptığım bir şeyin kodunu yazdım, ama paylaşacağımı düşündüm.

Kodum farklı, ancak fikirler @infinity ile çok benzer (aslında - / proc / * / root hakkında şimdi bilmemin tek nedeni cevabı nedeniyle - teşekkürler @infinity!). Ayrıca harika ek işlevler ekledim

#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
}

Şimdi, krootun sökülebildiğinden emin olmak için 2 şey yaparsınız:

Kökte çalıştırılabilecek tüm işlemleri öldürün:

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

Kökün dışında çalışan, ancak buna müdahale eden tüm işlemleri öldürün (ör: eğer kroketiniz / mnt / chroot ise ve dd / mnt / chroot / testfile yazıyorsa, / mnt / chroot bağlantıyı kesemez)

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

Not: Tüm kodu root olarak çalıştır

Ayrıca, daha az karmaşık bir sürüm için KILL_PID değerini kill -SIGTERMyakill -SIGKILL


0

jchroot : daha fazla izolasyona sahip bir kroot .

Komutunuz yürütüldükten sonra, bu komutun yürütülmesi ile başlatılan herhangi bir işlem öldürülür, herhangi bir IPC serbest bırakılır, herhangi bir bağlama noktası kaldırılır. Hepsi temiz!

schroot henüz bunu yapamıyor, ancak bu planlandı

Docker veya lxc kullanamayan OpenVZ VPS'de başarıyla test ettim.

Ayrıntılar için lütfen yazarın blogunu okuyun:

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


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.