Jenkins chroot 환경에서 "unshare --mount"

Jenkins chroot 환경에서 "unshare --mount"

내 빌드 스크립트 중 일부에서는 스크립트가 종료될 때 마운트를 남기지 않고 마운트를 보호하기 위한 메커니즘으로 마운트 네임스페이스를 사용해 왔습니다. 네임스페이스의 마지막 프로세스가 종료되면 비공유 마운트 지점이 암시적으로 마운트 해제됩니다..

내 스크립트에는 일반적으로 다음과 같은 섹션이 포함됩니다.

#!/bin/bash
self_ns=$(ls -lh /proc/self/ns/mnt)
init_ns=$(ls -lh /proc/$PPID/ns/mnt)
if [ "${self_ns#*mnt:}" = "${init_ns#*mnt:}" ] ; then
    unshare --mount $0 "$@"
    exit $?
fi

이것은 한동안 잘 작동했지만 최근 Jenkins 빌드 서버에 문제가 발생했습니다.

문제는 빌드 스크립트 자체가젠킨스 chroot 환경. 따라서 스크립트가 실행되면 unshare --mount ...다음 오류와 함께 실패합니다.

unshare: cannot change root filesystem propagation: Invalid argument

안타깝게도 저는 이 제한 사항이나 이를 해결하는 방법을 잘 이해하지 못합니다. 명령줄에서 chroot를 시도하면 이 오류를 재현할 수 없습니다. 젠킨스 플러그인이 이 문제를 일으키는 원인이 무엇인지 모르겠습니다.

가장 중요한 것은 이러한 마운트 지점이 종료 시 삭제된다는 것입니다.매번 어김없이.

답변1

AB의 의견을 바탕으로 해결 방법을 찾았습니다.

AB는 다음과 같이 썼습니다.

lxc mount 및 unshare --mount bash. unshare --mount이것이 무엇을 의미하는지 모르겠지만 이것이 원인이나 해결 방법(파이프라인에 바인드 마운트 추가)을 찾는 데 도움이 되기를 바랍니다.

이를 바탕으로 이것이 작동하지 않는다는 것을 발견했습니다.

unshare --mount bash -c 'echo hello'

하지만 이것은 작동합니다:

mount --bind --make-private / /mnt 
chroot /mnt unshare --mount bash -c 'echo hello'
umount /mnt

답변2

문제의 원인은 unshare마운트 지점에 대해서만 수행할 수 있는 루트 디렉토리에 대한 마운트 전파 플래그를 설정하려고 시도하는 것입니다. Jenkins chroot 환경의 루트 디렉터리는 마운트 지점이 아닙니다.

예를 들어:

$ unshare -rm mount --make-rprivate /opt
mount: /opt: not mount point or bad option.

완전 재현:

#!/bin/bash
try() {
  mount -t tmpfs t /mnt
  mkdir /mnt/t
  for i in /bin /lib* /sbin /usr /home /proc
  do
    mkdir "/mnt/t$i"
    mount --rbind "$i" "/mnt/t$i"
  done
  chroot /mnt/t unshare -m echo OK
}
export -f try
unshare -rm bash -c try

간단한 해결 방법은 마운트 네임스페이스 외부에 마운트되지 않는다는 것입니다. 마운트 전파를 설정하기 위해 chroot를 이스케이프하고 chroot 외부에서 mount 명령을 사용할 수 있다고 가정합니다.

unshare --propagation unchanged -m sh -c \
'nsenter --mount=/proc/self/ns/mnt mount --make-rslave /; echo Do some mounts'

pivot_root또는 chroot를 환경 으로 변환하십시오 .

#define _GNU_SOURCE
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

static int pivot_root(const char *new_root, const char *put_old){
    return syscall(SYS_pivot_root, new_root, put_old);
}

int main(int argc, char **argv) {

    if (unshare(CLONE_NEWNS))
        error(1, errno, "unshare");

    int root = open("/", O_DIRECTORY | O_PATH | O_CLOEXEC);
    if (root < 0) error(1, errno, "open /");

    int ns = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
    if (ns < 0) error(1, errno, "open mount namespace");

    if (setns(ns, CLONE_NEWNS))
        error(1, errno, "setns");

    if (fchdir(root))
        error(1, errno, "fchdir");

    if (mount("/", "/", 0, MS_REC|MS_SLAVE, 0))
        error(1, errno, "mount --make-rslave");

    if (mount(".", "proc", 0, MS_REC|MS_BIND, 0))
        error(1, errno, "mount --rbind");

    if (chdir("proc"))
        error(1, errno, "chdir");

    if (pivot_root(".", "proc"))
        error(1, errno, "pivot_root");

    if (umount2("proc", MNT_DETACH))
        error(1, errno, "umount");

    execvp(argv[1], argv + 1);
    error(1, errno, "exec");
}

관련 정보