七天用 Go 寫個 docker(第六天)

pibigstar發表於2020-07-05

今天主要來實現一下 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
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章