自己动手写Docker系列 -- 5.5实现容器停止

本文介绍如何自己动手实现Docker的stop命令,通过解析容器配置文件获取PID,发送kill信号来停止容器,并更新配置文件记录停止状态。详细步骤包括定位配置文件、发送停止信号以及保存停止信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

在上篇中我们实现了通过exec命令,重新进入了后台运行中的容器,本篇将实现stop命令,将运行中的容器停止

源码说明

同时放到了Gitee和Github上,都可进行获取

本章节对应的版本标签是:5.5,防止后面代码过多,不好查看,可切换到标签版本进行查看

代码实现

实现该功能的主要思路如下:

1 首先根据容器名称,定位到其配置文件

2 读取配置文件,得到容器的PID,发送kill信号,停止进程,这样容器运行也停止了

3 将相关的停止信息更新写入配置文件中进行保存

stop命令新增

在main函数中新增stop命令:

func main() {
	app.Commands = []cli.Command{
		command.InitCommand,
		command.RunCommand,
		command.CommitCommand,
		command.ListCommand,
		command.LogCommand,
		command.ExecCommand,
		command.StopCommand,
	}
}

main_command.go新增相关的命令:

var StopCommand = cli.Command{
	Name:  "stop",
	Usage: "stop container",
	Action: func(context *cli.Context) {
		if len(context.Args()) < 1 {
			log.Errorf("missing container name")
			return
		}
		containerName := context.Args().Get(0)
		if err := run.StopContainer(containerName); err != nil {
			log.Errorf("stop container err: %v", err)
		}
	},
}

读取容器配置文件,停止并更新容器

我们根据容器名称,找到容器的配置文件的存放位置

读取配置文件后,我们能得到容器在宿主机上的PID

更加PID,我们就能发送kill命令,去停止容器

停止容器后,将配置文件中的容器状态改为停止,然后更新存储配置文件

具体的代码实现如下:

func StopContainer(containerName string) error {
	// 根据容器名称,得到PID
	pid, err := getContainerPidByName(containerName)
	if err != nil {
		return err
	}

	pidInt, err := strconv.Atoi(pid)
	if err != nil {
		return fmt.Errorf("convert pid %s to int err: %v", pid, err)
	}

	// 发送kill名称,停止容器进程
	if err := syscall.Kill(pidInt, syscall.SIGTERM); err != nil {
		return fmt.Errorf("send sigterm %d, err: %v", pid, err)
	}

	// 更新存储容器信息
	containerInfo, err := getContainerInfoByName(containerName)
	if err != nil {
		return fmt.Errorf("get container info err: %v", err)
	}

	containerInfo.Status = container.STOP
	containerInfo.Pid = ""
	newContainerInfo, err := json.Marshal(containerInfo)
	if err != nil {
		return fmt.Errorf("json marshal %v,err: %v", containerInfo, err)
	}

	dirUrl := fmt.Sprintf(container.DefaultInfoLocation, containerName)
	configPath := dirUrl + container.ConfigName
	if err := ioutil.WriteFile(configPath, newContainerInfo, 0622); err != nil {
		return fmt.Errorf("write file %s err: %v", configPath, err)
	}
	return nil
}

上面的代码中用到了两个函数,一个是根据容器名称得到容器的PID,一个是根据容器名称得到容器的配置(根据这两个好像可以优化下,拿到了容器的配置,里面就有容器的PID了,读者感兴趣的话可以试试)


func getContainerPidByName(containerName string) (string, error) {
	dirUrl := fmt.Sprintf(container.DefaultInfoLocation, containerName)
	configFilePath := dirUrl + container.ConfigName
	contentBytes, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		return "", fmt.Errorf("read file %s err: %v", configFilePath, err)
	}

	var containerInfo container.ContainerInfo
	if err := json.Unmarshal(contentBytes, &containerInfo); err != nil {
		return "", fmt.Errorf("json ummarshal err: %v", err)
	}
	return containerInfo.Pid, nil
}

func getContainerInfoByName(containerName string) (*container.ContainerInfo, error) {
	dirUrl := fmt.Sprintf(container.DefaultInfoLocation, containerName)
	configFilePath := dirUrl + container.ConfigName
	contentBytes, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		return nil, fmt.Errorf("read file %s err: %v", configFilePath, err)
	}

	var containerInfo container.ContainerInfo
	if err := json.Unmarshal(contentBytes, &containerInfo); err != nil {
		return nil, fmt.Errorf("json ummarshal err: %v", err)
	}
	return &containerInfo, nil
}

运行测试

我们测试如下:

  • 启动一个后台运行的top命令容器
  • ps查看状态:看到有一个预期的running的容器
  • 查看是否有对应的宿主机进程,看到有一个对应的top进程
  • 使用stop命令
  • ps查看状态:看到已经停止了
  • 查看宿主机是否有top进程,看到没有了
root@lw-Code-01-Series-PF5NU1G  ~/code/go/dockerDemo   main ●  ./main run --name bird -d top                                                                                       ✔  ⚡  417  07:28:53
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-04-09T07:29:00+08:00"}
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-04-09T07:29:00+08:00"}
{"level":"info","msg":"all command is : top","time":"2022-04-09T07:29:00+08:00"}
{"level":"info","msg":"parent process run","time":"2022-04-09T07:29:00+08:00"}

root@lw-Code-01-Series-PF5NU1G  ~/code/go/dockerDemo   main ●  ./main ps                                                                                                  SIG(127) ↵  ⚡  418  07:29:00
ID           NAME        PID         STATUS      COMMAND     CREATED
1808352378   bird        126298      running     top         9000-04-04 00:00:00

root@lw-Code-01-Series-PF5NU1G  ~/code/go/dockerDemo   main ●  ps -ef|grep top                                                                                                     ✔  ⚡  419  07:29:05
root        2751    1776  0 4月07 ?       00:00:01 /usr/libexec/xdg-desktop-portal
root        2778    1776  0 4月07 ?       00:00:00 /usr/libexec/xdg-desktop-portal-gtk
root      126298       1  0 07:28 pts/2    00:00:00 top
root      126537   22616  0 07:29 pts/2    00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox top

root@lw-Code-01-Series-PF5NU1G  ~/code/go/dockerDemo   main ●  ./main stop bird                                                                                                    ✔  ⚡  420  07:29:09
 
root@lw-Code-01-Series-PF5NU1G  ~/code/go/dockerDemo   main ●  ./main ps                                                                                                           ✔  ⚡  421  07:29:20
ID           NAME        PID         STATUS      COMMAND     CREATED
1808352378   bird                    stop        top         9000-04-04 00:00:00

root@lw-Code-01-Series-PF5NU1G  ~/code/go/dockerDemo   main ●  ps -ef|grep top                                                                                                     ✔  ⚡  422  07:29:24
root        2751    1776  0 4月07 ?       00:00:01 /usr/libexec/xdg-desktop-portal
root        2778    1776  0 4月07 ?       00:00:00 /usr/libexec/xdg-desktop-portal-gtk
root      126883   22616  0 07:29 pts/2    00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox top
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值