简介

在上篇中我们实现了将容器后台运行,本篇中我们将实现docker的ps命令,查看当前正在运行中的容器列表

源码说明

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

  • Gitee: https://gitee.com/free-love/docker-demo
  • GitHub: https://github.com/lw1243925457/dockerDemo

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

代码实现

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

1.在容器启动的时候,将容器的信息写到指定的目录的文件中

2.在查看正在运行中的容器时,读取存放容器信息文件的目录,获取所有的容器信息文件,便可以得到正在运行中的容器列表

3.容器退出的时候,将指定目录下的文件进行删除

核心思路就如上所示,总体来说是比较简单的,下面我们就开始看具体的代码实现

容器启动时,存储容器信息,退出时删除

在启动函数中,我们在启动时,添加存储容器信息的逻辑

首先我们定义好容器信息结构类,如下:

ContainerInfo 是容器信息类,存放容器的信息

定义了容器状态的常量,为后面做准备,DefaultInfoLocation是约定的容器信息存放的指定目录,ConfigName是约定的容器信息存储文件名

type ContainerInfo struct {Pid        string `json:"pid"` // 容器的init进程在宿主机上的PIDID         string `json:"id"`Name       string `json:"name"`Command    string `json:"command"`CreateTime string `json:"createTime"`Status     string `json:"status"`
}var (RUNNING             = "running"STOP                = "stop"EXIT                = "exited"DefaultInfoLocation = "/var/run/mydocker/%s/"ConfigName          = "config.json"
)

在run命令中,我们添加可选的-name参数,来设置容器的名称

