从零开始写 Docker(十三)---实现 mydocker rm 删除容器

本文为从零开始写 Docker 系列第十三篇,实现类似 docker rm 的功能,使得我们能够删除容器。


完整代码见:https://github.com/lixd/mydocker

欢迎 Star

推荐阅读以下文章对 docker 基本实现有一个大致认识:



开发环境如下:

bash 复制代码
root@mydocker:~# lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.2 LTS
Release:	20.04
Codename:	focal
root@mydocker:~# uname -r
5.4.0-74-generic

注意:需要使用 root 用户

1. 概述

之前实现了 mydocker stop 能够停止后台运行的容器,那么,对于已经处于停止状态的容器,还剩余一个删除操作来补全容器的整个生命周期。

本篇就会完成这最后一步的清理工作,实现mydocker rm 命令,让我们能够直接删除已经停止的容器。

2. 实现

mydocker rm 实现起来很简单,主要是文件操作,因为容器对应的进程已经被停止,所以只需要将对应记录文件信息的目录删除即可。

docker 可以通过 -f 强制删除运行中的容器,具体见 moby/delete.go#L92,这里也加一下,指定 force 时先 stop 再删除即可。

removeCommand

同样是先定义 removeCommand,然后再添加到 main 函数中。

go 复制代码
var removeCommand = cli.Command{
	Name:  "rm",
	Usage: "remove unused containers,e.g. mydocker rm 1234567890",
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:  "f", // 强制删除
			Usage: "force delete running container,",
		}},
	Action: func(context *cli.Context) error {
		if len(context.Args()) < 1 {
			return fmt.Errorf("missing container id")
		}
		containerId := context.Args().Get(0)
		force := context.Bool("f")
		removeContainer(containerId, force)
		return nil
	},
}

这里只做参数解析,拿到 containerId 传递给 removeContainer 即可。

removeContainer

removeContainer 则是 rm 命令的真正实现,根据 Id 拿到容器信息,然后先判断状态:

  • STOP 状态,则直接删除
  • RUNNING 状态,如果带了 force flag 则先 Stop 然后再删除,否则打印错误信息
go 复制代码
func removeContainer(containerId string, force bool) {
	containerInfo, err := getInfoByContainerId(containerId)
	if err != nil {
		log.Errorf("Get container %s info error %v", containerId, err)
		return
	}

	switch containerInfo.Status {
	case container.STOP: // STOP 状态容器直接删除即可
		dirPath := fmt.Sprintf(container.InfoLocFormat, containerId)
		if err = os.RemoveAll(dirPath); err != nil {
			log.Errorf("Remove file %s error %v", dirPath, err)
			return
		}
	case container.RUNNING: // RUNNING 状态容器如果指定了 force 则先 stop 然后再删除
		if !force {
			log.Errorf("Couldn't remove running container [%s], Stop the container before attempting removal or"+
				" force remove", containerId)
			return
		}
		stopContainer(containerId)
		removeContainer(containerId, force)
	default:
		log.Errorf("Couldn't remove container,invalid status %s", containerInfo.Status)
		return
	}
}

3. 测试

删除 STOP 状态容器

首先创建一个 detach 容器

shell 复制代码
root@mydocker:~/feat-rm/mydocker# go build .
root@mydocker:~/feat-rm/mydocker# ./mydocker run -d -name rm1 top
{"level":"info","msg":"createTty false","time":"2024-01-30T15:11:20+08:00"}
{"level":"info","msg":"resConf:\u0026{ 0  }","time":"2024-01-30T15:11:20+08:00"}
{"level":"info","msg":"busybox:/root/busybox busybox.tar:/root/busybox.tar","time":"2024-01-30T15:11:20+08:00"}
{"level":"error","msg":"mkdir dir /root/merged error. mkdir /root/merged: file exists","time":"2024-01-30T15:11:20+08:00"}
{"level":"error","msg":"mkdir dir /root/upper error. mkdir /root/upper: file exists","time":"2024-01-30T15:11:20+08:00"}
{"level":"error","msg":"mkdir dir /root/work error. mkdir /root/work: file exists","time":"2024-01-30T15:11:20+08:00"}
{"level":"info","msg":"mount overlayfs: [/usr/bin/mount -t overlay overlay -o lowerdir=/root/busybox,upperdir=/root/upper,workdir=/root/work /root/merged]","time":"2024-01-30T15:11:20+08:00"}
{"level":"info","msg":"command all is top","time":"2024-01-30T15:11:20+08:00"}

mydocker ps 查看一下:

shell 复制代码
root@mydocker:~/feat-rm/mydocker# ./mydocker ps
ID           NAME        PID         STATUS      COMMAND     CREATED
0394026801   rm1         181151      running     top         2024-01-30 15:11:20

可以看到,容器正处于 running 状态。

尝试直接删除容器:

shell 复制代码
root@mydocker:~/feat-rm/mydocker# ./mydocker rm 0394026801
{"level":"error","msg":"Couldn't remove running container [0394026801], Stop the container before attempting removal or force remove","time":"2024-01-30T15:12:12+08:00"}

根据错误信息可知,不能直接删除运行中的容器

于是先把容器 stop 掉:

