Làm thế nào để tôi dừng tất cả các quá trình trong một chroot?


16

Tôi có một số phân vùng LVM, mỗi phân vùng chứa cài đặt Ubuntu. Thỉnh thoảng, tôi muốn thực hiện một apt-get dist-upgrade, để cập nhật cài đặt lên các gói gần đây nhất. Tôi làm điều này với chroot - quá trình thường là như sau:

$ 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

[không được hiển thị: Tôi cũng gắn kết và ngắt /mnt/chroot-0/{dev,sys,proc}kết nối dưới dạng gắn kết với thực tế /dev, /sys/proc, vì sự nâng cấp xa dường như mong đợi những thứ này sẽ có mặt]

Tuy nhiên, sau khi nâng cấp lên chính xác, quá trình này không còn hoạt động nữa - lần cuối cùng sẽ thất bại vì vẫn còn các tệp đang mở trên /mnt/chroot-0hệ thống tệp. lsofxác nhận rằng có các quy trình với các tệp đang mở trong chroot. Các quy trình này đã được bắt đầu trong quá trình nâng cấp, tôi cho rằng điều này là do các dịch vụ nhất định trong chroot cần được khởi động lại (ví dụ: thông qua service postgresql restart) sau khi gói được nâng cấp.

Vì vậy, tôi cho rằng tôi cần phải nói trước để ngăn chặn tất cả các dịch vụ đang chạy trong chroot này. Có cách nào đáng tin cậy để làm điều này?

Tôi đã thử:

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

Nơi initctl list dường như làm đúng và chỉ liệt kê các quy trình đã được bắt đầu trong root cụ thể này. Tôi cũng đã thử thêm điều này, theo đề xuất của 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

Tuy nhiên, những thứ này dường như không bắt được mọi thứ; các quy trình đã được chỉnh sửa và được sửa chữa thành PID 1 không bị dừng. Tôi cũng đã thử:

sudo chroot /mnt/chroot-0 telinit 0

Nhưng trong trường hợp này, init không phân biệt giữa các rễ riêng biệt và tắt toàn bộ máy.

Vì vậy, có cách nào để bảo init dừng tất cả các tiến trình trong một chroot cụ thể, để tôi có thể ngắt kết nối hệ thống tập tin một cách an toàn không? Có bắt đầu có bất kỳ cơ sở để SIGTERM / SIGKILL tất cả các quy trình con (như sẽ được thực hiện trong khi tắt máy thường xuyên) trong một chroot?


Đây không phải là câu trả lời cho câu hỏi thực tế của bạn, nhưng có thể hữu ích: Tôi khuyên bạn nên xem gói lxc. lxc cung cấp các công cụ dễ dàng để khởi động và tắt sạch các cá thể trong các thùng chứa.
ion

Câu trả lời:


16

Tôi không tin tưởng bất cứ điều gì ngoại trừ hạt nhân để giữ trạng thái lành mạnh ở đây, vì vậy tôi không (ab) sử dụng init để hoàn thành công việc này, tôi cũng không tin vào bản thân mình thực sự biết cái gì được hoặc không được gắn kết (một số gói có thể gắn kết các hệ thống tập tin bổ sung, như binfmt_misc). Vì vậy, để giết mổ quá trình, tôi sử dụng:

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

Và để vượt qua các chroots, tôi sử dụng:

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

Là một phụ lục, tôi chỉ ra rằng tiếp cận vấn đề này như một vấn đề init có lẽ là cách nhìn sai, trừ khi bạn thực sự có một init trong chroot và một không gian xử lý riêng (ví dụ: trong trường hợp các thùng chứa LXC) . Với một init duy nhất (bên ngoài chroot) và không gian quy trình dùng chung, đây không còn là "vấn đề của init", mà chỉ tùy thuộc vào bạn để tìm các quy trình xảy ra có đường dẫn vi phạm, do đó, bước đi trên.

Không rõ ràng từ bài đăng ban đầu của bạn nếu đây là những hệ thống có khả năng khởi động hoàn toàn mà bạn chỉ nâng cấp ra bên ngoài (đó là cách tôi đọc nó) hoặc nếu chúng là những thứ bạn sử dụng cho những thứ như xây dựng gói. Nếu đó là cái thứ hai, bạn cũng có thể muốn có một chính sách-RC.d (như cái được bỏ bởi mk-sbuild) chỉ cấm các công việc init bắt đầu ở vị trí đầu tiên. Rõ ràng, đó không phải là một giải pháp lành mạnh nếu đây cũng là những hệ thống có khả năng khởi động.


