aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-10-29 00:21:41 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-10-29 00:21:41 +0800
commit7ab30ab3ee572de1be2afc3083e4a28b594b1cba (patch)
treef65683c722d0abc30a17a9a8e4485f9ec5fd1f8b
parent4e6376cfc58e73fc14ddb691c0fac9da425efac9 (diff)
downloadrutils-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/README37
-rwxr-xr-xdebian-container/debian-container.sh21
-rw-r--r--debian-container/lib/common.sh34
-rw-r--r--debian-container/lib/config.sh12
-rw-r--r--debian-container/lib/container.sh60
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"
}