写点什么

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

作者:
  • 2022 年 4 月 11 日
  • 本文字数:3433 字

    阅读完需:约 11 分钟

简介

在上篇中我们实现了通过 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:00ID NAME PID STATUS COMMAND CREATED1808352378 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:05root 2751 1776 0 4月07 ? 00:00:01 /usr/libexec/xdg-desktop-portalroot 2778 1776 0 4月07 ? 00:00:00 /usr/libexec/xdg-desktop-portal-gtkroot 126298 1 0 07:28 pts/2 00:00:00 toproot 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:20ID NAME PID STATUS COMMAND CREATED1808352378 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:24root 2751 1776 0 4月07 ? 00:00:01 /usr/libexec/xdg-desktop-portalroot 2778 1776 0 4月07 ? 00:00:00 /usr/libexec/xdg-desktop-portal-gtkroot 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
复制代码


发布于: 刚刚阅读数: 2
用户头像

关注

还未添加个人签名 2018.09.09 加入

代码是门手艺活,也是门艺术活

评论

发布
暂无评论
自己动手写Docker系列 -- 5.5实现容器停止_Go_萧_InfoQ写作平台