Chúng là các hệ thống có thể khởi động, nhưng policy-rc.dtrông giống như một cách tiếp cận thú vị (tôi chỉ có thể loại bỏ nó sau khi tương tác với chroot). Điều này có ảnh hưởng đến cả hai /etc/rc*.d- và /etc/init/*.confkiểu công việc không?
Jeremy Kerr


Không bắt đầu hay sysvinit "tham khảo chính sách-RC.d", đó là invoke-rc.d, vì vậy tất cả các tập lệnh postinst đều được sử dụng để tương tác với các công việc init. Trong thực tế, dường như DTRT, ngoại trừ trong trường hợp các gói bị hỏng (cần được sửa chữa). Tuy nhiên, kịch bản "thanh trừng với lửa" ở trên thực hiện công việc, cho dù vấn đề là do chính sách nào đó bị trượt, không có chính sách nào hoặc quá trình lâu dài của một số loại khác bị bỏ lại (trường hợp sử dụng chính cho buildds ở đây là những thứ được làm nền trong quá trình xây dựng hoặc không được chỉnh sửa từ sbuild).
vô cùng

1
Một vấn đề với việc cố gắng làm việc xung quanh hỗ trợ chroot của utpstart. Tôi khá chắc chắn giết -9 sẽ không ngăn cản sự khởi đầu của công việc mới bắt đầu nếu nó được chỉ định hồi sinh. Vì vậy, bạn thực sự vẫn cần phải thẩm vấn mới bắt đầu từ bên trong chroot để tìm hiểu nếu mọi thứ vẫn đang chạy. Tôi nghĩ rằng điều này là khá đáng tiếc, và chúng ta nên có một số cách từ bên ngoài để tiêu diệt những công việc này. Điều đó nói rằng tôi thấy nơi tiếp cận danh sách initctl / awk / grep + của bạn sẽ được hoàn thành.
SpamapS

1
@SpamapS: điểm tốt - giết các công việc init theo cách thủ công thực sự dẫn đến việc chúng được khởi động lại. Sẽ thật tuyệt khi có thể nói với người mới bắt đầu thực hiện tắt máy cụ thể chroot, dừng các công việc đã xác định và sau đó giết bất kỳ quy trình được sửa chữa còn lại nào có thư mục gốc trong chroot.
Jeremy Kerr

0

Bạn đã tự xác định vấn đề: Một số thứ chạy service ...trong quá trình nâng cấp và servicekhông phải là một phần của Upstart, mà là một phần của sysvinit. Thêm ma thuật awk tương tự xung quanh service --status-allđể dừng các dịch vụ sysvinit như bạn đã sử dụng cho các dịch vụ Upstart.


3
À, cảm ơn. Nó gần như tốt hơn, nhưng điều đó cũng không bao gồm tất cả các dịch vụ. Tôi đã chạy sudo chroot /mnt/chroot-0 service --list-allsudo chroot /mnt/chroot-0 initctl listcả hai đều báo cáo không có dịch vụ nào đang chạy. Tuy nhiên, /usr/bin/epmd(từ erlang-base) vẫn đang chạy.
Jeremy Kerr

0

Tôi biết câu hỏi này khá cũ, nhưng tôi nghĩ nó có liên quan đến ngày hôm nay như năm 2012 và hy vọng ai đó thấy mã này hữu ích. Tôi đã viết mã cho một cái gì đó tôi đang làm, nhưng nghĩ rằng tôi sẽ chia sẻ nó.

Mã của tôi thì khác, nhưng các ý tưởng rất giống với @infinity (thực tế - lý do duy nhất bây giờ tôi biết về / Proc / * / root là vì câu trả lời của anh ấy - cảm ơn @infinity!). Tôi cũng đã thêm một số chức năng bổ sung thú vị

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

Bây giờ bạn sẽ làm 2 điều để đảm bảo chroot có thể được bỏ qua:

Giết tất cả các tiến trình có thể đang chạy trong 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

Giết tất cả các quy trình có thể đang chạy bên ngoài chroot, nhưng can thiệp vào nó (ví dụ: nếu chroot của bạn là / mnt / chroot và dd đang ghi vào / mnt / chroot / testfile, / mnt / chroot sẽ không thể kết nối)

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

Lưu ý: Chạy tất cả mã dưới quyền root

Ngoài ra, đối với phiên bản ít phức tạp hơn, hãy thay thế KILL_PID bằng kill -SIGTERMhoặckill -SIGKILL


0

jchroot : một chroot với sự cô lập nhiều hơn.

Sau khi lệnh của bạn đã được thực thi, bất kỳ quá trình nào được bắt đầu bằng việc thực thi lệnh này sẽ bị hủy, mọi IPC sẽ được giải phóng, mọi điểm gắn kết sẽ được ngắt. Mọi thứ đều sạch sẽ!

schroot chưa thể làm điều này, nhưng điều này đã được lên kế hoạch

Tôi đã thử nghiệm thành công trong VPS OpenVZ, không thể sử dụng docker hoặc lxc.

Vui lòng đọc blog của tác giả để biết chi tiết:

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


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.