今天主要来实现一下 go-docker ps 的功能,也就是查看当前有哪些容器,简单说下思路,当我们启动一个容器时就为该容器创建一个文件夹用来保存该容器的一些信息,如果我们给容器指定了名字,那么该文件夹名字就是我们指定的名字,如果未指定,就用我们自动生成的容器 ID 作为文件夹名,同时在该文件夹中创建 config.json 用来保存容器信息

文件夹结构

go-docker
└── 容器名/容器ID
    ├── config.json
    └── container.log
  • config.json 记录容器基础信息
  • container.log 记录容器内容日志

记录容器信息

创建容器时,同时给该容器创建一个文件夹,文件夹内并创建 config.json 来保存容器信息,这里先看下我们要保存容器哪些信息

type ContainerInfo struct {
    Pid         string   `json:"pid"`     // 容器的init进程再宿主机上的PID
    Id          string   `json:"id"`      // 容器ID
    Command     string   `json:"command"` // 容器内init进程的运行命令
    Name        string   `json:"name"`
    CreateTime  string   `json:"createTime"`
    Status      string   `json:"status"`
    Volume      string   `json:"volume"`      //容器的数据卷
    PortMapping []string `json:"portmapping"` //端口映射
}

看下代码实现,比较简单,一共就 3 步

  1. 创建以容器名或 ID 命名的文件夹
  2. 在该文件下创建 config.json
  3. 将容器信息保存到 config.json 中
// 记录容器信息
func RecordContainerInfo(containerPID int, cmdArray []string, containerName, containerID string) error {
    info := &ContainerInfo{
        Pid:        strconv.Itoa(containerPID),
        Id:         containerID,
        Command:    strings.Join(cmdArray, ""),
        Name:       containerName,
        CreateTime: time.Now().Format("2006-01-02 15:04:05"),
        Status:     common.Running,
    }

    dir := path.Join(common.DefaultContainerInfoPath, containerName)
    _, err := os.Stat(dir)
    if err != nil && os.IsNotExist(err) {
        err := os.MkdirAll(dir, os.ModePerm)
        if err != nil {
            logrus.Errorf("mkdir container dir: %s, err: %v", dir, err)
            return err
        }
    }

    fileName := fmt.Sprintf("%s/%s", dir, common.ContainerInfoFileName)
    file, err := os.Create(fileName)
    if err != nil {
        logrus.Errorf("create config.json, fileName: %s, err: %v", fileName, err)
        return err
    }

    bs, _ := json.Marshal(info)
    _, err = file.WriteString(string(bs))
    if err != nil {
        logrus.Errorf("write config.json, fileName: %s, err: %v", fileName, err)
        return err
    }

    return nil
}

遍历容器

简单说下实现逻辑

  1. 遍历 go-docker 文件夹
  2. 读取每个容器内的 config.json 文件
  3. 格式化打印
func ListContainerInfo() {
    files, err := ioutil.ReadDir(common.DefaultContainerInfoPath)
    if err != nil {
        logrus.Errorf("read info dir, err: %v", err)
    }

    var infos []*ContainerInfo
    for _, file := range files {
        info, err := getContainerInfo(file.Name())
        if err != nil {
            logrus.Errorf("get container info, name: %s, err: %v", file.Name(), err)
            continue
        }
        infos = append(infos, info)
    }

    // 打印
    w := tabwriter.NewWriter(os.Stdout, 12, 1, 2, ' ', 0)
    _, _ = fmt.Fprint(w, "ID\tNAME\tPID\tSTATUS\tCOMMAND\tCREATED\n")
    for _, info := range infos {
        _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", info.Id, info.Name, info.Pid, info.Status, info.Command, info.CreateTime)
    }

    // 刷新标准输出流缓存区,将容器列表打印出来
    if err := w.Flush(); err != nil {
        logrus.Errorf("flush info, err: %v", err)
    }
}

// 获取容器内基本信息
func getContainerInfo(containerName string) (*ContainerInfo, error) {
    filePath := path.Join(common.DefaultContainerInfoPath, containerName, common.ContainerInfoFileName)
    bs, err := ioutil.ReadFile(filePath)
    if err != nil {
        logrus.Errorf("read file, path: %s, err: %v", filePath, err)
        return nil, err
    }
    info := &ContainerInfo{}
    err = json.Unmarshal(bs, info)
    return info, err
}

转自:微信号 跟派大星学编程