Bagaimana cara menghentikan semua proses dalam chroot?


16

Saya memiliki sejumlah partisi LVM, masing-masing berisi instalasi Ubuntu. Kadang-kadang, saya ingin melakukan apt-get dist-upgrade, untuk memperbarui instalasi ke paket terbaru. Saya melakukan ini dengan chroot - prosesnya biasanya seperti:

$ 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

[tidak diperlihatkan: Saya juga me-mount dan unmount /mnt/chroot-0/{dev,sys,proc}sebagai bind-mounts ke yang asli /dev, /sysdan /proc, karena dist-upgrade sepertinya mengharapkan ini ada]

Namun, setelah memutakhirkan ke tepatnya, proses ini tidak lagi berfungsi - umount terakhir akan gagal karena masih ada file yang terbuka di sistem /mnt/chroot-0file. lsofmenegaskan bahwa ada proses dengan file terbuka di chroot. Proses-proses ini telah dimulai selama dist-upgrade, saya berasumsi ini karena layanan tertentu di chroot perlu direstart (misalnya, melalui service postgresql restart) setelah paket ditingkatkan.

Jadi, saya pikir saya perlu memberitahu pemula untuk menghentikan semua layanan yang berjalan dalam chroot ini. Apakah ada cara untuk melakukan ini dengan andal?

Saya sudah mencoba:

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

Di mana initctl list tampaknya melakukan hal yang benar dan hanya daftar proses yang telah dimulai di root khusus ini. Saya sudah mencoba menambahkan ini juga, seperti yang disarankan oleh 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

Namun, ini tampaknya tidak menangkap segalanya; proses yang telah daemonisasi dan telah diperbaiki ke PID 1 jangan dihentikan. Saya juga sudah mencoba:

sudo chroot /mnt/chroot-0 telinit 0

Tetapi dalam kasus ini, init tidak membedakan antara akar yang terpisah dan mematikan seluruh mesin.

Jadi, apakah ada cara untuk memberitahu init untuk menghentikan semua proses dalam chroot tertentu, sehingga saya dapat dengan aman meng-unmount sistem file? Apakah pemula memiliki fasilitas untuk SIGTERM / SIGKILL semua proses anak (seperti yang akan dilakukan selama shutdown reguler) dalam chroot?


Ini bukan jawaban untuk pertanyaan Anda yang sebenarnya, tetapi mungkin membantu: Saya sarankan melihat paket lxc. lxc menyediakan alat yang mudah untuk memulai dan mematikan mesin virtual dalam wadah.
ion

Jawaban:


16

Saya tidak mempercayai apa pun kecuali kernel untuk menjaga keadaan waras di sini, jadi saya tidak (ab) menggunakan init untuk menyelesaikan pekerjaan ini, saya juga tidak mengandalkan diri saya sendiri untuk mengetahui apa yang sudah atau tidak dipasang (beberapa paket dapat me-mount filesystem tambahan, seperti binfmt_misc). Jadi, untuk proses penyembelihan, saya menggunakan:

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

Dan untuk umroun chroot, saya menggunakan:

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

Sebagai tambahan, saya akan menunjukkan bahwa mendekati ini sebagai masalah init mungkin cara yang salah untuk melihatnya, kecuali jika Anda benar-benar memiliki init di chroot dan ruang proses yang terpisah (yaitu: dalam kasus kontainer LXC) . Dengan init tunggal (di luar chroot), dan ruang proses bersama, ini bukan lagi "masalah init", melainkan hanya terserah Anda untuk menemukan proses yang kebetulan memiliki jalur menyinggung, maka proses proc di atas berjalan.

Tidak jelas dari pos awal Anda apakah ini adalah sistem yang sepenuhnya dapat di-boot yang baru saja Anda tingkatkan secara eksternal (seperti yang saya baca), atau apakah itu chroot yang Anda gunakan untuk hal-hal seperti pembuatan paket. Jika yang terakhir, Anda mungkin juga menginginkan kebijakan-rc.d di tempat (seperti yang dijatuhkan oleh mk-sbuild) yang hanya melarang pekerjaan init dimulai di tempat pertama. Jelas, itu bukan solusi yang masuk akal jika ini dimaksudkan untuk menjadi sistem yang dapat di-boot juga.


