package docker import ( "os/exec" "strconv" "context" "errors" "regexp" "strings" "sync" "time" "github.com/moby/moby/api/types/container" "github.com/moby/moby/client" ) var ( ReDocker = regexp.MustCompile(`^.*docker[-/]([0-9a-z]*)(?:|.*)`) ContainerInfo *ContainersInfo = nil ) func NewContainersInfo() *ContainersInfo { ContainerInfo = &ContainersInfo{ inspectInfo: make(map[string]container.InspectResponse), listInfo: make(map[string]container.Summary), topInfo: make(map[string]container.TopResponse), time: time.Now(), inspectLock: sync.RWMutex{}, topLock: sync.RWMutex{}, listLock: sync.RWMutex{}, } return ContainerInfo } type ContainersInfo struct { time time.Time // 记录写入Info的时间 inspectInfo map[string]container.InspectResponse inspectLock sync.RWMutex listInfo map[string]container.Summary listLock sync.RWMutex topInfo map[string]container.TopResponse topLock sync.RWMutex } type ContainerPsInfo struct { Pid int32 Ppid int32 Uid string Cmd string } func ParsePsInfo(topInfo map[string]container.TopResponse) (map[string][]ContainerPsInfo, error) { if topInfo == nil { return nil, errors.New("topInfo is nil") } result := make(map[string][]ContainerPsInfo) for cid, topResp := range topInfo { indexMap, t := make(map[string]int), 0 result[cid] = make([]ContainerPsInfo, 0) for index, key := range topResp.Titles { switch strings.TrimSpace(strings.ToLower(key)) { case "pid": indexMap["pid"] = index t++ case "ppid": indexMap["ppid"] = index t++ case "uid": indexMap["uid"] = index t++ case "cmd": indexMap["cmd"] = index t++ default: } if t >= 4 { break } } for _, fields := range topResp.Processes { item := ContainerPsInfo{} if v, ok := indexMap["pid"]; ok { pid, err := strconv.ParseInt(fields[v], 10, 64) if err != nil { return nil, err } item.Pid = int32(pid) } if v, ok := indexMap["ppid"]; ok { ppid, err := strconv.ParseInt(fields[v], 10, 64) if err != nil { return nil, err } item.Ppid = int32(ppid) } if v, ok := indexMap["uid"]; ok { item.Uid = fields[v] } if v, ok := indexMap["cmd"]; ok { item.Cmd = fields[v] } result[cid] = append(result[cid], item) } } return result, nil } func (info *ContainersInfo) Update() error { info.listLock.Lock() info.topLock.Lock() info.inspectLock.Lock() defer func() { info.inspectLock.Unlock() info.topLock.Unlock() info.listLock.Unlock() }() i, s, t, err := getRunningContainerInfo() if err != nil { return err } info.inspectInfo = i info.listInfo = s info.topInfo = t info.time = time.Now() return nil } func (info *ContainersInfo) GetInspectInfo(update bool) (map[string]container.InspectResponse, sync.Locker) { if update { info.Update() } rl := info.inspectLock.RLocker() rl.Lock() if info.inspectInfo == nil { return make(map[string]container.InspectResponse), rl } return info.inspectInfo, rl } // GetProcessIdInDocker 获取所用容器的进程信息 func (info *ContainersInfo) GetProcessIdInDocker(update bool) (map[string][]ContainerPsInfo, error) { if update { err := info.Update() if err != nil { return nil, err } } rl := info.topLock.RLocker() rl.Lock() i, err := ParsePsInfo(info.topInfo) rl.Unlock() rl = nil if err != nil { return nil, err } return i, nil } // getRunningContainerInfo 获取所有正在运行的docker容器的详细信息 func getRunningContainerInfo() (map[string]container.InspectResponse, map[string]container.Summary, map[string]container.TopResponse, error) { cli, err := GetDockerClient() if err != nil { return nil, nil, nil, err } defer func() { _ = cli.Close() }() containerSum, err := cli.ContainerList(context.Background(), client.ContainerListOptions{All: false}) if err != nil { return nil, nil, nil, err } inspects := make(map[string]container.InspectResponse) lists := make(map[string]container.Summary) tops := make(map[string]container.TopResponse) for _, c := range containerSum { inspect, innerErr := cli.ContainerInspect(context.Background(), c.ID) if innerErr != nil { return nil, nil, nil, innerErr } inspects[c.ID] = inspect lists[c.ID] = c topInfo, innerErr := cli.ContainerTop(context.Background(), c.ID, nil) if innerErr != nil { return nil, nil, nil, innerErr } tops[c.ID] = topInfo } return inspects, lists, tops, nil } var ( reDockerApi = regexp.MustCompile(`(?i)^\s+api\s+version:\s+([0-9.]+).*$`) ) func GetDockerAPIVersion() (string, error) { output, err := exec.Command("docker", "version").Output() if err != nil { return "", err } lines := strings.Split(strings.Trim(string(output), "\n"), "\n") for _, v := range lines { if reDockerApi.MatchString(v) { m := reDockerApi.FindStringSubmatch(v) return m[1], nil } } return "", nil } func GetDockerClient() (*client.Client, error) { ver, err := GetDockerAPIVersion() if err != nil { return client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) } return client.NewClientWithOpts(client.FromEnv, client.WithVersion(ver)) }