shell 复制代码
root@mydocker:~/feat-rm/mydocker# ./mydocker stop 0394026801
root@mydocker:~/feat-rm/mydocker# ./mydocker ps
ID           NAME        PID         STATUS      COMMAND     CREATED
0394026801   rm1                     stopped     top         2024-01-30 15:11:20

此时已经是 stopped 状态,可以执行删除了。

shell 复制代码
root@mydocker:~/feat-rm/mydocker# ./mydocker rm 0394026801
root@mydocker:~/feat-rm/mydocker# ./mydocker ps
ID          NAME        PID         STATUS      COMMAND     CREATED

可以看到,容器信息已经不见了,说明删除成功。

强制删除 RUNNING 状态容器

再测试一下指定 -f 时能否删除 RUNNING 状态的容器。

首先,也是启动一个 detach 容器

bash 复制代码
root@mydocker:~/feat-rm/mydocker# ./mydocker run -d -name rm2 top
{"level":"info","msg":"createTty false","time":"2024-01-30T15:13:44+08:00"}
{"level":"info","msg":"resConf:\u0026{ 0  }","time":"2024-01-30T15:13:44+08:00"}
{"level":"info","msg":"busybox:/root/busybox busybox.tar:/root/busybox.tar","time":"2024-01-30T15:13:44+08:00"}
{"level":"error","msg":"mkdir dir /root/merged error. mkdir /root/merged: file exists","time":"2024-01-30T15:13:44+08:00"}
{"level":"error","msg":"mkdir dir /root/upper error. mkdir /root/upper: file exists","time":"2024-01-30T15:13:44+08:00"}
{"level":"error","msg":"mkdir dir /root/work error. mkdir /root/work: file exists","time":"2024-01-30T15:13:44+08:00"}
{"level":"info","msg":"mount overlayfs: [/usr/bin/mount -t overlay overlay -o lowerdir=/root/busybox,upperdir=/root/upper,workdir=/root/work /root/merged]","time":"2024-01-30T15:13:44+08:00"}
{"level":"info","msg":"command all is top","time":"2024-01-30T15:13:44+08:00"}

查看容器信息

bash 复制代码
root@mydocker:~/feat-rm/mydocker# ./mydocker ps
ID           NAME        PID         STATUS      COMMAND     CREATED
9293725578   rm2         181202      running     top         2024-01-30 15:13:44
root@mydocker:~/feat-rm/mydocker# ps -ef|grep top
root      181202       1  0 15:13 pts/10   00:00:00 top

普通删除和强制删除

bash 复制代码
root@mydocker:~/feat-rm/mydocker# ./mydocker rm 9293725578
{"level":"error","msg":"Couldn't remove running container [9293725578], Stop the container before attempting removal or force remove","time":"2024-01-30T15:15:10+08:00"}
root@mydocker:~/feat-rm/mydocker# ./mydocker rm -f 9293725578

普通删除提示失败,强制删除则成功了,看下是否真的删掉了

bash 复制代码
root@mydocker:~/feat-rm/mydocker# ./mydocker ps
ID          NAME        PID         STATUS      COMMAND     CREATED
root@mydocker:~/feat-rm/mydocker# ps -ef|grep top
root      181231  177607  0 15:15 pts/10   00:00:00 grep --color=auto top

容器信息删除了,进程也消失了,说明删除是成功的。

4. 小结

本篇主要实现 mydocker rm 命令,根据 containerId 找到记录容器信息的目录,然后删除该目录以实现删除容器的效果。

对于 RUNNING 状态容器可以指定-f 强制删除,或者先执行 stop 命令停止容器。


**【从零开始写 Docker 系列】**持续更新中,搜索公众号【探索云原生】订阅,阅读更多文章。



完整代码见:https://github.com/lixd/mydocker

欢迎关注~

相关代码见 feat-rm 分支,测试脚本如下:

需要提前在 /root 目录准备好 busybox.tar 文件,具体见第四篇第二节。

bash 复制代码
# 克隆代码
git clone -b feat-rm https://github.com/lixd/mydocker.git
cd mydocker
# 拉取依赖并编译
go mod tidy
go build .
# 测试 
./mydocker run -d -name c1 top
# 查看容器 Id
./mydocker ps
# stop 停止指定容器
./mydocker stop ${containerId}
相关推荐
深度学习真难19 分钟前
docker启动kafka、zookeeper、kafdrop
docker·zookeeper·kafka
qq_172805591 小时前
Docker login 报证书存储错误的解决办法
docker·容器·证书
EasyDSS1 小时前
互联网视频推拉流EasyDSS视频直播点播平台视频转码有哪些技术特点和应用?
运维·服务器·音视频·rtmp推流·视频转码
HengYuan_Tech2 小时前
gitlab ssh-key 绑定
运维·ssh·gitlab
有来技术2 小时前
Linux 服务器安装 Docker - CentOS 9 (Stream)
linux·服务器·docker
生活百般滋味,人生需要笑对。 --佚名2 小时前
docker如何安装redis
redis·docker·容器
DADIAN_GONG2 小时前
How to install tree on Centos? what is difference between apt and yum?
linux·运维·centos
Karoku0663 小时前
【docker集群应用】Docker常用命令
运维·数据库·docker·容器
怡雪~4 小时前
K8s的水平自动扩容和缩容HPA
云原生·容器·kubernetes
史努比.4 小时前
HPA - k8s自动伸缩机制
云原生·容器·kubernetes