package cgroup import ( "bufio" "fmt" "os" "path/filepath" "regexp" "strconv" "strings" "linkfog.com/public/lib/cgroup/fs" ) func GetContainerMemoryCgroupPath(pid int) (string, error) { if IsCgroup2() { return GetContainerCgroupPath(pid, "") } return GetContainerCgroupPath(pid, fs.MemorySubSystemName()) } func GetContainerCPUCgroupPath(pid int) (string, error) { if IsCgroup2() { return GetContainerCgroupPath(pid, "") } return GetContainerCgroupPath(pid, fs.CPUSubSystemName()) } func GetContainerIOCgroupPath(pid int) (string, error) { if IsCgroup2() { return GetContainerCgroupPath(pid, "") } return GetContainerCgroupPath(pid, fs.IOSubSystemName()) } // 获取容器cgroup路径,当pid为-1时,代表进程自身,cgroup路径选择顺序如下: // 1.主机视角,有hostPID权限:/proc/1/root/sys/fs/cgroup[/subsystem]/kubepods... // 2.主机视角,挂载主机host:/host/sys/fs/cgroup[/subsystem]/kubepods... // 3.容器视角,/proc//root/sys/fs/cgroup[/subsystem] func GetContainerCgroupPath(pid int, subsystem string) (string, error) { path, err := getContainerCgroupPathFromHost(pid, subsystem) if err == nil { return path, nil } path, err = getContainerCgroupPathFromContainer(pid, subsystem) if err == nil { return path, nil } return "", fmt.Errorf("unable to get container cgroup path") } // 主机视角,获取容器cgroup的特征 func GetContainerCgroupSpec(pid int, subsystem string) (string, error) { // 获取进程cgroup配置文件 var pidCgroup string if pid == -1 { pidCgroup = "/proc/self/cgroup" } else { pidCgroup = filepath.Join("/proc", strconv.Itoa(pid), "cgroup") } // 解析进程cgroup配置文件,获取相对cgroup路径 var relativeCgroupPath string var err error if IsCgroup2() { relativeCgroupPath, err = parseRelativeCgroupPathV2(pidCgroup) } else { relativeCgroupPath, err = parseRelativeCgroupPathV1(pidCgroup, subsystem) } return relativeCgroupPath, err } // 主机视角,获取容器cgroup路径 func getContainerCgroupPathFromHost(pid int, subsystem string) (string, error) { // 获取进程cgroup配置文件 var pidCgroup string if pid == -1 { pidCgroup = "/proc/self/cgroup" } else { pidCgroup = filepath.Join("/proc", strconv.Itoa(pid), "cgroup") } // 解析进程cgroup配置文件,获取相对cgroup路径 var relativeCgroupPath string var err error if IsCgroup2() { relativeCgroupPath, err = parseRelativeCgroupPathV2(pidCgroup) } else { relativeCgroupPath, err = parseRelativeCgroupPathV1(pidCgroup, subsystem) } if err != nil { return "", err } // 生成绝对cgroup路径 cgroupPrefixs := []string{ "/proc/1/root/sys/fs/cgroup", HostCgroup, } var absoluteCgroupPath string for _, prefix := range cgroupPrefixs { if IsCgroup2() { absoluteCgroupPath = filepath.Join(prefix, relativeCgroupPath) } else { absoluteCgroupPath = filepath.Join(prefix, subsystem, relativeCgroupPath) } if _, err := os.Stat(absoluteCgroupPath); err == nil { return absoluteCgroupPath, nil } } return "", fmt.Errorf("unable to get container cgroup path from host") } // 容器视角,获取容器cgroup路径 func getContainerCgroupPathFromContainer(pid int, subsystem string) (string, error) { var absoluteCgroupPath string if pid == -1 { absoluteCgroupPath = "/proc/self/root/sys/fs/cgroup" } else { absoluteCgroupPath = filepath.Join("/proc", strconv.Itoa(pid), "root/sys/fs/cgroup") } if !IsCgroup2() { absoluteCgroupPath = filepath.Join(absoluteCgroupPath, subsystem) } if _, err := os.Stat(absoluteCgroupPath); err == nil { return absoluteCgroupPath, nil } return "", fmt.Errorf("unable to get container cgroup path from container") } func parseRelativeCgroupPathV1(path string, subsystem string) (string, error) { f, err := os.Open(path) if err != nil { return "", err } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { if strings.Contains(scanner.Text(), subsystem) { tokens := strings.SplitN(scanner.Text(), ":", 3) if len(tokens) == 3 { return tokens[2], nil } break } } return "", fmt.Errorf("unable to find subsystem(%s) relative cgroup path "+ "in container cgroup file", subsystem) } func parseRelativeCgroupPathV2(path string) (string, error) { f, err := os.Open(path) if err != nil { return "", err } defer f.Close() reg := regexp.MustCompile("([0-9a-z]){64}") relativePath := "" scanner := bufio.NewScanner(f) for scanner.Scan() { tokens := strings.SplitN(scanner.Text(), ":", 3) if len(tokens) == 3 { // example: "0::/kubepods/besteffort/podad1189b4-15b6-4ee5-b509-08..." if strings.HasPrefix(tokens[2], "/kubepods") { return tokens[2], nil } // 当找不到/kubepods时,用于兜底 if relativePath == "" && reg.FindString(tokens[2]) != "" { relativePath = tokens[2] } } } if relativePath != "" { return relativePath, nil } return "", fmt.Errorf("unable to find relative cgroup path in container cgroup file") }