当前位置:首页 > 技术 > 正文内容

实现容器删除功能:仿照 Docker 设计 mydocker rm 命令

访客 技术 2026年6月6日 1

本文是"从零实现简易 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

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。