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 ビルド サーバーで問題が発生しました。

問題は、ビルドスクリプト自体がjenkins chroot 環境したがって、スクリプトを実行するとunshare --mount ...、次のエラーが発生して失敗します。

unshare: cannot change root filesystem propagation: Invalid argument

残念ながら、私はこの制限やそれを回避する方法をまったく理解していません。コマンド ラインで chroot を試みても、このエラーを再現できません。Jenkins プラグインが何を行ってこのエラーを引き起こしたのかはわかりません。

最も重要なことは、これらのマウントポイントは終了時に削除されることです。毎回必ず

答え1

AB のコメントに基づいて回避策を見つけました:

AB は次のように書いています:

lxc の rootfs ディレクトリ インストールに chroot すると、同じエラーが発生しますunshare --mount bash。最初にこのディレクトリを別の場所にバインド マウント (--private) してから、そこで chroot すると、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 の外部で使用可能であると想定します。

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

または、chroot をpivot_root環境に変換します。

#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");
}

関連情報