diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-10-29 00:21:41 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-10-29 00:21:41 +0800 |
| commit | 7ab30ab3ee572de1be2afc3083e4a28b594b1cba (patch) | |
| tree | f65683c722d0abc30a17a9a8e4485f9ec5fd1f8b | |
| parent | 4e6376cfc58e73fc14ddb691c0fac9da425efac9 (diff) | |
| download | rutils-7ab30ab3ee572de1be2afc3083e4a28b594b1cba.tar.gz rutils-7ab30ab3ee572de1be2afc3083e4a28b594b1cba.tar.xz | |
Add named container instances with exec, status, list, and delete commands.
Signed-off-by: Richard Qin <richard.qin.x@gmail.com>
| -rw-r--r-- | debian-container/README | 37 | ||||
| -rwxr-xr-x | debian-container/debian-container.sh | 21 | ||||
| -rw-r--r-- | debian-container/lib/common.sh | 34 | ||||
| -rw-r--r-- | debian-container/lib/config.sh | 12 | ||||
| -rw-r--r-- | debian-container/lib/container.sh | 60 |
5 files changed, 128 insertions, 36 deletions
diff --git a/debian-container/README b/debian-container/README index 0749f14..7c5b66f 100644 --- a/debian-container/README +++ b/debian-container/README @@ -11,18 +11,34 @@ sudo ./debian-container.sh base-create ./debian-stable.tar.gz 从 tarball 创建容器: -sudo ./debian-container.sh init --tarball ./debian-stable.tar.gz +sudo ./debian-container.sh init test-1 --tarball ./debian-stable.tar.gz +sudo ./debian-container.sh init test-2 --tarball ./debian-stable.tar.gz + +进入容器: + +sudo ./debian-container.sh enter test-1 + +在容器中执行命令: + +sudo ./debian-container.sh exec test-1 -- uname -a + +列出和查看容器: + +sudo ./debian-container.sh list +sudo ./debian-container.sh status test-1 + +删除容器: + +sudo ./debian-container.sh delete test-1 直接联网初始化容器: -sudo ./debian-container.sh init -sudo ./debian-container.sh enter -sudo ./debian-container.sh delete +sudo ./debian-container.sh init test-3 -默认情况下,镜像和挂载目录位于当前目录: +默认情况下,实例数据位于当前目录的 instances/ 下: -rootfs.ext4 -debian-root/ +instances/test-1/rootfs.ext4 +instances/test-1/root/ 默认基础 tarball: @@ -31,14 +47,13 @@ debian-base.tar.gz 可以通过环境变量修改路径: sudo env \ - DEBIAN_IMAGE=/path/to/rootfs.ext4 \ - DEBIAN_ROOT=/path/to/debian-root \ + DEBIAN_INSTANCES=/path/to/instances \ ./debian-container.sh enter 可配置的环境变量: -DEBIAN_IMAGE 镜像路径 -DEBIAN_ROOT 挂载目录 +DEBIAN_INSTANCES 实例目录 +DEBIAN_LOCK_FILE 锁文件路径 DEBIAN_TARBALL 默认基础 tarball 路径 DEBIAN_SUITE Debian 版本,默认 stable DEBIAN_MIRROR Debian 镜像地址 diff --git a/debian-container/debian-container.sh b/debian-container/debian-container.sh index 893d83b..5838fde 100755 --- a/debian-container/debian-container.sh +++ b/debian-container/debian-container.sh @@ -26,18 +26,35 @@ esac dc_require_root case "$DC_ACTION" in - base-create|init|enter|delete) + base-create|init|enter|exec|delete) # 防止多个会话同时读写同一 ext4 镜像。 dc_acquire_lock ;; esac case "$DC_ACTION" in + init|enter|exec|status|delete) + (($# > 0)) || dc_die "$DC_ACTION 需要实例名称。" + dc_select_instance "$1" + shift + ;; + _init|_enter|_exec) + (($# > 0)) || dc_die "$DC_ACTION 需要实例名称。" + dc_select_instance "$1" + shift + ;; +esac + +case "$DC_ACTION" in base-create) dc_base_create "$@" ;; init) dc_init "$@" ;; _init) dc_init_rootfs "$@" ;; - enter) dc_enter ;; + enter) dc_enter "$@" ;; _enter) dc_enter_rootfs ;; + exec) dc_exec "$@" ;; + _exec) dc_exec_rootfs "$@" ;; + status) dc_status ;; + list) dc_list ;; delete) dc_delete ;; *) dc_usage >&2 diff --git a/debian-container/lib/common.sh b/debian-container/lib/common.sh index 19bbd78..12a318b 100644 --- a/debian-container/lib/common.sh +++ b/debian-container/lib/common.sh @@ -24,23 +24,45 @@ dc_acquire_lock() { flock -n 9 || dc_die 'Debian 容器已经在运行或正在初始化。' } +dc_select_instance() { + local name=$1 + + [[ "$name" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || dc_die "实例名称无效:$name" + DC_INSTANCE=$name + DC_INSTANCE_DIR="$DC_INSTANCES/$DC_INSTANCE" + DC_IMAGE="$DC_INSTANCE_DIR/rootfs.ext4" + DC_ROOT="$DC_INSTANCE_DIR/root" +} + +dc_list() { + local path + + [[ -d "$DC_INSTANCES" ]] || return 0 + for path in "$DC_INSTANCES"/*; do + [[ -d "$path" ]] || continue + basename -- "$path" + done +} + dc_usage() { cat <<EOF 用法:sudo bash $DC_SCRIPT <命令> 命令: base-create [路径] 下载一次并创建 debootstrap 软件包 tarball - init [选项] 创建 Debian 容器镜像 - enter 进入容器 - delete 删除容器镜像 + init NAME [选项] 创建指定名称的容器镜像 + enter NAME 进入指定容器 + exec NAME -- CMD 在指定容器中执行命令 + status NAME 查看指定容器状态 + list 列出所有容器 + delete NAME 删除指定容器 init 选项: --tarball PATH 从本地 tarball 初始化,跳过软件包下载 环境变量: - DEBIAN_IMAGE 镜像路径(默认:当前目录/rootfs.ext4) - DEBIAN_ROOT 挂载目录(默认:当前目录/debian-root) - DEBIAN_LOCK_FILE 锁文件路径 + DEBIAN_INSTANCES 实例目录(默认:当前目录/instances) + DEBIAN_LOCK_FILE 锁文件路径(默认:/run/lock/debian-container.lock) DEBIAN_TARBALL 基础 tarball(默认:当前目录/debian-base.tar.gz) DEBIAN_SUITE Debian 版本(默认:stable) DEBIAN_MIRROR Debian 镜像地址 diff --git a/debian-container/lib/config.sh b/debian-container/lib/config.sh index cee5473..6efcab2 100644 --- a/debian-container/lib/config.sh +++ b/debian-container/lib/config.sh @@ -2,14 +2,16 @@ # SPDX-License-Identifier: GPL-3.0-or-later # Copyright (C) 2026 by Richard Qin -# Keep runtime data in the directory from which the tool is invoked for now. -# These variables can be overridden by the environment without changing the -# command interface. -DC_IMAGE=${DEBIAN_IMAGE:-"$PWD/rootfs.ext4"} -DC_ROOT=${DEBIAN_ROOT:-"$PWD/debian-root"} +# Keep instance data in the directory from which the tool is invoked for now. +DC_INSTANCES=${DEBIAN_INSTANCES:-"$PWD/instances"} DC_LOCK_FILE=${DEBIAN_LOCK_FILE:-/run/lock/debian-container.lock} DC_TARBALL=${DEBIAN_TARBALL:-"$PWD/debian-base.tar.gz"} DC_SUITE=${DEBIAN_SUITE:-stable} DC_MIRROR=${DEBIAN_MIRROR:-http://deb.debian.org/debian} DC_INCLUDE=${DEBIAN_INCLUDE:-passwd,util-linux,vim-tiny,e2fsprogs} +DC_INSTANCE= +DC_INSTANCE_DIR= +DC_IMAGE= +DC_ROOT= + diff --git a/debian-container/lib/container.sh b/debian-container/lib/container.sh index 05c3944..b3563eb 100644 --- a/debian-container/lib/container.sh +++ b/debian-container/lib/container.sh @@ -24,23 +24,24 @@ dc_init() { dc_require_commands debootstrap unshare mkfs.ext4 mount chroot setpriv flock - if [[ -n "$tarball" && ! -f "$tarball" ]]; then - dc_die "找不到 tarball:$tarball" + if [[ -n "$tarball" ]]; then + tarball=$(readlink -f -- "$tarball") + [[ -f "$tarball" ]] || dc_die "找不到 tarball:$tarball" fi - if [[ -e "$DC_IMAGE" ]]; then - dc_die "镜像已存在:$DC_IMAGE。请使用 enter 进入,避免覆盖容器数据。" + if [[ -e "$DC_INSTANCE_DIR" ]]; then + dc_die "实例已存在:$DC_INSTANCE。请使用 enter 进入,避免覆盖容器数据。" fi - mkdir -p "$(dirname -- "$DC_IMAGE")" "$DC_ROOT" + mkdir -p "$DC_INSTANCE_DIR" "$DC_ROOT" truncate -s 3G "$DC_IMAGE" mkfs.ext4 -F -q -L DEBIAN_CONTAINER "$DC_IMAGE" if [[ -n "$tarball" ]]; then - unshare --mount --propagation private -- bash "$DC_SCRIPT" _init "$tarball" + unshare --mount --propagation private -- bash "$DC_SCRIPT" _init "$DC_INSTANCE" "$tarball" else - unshare --mount --propagation private -- bash "$DC_SCRIPT" _init + unshare --mount --propagation private -- bash "$DC_SCRIPT" _init "$DC_INSTANCE" fi - echo "初始化完成。执行 sudo bash $DC_SCRIPT enter 进入容器。" + echo "初始化完成。执行 sudo bash $DC_SCRIPT enter $DC_INSTANCE 进入容器。" } dc_init_rootfs() { @@ -102,7 +103,7 @@ dc_enter() { [[ -f "$DC_IMAGE" ]] || dc_die '没有找到镜像,请先运行 init。' # 单独创建网络命名空间,暂时没有联网功能。 - unshare --mount --uts --ipc --pid --net --fork --propagation private -- bash "$DC_SCRIPT" _enter + unshare --mount --uts --ipc --pid --net --fork --propagation private -- bash "$DC_SCRIPT" _enter "$DC_INSTANCE" } dc_enter_rootfs() { @@ -117,9 +118,44 @@ dc_enter_rootfs() { chroot "$DC_ROOT" /bin/bash -l } +dc_exec() { + (($# > 0)) || dc_die '用法:exec NAME -- 命令 [参数...]' + [[ "$1" == -- ]] || dc_die '用法:exec NAME -- 命令 [参数...]' + shift + (($# > 0)) || dc_die 'exec 需要一个命令。' + [[ -f "$DC_IMAGE" ]] || dc_die '没有找到镜像,请先运行 init。' + + unshare --mount --uts --ipc --pid --net --fork --propagation private -- \ + bash "$DC_SCRIPT" _exec "$DC_INSTANCE" "$@" +} + +dc_exec_rootfs() { + dc_mount_root + trap dc_cleanup_mounts EXIT + dc_mount_virtual_filesystems + hostname debian-container + + setpriv --bounding-set=-sys_admin,-net_admin,-net_raw,-sys_module,-sys_boot,-sys_time,-sys_rawio,-sys_ptrace,-syslog -- \ + chroot "$DC_ROOT" "$@" +} + +dc_status() { + echo "实例:$DC_INSTANCE" + echo "目录:$DC_INSTANCE_DIR" + if [[ -f "$DC_IMAGE" ]]; then + echo '镜像:存在' + else + echo '镜像:不存在' + fi +} + dc_delete() { - [[ -f "$DC_IMAGE" ]] || { echo 'Debian 镜像已不存在。'; return 0; } - rm -- "$DC_IMAGE" - echo 'Debian 镜像已删除。' + [[ -d "$DC_INSTANCE_DIR" ]] || { echo 'Debian 实例已不存在。'; return 0; } + dc_require_commands findmnt + if findmnt --mountpoint "$DC_ROOT" >/dev/null 2>&1; then + dc_die "实例仍处于挂载状态:$DC_INSTANCE" + fi + rm -rf -- "$DC_INSTANCE_DIR" + echo "Debian 实例已删除:$DC_INSTANCE" } |