var RunCommand = cli.Command{Name:  "run",Usage: `Create a container with namespace and cgroups limit mydocker run -ti [command]`,Flags: []cli.Flag{......// 提供run后面的-name指定容器名字参数cli.StringFlag{Name:  "name",Usage: "container name",},},Action: func(context *cli.Context) error {......// 将取到的容器名称传递下去,如果没有则为空containerName := context.String("name")run.Run(tty, detach, cmdArray, resConfig, volume, containerName)return nil},
}

具体的Run函数逻辑如下:

func Run(tty, detach bool, cmdArray []string, config *subsystem.ResourceConfig, volume, containerName string) {......// 记录容器信息containerName, err = recordContainerInfo(parent.Process.Pid, cmdArray, containerName)if err != nil {log.Errorf("record contariner info err: %v", err)return}......log.Infof("parent process run")if !detach {_ = parent.Wait()deleteWorkSpace(rootUrl, mntUrl, volume)// 容器退出时,删除容器信息deleteContainerInfo(containerName)}os.Exit(-1)
}

存储容器信息的函数逻辑如下:

1.首先是生成运行时容器的基本信息

2.将其序列化成JSON存储在约定的文件中

如果没有传入容器名,则随机取名


func recordContainerInfo(pid int, cmdArray []string, containerName string) (string, error) {id := randStringBytes(10)createTime := time.Now().Format("2000-01-01 00:00:00")command := strings.Join(cmdArray, " ")if containerName == "" {containerName = id}containerInfo := &container.ContainerInfo{ID:         id,Pid:        strconv.Itoa(pid),Command:    command,CreateTime: createTime,Status:     container.RUNNING,Name:       containerName,}jsonBytes, err := json.Marshal(containerInfo)if err != nil {return "", fmt.Errorf("container info to json string err: %v", err)}jsonStr := string(jsonBytes)dirUrl := fmt.Sprintf(container.DefaultInfoLocation, containerName)if err := os.MkdirAll(dirUrl, 0622); err != nil {return "", fmt.Errorf("mkdir %s err: %v", dirUrl, err)}fileName := dirUrl + "/" + container.ConfigNamefile, err := os.Create(fileName)defer file.Close()if err != nil {return "", fmt.Errorf("create file %s, err: %v", fileName, err)}if _, err := file.WriteString(jsonStr); err != nil {return "", fmt.Errorf("file write string err: %v", err)}return containerName, nil
}func randStringBytes(n int) string {letterBytes := "1234567890"rand.Seed(time.Now().UnixNano())b := make([]byte, n)for i := range b {b[i] = letterBytes[rand.Intn(len(letterBytes))]}return string(b)
}

退出时删除容器的逻辑比较简单,直接删除文件即可:

func deleteContainerInfo(containerName string) {dirUrl := fmt.Sprintf(container.DefaultInfoLocation, containerName)if err := os.RemoveAll(dirUrl); err != nil {log.Errorf("remove dir %s err: %v", dirUrl, err)}
}

读取文件列表,显示正在运行中的容器

在上面的代码代码中,我们可以得到容器的信息存放在: /var/run/mydocker/{containerName}/config.json

我们遍历 /var/run/mydocker 便可以得到所有的容器目录,读取其下的config.json便可以得到容器信息

我们首先添加 main.go 中添加 ps 命令

func main() {app := cli.NewApp()app.Name = "mydocker"app.Usage = usageapp.Commands = []cli.Command{command.InitCommand,command.RunCommand,command.CommitCommand,command.ListCommand,}app.Before = func(context *cli.Context) error {log.SetFormatter(&log.JSONFormatter{})log.SetOutput(os.Stdout)return nil}if err := app.Run(os.Args); err != nil {log.Fatal(err)}
}

在main_command添加ps command

var ListCommand = cli.Command{Name:  "ps",Usage: "list all the container",Action: func(context *cli.Context) error {return run.ListContainers()},
}

ps 命令的具体实现如下:

遍历 /var/run/mydocker 便可以得到所有的容器目录,读取其下的config.json便可以得到容器信息

然后在控制台上进行打印

func ListContainers() error {dirUrl := fmt.Sprintf(container.DefaultInfoLocation, "")dirUrl = dirUrl[:len(dirUrl)-1]files, err := ioutil.ReadDir(dirUrl)if err != nil {return fmt.Errorf("read dir %s err: %v", dirUrl, err)}var containers []*container.ContainerInfofor _, file := range files {tmpContainer, err := getContainerInfo(file)if err != nil {return err}containers = append(containers, tmpContainer)}w := tabwriter.NewWriter(os.Stdout, 12, 1, 3, ' ', 0)_, _ = fmt.Fprint(w, "ID\tNAME\tPID\tSTATUS\tCOMMAND\tCREATED\n")for _, item := range containers {_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", item.ID, item.Name, item.Pid, item.Status, item.Command, item.CreateTime)}if err := w.Flush(); err != nil {return fmt.Errorf("flush ps write err: %v", err)}return nil
}func getContainerInfo(file fs.FileInfo) (*container.ContainerInfo, error) {containerName := file.Name()configFileDir := fmt.Sprintf(container.DefaultInfoLocation, containerName)configFilePath := configFileDir + container.ConfigNamecontent, err := ioutil.ReadFile(configFilePath)if err != nil {return nil, fmt.Errorf("read file %s err: %v", configFilePath, err)}var containerInfo container.ContainerInfoif err := json.Unmarshal(content, &containerInfo); err != nil {return nil, fmt.Errorf("json unmarshal err: %v", err)}return &containerInfo, nil
}

运行测试

我们启动两个后台容器,一个有名字,一个没有,结果如下,很nice

➜  dockerDemo git:(main) ./main run -d sh
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-22T20:36:06+08:00"}
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-22T20:36:06+08:00"}
{"level":"info","msg":"all command is : sh","time":"2022-03-22T20:36:06+08:00"}
{"level":"info","msg":"parent process run","time":"2022-03-22T20:36:06+08:00"}
➜  dockerDemo git:(main) ./main run -d -name test1 sh
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-22T20:36:14+08:00"}
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-22T20:36:14+08:00"}
{"level":"info","msg":"all command is : sh","time":"2022-03-22T20:36:14+08:00"}
{"level":"info","msg":"parent process run","time":"2022-03-22T20:36:14+08:00"}
➜  dockerDemo git:(main) ./main ps
ID           NAME         PID         STATUS      COMMAND     CREATED
7475097580   7475097580   21886       running     sh          22000-03-03 00:00:00
3160412281   test1        21912       running     sh          22000-03-03 00:00:00
➜  dockerDemo git:(main)

但目前还存在问题,如果是前台运行的容器,退出后,容器信息文件会随着删除,但后台运行的就不行,导致ps命令还是有些异常,会显示已经退出运行的容器

也是可能是我自己写的有问题,后面需要调整修复下

自己动手写Docker系列 -- 5.2实现查看运行中的容器相关推荐

  1. 自己动手写Docker系列 -- 5.7实现通过容器制作镜像

    简介 在上篇中我们实现了rm命令,删除存在的容器,本篇中,将完善之前的文件系统隔离,实现容器与容器之间的文件系统隔离 源码说明 同时放到了Gitee和Github上,都可进行获取 Gitee: htt ...

  2. docker ps命令详解 列出运行中的容器

    docker ps命令详解 列出运行中的容器 使用docker ps命令即可列出运行中的容器,执行该命令后,会出现如下7列表格 CONTAINER_ID      表示容器ID IMAGE       ...

  3. 自己动手写Docker系列 -- 6.3 手动配置容器网络(下)

    简介 网络部分较为复杂,本篇先利用之前写好的基础容器和网桥部分,加上手工给容器配置网络,让其容器与外部网络部分功能正常,为后面程序编写打下基础 源码说明 同时放到了Gitee和Github上,都可进行 ...

  4. 【无标题】自己动手写Docker系列 -- 6.3 手动配置容器网络(上)

    简介 网络部分较为复杂,本篇先利用之前写好的基础容器和网桥部分,加上手工给容器配置网络,让其容器与宿主机网络部分功能正常,为后面程序编写打下基础 源码说明 同时放到了Gitee和Github上,都可进 ...

  5. 自己动手写Docker系列 -- 3.1构造实现run命令版本的容器

    简介 通过对前面Linux的Namespace.Cgroups.Union File System的学习,对Docker实现的基础知识有了一点点了解,接下来就跟着作者开始编写 源码说明 同时放到了Gi ...

  6. 自己动手写Docker系列 -- 5.4实现进入容器的namespace,exec命令

    简介 在上篇中我们实现了将容器后台运行,本篇中我们将实现docker的ps命令,查看当前正在运行中的容器列表 源码说明 同时放到了Gitee和Github上,都可进行获取 Gitee: https:/ ...

  7. 自己动手写Docker系列 -- 4.1使用busybox创建容器

    简介 目前docker demo中还是使用的系统原有proc,不怎么纯净,本篇中使用busybox来更换docker demo的系统挂载点 源码说明 同时放到了Gitee和Github上,都可进行获取 ...

  8. 自己动手写Docker系列 -- 5.1实现容器的后台运行

    简介 在前几篇中,我们已经构建了一个基础的镜像,本篇开始做一些进阶的功能,下面就是实现docker中的-d命令,让容器能够后台运行 源码说明 同时放到了Gitee和Github上,都可进行获取 Git ...

  9. 自己动手写Docker系列 -- 4.3实现volume数据卷

    简介 在上篇中对容器和镜像实现了进一步的文件隔离,是容器内的修改不影响到宿主机.本篇中将实现docker中的volume,提供持久化存储能力 源码说明 同时放到了Gitee和Github上,都可进行获 ...

最新文章

  1. 过关斩将打进Kaggle竞赛Top 0.3%,我是这样做的
  2. Java安卓 使用视图组 布局
  3. 【转载】 regsvr32 注册dll
  4. [转载]如何在只能力加载的有限元程序里面实现按位移加载
  5. canal应用二:mysql数据实时同步到redis
  6. 一条数据的漫游 -- X-Engine SIGMOD Paper Introduction
  7. https://blog.csdn.net/weixin_40412037/article/details/112235003
  8. Vue实例和生命周期
  9. Java中整型的缓存机制
  10. 虚拟机安装mysql5.7.20_虚拟机环境下CentOS 7 中安装Mysql 5.7.24(源码方式安装)
  11. 11-标志寄存器+adc/sbb+cmp+条件转移指令
  12. log4j 日志书写格式_Log4J日志配置详解
  13. 利用Python使图片完美去除水印,我想试试马赛克的效果∧v∧
  14. 常用Jquery插件整理大全
  15. 【Redis】Redis常用命令
  16. SpringBoot的banner竟然可以用美女图片在线制作,难以相信
  17. 使用python爬取高德POI数据,并转换为WGS84经纬度坐标的点矢量
  18. VUE Echarts世界地图 中文名称显示国家
  19. 2020-01-08 Oracle 数据库储存生僻字
  20. Liunx7 DNS服务器正反解析,域名转发

热门文章

  1. HDU 1085 Holding Bin-Laden Captive!
  2. 【腾讯优测干货分享】使用多张图片做帧动画的性能优化
  3. 关于javascript dom扩展:Selector API
  4. Basic Level 1006. 换个格式输出整数 (15)
  5. ActionBarSherlock SlidingMenu整合,解决SlidingMenu example的getSupportActionBar()方法不能用问题
  6. js递归性能影响及解决方案
  7. Non-parseable POM C:\Users\admin\.m2\repository\org\springframework问题解决方案
  8. 如何在Java的特定范围内生成随机整数? [英]How do I generate random integers within a specific range in Java?
  9. 2021年1月十大热门报告盘点(5天VIP会员免费送)
  10. leetcode力扣94. 二叉树的中序遍历