Mereka adalah sistem yang dapat di-boot, tetapi policy-rc.dterlihat seperti pendekatan yang menarik (saya bisa menghapusnya setelah berinteraksi dengan chroot). Apakah ini mempengaruhi pekerjaan /etc/rc*.d- dan- /etc/init/*.confgaya?
Jeremy Kerr


Baik pemula maupun sysvinit "berkonsultasi kebijakan-rc.d", itu memanggil-rc.d yang melakukannya, yang semua skrip postin dimaksudkan untuk digunakan untuk berinteraksi dengan pekerjaan init. Dalam praktiknya, tampaknya DTRT, kecuali dalam kasus paket rusak (yang harus diperbaiki). Namun, skrip "pembersihan dengan api" di atas berfungsi dengan baik, apakah masalahnya adalah sesuatu yang menyelinap melewati kebijakan, tidak ada kebijakan yang berlaku, atau proses jangka panjang dari jenis lain yang tersisa (kasus penggunaan utama untuk buildds di sini adalah hal-hal yang dilatarbelakangi selama build itu sendiri atau sebaliknya dari sbuild).
infinity

1
Satu masalah dengan mencoba mengatasi dukungan chroot utpstart. Saya yakin pasti kill -9 tidak akan mencegah pemula memulai kembali dari pekerjaan pemula jika telah respawn ditentukan. Jadi Anda benar-benar masih perlu menginterogasi pemula dari dalam chroot untuk mencari tahu apakah semuanya masih berjalan. Saya pikir ini sangat disayangkan, dan kita harus memiliki cara dari chroots luar untuk mematikan pekerjaan ini. Yang mengatakan saya melihat di mana daftar initctl / awk / grep + pendekatan Anda harus lengkap.
SpamapS

1
@SpamapS: poin bagus - membunuh pekerjaan init secara manual memang menyebabkannya dimulai ulang. Akan lebih baik untuk dapat memberitahu pemula untuk melakukan shutdown khusus chroot, menghentikan pekerjaan yang ditentukan, dan kemudian membunuh proses yang tersisa yang memiliki direktori root di dalam chroot.
Jeremy Kerr

0

Anda telah mengidentifikasi sendiri masalahnya: Beberapa hal berjalan service ...selama dist-upgrade dan servicebukan bagian dari pemula, tetapi bagian dari sysvinit. Tambahkan sihir awk serupa di sekitar service --status-alluntuk menghentikan layanan sysvinit seperti yang Anda gunakan untuk layanan pemula.


3
Ah terima kasih. Ini hampir lebih baik, tetapi itu tidak mencakup semua layanan juga. Saya sudah menjalankan sudo chroot /mnt/chroot-0 service --list-alldan sudo chroot /mnt/chroot-0 initctl list, yang keduanya melaporkan tidak ada layanan yang berjalan. Namun, /usr/bin/epmd(dari erlang-base) masih berjalan.
Jeremy Kerr

0

Saya tahu pertanyaan ini cukup lama, tetapi saya pikir itu relevan hari ini seperti pada tahun 2012, dan semoga seseorang menemukan kode ini berguna. Saya menulis kode untuk sesuatu yang saya lakukan, tetapi saya pikir saya akan membagikannya.

Kode saya berbeda, tetapi idenya sangat mirip dengan @ infinity (pada kenyataannya - satu-satunya alasan saya sekarang tahu tentang / proc / * / root adalah karena jawabannya - terima kasih @ infinity!). Saya juga menambahkan beberapa fungsi tambahan keren

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

Sekarang Anda akan melakukan 2 hal untuk memastikan chroot dapat dilepas:

Bunuh semua proses yang mungkin berjalan di 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

Bunuh semua proses yang mungkin berjalan di luar chroot, tetapi mengganggu itu (mis: jika chroot Anda adalah / mnt / chroot dan dd menulis ke / mnt / chroot / testfile, / mnt / chroot akan gagal di-unmount)

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

Catatan: Jalankan semua kode sebagai root

Selain itu, untuk versi yang tidak terlalu rumit, ganti KILL_PID dengan keduanya kill -SIGTERM ataukill -SIGKILL


0

jchroot : chroot dengan lebih banyak isolasi.

Setelah perintah Anda dieksekusi, proses apa pun yang dimulai dengan eksekusi perintah ini akan dimatikan, IPC apa pun akan dibebaskan, setiap titik pemasangan akan dilepas. Semua bersih!

schroot belum dapat melakukan ini, tetapi ini sudah direncanakan

Saya telah mengujinya dengan sukses di OpenVZ VPS, yang tidak dapat menggunakan buruh pelabuhan atau lxc.

Silakan baca blog penulis untuk detailnya:

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


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.