实现容器删除功能:仿照 Docker 设计 mydocker rm 命令
本文是"从零实现简易 Docker"系列的第十三篇文章,重点在于实现类似于 docker rm 的命令,用于移除已停止或强制终止运行中的容器。通过本篇内容,我们将补全容器生命周期管理的最后一环——删除操作。
项目完整源码地址:https://github.com/lixd/mydocker
建议使用 root 权限运行,开发环境如下:
Distributor ID: Ubuntu
Description: Ubuntu 20.04.2 LTS
Release: 20.04
Kernel: 5.4.0-74-generic
设计思路
在前序章节中,我们已经实现了 mydocker stop 命令来安全停止一个正在运行的容器。当容器进入 STOPPED 状态后,其对应的进程已被回收,但元数据和目录仍保留在系统中。为了释放资源并清理无用状态,需要提供一种机制将其彻底移除。
因此,mydocker rm 的核心逻辑非常直接:
- 根据用户输入的容器 ID 查找对应的容器信息;
- 检查当前容器状态:
- 若为 STOPPED 状态,则直接删除存储该容器元数据的目录;
- 若为 RUNNING 状态且未指定强制标志(-f),则提示需先停止容器;
- 若指定了
-f标志,则先调用 stop 操作再执行删除。
命令定义与解析
使用 cli 包注册 rm 子命令,并支持布尔标志 -f 实现强制删除功能:
var removeCommand = cli.Command{
Name: "rm",
Usage: "删除未使用的容器,例如:mydocker rm abc12345",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "f",
Usage: "强制删除运行中的容器",
},
},
Action: func(ctx *cli.Context) error {
if len(ctx.Args()) == 0 {
return fmt.Errorf("缺少容器 ID")
}
containerID := ctx.Args().First()
force := ctx.Bool("f")
deleteContainer(containerID, force)
return nil
},
}
上述代码完成参数解析后,将控制权交给 deleteContainer 函数处理具体逻辑。
删除逻辑实现
deleteContainer 是实际执行删除动作的核心函数,它会读取容器状态并决定是否允许删除:
func deleteContainer(containerID string, force bool) {
info, err := loadContainerInfo(containerID)
if err != nil {
log.Errorf("无法获取容器 [%s] 的信息: %v", containerID, err)
return
}
switch info.Status {
case container.STOP:
removeDirAndData(containerID)
case container.RUNNING:
if !force {
log.Errorf("无法删除运行中的容器 [%s],请先 stop 或使用 -f 强制删除", containerID)
return
}
// 强制模式下先停止再删除
stopContainer(containerID)
deleteContainer(containerID, force) // 递归调用确保删除
default:
log.Errorf("不支持的状态 %s,无法删除容器", info.Status)
}
}
// 移除容器元数据目录
func removeDirAndData(id string) {
path := fmt.Sprintf(container.InfoLocationTemplate, id)
if err := os.RemoveAll(path); err != nil {
log.Errorf("删除容器目录失败 [%s]: %v", path, err)
} else {
log.Infof("成功删除容器数据目录: %s", path)
}
}
其中 loadContainerInfo 负责从本地 JSON 文件加载容器元信息,而 removeDirAndData 则负责清理磁盘上的持久化数据。
功能测试验证
以下为完整的端到端测试流程:
1. 创建并删除已停止的容器
启动一个后台容器:
./mydocker run -d -name test_rm top
查看状态:
./mydocker ps
# 输出示例:
ID NAME PID STATUS COMMAND CREATED
a1b2c3d4e5 test_rm 200100 running top 2024-01-30 16:00:00
尝试直接删除运行中容器会失败:
./mydocker rm a1b2c3d4e5
# 错误输出:
{"level":"error","msg":"无法删除运行中的容器 [a1b2c3d4e5]..."}
先停止容器:
./mydocker stop a1b2c3d4e5
再次删除即可成功:
./mydocker rm a1b2c3d4e5
./mydocker ps # 容器列表为空
2. 强制删除运行中容器
重新创建一个新的运行态容器:
./mydocker run -d -name force_rm top
使用 -f 参数进行强制删除:
./mydocker rm -f force_rm
观察结果:
- 容器进程被终止(可通过
ps aux | grep top验证); - 元数据目录被清除;
mydocker ps不再显示该容器。
说明强制删除逻辑生效。
总结
本文完成了简易 Docker 的容器删除功能,实现了 mydocker rm 命令的基本行为:
- 支持删除处于 STOPPED 状态的容器;
- 通过
-f支持强制删除运行中容器,内部自动触发 stop 流程; - 利用文件系统操作完成元数据清理。
至此,容器的典型生命周期(create → start → stop → rm)已全部覆盖。后续可进一步扩展如批量删除、只允许删除已停止容器等策略控制。
更多技术细节可参考 GitHub 仓库 feat-rm 分支:
git clone -b feat-rm https://github.com/lixd/mydocker.git
cd mydocker && go mod tidy && go build